From 2152229c45f470a14dee33507b331ac38055e062 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 7 Oct 2011 18:51:55 +0200 Subject: [PATCH] add support for calls to a hotplug script on interface state changes --- CMakeLists.txt | 3 +- interface-hotplug.c | 101 ++++++++++++++++++++++++++++++++++++++++++++ interface.c | 3 +- interface.h | 7 ++- main.c | 5 +++ netifd.h | 7 +++ scripts/hotplug-cmd | 1 + 7 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 interface-hotplug.c create mode 100755 scripts/hotplug-cmd diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b847ec..47a3509 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,8 @@ IF(APPLE) ENDIF() SET(SOURCES - main.c utils.c interface.c interface-ip.c + main.c utils.c + interface.c interface-ip.c interface-hotplug.c proto.c proto-static.c proto-shell.c config.c device.c bridge.c vlan.c ubus.c) diff --git a/interface-hotplug.c b/interface-hotplug.c new file mode 100644 index 0000000..fff488a --- /dev/null +++ b/interface-hotplug.c @@ -0,0 +1,101 @@ +#include +#include +#include +#include + +#include + +#include "netifd.h" +#include "interface.h" + +char *hotplug_cmd_path = DEFAULT_HOTPLUG_PATH; +static struct interface *current; +static enum interface_event current_ev; +static struct list_head pending = LIST_HEAD_INIT(pending); + +static void task_complete(struct uloop_process *proc, int ret); +static struct uloop_process task = { + .cb = task_complete, +}; + +static void +run_cmd(const char *ifname, bool up) +{ + char *argv[3]; + int pid; + + pid = fork(); + if (pid < 0) + return task_complete(NULL, -1); + + if (pid > 0) { + task.pid = pid; + uloop_process_add(&task); + return; + } + + setenv("ACTION", up ? "ifup" : "ifdown", 1); + setenv("INTERFACE", ifname, 1); + argv[0] = hotplug_cmd_path; + argv[1] = "network"; + argv[2] = NULL; + execvp(argv[0], argv); + exit(127); +} + +static void +call_hotplug(void) +{ + if (list_empty(&pending)) + return; + + current = list_first_entry(&pending, struct interface, hotplug_list); + current_ev = current->hotplug_ev; + list_del_init(¤t->hotplug_list); + run_cmd(current->name, current_ev == IFEV_UP); +} + +static void +task_complete(struct uloop_process *proc, int ret) +{ + current = NULL; + call_hotplug(); +} + +/* + * Queue an interface for an up/down event. + * An interface can only have one event in the queue and one + * event waiting for completion. + * When queueing an event that is the same as the one waiting for + * completion, remove the interface from the queue + */ +void +interface_queue_event(struct interface *iface, enum interface_event ev) +{ + enum interface_event last_ev; + + D(SYSTEM, "Queue hotplug handler for interface '%s'\n", iface->name); + if (current == iface) + last_ev = current_ev; + else + last_ev = iface->hotplug_ev; + + iface->hotplug_ev = ev; + if (last_ev == ev && !list_empty(&iface->hotplug_list)) + list_del(&iface->hotplug_list); + else if (last_ev != ev && list_empty(&iface->hotplug_list)) + list_add(&iface->hotplug_list, &pending); + + if (!task.pending && !current) + call_hotplug(); +} + +void +interface_dequeue_event(struct interface *iface) +{ + if (iface == current) + current = NULL; + + if (!list_empty(&iface->hotplug_list)) + list_del_init(&iface->hotplug_list); +} diff --git a/interface.c b/interface.c index f7ec520..3820050 100644 --- a/interface.c +++ b/interface.c @@ -78,7 +78,7 @@ void interface_add_error(struct interface *iface, const char *subsystem, static void interface_event(struct interface *iface, enum interface_event ev) { - /* TODO */ + interface_queue_event(iface, ev); } static void @@ -282,6 +282,7 @@ interface_init(struct interface *iface, const char *name, strncpy(iface->name, name, sizeof(iface->name) - 1); INIT_LIST_HEAD(&iface->errors); + INIT_LIST_HEAD(&iface->hotplug_list); iface->main_dev.cb = interface_cb; iface->l3_dev = &iface->main_dev; diff --git a/interface.h b/interface.h index 9a9b211..4d53c83 100644 --- a/interface.h +++ b/interface.h @@ -8,8 +8,8 @@ struct interface; struct interface_proto_state; enum interface_event { - IFEV_UP, IFEV_DOWN, + IFEV_UP, }; enum interface_state { @@ -38,6 +38,8 @@ struct interface_error { */ struct interface { struct vlist_node node; + struct list_head hotplug_list; + enum interface_event hotplug_ev; char name[IFNAMSIZ]; const char *ifname; @@ -91,6 +93,9 @@ void interface_remove_link(struct interface *iface, struct device *llif); void interface_add_error(struct interface *iface, const char *subsystem, const char *code, const char **data, int n_data); +void interface_queue_event(struct interface *iface, enum interface_event ev); +void interface_dequeue_event(struct interface *iface); + void interface_start_pending(void); #endif diff --git a/main.c b/main.c index 465398f..4757b79 100644 --- a/main.c +++ b/main.c @@ -46,6 +46,8 @@ static int usage(const char *progname) " -d : Mask for debug messages\n" " -s : Path to the ubus socket\n" " -p : Path to netifd addons (default: %s)\n" + " -h : Path to the hotplug script\n" + " (default: "DEFAULT_HOTPLUG_PATH")\n" "\n", progname, main_path); return 1; @@ -69,6 +71,9 @@ int main(int argc, char **argv) case 'p': main_path = optarg; break; + case 'h': + hotplug_cmd_path = optarg; + break; default: return usage(argv[0]); } diff --git a/netifd.h b/netifd.h index 1bb3820..926cab8 100644 --- a/netifd.h +++ b/netifd.h @@ -13,6 +13,13 @@ #include "utils.h" +#ifdef DUMMY_MODE +#define DEFAULT_HOTPLUG_PATH "./scripts/hotplug-cmd" +#else +#define DEFAULT_HOTPLUG_PATH "/sbin/hotplug-cmd" +#endif + +extern char *hotplug_cmd_path; extern unsigned int debug_mask; enum { diff --git a/scripts/hotplug-cmd b/scripts/hotplug-cmd new file mode 100755 index 0000000..2488a77 --- /dev/null +++ b/scripts/hotplug-cmd @@ -0,0 +1 @@ +echo "Action: $ACTION, Interface: $INTERFACE" -- 2.30.2