i3
ipc.c
Go to the documentation of this file.
1 #undef I3__FILE__
2 #define I3__FILE__ "ipc.c"
3 /*
4  * vim:ts=4:sw=4:expandtab
5  *
6  * i3 - an improved dynamic tiling window manager
7  * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
8  *
9  * ipc.c: UNIX domain socket IPC (initialization, client handling, protocol).
10  *
11  */
12 #include "all.h"
13 #include "yajl_utils.h"
14 
15 #include <sys/socket.h>
16 #include <sys/un.h>
17 #include <fcntl.h>
18 #include <libgen.h>
19 #include <ev.h>
20 #include <yajl/yajl_gen.h>
21 #include <yajl/yajl_parse.h>
22 
23 char *current_socketpath = NULL;
24 
25 TAILQ_HEAD(ipc_client_head, ipc_client) all_clients = TAILQ_HEAD_INITIALIZER(all_clients);
26 
27 /*
28  * Puts the given socket file descriptor into non-blocking mode or dies if
29  * setting O_NONBLOCK failed. Non-blocking sockets are a good idea for our
30  * IPC model because we should by no means block the window manager.
31  *
32  */
33 static void set_nonblock(int sockfd) {
34  int flags = fcntl(sockfd, F_GETFL, 0);
35  flags |= O_NONBLOCK;
36  if (fcntl(sockfd, F_SETFL, flags) < 0)
37  err(-1, "Could not set O_NONBLOCK");
38 }
39 
40 /*
41  * Emulates mkdir -p (creates any missing folders)
42  *
43  */
44 static bool mkdirp(const char *path) {
45  if (mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0)
46  return true;
47  if (errno != ENOENT) {
48  ELOG("mkdir(%s) failed: %s\n", path, strerror(errno));
49  return false;
50  }
51  char *copy = sstrdup(path);
52  /* strip trailing slashes, if any */
53  while (copy[strlen(copy) - 1] == '/')
54  copy[strlen(copy) - 1] = '\0';
55 
56  char *sep = strrchr(copy, '/');
57  if (sep == NULL) {
58  FREE(copy);
59  return false;
60  }
61  *sep = '\0';
62  bool result = false;
63  if (mkdirp(copy))
64  result = mkdirp(path);
65  free(copy);
66 
67  return result;
68 }
69 
70 /*
71  * Sends the specified event to all IPC clients which are currently connected
72  * and subscribed to this kind of event.
73  *
74  */
75 void ipc_send_event(const char *event, uint32_t message_type, const char *payload) {
76  ipc_client *current;
77  TAILQ_FOREACH (current, &all_clients, clients) {
78  /* see if this client is interested in this event */
79  bool interested = false;
80  for (int i = 0; i < current->num_events; i++) {
81  if (strcasecmp(current->events[i], event) != 0)
82  continue;
83  interested = true;
84  break;
85  }
86  if (!interested)
87  continue;
88 
89  ipc_send_message(current->fd, strlen(payload), message_type, (const uint8_t *)payload);
90  }
91 }
92 
93 /*
94  * Calls shutdown() on each socket and closes it. This function to be called
95  * when exiting or restarting only!
96  *
97  */
98 void ipc_shutdown(void) {
99  ipc_client *current;
100  while (!TAILQ_EMPTY(&all_clients)) {
101  current = TAILQ_FIRST(&all_clients);
102  shutdown(current->fd, SHUT_RDWR);
103  close(current->fd);
104  TAILQ_REMOVE(&all_clients, current, clients);
105  free(current);
106  }
107 }
108 
109 /*
110  * Executes the command and returns whether it could be successfully parsed
111  * or not (at the moment, always returns true).
112  *
113  */
114 IPC_HANDLER(command) {
115  /* To get a properly terminated buffer, we copy
116  * message_size bytes out of the buffer */
117  char *command = scalloc(message_size + 1);
118  strncpy(command, (const char *)message, message_size);
119  LOG("IPC: received: *%s*\n", command);
120  yajl_gen gen = yajl_gen_alloc(NULL);
121 
122  CommandResult *result = parse_command((const char *)command, gen);
123  free(command);
124 
125  if (result->needs_tree_render)
126  tree_render();
127 
128  command_result_free(result);
129 
130  const unsigned char *reply;
131  ylength length;
132  yajl_gen_get_buf(gen, &reply, &length);
133 
134  ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_COMMAND,
135  (const uint8_t *)reply);
136 
137  yajl_gen_free(gen);
138 }
139 
140 static void dump_rect(yajl_gen gen, const char *name, Rect r) {
141  ystr(name);
142  y(map_open);
143  ystr("x");
144  y(integer, r.x);
145  ystr("y");
146  y(integer, r.y);
147  ystr("width");
148  y(integer, r.width);
149  ystr("height");
150  y(integer, r.height);
151  y(map_close);
152 }
153 
154 void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
155  y(map_open);
156  ystr("id");
157  y(integer, (long int)con);
158 
159  ystr("type");
160  switch (con->type) {
161  case CT_ROOT:
162  ystr("root");
163  break;
164  case CT_OUTPUT:
165  ystr("output");
166  break;
167  case CT_CON:
168  ystr("con");
169  break;
170  case CT_FLOATING_CON:
171  ystr("floating_con");
172  break;
173  case CT_WORKSPACE:
174  ystr("workspace");
175  break;
176  case CT_DOCKAREA:
177  ystr("dockarea");
178  break;
179  default:
180  DLOG("About to dump unknown container type=%d. This is a bug.\n", con->type);
181  assert(false);
182  break;
183  }
184 
185  /* provided for backwards compatibility only. */
186  ystr("orientation");
187  if (!con_is_split(con))
188  ystr("none");
189  else {
190  if (con_orientation(con) == HORIZ)
191  ystr("horizontal");
192  else
193  ystr("vertical");
194  }
195 
196  ystr("scratchpad_state");
197  switch (con->scratchpad_state) {
198  case SCRATCHPAD_NONE:
199  ystr("none");
200  break;
201  case SCRATCHPAD_FRESH:
202  ystr("fresh");
203  break;
204  case SCRATCHPAD_CHANGED:
205  ystr("changed");
206  break;
207  }
208 
209  ystr("percent");
210  if (con->percent == 0.0)
211  y(null);
212  else
213  y(double, con->percent);
214 
215  ystr("urgent");
216  y(bool, con->urgent);
217 
218  if (con->mark != NULL) {
219  ystr("mark");
220  ystr(con->mark);
221  }
222 
223  ystr("focused");
224  y(bool, (con == focused));
225 
226  ystr("layout");
227  switch (con->layout) {
228  case L_DEFAULT:
229  DLOG("About to dump layout=default, this is a bug in the code.\n");
230  assert(false);
231  break;
232  case L_SPLITV:
233  ystr("splitv");
234  break;
235  case L_SPLITH:
236  ystr("splith");
237  break;
238  case L_STACKED:
239  ystr("stacked");
240  break;
241  case L_TABBED:
242  ystr("tabbed");
243  break;
244  case L_DOCKAREA:
245  ystr("dockarea");
246  break;
247  case L_OUTPUT:
248  ystr("output");
249  break;
250  }
251 
252  ystr("workspace_layout");
253  switch (con->workspace_layout) {
254  case L_DEFAULT:
255  ystr("default");
256  break;
257  case L_STACKED:
258  ystr("stacked");
259  break;
260  case L_TABBED:
261  ystr("tabbed");
262  break;
263  default:
264  DLOG("About to dump workspace_layout=%d (none of default/stacked/tabbed), this is a bug.\n", con->workspace_layout);
265  assert(false);
266  break;
267  }
268 
269  ystr("last_split_layout");
270  switch (con->layout) {
271  case L_SPLITV:
272  ystr("splitv");
273  break;
274  default:
275  ystr("splith");
276  break;
277  }
278 
279  ystr("border");
280  switch (con->border_style) {
281  case BS_NORMAL:
282  ystr("normal");
283  break;
284  case BS_NONE:
285  ystr("none");
286  break;
287  case BS_PIXEL:
288  ystr("pixel");
289  break;
290  }
291 
292  ystr("current_border_width");
293  y(integer, con->current_border_width);
294 
295  dump_rect(gen, "rect", con->rect);
296  dump_rect(gen, "window_rect", con->window_rect);
297  dump_rect(gen, "geometry", con->geometry);
298 
299  ystr("name");
300  if (con->window && con->window->name)
301  ystr(i3string_as_utf8(con->window->name));
302  else
303  ystr(con->name);
304 
305  if (con->type == CT_WORKSPACE) {
306  ystr("num");
307  y(integer, con->num);
308  }
309 
310  ystr("window");
311  if (con->window)
312  y(integer, con->window->id);
313  else
314  y(null);
315 
316  if (con->window && !inplace_restart) {
317  /* Window properties are useless to preserve when restarting because
318  * they will be queried again anyway. However, for i3-save-tree(1),
319  * they are very useful and save i3-save-tree dealing with X11. */
320  ystr("window_properties");
321  y(map_open);
322 
323 #define DUMP_PROPERTY(key, prop_name) \
324  do { \
325  if (con->window->prop_name != NULL) { \
326  ystr(key); \
327  ystr(con->window->prop_name); \
328  } \
329  } while (0)
330 
331  DUMP_PROPERTY("class", class_class);
332  DUMP_PROPERTY("instance", class_instance);
333  DUMP_PROPERTY("window_role", role);
334 
335  if (con->window->name != NULL) {
336  ystr("title");
337  ystr(i3string_as_utf8(con->window->name));
338  }
339 
340  y(map_close);
341  }
342 
343  ystr("nodes");
344  y(array_open);
345  Con *node;
346  if (con->type != CT_DOCKAREA || !inplace_restart) {
347  TAILQ_FOREACH (node, &(con->nodes_head), nodes) {
348  dump_node(gen, node, inplace_restart);
349  }
350  }
351  y(array_close);
352 
353  ystr("floating_nodes");
354  y(array_open);
355  TAILQ_FOREACH (node, &(con->floating_head), floating_windows) {
356  dump_node(gen, node, inplace_restart);
357  }
358  y(array_close);
359 
360  ystr("focus");
361  y(array_open);
362  TAILQ_FOREACH (node, &(con->focus_head), focused) {
363  y(integer, (long int)node);
364  }
365  y(array_close);
366 
367  ystr("fullscreen_mode");
368  y(integer, con->fullscreen_mode);
369 
370  ystr("floating");
371  switch (con->floating) {
372  case FLOATING_AUTO_OFF:
373  ystr("auto_off");
374  break;
375  case FLOATING_AUTO_ON:
376  ystr("auto_on");
377  break;
378  case FLOATING_USER_OFF:
379  ystr("user_off");
380  break;
381  case FLOATING_USER_ON:
382  ystr("user_on");
383  break;
384  }
385 
386  ystr("swallows");
387  y(array_open);
388  Match *match;
389  TAILQ_FOREACH (match, &(con->swallow_head), matches) {
390  y(map_open);
391  if (match->dock != -1) {
392  ystr("dock");
393  y(integer, match->dock);
394  ystr("insert_where");
395  y(integer, match->insert_where);
396  }
397 
398 #define DUMP_REGEX(re_name) \
399  do { \
400  if (match->re_name != NULL) { \
401  ystr(#re_name); \
402  ystr(match->re_name->pattern); \
403  } \
404  } while (0)
405 
406  DUMP_REGEX(class);
407  DUMP_REGEX(instance);
408  DUMP_REGEX(window_role);
409  DUMP_REGEX(title);
410 
411 #undef DUMP_REGEX
412  y(map_close);
413  }
414 
415  if (inplace_restart) {
416  if (con->window != NULL) {
417  y(map_open);
418  ystr("id");
419  y(integer, con->window->id);
420  ystr("restart_mode");
421  y(bool, true);
422  y(map_close);
423  }
424  }
425  y(array_close);
426 
427  if (inplace_restart && con->window != NULL) {
428  ystr("depth");
429  y(integer, con->depth);
430  }
431 
432  y(map_close);
433 }
434 
435 static void dump_bar_config(yajl_gen gen, Barconfig *config) {
436  y(map_open);
437 
438  ystr("id");
439  ystr(config->id);
440 
441  if (config->num_outputs > 0) {
442  ystr("outputs");
443  y(array_open);
444  for (int c = 0; c < config->num_outputs; c++)
445  ystr(config->outputs[c]);
446  y(array_close);
447  }
448 
449 #define YSTR_IF_SET(name) \
450  do { \
451  if (config->name) { \
452  ystr(#name); \
453  ystr(config->name); \
454  } \
455  } while (0)
456 
457  YSTR_IF_SET(tray_output);
458  YSTR_IF_SET(socket_path);
459 
460  ystr("mode");
461  switch (config->mode) {
462  case M_HIDE:
463  ystr("hide");
464  break;
465  case M_INVISIBLE:
466  ystr("invisible");
467  break;
468  case M_DOCK:
469  default:
470  ystr("dock");
471  break;
472  }
473 
474  ystr("hidden_state");
475  switch (config->hidden_state) {
476  case S_SHOW:
477  ystr("show");
478  break;
479  case S_HIDE:
480  default:
481  ystr("hide");
482  break;
483  }
484 
485  ystr("modifier");
486  switch (config->modifier) {
487  case M_CONTROL:
488  ystr("ctrl");
489  break;
490  case M_SHIFT:
491  ystr("shift");
492  break;
493  case M_MOD1:
494  ystr("Mod1");
495  break;
496  case M_MOD2:
497  ystr("Mod2");
498  break;
499  case M_MOD3:
500  ystr("Mod3");
501  break;
502  /*
503  case M_MOD4:
504  ystr("Mod4");
505  break;
506  */
507  case M_MOD5:
508  ystr("Mod5");
509  break;
510  default:
511  ystr("Mod4");
512  break;
513  }
514 
515  ystr("position");
516  if (config->position == P_BOTTOM)
517  ystr("bottom");
518  else
519  ystr("top");
520 
521  YSTR_IF_SET(status_command);
522  YSTR_IF_SET(font);
523 
524  ystr("workspace_buttons");
525  y(bool, !config->hide_workspace_buttons);
526 
527  ystr("strip_workspace_numbers");
528  y(bool, config->strip_workspace_numbers);
529 
530  ystr("binding_mode_indicator");
531  y(bool, !config->hide_binding_mode_indicator);
532 
533  ystr("verbose");
534  y(bool, config->verbose);
535 
536 #undef YSTR_IF_SET
537 #define YSTR_IF_SET(name) \
538  do { \
539  if (config->colors.name) { \
540  ystr(#name); \
541  ystr(config->colors.name); \
542  } \
543  } while (0)
544 
545  ystr("colors");
546  y(map_open);
547  YSTR_IF_SET(background);
548  YSTR_IF_SET(statusline);
549  YSTR_IF_SET(separator);
550  YSTR_IF_SET(focused_workspace_border);
551  YSTR_IF_SET(focused_workspace_bg);
552  YSTR_IF_SET(focused_workspace_text);
553  YSTR_IF_SET(active_workspace_border);
554  YSTR_IF_SET(active_workspace_bg);
555  YSTR_IF_SET(active_workspace_text);
556  YSTR_IF_SET(inactive_workspace_border);
557  YSTR_IF_SET(inactive_workspace_bg);
558  YSTR_IF_SET(inactive_workspace_text);
559  YSTR_IF_SET(urgent_workspace_border);
560  YSTR_IF_SET(urgent_workspace_bg);
561  YSTR_IF_SET(urgent_workspace_text);
562  y(map_close);
563 
564  y(map_close);
565 #undef YSTR_IF_SET
566 }
567 
568 IPC_HANDLER(tree) {
569  setlocale(LC_NUMERIC, "C");
570  yajl_gen gen = ygenalloc();
571  dump_node(gen, croot, false);
572  setlocale(LC_NUMERIC, "");
573 
574  const unsigned char *payload;
575  ylength length;
576  y(get_buf, &payload, &length);
577 
578  ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_TREE, payload);
579  y(free);
580 }
581 
582 /*
583  * Formats the reply message for a GET_WORKSPACES request and sends it to the
584  * client
585  *
586  */
587 IPC_HANDLER(get_workspaces) {
588  yajl_gen gen = ygenalloc();
589  y(array_open);
590 
591  Con *focused_ws = con_get_workspace(focused);
592 
593  Con *output;
594  TAILQ_FOREACH (output, &(croot->nodes_head), nodes) {
595  if (con_is_internal(output))
596  continue;
597  Con *ws;
598  TAILQ_FOREACH (ws, &(output_get_content(output)->nodes_head), nodes) {
599  assert(ws->type == CT_WORKSPACE);
600  y(map_open);
601 
602  ystr("num");
603  if (ws->num == -1)
604  y(null);
605  else
606  y(integer, ws->num);
607 
608  ystr("name");
609  ystr(ws->name);
610 
611  ystr("visible");
612  y(bool, workspace_is_visible(ws));
613 
614  ystr("focused");
615  y(bool, ws == focused_ws);
616 
617  ystr("rect");
618  y(map_open);
619  ystr("x");
620  y(integer, ws->rect.x);
621  ystr("y");
622  y(integer, ws->rect.y);
623  ystr("width");
624  y(integer, ws->rect.width);
625  ystr("height");
626  y(integer, ws->rect.height);
627  y(map_close);
628 
629  ystr("output");
630  ystr(output->name);
631 
632  ystr("urgent");
633  y(bool, ws->urgent);
634 
635  y(map_close);
636  }
637  }
638 
639  y(array_close);
640 
641  const unsigned char *payload;
642  ylength length;
643  y(get_buf, &payload, &length);
644 
645  ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_WORKSPACES, payload);
646  y(free);
647 }
648 
649 /*
650  * Formats the reply message for a GET_OUTPUTS request and sends it to the
651  * client
652  *
653  */
654 IPC_HANDLER(get_outputs) {
655  yajl_gen gen = ygenalloc();
656  y(array_open);
657 
658  Output *output;
659  TAILQ_FOREACH (output, &outputs, outputs) {
660  y(map_open);
661 
662  ystr("name");
663  ystr(output->name);
664 
665  ystr("active");
666  y(bool, output->active);
667 
668  ystr("primary");
669  y(bool, output->primary);
670 
671  ystr("rect");
672  y(map_open);
673  ystr("x");
674  y(integer, output->rect.x);
675  ystr("y");
676  y(integer, output->rect.y);
677  ystr("width");
678  y(integer, output->rect.width);
679  ystr("height");
680  y(integer, output->rect.height);
681  y(map_close);
682 
683  ystr("current_workspace");
684  Con *ws = NULL;
685  if (output->con && (ws = con_get_fullscreen_con(output->con, CF_OUTPUT)))
686  ystr(ws->name);
687  else
688  y(null);
689 
690  y(map_close);
691  }
692 
693  y(array_close);
694 
695  const unsigned char *payload;
696  ylength length;
697  y(get_buf, &payload, &length);
698 
699  ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_OUTPUTS, payload);
700  y(free);
701 }
702 
703 /*
704  * Formats the reply message for a GET_MARKS request and sends it to the
705  * client
706  *
707  */
708 IPC_HANDLER(get_marks) {
709  yajl_gen gen = ygenalloc();
710  y(array_open);
711 
712  Con *con;
714  if (con->mark != NULL)
715  ystr(con->mark);
716 
717  y(array_close);
718 
719  const unsigned char *payload;
720  ylength length;
721  y(get_buf, &payload, &length);
722 
723  ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_MARKS, payload);
724  y(free);
725 }
726 
727 /*
728  * Returns the version of i3
729  *
730  */
731 IPC_HANDLER(get_version) {
732  yajl_gen gen = ygenalloc();
733  y(map_open);
734 
735  ystr("major");
736  y(integer, MAJOR_VERSION);
737 
738  ystr("minor");
739  y(integer, MINOR_VERSION);
740 
741  ystr("patch");
742  y(integer, PATCH_VERSION);
743 
744  ystr("human_readable");
745  ystr(I3_VERSION);
746 
747  y(map_close);
748 
749  const unsigned char *payload;
750  ylength length;
751  y(get_buf, &payload, &length);
752 
753  ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_VERSION, payload);
754  y(free);
755 }
756 
757 /*
758  * Formats the reply message for a GET_BAR_CONFIG request and sends it to the
759  * client.
760  *
761  */
762 IPC_HANDLER(get_bar_config) {
763  yajl_gen gen = ygenalloc();
764 
765  /* If no ID was passed, we return a JSON array with all IDs */
766  if (message_size == 0) {
767  y(array_open);
768  Barconfig *current;
769  TAILQ_FOREACH (current, &barconfigs, configs) {
770  ystr(current->id);
771  }
772  y(array_close);
773 
774  const unsigned char *payload;
775  ylength length;
776  y(get_buf, &payload, &length);
777 
778  ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_BAR_CONFIG, payload);
779  y(free);
780  return;
781  }
782 
783  /* To get a properly terminated buffer, we copy
784  * message_size bytes out of the buffer */
785  char *bar_id = scalloc(message_size + 1);
786  strncpy(bar_id, (const char *)message, message_size);
787  LOG("IPC: looking for config for bar ID \"%s\"\n", bar_id);
788  Barconfig *current, *config = NULL;
789  TAILQ_FOREACH (current, &barconfigs, configs) {
790  if (strcmp(current->id, bar_id) != 0)
791  continue;
792 
793  config = current;
794  break;
795  }
796 
797  if (!config) {
798  /* If we did not find a config for the given ID, the reply will contain
799  * a null 'id' field. */
800  y(map_open);
801 
802  ystr("id");
803  y(null);
804 
805  y(map_close);
806  } else {
807  dump_bar_config(gen, config);
808  }
809 
810  const unsigned char *payload;
811  ylength length;
812  y(get_buf, &payload, &length);
813 
814  ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_BAR_CONFIG, payload);
815  y(free);
816 }
817 
818 /*
819  * Callback for the YAJL parser (will be called when a string is parsed).
820  *
821  */
822 static int add_subscription(void *extra, const unsigned char *s,
823  ylength len) {
824  ipc_client *client = extra;
825 
826  DLOG("should add subscription to extra %p, sub %.*s\n", client, (int)len, s);
827  int event = client->num_events;
828 
829  client->num_events++;
830  client->events = realloc(client->events, client->num_events * sizeof(char *));
831  /* We copy the string because it is not null-terminated and strndup()
832  * is missing on some BSD systems */
833  client->events[event] = scalloc(len + 1);
834  memcpy(client->events[event], s, len);
835 
836  DLOG("client is now subscribed to:\n");
837  for (int i = 0; i < client->num_events; i++)
838  DLOG("event %s\n", client->events[i]);
839  DLOG("(done)\n");
840 
841  return 1;
842 }
843 
844 /*
845  * Subscribes this connection to the event types which were given as a JSON
846  * serialized array in the payload field of the message.
847  *
848  */
849 IPC_HANDLER(subscribe) {
850  yajl_handle p;
851  yajl_status stat;
852  ipc_client *current, *client = NULL;
853 
854  /* Search the ipc_client structure for this connection */
855  TAILQ_FOREACH (current, &all_clients, clients) {
856  if (current->fd != fd)
857  continue;
858 
859  client = current;
860  break;
861  }
862 
863  if (client == NULL) {
864  ELOG("Could not find ipc_client data structure for fd %d\n", fd);
865  return;
866  }
867 
868  /* Setup the JSON parser */
869  static yajl_callbacks callbacks = {
870  .yajl_string = add_subscription,
871  };
872 
873  p = yalloc(&callbacks, (void *)client);
874  stat = yajl_parse(p, (const unsigned char *)message, message_size);
875  if (stat != yajl_status_ok) {
876  unsigned char *err;
877  err = yajl_get_error(p, true, (const unsigned char *)message,
878  message_size);
879  ELOG("YAJL parse error: %s\n", err);
880  yajl_free_error(p, err);
881 
882  const char *reply = "{\"success\":false}";
883  ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (const uint8_t *)reply);
884  yajl_free(p);
885  return;
886  }
887  yajl_free(p);
888  const char *reply = "{\"success\":true}";
889  ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (const uint8_t *)reply);
890 }
891 
892 /* The index of each callback function corresponds to the numeric
893  * value of the message type (see include/i3/ipc.h) */
895  handle_command,
896  handle_get_workspaces,
897  handle_subscribe,
898  handle_get_outputs,
899  handle_tree,
900  handle_get_marks,
901  handle_get_bar_config,
902  handle_get_version,
903 };
904 
905 /*
906  * Handler for activity on a client connection, receives a message from a
907  * client.
908  *
909  * For now, the maximum message size is 2048. I’m not sure for what the
910  * IPC interface will be used in the future, thus I’m not implementing a
911  * mechanism for arbitrarily long messages, as it seems like overkill
912  * at the moment.
913  *
914  */
915 static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) {
916  uint32_t message_type;
917  uint32_t message_length;
918  uint8_t *message = NULL;
919 
920  int ret = ipc_recv_message(w->fd, &message_type, &message_length, &message);
921  /* EOF or other error */
922  if (ret < 0) {
923  /* Was this a spurious read? See ev(3) */
924  if (ret == -1 && errno == EAGAIN) {
925  FREE(message);
926  return;
927  }
928 
929  /* If not, there was some kind of error. We don’t bother
930  * and close the connection */
931  close(w->fd);
932 
933  /* Delete the client from the list of clients */
934  ipc_client *current;
935  TAILQ_FOREACH (current, &all_clients, clients) {
936  if (current->fd != w->fd)
937  continue;
938 
939  for (int i = 0; i < current->num_events; i++)
940  free(current->events[i]);
941  /* We can call TAILQ_REMOVE because we break out of the
942  * TAILQ_FOREACH afterwards */
943  TAILQ_REMOVE(&all_clients, current, clients);
944  free(current);
945  break;
946  }
947 
948  ev_io_stop(EV_A_ w);
949  free(w);
950  FREE(message);
951 
952  DLOG("IPC: client disconnected\n");
953  return;
954  }
955 
956  if (message_type >= (sizeof(handlers) / sizeof(handler_t)))
957  DLOG("Unhandled message type: %d\n", message_type);
958  else {
959  handler_t h = handlers[message_type];
960  h(w->fd, message, 0, message_length, message_type);
961  }
962 
963  FREE(message);
964 }
965 
966 /*
967  * Handler for activity on the listening socket, meaning that a new client
968  * has just connected and we should accept() him. Sets up the event handler
969  * for activity on the new connection and inserts the file descriptor into
970  * the list of clients.
971  *
972  */
973 void ipc_new_client(EV_P_ struct ev_io *w, int revents) {
974  struct sockaddr_un peer;
975  socklen_t len = sizeof(struct sockaddr_un);
976  int client;
977  if ((client = accept(w->fd, (struct sockaddr *)&peer, &len)) < 0) {
978  if (errno == EINTR)
979  return;
980  else
981  perror("accept()");
982  return;
983  }
984 
985  /* Close this file descriptor on exec() */
986  (void)fcntl(client, F_SETFD, FD_CLOEXEC);
987 
988  set_nonblock(client);
989 
990  struct ev_io *package = scalloc(sizeof(struct ev_io));
991  ev_io_init(package, ipc_receive_message, client, EV_READ);
992  ev_io_start(EV_A_ package);
993 
994  DLOG("IPC: new client connected on fd %d\n", w->fd);
995 
996  ipc_client *new = scalloc(sizeof(ipc_client));
997  new->fd = client;
998 
999  TAILQ_INSERT_TAIL(&all_clients, new, clients);
1000 }
1001 
1002 /*
1003  * Creates the UNIX domain socket at the given path, sets it to non-blocking
1004  * mode, bind()s and listen()s on it.
1005  *
1006  */
1007 int ipc_create_socket(const char *filename) {
1008  int sockfd;
1009 
1011 
1012  char *resolved = resolve_tilde(filename);
1013  DLOG("Creating IPC-socket at %s\n", resolved);
1014  char *copy = sstrdup(resolved);
1015  const char *dir = dirname(copy);
1016  if (!path_exists(dir))
1017  mkdirp(dir);
1018  free(copy);
1019 
1020  /* Unlink the unix domain socket before */
1021  unlink(resolved);
1022 
1023  if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
1024  perror("socket()");
1025  free(resolved);
1026  return -1;
1027  }
1028 
1029  (void)fcntl(sockfd, F_SETFD, FD_CLOEXEC);
1030 
1031  struct sockaddr_un addr;
1032  memset(&addr, 0, sizeof(struct sockaddr_un));
1033  addr.sun_family = AF_LOCAL;
1034  strncpy(addr.sun_path, resolved, sizeof(addr.sun_path) - 1);
1035  if (bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
1036  perror("bind()");
1037  free(resolved);
1038  return -1;
1039  }
1040 
1041  set_nonblock(sockfd);
1042 
1043  if (listen(sockfd, 5) < 0) {
1044  perror("listen()");
1045  free(resolved);
1046  return -1;
1047  }
1048 
1049  current_socketpath = resolved;
1050  return sockfd;
1051 }
1052 
1053 /*
1054  * For the workspace "focus" event we send, along the usual "change" field,
1055  * also the current and previous workspace, in "current" and "old"
1056  * respectively.
1057  */
1059  setlocale(LC_NUMERIC, "C");
1060  yajl_gen gen = ygenalloc();
1061 
1062  y(map_open);
1063 
1064  ystr("change");
1065  ystr("focus");
1066 
1067  ystr("current");
1068  dump_node(gen, current, false);
1069 
1070  ystr("old");
1071  if (old == NULL)
1072  y(null);
1073  else
1074  dump_node(gen, old, false);
1075 
1076  y(map_close);
1077 
1078  const unsigned char *payload;
1079  ylength length;
1080  y(get_buf, &payload, &length);
1081 
1082  ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload);
1083  y(free);
1084  setlocale(LC_NUMERIC, "");
1085 }
1086 
1091 void ipc_send_window_event(const char *property, Con *con) {
1092  DLOG("Issue IPC window %s event (con = %p, window = 0x%08x)\n",
1093  property, con, (con->window ? con->window->id : XCB_WINDOW_NONE));
1094 
1095  setlocale(LC_NUMERIC, "C");
1096  yajl_gen gen = ygenalloc();
1097 
1098  y(map_open);
1099 
1100  ystr("change");
1101  ystr(property);
1102 
1103  ystr("container");
1104  dump_node(gen, con, false);
1105 
1106  y(map_close);
1107 
1108  const unsigned char *payload;
1109  ylength length;
1110  y(get_buf, &payload, &length);
1111 
1112  ipc_send_event("window", I3_IPC_EVENT_WINDOW, (const char *)payload);
1113  y(free);
1114  setlocale(LC_NUMERIC, "");
1115 }
1116 
1121  DLOG("Issue barconfig_update event for id = %s\n", barconfig->id);
1122  setlocale(LC_NUMERIC, "C");
1123  yajl_gen gen = ygenalloc();
1124 
1125  dump_bar_config(gen, barconfig);
1126 
1127  const unsigned char *payload;
1128  ylength length;
1129  y(get_buf, &payload, &length);
1130 
1131  ipc_send_event("barconfig_update", I3_IPC_EVENT_BARCONFIG_UPDATE, (const char *)payload);
1132  y(free);
1133  setlocale(LC_NUMERIC, "");
1134 }
Definition: ipc.h:25
Definition: data.h:87
const char * i3string_as_utf8(i3String *str)
Returns the UTF-8 encoded version of the i3String.
char * sstrdup(const char *str)
Safe-wrapper around strdup which exits if malloc returns NULL (meaning that there is no more memory a...
void ipc_shutdown(void)
Calls shutdown() on each socket and closes it.
Definition: ipc.c:98
void ipc_send_workspace_focus_event(Con *current, Con *old)
For the workspace "focus" event we send, along the usual "change" field, also the current and previou...
Definition: ipc.c:1058
bool urgent
Definition: data.h:484
char * name
Name of the output.
Definition: data.h:316
void tree_render(void)
Renders the tree, that is rendering all outputs using render_con() and pushing the changes to X11 usi...
Definition: tree.c:507
Definition: data.h:56
uint32_t y
Definition: data.h:124
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:386
Config config
Definition: config.c:19
bool hide_binding_mode_indicator
Hide mode button? Configuration option is 'binding_mode_indicator no' but we invert the bool for the ...
Definition: config.h:287
Con * con_get_workspace(Con *con)
Gets the workspace container this node is on.
Definition: con.c:317
static void dump_bar_config(yajl_gen gen, Barconfig *config)
Definition: ipc.c:435
Stores a rectangle, for example the size of a window, the child window etc.
Definition: data.h:122
#define LOG(fmt,...)
Definition: libi3.h:76
bool workspace_is_visible(Con *ws)
Returns true if the workspace is currently visible.
Definition: workspace.c:232
enum Barconfig::@5 mode
Bar display mode (hide unless modifier is pressed or show in dock mode or always hide in invisible mo...
Definition: data.h:56
enum Barconfig::@8 position
Bar position (bottom by default).
struct Rect rect
Definition: data.h:514
struct all_cons_head all_cons
Definition: tree.c:17
char * mark
Definition: data.h:528
Rect rect
x, y, width, height
Definition: data.h:322
#define ystr(str)
Definition: commands.c:20
CommandResult * parse_command(const char *input, yajl_gen gen)
Parses and executes the given command.
bool verbose
Enable verbose mode? Useful for debugging purposes.
Definition: config.h:290
static bool mkdirp(const char *path)
Definition: ipc.c:44
size_t ylength
Definition: yajl_utils.h:22
#define TAILQ_FIRST(head)
Definition: queue.h:323
TAILQ_HEAD(ipc_client_head, ipc_client)
Definition: ipc.c:25
struct Window * window
Definition: data.h:547
IPC_HANDLER(command)
Definition: ipc.c:114
void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart)
Definition: ipc.c:154
handler_t handlers[8]
Definition: ipc.c:894
An Output is a physical output on your graphics driver.
Definition: data.h:301
int ipc_recv_message(int sockfd, uint32_t *message_type, uint32_t *reply_length, uint8_t **reply)
Reads a message from the given socket file descriptor and stores its length (reply_length) as well as...
bool con_is_internal(Con *con)
Returns true if the container is internal, such as __i3_scratch.
Definition: con.c:411
void command_result_free(CommandResult *result)
Frees a CommandResult.
static void ipc_receive_message(EV_P_ struct ev_io *w, int revents)
Definition: ipc.c:915
void ipc_send_event(const char *event, uint32_t message_type, const char *payload)
Sends the specified event to all IPC clients which are currently connected and subscribed to this kin...
Definition: ipc.c:75
enum Con::@18 type
Definition: data.h:85
#define DLOG(fmt,...)
Definition: libi3.h:86
A struct that contains useful information about the result of a command as a whole (e...
Definition: data.h:86
void(* handler_t)(int, uint8_t *, int, uint32_t, uint32_t)
Definition: ipc.h:45
int ipc_create_socket(const char *filename)
Creates the UNIX domain socket at the given path, sets it to non-blocking mode, bind()s and listen()s...
Definition: ipc.c:1007
Con * con_get_fullscreen_con(Con *con, fullscreen_mode_t fullscreen_mode)
Returns the first fullscreen node below this node.
Definition: con.c:365
char ** events
Definition: ipc.h:30
struct outputs_head outputs
Definition: randr.c:28
uint32_t height
Definition: data.h:126
Con * focused
Definition: tree.c:15
Con * con
Pointer to the Con which represents this output.
Definition: data.h:319
char * id
Automatically generated ID for this bar config.
Definition: config.h:226
int num
the workspace number, if this Con is of type CT_WORKSPACE and the workspace is not a named workspace ...
Definition: data.h:510
char * name
Definition: data.h:520
Definition: data.h:91
enum Barconfig::@7 modifier
Bar modifier (to show bar when in hide mode).
static void dump_rect(yajl_gen gen, const char *name, Rect r)
Definition: ipc.c:140
int num_events
Definition: ipc.h:29
#define TAILQ_EMPTY(head)
Definition: queue.h:331
int ipc_send_message(int sockfd, const uint32_t message_size, const uint32_t message_type, const uint8_t *payload)
Formats a message (payload) of the given size and type and sends it to i3 via the given socket file d...
#define yalloc(callbacks, client)
Definition: yajl_utils.h:21
#define YSTR_IF_SET(name)
bool strip_workspace_numbers
Strip workspace numbers? Configuration option is 'strip_workspace_numbers yes'.
Definition: config.h:283
#define ygenalloc()
Definition: yajl_utils.h:20
bool active
Whether the output is currently active (has a CRTC attached with a valid mode)
Definition: data.h:307
char * resolve_tilde(const char *path)
This function resolves ~ in pathnames.
Definition: util.c:168
bool con_is_split(Con *con)
Definition: con.c:265
enum Match::@13 dock
#define ELOG(fmt,...)
Definition: libi3.h:81
Definition: data.h:55
A "match" is a data structure which acts like a mask or expression to match certain windows or not...
Definition: data.h:390
static int add_subscription(void *extra, const unsigned char *s, ylength len)
Definition: ipc.c:822
orientation_t con_orientation(Con *con)
Returns the orientation of the given container (for stacked containers, vertical orientation is used ...
Definition: con.c:843
A 'Con' represents everything from the X11 root window down to a single X11 window.
Definition: data.h:479
uint32_t x
Definition: data.h:123
enum Barconfig::@6 hidden_state
void * scalloc(size_t size)
Safe-wrapper around calloc which exits if malloc returns NULL (meaning that there is no more memory a...
char * current_socketpath
Definition: ipc.c:23
Definition: data.h:89
enum Match::@15 insert_where
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:362
struct Con * croot
Definition: tree.c:14
bool hide_workspace_buttons
Hide workspace buttons? Configuration option is 'workspace_buttons no' but we invert the bool to get ...
Definition: config.h:279
#define TAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:311
bool path_exists(const char *path)
Checks if the given path exists by calling stat().
Definition: util.c:198
void ipc_new_client(EV_P_ struct ev_io *w, int revents)
Handler for activity on the listening socket, meaning that a new client has just connected and we sho...
Definition: ipc.c:973
struct barconfig_head barconfigs
Definition: config.c:21
char ** outputs
Outputs on which this bar should show up on.
Definition: config.h:232
Con * output_get_content(Con *output)
Returns the output container below the given output container.
Definition: output.c:18
uint32_t y
Definition: data.h:31
xcb_window_t id
Definition: data.h:333
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:334
#define DUMP_PROPERTY(key, prop_name)
Holds the status bar configuration (i3bar).
Definition: config.h:223
#define FREE(pointer)
Definition: util.h:46
bool primary
Definition: data.h:313
void ipc_send_window_event(const char *property, Con *con)
For the window events we send, along the usual "change" field, also the window container, in "container".
Definition: ipc.c:1091
Definition: data.h:56
int fd
Definition: ipc.h:26
uint32_t width
Definition: data.h:125
#define DUMP_REGEX(re_name)
Definition: data.h:90
int num_outputs
Number of outputs in the outputs array.
Definition: config.h:229
void ipc_send_barconfig_update_event(Barconfig *barconfig)
For the barconfig update events, we send the serialized barconfig.
Definition: ipc.c:1120