1 /* vi: set sw=4 ts=4: */
2 /*
3  * ifplugd for busybox, based on ifplugd 0.28 (written by Lennart Poettering).
4  *
5  * Copyright (C) 2009 Maksym Kryzhanovskyy <xmaks@email.cz>
6  *
7  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8  */
9 //config:config IFPLUGD
10 //config:	bool "ifplugd (10 kb)"
11 //config:	default y
12 //config:	help
13 //config:	Network interface plug detection daemon.
14 
15 //applet:IF_IFPLUGD(APPLET(ifplugd, BB_DIR_USR_SBIN, BB_SUID_DROP))
16 
17 //kbuild:lib-$(CONFIG_IFPLUGD) += ifplugd.o
18 
19 //usage:#define ifplugd_trivial_usage
20 //usage:       "[OPTIONS]"
21 //usage:#define ifplugd_full_usage "\n\n"
22 //usage:       "Network interface plug detection daemon\n"
23 //usage:     "\n	-n		Run in foreground"
24 //usage:     "\n	-s		Don't log to syslog"
25 //usage:     "\n	-i IFACE	Interface"
26 //usage:     "\n	-f/-F		Treat link detection error as link down/link up"
27 //usage:     "\n			(otherwise exit on error)"
28 //usage:     "\n	-a		Don't up interface at each link probe"
29 //usage:     "\n	-M		Monitor creation/destruction of interface"
30 //usage:     "\n			(otherwise it must exist)"
31 //usage:     "\n	-r PROG		Script to run"
32 //usage:     "\n	-x ARG		Extra argument for script"
33 //usage:     "\n	-I		Don't exit on nonzero exit code from script"
34 //usage:     "\n	-p		Don't run \"up\" script on startup"
35 //usage:     "\n	-q		Don't run \"down\" script on exit"
36 //usage:     "\n	-l		Always run script on startup"
37 //usage:     "\n	-t SECS		Poll time in seconds"
38 //usage:     "\n	-u SECS		Delay before running script after link up"
39 //usage:     "\n	-d SECS		Delay after link down"
40 //usage:     "\n	-m MODE		API mode (mii, priv, ethtool, wlan, iff, auto)"
41 //usage:     "\n	-k		Kill running daemon"
42 
43 #include "libbb.h"
44 
45 #include "fix_u32.h"
46 #include <linux/if.h>
47 #include <linux/mii.h>
48 #include <linux/ethtool.h>
49 #ifdef HAVE_NET_ETHERNET_H
50 /* musl breakage:
51  * In file included from /usr/include/net/ethernet.h:10,
52  *                  from networking/ifplugd.c:41:
53  * /usr/include/netinet/if_ether.h:96: error: redefinition of 'struct ethhdr'
54  *
55  * Build succeeds without it on musl. Commented it out.
56  * If on your system you need it, consider removing <linux/ethtool.h>
57  * and copy-pasting its definitions here (<linux/ethtool.h> is what pulls in
58  * conflicting definition of struct ethhdr on musl).
59  */
60 /* # include <net/ethernet.h> */
61 #endif
62 #include <linux/netlink.h>
63 #include <linux/rtnetlink.h>
64 #include <linux/sockios.h>
65 #include <syslog.h>
66 
67 #define __user
68 #include <linux/wireless.h>
69 
70 #ifndef ETH_ALEN
71 # define ETH_ALEN  6
72 #endif
73 
74 /*
75 From initial port to busybox, removed most of the redundancy by
76 converting implementation of a polymorphic interface to the strict
77 functional style. The main role is run a script when link state
78 changed, other activities like audio signal or detailed reports
79 are on the script itself.
80 
81 One questionable point of the design is netlink usage:
82 
83 We have 1 second timeout by default to poll the link status,
84 it is short enough so that there are no real benefits in
85 using netlink to get "instantaneous" interface creation/deletion
86 notifications. We can check for interface existence by just
87 doing some fast ioctl using its name.
88 
89 Netlink code then can be just dropped (1k or more?)
90 */
91 
92 
93 #define IFPLUGD_ENV_PREVIOUS "IFPLUGD_PREVIOUS"
94 #define IFPLUGD_ENV_CURRENT "IFPLUGD_CURRENT"
95 
96 enum {
97 	FLAG_NO_AUTO			= 1 <<  0, // -a, Do not enable interface automatically
98 	FLAG_NO_DAEMON			= 1 <<  1, // -n, Do not daemonize
99 	FLAG_NO_SYSLOG			= 1 <<  2, // -s, Do not use syslog, use stderr instead
100 	FLAG_IGNORE_FAIL		= 1 <<  3, // -f, Ignore detection failure, retry instead (failure is treated as DOWN)
101 	FLAG_IGNORE_FAIL_POSITIVE	= 1 <<  4, // -F, Ignore detection failure, retry instead (failure is treated as UP)
102 	FLAG_IFACE			= 1 <<  5, // -i, Specify ethernet interface
103 	FLAG_RUN			= 1 <<  6, // -r, Specify program to execute
104 	FLAG_IGNORE_RETVAL		= 1 <<  7, // -I, Don't exit on nonzero return value of program executed
105 	FLAG_POLL_TIME			= 1 <<  8, // -t, Specify poll time in seconds
106 	FLAG_DELAY_UP			= 1 <<  9, // -u, Specify delay for configuring interface
107 	FLAG_DELAY_DOWN			= 1 << 10, // -d, Specify delay for deconfiguring interface
108 	FLAG_API_MODE			= 1 << 11, // -m, Force API mode (mii, priv, ethtool, wlan, auto)
109 	FLAG_NO_STARTUP			= 1 << 12, // -p, Don't run script on daemon startup
110 	FLAG_NO_SHUTDOWN		= 1 << 13, // -q, Don't run script on daemon quit
111 	FLAG_INITIAL_DOWN		= 1 << 14, // -l, Run "down" script on startup if no cable is detected
112 	FLAG_EXTRA_ARG			= 1 << 15, // -x, Specify an extra argument for action script
113 	FLAG_MONITOR			= 1 << 16, // -M, Use interface monitoring
114 #if ENABLE_FEATURE_PIDFILE
115 	FLAG_KILL			= 1 << 17, // -k, Kill a running daemon
116 #endif
117 };
118 #if ENABLE_FEATURE_PIDFILE
119 # define OPTION_STR "+ansfFi:r:It:+u:+d:+m:pqlx:Mk"
120 #else
121 # define OPTION_STR "+ansfFi:r:It:+u:+d:+m:pqlx:M"
122 #endif
123 
124 enum { // interface status
125 	IFSTATUS_ERR = -1,
126 	IFSTATUS_DOWN = 0,
127 	IFSTATUS_UP = 1,
128 };
129 
130 enum { // constant fds
131 	ioctl_fd = 3,
132 	netlink_fd = 4,
133 };
134 
135 struct globals {
136 	smallint iface_last_status;
137 	smallint iface_prev_status;
138 	smallint iface_exists;
139 	smallint api_method_num;
140 
141 	/* Used in getopt32, must have sizeof == sizeof(int) */
142 	unsigned poll_time;
143 	unsigned delay_up;
144 	unsigned delay_down;
145 
146 	const char *iface;
147 	const char *api_mode;
148 	const char *script_name;
149 	const char *extra_arg;
150 };
151 #define G (*ptr_to_globals)
152 #define INIT_G() do { \
153 	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
154 	G.iface_last_status = -1; \
155 	G.iface_exists   = 1; \
156 	G.poll_time      = 1; \
157 	G.delay_down     = 5; \
158 	G.iface          = "eth0"; \
159 	G.api_mode       = "a"; \
160 	G.script_name    = "/etc/ifplugd/ifplugd.action"; \
161 } while (0)
162 
163 
164 /* Utility routines */
165 
set_ifreq_to_ifname(struct ifreq * ifreq)166 static void set_ifreq_to_ifname(struct ifreq *ifreq)
167 {
168 	memset(ifreq, 0, sizeof(struct ifreq));
169 	strncpy_IFNAMSIZ(ifreq->ifr_name, G.iface);
170 }
171 
network_ioctl(int request,void * data,const char * errmsg)172 static int network_ioctl(int request, void* data, const char *errmsg)
173 {
174 	int r = ioctl(ioctl_fd, request, data);
175 	if (r < 0 && errmsg)
176 		bb_perror_msg("%s failed", errmsg);
177 	return r;
178 }
179 
180 /* Link detection routines and table */
181 
detect_link_mii(void)182 static smallint detect_link_mii(void)
183 {
184 	/* char buffer instead of bona-fide struct avoids aliasing warning */
185 	char buf[sizeof(struct ifreq)];
186 	struct ifreq *const ifreq = (void *)buf;
187 
188 	struct mii_ioctl_data *mii = (void *)&ifreq->ifr_data;
189 
190 	set_ifreq_to_ifname(ifreq);
191 
192 	if (network_ioctl(SIOCGMIIPHY, ifreq, "SIOCGMIIPHY") < 0) {
193 		return IFSTATUS_ERR;
194 	}
195 
196 	mii->reg_num = 1;
197 
198 	if (network_ioctl(SIOCGMIIREG, ifreq, "SIOCGMIIREG") < 0) {
199 		return IFSTATUS_ERR;
200 	}
201 
202 	return (mii->val_out & 0x0004) ? IFSTATUS_UP : IFSTATUS_DOWN;
203 }
204 
detect_link_priv(void)205 static smallint detect_link_priv(void)
206 {
207 	/* char buffer instead of bona-fide struct avoids aliasing warning */
208 	char buf[sizeof(struct ifreq)];
209 	struct ifreq *const ifreq = (void *)buf;
210 
211 	struct mii_ioctl_data *mii = (void *)&ifreq->ifr_data;
212 
213 	set_ifreq_to_ifname(ifreq);
214 
215 	if (network_ioctl(SIOCDEVPRIVATE, ifreq, "SIOCDEVPRIVATE") < 0) {
216 		return IFSTATUS_ERR;
217 	}
218 
219 	mii->reg_num = 1;
220 
221 	if (network_ioctl(SIOCDEVPRIVATE+1, ifreq, "SIOCDEVPRIVATE+1") < 0) {
222 		return IFSTATUS_ERR;
223 	}
224 
225 	return (mii->val_out & 0x0004) ? IFSTATUS_UP : IFSTATUS_DOWN;
226 }
227 
detect_link_ethtool(void)228 static smallint detect_link_ethtool(void)
229 {
230 	struct ifreq ifreq;
231 	struct ethtool_value edata;
232 
233 	set_ifreq_to_ifname(&ifreq);
234 
235 	edata.cmd = ETHTOOL_GLINK;
236 	ifreq.ifr_data = (void*) &edata;
237 
238 	if (network_ioctl(SIOCETHTOOL, &ifreq, "ETHTOOL_GLINK") < 0) {
239 		return IFSTATUS_ERR;
240 	}
241 
242 	return edata.data ? IFSTATUS_UP : IFSTATUS_DOWN;
243 }
244 
detect_link_iff(void)245 static smallint detect_link_iff(void)
246 {
247 	struct ifreq ifreq;
248 
249 	set_ifreq_to_ifname(&ifreq);
250 
251 	if (network_ioctl(SIOCGIFFLAGS, &ifreq, "SIOCGIFFLAGS") < 0) {
252 		return IFSTATUS_ERR;
253 	}
254 
255 	/* If IFF_UP is not set (interface is down), IFF_RUNNING is never set
256 	 * regardless of link status. Simply continue to report last status -
257 	 * no point in reporting spurious link downs if interface is disabled
258 	 * by admin. When/if it will be brought up,
259 	 * we'll report real link status.
260 	 */
261 	if (!(ifreq.ifr_flags & IFF_UP) && G.iface_last_status != IFSTATUS_ERR)
262 		return G.iface_last_status;
263 
264 	return (ifreq.ifr_flags & IFF_RUNNING) ? IFSTATUS_UP : IFSTATUS_DOWN;
265 }
266 
detect_link_wlan(void)267 static smallint detect_link_wlan(void)
268 {
269 	int i;
270 	struct iwreq iwrequest;
271 	uint8_t mac[ETH_ALEN];
272 
273 	memset(&iwrequest, 0, sizeof(iwrequest));
274 	strncpy_IFNAMSIZ(iwrequest.ifr_ifrn.ifrn_name, G.iface);
275 
276 	if (network_ioctl(SIOCGIWAP, &iwrequest, "SIOCGIWAP") < 0) {
277 		return IFSTATUS_ERR;
278 	}
279 
280 	memcpy(mac, &iwrequest.u.ap_addr.sa_data, ETH_ALEN);
281 
282 	if (mac[0] == 0xFF || mac[0] == 0x44 || mac[0] == 0x00) {
283 		for (i = 1; i < ETH_ALEN; ++i) {
284 			if (mac[i] != mac[0])
285 				return IFSTATUS_UP;
286 		}
287 		return IFSTATUS_DOWN;
288 	}
289 
290 	return IFSTATUS_UP;
291 }
292 
293 enum { // api mode
294 	API_ETHTOOL, // 'e'
295 	API_MII,     // 'm'
296 	API_PRIVATE, // 'p'
297 	API_WLAN,    // 'w'
298 	API_IFF,     // 'i'
299 	API_AUTO,    // 'a'
300 };
301 
302 static const char api_modes[] ALIGN1 = "empwia";
303 
304 static const struct {
305 	const char *name;
306 	smallint (*func)(void);
307 } method_table[] ALIGN_PTR = {
308 	{ "SIOCETHTOOL"       , &detect_link_ethtool },
309 	{ "SIOCGMIIPHY"       , &detect_link_mii     },
310 	{ "SIOCDEVPRIVATE"    , &detect_link_priv    },
311 	{ "wireless extension", &detect_link_wlan    },
312 	{ "IFF_RUNNING"       , &detect_link_iff     },
313 };
314 
strstatus(int status)315 static const char *strstatus(int status)
316 {
317 	if (status == IFSTATUS_ERR)
318 		return "error";
319 	return "down\0up" + (status * 5);
320 }
321 
run_script(const char * action)322 static int run_script(const char *action)
323 {
324 	char *env_PREVIOUS, *env_CURRENT;
325 	char *argv[5];
326 	int r;
327 
328 	bb_info_msg("executing '%s %s %s'", G.script_name, G.iface, action);
329 
330 	argv[0] = (char*) G.script_name;
331 	argv[1] = (char*) G.iface;
332 	argv[2] = (char*) action;
333 	argv[3] = (char*) G.extra_arg;
334 	argv[4] = NULL;
335 
336 	env_PREVIOUS = xasprintf("%s=%s", IFPLUGD_ENV_PREVIOUS, strstatus(G.iface_prev_status));
337 	putenv(env_PREVIOUS);
338 	env_CURRENT = xasprintf("%s=%s", IFPLUGD_ENV_CURRENT, strstatus(G.iface_last_status));
339 	putenv(env_CURRENT);
340 
341 	/* r < 0 - can't exec, 0 <= r < 0x180 - exited, >=0x180 - killed by sig (r-0x180) */
342 	r = spawn_and_wait(argv);
343 
344 	bb_unsetenv_and_free(env_PREVIOUS);
345 	bb_unsetenv_and_free(env_CURRENT);
346 
347 	bb_info_msg("exit code: %d", r & 0xff);
348 	return (option_mask32 & FLAG_IGNORE_RETVAL) ? 0 : r;
349 }
350 
up_iface(void)351 static void up_iface(void)
352 {
353 	struct ifreq ifrequest;
354 
355 	if (!G.iface_exists)
356 		return;
357 
358 	set_ifreq_to_ifname(&ifrequest);
359 	if (network_ioctl(SIOCGIFFLAGS, &ifrequest, "getting interface flags") < 0) {
360 		G.iface_exists = 0;
361 		return;
362 	}
363 
364 	if (!(ifrequest.ifr_flags & IFF_UP)) {
365 		ifrequest.ifr_flags |= IFF_UP;
366 		/* Let user know we mess up with interface */
367 		bb_simple_info_msg("upping interface");
368 		if (network_ioctl(SIOCSIFFLAGS, &ifrequest, "setting interface flags") < 0) {
369 			if (errno != ENODEV && errno != EADDRNOTAVAIL)
370 				xfunc_die();
371 			G.iface_exists = 0;
372 			return;
373 		}
374 	}
375 
376 #if 0 /* why do we mess with IP addr? It's not our business */
377 	if (network_ioctl(SIOCGIFADDR, &ifrequest, "can't get interface address") < 0) {
378 	} else if (ifrequest.ifr_addr.sa_family != AF_INET) {
379 		bb_perror_msg("the interface is not IP-based");
380 	} else {
381 		((struct sockaddr_in*)(&ifrequest.ifr_addr))->sin_addr.s_addr = INADDR_ANY;
382 		network_ioctl(SIOCSIFADDR, &ifrequest, "can't set interface address");
383 	}
384 	network_ioctl(SIOCGIFFLAGS, &ifrequest, "can't get interface flags");
385 #endif
386 }
387 
maybe_up_new_iface(void)388 static void maybe_up_new_iface(void)
389 {
390 	if (!(option_mask32 & FLAG_NO_AUTO))
391 		up_iface();
392 
393 #if 0 /* bloat */
394 	struct ifreq ifrequest;
395 	struct ethtool_drvinfo driver_info;
396 
397 	set_ifreq_to_ifname(&ifrequest);
398 	driver_info.cmd = ETHTOOL_GDRVINFO;
399 	ifrequest.ifr_data = &driver_info;
400 	if (network_ioctl(SIOCETHTOOL, &ifrequest, NULL) == 0) {
401 		char buf[sizeof("/xx:xx:xx:xx:xx:xx")];
402 
403 		/* Get MAC */
404 		buf[0] = '\0';
405 		set_ifreq_to_ifname(&ifrequest);
406 		if (network_ioctl(SIOCGIFHWADDR, &ifrequest, NULL) == 0) {
407 			sprintf(buf, "/%02X:%02X:%02X:%02X:%02X:%02X",
408 				(uint8_t)(ifrequest.ifr_hwaddr.sa_data[0]),
409 				(uint8_t)(ifrequest.ifr_hwaddr.sa_data[1]),
410 				(uint8_t)(ifrequest.ifr_hwaddr.sa_data[2]),
411 				(uint8_t)(ifrequest.ifr_hwaddr.sa_data[3]),
412 				(uint8_t)(ifrequest.ifr_hwaddr.sa_data[4]),
413 				(uint8_t)(ifrequest.ifr_hwaddr.sa_data[5]));
414 		}
415 
416 		bb_info_msg("using interface %s%s with driver<%s> (version: %s)",
417 			G.iface, buf, driver_info.driver, driver_info.version);
418 	}
419 #endif
420 	if (G.api_mode[0] == 'a')
421 		G.api_method_num = API_AUTO;
422 }
423 
detect_link(void)424 static smallint detect_link(void)
425 {
426 	smallint status;
427 
428 	if (!G.iface_exists)
429 		return (option_mask32 & FLAG_MONITOR) ? IFSTATUS_DOWN : IFSTATUS_ERR;
430 
431 	/* Some drivers can't detect link status when the interface is down.
432 	 * I imagine detect_link_iff() is the most vulnerable.
433 	 * That's why -a "noauto" in an option, not a hardwired behavior.
434 	 */
435 	if (!(option_mask32 & FLAG_NO_AUTO))
436 		up_iface();
437 
438 	if (G.api_method_num == API_AUTO) {
439 		int i;
440 		smallint sv_logmode;
441 
442 		sv_logmode = logmode;
443 		for (i = 0; i < ARRAY_SIZE(method_table); i++) {
444 			logmode = LOGMODE_NONE;
445 			status = method_table[i].func();
446 			logmode = sv_logmode;
447 			if (status != IFSTATUS_ERR) {
448 				G.api_method_num = i;
449 				bb_info_msg("using %s detection mode", method_table[i].name);
450 				break;
451 			}
452 		}
453 	} else {
454 		status = method_table[G.api_method_num].func();
455 	}
456 
457 	if (status == IFSTATUS_ERR) {
458 		if (option_mask32 & FLAG_IGNORE_FAIL)
459 			status = IFSTATUS_DOWN;
460 		else if (option_mask32 & FLAG_IGNORE_FAIL_POSITIVE)
461 			status = IFSTATUS_UP;
462 		else if (G.api_mode[0] == 'a')
463 			bb_simple_error_msg("can't detect link status");
464 	}
465 
466 	if (status != G.iface_last_status) {
467 		G.iface_prev_status = G.iface_last_status;
468 		G.iface_last_status = status;
469 	}
470 
471 	return status;
472 }
473 
check_existence_through_netlink(void)474 static NOINLINE int check_existence_through_netlink(void)
475 {
476 	int iface_len;
477 	/* Buffer was 1K, but on linux-3.9.9 it was reported to be too small.
478 	 * netlink.h: "limit to 8K to avoid MSG_TRUNC when PAGE_SIZE is very large".
479 	 * Note: on error returns (-1) we exit, no need to free replybuf.
480 	 */
481 	enum { BUF_SIZE = 8 * 1024 };
482 	char *replybuf = xmalloc(BUF_SIZE);
483 
484 	iface_len = strlen(G.iface);
485 	while (1) {
486 		struct nlmsghdr *mhdr;
487 		ssize_t bytes;
488 
489 		bytes = recv(netlink_fd, replybuf, BUF_SIZE, MSG_DONTWAIT);
490 		if (bytes < 0) {
491 			if (errno == EAGAIN)
492 				goto ret;
493 			if (errno == EINTR)
494 				continue;
495 			bb_simple_perror_msg("netlink: recv");
496 			return -1;
497 		}
498 
499 		mhdr = (struct nlmsghdr*)replybuf;
500 		while (bytes > 0) {
501 			if (!NLMSG_OK(mhdr, bytes)) {
502 				bb_simple_error_msg("netlink packet too small or truncated");
503 				return -1;
504 			}
505 
506 			if (mhdr->nlmsg_type == RTM_NEWLINK || mhdr->nlmsg_type == RTM_DELLINK) {
507 				struct rtattr *attr;
508 				int attr_len;
509 
510 				if (mhdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) {
511 					bb_simple_error_msg("netlink packet too small or truncated");
512 					return -1;
513 				}
514 
515 				attr = IFLA_RTA(NLMSG_DATA(mhdr));
516 				attr_len = IFLA_PAYLOAD(mhdr);
517 
518 				while (RTA_OK(attr, attr_len)) {
519 					if (attr->rta_type == IFLA_IFNAME) {
520 						int len = RTA_PAYLOAD(attr);
521 						if (len > IFNAMSIZ)
522 							len = IFNAMSIZ;
523 						if (iface_len <= len
524 						 && strncmp(G.iface, RTA_DATA(attr), len) == 0
525 						) {
526 							G.iface_exists = (mhdr->nlmsg_type == RTM_NEWLINK);
527 						}
528 					}
529 					attr = RTA_NEXT(attr, attr_len);
530 				}
531 			}
532 
533 			mhdr = NLMSG_NEXT(mhdr, bytes);
534 		}
535 	}
536 
537  ret:
538 	free(replybuf);
539 	return G.iface_exists;
540 }
541 
542 #if ENABLE_FEATURE_PIDFILE
read_pid(const char * filename)543 static NOINLINE pid_t read_pid(const char *filename)
544 {
545 	int len;
546 	char buf[128];
547 
548 	len = open_read_close(filename, buf, 127);
549 	if (len > 0) {
550 		buf[len] = '\0';
551 		/* returns ULONG_MAX on error => -1 */
552 		return bb_strtoul(buf, NULL, 10);
553 	}
554 	return 0;
555 }
556 #endif
557 
558 int ifplugd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
ifplugd_main(int argc UNUSED_PARAM,char ** argv)559 int ifplugd_main(int argc UNUSED_PARAM, char **argv)
560 {
561 	int iface_status;
562 	int delay_time;
563 	const char *iface_status_str;
564 	struct pollfd netlink_pollfd[1];
565 	unsigned opts;
566 	const char *api_mode_found;
567 #if ENABLE_FEATURE_PIDFILE
568 	char *pidfile_name;
569 	pid_t pid_from_pidfile;
570 #endif
571 
572 	INIT_G();
573 
574 	opts = getopt32(argv, OPTION_STR,
575 		&G.iface, &G.script_name, &G.poll_time, &G.delay_up,
576 		&G.delay_down, &G.api_mode, &G.extra_arg);
577 	G.poll_time *= 1000;
578 
579 	applet_name = xasprintf("ifplugd(%s)", G.iface);
580 
581 #if ENABLE_FEATURE_PIDFILE
582 	pidfile_name = xasprintf(CONFIG_PID_FILE_PATH "/ifplugd.%s.pid", G.iface);
583 	pid_from_pidfile = read_pid(pidfile_name);
584 
585 	if (opts & FLAG_KILL) {
586 		if (pid_from_pidfile > 0)
587 			/* Upstream tool use SIGINT for -k */
588 			kill(pid_from_pidfile, SIGINT);
589 		return EXIT_SUCCESS;
590 	}
591 
592 	if (pid_from_pidfile > 0 && kill(pid_from_pidfile, 0) == 0)
593 		bb_simple_error_msg_and_die("daemon already running");
594 #endif
595 
596 	api_mode_found = strchr(api_modes, G.api_mode[0]);
597 	if (!api_mode_found)
598 		bb_error_msg_and_die("unknown API mode '%s'", G.api_mode);
599 	G.api_method_num = api_mode_found - api_modes;
600 
601 	if (!(opts & FLAG_NO_DAEMON))
602 		bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
603 
604 	xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), ioctl_fd);
605 	if (opts & FLAG_MONITOR) {
606 		int fd = create_and_bind_to_netlink(NETLINK_ROUTE, RTMGRP_LINK, 0);
607 		xmove_fd(fd, netlink_fd);
608 	}
609 
610 	write_pidfile(pidfile_name);
611 
612 	/* this can't be moved before socket creation */
613 	if (!(opts & FLAG_NO_SYSLOG)) {
614 		openlog(applet_name, 0, LOG_DAEMON);
615 		logmode |= LOGMODE_SYSLOG;
616 	}
617 
618 	bb_signals(0
619 		| (1 << SIGINT )
620 		| (1 << SIGTERM)
621 		| (1 << SIGQUIT)
622 		| (1 << SIGHUP ) /* why we ignore it? */
623 		/* | (1 << SIGCHLD) - run_script does not use it anymore */
624 		, record_signo);
625 
626 	bb_info_msg("started: %s", bb_banner);
627 
628 	if (opts & FLAG_MONITOR) {
629 		struct ifreq ifrequest;
630 		set_ifreq_to_ifname(&ifrequest);
631 		G.iface_exists = (network_ioctl(SIOCGIFINDEX, &ifrequest, NULL) == 0);
632 	}
633 
634 	if (G.iface_exists)
635 		maybe_up_new_iface();
636 
637 	iface_status = detect_link();
638 	if (iface_status == IFSTATUS_ERR)
639 		goto exiting;
640 	iface_status_str = strstatus(iface_status);
641 
642 	if (opts & FLAG_MONITOR) {
643 		bb_info_msg("interface %s",
644 			G.iface_exists ? "exists"
645 			: "doesn't exist, waiting");
646 	}
647 	/* else we assume it always exists, but don't mislead user
648 	 * by potentially lying that it really exists */
649 
650 	if (G.iface_exists) {
651 		bb_info_msg("link is %s", iface_status_str);
652 	}
653 
654 	if ((!(opts & FLAG_NO_STARTUP)
655 	     && iface_status == IFSTATUS_UP
656 	    )
657 	 || (opts & FLAG_INITIAL_DOWN)
658 	) {
659 		if (run_script(iface_status_str) != 0)
660 			goto exiting;
661 	}
662 
663 	/* Main loop */
664 	netlink_pollfd[0].fd = netlink_fd;
665 	netlink_pollfd[0].events = POLLIN;
666 	delay_time = 0;
667 	while (1) {
668 		int iface_status_old;
669 
670 		switch (bb_got_signal) {
671 		case SIGINT:
672 		case SIGTERM:
673 			bb_got_signal = 0;
674 			goto cleanup;
675 		case SIGQUIT:
676 			bb_got_signal = 0;
677 			goto exiting;
678 		default:
679 			bb_got_signal = 0;
680 		/* do not clear bb_got_signal if already 0, this can lose signals */
681 		case 0:
682 			break;
683 		}
684 
685 		if (poll(netlink_pollfd,
686 				(opts & FLAG_MONITOR) ? 1 : 0,
687 				G.poll_time
688 			) < 0
689 		) {
690 			if (errno == EINTR)
691 				continue;
692 			bb_simple_perror_msg("poll");
693 			goto exiting;
694 		}
695 
696 		if ((opts & FLAG_MONITOR)
697 		 && (netlink_pollfd[0].revents & POLLIN)
698 		) {
699 			int iface_exists_old;
700 
701 			iface_exists_old = G.iface_exists;
702 			G.iface_exists = check_existence_through_netlink();
703 			if (G.iface_exists < 0) /* error */
704 				goto exiting;
705 			if (iface_exists_old != G.iface_exists) {
706 				bb_info_msg("interface %sappeared",
707 						G.iface_exists ? "" : "dis");
708 				if (G.iface_exists)
709 					maybe_up_new_iface();
710 			}
711 		}
712 
713 		/* note: if !G.iface_exists, returns DOWN */
714 		iface_status_old = iface_status;
715 		iface_status = detect_link();
716 		if (iface_status == IFSTATUS_ERR) {
717 			if (!(opts & FLAG_MONITOR))
718 				goto exiting;
719 			iface_status = IFSTATUS_DOWN;
720 		}
721 		iface_status_str = strstatus(iface_status);
722 
723 		if (iface_status_old != iface_status) {
724 			bb_info_msg("link is %s", iface_status_str);
725 
726 			if (delay_time) {
727 				/* link restored its old status before
728 				 * we ran script. don't run the script: */
729 				delay_time = 0;
730 			} else {
731 				delay_time = monotonic_sec();
732 				if (iface_status == IFSTATUS_UP)
733 					delay_time += G.delay_up;
734 				if (iface_status == IFSTATUS_DOWN)
735 					delay_time += G.delay_down;
736 #if 0  /* if you are back in 1970... */
737 				if (delay_time == 0) {
738 					sleep1();
739 					delay_time = 1;
740 				}
741 #endif
742 			}
743 		}
744 
745 		if (delay_time && (int)(monotonic_sec() - delay_time) >= 0) {
746 			if (run_script(iface_status_str) != 0)
747 				goto exiting;
748 			delay_time = 0;
749 		}
750 	} /* while (1) */
751 
752  cleanup:
753 	if (!(opts & FLAG_NO_SHUTDOWN)
754 	 && (iface_status == IFSTATUS_UP
755 	     || (iface_status == IFSTATUS_DOWN && delay_time)
756 	    )
757 	) {
758 		setenv(IFPLUGD_ENV_PREVIOUS, strstatus(iface_status), 1);
759 		setenv(IFPLUGD_ENV_CURRENT, strstatus(-1), 1);
760 		run_script("down\0up"); /* reusing string */
761 	}
762 
763  exiting:
764 	remove_pidfile(pidfile_name);
765 	bb_simple_error_msg_and_die("exiting");
766 }
767