i3
|
00001 /* 00002 * vim:ts=4:sw=4:expandtab 00003 * 00004 * i3 - an improved dynamic tiling window manager 00005 * 00006 * © 2009-2011 Michael Stapelberg and contributors 00007 * 00008 * See file LICENSE for license information. 00009 * 00010 * ipc.c: Everything about the UNIX domain sockets for IPC 00011 * 00012 */ 00013 #include <sys/socket.h> 00014 #include <sys/un.h> 00015 #include <fcntl.h> 00016 #include <libgen.h> 00017 #include <ev.h> 00018 #include <yajl/yajl_gen.h> 00019 #include <yajl/yajl_parse.h> 00020 #include <yajl/yajl_version.h> 00021 00022 #include "all.h" 00023 00024 char *current_socketpath = NULL; 00025 00026 /* Shorter names for all those yajl_gen_* functions */ 00027 #define y(x, ...) yajl_gen_ ## x (gen, ##__VA_ARGS__) 00028 #define ystr(str) yajl_gen_string(gen, (unsigned char*)str, strlen(str)) 00029 00030 TAILQ_HEAD(ipc_client_head, ipc_client) all_clients = TAILQ_HEAD_INITIALIZER(all_clients); 00031 00032 /* 00033 * Puts the given socket file descriptor into non-blocking mode or dies if 00034 * setting O_NONBLOCK failed. Non-blocking sockets are a good idea for our 00035 * IPC model because we should by no means block the window manager. 00036 * 00037 */ 00038 static void set_nonblock(int sockfd) { 00039 int flags = fcntl(sockfd, F_GETFL, 0); 00040 flags |= O_NONBLOCK; 00041 if (fcntl(sockfd, F_SETFL, flags) < 0) 00042 err(-1, "Could not set O_NONBLOCK"); 00043 } 00044 00045 /* 00046 * Emulates mkdir -p (creates any missing folders) 00047 * 00048 */ 00049 static bool mkdirp(const char *path) { 00050 if (mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0) 00051 return true; 00052 if (errno != ENOENT) { 00053 ELOG("mkdir(%s) failed: %s\n", path, strerror(errno)); 00054 return false; 00055 } 00056 char *copy = strdup(path); 00057 /* strip trailing slashes, if any */ 00058 while (copy[strlen(copy)-1] == '/') 00059 copy[strlen(copy)-1] = '\0'; 00060 00061 char *sep = strrchr(copy, '/'); 00062 if (sep == NULL) 00063 return false; 00064 *sep = '\0'; 00065 bool result = false; 00066 if (mkdirp(copy)) 00067 result = mkdirp(path); 00068 free(copy); 00069 00070 return result; 00071 } 00072 00073 static void ipc_send_message(int fd, const unsigned char *payload, 00074 int message_type, int message_size) { 00075 int buffer_size = strlen("i3-ipc") + sizeof(uint32_t) + 00076 sizeof(uint32_t) + message_size; 00077 char msg[buffer_size]; 00078 char *walk = msg; 00079 00080 strncpy(walk, "i3-ipc", buffer_size - 1); 00081 walk += strlen("i3-ipc"); 00082 memcpy(walk, &message_size, sizeof(uint32_t)); 00083 walk += sizeof(uint32_t); 00084 memcpy(walk, &message_type, sizeof(uint32_t)); 00085 walk += sizeof(uint32_t); 00086 memcpy(walk, payload, message_size); 00087 00088 int sent_bytes = 0; 00089 int bytes_to_go = buffer_size; 00090 while (sent_bytes < bytes_to_go) { 00091 int n = write(fd, msg + sent_bytes, bytes_to_go); 00092 if (n == -1) { 00093 DLOG("write() failed: %s\n", strerror(errno)); 00094 return; 00095 } 00096 00097 sent_bytes += n; 00098 bytes_to_go -= n; 00099 } 00100 } 00101 00102 /* 00103 * Sends the specified event to all IPC clients which are currently connected 00104 * and subscribed to this kind of event. 00105 * 00106 */ 00107 void ipc_send_event(const char *event, uint32_t message_type, const char *payload) { 00108 ipc_client *current; 00109 TAILQ_FOREACH(current, &all_clients, clients) { 00110 /* see if this client is interested in this event */ 00111 bool interested = false; 00112 for (int i = 0; i < current->num_events; i++) { 00113 if (strcasecmp(current->events[i], event) != 0) 00114 continue; 00115 interested = true; 00116 break; 00117 } 00118 if (!interested) 00119 continue; 00120 00121 ipc_send_message(current->fd, (const unsigned char*)payload, 00122 message_type, strlen(payload)); 00123 } 00124 } 00125 00126 /* 00127 * Calls shutdown() on each socket and closes it. This function to be called 00128 * when exiting or restarting only! 00129 * 00130 */ 00131 void ipc_shutdown() { 00132 ipc_client *current; 00133 TAILQ_FOREACH(current, &all_clients, clients) { 00134 shutdown(current->fd, SHUT_RDWR); 00135 close(current->fd); 00136 } 00137 } 00138 00139 /* 00140 * Executes the command and returns whether it could be successfully parsed 00141 * or not (at the moment, always returns true). 00142 * 00143 */ 00144 IPC_HANDLER(command) { 00145 /* To get a properly terminated buffer, we copy 00146 * message_size bytes out of the buffer */ 00147 char *command = scalloc(message_size + 1); 00148 strncpy(command, (const char*)message, message_size); 00149 LOG("IPC: received: *%s*\n", command); 00150 const char *reply = parse_cmd((const char*)command); 00151 free(command); 00152 00153 /* If no reply was provided, we just use the default success message */ 00154 if (reply == NULL) 00155 reply = "{\"success\":true}"; 00156 ipc_send_message(fd, (const unsigned char*)reply, 00157 I3_IPC_REPLY_TYPE_COMMAND, strlen(reply)); 00158 } 00159 00160 static void dump_rect(yajl_gen gen, const char *name, Rect r) { 00161 ystr(name); 00162 y(map_open); 00163 ystr("x"); 00164 y(integer, r.x); 00165 ystr("y"); 00166 y(integer, r.y); 00167 ystr("width"); 00168 y(integer, r.width); 00169 ystr("height"); 00170 y(integer, r.height); 00171 y(map_close); 00172 } 00173 00174 void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { 00175 y(map_open); 00176 ystr("id"); 00177 y(integer, (long int)con); 00178 00179 ystr("type"); 00180 y(integer, con->type); 00181 00182 ystr("orientation"); 00183 switch (con->orientation) { 00184 case NO_ORIENTATION: 00185 ystr("none"); 00186 break; 00187 case HORIZ: 00188 ystr("horizontal"); 00189 break; 00190 case VERT: 00191 ystr("vertical"); 00192 break; 00193 } 00194 00195 ystr("percent"); 00196 if (con->percent == 0.0) 00197 y(null); 00198 else y(double, con->percent); 00199 00200 ystr("urgent"); 00201 y(bool, con->urgent); 00202 00203 ystr("focused"); 00204 y(bool, (con == focused)); 00205 00206 ystr("layout"); 00207 switch (con->layout) { 00208 case L_DEFAULT: 00209 ystr("default"); 00210 break; 00211 case L_STACKED: 00212 ystr("stacked"); 00213 break; 00214 case L_TABBED: 00215 ystr("tabbed"); 00216 break; 00217 case L_DOCKAREA: 00218 ystr("dockarea"); 00219 break; 00220 case L_OUTPUT: 00221 ystr("output"); 00222 break; 00223 } 00224 00225 ystr("border"); 00226 switch (con->border_style) { 00227 case BS_NORMAL: 00228 ystr("normal"); 00229 break; 00230 case BS_NONE: 00231 ystr("none"); 00232 break; 00233 case BS_1PIXEL: 00234 ystr("1pixel"); 00235 break; 00236 } 00237 00238 dump_rect(gen, "rect", con->rect); 00239 dump_rect(gen, "window_rect", con->window_rect); 00240 dump_rect(gen, "geometry", con->geometry); 00241 00242 ystr("name"); 00243 ystr(con->name); 00244 00245 if (con->type == CT_WORKSPACE) { 00246 ystr("num"); 00247 y(integer, con->num); 00248 } 00249 00250 ystr("window"); 00251 if (con->window) 00252 y(integer, con->window->id); 00253 else y(null); 00254 00255 ystr("nodes"); 00256 y(array_open); 00257 Con *node; 00258 if (con->type != CT_DOCKAREA || !inplace_restart) { 00259 TAILQ_FOREACH(node, &(con->nodes_head), nodes) { 00260 dump_node(gen, node, inplace_restart); 00261 } 00262 } 00263 y(array_close); 00264 00265 ystr("floating_nodes"); 00266 y(array_open); 00267 TAILQ_FOREACH(node, &(con->floating_head), floating_windows) { 00268 dump_node(gen, node, inplace_restart); 00269 } 00270 y(array_close); 00271 00272 ystr("focus"); 00273 y(array_open); 00274 TAILQ_FOREACH(node, &(con->focus_head), nodes) { 00275 y(integer, (long int)node); 00276 } 00277 y(array_close); 00278 00279 ystr("fullscreen_mode"); 00280 y(integer, con->fullscreen_mode); 00281 00282 ystr("swallows"); 00283 y(array_open); 00284 Match *match; 00285 TAILQ_FOREACH(match, &(con->swallow_head), matches) { 00286 if (match->dock != -1) { 00287 y(map_open); 00288 ystr("dock"); 00289 y(integer, match->dock); 00290 ystr("insert_where"); 00291 y(integer, match->insert_where); 00292 y(map_close); 00293 } 00294 00295 /* TODO: the other swallow keys */ 00296 } 00297 00298 if (inplace_restart) { 00299 if (con->window != NULL) { 00300 y(map_open); 00301 ystr("id"); 00302 y(integer, con->window->id); 00303 y(map_close); 00304 } 00305 } 00306 y(array_close); 00307 00308 y(map_close); 00309 } 00310 00311 IPC_HANDLER(tree) { 00312 setlocale(LC_NUMERIC, "C"); 00313 #if YAJL_MAJOR >= 2 00314 yajl_gen gen = yajl_gen_alloc(NULL); 00315 #else 00316 yajl_gen gen = yajl_gen_alloc(NULL, NULL); 00317 #endif 00318 dump_node(gen, croot, false); 00319 setlocale(LC_NUMERIC, ""); 00320 00321 const unsigned char *payload; 00322 #if YAJL_MAJOR >= 2 00323 size_t length; 00324 #else 00325 unsigned int length; 00326 #endif 00327 y(get_buf, &payload, &length); 00328 00329 ipc_send_message(fd, payload, I3_IPC_REPLY_TYPE_TREE, length); 00330 y(free); 00331 } 00332 00333 /* 00334 * Formats the reply message for a GET_WORKSPACES request and sends it to the 00335 * client 00336 * 00337 */ 00338 IPC_HANDLER(get_workspaces) { 00339 #if YAJL_MAJOR >= 2 00340 yajl_gen gen = yajl_gen_alloc(NULL); 00341 #else 00342 yajl_gen gen = yajl_gen_alloc(NULL, NULL); 00343 #endif 00344 y(array_open); 00345 00346 Con *focused_ws = con_get_workspace(focused); 00347 00348 Con *output; 00349 TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { 00350 Con *ws; 00351 TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) { 00352 assert(ws->type == CT_WORKSPACE); 00353 y(map_open); 00354 00355 ystr("num"); 00356 if (ws->num == -1) 00357 y(null); 00358 else y(integer, ws->num); 00359 00360 ystr("name"); 00361 ystr(ws->name); 00362 00363 ystr("visible"); 00364 y(bool, workspace_is_visible(ws)); 00365 00366 ystr("focused"); 00367 y(bool, ws == focused_ws); 00368 00369 ystr("rect"); 00370 y(map_open); 00371 ystr("x"); 00372 y(integer, ws->rect.x); 00373 ystr("y"); 00374 y(integer, ws->rect.y); 00375 ystr("width"); 00376 y(integer, ws->rect.width); 00377 ystr("height"); 00378 y(integer, ws->rect.height); 00379 y(map_close); 00380 00381 ystr("output"); 00382 ystr(output->name); 00383 00384 ystr("urgent"); 00385 y(bool, ws->urgent); 00386 00387 y(map_close); 00388 } 00389 } 00390 00391 y(array_close); 00392 00393 const unsigned char *payload; 00394 #if YAJL_MAJOR >= 2 00395 size_t length; 00396 #else 00397 unsigned int length; 00398 #endif 00399 y(get_buf, &payload, &length); 00400 00401 ipc_send_message(fd, payload, I3_IPC_REPLY_TYPE_WORKSPACES, length); 00402 y(free); 00403 } 00404 00405 /* 00406 * Formats the reply message for a GET_OUTPUTS request and sends it to the 00407 * client 00408 * 00409 */ 00410 IPC_HANDLER(get_outputs) { 00411 #if YAJL_MAJOR >= 2 00412 yajl_gen gen = yajl_gen_alloc(NULL); 00413 #else 00414 yajl_gen gen = yajl_gen_alloc(NULL, NULL); 00415 #endif 00416 y(array_open); 00417 00418 Output *output; 00419 TAILQ_FOREACH(output, &outputs, outputs) { 00420 y(map_open); 00421 00422 ystr("name"); 00423 ystr(output->name); 00424 00425 ystr("active"); 00426 y(bool, output->active); 00427 00428 ystr("rect"); 00429 y(map_open); 00430 ystr("x"); 00431 y(integer, output->rect.x); 00432 ystr("y"); 00433 y(integer, output->rect.y); 00434 ystr("width"); 00435 y(integer, output->rect.width); 00436 ystr("height"); 00437 y(integer, output->rect.height); 00438 y(map_close); 00439 00440 ystr("current_workspace"); 00441 Con *ws = NULL; 00442 if (output->con && (ws = con_get_fullscreen_con(output->con, CF_OUTPUT))) 00443 ystr(ws->name); 00444 else y(null); 00445 00446 y(map_close); 00447 } 00448 00449 y(array_close); 00450 00451 const unsigned char *payload; 00452 #if YAJL_MAJOR >= 2 00453 size_t length; 00454 #else 00455 unsigned int length; 00456 #endif 00457 y(get_buf, &payload, &length); 00458 00459 ipc_send_message(fd, payload, I3_IPC_REPLY_TYPE_OUTPUTS, length); 00460 y(free); 00461 } 00462 00463 /* 00464 * Callback for the YAJL parser (will be called when a string is parsed). 00465 * 00466 */ 00467 #if YAJL_MAJOR < 2 00468 static int add_subscription(void *extra, const unsigned char *s, 00469 unsigned int len) { 00470 #else 00471 static int add_subscription(void *extra, const unsigned char *s, 00472 size_t len) { 00473 #endif 00474 ipc_client *client = extra; 00475 00476 DLOG("should add subscription to extra %p, sub %.*s\n", client, len, s); 00477 int event = client->num_events; 00478 00479 client->num_events++; 00480 client->events = realloc(client->events, client->num_events * sizeof(char*)); 00481 /* We copy the string because it is not null-terminated and strndup() 00482 * is missing on some BSD systems */ 00483 client->events[event] = scalloc(len+1); 00484 memcpy(client->events[event], s, len); 00485 00486 DLOG("client is now subscribed to:\n"); 00487 for (int i = 0; i < client->num_events; i++) 00488 DLOG("event %s\n", client->events[i]); 00489 DLOG("(done)\n"); 00490 00491 return 1; 00492 } 00493 00494 /* 00495 * Subscribes this connection to the event types which were given as a JSON 00496 * serialized array in the payload field of the message. 00497 * 00498 */ 00499 IPC_HANDLER(subscribe) { 00500 yajl_handle p; 00501 yajl_callbacks callbacks; 00502 yajl_status stat; 00503 ipc_client *current, *client = NULL; 00504 00505 /* Search the ipc_client structure for this connection */ 00506 TAILQ_FOREACH(current, &all_clients, clients) { 00507 if (current->fd != fd) 00508 continue; 00509 00510 client = current; 00511 break; 00512 } 00513 00514 if (client == NULL) { 00515 ELOG("Could not find ipc_client data structure for fd %d\n", fd); 00516 return; 00517 } 00518 00519 /* Setup the JSON parser */ 00520 memset(&callbacks, 0, sizeof(yajl_callbacks)); 00521 callbacks.yajl_string = add_subscription; 00522 00523 #if YAJL_MAJOR >= 2 00524 p = yajl_alloc(&callbacks, NULL, (void*)client); 00525 #else 00526 p = yajl_alloc(&callbacks, NULL, NULL, (void*)client); 00527 #endif 00528 stat = yajl_parse(p, (const unsigned char*)message, message_size); 00529 if (stat != yajl_status_ok) { 00530 unsigned char *err; 00531 err = yajl_get_error(p, true, (const unsigned char*)message, 00532 message_size); 00533 ELOG("YAJL parse error: %s\n", err); 00534 yajl_free_error(p, err); 00535 00536 const char *reply = "{\"success\":false}"; 00537 ipc_send_message(fd, (const unsigned char*)reply, 00538 I3_IPC_REPLY_TYPE_SUBSCRIBE, strlen(reply)); 00539 yajl_free(p); 00540 return; 00541 } 00542 yajl_free(p); 00543 const char *reply = "{\"success\":true}"; 00544 ipc_send_message(fd, (const unsigned char*)reply, 00545 I3_IPC_REPLY_TYPE_SUBSCRIBE, strlen(reply)); 00546 } 00547 00548 /* The index of each callback function corresponds to the numeric 00549 * value of the message type (see include/i3/ipc.h) */ 00550 handler_t handlers[5] = { 00551 handle_command, 00552 handle_get_workspaces, 00553 handle_subscribe, 00554 handle_get_outputs, 00555 handle_tree 00556 }; 00557 00558 /* 00559 * Handler for activity on a client connection, receives a message from a 00560 * client. 00561 * 00562 * For now, the maximum message size is 2048. I’m not sure for what the 00563 * IPC interface will be used in the future, thus I’m not implementing a 00564 * mechanism for arbitrarily long messages, as it seems like overkill 00565 * at the moment. 00566 * 00567 */ 00568 static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) { 00569 char buf[2048]; 00570 int n = read(w->fd, buf, sizeof(buf)); 00571 00572 /* On error or an empty message, we close the connection */ 00573 if (n <= 0) { 00574 #if 0 00575 /* FIXME: I get these when closing a client socket, 00576 * therefore we just treat them as an error. Is this 00577 * correct? */ 00578 if (errno == EAGAIN || errno == EWOULDBLOCK) 00579 return; 00580 #endif 00581 00582 /* If not, there was some kind of error. We don’t bother 00583 * and close the connection */ 00584 close(w->fd); 00585 00586 /* Delete the client from the list of clients */ 00587 ipc_client *current; 00588 TAILQ_FOREACH(current, &all_clients, clients) { 00589 if (current->fd != w->fd) 00590 continue; 00591 00592 for (int i = 0; i < current->num_events; i++) 00593 free(current->events[i]); 00594 /* We can call TAILQ_REMOVE because we break out of the 00595 * TAILQ_FOREACH afterwards */ 00596 TAILQ_REMOVE(&all_clients, current, clients); 00597 break; 00598 } 00599 00600 ev_io_stop(EV_A_ w); 00601 00602 DLOG("IPC: client disconnected\n"); 00603 return; 00604 } 00605 00606 /* Terminate the message correctly */ 00607 buf[n] = '\0'; 00608 00609 /* Check if the message starts with the i3 IPC magic code */ 00610 if (n < strlen(I3_IPC_MAGIC)) { 00611 DLOG("IPC: message too short, ignoring\n"); 00612 return; 00613 } 00614 00615 if (strncmp(buf, I3_IPC_MAGIC, strlen(I3_IPC_MAGIC)) != 0) { 00616 DLOG("IPC: message does not start with the IPC magic\n"); 00617 return; 00618 } 00619 00620 uint8_t *message = (uint8_t*)buf; 00621 while (n > 0) { 00622 DLOG("IPC: n = %d\n", n); 00623 message += strlen(I3_IPC_MAGIC); 00624 n -= strlen(I3_IPC_MAGIC); 00625 00626 /* The next 32 bit after the magic are the message size */ 00627 uint32_t message_size; 00628 memcpy(&message_size, (uint32_t*)message, sizeof(uint32_t)); 00629 message += sizeof(uint32_t); 00630 n -= sizeof(uint32_t); 00631 00632 if (message_size > n) { 00633 DLOG("IPC: Either the message size was wrong or the message was not read completely, dropping\n"); 00634 return; 00635 } 00636 00637 /* The last 32 bits of the header are the message type */ 00638 uint32_t message_type; 00639 memcpy(&message_type, (uint32_t*)message, sizeof(uint32_t)); 00640 message += sizeof(uint32_t); 00641 n -= sizeof(uint32_t); 00642 00643 if (message_type >= (sizeof(handlers) / sizeof(handler_t))) 00644 DLOG("Unhandled message type: %d\n", message_type); 00645 else { 00646 handler_t h = handlers[message_type]; 00647 h(w->fd, message, n, message_size, message_type); 00648 } 00649 n -= message_size; 00650 message += message_size; 00651 } 00652 } 00653 00654 /* 00655 * Handler for activity on the listening socket, meaning that a new client 00656 * has just connected and we should accept() him. Sets up the event handler 00657 * for activity on the new connection and inserts the file descriptor into 00658 * the list of clients. 00659 * 00660 */ 00661 void ipc_new_client(EV_P_ struct ev_io *w, int revents) { 00662 struct sockaddr_un peer; 00663 socklen_t len = sizeof(struct sockaddr_un); 00664 int client; 00665 if ((client = accept(w->fd, (struct sockaddr*)&peer, &len)) < 0) { 00666 if (errno == EINTR) 00667 return; 00668 else perror("accept()"); 00669 return; 00670 } 00671 00672 set_nonblock(client); 00673 00674 struct ev_io *package = scalloc(sizeof(struct ev_io)); 00675 ev_io_init(package, ipc_receive_message, client, EV_READ); 00676 ev_io_start(EV_A_ package); 00677 00678 DLOG("IPC: new client connected\n"); 00679 00680 ipc_client *new = scalloc(sizeof(ipc_client)); 00681 new->fd = client; 00682 00683 TAILQ_INSERT_TAIL(&all_clients, new, clients); 00684 } 00685 00686 /* 00687 * Creates the UNIX domain socket at the given path, sets it to non-blocking 00688 * mode, bind()s and listen()s on it. 00689 * 00690 */ 00691 int ipc_create_socket(const char *filename) { 00692 int sockfd; 00693 00694 FREE(current_socketpath); 00695 00696 char *resolved = resolve_tilde(filename); 00697 DLOG("Creating IPC-socket at %s\n", resolved); 00698 char *copy = sstrdup(resolved); 00699 const char *dir = dirname(copy); 00700 if (!path_exists(dir)) 00701 mkdirp(dir); 00702 free(copy); 00703 00704 /* Unlink the unix domain socket before */ 00705 unlink(resolved); 00706 00707 if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) { 00708 perror("socket()"); 00709 free(resolved); 00710 return -1; 00711 } 00712 00713 (void)fcntl(sockfd, F_SETFD, FD_CLOEXEC); 00714 00715 struct sockaddr_un addr; 00716 memset(&addr, 0, sizeof(struct sockaddr_un)); 00717 addr.sun_family = AF_LOCAL; 00718 strncpy(addr.sun_path, resolved, sizeof(addr.sun_path) - 1); 00719 if (bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_un)) < 0) { 00720 perror("bind()"); 00721 free(resolved); 00722 return -1; 00723 } 00724 00725 set_nonblock(sockfd); 00726 00727 if (listen(sockfd, 5) < 0) { 00728 perror("listen()"); 00729 free(resolved); 00730 return -1; 00731 } 00732 00733 current_socketpath = resolved; 00734 return sockfd; 00735 }