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