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