summaryrefslogtreecommitdiffstats
path: root/plug/hotplug.c
diff options
context:
space:
mode:
Diffstat (limited to 'plug/hotplug.c')
-rw-r--r--plug/hotplug.c166
1 files changed, 166 insertions, 0 deletions
diff --git a/plug/hotplug.c b/plug/hotplug.c
index 1a98e8b..83ddc2b 100644
--- a/plug/hotplug.c
+++ b/plug/hotplug.c
@@ -19,6 +19,7 @@
#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>
@@ -43,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;
@@ -157,6 +171,150 @@ 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));
@@ -254,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,
},