i3
|
00001 /* 00002 * vim:ts=4:sw=4:expandtab 00003 * 00004 * i3 - an improved dynamic tiling window manager 00005 * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE) 00006 * 00007 * ipc.c: UNIX domain socket IPC (initialization, client handling, protocol). 00008 * 00009 */ 00010 #include "all.h" 00011 00012 #include <sys/socket.h> 00013 #include <sys/un.h> 00014 #include <fcntl.h> 00015 #include <libgen.h> 00016 #include <ev.h> 00017 #include <yajl/yajl_gen.h> 00018 #include <yajl/yajl_parse.h> 00019 #include <yajl/yajl_version.h> 00020 00021 char *current_socketpath = NULL; 00022 00023 /* Shorter names for all those yajl_gen_* functions */ 00024 #define y(x, ...) yajl_gen_ ## x (gen, ##__VA_ARGS__) 00025 #define ystr(str) yajl_gen_string(gen, (unsigned char*)str, strlen(str)) 00026 00027 TAILQ_HEAD(ipc_client_head, ipc_client) all_clients = TAILQ_HEAD_INITIALIZER(all_clients); 00028 00029 /* 00030 * Puts the given socket file descriptor into non-blocking mode or dies if 00031 * setting O_NONBLOCK failed. Non-blocking sockets are a good idea for our 00032 * IPC model because we should by no means block the window manager. 00033 * 00034 */ 00035 static void set_nonblock(int sockfd) { 00036 int flags = fcntl(sockfd, F_GETFL, 0); 00037 flags |= O_NONBLOCK; 00038 if (fcntl(sockfd, F_SETFL, flags) < 0) 00039 err(-1, "Could not set O_NONBLOCK"); 00040 } 00041 00042 /* 00043 * Emulates mkdir -p (creates any missing folders) 00044 * 00045 */ 00046 static bool mkdirp(const char *path) { 00047 if (mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0) 00048 return true; 00049 if (errno != ENOENT) { 00050 ELOG("mkdir(%s) failed: %s\n", path, strerror(errno)); 00051 return false; 00052 } 00053 char *copy = strdup(path); 00054 /* strip trailing slashes, if any */ 00055 while (copy[strlen(copy)-1] == '/') 00056 copy[strlen(copy)-1] = '\0'; 00057 00058 char *sep = strrchr(copy, '/'); 00059 if (sep == NULL) { 00060 FREE(copy); 00061 return false; 00062 } 00063 *sep = '\0'; 00064 bool result = false; 00065 if (mkdirp(copy)) 00066 result = mkdirp(path); 00067 free(copy); 00068 00069 return result; 00070 } 00071 00072 /* 00073 * Sends the specified event to all IPC clients which are currently connected 00074 * and subscribed to this kind of event. 00075 * 00076 */ 00077 void ipc_send_event(const char *event, uint32_t message_type, const char *payload) { 00078 ipc_client *current; 00079 TAILQ_FOREACH(current, &all_clients, clients) { 00080 /* see if this client is interested in this event */ 00081 bool interested = false; 00082 for (int i = 0; i < current->num_events; i++) { 00083 if (strcasecmp(current->events[i], event) != 0) 00084 continue; 00085 interested = true; 00086 break; 00087 } 00088 if (!interested) 00089 continue; 00090 00091 ipc_send_message(current->fd, strlen(payload), message_type, (const uint8_t*)payload); 00092 } 00093 } 00094 00095 /* 00096 * Calls shutdown() on each socket and closes it. This function to be called 00097 * when exiting or restarting only! 00098 * 00099 */ 00100 void ipc_shutdown(void) { 00101 ipc_client *current; 00102 while (!TAILQ_EMPTY(&all_clients)) { 00103 current = TAILQ_FIRST(&all_clients); 00104 shutdown(current->fd, SHUT_RDWR); 00105 close(current->fd); 00106 TAILQ_REMOVE(&all_clients, current, clients); 00107 free(current); 00108 } 00109 } 00110 00111 /* 00112 * Executes the command and returns whether it could be successfully parsed 00113 * or not (at the moment, always returns true). 00114 * 00115 */ 00116 IPC_HANDLER(command) { 00117 /* To get a properly terminated buffer, we copy 00118 * message_size bytes out of the buffer */ 00119 char *command = scalloc(message_size + 1); 00120 strncpy(command, (const char*)message, message_size); 00121 LOG("IPC: received: *%s*\n", command); 00122 struct CommandResult *command_output = parse_command((const char*)command); 00123 free(command); 00124 00125 if (command_output->needs_tree_render) 00126 tree_render(); 00127 00128 /* If no reply was provided, we just use the default success message */ 00129 ipc_send_message(fd, strlen(command_output->json_output), 00130 I3_IPC_REPLY_TYPE_COMMAND, 00131 (const uint8_t*)command_output->json_output); 00132 00133 free(command_output->json_output); 00134 } 00135 00136 static void dump_rect(yajl_gen gen, const char *name, Rect r) { 00137 ystr(name); 00138 y(map_open); 00139 ystr("x"); 00140 y(integer, r.x); 00141 ystr("y"); 00142 y(integer, r.y); 00143 ystr("width"); 00144 y(integer, r.width); 00145 ystr("height"); 00146 y(integer, r.height); 00147 y(map_close); 00148 } 00149 00150 void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { 00151 y(map_open); 00152 ystr("id"); 00153 y(integer, (long int)con); 00154 00155 ystr("type"); 00156 y(integer, con->type); 00157 00158 ystr("orientation"); 00159 switch (con->orientation) { 00160 case NO_ORIENTATION: 00161 ystr("none"); 00162 break; 00163 case HORIZ: 00164 ystr("horizontal"); 00165 break; 00166 case VERT: 00167 ystr("vertical"); 00168 break; 00169 } 00170 00171 ystr("scratchpad_state"); 00172 switch (con->scratchpad_state) { 00173 case SCRATCHPAD_NONE: 00174 ystr("none"); 00175 break; 00176 case SCRATCHPAD_FRESH: 00177 ystr("fresh"); 00178 break; 00179 case SCRATCHPAD_CHANGED: 00180 ystr("changed"); 00181 break; 00182 } 00183 00184 ystr("percent"); 00185 if (con->percent == 0.0) 00186 y(null); 00187 else y(double, con->percent); 00188 00189 ystr("urgent"); 00190 y(bool, con->urgent); 00191 00192 if (con->mark != NULL) { 00193 ystr("mark"); 00194 ystr(con->mark); 00195 } 00196 00197 ystr("focused"); 00198 y(bool, (con == focused)); 00199 00200 ystr("layout"); 00201 switch (con->layout) { 00202 case L_DEFAULT: 00203 ystr("default"); 00204 break; 00205 case L_STACKED: 00206 ystr("stacked"); 00207 break; 00208 case L_TABBED: 00209 ystr("tabbed"); 00210 break; 00211 case L_DOCKAREA: 00212 ystr("dockarea"); 00213 break; 00214 case L_OUTPUT: 00215 ystr("output"); 00216 break; 00217 } 00218 00219 ystr("border"); 00220 switch (con->border_style) { 00221 case BS_NORMAL: 00222 ystr("normal"); 00223 break; 00224 case BS_NONE: 00225 ystr("none"); 00226 break; 00227 case BS_1PIXEL: 00228 ystr("1pixel"); 00229 break; 00230 } 00231 00232 dump_rect(gen, "rect", con->rect); 00233 dump_rect(gen, "window_rect", con->window_rect); 00234 dump_rect(gen, "geometry", con->geometry); 00235 00236 ystr("name"); 00237 if (con->window && con->window->name_json) 00238 ystr(con->window->name_json); 00239 else 00240 ystr(con->name); 00241 00242 if (con->type == CT_WORKSPACE) { 00243 ystr("num"); 00244 y(integer, con->num); 00245 } 00246 00247 ystr("window"); 00248 if (con->window) 00249 y(integer, con->window->id); 00250 else y(null); 00251 00252 ystr("nodes"); 00253 y(array_open); 00254 Con *node; 00255 if (con->type != CT_DOCKAREA || !inplace_restart) { 00256 TAILQ_FOREACH(node, &(con->nodes_head), nodes) { 00257 dump_node(gen, node, inplace_restart); 00258 } 00259 } 00260 y(array_close); 00261 00262 ystr("floating_nodes"); 00263 y(array_open); 00264 TAILQ_FOREACH(node, &(con->floating_head), floating_windows) { 00265 dump_node(gen, node, inplace_restart); 00266 } 00267 y(array_close); 00268 00269 ystr("focus"); 00270 y(array_open); 00271 TAILQ_FOREACH(node, &(con->focus_head), focused) { 00272 y(integer, (long int)node); 00273 } 00274 y(array_close); 00275 00276 ystr("fullscreen_mode"); 00277 y(integer, con->fullscreen_mode); 00278 00279 ystr("floating"); 00280 switch (con->floating) { 00281 case FLOATING_AUTO_OFF: 00282 ystr("auto_off"); 00283 break; 00284 case FLOATING_AUTO_ON: 00285 ystr("auto_on"); 00286 break; 00287 case FLOATING_USER_OFF: 00288 ystr("user_off"); 00289 break; 00290 case FLOATING_USER_ON: 00291 ystr("user_on"); 00292 break; 00293 } 00294 00295 ystr("swallows"); 00296 y(array_open); 00297 Match *match; 00298 TAILQ_FOREACH(match, &(con->swallow_head), matches) { 00299 if (match->dock != -1) { 00300 y(map_open); 00301 ystr("dock"); 00302 y(integer, match->dock); 00303 ystr("insert_where"); 00304 y(integer, match->insert_where); 00305 y(map_close); 00306 } 00307 00308 /* TODO: the other swallow keys */ 00309 } 00310 00311 if (inplace_restart) { 00312 if (con->window != NULL) { 00313 y(map_open); 00314 ystr("id"); 00315 y(integer, con->window->id); 00316 ystr("restart_mode"); 00317 y(bool, true); 00318 y(map_close); 00319 } 00320 } 00321 y(array_close); 00322 00323 y(map_close); 00324 } 00325 00326 IPC_HANDLER(tree) { 00327 setlocale(LC_NUMERIC, "C"); 00328 #if YAJL_MAJOR >= 2 00329 yajl_gen gen = yajl_gen_alloc(NULL); 00330 #else 00331 yajl_gen gen = yajl_gen_alloc(NULL, NULL); 00332 #endif 00333 dump_node(gen, croot, false); 00334 setlocale(LC_NUMERIC, ""); 00335 00336 const unsigned char *payload; 00337 #if YAJL_MAJOR >= 2 00338 size_t length; 00339 #else 00340 unsigned int length; 00341 #endif 00342 y(get_buf, &payload, &length); 00343 00344 ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_TREE, payload); 00345 y(free); 00346 } 00347 00348 00349 /* 00350 * Formats the reply message for a GET_WORKSPACES request and sends it to the 00351 * client 00352 * 00353 */ 00354 IPC_HANDLER(get_workspaces) { 00355 #if YAJL_MAJOR >= 2 00356 yajl_gen gen = yajl_gen_alloc(NULL); 00357 #else 00358 yajl_gen gen = yajl_gen_alloc(NULL, NULL); 00359 #endif 00360 y(array_open); 00361 00362 Con *focused_ws = con_get_workspace(focused); 00363 00364 Con *output; 00365 TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { 00366 if (output->name[0] == '_' && output->name[1] == '_') 00367 continue; 00368 Con *ws; 00369 TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) { 00370 assert(ws->type == CT_WORKSPACE); 00371 y(map_open); 00372 00373 ystr("num"); 00374 if (ws->num == -1) 00375 y(null); 00376 else y(integer, ws->num); 00377 00378 ystr("name"); 00379 ystr(ws->name); 00380 00381 ystr("visible"); 00382 y(bool, workspace_is_visible(ws)); 00383 00384 ystr("focused"); 00385 y(bool, ws == focused_ws); 00386 00387 ystr("rect"); 00388 y(map_open); 00389 ystr("x"); 00390 y(integer, ws->rect.x); 00391 ystr("y"); 00392 y(integer, ws->rect.y); 00393 ystr("width"); 00394 y(integer, ws->rect.width); 00395 ystr("height"); 00396 y(integer, ws->rect.height); 00397 y(map_close); 00398 00399 ystr("output"); 00400 ystr(output->name); 00401 00402 ystr("urgent"); 00403 y(bool, ws->urgent); 00404 00405 y(map_close); 00406 } 00407 } 00408 00409 y(array_close); 00410 00411 const unsigned char *payload; 00412 #if YAJL_MAJOR >= 2 00413 size_t length; 00414 #else 00415 unsigned int length; 00416 #endif 00417 y(get_buf, &payload, &length); 00418 00419 ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_WORKSPACES, payload); 00420 y(free); 00421 } 00422 00423 /* 00424 * Formats the reply message for a GET_OUTPUTS request and sends it to the 00425 * client 00426 * 00427 */ 00428 IPC_HANDLER(get_outputs) { 00429 #if YAJL_MAJOR >= 2 00430 yajl_gen gen = yajl_gen_alloc(NULL); 00431 #else 00432 yajl_gen gen = yajl_gen_alloc(NULL, NULL); 00433 #endif 00434 y(array_open); 00435 00436 Output *output; 00437 TAILQ_FOREACH(output, &outputs, outputs) { 00438 y(map_open); 00439 00440 ystr("name"); 00441 ystr(output->name); 00442 00443 ystr("active"); 00444 y(bool, output->active); 00445 00446 ystr("primary"); 00447 y(bool, output->primary); 00448 00449 ystr("rect"); 00450 y(map_open); 00451 ystr("x"); 00452 y(integer, output->rect.x); 00453 ystr("y"); 00454 y(integer, output->rect.y); 00455 ystr("width"); 00456 y(integer, output->rect.width); 00457 ystr("height"); 00458 y(integer, output->rect.height); 00459 y(map_close); 00460 00461 ystr("current_workspace"); 00462 Con *ws = NULL; 00463 if (output->con && (ws = con_get_fullscreen_con(output->con, CF_OUTPUT))) 00464 ystr(ws->name); 00465 else y(null); 00466 00467 y(map_close); 00468 } 00469 00470 y(array_close); 00471 00472 const unsigned char *payload; 00473 #if YAJL_MAJOR >= 2 00474 size_t length; 00475 #else 00476 unsigned int length; 00477 #endif 00478 y(get_buf, &payload, &length); 00479 00480 ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_OUTPUTS, payload); 00481 y(free); 00482 } 00483 00484 /* 00485 * Formats the reply message for a GET_MARKS request and sends it to the 00486 * client 00487 * 00488 */ 00489 IPC_HANDLER(get_marks) { 00490 #if YAJL_MAJOR >= 2 00491 yajl_gen gen = yajl_gen_alloc(NULL); 00492 #else 00493 yajl_gen gen = yajl_gen_alloc(NULL, NULL); 00494 #endif 00495 y(array_open); 00496 00497 Con *con; 00498 TAILQ_FOREACH(con, &all_cons, all_cons) 00499 if (con->mark != NULL) 00500 ystr(con->mark); 00501 00502 y(array_close); 00503 00504 const unsigned char *payload; 00505 #if YAJL_MAJOR >= 2 00506 size_t length; 00507 #else 00508 unsigned int length; 00509 #endif 00510 y(get_buf, &payload, &length); 00511 00512 ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_MARKS, payload); 00513 y(free); 00514 } 00515 00516 /* 00517 * Formats the reply message for a GET_BAR_CONFIG request and sends it to the 00518 * client. 00519 * 00520 */ 00521 IPC_HANDLER(get_bar_config) { 00522 #if YAJL_MAJOR >= 2 00523 yajl_gen gen = yajl_gen_alloc(NULL); 00524 #else 00525 yajl_gen gen = yajl_gen_alloc(NULL, NULL); 00526 #endif 00527 00528 /* If no ID was passed, we return a JSON array with all IDs */ 00529 if (message_size == 0) { 00530 y(array_open); 00531 Barconfig *current; 00532 TAILQ_FOREACH(current, &barconfigs, configs) { 00533 ystr(current->id); 00534 } 00535 y(array_close); 00536 00537 const unsigned char *payload; 00538 #if YAJL_MAJOR >= 2 00539 size_t length; 00540 #else 00541 unsigned int length; 00542 #endif 00543 y(get_buf, &payload, &length); 00544 00545 ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_BAR_CONFIG, payload); 00546 y(free); 00547 return; 00548 } 00549 00550 /* To get a properly terminated buffer, we copy 00551 * message_size bytes out of the buffer */ 00552 char *bar_id = scalloc(message_size + 1); 00553 strncpy(bar_id, (const char*)message, message_size); 00554 LOG("IPC: looking for config for bar ID \"%s\"\n", bar_id); 00555 Barconfig *current, *config = NULL; 00556 TAILQ_FOREACH(current, &barconfigs, configs) { 00557 if (strcmp(current->id, bar_id) != 0) 00558 continue; 00559 00560 config = current; 00561 break; 00562 } 00563 00564 y(map_open); 00565 00566 if (!config) { 00567 /* If we did not find a config for the given ID, the reply will contain 00568 * a null 'id' field. */ 00569 ystr("id"); 00570 y(null); 00571 } else { 00572 ystr("id"); 00573 ystr(config->id); 00574 00575 if (config->num_outputs > 0) { 00576 ystr("outputs"); 00577 y(array_open); 00578 for (int c = 0; c < config->num_outputs; c++) 00579 ystr(config->outputs[c]); 00580 y(array_close); 00581 } 00582 00583 #define YSTR_IF_SET(name) \ 00584 do { \ 00585 if (config->name) { \ 00586 ystr( # name); \ 00587 ystr(config->name); \ 00588 } \ 00589 } while (0) 00590 00591 YSTR_IF_SET(tray_output); 00592 YSTR_IF_SET(socket_path); 00593 00594 ystr("mode"); 00595 if (config->mode == M_HIDE) 00596 ystr("hide"); 00597 else ystr("dock"); 00598 00599 ystr("modifier"); 00600 switch (config->modifier) { 00601 case M_CONTROL: 00602 ystr("ctrl"); 00603 break; 00604 case M_SHIFT: 00605 ystr("shift"); 00606 break; 00607 case M_MOD1: 00608 ystr("Mod1"); 00609 break; 00610 case M_MOD2: 00611 ystr("Mod2"); 00612 break; 00613 case M_MOD3: 00614 ystr("Mod3"); 00615 break; 00616 /* 00617 case M_MOD4: 00618 ystr("Mod4"); 00619 break; 00620 */ 00621 case M_MOD5: 00622 ystr("Mod5"); 00623 break; 00624 default: 00625 ystr("Mod4"); 00626 break; 00627 } 00628 00629 ystr("position"); 00630 if (config->position == P_BOTTOM) 00631 ystr("bottom"); 00632 else ystr("top"); 00633 00634 YSTR_IF_SET(status_command); 00635 YSTR_IF_SET(font); 00636 00637 ystr("workspace_buttons"); 00638 y(bool, !config->hide_workspace_buttons); 00639 00640 ystr("verbose"); 00641 y(bool, config->verbose); 00642 00643 #undef YSTR_IF_SET 00644 #define YSTR_IF_SET(name) \ 00645 do { \ 00646 if (config->colors.name) { \ 00647 ystr( # name); \ 00648 ystr(config->colors.name); \ 00649 } \ 00650 } while (0) 00651 00652 ystr("colors"); 00653 y(map_open); 00654 YSTR_IF_SET(background); 00655 YSTR_IF_SET(statusline); 00656 YSTR_IF_SET(focused_workspace_border); 00657 YSTR_IF_SET(focused_workspace_bg); 00658 YSTR_IF_SET(focused_workspace_text); 00659 YSTR_IF_SET(active_workspace_border); 00660 YSTR_IF_SET(active_workspace_bg); 00661 YSTR_IF_SET(active_workspace_text); 00662 YSTR_IF_SET(inactive_workspace_border); 00663 YSTR_IF_SET(inactive_workspace_bg); 00664 YSTR_IF_SET(inactive_workspace_text); 00665 YSTR_IF_SET(urgent_workspace_border); 00666 YSTR_IF_SET(urgent_workspace_bg); 00667 YSTR_IF_SET(urgent_workspace_text); 00668 y(map_close); 00669 00670 #undef YSTR_IF_SET 00671 } 00672 00673 y(map_close); 00674 00675 const unsigned char *payload; 00676 #if YAJL_MAJOR >= 2 00677 size_t length; 00678 #else 00679 unsigned int length; 00680 #endif 00681 y(get_buf, &payload, &length); 00682 00683 ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_BAR_CONFIG, payload); 00684 y(free); 00685 } 00686 00687 /* 00688 * Callback for the YAJL parser (will be called when a string is parsed). 00689 * 00690 */ 00691 #if YAJL_MAJOR < 2 00692 static int add_subscription(void *extra, const unsigned char *s, 00693 unsigned int len) { 00694 #else 00695 static int add_subscription(void *extra, const unsigned char *s, 00696 size_t len) { 00697 #endif 00698 ipc_client *client = extra; 00699 00700 DLOG("should add subscription to extra %p, sub %.*s\n", client, len, s); 00701 int event = client->num_events; 00702 00703 client->num_events++; 00704 client->events = realloc(client->events, client->num_events * sizeof(char*)); 00705 /* We copy the string because it is not null-terminated and strndup() 00706 * is missing on some BSD systems */ 00707 client->events[event] = scalloc(len+1); 00708 memcpy(client->events[event], s, len); 00709 00710 DLOG("client is now subscribed to:\n"); 00711 for (int i = 0; i < client->num_events; i++) 00712 DLOG("event %s\n", client->events[i]); 00713 DLOG("(done)\n"); 00714 00715 return 1; 00716 } 00717 00718 /* 00719 * Subscribes this connection to the event types which were given as a JSON 00720 * serialized array in the payload field of the message. 00721 * 00722 */ 00723 IPC_HANDLER(subscribe) { 00724 yajl_handle p; 00725 yajl_callbacks callbacks; 00726 yajl_status stat; 00727 ipc_client *current, *client = NULL; 00728 00729 /* Search the ipc_client structure for this connection */ 00730 TAILQ_FOREACH(current, &all_clients, clients) { 00731 if (current->fd != fd) 00732 continue; 00733 00734 client = current; 00735 break; 00736 } 00737 00738 if (client == NULL) { 00739 ELOG("Could not find ipc_client data structure for fd %d\n", fd); 00740 return; 00741 } 00742 00743 /* Setup the JSON parser */ 00744 memset(&callbacks, 0, sizeof(yajl_callbacks)); 00745 callbacks.yajl_string = add_subscription; 00746 00747 #if YAJL_MAJOR >= 2 00748 p = yajl_alloc(&callbacks, NULL, (void*)client); 00749 #else 00750 p = yajl_alloc(&callbacks, NULL, NULL, (void*)client); 00751 #endif 00752 stat = yajl_parse(p, (const unsigned char*)message, message_size); 00753 if (stat != yajl_status_ok) { 00754 unsigned char *err; 00755 err = yajl_get_error(p, true, (const unsigned char*)message, 00756 message_size); 00757 ELOG("YAJL parse error: %s\n", err); 00758 yajl_free_error(p, err); 00759 00760 const char *reply = "{\"success\":false}"; 00761 ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (const uint8_t*)reply); 00762 yajl_free(p); 00763 return; 00764 } 00765 yajl_free(p); 00766 const char *reply = "{\"success\":true}"; 00767 ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (const uint8_t*)reply); 00768 } 00769 00770 /* The index of each callback function corresponds to the numeric 00771 * value of the message type (see include/i3/ipc.h) */ 00772 handler_t handlers[7] = { 00773 handle_command, 00774 handle_get_workspaces, 00775 handle_subscribe, 00776 handle_get_outputs, 00777 handle_tree, 00778 handle_get_marks, 00779 handle_get_bar_config, 00780 }; 00781 00782 /* 00783 * Handler for activity on a client connection, receives a message from a 00784 * client. 00785 * 00786 * For now, the maximum message size is 2048. I’m not sure for what the 00787 * IPC interface will be used in the future, thus I’m not implementing a 00788 * mechanism for arbitrarily long messages, as it seems like overkill 00789 * at the moment. 00790 * 00791 */ 00792 static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) { 00793 char buf[2048]; 00794 int n = read(w->fd, buf, sizeof(buf)); 00795 00796 /* On error or an empty message, we close the connection */ 00797 if (n <= 0) { 00798 #if 0 00799 /* FIXME: I get these when closing a client socket, 00800 * therefore we just treat them as an error. Is this 00801 * correct? */ 00802 if (errno == EAGAIN || errno == EWOULDBLOCK) 00803 return; 00804 #endif 00805 00806 /* If not, there was some kind of error. We don’t bother 00807 * and close the connection */ 00808 close(w->fd); 00809 00810 /* Delete the client from the list of clients */ 00811 ipc_client *current; 00812 TAILQ_FOREACH(current, &all_clients, clients) { 00813 if (current->fd != w->fd) 00814 continue; 00815 00816 for (int i = 0; i < current->num_events; i++) 00817 free(current->events[i]); 00818 /* We can call TAILQ_REMOVE because we break out of the 00819 * TAILQ_FOREACH afterwards */ 00820 TAILQ_REMOVE(&all_clients, current, clients); 00821 free(current); 00822 break; 00823 } 00824 00825 ev_io_stop(EV_A_ w); 00826 free(w); 00827 00828 DLOG("IPC: client disconnected\n"); 00829 return; 00830 } 00831 00832 /* Terminate the message correctly */ 00833 buf[n] = '\0'; 00834 00835 /* Check if the message starts with the i3 IPC magic code */ 00836 if (n < strlen(I3_IPC_MAGIC)) { 00837 DLOG("IPC: message too short, ignoring\n"); 00838 return; 00839 } 00840 00841 if (strncmp(buf, I3_IPC_MAGIC, strlen(I3_IPC_MAGIC)) != 0) { 00842 DLOG("IPC: message does not start with the IPC magic\n"); 00843 return; 00844 } 00845 00846 uint8_t *message = (uint8_t*)buf; 00847 while (n > 0) { 00848 DLOG("IPC: n = %d\n", n); 00849 message += strlen(I3_IPC_MAGIC); 00850 n -= strlen(I3_IPC_MAGIC); 00851 00852 /* The next 32 bit after the magic are the message size */ 00853 uint32_t message_size; 00854 memcpy(&message_size, (uint32_t*)message, sizeof(uint32_t)); 00855 message += sizeof(uint32_t); 00856 n -= sizeof(uint32_t); 00857 00858 if (message_size > n) { 00859 DLOG("IPC: Either the message size was wrong or the message was not read completely, dropping\n"); 00860 return; 00861 } 00862 00863 /* The last 32 bits of the header are the message type */ 00864 uint32_t message_type; 00865 memcpy(&message_type, (uint32_t*)message, sizeof(uint32_t)); 00866 message += sizeof(uint32_t); 00867 n -= sizeof(uint32_t); 00868 00869 if (message_type >= (sizeof(handlers) / sizeof(handler_t))) 00870 DLOG("Unhandled message type: %d\n", message_type); 00871 else { 00872 handler_t h = handlers[message_type]; 00873 h(w->fd, message, n, message_size, message_type); 00874 } 00875 n -= message_size; 00876 message += message_size; 00877 } 00878 } 00879 00880 /* 00881 * Handler for activity on the listening socket, meaning that a new client 00882 * has just connected and we should accept() him. Sets up the event handler 00883 * for activity on the new connection and inserts the file descriptor into 00884 * the list of clients. 00885 * 00886 */ 00887 void ipc_new_client(EV_P_ struct ev_io *w, int revents) { 00888 struct sockaddr_un peer; 00889 socklen_t len = sizeof(struct sockaddr_un); 00890 int client; 00891 if ((client = accept(w->fd, (struct sockaddr*)&peer, &len)) < 0) { 00892 if (errno == EINTR) 00893 return; 00894 else perror("accept()"); 00895 return; 00896 } 00897 00898 /* Close this file descriptor on exec() */ 00899 (void)fcntl(client, F_SETFD, FD_CLOEXEC); 00900 00901 set_nonblock(client); 00902 00903 struct ev_io *package = scalloc(sizeof(struct ev_io)); 00904 ev_io_init(package, ipc_receive_message, client, EV_READ); 00905 ev_io_start(EV_A_ package); 00906 00907 DLOG("IPC: new client connected on fd %d\n", w->fd); 00908 00909 ipc_client *new = scalloc(sizeof(ipc_client)); 00910 new->fd = client; 00911 00912 TAILQ_INSERT_TAIL(&all_clients, new, clients); 00913 } 00914 00915 /* 00916 * Creates the UNIX domain socket at the given path, sets it to non-blocking 00917 * mode, bind()s and listen()s on it. 00918 * 00919 */ 00920 int ipc_create_socket(const char *filename) { 00921 int sockfd; 00922 00923 FREE(current_socketpath); 00924 00925 char *resolved = resolve_tilde(filename); 00926 DLOG("Creating IPC-socket at %s\n", resolved); 00927 char *copy = sstrdup(resolved); 00928 const char *dir = dirname(copy); 00929 if (!path_exists(dir)) 00930 mkdirp(dir); 00931 free(copy); 00932 00933 /* Unlink the unix domain socket before */ 00934 unlink(resolved); 00935 00936 if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) { 00937 perror("socket()"); 00938 free(resolved); 00939 return -1; 00940 } 00941 00942 (void)fcntl(sockfd, F_SETFD, FD_CLOEXEC); 00943 00944 struct sockaddr_un addr; 00945 memset(&addr, 0, sizeof(struct sockaddr_un)); 00946 addr.sun_family = AF_LOCAL; 00947 strncpy(addr.sun_path, resolved, sizeof(addr.sun_path) - 1); 00948 if (bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_un)) < 0) { 00949 perror("bind()"); 00950 free(resolved); 00951 return -1; 00952 } 00953 00954 set_nonblock(sockfd); 00955 00956 if (listen(sockfd, 5) < 0) { 00957 perror("listen()"); 00958 free(resolved); 00959 return -1; 00960 } 00961 00962 current_socketpath = resolved; 00963 return sockfd; 00964 }