1 /*
2  * vim:ts=4:sw=4:expandtab
3  *
4  * i3 - an improved dynamic tiling window manager
5  * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
6  *
7  * ipc.c: UNIX domain socket IPC (initialization, client handling, protocol).
8  *
9  */
10 
11 #include "all.h"
12 #include "yajl_utils.h"
13 
14 #include <ev.h>
15 #include <fcntl.h>
16 #include <libgen.h>
17 #include <locale.h>
18 #include <stdint.h>
19 #include <sys/socket.h>
20 #include <sys/un.h>
21 #include <unistd.h>
22 
23 #include <yajl/yajl_gen.h>
24 #include <yajl/yajl_parse.h>
25 
26 char *current_socketpath = NULL;
27 
28 TAILQ_HEAD(ipc_client_head, ipc_client) all_clients = TAILQ_HEAD_INITIALIZER(all_clients);
29 
30 static void ipc_client_timeout(EV_P_ ev_timer *w, int revents);
31 static void ipc_socket_writeable_cb(EV_P_ struct ev_io *w, int revents);
32 
33 static ev_tstamp kill_timeout = 10.0;
34 
ipc_set_kill_timeout(ev_tstamp new)35 void ipc_set_kill_timeout(ev_tstamp new) {
36     kill_timeout = new;
37 }
38 
39 /*
40  * Try to write the contents of the pending buffer to the client's subscription
41  * socket. Will set, reset or clear the timeout and io write callbacks depending
42  * on the result of the write operation.
43  *
44  */
ipc_push_pending(ipc_client * client)45 static void ipc_push_pending(ipc_client *client) {
46     const ssize_t result = writeall_nonblock(client->fd, client->buffer, client->buffer_size);
47     if (result < 0) {
48         return;
49     }
50 
51     if ((size_t)result == client->buffer_size) {
52         /* Everything was written successfully: clear the timer and stop the io
53          * callback. */
54         FREE(client->buffer);
55         client->buffer_size = 0;
56         if (client->timeout) {
57             ev_timer_stop(main_loop, client->timeout);
58             FREE(client->timeout);
59         }
60         ev_io_stop(main_loop, client->write_callback);
61         return;
62     }
63 
64     /* Otherwise, make sure that the io callback is enabled and create a new
65      * timer if needed. */
66     ev_io_start(main_loop, client->write_callback);
67 
68     if (!client->timeout) {
69         struct ev_timer *timeout = scalloc(1, sizeof(struct ev_timer));
70         ev_timer_init(timeout, ipc_client_timeout, kill_timeout, 0.);
71         timeout->data = client;
72         client->timeout = timeout;
73         ev_set_priority(timeout, EV_MINPRI);
74         ev_timer_start(main_loop, client->timeout);
75     } else if (result > 0) {
76         /* Keep the old timeout when nothing is written. Otherwise, we would
77          * keep a dead connection by continuously renewing its timeouts. */
78         ev_timer_stop(main_loop, client->timeout);
79         ev_timer_set(client->timeout, kill_timeout, 0.0);
80         ev_timer_start(main_loop, client->timeout);
81     }
82     if (result == 0) {
83         return;
84     }
85 
86     /* Shift the buffer to the left and reduce the allocated space. */
87     client->buffer_size -= (size_t)result;
88     memmove(client->buffer, client->buffer + result, client->buffer_size);
89     client->buffer = srealloc(client->buffer, client->buffer_size);
90 }
91 
92 /*
93  * Given a message and a message type, create the corresponding header, merge it
94  * with the message and append it to the given client's output buffer. Also,
95  * send the message if the client's buffer was empty.
96  *
97  */
ipc_send_client_message(ipc_client * client,size_t size,const uint32_t message_type,const uint8_t * payload)98 static void ipc_send_client_message(ipc_client *client, size_t size, const uint32_t message_type, const uint8_t *payload) {
99     const i3_ipc_header_t header = {
100         .magic = {'i', '3', '-', 'i', 'p', 'c'},
101         .size = size,
102         .type = message_type};
103     const size_t header_size = sizeof(i3_ipc_header_t);
104     const size_t message_size = header_size + size;
105 
106     const bool push_now = (client->buffer_size == 0);
107     client->buffer = srealloc(client->buffer, client->buffer_size + message_size);
108     memcpy(client->buffer + client->buffer_size, ((void *)&header), header_size);
109     memcpy(client->buffer + client->buffer_size + header_size, payload, size);
110     client->buffer_size += message_size;
111 
112     if (push_now) {
113         ipc_push_pending(client);
114     }
115 }
116 
free_ipc_client(ipc_client * client,int exempt_fd)117 static void free_ipc_client(ipc_client *client, int exempt_fd) {
118     if (client->fd != exempt_fd) {
119         DLOG("Disconnecting client on fd %d\n", client->fd);
120         close(client->fd);
121     }
122 
123     ev_io_stop(main_loop, client->read_callback);
124     FREE(client->read_callback);
125     ev_io_stop(main_loop, client->write_callback);
126     FREE(client->write_callback);
127     if (client->timeout) {
128         ev_timer_stop(main_loop, client->timeout);
129         FREE(client->timeout);
130     }
131 
132     free(client->buffer);
133 
134     for (int i = 0; i < client->num_events; i++) {
135         free(client->events[i]);
136     }
137     free(client->events);
138     TAILQ_REMOVE(&all_clients, client, clients);
139     free(client);
140 }
141 
142 /*
143  * Sends the specified event to all IPC clients which are currently connected
144  * and subscribed to this kind of event.
145  *
146  */
ipc_send_event(const char * event,uint32_t message_type,const char * payload)147 void ipc_send_event(const char *event, uint32_t message_type, const char *payload) {
148     ipc_client *current;
149     TAILQ_FOREACH (current, &all_clients, clients) {
150         for (int i = 0; i < current->num_events; i++) {
151             if (strcasecmp(current->events[i], event) == 0) {
152                 ipc_send_client_message(current, strlen(payload), message_type, (uint8_t *)payload);
153                 break;
154             }
155         }
156     }
157 }
158 
159 /*
160  * For shutdown events, we send the reason for the shutdown.
161  */
ipc_send_shutdown_event(shutdown_reason_t reason)162 static void ipc_send_shutdown_event(shutdown_reason_t reason) {
163     yajl_gen gen = ygenalloc();
164     y(map_open);
165 
166     ystr("change");
167 
168     if (reason == SHUTDOWN_REASON_RESTART) {
169         ystr("restart");
170     } else if (reason == SHUTDOWN_REASON_EXIT) {
171         ystr("exit");
172     }
173 
174     y(map_close);
175 
176     const unsigned char *payload;
177     ylength length;
178 
179     y(get_buf, &payload, &length);
180     ipc_send_event("shutdown", I3_IPC_EVENT_SHUTDOWN, (const char *)payload);
181 
182     y(free);
183 }
184 
185 /*
186  * Calls shutdown() on each socket and closes it. This function is to be called
187  * when exiting or restarting only!
188  *
189  * exempt_fd is never closed. Set to -1 to close all fds.
190  *
191  */
ipc_shutdown(shutdown_reason_t reason,int exempt_fd)192 void ipc_shutdown(shutdown_reason_t reason, int exempt_fd) {
193     ipc_send_shutdown_event(reason);
194 
195     ipc_client *current;
196     while (!TAILQ_EMPTY(&all_clients)) {
197         current = TAILQ_FIRST(&all_clients);
198         if (current->fd != exempt_fd) {
199             shutdown(current->fd, SHUT_RDWR);
200         }
201         free_ipc_client(current, exempt_fd);
202     }
203 }
204 
205 /*
206  * Executes the given command.
207  *
208  */
IPC_HANDLER(run_command)209 IPC_HANDLER(run_command) {
210     /* To get a properly terminated buffer, we copy
211      * message_size bytes out of the buffer */
212     char *command = sstrndup((const char *)message, message_size);
213     LOG("IPC: received: *%.4000s*\n", command);
214     yajl_gen gen = yajl_gen_alloc(NULL);
215 
216     CommandResult *result = parse_command(command, gen, client);
217     free(command);
218 
219     if (result->needs_tree_render)
220         tree_render();
221 
222     command_result_free(result);
223 
224     const unsigned char *reply;
225     ylength length;
226     yajl_gen_get_buf(gen, &reply, &length);
227 
228     ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_COMMAND,
229                             (const uint8_t *)reply);
230 
231     yajl_gen_free(gen);
232 }
233 
dump_rect(yajl_gen gen,const char * name,Rect r)234 static void dump_rect(yajl_gen gen, const char *name, Rect r) {
235     ystr(name);
236     y(map_open);
237     ystr("x");
238     y(integer, (int32_t)r.x);
239     ystr("y");
240     y(integer, (int32_t)r.y);
241     ystr("width");
242     y(integer, r.width);
243     ystr("height");
244     y(integer, r.height);
245     y(map_close);
246 }
247 
dump_event_state_mask(yajl_gen gen,Binding * bind)248 static void dump_event_state_mask(yajl_gen gen, Binding *bind) {
249     y(array_open);
250     for (int i = 0; i < 20; i++) {
251         if (bind->event_state_mask & (1 << i)) {
252             switch (1 << i) {
253                 case XCB_KEY_BUT_MASK_SHIFT:
254                     ystr("shift");
255                     break;
256                 case XCB_KEY_BUT_MASK_LOCK:
257                     ystr("lock");
258                     break;
259                 case XCB_KEY_BUT_MASK_CONTROL:
260                     ystr("ctrl");
261                     break;
262                 case XCB_KEY_BUT_MASK_MOD_1:
263                     ystr("Mod1");
264                     break;
265                 case XCB_KEY_BUT_MASK_MOD_2:
266                     ystr("Mod2");
267                     break;
268                 case XCB_KEY_BUT_MASK_MOD_3:
269                     ystr("Mod3");
270                     break;
271                 case XCB_KEY_BUT_MASK_MOD_4:
272                     ystr("Mod4");
273                     break;
274                 case XCB_KEY_BUT_MASK_MOD_5:
275                     ystr("Mod5");
276                     break;
277                 case XCB_KEY_BUT_MASK_BUTTON_1:
278                     ystr("Button1");
279                     break;
280                 case XCB_KEY_BUT_MASK_BUTTON_2:
281                     ystr("Button2");
282                     break;
283                 case XCB_KEY_BUT_MASK_BUTTON_3:
284                     ystr("Button3");
285                     break;
286                 case XCB_KEY_BUT_MASK_BUTTON_4:
287                     ystr("Button4");
288                     break;
289                 case XCB_KEY_BUT_MASK_BUTTON_5:
290                     ystr("Button5");
291                     break;
292                 case (I3_XKB_GROUP_MASK_1 << 16):
293                     ystr("Group1");
294                     break;
295                 case (I3_XKB_GROUP_MASK_2 << 16):
296                     ystr("Group2");
297                     break;
298                 case (I3_XKB_GROUP_MASK_3 << 16):
299                     ystr("Group3");
300                     break;
301                 case (I3_XKB_GROUP_MASK_4 << 16):
302                     ystr("Group4");
303                     break;
304             }
305         }
306     }
307     y(array_close);
308 }
309 
dump_binding(yajl_gen gen,Binding * bind)310 static void dump_binding(yajl_gen gen, Binding *bind) {
311     y(map_open);
312     ystr("input_code");
313     y(integer, bind->keycode);
314 
315     ystr("input_type");
316     ystr((const char *)(bind->input_type == B_KEYBOARD ? "keyboard" : "mouse"));
317 
318     ystr("symbol");
319     if (bind->symbol == NULL)
320         y(null);
321     else
322         ystr(bind->symbol);
323 
324     ystr("command");
325     ystr(bind->command);
326 
327     // This key is only provided for compatibility, new programs should use
328     // event_state_mask instead.
329     ystr("mods");
330     dump_event_state_mask(gen, bind);
331 
332     ystr("event_state_mask");
333     dump_event_state_mask(gen, bind);
334 
335     y(map_close);
336 }
337 
dump_node(yajl_gen gen,struct Con * con,bool inplace_restart)338 void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
339     y(map_open);
340     ystr("id");
341     y(integer, (uintptr_t)con);
342 
343     ystr("type");
344     switch (con->type) {
345         case CT_ROOT:
346             ystr("root");
347             break;
348         case CT_OUTPUT:
349             ystr("output");
350             break;
351         case CT_CON:
352             ystr("con");
353             break;
354         case CT_FLOATING_CON:
355             ystr("floating_con");
356             break;
357         case CT_WORKSPACE:
358             ystr("workspace");
359             break;
360         case CT_DOCKAREA:
361             ystr("dockarea");
362             break;
363     }
364 
365     /* provided for backwards compatibility only. */
366     ystr("orientation");
367     if (!con_is_split(con))
368         ystr("none");
369     else {
370         if (con_orientation(con) == HORIZ)
371             ystr("horizontal");
372         else
373             ystr("vertical");
374     }
375 
376     ystr("scratchpad_state");
377     switch (con->scratchpad_state) {
378         case SCRATCHPAD_NONE:
379             ystr("none");
380             break;
381         case SCRATCHPAD_FRESH:
382             ystr("fresh");
383             break;
384         case SCRATCHPAD_CHANGED:
385             ystr("changed");
386             break;
387     }
388 
389     ystr("percent");
390     if (con->percent == 0.0)
391         y(null);
392     else
393         y(double, con->percent);
394 
395     ystr("urgent");
396     y(bool, con->urgent);
397 
398     ystr("marks");
399     y(array_open);
400     mark_t *mark;
401     TAILQ_FOREACH (mark, &(con->marks_head), marks) {
402         ystr(mark->name);
403     }
404     y(array_close);
405 
406     ystr("focused");
407     y(bool, (con == focused));
408 
409     if (con->type != CT_ROOT && con->type != CT_OUTPUT) {
410         ystr("output");
411         ystr(con_get_output(con)->name);
412     }
413 
414     ystr("layout");
415     switch (con->layout) {
416         case L_DEFAULT:
417             DLOG("About to dump layout=default, this is a bug in the code.\n");
418             assert(false);
419             break;
420         case L_SPLITV:
421             ystr("splitv");
422             break;
423         case L_SPLITH:
424             ystr("splith");
425             break;
426         case L_STACKED:
427             ystr("stacked");
428             break;
429         case L_TABBED:
430             ystr("tabbed");
431             break;
432         case L_DOCKAREA:
433             ystr("dockarea");
434             break;
435         case L_OUTPUT:
436             ystr("output");
437             break;
438     }
439 
440     ystr("workspace_layout");
441     switch (con->workspace_layout) {
442         case L_DEFAULT:
443             ystr("default");
444             break;
445         case L_STACKED:
446             ystr("stacked");
447             break;
448         case L_TABBED:
449             ystr("tabbed");
450             break;
451         default:
452             DLOG("About to dump workspace_layout=%d (none of default/stacked/tabbed), this is a bug.\n", con->workspace_layout);
453             assert(false);
454             break;
455     }
456 
457     ystr("last_split_layout");
458     switch (con->layout) {
459         case L_SPLITV:
460             ystr("splitv");
461             break;
462         default:
463             ystr("splith");
464             break;
465     }
466 
467     ystr("border");
468     switch (con->border_style) {
469         case BS_NORMAL:
470             ystr("normal");
471             break;
472         case BS_NONE:
473             ystr("none");
474             break;
475         case BS_PIXEL:
476             ystr("pixel");
477             break;
478     }
479 
480     ystr("current_border_width");
481     y(integer, con->current_border_width);
482 
483     dump_rect(gen, "rect", con->rect);
484     dump_rect(gen, "deco_rect", con->deco_rect);
485     dump_rect(gen, "window_rect", con->window_rect);
486     dump_rect(gen, "geometry", con->geometry);
487 
488     ystr("name");
489     if (con->window && con->window->name)
490         ystr(i3string_as_utf8(con->window->name));
491     else if (con->name != NULL)
492         ystr(con->name);
493     else
494         y(null);
495 
496     if (con->title_format != NULL) {
497         ystr("title_format");
498         ystr(con->title_format);
499     }
500 
501     ystr("window_icon_padding");
502     y(integer, con->window_icon_padding);
503 
504     if (con->type == CT_WORKSPACE) {
505         ystr("num");
506         y(integer, con->num);
507     }
508 
509     ystr("window");
510     if (con->window)
511         y(integer, con->window->id);
512     else
513         y(null);
514 
515     ystr("window_type");
516     if (con->window) {
517         if (con->window->window_type == A__NET_WM_WINDOW_TYPE_NORMAL) {
518             ystr("normal");
519         } else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_DOCK) {
520             ystr("dock");
521         } else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_DIALOG) {
522             ystr("dialog");
523         } else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_UTILITY) {
524             ystr("utility");
525         } else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_TOOLBAR) {
526             ystr("toolbar");
527         } else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_SPLASH) {
528             ystr("splash");
529         } else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_MENU) {
530             ystr("menu");
531         } else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_DROPDOWN_MENU) {
532             ystr("dropdown_menu");
533         } else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_POPUP_MENU) {
534             ystr("popup_menu");
535         } else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_TOOLTIP) {
536             ystr("tooltip");
537         } else if (con->window->window_type == A__NET_WM_WINDOW_TYPE_NOTIFICATION) {
538             ystr("notification");
539         } else {
540             ystr("unknown");
541         }
542     } else
543         y(null);
544 
545     if (con->window && !inplace_restart) {
546         /* Window properties are useless to preserve when restarting because
547          * they will be queried again anyway. However, for i3-save-tree(1),
548          * they are very useful and save i3-save-tree dealing with X11. */
549         ystr("window_properties");
550         y(map_open);
551 
552 #define DUMP_PROPERTY(key, prop_name)         \
553     do {                                      \
554         if (con->window->prop_name != NULL) { \
555             ystr(key);                        \
556             ystr(con->window->prop_name);     \
557         }                                     \
558     } while (0)
559 
560         DUMP_PROPERTY("class", class_class);
561         DUMP_PROPERTY("instance", class_instance);
562         DUMP_PROPERTY("window_role", role);
563         DUMP_PROPERTY("machine", machine);
564 
565         if (con->window->name != NULL) {
566             ystr("title");
567             ystr(i3string_as_utf8(con->window->name));
568         }
569 
570         ystr("transient_for");
571         if (con->window->transient_for == XCB_NONE)
572             y(null);
573         else
574             y(integer, con->window->transient_for);
575 
576         y(map_close);
577     }
578 
579     ystr("nodes");
580     y(array_open);
581     Con *node;
582     if (con->type != CT_DOCKAREA || !inplace_restart) {
583         TAILQ_FOREACH (node, &(con->nodes_head), nodes) {
584             dump_node(gen, node, inplace_restart);
585         }
586     }
587     y(array_close);
588 
589     ystr("floating_nodes");
590     y(array_open);
591     TAILQ_FOREACH (node, &(con->floating_head), floating_windows) {
592         dump_node(gen, node, inplace_restart);
593     }
594     y(array_close);
595 
596     ystr("focus");
597     y(array_open);
598     TAILQ_FOREACH (node, &(con->focus_head), focused) {
599         y(integer, (uintptr_t)node);
600     }
601     y(array_close);
602 
603     ystr("fullscreen_mode");
604     y(integer, con->fullscreen_mode);
605 
606     ystr("sticky");
607     y(bool, con->sticky);
608 
609     ystr("floating");
610     switch (con->floating) {
611         case FLOATING_AUTO_OFF:
612             ystr("auto_off");
613             break;
614         case FLOATING_AUTO_ON:
615             ystr("auto_on");
616             break;
617         case FLOATING_USER_OFF:
618             ystr("user_off");
619             break;
620         case FLOATING_USER_ON:
621             ystr("user_on");
622             break;
623     }
624 
625     ystr("swallows");
626     y(array_open);
627     Match *match;
628     TAILQ_FOREACH (match, &(con->swallow_head), matches) {
629         /* We will generate a new restart_mode match specification after this
630          * loop, so skip this one. */
631         if (match->restart_mode)
632             continue;
633         y(map_open);
634         if (match->dock != M_DONTCHECK) {
635             ystr("dock");
636             y(integer, match->dock);
637             ystr("insert_where");
638             y(integer, match->insert_where);
639         }
640 
641 #define DUMP_REGEX(re_name)                \
642     do {                                   \
643         if (match->re_name != NULL) {      \
644             ystr(#re_name);                \
645             ystr(match->re_name->pattern); \
646         }                                  \
647     } while (0)
648 
649         DUMP_REGEX(class);
650         DUMP_REGEX(instance);
651         DUMP_REGEX(window_role);
652         DUMP_REGEX(title);
653         DUMP_REGEX(machine);
654 
655 #undef DUMP_REGEX
656         y(map_close);
657     }
658 
659     if (inplace_restart) {
660         if (con->window != NULL) {
661             y(map_open);
662             ystr("id");
663             y(integer, con->window->id);
664             ystr("restart_mode");
665             y(bool, true);
666             y(map_close);
667         }
668     }
669     y(array_close);
670 
671     if (inplace_restart && con->window != NULL) {
672         ystr("depth");
673         y(integer, con->depth);
674     }
675 
676     if (inplace_restart && con->type == CT_ROOT && previous_workspace_name) {
677         ystr("previous_workspace_name");
678         ystr(previous_workspace_name);
679     }
680 
681     y(map_close);
682 }
683 
dump_bar_bindings(yajl_gen gen,Barconfig * config)684 static void dump_bar_bindings(yajl_gen gen, Barconfig *config) {
685     if (TAILQ_EMPTY(&(config->bar_bindings)))
686         return;
687 
688     ystr("bindings");
689     y(array_open);
690 
691     struct Barbinding *current;
692     TAILQ_FOREACH (current, &(config->bar_bindings), bindings) {
693         y(map_open);
694 
695         ystr("input_code");
696         y(integer, current->input_code);
697         ystr("command");
698         ystr(current->command);
699         ystr("release");
700         y(bool, current->release == B_UPON_KEYRELEASE);
701 
702         y(map_close);
703     }
704 
705     y(array_close);
706 }
707 
canonicalize_output_name(char * name)708 static char *canonicalize_output_name(char *name) {
709     /* Do not canonicalize special output names. */
710     if (strcasecmp(name, "primary") == 0) {
711         return name;
712     }
713     Output *output = get_output_by_name(name, false);
714     return output ? output_primary_name(output) : name;
715 }
716 
dump_bar_config(yajl_gen gen,Barconfig * config)717 static void dump_bar_config(yajl_gen gen, Barconfig *config) {
718     y(map_open);
719 
720     ystr("id");
721     ystr(config->id);
722 
723     if (config->num_outputs > 0) {
724         ystr("outputs");
725         y(array_open);
726         for (int c = 0; c < config->num_outputs; c++) {
727             /* Convert monitor names (RandR ≥ 1.5) or output names
728              * (RandR < 1.5) into monitor names. This way, existing
729              * configs which use output names transparently keep
730              * working. */
731             ystr(canonicalize_output_name(config->outputs[c]));
732         }
733         y(array_close);
734     }
735 
736     if (!TAILQ_EMPTY(&(config->tray_outputs))) {
737         ystr("tray_outputs");
738         y(array_open);
739 
740         struct tray_output_t *tray_output;
741         TAILQ_FOREACH (tray_output, &(config->tray_outputs), tray_outputs) {
742             ystr(canonicalize_output_name(tray_output->output));
743         }
744 
745         y(array_close);
746     }
747 
748 #define YSTR_IF_SET(name)       \
749     do {                        \
750         if (config->name) {     \
751             ystr(#name);        \
752             ystr(config->name); \
753         }                       \
754     } while (0)
755 
756     ystr("tray_padding");
757     y(integer, config->tray_padding);
758 
759     YSTR_IF_SET(socket_path);
760 
761     ystr("mode");
762     switch (config->mode) {
763         case M_HIDE:
764             ystr("hide");
765             break;
766         case M_INVISIBLE:
767             ystr("invisible");
768             break;
769         case M_DOCK:
770         default:
771             ystr("dock");
772             break;
773     }
774 
775     ystr("hidden_state");
776     switch (config->hidden_state) {
777         case S_SHOW:
778             ystr("show");
779             break;
780         case S_HIDE:
781         default:
782             ystr("hide");
783             break;
784     }
785 
786     ystr("modifier");
787     y(integer, config->modifier);
788 
789     dump_bar_bindings(gen, config);
790 
791     ystr("position");
792     if (config->position == P_BOTTOM)
793         ystr("bottom");
794     else
795         ystr("top");
796 
797     YSTR_IF_SET(status_command);
798     YSTR_IF_SET(font);
799 
800     if (config->separator_symbol) {
801         ystr("separator_symbol");
802         ystr(config->separator_symbol);
803     }
804 
805     ystr("workspace_buttons");
806     y(bool, !config->hide_workspace_buttons);
807 
808     ystr("workspace_min_width");
809     y(integer, config->workspace_min_width);
810 
811     ystr("strip_workspace_numbers");
812     y(bool, config->strip_workspace_numbers);
813 
814     ystr("strip_workspace_name");
815     y(bool, config->strip_workspace_name);
816 
817     ystr("binding_mode_indicator");
818     y(bool, !config->hide_binding_mode_indicator);
819 
820     ystr("verbose");
821     y(bool, config->verbose);
822 
823 #undef YSTR_IF_SET
824 #define YSTR_IF_SET(name)              \
825     do {                               \
826         if (config->colors.name) {     \
827             ystr(#name);               \
828             ystr(config->colors.name); \
829         }                              \
830     } while (0)
831 
832     ystr("colors");
833     y(map_open);
834     YSTR_IF_SET(background);
835     YSTR_IF_SET(statusline);
836     YSTR_IF_SET(separator);
837     YSTR_IF_SET(focused_background);
838     YSTR_IF_SET(focused_statusline);
839     YSTR_IF_SET(focused_separator);
840     YSTR_IF_SET(focused_workspace_border);
841     YSTR_IF_SET(focused_workspace_bg);
842     YSTR_IF_SET(focused_workspace_text);
843     YSTR_IF_SET(active_workspace_border);
844     YSTR_IF_SET(active_workspace_bg);
845     YSTR_IF_SET(active_workspace_text);
846     YSTR_IF_SET(inactive_workspace_border);
847     YSTR_IF_SET(inactive_workspace_bg);
848     YSTR_IF_SET(inactive_workspace_text);
849     YSTR_IF_SET(urgent_workspace_border);
850     YSTR_IF_SET(urgent_workspace_bg);
851     YSTR_IF_SET(urgent_workspace_text);
852     YSTR_IF_SET(binding_mode_border);
853     YSTR_IF_SET(binding_mode_bg);
854     YSTR_IF_SET(binding_mode_text);
855     y(map_close);
856 
857     y(map_close);
858 #undef YSTR_IF_SET
859 }
860 
IPC_HANDLER(tree)861 IPC_HANDLER(tree) {
862     setlocale(LC_NUMERIC, "C");
863     yajl_gen gen = ygenalloc();
864     dump_node(gen, croot, false);
865     setlocale(LC_NUMERIC, "");
866 
867     const unsigned char *payload;
868     ylength length;
869     y(get_buf, &payload, &length);
870 
871     ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_TREE, payload);
872     y(free);
873 }
874 
875 /*
876  * Formats the reply message for a GET_WORKSPACES request and sends it to the
877  * client
878  *
879  */
IPC_HANDLER(get_workspaces)880 IPC_HANDLER(get_workspaces) {
881     yajl_gen gen = ygenalloc();
882     y(array_open);
883 
884     Con *focused_ws = con_get_workspace(focused);
885 
886     Con *output;
887     TAILQ_FOREACH (output, &(croot->nodes_head), nodes) {
888         if (con_is_internal(output))
889             continue;
890         Con *ws;
891         TAILQ_FOREACH (ws, &(output_get_content(output)->nodes_head), nodes) {
892             assert(ws->type == CT_WORKSPACE);
893             y(map_open);
894 
895             ystr("id");
896             y(integer, (uintptr_t)ws);
897 
898             ystr("num");
899             y(integer, ws->num);
900 
901             ystr("name");
902             ystr(ws->name);
903 
904             ystr("visible");
905             y(bool, workspace_is_visible(ws));
906 
907             ystr("focused");
908             y(bool, ws == focused_ws);
909 
910             ystr("rect");
911             y(map_open);
912             ystr("x");
913             y(integer, ws->rect.x);
914             ystr("y");
915             y(integer, ws->rect.y);
916             ystr("width");
917             y(integer, ws->rect.width);
918             ystr("height");
919             y(integer, ws->rect.height);
920             y(map_close);
921 
922             ystr("output");
923             ystr(output->name);
924 
925             ystr("urgent");
926             y(bool, ws->urgent);
927 
928             y(map_close);
929         }
930     }
931 
932     y(array_close);
933 
934     const unsigned char *payload;
935     ylength length;
936     y(get_buf, &payload, &length);
937 
938     ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_WORKSPACES, payload);
939     y(free);
940 }
941 
942 /*
943  * Formats the reply message for a GET_OUTPUTS request and sends it to the
944  * client
945  *
946  */
IPC_HANDLER(get_outputs)947 IPC_HANDLER(get_outputs) {
948     yajl_gen gen = ygenalloc();
949     y(array_open);
950 
951     Output *output;
952     TAILQ_FOREACH (output, &outputs, outputs) {
953         y(map_open);
954 
955         ystr("name");
956         ystr(output_primary_name(output));
957 
958         ystr("active");
959         y(bool, output->active);
960 
961         ystr("primary");
962         y(bool, output->primary);
963 
964         ystr("rect");
965         y(map_open);
966         ystr("x");
967         y(integer, output->rect.x);
968         ystr("y");
969         y(integer, output->rect.y);
970         ystr("width");
971         y(integer, output->rect.width);
972         ystr("height");
973         y(integer, output->rect.height);
974         y(map_close);
975 
976         ystr("current_workspace");
977         Con *ws = NULL;
978         if (output->con && (ws = con_get_fullscreen_con(output->con, CF_OUTPUT)))
979             ystr(ws->name);
980         else
981             y(null);
982 
983         y(map_close);
984     }
985 
986     y(array_close);
987 
988     const unsigned char *payload;
989     ylength length;
990     y(get_buf, &payload, &length);
991 
992     ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_OUTPUTS, payload);
993     y(free);
994 }
995 
996 /*
997  * Formats the reply message for a GET_MARKS request and sends it to the
998  * client
999  *
1000  */
IPC_HANDLER(get_marks)1001 IPC_HANDLER(get_marks) {
1002     yajl_gen gen = ygenalloc();
1003     y(array_open);
1004 
1005     Con *con;
1006     TAILQ_FOREACH (con, &all_cons, all_cons) {
1007         mark_t *mark;
1008         TAILQ_FOREACH (mark, &(con->marks_head), marks) {
1009             ystr(mark->name);
1010         }
1011     }
1012 
1013     y(array_close);
1014 
1015     const unsigned char *payload;
1016     ylength length;
1017     y(get_buf, &payload, &length);
1018 
1019     ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_MARKS, payload);
1020     y(free);
1021 }
1022 
1023 /*
1024  * Returns the version of i3
1025  *
1026  */
IPC_HANDLER(get_version)1027 IPC_HANDLER(get_version) {
1028     yajl_gen gen = ygenalloc();
1029     y(map_open);
1030 
1031     ystr("major");
1032     y(integer, MAJOR_VERSION);
1033 
1034     ystr("minor");
1035     y(integer, MINOR_VERSION);
1036 
1037     ystr("patch");
1038     y(integer, PATCH_VERSION);
1039 
1040     ystr("human_readable");
1041     ystr(i3_version);
1042 
1043     ystr("loaded_config_file_name");
1044     ystr(current_configpath);
1045 
1046     ystr("included_config_file_names");
1047     y(array_open);
1048     IncludedFile *file;
1049     TAILQ_FOREACH (file, &included_files, files) {
1050         if (file == TAILQ_FIRST(&included_files)) {
1051             /* Skip the first file, which is current_configpath. */
1052             continue;
1053         }
1054         ystr(file->path);
1055     }
1056     y(array_close);
1057     y(map_close);
1058 
1059     const unsigned char *payload;
1060     ylength length;
1061     y(get_buf, &payload, &length);
1062 
1063     ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_VERSION, payload);
1064     y(free);
1065 }
1066 
1067 /*
1068  * Formats the reply message for a GET_BAR_CONFIG request and sends it to the
1069  * client.
1070  *
1071  */
IPC_HANDLER(get_bar_config)1072 IPC_HANDLER(get_bar_config) {
1073     yajl_gen gen = ygenalloc();
1074 
1075     /* If no ID was passed, we return a JSON array with all IDs */
1076     if (message_size == 0) {
1077         y(array_open);
1078         Barconfig *current;
1079         TAILQ_FOREACH (current, &barconfigs, configs) {
1080             ystr(current->id);
1081         }
1082         y(array_close);
1083 
1084         const unsigned char *payload;
1085         ylength length;
1086         y(get_buf, &payload, &length);
1087 
1088         ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_BAR_CONFIG, payload);
1089         y(free);
1090         return;
1091     }
1092 
1093     /* To get a properly terminated buffer, we copy
1094      * message_size bytes out of the buffer */
1095     char *bar_id = NULL;
1096     sasprintf(&bar_id, "%.*s", message_size, message);
1097     LOG("IPC: looking for config for bar ID \"%s\"\n", bar_id);
1098     Barconfig *current, *config = NULL;
1099     TAILQ_FOREACH (current, &barconfigs, configs) {
1100         if (strcmp(current->id, bar_id) != 0)
1101             continue;
1102 
1103         config = current;
1104         break;
1105     }
1106     free(bar_id);
1107 
1108     if (!config) {
1109         /* If we did not find a config for the given ID, the reply will contain
1110          * a null 'id' field. */
1111         y(map_open);
1112 
1113         ystr("id");
1114         y(null);
1115 
1116         y(map_close);
1117     } else {
1118         dump_bar_config(gen, config);
1119     }
1120 
1121     const unsigned char *payload;
1122     ylength length;
1123     y(get_buf, &payload, &length);
1124 
1125     ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_BAR_CONFIG, payload);
1126     y(free);
1127 }
1128 
1129 /*
1130  * Returns a list of configured binding modes
1131  *
1132  */
IPC_HANDLER(get_binding_modes)1133 IPC_HANDLER(get_binding_modes) {
1134     yajl_gen gen = ygenalloc();
1135 
1136     y(array_open);
1137     struct Mode *mode;
1138     SLIST_FOREACH (mode, &modes, modes) {
1139         ystr(mode->name);
1140     }
1141     y(array_close);
1142 
1143     const unsigned char *payload;
1144     ylength length;
1145     y(get_buf, &payload, &length);
1146 
1147     ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_BINDING_MODES, payload);
1148     y(free);
1149 }
1150 
1151 /*
1152  * Callback for the YAJL parser (will be called when a string is parsed).
1153  *
1154  */
add_subscription(void * extra,const unsigned char * s,ylength len)1155 static int add_subscription(void *extra, const unsigned char *s,
1156                             ylength len) {
1157     ipc_client *client = extra;
1158 
1159     DLOG("should add subscription to extra %p, sub %.*s\n", client, (int)len, s);
1160     int event = client->num_events;
1161 
1162     client->num_events++;
1163     client->events = srealloc(client->events, client->num_events * sizeof(char *));
1164     /* We copy the string because it is not null-terminated and strndup()
1165      * is missing on some BSD systems */
1166     client->events[event] = scalloc(len + 1, 1);
1167     memcpy(client->events[event], s, len);
1168 
1169     DLOG("client is now subscribed to:\n");
1170     for (int i = 0; i < client->num_events; i++) {
1171         DLOG("event %s\n", client->events[i]);
1172     }
1173     DLOG("(done)\n");
1174 
1175     return 1;
1176 }
1177 
1178 /*
1179  * Subscribes this connection to the event types which were given as a JSON
1180  * serialized array in the payload field of the message.
1181  *
1182  */
IPC_HANDLER(subscribe)1183 IPC_HANDLER(subscribe) {
1184     yajl_handle p;
1185     yajl_status stat;
1186 
1187     /* Setup the JSON parser */
1188     static yajl_callbacks callbacks = {
1189         .yajl_string = add_subscription,
1190     };
1191 
1192     p = yalloc(&callbacks, (void *)client);
1193     stat = yajl_parse(p, (const unsigned char *)message, message_size);
1194     if (stat != yajl_status_ok) {
1195         unsigned char *err;
1196         err = yajl_get_error(p, true, (const unsigned char *)message,
1197                              message_size);
1198         ELOG("YAJL parse error: %s\n", err);
1199         yajl_free_error(p, err);
1200 
1201         const char *reply = "{\"success\":false}";
1202         ipc_send_client_message(client, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (const uint8_t *)reply);
1203         yajl_free(p);
1204         return;
1205     }
1206     yajl_free(p);
1207     const char *reply = "{\"success\":true}";
1208     ipc_send_client_message(client, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (const uint8_t *)reply);
1209 
1210     if (client->first_tick_sent) {
1211         return;
1212     }
1213 
1214     bool is_tick = false;
1215     for (int i = 0; i < client->num_events; i++) {
1216         if (strcmp(client->events[i], "tick") == 0) {
1217             is_tick = true;
1218             break;
1219         }
1220     }
1221     if (!is_tick) {
1222         return;
1223     }
1224 
1225     client->first_tick_sent = true;
1226     const char *payload = "{\"first\":true,\"payload\":\"\"}";
1227     ipc_send_client_message(client, strlen(payload), I3_IPC_EVENT_TICK, (const uint8_t *)payload);
1228 }
1229 
1230 /*
1231  * Returns the raw last loaded i3 configuration file contents.
1232  */
IPC_HANDLER(get_config)1233 IPC_HANDLER(get_config) {
1234     yajl_gen gen = ygenalloc();
1235 
1236     y(map_open);
1237 
1238     ystr("config");
1239     IncludedFile *file = TAILQ_FIRST(&included_files);
1240     ystr(file->raw_contents);
1241 
1242     ystr("included_configs");
1243     y(array_open);
1244     TAILQ_FOREACH (file, &included_files, files) {
1245         y(map_open);
1246         ystr("path");
1247         ystr(file->path);
1248         ystr("raw_contents");
1249         ystr(file->raw_contents);
1250         ystr("variable_replaced_contents");
1251         ystr(file->variable_replaced_contents);
1252         y(map_close);
1253     }
1254     y(array_close);
1255 
1256     y(map_close);
1257 
1258     const unsigned char *payload;
1259     ylength length;
1260     y(get_buf, &payload, &length);
1261 
1262     ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_CONFIG, payload);
1263     y(free);
1264 }
1265 
1266 /*
1267  * Sends the tick event from the message payload to subscribers. Establishes a
1268  * synchronization point in event-related tests.
1269  */
IPC_HANDLER(send_tick)1270 IPC_HANDLER(send_tick) {
1271     yajl_gen gen = ygenalloc();
1272 
1273     y(map_open);
1274 
1275     ystr("first");
1276     y(bool, false);
1277 
1278     ystr("payload");
1279     yajl_gen_string(gen, (unsigned char *)message, message_size);
1280 
1281     y(map_close);
1282 
1283     const unsigned char *payload;
1284     ylength length;
1285     y(get_buf, &payload, &length);
1286 
1287     ipc_send_event("tick", I3_IPC_EVENT_TICK, (const char *)payload);
1288     y(free);
1289 
1290     const char *reply = "{\"success\":true}";
1291     ipc_send_client_message(client, strlen(reply), I3_IPC_REPLY_TYPE_TICK, (const uint8_t *)reply);
1292     DLOG("Sent tick event\n");
1293 }
1294 
1295 struct sync_state {
1296     char *last_key;
1297     uint32_t rnd;
1298     xcb_window_t window;
1299 };
1300 
_sync_json_key(void * extra,const unsigned char * val,size_t len)1301 static int _sync_json_key(void *extra, const unsigned char *val, size_t len) {
1302     struct sync_state *state = extra;
1303     FREE(state->last_key);
1304     state->last_key = scalloc(len + 1, 1);
1305     memcpy(state->last_key, val, len);
1306     return 1;
1307 }
1308 
_sync_json_int(void * extra,long long val)1309 static int _sync_json_int(void *extra, long long val) {
1310     struct sync_state *state = extra;
1311     if (strcasecmp(state->last_key, "rnd") == 0) {
1312         state->rnd = val;
1313     } else if (strcasecmp(state->last_key, "window") == 0) {
1314         state->window = (xcb_window_t)val;
1315     }
1316     return 1;
1317 }
1318 
IPC_HANDLER(sync)1319 IPC_HANDLER(sync) {
1320     yajl_handle p;
1321     yajl_status stat;
1322 
1323     /* Setup the JSON parser */
1324     static yajl_callbacks callbacks = {
1325         .yajl_map_key = _sync_json_key,
1326         .yajl_integer = _sync_json_int,
1327     };
1328 
1329     struct sync_state state;
1330     memset(&state, '\0', sizeof(struct sync_state));
1331     p = yalloc(&callbacks, (void *)&state);
1332     stat = yajl_parse(p, (const unsigned char *)message, message_size);
1333     FREE(state.last_key);
1334     if (stat != yajl_status_ok) {
1335         unsigned char *err;
1336         err = yajl_get_error(p, true, (const unsigned char *)message,
1337                              message_size);
1338         ELOG("YAJL parse error: %s\n", err);
1339         yajl_free_error(p, err);
1340 
1341         const char *reply = "{\"success\":false}";
1342         ipc_send_client_message(client, strlen(reply), I3_IPC_REPLY_TYPE_SYNC, (const uint8_t *)reply);
1343         yajl_free(p);
1344         return;
1345     }
1346     yajl_free(p);
1347 
1348     DLOG("received IPC sync request (rnd = %d, window = 0x%08x)\n", state.rnd, state.window);
1349     sync_respond(state.window, state.rnd);
1350     const char *reply = "{\"success\":true}";
1351     ipc_send_client_message(client, strlen(reply), I3_IPC_REPLY_TYPE_SYNC, (const uint8_t *)reply);
1352 }
1353 
IPC_HANDLER(get_binding_state)1354 IPC_HANDLER(get_binding_state) {
1355     yajl_gen gen = ygenalloc();
1356 
1357     y(map_open);
1358 
1359     ystr("name");
1360     ystr(current_binding_mode);
1361 
1362     y(map_close);
1363 
1364     const unsigned char *payload;
1365     ylength length;
1366     y(get_buf, &payload, &length);
1367 
1368     ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_GET_BINDING_STATE, payload);
1369     y(free);
1370 }
1371 
1372 /* The index of each callback function corresponds to the numeric
1373  * value of the message type (see include/i3/ipc.h) */
1374 handler_t handlers[13] = {
1375     handle_run_command,
1376     handle_get_workspaces,
1377     handle_subscribe,
1378     handle_get_outputs,
1379     handle_tree,
1380     handle_get_marks,
1381     handle_get_bar_config,
1382     handle_get_version,
1383     handle_get_binding_modes,
1384     handle_get_config,
1385     handle_send_tick,
1386     handle_sync,
1387     handle_get_binding_state,
1388 };
1389 
1390 /*
1391  * Handler for activity on a client connection, receives a message from a
1392  * client.
1393  *
1394  * For now, the maximum message size is 2048. I’m not sure for what the
1395  * IPC interface will be used in the future, thus I’m not implementing a
1396  * mechanism for arbitrarily long messages, as it seems like overkill
1397  * at the moment.
1398  *
1399  */
ipc_receive_message(EV_P_ struct ev_io * w,int revents)1400 static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) {
1401     uint32_t message_type;
1402     uint32_t message_length;
1403     uint8_t *message = NULL;
1404     ipc_client *client = (ipc_client *)w->data;
1405     assert(client->fd == w->fd);
1406 
1407     int ret = ipc_recv_message(w->fd, &message_type, &message_length, &message);
1408     /* EOF or other error */
1409     if (ret < 0) {
1410         /* Was this a spurious read? See ev(3) */
1411         if (ret == -1 && errno == EAGAIN) {
1412             FREE(message);
1413             return;
1414         }
1415 
1416         /* If not, there was some kind of error. We don’t bother and close the
1417          * connection. Delete the client from the list of clients. */
1418         free_ipc_client(client, -1);
1419         FREE(message);
1420         return;
1421     }
1422 
1423     if (message_type >= (sizeof(handlers) / sizeof(handler_t)))
1424         DLOG("Unhandled message type: %d\n", message_type);
1425     else {
1426         handler_t h = handlers[message_type];
1427         h(client, message, 0, message_length, message_type);
1428     }
1429 
1430     FREE(message);
1431 }
1432 
ipc_client_timeout(EV_P_ ev_timer * w,int revents)1433 static void ipc_client_timeout(EV_P_ ev_timer *w, int revents) {
1434     /* No need to be polite and check for writeability, the other callback would
1435      * have been called by now. */
1436     ipc_client *client = (ipc_client *)w->data;
1437 
1438     char *cmdline = NULL;
1439 #if defined(__linux__) && defined(SO_PEERCRED)
1440     struct ucred peercred;
1441     socklen_t so_len = sizeof(peercred);
1442     if (getsockopt(client->fd, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0) {
1443         goto end;
1444     }
1445     char *exepath;
1446     sasprintf(&exepath, "/proc/%d/cmdline", peercred.pid);
1447 
1448     int fd = open(exepath, O_RDONLY);
1449     free(exepath);
1450     if (fd == -1) {
1451         goto end;
1452     }
1453     char buf[512] = {'\0'}; /* cut off cmdline for the error message. */
1454     const ssize_t n = read(fd, buf, sizeof(buf));
1455     close(fd);
1456     if (n < 0) {
1457         goto end;
1458     }
1459     for (char *walk = buf; walk < buf + n - 1; walk++) {
1460         if (*walk == '\0') {
1461             *walk = ' ';
1462         }
1463     }
1464     cmdline = buf;
1465 
1466     if (cmdline) {
1467         ELOG("client %p with pid %d and cmdline '%s' on fd %d timed out, killing\n", client, peercred.pid, cmdline, client->fd);
1468     }
1469 
1470 end:
1471 #endif
1472     if (!cmdline) {
1473         ELOG("client %p on fd %d timed out, killing\n", client, client->fd);
1474     }
1475 
1476     free_ipc_client(client, -1);
1477 }
1478 
ipc_socket_writeable_cb(EV_P_ ev_io * w,int revents)1479 static void ipc_socket_writeable_cb(EV_P_ ev_io *w, int revents) {
1480     DLOG("fd %d writeable\n", w->fd);
1481     ipc_client *client = (ipc_client *)w->data;
1482 
1483     /* If this callback is called then there should be a corresponding active
1484      * timer. */
1485     assert(client->timeout != NULL);
1486     ipc_push_pending(client);
1487 }
1488 
1489 /*
1490  * Handler for activity on the listening socket, meaning that a new client
1491  * has just connected and we should accept() him. Sets up the event handler
1492  * for activity on the new connection and inserts the file descriptor into
1493  * the list of clients.
1494  *
1495  */
ipc_new_client(EV_P_ struct ev_io * w,int revents)1496 void ipc_new_client(EV_P_ struct ev_io *w, int revents) {
1497     struct sockaddr_un peer;
1498     socklen_t len = sizeof(struct sockaddr_un);
1499     int fd;
1500     if ((fd = accept(w->fd, (struct sockaddr *)&peer, &len)) < 0) {
1501         if (errno != EINTR) {
1502             perror("accept()");
1503         }
1504         return;
1505     }
1506 
1507     /* Close this file descriptor on exec() */
1508     (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
1509 
1510     ipc_new_client_on_fd(EV_A_ fd);
1511 }
1512 
1513 /*
1514  * ipc_new_client_on_fd() only sets up the event handler
1515  * for activity on the new connection and inserts the file descriptor into
1516  * the list of clients.
1517  *
1518  * This variant is useful for the inherited IPC connection when restarting.
1519  *
1520  */
ipc_new_client_on_fd(EV_P_ int fd)1521 ipc_client *ipc_new_client_on_fd(EV_P_ int fd) {
1522     set_nonblock(fd);
1523 
1524     ipc_client *client = scalloc(1, sizeof(ipc_client));
1525     client->fd = fd;
1526 
1527     client->read_callback = scalloc(1, sizeof(struct ev_io));
1528     client->read_callback->data = client;
1529     ev_io_init(client->read_callback, ipc_receive_message, fd, EV_READ);
1530     ev_io_start(EV_A_ client->read_callback);
1531 
1532     client->write_callback = scalloc(1, sizeof(struct ev_io));
1533     client->write_callback->data = client;
1534     ev_io_init(client->write_callback, ipc_socket_writeable_cb, fd, EV_WRITE);
1535 
1536     DLOG("IPC: new client connected on fd %d\n", fd);
1537     TAILQ_INSERT_TAIL(&all_clients, client, clients);
1538     return client;
1539 }
1540 
1541 /*
1542  * Generates a json workspace event. Returns a dynamically allocated yajl
1543  * generator. Free with yajl_gen_free().
1544  */
ipc_marshal_workspace_event(const char * change,Con * current,Con * old)1545 yajl_gen ipc_marshal_workspace_event(const char *change, Con *current, Con *old) {
1546     setlocale(LC_NUMERIC, "C");
1547     yajl_gen gen = ygenalloc();
1548 
1549     y(map_open);
1550 
1551     ystr("change");
1552     ystr(change);
1553 
1554     ystr("current");
1555     if (current == NULL)
1556         y(null);
1557     else
1558         dump_node(gen, current, false);
1559 
1560     ystr("old");
1561     if (old == NULL)
1562         y(null);
1563     else
1564         dump_node(gen, old, false);
1565 
1566     y(map_close);
1567 
1568     setlocale(LC_NUMERIC, "");
1569 
1570     return gen;
1571 }
1572 
1573 /*
1574  * For the workspace events we send, along with the usual "change" field, also
1575  * the workspace container in "current". For focus events, we send the
1576  * previously focused workspace in "old".
1577  */
ipc_send_workspace_event(const char * change,Con * current,Con * old)1578 void ipc_send_workspace_event(const char *change, Con *current, Con *old) {
1579     yajl_gen gen = ipc_marshal_workspace_event(change, current, old);
1580 
1581     const unsigned char *payload;
1582     ylength length;
1583     y(get_buf, &payload, &length);
1584 
1585     ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload);
1586 
1587     y(free);
1588 }
1589 
1590 /*
1591  * For the window events we send, along the usual "change" field,
1592  * also the window container, in "container".
1593  */
ipc_send_window_event(const char * property,Con * con)1594 void ipc_send_window_event(const char *property, Con *con) {
1595     DLOG("Issue IPC window %s event (con = %p, window = 0x%08x)\n",
1596          property, con, (con->window ? con->window->id : XCB_WINDOW_NONE));
1597 
1598     setlocale(LC_NUMERIC, "C");
1599     yajl_gen gen = ygenalloc();
1600 
1601     y(map_open);
1602 
1603     ystr("change");
1604     ystr(property);
1605 
1606     ystr("container");
1607     dump_node(gen, con, false);
1608 
1609     y(map_close);
1610 
1611     const unsigned char *payload;
1612     ylength length;
1613     y(get_buf, &payload, &length);
1614 
1615     ipc_send_event("window", I3_IPC_EVENT_WINDOW, (const char *)payload);
1616     y(free);
1617     setlocale(LC_NUMERIC, "");
1618 }
1619 
1620 /*
1621  * For the barconfig update events, we send the serialized barconfig.
1622  */
ipc_send_barconfig_update_event(Barconfig * barconfig)1623 void ipc_send_barconfig_update_event(Barconfig *barconfig) {
1624     DLOG("Issue barconfig_update event for id = %s\n", barconfig->id);
1625     setlocale(LC_NUMERIC, "C");
1626     yajl_gen gen = ygenalloc();
1627 
1628     dump_bar_config(gen, barconfig);
1629 
1630     const unsigned char *payload;
1631     ylength length;
1632     y(get_buf, &payload, &length);
1633 
1634     ipc_send_event("barconfig_update", I3_IPC_EVENT_BARCONFIG_UPDATE, (const char *)payload);
1635     y(free);
1636     setlocale(LC_NUMERIC, "");
1637 }
1638 
1639 /*
1640  * For the binding events, we send the serialized binding struct.
1641  */
ipc_send_binding_event(const char * event_type,Binding * bind)1642 void ipc_send_binding_event(const char *event_type, Binding *bind) {
1643     DLOG("Issue IPC binding %s event (sym = %s, code = %d)\n", event_type, bind->symbol, bind->keycode);
1644 
1645     setlocale(LC_NUMERIC, "C");
1646 
1647     yajl_gen gen = ygenalloc();
1648 
1649     y(map_open);
1650 
1651     ystr("change");
1652     ystr(event_type);
1653 
1654     ystr("binding");
1655     dump_binding(gen, bind);
1656 
1657     y(map_close);
1658 
1659     const unsigned char *payload;
1660     ylength length;
1661     y(get_buf, &payload, &length);
1662 
1663     ipc_send_event("binding", I3_IPC_EVENT_BINDING, (const char *)payload);
1664 
1665     y(free);
1666     setlocale(LC_NUMERIC, "");
1667 }
1668 
1669 /*
1670  * Sends a restart reply to the IPC client on the specified fd.
1671  */
ipc_confirm_restart(ipc_client * client)1672 void ipc_confirm_restart(ipc_client *client) {
1673     DLOG("ipc_confirm_restart(fd %d)\n", client->fd);
1674     static const char *reply = "[{\"success\":true}]";
1675     ipc_send_client_message(
1676         client, strlen(reply), I3_IPC_REPLY_TYPE_COMMAND,
1677         (const uint8_t *)reply);
1678     ipc_push_pending(client);
1679 }
1680