aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/log.h1
-rw-r--r--sway/ipc.c191
-rw-r--r--sway/log.c30
3 files changed, 162 insertions, 60 deletions
diff --git a/include/log.h b/include/log.h
index 7aea2ded..47a83321 100644
--- a/include/log.h
+++ b/include/log.h
@@ -13,6 +13,7 @@ typedef enum {
13void init_log(int verbosity); 13void init_log(int verbosity);
14void sway_log_colors(int mode); 14void sway_log_colors(int mode);
15void sway_log(int verbosity, const char* format, ...) __attribute__((format(printf,2,3))); 15void sway_log(int verbosity, const char* format, ...) __attribute__((format(printf,2,3)));
16void sway_log_errno(int verbosity, char* format, ...) __attribute__((format(printf,2,3)));
16void sway_abort(const char* format, ...) __attribute__((format(printf,1,2))); 17void sway_abort(const char* format, ...) __attribute__((format(printf,1,2)));
17bool sway_assert(bool condition, const char* format, ...) __attribute__((format(printf,2,3))); 18bool sway_assert(bool condition, const char* format, ...) __attribute__((format(printf,2,3)));
18 19
diff --git a/sway/ipc.c b/sway/ipc.c
index 1a074788..292d9593 100644
--- a/sway/ipc.c
+++ b/sway/ipc.c
@@ -1,3 +1,5 @@
1// See https://i3wm.org/docs/ipc.html for protocol information
2
1#include <errno.h> 3#include <errno.h>
2#include <string.h> 4#include <string.h>
3#include <sys/socket.h> 5#include <sys/socket.h>
@@ -6,6 +8,8 @@
6#include <wlc/wlc.h> 8#include <wlc/wlc.h>
7#include <unistd.h> 9#include <unistd.h>
8#include <stdlib.h> 10#include <stdlib.h>
11#include <stropts.h>
12#include <sys/ioctl.h>
9#include "ipc.h" 13#include "ipc.h"
10#include "log.h" 14#include "log.h"
11#include "config.h" 15#include "config.h"
@@ -15,10 +19,18 @@ static int ipc_socket = -1;
15 19
16static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; 20static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'};
17 21
18int ipc_handle_connection(int fd, uint32_t mask, void *data); 22struct ipc_client {
19size_t ipc_handle_command(char **reply_data, char *data, ssize_t length); 23 struct wlc_event_source *event_source;
20size_t ipc_format_reply(char **data, enum ipc_command_type command_type, const char *payload, uint32_t payload_length); 24 int fd;
25 uint32_t payload_length;
26 enum ipc_command_type current_command;
27};
21 28
29int ipc_handle_connection(int fd, uint32_t mask, void *data);
30int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data);
31void ipc_client_disconnect(struct ipc_client *client);
32void ipc_client_handle_command(struct ipc_client *client);
33bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length);
22 34
23void init_ipc() { 35void init_ipc() {
24 ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); 36 ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
@@ -46,91 +58,150 @@ void init_ipc() {
46 58
47int ipc_handle_connection(int fd, uint32_t mask, void *data) { 59int ipc_handle_connection(int fd, uint32_t mask, void *data) {
48 sway_log(L_DEBUG, "Event on IPC listening socket"); 60 sway_log(L_DEBUG, "Event on IPC listening socket");
49 int client_socket = accept(ipc_socket, NULL, NULL); 61 assert(mask == WLC_EVENT_READABLE);
50 if (client_socket == -1) {
51 char error[256];
52 strerror_r(errno, error, sizeof(error));
53 sway_log(L_INFO, "Unable to accept IPC client connection: %s", error);
54 return 0;
55 }
56 62
57 char buf[1024]; 63 int client_fd = accept(ipc_socket, NULL, NULL);
58 // Leave one byte of space at the end of the buffer for NULL terminator 64 if (client_fd == -1) {
59 ssize_t received = recv(client_socket, buf, sizeof(buf) - 1, 0); 65 sway_log_errno(L_INFO, "Unable to accept IPC client connection");
60 if (received == -1) {
61 char error[256];
62 strerror_r(errno, error, sizeof(error));
63 sway_log(L_INFO, "Unable to receive from IPC client: %s", error);
64 close(client_socket);
65 return 0; 66 return 0;
66 } 67 }
67 68
68 char *reply_buf; 69 struct ipc_client* client = malloc(sizeof(struct ipc_client));
69 size_t reply_length = ipc_handle_command(&reply_buf, buf, received); 70 client->payload_length = 0;
70 sway_log(L_DEBUG, "IPC reply: %s", reply_buf); 71 client->fd = client_fd;
72 client->event_source = wlc_event_loop_add_fd(client_fd, WLC_EVENT_READABLE, ipc_client_handle_readable, client);
71 73
72 if (send(client_socket, reply_buf, reply_length, 0) == -1) {
73 char error[256];
74 strerror_r(errno, error, sizeof(error));
75 sway_log(L_INFO, "Unable to send to IPC client: %s", error);
76 }
77
78 free(reply_buf);
79 close(client_socket);
80 return 0; 74 return 0;
81} 75}
82 76
83static const int ipc_header_size = sizeof(ipc_magic)+8; 77static const int ipc_header_size = sizeof(ipc_magic)+8;
84 78
85size_t ipc_handle_command(char **reply_data, char *data, ssize_t length) { 79int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
86 // See https://i3wm.org/docs/ipc.html for protocol details 80 struct ipc_client *client = data;
81 sway_log(L_DEBUG, "Event on IPC client socket %d", client_fd);
87 82
88 if (length < ipc_header_size) { 83 if (mask & WLC_EVENT_ERROR) {
89 sway_log(L_DEBUG, "IPC data too short"); 84 sway_log(L_INFO, "IPC Client socket error, removing client");
90 return false; 85 ipc_client_disconnect(client);
86 return 0;
87 }
88
89 if (mask & WLC_EVENT_HANGUP) {
90 ipc_client_disconnect(client);
91 return 0;
91 } 92 }
92 93
93 if (memcmp(data, ipc_magic, sizeof(ipc_magic)) != 0) { 94 int read_available;
95 ioctl(client_fd, FIONREAD, &read_available);
96
97 // Wait for the rest of the command payload in case the header has already been read
98 if (client->payload_length > 0) {
99 if (read_available >= client->payload_length) {
100 ipc_client_handle_command(client);
101 }
102 else {
103 sway_log(L_DEBUG, "Too little data to read payload on IPC Client socket, waiting for more (%d < %d)", read_available, client->payload_length);
104 }
105 return 0;
106 }
107
108 if (read_available < ipc_header_size) {
109 sway_log(L_DEBUG, "Too little data to read header on IPC Client socket, waiting for more (%d < %d)", read_available, ipc_header_size);
110 return 0;
111 }
112
113 char buf[ipc_header_size];
114 ssize_t received = recv(client_fd, buf, ipc_header_size, 0);
115 if (received == -1) {
116 sway_log_errno(L_INFO, "Unable to receive header from IPC client");
117 ipc_client_disconnect(client);
118 return 0;
119 }
120
121 if (memcmp(buf, ipc_magic, sizeof(ipc_magic)) != 0) {
94 sway_log(L_DEBUG, "IPC header check failed"); 122 sway_log(L_DEBUG, "IPC header check failed");
95 return false; 123 ipc_client_disconnect(client);
124 return 0;
96 } 125 }
97 126
98 uint32_t payload_length = *(uint32_t *)&data[sizeof(ipc_magic)]; 127 client->payload_length = *(uint32_t *)&buf[sizeof(ipc_magic)];
99 uint32_t command_type = *(uint32_t *)&data[sizeof(ipc_magic)+4]; 128 client->current_command = (enum ipc_command_type) *(uint32_t *)&buf[sizeof(ipc_magic)+4];
100 129
101 if (length != payload_length + ipc_header_size) { 130 if (read_available - received >= client->payload_length) {
102 // TODO: try to read enough data 131 ipc_client_handle_command(client);
103 sway_log(L_DEBUG, "IPC payload size mismatch"); 132 }
104 return false; 133
134 return 0;
135}
136
137void ipc_client_disconnect(struct ipc_client *client)
138{
139 if (!sway_assert(client != NULL, "client != NULL")) {
140 return;
141 }
142
143 sway_log(L_INFO, "IPC Client %d disconnected", client->fd);
144 wlc_event_source_remove(client->event_source);
145 close(client->fd);
146 free(client);
147}
148
149void ipc_client_handle_command(struct ipc_client *client) {
150 if (!sway_assert(client != NULL, "client != NULL")) {
151 return;
152 }
153
154 char buf[client->payload_length + 1];
155 if (client->payload_length > 0)
156 {
157 ssize_t received = recv(client->fd, buf, client->payload_length, 0);
158 if (received == -1)
159 {
160 sway_log_errno(L_INFO, "Unable to receive payload from IPC client");
161 ipc_client_disconnect(client);
162 return;
163 }
105 } 164 }
106 165
107 switch (command_type) { 166 switch (client->current_command) {
108 case IPC_COMMAND: 167 case IPC_COMMAND:
109 { 168 {
110 char *cmd = &data[ipc_header_size]; 169 buf[client->payload_length] = '\0';
111 data[ipc_header_size + payload_length] = '\0'; 170 bool success = handle_command(config, buf);
112 bool success = handle_command(config, cmd); 171 char reply[64];
113 char buf[64]; 172 int length = snprintf(reply, sizeof(reply), "{\"success\":%s}", success ? "true" : "false");
114 int length = snprintf(buf, sizeof(buf), "{\"success\":%s}", success ? "true" : "false"); 173 ipc_send_reply(client, reply, (uint32_t) length);
115 return ipc_format_reply(reply_data, IPC_COMMAND, buf, (uint32_t) length); 174 break;
116 } 175 }
117 default: 176 default:
118 sway_log(L_INFO, "Unknown IPC command type %i", command_type); 177 sway_log(L_INFO, "Unknown IPC command type %i", client->current_command);
119 return false; 178 ipc_client_disconnect(client);
179 break;
120 } 180 }
181
182 client->payload_length = 0;
121} 183}
122 184
123size_t ipc_format_reply(char **data, enum ipc_command_type command_type, const char *payload, uint32_t payload_length) { 185bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length) {
124 assert(data);
125 assert(payload); 186 assert(payload);
126 187
127 size_t length = ipc_header_size + payload_length; 188 char data[ipc_header_size];
128 *data = malloc(length);
129 189
130 memcpy(*data, ipc_magic, sizeof(ipc_magic)); 190 memcpy(data, ipc_magic, sizeof(ipc_magic));
131 *(uint32_t *)&((*data)[sizeof(ipc_magic)]) = payload_length; 191 *(uint32_t *)&(data[sizeof(ipc_magic)]) = payload_length;
132 *(uint32_t *)&((*data)[sizeof(ipc_magic)+4]) = command_type; 192 *(uint32_t *)&(data[sizeof(ipc_magic)+4]) = client->current_command;
133 memcpy(&(*data)[ipc_header_size], payload, payload_length); 193
194 if (write(client->fd, data, ipc_header_size) == -1) {
195 sway_log_errno(L_INFO, "Unable to send header to IPC client");
196 ipc_client_disconnect(client);
197 return false;
198 }
199
200 if (write(client->fd, payload, payload_length) == -1) {
201 sway_log_errno(L_INFO, "Unable to send payload to IPC client");
202 ipc_client_disconnect(client);
203 return false;
204 }
134 205
135 return length; 206 return true;
136} 207}
diff --git a/sway/log.c b/sway/log.c
index 2d633abb..e8c1b78f 100644
--- a/sway/log.c
+++ b/sway/log.c
@@ -5,6 +5,8 @@
5#include <fcntl.h> 5#include <fcntl.h>
6#include <unistd.h> 6#include <unistd.h>
7#include <signal.h> 7#include <signal.h>
8#include <errno.h>
9#include <string.h>
8 10
9int colored = 1; 11int colored = 1;
10int v = 0; 12int v = 0;
@@ -66,6 +68,34 @@ void sway_log(int verbosity, const char* format, ...) {
66 } 68 }
67} 69}
68 70
71void sway_log_errno(int verbosity, char* format, ...) {
72 if (verbosity <= v) {
73 int c = verbosity;
74 if (c > sizeof(verbosity_colors) / sizeof(char *)) {
75 c = sizeof(verbosity_colors) / sizeof(char *) - 1;
76 }
77
78 if (colored) {
79 fprintf(stderr, verbosity_colors[c]);
80 }
81
82 va_list args;
83 va_start(args, format);
84 vfprintf(stderr, format, args);
85 va_end(args);
86
87 fprintf(stderr, ": ");
88 char error[256];
89 strerror_r(errno, error, sizeof(error));
90 fprintf(stderr, error);
91
92 if (colored) {
93 fprintf(stderr, "\x1B[0m");
94 }
95 fprintf(stderr, "\n");
96 }
97}
98
69bool sway_assert(bool condition, const char* format, ...) { 99bool sway_assert(bool condition, const char* format, ...) {
70 if (condition) { 100 if (condition) {
71 return true; 101 return true;