X-Git-Url: https://git.archive.openwrt.org/?p=project%2Fnetifd.git;a=blobdiff_plain;f=proto-shell.c;h=557e5237e44203e6541e650e3121cf523f3e9dff;hp=8310f59f2d2667f2a617b39cc6de0b728b1c78b4;hb=82d2762a5ffaf67307bab6f0753dc31311e16f21;hpb=7ecca4a6852a69ecbbdf5d831fda329612e2388b diff --git a/proto-shell.c b/proto-shell.c index 8310f59..557e523 100644 --- a/proto-shell.c +++ b/proto-shell.c @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -12,7 +13,6 @@ #include "interface-ip.h" #include "proto.h" -static LIST_HEAD(handlers); static int proto_fd; struct proto_shell_handler { @@ -27,11 +27,19 @@ struct proto_shell_state { struct interface_proto_state proto; struct proto_shell_handler *handler; struct blob_attr *config; + + struct device_user l3_dev; + + struct uloop_timeout setup_timeout; + struct uloop_process setup_task; + struct uloop_process teardown_task; + bool teardown_pending; }; -static int run_script(const char **argv) +static int +run_script(const char **argv, struct uloop_process *proc) { - int pid, ret; + int pid; if ((pid = fork()) < 0) return -1; @@ -42,11 +50,11 @@ static int run_script(const char **argv) exit(127); } - if (waitpid(pid, &ret, 0) == -1) - ret = -1; + if (pid < 0) + return -1; - if (ret > 0) - return -ret; + proc->pid = pid; + uloop_process_add(proc); return 0; } @@ -57,33 +65,82 @@ proto_shell_handler(struct interface_proto_state *proto, { struct proto_shell_state *state; struct proto_shell_handler *handler; + struct uloop_process *proc; const char *argv[6]; + const char *action; char *config; int ret, i = 0; state = container_of(proto, struct proto_shell_state, proto); handler = state->handler; + if (cmd == PROTO_CMD_SETUP) { + action = "setup"; + proc = &state->setup_task; + } else { + action = "teardown"; + proc = &state->teardown_task; + if (state->setup_task.pending) { + uloop_timeout_set(&state->setup_timeout, 1000); + kill(state->setup_task.pid, SIGINT); + state->teardown_pending = true; + return 0; + } + } + config = blobmsg_format_json(state->config, true); if (!config) return -1; argv[i++] = handler->script_name; argv[i++] = handler->proto.name; - argv[i++] = cmd == PROTO_CMD_SETUP ? "setup" : "teardown"; + argv[i++] = action; argv[i++] = proto->iface->name; argv[i++] = config; if (proto->iface->main_dev.dev) argv[i++] = proto->iface->main_dev.dev->ifname; argv[i] = NULL; - ret = run_script(argv); + ret = run_script(argv, proc); free(config); return ret; } static void +proto_shell_setup_timeout_cb(struct uloop_timeout *timeout) +{ + struct proto_shell_state *state; + + state = container_of(timeout, struct proto_shell_state, setup_timeout); + kill(state->setup_task.pid, SIGKILL); +} + +static void +proto_shell_setup_cb(struct uloop_process *p, int ret) +{ + struct proto_shell_state *state; + + state = container_of(p, struct proto_shell_state, setup_task); + uloop_timeout_cancel(&state->setup_timeout); + if (state->teardown_pending) { + state->teardown_pending = false; + proto_shell_handler(&state->proto, PROTO_CMD_TEARDOWN, false); + } +} + +static void +proto_shell_teardown_cb(struct uloop_process *p, int ret) +{ + struct proto_shell_state *state; + + state = container_of(p, struct proto_shell_state, teardown_task); + state->proto.proto_event(&state->proto, IFPEV_DOWN); + if (state->l3_dev.dev) + device_remove_user(&state->l3_dev); +} + +static void proto_shell_free(struct interface_proto_state *proto) { struct proto_shell_state *state; @@ -93,6 +150,93 @@ proto_shell_free(struct interface_proto_state *proto) free(state); } +static void +proto_shell_parse_addr_list(struct interface *iface, struct blob_attr *attr, + bool v6, bool external) +{ + struct device_addr *addr; + struct blob_attr *cur; + int rem; + + blobmsg_for_each_attr(cur, attr, rem) { + if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) { + DPRINTF("Ignore wrong address type: %d\n", blobmsg_type(cur)); + continue; + } + + addr = proto_parse_ip_addr_string(blobmsg_data(cur), v6, v6 ? 32 : 128); + if (!addr) { + DPRINTF("Failed to parse IP address string: %s\n", (char *) blobmsg_data(cur)); + continue; + } + + if (external) + addr->flags |= DEVADDR_EXTERNAL; + + vlist_add(&iface->proto_addr, &addr->node); + } +} + + +enum { + NOTIFY_LINK_UP, + NOTIFY_IFNAME, + NOTIFY_ADDR_EXT, + NOTIFY_IPADDR, + NOTIFY_IP6ADDR, + __NOTIFY_LAST +}; + +static const struct blobmsg_policy notify_attr[__NOTIFY_LAST] = { + [NOTIFY_LINK_UP] = { .name = "link-up", .type = BLOBMSG_TYPE_BOOL }, + [NOTIFY_IFNAME] = { .name = "ifname", .type = BLOBMSG_TYPE_STRING }, + [NOTIFY_ADDR_EXT] = { .name = "address-external", .type = BLOBMSG_TYPE_BOOL }, + [NOTIFY_IPADDR] = { .name = "ipaddr", .type = BLOBMSG_TYPE_ARRAY }, + [NOTIFY_IP6ADDR] = { .name = "ip6addr", .type = BLOBMSG_TYPE_ARRAY }, +}; + +static int +proto_shell_notify(struct interface_proto_state *proto, struct blob_attr *attr) +{ + struct proto_shell_state *state; + struct blob_attr *tb[__NOTIFY_LAST], *cur; + bool addr_ext = false; + bool up; + + state = container_of(proto, struct proto_shell_state, proto); + + blobmsg_parse(notify_attr, __NOTIFY_LAST, tb, blob_data(attr), blob_len(attr)); + if (!tb[NOTIFY_LINK_UP]) + return UBUS_STATUS_INVALID_ARGUMENT; + + up = blobmsg_get_bool(tb[NOTIFY_LINK_UP]); + if (up) { + if (!tb[NOTIFY_IFNAME]) + return UBUS_STATUS_INVALID_ARGUMENT; + + if (!state->l3_dev.dev) { + device_add_user(&state->l3_dev, + device_get(blobmsg_data(tb[NOTIFY_IFNAME]), true)); + device_claim(&state->l3_dev); + state->proto.iface->l3_dev = &state->l3_dev; + } + state->proto.proto_event(&state->proto, IFPEV_UP); + } else { + state->proto.proto_event(&state->proto, IFPEV_LINK_LOST); + } + + if ((cur = tb[NOTIFY_ADDR_EXT]) != NULL) + addr_ext = blobmsg_get_bool(cur); + + if ((cur = tb[NOTIFY_IPADDR]) != NULL) + proto_shell_parse_addr_list(state->proto.iface, cur, false, addr_ext); + + if ((cur = tb[NOTIFY_IP6ADDR]) != NULL) + proto_shell_parse_addr_list(state->proto.iface, cur, true, addr_ext); + + return 0; +} + struct interface_proto_state * proto_shell_attach(const struct proto_handler *h, struct interface *iface, struct blob_attr *attr) @@ -106,7 +250,11 @@ proto_shell_attach(const struct proto_handler *h, struct interface *iface, memcpy(state->config, attr, blob_pad_len(attr)); state->proto.free = proto_shell_free; + state->proto.notify = proto_shell_notify; state->proto.cb = proto_shell_handler; + state->setup_timeout.cb = proto_shell_setup_timeout_cb; + state->setup_task.cb = proto_shell_setup_cb; + state->teardown_task.cb = proto_shell_teardown_cb; state->handler = container_of(h, struct proto_shell_handler, proto); return &state->proto;