X-Git-Url: http://git.openwrt.org/?p=project%2Fprocd.git;a=blobdiff_plain;f=plug%2Fhotplug.c;h=de0511fbd4891ff5e226f0059e29d3cf016915fe;hp=1a98e8bb6f4be7245adc08c3f0ff78f437400414;hb=4a127c3c60af888b0aa5185a90799e5d12aa15c4;hpb=c5cdda8f73aa99e4ec242e6980a1138c641d3614 diff --git a/plug/hotplug.c b/plug/hotplug.c index 1a98e8b..de0511f 100644 --- a/plug/hotplug.c +++ b/plug/hotplug.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -24,10 +25,12 @@ #include #include +#include #include #include #include #include +#include #include "../procd.h" @@ -35,20 +38,57 @@ #define HOTPLUG_WAIT 500 +struct cmd_handler; struct cmd_queue { struct list_head list; struct blob_attr *msg; struct blob_attr *data; + int timeout; + void (*handler)(struct blob_attr *msg, struct blob_attr *data); + void (*start)(struct blob_attr *msg, struct blob_attr *data); + void (*complete)(struct blob_attr *msg, struct blob_attr *data, int ret); +}; + +struct button_timeout { + struct list_head list; + struct uloop_timeout timeout; + char *name; + int seen; + struct blob_attr *data; }; static LIST_HEAD(cmd_queue); +static LIST_HEAD(button_timer); static struct uloop_process queue_proc; static struct uloop_timeout last_event; -static struct blob_buf b; +static struct blob_buf b, button_buf; static char *rule_file; static struct blob_buf script; +static struct cmd_queue *current; + +static void queue_add(struct cmd_handler *h, struct blob_attr *msg, struct blob_attr *data); +static void handle_button_complete(struct blob_attr *msg, struct blob_attr *data, int ret); + +static void button_free(struct button_timeout *b) +{ + uloop_timeout_cancel(&b->timeout); + list_del(&b->list); + free(b->data); + free(b->name); + free(b); +} + +static void button_timeout_remove(char *button) +{ + struct button_timeout *b, *c; + + if (!list_empty(&button_timer)) list_for_each_entry_safe(b, c, &button_timer, list) { + if (!strcmp(b->name, button)) + button_free(b); + } +} static char *hotplug_msg_find_var(struct blob_attr *msg, const char *name) { @@ -80,19 +120,44 @@ static void mkdir_p(char *dir) } } +static void chgrp_error(const char *group, const char *target, const char *failed) +{ + ERROR("cannot set group %s for %s (%s: %d)\n", + group, target, failed, errno); +} + +static void chgrp_target(struct blob_attr *bgroup, struct blob_attr *btarget) +{ + int ret = 0; + struct group *g = NULL; + const char *group = blobmsg_get_string(bgroup); + const char *target = blobmsg_get_string(btarget); + + errno = 0; + + g = getgrnam(group); + if (!g) + return chgrp_error(group, target, "getgrnam"); + + ret = chown(target, 0, g->gr_gid); + if (ret < 0) + return chgrp_error(group, target, "chown"); +} + static void handle_makedev(struct blob_attr *msg, struct blob_attr *data) { unsigned int oldumask = umask(0); - static struct blobmsg_policy mkdev_policy[2] = { + static struct blobmsg_policy mkdev_policy[3] = { + { .type = BLOBMSG_TYPE_STRING }, { .type = BLOBMSG_TYPE_STRING }, { .type = BLOBMSG_TYPE_STRING }, }; - struct blob_attr *tb[2]; + struct blob_attr *tb[3]; char *minor = hotplug_msg_find_var(msg, "MINOR"); char *major = hotplug_msg_find_var(msg, "MAJOR"); char *subsystem = hotplug_msg_find_var(msg, "SUBSYSTEM"); - blobmsg_parse_array(mkdev_policy, 2, tb, blobmsg_data(data), blobmsg_data_len(data)); + blobmsg_parse_array(mkdev_policy, 3, tb, blobmsg_data(data), blobmsg_data_len(data)); if (tb[0] && tb[1] && minor && major && subsystem) { mode_t m = S_IFCHR; char *d = strdup(blobmsg_get_string(tb[0])); @@ -106,6 +171,8 @@ static void handle_makedev(struct blob_attr *msg, struct blob_attr *data) mknod(blobmsg_get_string(tb[0]), m | strtoul(blobmsg_data(tb[1]), NULL, 8), makedev(atoi(major), atoi(minor))); + if (tb[2]) + chgrp_target(tb[2], tb[0]); } umask(oldumask); } @@ -154,7 +221,15 @@ static void handle_exec(struct blob_attr *msg, struct blob_attr *data) argv[i] = NULL; execvp(argv[0], &argv[0]); } - exit(-1); + exit(EXIT_FAILURE); +} + +static void handle_button_start(struct blob_attr *msg, struct blob_attr *data) +{ + char *button = hotplug_msg_find_var(msg, "BUTTON"); + + if (button) + button_timeout_remove(button); } static void handle_firmware(struct blob_attr *msg, struct blob_attr *data) @@ -171,14 +246,14 @@ static void handle_firmware(struct blob_attr *msg, struct blob_attr *data) if (!file || !dir || !dev) { ERROR("Request for unknown firmware %s/%s\n", dir, file); - exit(-1); + exit(EXIT_FAILURE); } 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); + ERROR("Could not find firmware %s: %m\n", path); src = -1; s.st_size = 0; goto send_to_kernel; @@ -186,7 +261,7 @@ static void handle_firmware(struct blob_attr *msg, struct blob_attr *data) src = open(path, O_RDONLY); if (src < 0) { - ERROR("Failed to open %s\n", path); + ERROR("Failed to open %s: %m\n", path); s.st_size = 0; goto send_to_kernel; } @@ -195,20 +270,20 @@ 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); + ERROR("Failed to open %s: %m\n", loadpath); + exit(EXIT_FAILURE); } if (write(load, "1", 1) == -1) { - ERROR("Failed to write to %s\n", loadpath); - exit(-1); + ERROR("Failed to write to %s: %m\n", loadpath); + exit(EXIT_FAILURE); } close(load); snprintf(syspath, sizeof(syspath), "/sys/%s/data", dev); fw = open(syspath, O_WRONLY); if (fw < 0) { - ERROR("Failed to open %s\n", syspath); - exit(-1); + ERROR("Failed to open %s: %m\n", syspath); + exit(EXIT_FAILURE); } len = s.st_size; @@ -218,7 +293,7 @@ send_to_kernel: break; if (write(fw, buf, len) == -1) { - ERROR("failed to write firmware file %s/%s to %s\n", dir, file, dev); + ERROR("failed to write firmware file %s/%s to %s: %m\n", dir, file, dev); break; } } @@ -229,34 +304,72 @@ send_to_kernel: load = open(loadpath, O_WRONLY); if (write(load, "0", 1) == -1) - ERROR("failed to write to %s\n", loadpath); + ERROR("failed to write to %s: %m\n", loadpath); close(load); DEBUG(2, "Done loading %s\n", path); - exit(-1); + exit(EXIT_FAILURE); +} + +static void handle_start_console(struct blob_attr *msg, struct blob_attr *data) +{ + char *dev = blobmsg_get_string(blobmsg_data(data)); + + DEBUG(2, "Start console request for %s\n", dev); + + procd_inittab_run("respawn"); + procd_inittab_run("askfirst"); + + DEBUG(2, "Done starting console for %s\n", dev); + + exit(EXIT_FAILURE); } +enum { + HANDLER_MKDEV = 0, + HANDLER_RM, + HANDLER_EXEC, + HANDLER_BUTTON, + HANDLER_FW, + HANDLER_START_CONSOLE, +}; + static struct cmd_handler { char *name; int atomic; void (*handler)(struct blob_attr *msg, struct blob_attr *data); + void (*start)(struct blob_attr *msg, struct blob_attr *data); + void (*complete)(struct blob_attr *msg, struct blob_attr *data, int ret); } handlers[] = { - { + [HANDLER_MKDEV] = { .name = "makedev", .atomic = 1, .handler = handle_makedev, - }, { + }, + [HANDLER_RM] = { .name = "rm", .atomic = 1, .handler = handle_rm, - }, { + }, + [HANDLER_EXEC] = { .name = "exec", .handler = handle_exec, - }, { + }, + [HANDLER_BUTTON] = { + .name = "button", + .handler = handle_exec, + .start = handle_button_start, + .complete = handle_button_complete, + }, + [HANDLER_FW] = { .name = "load-firmware", .handler = handle_firmware, }, + [HANDLER_START_CONSOLE] = { + .name = "start-console", + .handler = handle_start_console, + }, }; static void queue_next(void) @@ -274,10 +387,13 @@ static void queue_next(void) c->handler(c->msg, c->data); exit(0); } - + if (c->start) + c->start(c->msg, c->data); list_del(&c->list); - free(c); - + if (c->complete) + current = c; + else + free(c); if (queue_proc.pid <= 0) { queue_next(); return; @@ -292,6 +408,11 @@ static void queue_proc_cb(struct uloop_process *c, int ret) { DEBUG(4, "Finished hotplug exec instance, pid=%d\n", (int) c->pid); + if (current) { + current->complete(current->msg, current->data, ret); + free(current); + current = NULL; + } queue_next(); } @@ -305,19 +426,63 @@ static void queue_add(struct cmd_handler *h, struct blob_attr *msg, struct blob_ &_data, blob_pad_len(data), NULL); - c->msg = _msg; - c->data = _data; - if (!c) return; + c->msg = _msg; + c->data = _data; + memcpy(c->msg, msg, blob_pad_len(msg)); memcpy(c->data, data, blob_pad_len(data)); c->handler = h->handler; + c->complete = h->complete; + c->start = h->start; list_add_tail(&c->list, &cmd_queue); queue_next(); } +static void handle_button_timeout(struct uloop_timeout *t) +{ + struct button_timeout *b; + char seen[16]; + + b = container_of(t, struct button_timeout, timeout); + blob_buf_init(&button_buf, 0); + blobmsg_add_string(&button_buf, "BUTTON", b->name); + blobmsg_add_string(&button_buf, "ACTION", "timeout"); + snprintf(seen, sizeof(seen), "%d", b->seen); + blobmsg_add_string(&button_buf, "SEEN", seen); + queue_add(&handlers[HANDLER_EXEC], button_buf.head, b->data); + button_free(b); +} + +static void handle_button_complete(struct blob_attr *msg, struct blob_attr *data, int ret) +{ + char *name = hotplug_msg_find_var(msg, "BUTTON"); + struct button_timeout *b; + int timeout = ret >> 8; + + if (!timeout) + return; + + if (!name) + return; + + b = calloc(1, sizeof(*b)); + if (!b) + return; + + b->data = malloc(blob_pad_len(data)); + b->name = strdup(name); + b->seen = timeout; + + memcpy(b->data, data, blob_pad_len(data)); + b->timeout.cb = handle_button_timeout; + + uloop_timeout_set(&b->timeout, timeout * 1000); + list_add(&b->list, &button_timer); +} + static const char* rule_handle_var(struct json_script_ctx *ctx, const char *name, struct blob_attr *vars) { const char *str, *sep; @@ -359,15 +524,13 @@ static void rule_handle_command(struct json_script_ctx *ctx, const char *name, int rem, i; if (debug > 3) { - DEBUG(4, "Command: %s", name); + DEBUG(4, "Command: %s\n", name); blobmsg_for_each_attr(cur, data, rem) - DEBUG(4, " %s", (char *) blobmsg_data(cur)); - DEBUG(4, "\n"); + DEBUG(4, " %s\n", (char *) blobmsg_data(cur)); - DEBUG(4, "Message:"); + DEBUG(4, "Message:\n"); blobmsg_for_each_attr(cur, vars, rem) - DEBUG(4, " %s=%s", blobmsg_name(cur), (char *) blobmsg_data(cur)); - DEBUG(4, "\n"); + DEBUG(4, " %s=%s\n", blobmsg_name(cur), (char *) blobmsg_data(cur)); } for (i = 0; i < ARRAY_SIZE(handlers); i++) @@ -416,11 +579,13 @@ static void hotplug_handler(struct uloop_fd *u, unsigned int ev) { int i = 0; static char buf[4096]; - int len = recv(u->fd, buf, sizeof(buf), MSG_DONTWAIT); + int len = recv(u->fd, buf, sizeof(buf) - 1, MSG_DONTWAIT); void *index; if (len < 1) return; + buf[len] = '\0'; + blob_buf_init(&b, 0); index = blobmsg_open_table(&b, NULL); while (i < len) { @@ -453,26 +618,25 @@ void hotplug_last_event(uloop_timeout_handler handler) void hotplug(char *rules) { - struct sockaddr_nl nls; + struct sockaddr_nl nls = {}; int nlbufsize = 512 * 1024; rule_file = strdup(rules); - memset(&nls,0,sizeof(struct sockaddr_nl)); nls.nl_family = AF_NETLINK; nls.nl_pid = getpid(); nls.nl_groups = -1; if ((hotplug_fd.fd = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT)) == -1) { - ERROR("Failed to open hotplug socket: %s\n", strerror(errno)); + ERROR("Failed to open hotplug socket: %m\n"); exit(1); } if (bind(hotplug_fd.fd, (void *)&nls, sizeof(struct sockaddr_nl))) { - ERROR("Failed to bind hotplug socket: %s\n", strerror(errno)); + ERROR("Failed to bind hotplug socket: %m\n"); exit(1); } if (setsockopt(hotplug_fd.fd, SOL_SOCKET, SO_RCVBUFFORCE, &nlbufsize, sizeof(nlbufsize))) - ERROR("Failed to resize receive buffer: %s\n", strerror(errno)); + ERROR("Failed to resize receive buffer: %m\n"); json_script_init(&jctx); queue_proc.cb = queue_proc_cb;