#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>
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;
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);
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);
.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,
},
json_object *obj;
obj = json_object_from_file((char*)name);
- if (is_error(obj))
+ if (!obj)
return NULL;
blob_buf_init(&script, 0);
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;