]> git.sur5r.net Git - i3/i3/blobdiff - src/ipc.c
i3bar: make modifier behave like floating_modifier
[i3/i3] / src / ipc.c
index 52f7db2efe432c50fedecefb0940b8404b8ef2b2..eebb576ef4cefa8899d04a04f92b179475f95026 100644 (file)
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -1,17 +1,17 @@
-#undef I3__FILE__
-#define I3__FILE__ "ipc.c"
 /*
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * ipc.c: UNIX domain socket IPC (initialization, client handling, protocol).
  *
  */
 #include "all.h"
+
 #include "yajl_utils.h"
 
+#include <stdint.h>
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <fcntl.h>
@@ -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,36 +38,6 @@ static void set_nonblock(int sockfd) {
         err(-1, "Could not set O_NONBLOCK");
 }
 
-/*
- * Emulates mkdir -p (creates any missing folders)
- *
- */
-bool mkdirp(const char *path) {
-    if (mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0)
-        return true;
-    if (errno != ENOENT) {
-        ELOG("mkdir(%s) failed: %s\n", path, strerror(errno));
-        return false;
-    }
-    char *copy = sstrdup(path);
-    /* strip trailing slashes, if any */
-    while (copy[strlen(copy) - 1] == '/')
-        copy[strlen(copy) - 1] = '\0';
-
-    char *sep = strrchr(copy, '/');
-    if (sep == NULL) {
-        FREE(copy);
-        return false;
-    }
-    *sep = '\0';
-    bool result = false;
-    if (mkdirp(copy))
-        result = mkdirp(path);
-    free(copy);
-
-    return result;
-}
-
 /*
  * Sends the specified event to all IPC clients which are currently connected
  * and subscribed to this kind of event.
@@ -91,16 +62,47 @@ void ipc_send_event(const char *event, uint32_t message_type, const char *payloa
 }
 
 /*
- * Calls shutdown() on each socket and closes it. This function to be called
+ * 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 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);
+        for (int i = 0; i < current->num_events; i++)
+            free(current->events[i]);
+        free(current->events);
         TAILQ_REMOVE(&all_clients, current, clients);
         free(current);
     }
@@ -111,10 +113,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);
@@ -151,56 +153,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);
 }
@@ -208,7 +246,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) {
@@ -230,10 +268,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. */
@@ -269,14 +303,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:
@@ -359,6 +405,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);
@@ -423,13 +474,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:
@@ -455,7 +509,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");
@@ -499,6 +553,39 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
     y(map_close);
 }
 
+static void dump_bar_bindings(yajl_gen gen, Barconfig *config) {
+    if (TAILQ_EMPTY(&(config->bar_bindings)))
+        return;
+
+    ystr("bindings");
+    y(array_open);
+
+    struct Barbinding *current;
+    TAILQ_FOREACH(current, &(config->bar_bindings), bindings) {
+        y(map_open);
+
+        ystr("input_code");
+        y(integer, current->input_code);
+        ystr("command");
+        ystr(current->command);
+        ystr("release");
+        y(bool, current->release == B_UPON_KEYRELEASE);
+
+        y(map_close);
+    }
+
+    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);
 
@@ -508,8 +595,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);
     }
 
@@ -521,7 +625,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");
@@ -550,44 +656,9 @@ 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);
 
-    if (config->wheel_up_cmd) {
-        ystr("wheel_up_cmd");
-        ystr(config->wheel_up_cmd);
-    }
-
-    if (config->wheel_down_cmd) {
-        ystr("wheel_down_cmd");
-        ystr(config->wheel_down_cmd);
-    }
+    dump_bar_bindings(gen, config);
 
     ystr("position");
     if (config->position == P_BOTTOM)
@@ -609,6 +680,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);
 
@@ -629,6 +703,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);
@@ -641,6 +718,9 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
     YSTR_IF_SET(urgent_workspace_border);
     YSTR_IF_SET(urgent_workspace_bg);
     YSTR_IF_SET(urgent_workspace_text);
+    YSTR_IF_SET(binding_mode_border);
+    YSTR_IF_SET(binding_mode_bg);
+    YSTR_IF_SET(binding_mode_text);
     y(map_close);
 
     y(map_close);
@@ -739,7 +819,7 @@ IPC_HANDLER(get_outputs) {
         y(map_open);
 
         ystr("name");
-        ystr(output->name);
+        ystr(output_primary_name(output));
 
         ystr("active");
         y(bool, output->active);
@@ -789,9 +869,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);
 
@@ -821,7 +904,10 @@ IPC_HANDLER(get_version) {
     y(integer, PATCH_VERSION);
 
     ystr("human_readable");
-    ystr(I3_VERSION);
+    ystr(i3_version);
+
+    ystr("loaded_config_file_name");
+    ystr(current_configpath);
 
     y(map_close);
 
@@ -861,8 +947,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) {
@@ -872,6 +958,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
@@ -894,6 +981,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).
  *
@@ -906,15 +1015,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;
@@ -966,12 +1076,137 @@ 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("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,
@@ -979,6 +1214,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,
 };
 
 /*
@@ -1017,6 +1256,7 @@ static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) {
 
             for (int i = 0; i < current->num_events; i++)
                 free(current->events[i]);
+            free(current->events);
             /* We can call TAILQ_REMOVE because we break out of the
              * TAILQ_FOREACH afterwards */
             TAILQ_REMOVE(&all_clients, current, clients);
@@ -1066,13 +1306,13 @@ 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);
 
     DLOG("IPC: new client connected on fd %d\n", w->fd);
 
-    ipc_client *new = scalloc(sizeof(ipc_client));
+    ipc_client *new = scalloc(1, sizeof(ipc_client));
     new->fd = client;
 
     TAILQ_INSERT_TAIL(&all_clients, new, clients);
@@ -1093,7 +1333,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 */