diff options
Diffstat (limited to 'sway/ipc-server.c')
-rw-r--r-- | sway/ipc-server.c | 108 |
1 files changed, 98 insertions, 10 deletions
diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 5e1e93ce..4ce2b7eb 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c | |||
@@ -41,11 +41,15 @@ static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; | |||
41 | 41 | ||
42 | struct ipc_client { | 42 | struct ipc_client { |
43 | struct wlc_event_source *event_source; | 43 | struct wlc_event_source *event_source; |
44 | struct wlc_event_source *writable_event_source; | ||
44 | int fd; | 45 | int fd; |
45 | uint32_t payload_length; | 46 | uint32_t payload_length; |
46 | uint32_t security_policy; | 47 | uint32_t security_policy; |
47 | enum ipc_command_type current_command; | 48 | enum ipc_command_type current_command; |
48 | enum ipc_command_type subscribed_events; | 49 | enum ipc_command_type subscribed_events; |
50 | size_t write_buffer_len; | ||
51 | size_t write_buffer_size; | ||
52 | char *write_buffer; | ||
49 | }; | 53 | }; |
50 | 54 | ||
51 | static list_t *ipc_get_pixel_requests = NULL; | 55 | static list_t *ipc_get_pixel_requests = NULL; |
@@ -72,6 +76,7 @@ struct get_clipboard_request { | |||
72 | struct sockaddr_un *ipc_user_sockaddr(void); | 76 | struct sockaddr_un *ipc_user_sockaddr(void); |
73 | int ipc_handle_connection(int fd, uint32_t mask, void *data); | 77 | int ipc_handle_connection(int fd, uint32_t mask, void *data); |
74 | int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); | 78 | int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); |
79 | int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data); | ||
75 | void ipc_client_disconnect(struct ipc_client *client); | 80 | void ipc_client_disconnect(struct ipc_client *client); |
76 | void ipc_client_handle_command(struct ipc_client *client); | 81 | void ipc_client_handle_command(struct ipc_client *client); |
77 | bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length); | 82 | bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length); |
@@ -182,6 +187,12 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) { | |||
182 | close(client_fd); | 187 | close(client_fd); |
183 | return 0; | 188 | return 0; |
184 | } | 189 | } |
190 | if ((flags = fcntl(client_fd, F_GETFL)) == -1 | ||
191 | || fcntl(client_fd, F_SETFL, flags|O_NONBLOCK) == -1) { | ||
192 | sway_log_errno(L_ERROR, "Unable to set NONBLOCK on IPC client socket"); | ||
193 | close(client_fd); | ||
194 | return 0; | ||
195 | } | ||
185 | 196 | ||
186 | struct ipc_client* client = malloc(sizeof(struct ipc_client)); | 197 | struct ipc_client* client = malloc(sizeof(struct ipc_client)); |
187 | if (!client) { | 198 | if (!client) { |
@@ -193,10 +204,22 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) { | |||
193 | client->fd = client_fd; | 204 | client->fd = client_fd; |
194 | client->subscribed_events = 0; | 205 | client->subscribed_events = 0; |
195 | client->event_source = wlc_event_loop_add_fd(client_fd, WLC_EVENT_READABLE, ipc_client_handle_readable, client); | 206 | client->event_source = wlc_event_loop_add_fd(client_fd, WLC_EVENT_READABLE, ipc_client_handle_readable, client); |
207 | client->writable_event_source = NULL; | ||
208 | |||
209 | client->write_buffer_size = 128; | ||
210 | client->write_buffer_len = 0; | ||
211 | client->write_buffer = malloc(client->write_buffer_size); | ||
212 | if (!client->write_buffer) { | ||
213 | sway_log(L_ERROR, "Unable to allocate ipc client write buffer"); | ||
214 | close(client_fd); | ||
215 | return 0; | ||
216 | } | ||
196 | 217 | ||
197 | pid_t pid = get_client_pid(client->fd); | 218 | pid_t pid = get_client_pid(client->fd); |
198 | client->security_policy = get_ipc_policy_mask(pid); | 219 | client->security_policy = get_ipc_policy_mask(pid); |
199 | 220 | ||
221 | sway_log(L_DEBUG, "New client: fd %d, pid %d", client_fd, pid); | ||
222 | |||
200 | list_add(ipc_client_list, client); | 223 | list_add(ipc_client_list, client); |
201 | 224 | ||
202 | return 0; | 225 | return 0; |
@@ -219,6 +242,8 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { | |||
219 | return 0; | 242 | return 0; |
220 | } | 243 | } |
221 | 244 | ||
245 | sway_log(L_DEBUG, "Client %d readable", client->fd); | ||
246 | |||
222 | int read_available; | 247 | int read_available; |
223 | if (ioctl(client_fd, FIONREAD, &read_available) == -1) { | 248 | if (ioctl(client_fd, FIONREAD, &read_available) == -1) { |
224 | sway_log_errno(L_INFO, "Unable to read IPC socket buffer size"); | 249 | sway_log_errno(L_INFO, "Unable to read IPC socket buffer size"); |
@@ -240,6 +265,7 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { | |||
240 | 265 | ||
241 | uint8_t buf[ipc_header_size]; | 266 | uint8_t buf[ipc_header_size]; |
242 | uint32_t *buf32 = (uint32_t*)(buf + sizeof(ipc_magic)); | 267 | uint32_t *buf32 = (uint32_t*)(buf + sizeof(ipc_magic)); |
268 | // Should be fully available, because read_available >= ipc_header_size | ||
243 | ssize_t received = recv(client_fd, buf, ipc_header_size, 0); | 269 | ssize_t received = recv(client_fd, buf, ipc_header_size, 0); |
244 | if (received == -1) { | 270 | if (received == -1) { |
245 | sway_log_errno(L_INFO, "Unable to receive header from IPC client"); | 271 | sway_log_errno(L_INFO, "Unable to receive header from IPC client"); |
@@ -263,6 +289,48 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { | |||
263 | return 0; | 289 | return 0; |
264 | } | 290 | } |
265 | 291 | ||
292 | int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { | ||
293 | struct ipc_client *client = data; | ||
294 | |||
295 | if (mask & WLC_EVENT_ERROR) { | ||
296 | sway_log(L_ERROR, "IPC Client socket error, removing client"); | ||
297 | ipc_client_disconnect(client); | ||
298 | return 0; | ||
299 | } | ||
300 | |||
301 | if (mask & WLC_EVENT_HANGUP) { | ||
302 | sway_log(L_DEBUG, "Client %d hung up", client->fd); | ||
303 | ipc_client_disconnect(client); | ||
304 | return 0; | ||
305 | } | ||
306 | |||
307 | if (client->write_buffer_len <= 0) { | ||
308 | return 0; | ||
309 | } | ||
310 | |||
311 | sway_log(L_DEBUG, "Client %d writable", client->fd); | ||
312 | |||
313 | ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len); | ||
314 | |||
315 | if (written == -1 && errno == EAGAIN) { | ||
316 | return 0; | ||
317 | } else if (written == -1) { | ||
318 | sway_log_errno(L_INFO, "Unable to send data from queue to IPC client"); | ||
319 | ipc_client_disconnect(client); | ||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | memmove(client->write_buffer, client->write_buffer + written, client->write_buffer_len - written); | ||
324 | client->write_buffer_len -= written; | ||
325 | |||
326 | if (client->write_buffer_len == 0 && client->writable_event_source) { | ||
327 | wlc_event_source_remove(client->writable_event_source); | ||
328 | client->writable_event_source = NULL; | ||
329 | } | ||
330 | |||
331 | return 0; | ||
332 | } | ||
333 | |||
266 | void ipc_client_disconnect(struct ipc_client *client) { | 334 | void ipc_client_disconnect(struct ipc_client *client) { |
267 | if (!sway_assert(client != NULL, "client != NULL")) { | 335 | if (!sway_assert(client != NULL, "client != NULL")) { |
268 | return; | 336 | return; |
@@ -274,9 +342,13 @@ void ipc_client_disconnect(struct ipc_client *client) { | |||
274 | 342 | ||
275 | sway_log(L_INFO, "IPC Client %d disconnected", client->fd); | 343 | sway_log(L_INFO, "IPC Client %d disconnected", client->fd); |
276 | wlc_event_source_remove(client->event_source); | 344 | wlc_event_source_remove(client->event_source); |
345 | if (client->writable_event_source) { | ||
346 | wlc_event_source_remove(client->writable_event_source); | ||
347 | } | ||
277 | int i = 0; | 348 | int i = 0; |
278 | while (i < ipc_client_list->length && ipc_client_list->items[i] != client) i++; | 349 | while (i < ipc_client_list->length && ipc_client_list->items[i] != client) i++; |
279 | list_del(ipc_client_list, i); | 350 | list_del(ipc_client_list, i); |
351 | free(client->write_buffer); | ||
280 | close(client->fd); | 352 | close(client->fd); |
281 | free(client); | 353 | free(client); |
282 | } | 354 | } |
@@ -608,6 +680,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
608 | return; | 680 | return; |
609 | } | 681 | } |
610 | if (client->payload_length > 0) { | 682 | if (client->payload_length > 0) { |
683 | // Payload should be fully available | ||
611 | ssize_t received = recv(client->fd, buf, client->payload_length, 0); | 684 | ssize_t received = recv(client->fd, buf, client->payload_length, 0); |
612 | if (received == -1) | 685 | if (received == -1) |
613 | { | 686 | { |
@@ -874,17 +947,36 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay | |||
874 | data32[0] = payload_length; | 947 | data32[0] = payload_length; |
875 | data32[1] = client->current_command; | 948 | data32[1] = client->current_command; |
876 | 949 | ||
877 | if (write(client->fd, data, ipc_header_size) == -1) { | 950 | while (client->write_buffer_len + ipc_header_size + payload_length >= |
878 | sway_log_errno(L_INFO, "Unable to send header to IPC client"); | 951 | client->write_buffer_size) { |
952 | client->write_buffer_size *= 2; | ||
953 | } | ||
954 | |||
955 | // TODO: reduce the limit back to 4 MB when screenshooter is implemented | ||
956 | if (client->write_buffer_size > (1 << 28)) { // 256 MB | ||
957 | sway_log(L_ERROR, "Client write buffer too big, disconnecting client"); | ||
958 | ipc_client_disconnect(client); | ||
879 | return false; | 959 | return false; |
880 | } | 960 | } |
881 | 961 | ||
882 | if (write(client->fd, payload, payload_length) == -1) { | 962 | char *new_buffer = realloc(client->write_buffer, client->write_buffer_size); |
883 | sway_log_errno(L_INFO, "Unable to send payload to IPC client"); | 963 | if (!new_buffer) { |
964 | sway_log(L_ERROR, "Unable to reallocate ipc client write buffer"); | ||
965 | ipc_client_disconnect(client); | ||
884 | return false; | 966 | return false; |
885 | } | 967 | } |
968 | client->write_buffer = new_buffer; | ||
969 | |||
970 | memcpy(client->write_buffer + client->write_buffer_len, data, ipc_header_size); | ||
971 | client->write_buffer_len += ipc_header_size; | ||
972 | memcpy(client->write_buffer + client->write_buffer_len, payload, payload_length); | ||
973 | client->write_buffer_len += payload_length; | ||
974 | |||
975 | if (!client->writable_event_source) { | ||
976 | client->writable_event_source = wlc_event_loop_add_fd(client->fd, WLC_EVENT_WRITABLE, ipc_client_handle_writable, client); | ||
977 | } | ||
886 | 978 | ||
887 | sway_log(L_DEBUG, "Send IPC reply: %s", payload); | 979 | sway_log(L_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload); |
888 | 980 | ||
889 | return true; | 981 | return true; |
890 | } | 982 | } |
@@ -984,11 +1076,7 @@ void ipc_event_window(swayc_t *window, const char *change) { | |||
984 | sway_log(L_DEBUG, "Sending window::%s event", change); | 1076 | sway_log(L_DEBUG, "Sending window::%s event", change); |
985 | json_object *obj = json_object_new_object(); | 1077 | json_object *obj = json_object_new_object(); |
986 | json_object_object_add(obj, "change", json_object_new_string(change)); | 1078 | json_object_object_add(obj, "change", json_object_new_string(change)); |
987 | if (strcmp(change, "close") == 0 || !window) { | 1079 | json_object_object_add(obj, "container", ipc_json_describe_container_recursive(window)); |
988 | json_object_object_add(obj, "container", NULL); | ||
989 | } else { | ||
990 | json_object_object_add(obj, "container", ipc_json_describe_container(window)); | ||
991 | } | ||
992 | 1080 | ||
993 | const char *json_string = json_object_to_json_string(obj); | 1081 | const char *json_string = json_object_to_json_string(obj); |
994 | ipc_send_event(json_string, IPC_EVENT_WINDOW); | 1082 | ipc_send_event(json_string, IPC_EVENT_WINDOW); |