fix /dev/shm permissions, this time for real
[project/procd.git] / plug / hotplug.c
index 075062b..83ddc2b 100644 (file)
 #include <linux/types.h>
 #include <linux/netlink.h>
 
+#include <libubox/avl-cmp.h>
 #include <libubox/blobmsg_json.h>
 #include <libubox/json_script.h>
 #include <libubox/uloop.h>
+#include <json-c/json.h>
 
 #include <fcntl.h>
 #include <unistd.h>
@@ -42,7 +44,20 @@ struct cmd_queue {
        void (*handler)(struct blob_attr *msg, struct blob_attr *data);
 };
 
+struct cmd_interval {
+       struct avl_node avl;
+
+       bool cancelled;
+       struct timespec start;
+       struct uloop_timeout timeout;
+       struct uloop_process process;
+
+       struct blob_attr *msg;
+       struct blob_attr *data;
+};
+
 static LIST_HEAD(cmd_queue);
+static AVL_TREE(cmd_intervals, avl_strcmp, false, NULL);
 static struct uloop_process queue_proc;
 static struct uloop_timeout last_event;
 static struct blob_buf b;
@@ -156,15 +171,159 @@ static void handle_exec(struct blob_attr *msg, struct blob_attr *data)
        exit(-1);
 }
 
+static void handle_set_interval_timeout(struct uloop_timeout *timeout)
+{
+       struct cmd_interval *interval = container_of(timeout, struct cmd_interval, timeout);
+       struct blob_attr *cur;
+       char *argv[8];
+       int rem, fd;
+       int msecs = 0;
+       int i = 0;
+
+       blobmsg_for_each_attr(cur, interval->data, rem) {
+               switch (i) {
+               case 0:
+                       break;
+               case 1:
+                       msecs = strtol(blobmsg_get_string(cur), NULL, 0);
+                       break;
+               default:
+                       argv[i - 2] = blobmsg_data(cur);
+               }
+               i++;
+               if (i - 2 == 7)
+                       break;
+       }
+
+       if (interval->process.pending) {
+               uloop_timeout_set(&interval->timeout, msecs);
+               return;
+       }
+
+       interval->process.pid = fork();
+       if (interval->process.pid < 0) {
+               perror("fork");
+       } else if (interval->process.pid == 0) {
+               struct timespec now;
+               char elapsed[6];
+
+               if (i - 2 <= 0)
+                       return;
+
+               clock_gettime(CLOCK_MONOTONIC, &now);
+               snprintf(elapsed, sizeof(elapsed), "%ld", now.tv_sec - interval->start.tv_sec);
+
+               blobmsg_for_each_attr(cur, interval->msg, rem)
+                       setenv(blobmsg_name(cur), blobmsg_data(cur), 1);
+               setenv("ACTION", "interval", 1);
+               setenv("ELAPSED", elapsed, 1);
+               unsetenv("SEEN");
+
+               if (debug < 3) {
+                       fd = open("/dev/null", O_RDWR);
+                       if (fd > -1) {
+                               dup2(fd, STDIN_FILENO);
+                               dup2(fd, STDOUT_FILENO);
+                               dup2(fd, STDERR_FILENO);
+                               if (fd > STDERR_FILENO)
+                                       close(fd);
+                       }
+               }
+
+               argv[i - 2] = NULL;
+               execvp(argv[0], &argv[0]);
+               exit(-1);
+       } else {
+               uloop_process_add(&interval->process);
+               uloop_timeout_set(&interval->timeout, msecs);
+       }
+}
+
+static void handle_set_interval_process_cb(struct uloop_process *process, int ret)
+{
+       struct cmd_interval *interval = container_of(process, struct cmd_interval, process);
+
+       if (interval->cancelled)
+               free(interval);
+}
+
+static void handle_set_interval(struct blob_attr *msg, struct blob_attr *data)
+{
+       static struct blobmsg_policy set_interval_policy[2] = {
+               { .type = BLOBMSG_TYPE_STRING },
+               { .type = BLOBMSG_TYPE_STRING },
+       };
+       struct blob_attr *tb[2];
+       struct cmd_interval *interval;
+       struct blob_attr *_msg, *_data;
+       char *_key;
+       char *name;
+       int msecs;
+
+       blobmsg_parse_array(set_interval_policy, 2, tb, blobmsg_data(data), blobmsg_data_len(data));
+       if (!tb[0] || !tb[1])
+               return;
+       name = blobmsg_get_string(tb[0]);
+       msecs = strtol(blobmsg_get_string(tb[1]), NULL, 0);
+
+       interval = calloc_a(sizeof(struct cmd_interval),
+               &_key, strlen(name) + 1,
+               &_msg, blob_pad_len(msg),
+               &_data, blob_pad_len(data),
+               NULL);
+       if (!interval)
+               return;
+
+       strcpy(_key, name);
+       interval->avl.key = _key;
+       interval->msg = _msg;
+       interval->data = _data;
+       clock_gettime(CLOCK_MONOTONIC, &interval->start);
+       interval->timeout.cb = handle_set_interval_timeout;
+       interval->process.cb = handle_set_interval_process_cb;
+
+       memcpy(interval->msg, msg, blob_pad_len(msg));
+       memcpy(interval->data, data, blob_pad_len(data));
+
+       avl_insert(&cmd_intervals, &interval->avl);
+
+       uloop_timeout_set(&interval->timeout, msecs);
+}
+
+static void handle_clear_interval(struct blob_attr *msg, struct blob_attr *data)
+{
+       static struct blobmsg_policy clear_interval_policy = {
+               .type = BLOBMSG_TYPE_STRING,
+       };
+       struct blob_attr *tb;
+       struct cmd_interval *interval;
+       char *name;
+
+       blobmsg_parse_array(&clear_interval_policy, 1, &tb, blobmsg_data(data), blobmsg_data_len(data));
+       if (!tb)
+               return;
+       name = blobmsg_get_string(tb);
+
+       interval = avl_find_element(&cmd_intervals, name, interval, avl);
+       if (interval) {
+               uloop_timeout_cancel(&interval->timeout);
+               avl_delete(&cmd_intervals, &interval->avl);
+               if (interval->process.pending)
+                       interval->cancelled = true;
+               else
+                       free(interval);
+       }
+}
+
 static void handle_firmware(struct blob_attr *msg, struct blob_attr *data)
 {
        char *dir = blobmsg_get_string(blobmsg_data(data));
        char *file = hotplug_msg_find_var(msg, "FIRMWARE");
        char *dev = hotplug_msg_find_var(msg, "DEVPATH");
-       void *fw_data;
-       struct stat s;
+       struct stat s = { 0 };
        char *path, loadpath[256], syspath[256];
-       int fw, load, sys, len;
+       int fw, src, load, len;
+       static char buf[4096];
 
        DEBUG(2, "Firmware request for %s/%s\n", dir, file);
 
@@ -173,62 +332,62 @@ static void handle_firmware(struct blob_attr *msg, struct blob_attr *data)
                exit(-1);
        }
 
-       path = malloc(strlen(dir) + strlen(file) + 2);
-       if (!path) {
-               ERROR("Failed to allocate memory\n");
-               exit(-1);
-       }
+       path = alloca(strlen(dir) + strlen(file) + 2);
        sprintf(path, "%s/%s", dir, file);
 
        if (stat(path, &s)) {
                ERROR("Could not find firmware %s\n", path);
-               exit(-1);
-       }
-
-       fw_data = malloc(s.st_size);
-       if (!fw_data) {
-               ERROR("Failed to allocate firmware data memory\n");
-               exit(-1);
+               src = -1;
+               s.st_size = 0;
+               goto send_to_kernel;
        }
 
-       fw = open(path, O_RDONLY);
-       if (!fw) {
+       src = open(path, O_RDONLY);
+       if (src < 0) {
                ERROR("Failed to open %s\n", path);
-               exit(-1);
+               s.st_size = 0;
+               goto send_to_kernel;
        }
-       if (read(fw, fw_data, s.st_size) != s.st_size) {
-               ERROR("Failed to read firmware data\n");
-               exit(-1);
-       }
-       close(fw);
 
+send_to_kernel:
        snprintf(loadpath, sizeof(loadpath), "/sys/%s/loading", dev);
        load = open(loadpath, O_WRONLY);
        if (!load) {
                ERROR("Failed to open %s\n", loadpath);
                exit(-1);
        }
-       write(load, "1", 1);
+       if (write(load, "1", 1) == -1) {
+               ERROR("Failed to write to %s\n", loadpath);
+               exit(-1);
+       }
        close(load);
 
        snprintf(syspath, sizeof(syspath), "/sys/%s/data", dev);
-       sys = open(syspath, O_WRONLY);
-       if (!sys) {
+       fw = open(syspath, O_WRONLY);
+       if (fw < 0) {
                ERROR("Failed to open %s\n", syspath);
                exit(-1);
        }
 
        len = s.st_size;
-       while (len > 4096) {
-               write(fw, fw_data, 4096);
-               len -= 4096;
+       while (len) {
+               len = read(src, buf, sizeof(buf));
+               if (len <= 0)
+                       break;
+
+               if (write(fw, buf, len) == -1) {
+                       ERROR("failed to write firmware file %s/%s to %s\n", dir, file, dev);
+                       break;
+               }
        }
-       if (len)
-               write(fw, fw_data, len);
+
+       if (src >= 0)
+               close(src);
        close(fw);
 
        load = open(loadpath, O_WRONLY);
-       write(load, "0", 1);
+       if (write(load, "0", 1) == -1)
+               ERROR("failed to write to %s\n", loadpath);
        close(load);
 
        DEBUG(2, "Done loading %s\n", path);
@@ -253,6 +412,14 @@ static struct cmd_handler {
                .name = "exec",
                .handler = handle_exec,
        }, {
+               .name = "set-interval",
+               .atomic = 1,
+               .handler = handle_set_interval,
+       }, {
+               .name = "clear-interval",
+               .atomic = 1,
+               .handler = handle_clear_interval,
+       }, {
                .name = "load-firmware",
                .handler = handle_firmware,
        },
@@ -342,7 +509,7 @@ rule_handle_file(struct json_script_ctx *ctx, const char *name)
        json_object *obj;
 
        obj = json_object_from_file((char*)name);
-       if (is_error(obj))
+       if (!obj)
                return NULL;
 
        blob_buf_init(&script, 0);
@@ -470,10 +637,8 @@ void hotplug(char *rules)
                exit(1);
        }
 
-       if (setsockopt(hotplug_fd.fd, SOL_SOCKET, SO_RCVBUFFORCE, &nlbufsize, sizeof(nlbufsize))) {
+       if (setsockopt(hotplug_fd.fd, SOL_SOCKET, SO_RCVBUFFORCE, &nlbufsize, sizeof(nlbufsize)))
                ERROR("Failed to resize receive buffer: %s\n", strerror(errno));
-               exit(1);
-       }
 
        json_script_init(&jctx);
        queue_proc.cb = queue_proc_cb;