X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fipc.c;h=3022d5b399bd99d6f382a33e9d533e0f2cb85b4d;hb=37b879f4adcee8c7ab360512487ab617211a4e2a;hp=be6009788c47f9bfec06574ff94ff6eacd9d9d3c;hpb=9eb255d5faad2151b91401bc7c451a191e43a2fe;p=i3%2Fi3 diff --git a/src/ipc.c b/src/ipc.c index be600978..3022d5b3 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "ipc.c" /* * vim:ts=4:sw=4:expandtab * @@ -10,8 +8,10 @@ * */ #include "all.h" + #include "yajl_utils.h" +#include #include #include #include @@ -22,7 +22,8 @@ char *current_socketpath = NULL; -TAILQ_HEAD(ipc_client_head, ipc_client) all_clients = TAILQ_HEAD_INITIALIZER(all_clients); +TAILQ_HEAD(ipc_client_head, ipc_client) +all_clients = TAILQ_HEAD_INITIALIZER(all_clients); /* * Puts the given socket file descriptor into non-blocking mode or dies if @@ -37,6 +38,108 @@ static void set_nonblock(int sockfd) { err(-1, "Could not set O_NONBLOCK"); } +/* + * Given a message and a message type, create the corresponding header, merge it + * with the message and append it to the given client's output buffer. + * + */ +static void append_payload(ipc_client *client, uint32_t message_type, const char *payload) { + const size_t size = strlen(payload); + const i3_ipc_header_t header = { + .magic = {'i', '3', '-', 'i', 'p', 'c'}, + .size = size, + .type = message_type}; + const size_t header_size = sizeof(i3_ipc_header_t); + const size_t message_size = header_size + size; + + client->buffer = srealloc(client->buffer, client->buffer_size + message_size); + memcpy(client->buffer + client->buffer_size, ((void *)&header), header_size); + memcpy(client->buffer + client->buffer_size + header_size, payload, size); + client->buffer_size += message_size; +} + +static void free_ipc_client(ipc_client *client) { + close(client->fd); + + ev_io_stop(main_loop, client->callback); + FREE(client->callback); + if (client->timeout) { + ev_timer_stop(main_loop, client->timeout); + FREE(client->timeout); + } + + free(client->buffer); + + for (int i = 0; i < client->num_events; i++) { + free(client->events[i]); + } + free(client->events); + TAILQ_REMOVE(&all_clients, client, clients); + free(client); +} + +static void ipc_client_timeout(EV_P_ ev_timer *w, int revents); +static void ipc_socket_writeable_cb(EV_P_ struct ev_io *w, int revents); + +static ev_tstamp kill_timeout = 10.0; + +void ipc_set_kill_timeout(ev_tstamp new) { + kill_timeout = new; +} + +/* + * Try to write the contents of the pending buffer to the client's subscription + * socket. Will set, reset or clear the timeout and io callbacks depending on + * the result of the write operation. + * + */ +static void ipc_push_pending(ipc_client *client) { + const ssize_t result = writeall_nonblock(client->fd, client->buffer, client->buffer_size); + if (result < 0) { + return; + } + + if ((size_t)result == client->buffer_size) { + /* Everything was written successfully: clear the timer and stop the io + * callback. */ + FREE(client->buffer); + client->buffer_size = 0; + if (client->timeout) { + ev_timer_stop(main_loop, client->timeout); + FREE(client->timeout); + } + ev_io_stop(main_loop, client->callback); + return; + } + + /* Otherwise, make sure that the io callback is enabled and create a new + * timer if needed. */ + ev_io_start(main_loop, client->callback); + + if (!client->timeout) { + struct ev_timer *timeout = scalloc(1, sizeof(struct ev_timer)); + ev_timer_init(timeout, ipc_client_timeout, kill_timeout, 0.); + timeout->data = client; + client->timeout = timeout; + ev_set_priority(timeout, EV_MINPRI); + ev_timer_start(main_loop, client->timeout); + } else if (result > 0) { + /* Keep the old timeout when nothing is written. Otherwise, we would + * keep a dead connection by continuously renewing its timeouts. */ + ev_timer_stop(main_loop, client->timeout); + ev_timer_set(client->timeout, kill_timeout, 0.0); + ev_timer_start(main_loop, client->timeout); + } + if (result == 0) { + return; + } + + /* Shift the buffer to the left and reduce the allocated space. */ + client->buffer_size -= (size_t)result; + memmove(client->buffer, client->buffer + result, client->buffer_size); + client->buffer = srealloc(client->buffer, client->buffer_size); +} + /* * Sends the specified event to all IPC clients which are currently connected * and subscribed to this kind of event. @@ -56,23 +159,53 @@ void ipc_send_event(const char *event, uint32_t message_type, const char *payloa if (!interested) continue; - ipc_send_message(current->fd, strlen(payload), message_type, (const uint8_t *)payload); + const bool push_now = (current->buffer_size == 0); + append_payload(current, message_type, payload); + if (push_now) { + ipc_push_pending(current); + } + } +} + +/* + * For shutdown events, we send the reason for the shutdown. + */ +static void ipc_send_shutdown_event(shutdown_reason_t reason) { + yajl_gen gen = ygenalloc(); + y(map_open); + + ystr("change"); + + if (reason == SHUTDOWN_REASON_RESTART) { + ystr("restart"); + } else if (reason == SHUTDOWN_REASON_EXIT) { + ystr("exit"); } + + y(map_close); + + const unsigned char *payload; + ylength length; + + y(get_buf, &payload, &length); + ipc_send_event("shutdown", I3_IPC_EVENT_SHUTDOWN, (const char *)payload); + + y(free); } /* - * Calls shutdown() on each socket and closes it. This function to be called + * Calls shutdown() on each socket and closes it. This function is to be called * when exiting or restarting only! * */ -void ipc_shutdown(void) { +void ipc_shutdown(shutdown_reason_t reason) { + ipc_send_shutdown_event(reason); + ipc_client *current; while (!TAILQ_EMPTY(&all_clients)) { current = TAILQ_FIRST(&all_clients); shutdown(current->fd, SHUT_RDWR); - close(current->fd); - TAILQ_REMOVE(&all_clients, current, clients); - free(current); + free_ipc_client(current); } } @@ -81,10 +214,10 @@ void ipc_shutdown(void) { * or not (at the moment, always returns true). * */ -IPC_HANDLER(command) { +IPC_HANDLER(run_command) { /* To get a properly terminated buffer, we copy * message_size bytes out of the buffer */ - char *command = scalloc(message_size + 1); + char *command = scalloc(message_size + 1, 1); strncpy(command, (const char *)message, message_size); LOG("IPC: received: *%s*\n", command); yajl_gen gen = yajl_gen_alloc(NULL); @@ -121,56 +254,92 @@ static void dump_rect(yajl_gen gen, const char *name, Rect r) { y(map_close); } -static void dump_binding(yajl_gen gen, Binding *bind) { - y(map_open); - ystr("input_code"); - y(integer, bind->keycode); - - ystr("input_type"); - ystr((const char *)(bind->input_type == B_KEYBOARD ? "keyboard" : "mouse")); - - ystr("symbol"); - if (bind->symbol == NULL) - y(null); - else - ystr(bind->symbol); - - ystr("command"); - ystr(bind->command); - - ystr("mods"); +static void dump_event_state_mask(yajl_gen gen, Binding *bind) { y(array_open); - for (int i = 0; i < 8; i++) { - if (bind->mods & (1 << i)) { + for (int i = 0; i < 20; i++) { + if (bind->event_state_mask & (1 << i)) { switch (1 << i) { - case XCB_MOD_MASK_SHIFT: + case XCB_KEY_BUT_MASK_SHIFT: ystr("shift"); break; - case XCB_MOD_MASK_LOCK: + case XCB_KEY_BUT_MASK_LOCK: ystr("lock"); break; - case XCB_MOD_MASK_CONTROL: + case XCB_KEY_BUT_MASK_CONTROL: ystr("ctrl"); break; - case XCB_MOD_MASK_1: + case XCB_KEY_BUT_MASK_MOD_1: ystr("Mod1"); break; - case XCB_MOD_MASK_2: + case XCB_KEY_BUT_MASK_MOD_2: ystr("Mod2"); break; - case XCB_MOD_MASK_3: + case XCB_KEY_BUT_MASK_MOD_3: ystr("Mod3"); break; - case XCB_MOD_MASK_4: + case XCB_KEY_BUT_MASK_MOD_4: ystr("Mod4"); break; - case XCB_MOD_MASK_5: + case XCB_KEY_BUT_MASK_MOD_5: ystr("Mod5"); break; + case XCB_KEY_BUT_MASK_BUTTON_1: + ystr("Button1"); + break; + case XCB_KEY_BUT_MASK_BUTTON_2: + ystr("Button2"); + break; + case XCB_KEY_BUT_MASK_BUTTON_3: + ystr("Button3"); + break; + case XCB_KEY_BUT_MASK_BUTTON_4: + ystr("Button4"); + break; + case XCB_KEY_BUT_MASK_BUTTON_5: + ystr("Button5"); + break; + case (I3_XKB_GROUP_MASK_1 << 16): + ystr("Group1"); + break; + case (I3_XKB_GROUP_MASK_2 << 16): + ystr("Group2"); + break; + case (I3_XKB_GROUP_MASK_3 << 16): + ystr("Group3"); + break; + case (I3_XKB_GROUP_MASK_4 << 16): + ystr("Group4"); + break; } } } y(array_close); +} + +static void dump_binding(yajl_gen gen, Binding *bind) { + y(map_open); + ystr("input_code"); + y(integer, bind->keycode); + + ystr("input_type"); + ystr((const char *)(bind->input_type == B_KEYBOARD ? "keyboard" : "mouse")); + + ystr("symbol"); + if (bind->symbol == NULL) + y(null); + else + ystr(bind->symbol); + + ystr("command"); + ystr(bind->command); + + // This key is only provided for compatibility, new programs should use + // event_state_mask instead. + ystr("mods"); + dump_event_state_mask(gen, bind); + + ystr("event_state_mask"); + dump_event_state_mask(gen, bind); y(map_close); } @@ -178,7 +347,7 @@ static void dump_binding(yajl_gen gen, Binding *bind) { void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { y(map_open); ystr("id"); - y(integer, (long int)con); + y(integer, (uintptr_t)con); ystr("type"); switch (con->type) { @@ -200,10 +369,6 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { case CT_DOCKAREA: ystr("dockarea"); break; - default: - DLOG("About to dump unknown container type=%d. This is a bug.\n", con->type); - assert(false); - break; } /* provided for backwards compatibility only. */ @@ -239,14 +404,26 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { ystr("urgent"); y(bool, con->urgent); - if (con->mark != NULL) { - ystr("mark"); - ystr(con->mark); + if (!TAILQ_EMPTY(&(con->marks_head))) { + ystr("marks"); + y(array_open); + + mark_t *mark; + TAILQ_FOREACH(mark, &(con->marks_head), marks) { + ystr(mark->name); + } + + y(array_close); } ystr("focused"); y(bool, (con == focused)); + if (con->type != CT_ROOT && con->type != CT_OUTPUT) { + ystr("output"); + ystr(con_get_output(con)->name); + } + ystr("layout"); switch (con->layout) { case L_DEFAULT: @@ -329,6 +506,11 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { else y(null); + if (con->title_format != NULL) { + ystr("title_format"); + ystr(con->title_format); + } + if (con->type == CT_WORKSPACE) { ystr("num"); y(integer, con->num); @@ -393,13 +575,16 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { ystr("focus"); y(array_open); TAILQ_FOREACH(node, &(con->focus_head), focused) { - y(integer, (long int)node); + y(integer, (uintptr_t)node); } y(array_close); ystr("fullscreen_mode"); y(integer, con->fullscreen_mode); + ystr("sticky"); + y(bool, con->sticky); + ystr("floating"); switch (con->floating) { case FLOATING_AUTO_OFF: @@ -425,7 +610,7 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { if (match->restart_mode) continue; y(map_open); - if (match->dock != -1) { + if (match->dock != M_DONTCHECK) { ystr("dock"); y(integer, match->dock); ystr("insert_where"); @@ -484,6 +669,8 @@ static void dump_bar_bindings(yajl_gen gen, Barconfig *config) { y(integer, current->input_code); ystr("command"); ystr(current->command); + ystr("release"); + y(bool, current->release == B_UPON_KEYRELEASE); y(map_close); } @@ -491,6 +678,15 @@ static void dump_bar_bindings(yajl_gen gen, Barconfig *config) { y(array_close); } +static char *canonicalize_output_name(char *name) { + /* Do not canonicalize special output names. */ + if (strcasecmp(name, "primary") == 0) { + return name; + } + Output *output = get_output_by_name(name, false); + return output ? output_primary_name(output) : name; +} + static void dump_bar_config(yajl_gen gen, Barconfig *config) { y(map_open); @@ -500,8 +696,25 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) { if (config->num_outputs > 0) { ystr("outputs"); y(array_open); - for (int c = 0; c < config->num_outputs; c++) - ystr(config->outputs[c]); + for (int c = 0; c < config->num_outputs; c++) { + /* Convert monitor names (RandR ≥ 1.5) or output names + * (RandR < 1.5) into monitor names. This way, existing + * configs which use output names transparently keep + * working. */ + ystr(canonicalize_output_name(config->outputs[c])); + } + y(array_close); + } + + if (!TAILQ_EMPTY(&(config->tray_outputs))) { + ystr("tray_outputs"); + y(array_open); + + struct tray_output_t *tray_output; + TAILQ_FOREACH(tray_output, &(config->tray_outputs), tray_outputs) { + ystr(canonicalize_output_name(tray_output->output)); + } + y(array_close); } @@ -513,7 +726,9 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) { } \ } while (0) - YSTR_IF_SET(tray_output); + ystr("tray_padding"); + y(integer, config->tray_padding); + YSTR_IF_SET(socket_path); ystr("mode"); @@ -542,34 +757,7 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) { } ystr("modifier"); - switch (config->modifier) { - case M_CONTROL: - ystr("ctrl"); - break; - case M_SHIFT: - ystr("shift"); - break; - case M_MOD1: - ystr("Mod1"); - break; - case M_MOD2: - ystr("Mod2"); - break; - case M_MOD3: - ystr("Mod3"); - break; - /* - case M_MOD4: - ystr("Mod4"); - break; - */ - case M_MOD5: - ystr("Mod5"); - break; - default: - ystr("Mod4"); - break; - } + y(integer, config->modifier); dump_bar_bindings(gen, config); @@ -593,6 +781,9 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) { ystr("strip_workspace_numbers"); y(bool, config->strip_workspace_numbers); + ystr("strip_workspace_name"); + y(bool, config->strip_workspace_name); + ystr("binding_mode_indicator"); y(bool, !config->hide_binding_mode_indicator); @@ -613,6 +804,9 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) { YSTR_IF_SET(background); YSTR_IF_SET(statusline); YSTR_IF_SET(separator); + YSTR_IF_SET(focused_background); + YSTR_IF_SET(focused_statusline); + YSTR_IF_SET(focused_separator); YSTR_IF_SET(focused_workspace_border); YSTR_IF_SET(focused_workspace_bg); YSTR_IF_SET(focused_workspace_text); @@ -726,7 +920,7 @@ IPC_HANDLER(get_outputs) { y(map_open); ystr("name"); - ystr(output->name); + ystr(output_primary_name(output)); ystr("active"); y(bool, output->active); @@ -776,9 +970,12 @@ IPC_HANDLER(get_marks) { y(array_open); Con *con; - TAILQ_FOREACH(con, &all_cons, all_cons) - if (con->mark != NULL) - ystr(con->mark); + TAILQ_FOREACH(con, &all_cons, all_cons) { + mark_t *mark; + TAILQ_FOREACH(mark, &(con->marks_head), marks) { + ystr(mark->name); + } + } y(array_close); @@ -810,6 +1007,9 @@ IPC_HANDLER(get_version) { ystr("human_readable"); ystr(i3_version); + ystr("loaded_config_file_name"); + ystr(current_configpath); + y(map_close); const unsigned char *payload; @@ -848,8 +1048,8 @@ IPC_HANDLER(get_bar_config) { /* To get a properly terminated buffer, we copy * message_size bytes out of the buffer */ - char *bar_id = scalloc(message_size + 1); - strncpy(bar_id, (const char *)message, message_size); + char *bar_id = NULL; + sasprintf(&bar_id, "%.*s", message_size, message); LOG("IPC: looking for config for bar ID \"%s\"\n", bar_id); Barconfig *current, *config = NULL; TAILQ_FOREACH(current, &barconfigs, configs) { @@ -859,6 +1059,7 @@ IPC_HANDLER(get_bar_config) { config = current; break; } + free(bar_id); if (!config) { /* If we did not find a config for the given ID, the reply will contain @@ -881,6 +1082,28 @@ IPC_HANDLER(get_bar_config) { y(free); } +/* + * Returns a list of configured binding modes + * + */ +IPC_HANDLER(get_binding_modes) { + yajl_gen gen = ygenalloc(); + + y(array_open); + struct Mode *mode; + SLIST_FOREACH(mode, &modes, modes) { + ystr(mode->name); + } + y(array_close); + + const unsigned char *payload; + ylength length; + y(get_buf, &payload, &length); + + ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_BINDING_MODES, payload); + y(free); +} + /* * Callback for the YAJL parser (will be called when a string is parsed). * @@ -893,15 +1116,16 @@ static int add_subscription(void *extra, const unsigned char *s, int event = client->num_events; client->num_events++; - client->events = realloc(client->events, client->num_events * sizeof(char *)); + client->events = srealloc(client->events, client->num_events * sizeof(char *)); /* We copy the string because it is not null-terminated and strndup() * is missing on some BSD systems */ - client->events[event] = scalloc(len + 1); + client->events[event] = scalloc(len + 1, 1); memcpy(client->events[event], s, len); DLOG("client is now subscribed to:\n"); - for (int i = 0; i < client->num_events; i++) + for (int i = 0; i < client->num_events; i++) { DLOG("event %s\n", client->events[i]); + } DLOG("(done)\n"); return 1; @@ -953,12 +1177,140 @@ IPC_HANDLER(subscribe) { yajl_free(p); const char *reply = "{\"success\":true}"; ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (const uint8_t *)reply); + + if (client->first_tick_sent) { + return; + } + + bool is_tick = false; + for (int i = 0; i < client->num_events; i++) { + if (strcmp(client->events[i], "tick") == 0) { + is_tick = true; + break; + } + } + if (!is_tick) { + return; + } + + client->first_tick_sent = true; + const char *payload = "{\"first\":true,\"payload\":\"\"}"; + ipc_send_message(client->fd, strlen(payload), I3_IPC_EVENT_TICK, (const uint8_t *)payload); +} + +/* + * Returns the raw last loaded i3 configuration file contents. + */ +IPC_HANDLER(get_config) { + yajl_gen gen = ygenalloc(); + + y(map_open); + + ystr("config"); + ystr(current_config); + + y(map_close); + + const unsigned char *payload; + ylength length; + y(get_buf, &payload, &length); + + ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_CONFIG, payload); + y(free); +} + +/* + * Sends the tick event from the message payload to subscribers. Establishes a + * synchronization point in event-related tests. + */ +IPC_HANDLER(send_tick) { + yajl_gen gen = ygenalloc(); + + y(map_open); + + ystr("first"); + y(bool, false); + + ystr("payload"); + yajl_gen_string(gen, (unsigned char *)message, message_size); + + y(map_close); + + const unsigned char *payload; + ylength length; + y(get_buf, &payload, &length); + + ipc_send_event("tick", I3_IPC_EVENT_TICK, (const char *)payload); + y(free); + + const char *reply = "{\"success\":true}"; + ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_TICK, (const uint8_t *)reply); + DLOG("Sent tick event\n"); +} + +struct sync_state { + char *last_key; + uint32_t rnd; + xcb_window_t window; +}; + +static int _sync_json_key(void *extra, const unsigned char *val, size_t len) { + struct sync_state *state = extra; + FREE(state->last_key); + state->last_key = scalloc(len + 1, 1); + memcpy(state->last_key, val, len); + return 1; +} + +static int _sync_json_int(void *extra, long long val) { + struct sync_state *state = extra; + if (strcasecmp(state->last_key, "rnd") == 0) { + state->rnd = val; + } else if (strcasecmp(state->last_key, "window") == 0) { + state->window = (xcb_window_t)val; + } + return 1; +} + +IPC_HANDLER(sync) { + yajl_handle p; + yajl_status stat; + + /* Setup the JSON parser */ + static yajl_callbacks callbacks = { + .yajl_map_key = _sync_json_key, + .yajl_integer = _sync_json_int, + }; + + struct sync_state state; + memset(&state, '\0', sizeof(struct sync_state)); + p = yalloc(&callbacks, (void *)&state); + stat = yajl_parse(p, (const unsigned char *)message, message_size); + FREE(state.last_key); + if (stat != yajl_status_ok) { + unsigned char *err; + err = yajl_get_error(p, true, (const unsigned char *)message, + message_size); + ELOG("YAJL parse error: %s\n", err); + yajl_free_error(p, err); + + const char *reply = "{\"success\":false}"; + ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SYNC, (const uint8_t *)reply); + yajl_free(p); + return; + } + yajl_free(p); + + DLOG("received IPC sync request (rnd = %d, window = 0x%08x)\n", state.rnd, state.window); + sync_respond(state.window, state.rnd); + const char *reply = "{\"success\":true}"; + ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SYNC, (const uint8_t *)reply); } /* The index of each callback function corresponds to the numeric * value of the message type (see include/i3/ipc.h) */ -handler_t handlers[8] = { - handle_command, +handler_t handlers[12] = { + handle_run_command, handle_get_workspaces, handle_subscribe, handle_get_outputs, @@ -966,6 +1318,10 @@ handler_t handlers[8] = { handle_get_marks, handle_get_bar_config, handle_get_version, + handle_get_binding_modes, + handle_get_config, + handle_send_tick, + handle_sync, }; /* @@ -992,24 +1348,21 @@ static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) { return; } - /* If not, there was some kind of error. We don’t bother - * and close the connection */ - close(w->fd); - - /* Delete the client from the list of clients */ + /* If not, there was some kind of error. We don’t bother and close the + * connection. Delete the client from the list of clients. */ + bool closed = false; ipc_client *current; TAILQ_FOREACH(current, &all_clients, clients) { if (current->fd != w->fd) continue; - for (int i = 0; i < current->num_events; i++) - free(current->events[i]); - /* We can call TAILQ_REMOVE because we break out of the - * TAILQ_FOREACH afterwards */ - TAILQ_REMOVE(&all_clients, current, clients); - free(current); + free_ipc_client(current); + closed = true; break; } + if (!closed) { + close(w->fd); + } ev_io_stop(EV_A_ w); free(w); @@ -1029,6 +1382,62 @@ static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) { FREE(message); } +static void ipc_client_timeout(EV_P_ ev_timer *w, int revents) { + /* No need to be polite and check for writeability, the other callback would + * have been called by now. */ + ipc_client *client = (ipc_client *)w->data; + + char *cmdline = NULL; +#if defined(__linux__) && defined(SO_PEERCRED) + struct ucred peercred; + socklen_t so_len = sizeof(peercred); + if (getsockopt(client->fd, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0) { + goto end; + } + char *exepath; + sasprintf(&exepath, "/proc/%d/cmdline", peercred.pid); + + int fd = open(exepath, O_RDONLY); + free(exepath); + if (fd == -1) { + goto end; + } + char buf[512] = {'\0'}; /* cut off cmdline for the error message. */ + const ssize_t n = read(fd, buf, sizeof(buf)); + close(fd); + if (n < 0) { + goto end; + } + for (char *walk = buf; walk < buf + n - 1; walk++) { + if (*walk == '\0') { + *walk = ' '; + } + } + cmdline = buf; + + if (cmdline) { + ELOG("client %p with pid %d and cmdline '%s' on fd %d timed out, killing\n", client, peercred.pid, cmdline, client->fd); + } + +end: +#endif + if (!cmdline) { + ELOG("client %p on fd %d timed out, killing\n", client, client->fd); + } + + free_ipc_client(client); +} + +static void ipc_socket_writeable_cb(EV_P_ ev_io *w, int revents) { + DLOG("fd %d writeable\n", w->fd); + ipc_client *client = (ipc_client *)w->data; + + /* If this callback is called then there should be a corresponding active + * timer. */ + assert(client->timeout != NULL); + ipc_push_pending(client); +} + /* * Handler for activity on the listening socket, meaning that a new client * has just connected and we should accept() him. Sets up the event handler @@ -1053,14 +1462,20 @@ void ipc_new_client(EV_P_ struct ev_io *w, int revents) { set_nonblock(client); - struct ev_io *package = scalloc(sizeof(struct ev_io)); + struct ev_io *package = scalloc(1, sizeof(struct ev_io)); ev_io_init(package, ipc_receive_message, client, EV_READ); ev_io_start(EV_A_ package); + ipc_client *new = scalloc(1, sizeof(ipc_client)); + + package = scalloc(1, sizeof(struct ev_io)); + package->data = new; + ev_io_init(package, ipc_socket_writeable_cb, client, EV_WRITE); + DLOG("IPC: new client connected on fd %d\n", w->fd); - ipc_client *new = scalloc(sizeof(ipc_client)); new->fd = client; + new->callback = package; TAILQ_INSERT_TAIL(&all_clients, new, clients); } @@ -1080,7 +1495,7 @@ int ipc_create_socket(const char *filename) { char *copy = sstrdup(resolved); const char *dir = dirname(copy); if (!path_exists(dir)) - mkdirp(dir); + mkdirp(dir, DEFAULT_DIR_MODE); free(copy); /* Unlink the unix domain socket before */