1 /* 2 * netifd - network interface daemon 3 * Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 7 * as published by the Free Software Foundation 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 */ 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 17 18 #include <libubox/uloop.h> 19 20 #include "netifd.h" 21 #include "interface.h" 22 #include "ubus.h" 23 24 char *hotplug_cmd_path = DEFAULT_HOTPLUG_PATH; 25 static struct interface *current; 26 static enum interface_event current_ev; 27 static struct list_head pending = LIST_HEAD_INIT(pending); 28 29 static void task_complete(struct uloop_process *proc, int ret); 30 static struct uloop_process task = { 31 .cb = task_complete, 32 }; 33 static const char * const eventnames[] = { 34 [IFEV_DOWN] = "ifdown", 35 [IFEV_UP] = "ifup", 36 [IFEV_UP_FAILED] = "ifup-failed", 37 [IFEV_UPDATE] = "ifupdate", 38 [IFEV_FREE] = "free", 39 [IFEV_RELOAD] = "reload", 40 [IFEV_LINK_UP] = "iflink", 41 [IFEV_CREATE] = "create", 42 }; 43 44 static void 45 run_cmd(const char *ifname, const char *device, enum interface_event event, 46 enum interface_update_flags updated) 47 { 48 char *argv[3]; 49 int pid; 50 51 pid = fork(); 52 if (pid < 0) { 53 task_complete(NULL, -1); 54 return; 55 } 56 57 if (pid > 0) { 58 task.pid = pid; 59 uloop_process_add(&task); 60 return; 61 } 62 63 setenv("ACTION", eventnames[event], 1); 64 setenv("INTERFACE", ifname, 1); 65 if (device) 66 setenv("DEVICE", device, 1); 67 68 if (event == IFEV_UPDATE) { 69 if (updated & IUF_ADDRESS) 70 setenv("IFUPDATE_ADDRESSES", "1", 1); 71 if (updated & IUF_ROUTE) 72 setenv("IFUPDATE_ROUTES", "1", 1); 73 if (updated & IUF_PREFIX) 74 setenv("IFUPDATE_PREFIXES", "1", 1); 75 if (updated & IUF_DATA) 76 setenv("IFUPDATE_DATA", "1", 1); 77 } 78 79 argv[0] = hotplug_cmd_path; 80 argv[1] = "iface"; 81 argv[2] = NULL; 82 execvp(argv[0], argv); 83 exit(127); 84 } 85 86 static void 87 call_hotplug(void) 88 { 89 const char *device = NULL; 90 if (list_empty(&pending)) 91 return; 92 93 current = list_first_entry(&pending, struct interface, hotplug_list); 94 current_ev = current->hotplug_ev; 95 list_del_init(¤t->hotplug_list); 96 97 if ((current_ev == IFEV_UP || current_ev == IFEV_UPDATE) && current->l3_dev.dev) 98 device = current->l3_dev.dev->ifname; 99 100 D(SYSTEM, "Call hotplug handler for interface '%s', event '%s' (%s)", 101 current->name, eventnames[current_ev], device ? device : "none"); 102 run_cmd(current->name, device, current_ev, current->updated); 103 } 104 105 static void 106 task_complete(struct uloop_process *proc, int ret) 107 { 108 if (current) 109 D(SYSTEM, "Complete hotplug handler for interface '%s'", current->name); 110 current = NULL; 111 call_hotplug(); 112 } 113 114 /* 115 * Queue an interface for an up/down event. 116 * An interface can only have one event in the queue and one 117 * event waiting for completion. 118 * When queueing an event that is the same as the one waiting for 119 * completion, remove the interface from the queue 120 */ 121 static void 122 interface_queue_event(struct interface *iface, enum interface_event ev) 123 { 124 D(SYSTEM, "Queue hotplug handler for interface '%s', event '%s'", 125 iface->name, eventnames[ev]); 126 if (ev == IFEV_UP || ev == IFEV_DOWN) 127 netifd_ubus_interface_event(iface, ev == IFEV_UP); 128 129 netifd_ubus_interface_notify(iface, ev != IFEV_DOWN); 130 131 /* no hotplug.d calls for link up */ 132 if (ev == IFEV_LINK_UP) 133 return; 134 135 if (current == iface) { 136 /* an event for iface is being processed */ 137 if (!list_empty(&iface->hotplug_list)) { 138 /* an additional event for iface is pending */ 139 /* overwrite pending event if it differs from */ 140 /* an update */ 141 if (ev != IFEV_UPDATE) 142 iface->hotplug_ev = ev; 143 } 144 else { 145 /* no additional event for iface is pending */ 146 if (ev != current_ev || ev == IFEV_UPDATE) { 147 /* only add the interface to the pending list if 148 * the event is different from the one being 149 * handled or if it is an update */ 150 iface->hotplug_ev = ev; 151 /* Handle hotplug calls FIFO */ 152 list_add_tail(&iface->hotplug_list, &pending); 153 } 154 } 155 } 156 else { 157 /* currently not handling an event or handling an event 158 * for another interface */ 159 if (!list_empty(&iface->hotplug_list)) { 160 /* an event for iface is pending */ 161 if (!(iface->hotplug_ev == IFEV_UP && 162 ev == IFEV_UPDATE)) { 163 /* overwrite pending event, unless the incoming 164 * event is an ifupdate while the pending one 165 * is an ifup */ 166 iface->hotplug_ev = ev; 167 } 168 } 169 else { 170 /* an event for the interface is not yet pending, 171 * queue it */ 172 iface->hotplug_ev = ev; 173 /* Handle hotplug calls FIFO */ 174 list_add_tail(&iface->hotplug_list, &pending); 175 } 176 } 177 178 if (!task.pending && !current) 179 call_hotplug(); 180 } 181 182 static void 183 interface_dequeue_event(struct interface *iface) 184 { 185 if (iface == current) 186 current = NULL; 187 188 if (!list_empty(&iface->hotplug_list)) 189 list_del_init(&iface->hotplug_list); 190 } 191 192 static void interface_event_cb(struct interface_user *dep, struct interface *iface, 193 enum interface_event ev) 194 { 195 switch (ev) { 196 case IFEV_LINK_UP: 197 case IFEV_UP: 198 case IFEV_UP_FAILED: 199 case IFEV_UPDATE: 200 case IFEV_DOWN: 201 interface_queue_event(iface, ev); 202 break; 203 case IFEV_FREE: 204 interface_dequeue_event(iface); 205 break; 206 default: 207 break; 208 } 209 } 210 211 static struct interface_user event_user = { 212 .cb = interface_event_cb 213 }; 214 215 static void __init interface_event_init(void) 216 { 217 interface_add_user(&event_user, NULL); 218 } 219
This page was automatically generated by LXR 0.3.1. • OpenWrt