SSID: Respect ASCII character Label.
[tomato.git] / release / src / router / busybox / networking / ifplugd.c
blobd8358cdfd5f92d25ae70a0d2b34236c897dd40c4
1 /* vi: set sw=4 ts=4: */
2 /*
3 * ifplugd for busybox, based on ifplugd 0.28 (written by Lennart Poettering).
5 * Copyright (C) 2009 Maksym Kryzhanovskyy <xmaks@email.cz>
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 */
10 //usage:#define ifplugd_trivial_usage
11 //usage: "[OPTIONS]"
12 //usage:#define ifplugd_full_usage "\n\n"
13 //usage: "Network interface plug detection daemon\n"
14 //usage: "\n -n Don't daemonize"
15 //usage: "\n -s Don't log to syslog"
16 //usage: "\n -i IFACE Interface"
17 //usage: "\n -f/-F Treat link detection error as link down/link up"
18 //usage: "\n (otherwise exit on error)"
19 //usage: "\n -a Don't up interface at each link probe"
20 //usage: "\n -M Monitor creation/destruction of interface"
21 //usage: "\n (otherwise it must exist)"
22 //usage: "\n -r PROG Script to run"
23 //usage: "\n -x ARG Extra argument for script"
24 //usage: "\n -I Don't exit on nonzero exit code from script"
25 //usage: "\n -p Don't run script on daemon startup"
26 //usage: "\n -q Don't run script on daemon quit"
27 //usage: "\n -l Run script on startup even if no cable is detected"
28 //usage: "\n -t SECS Poll time in seconds"
29 //usage: "\n -u SECS Delay before running script after link up"
30 //usage: "\n -d SECS Delay after link down"
31 //usage: "\n -m MODE API mode (mii, priv, ethtool, wlan, iff, auto)"
32 //usage: "\n -k Kill running daemon"
34 #include "libbb.h"
36 #include "fix_u32.h"
37 #include <linux/if.h>
38 #include <linux/mii.h>
39 #include <linux/ethtool.h>
40 #ifdef HAVE_NET_ETHERNET_H
41 # include <net/ethernet.h>
42 #endif
43 #include <linux/netlink.h>
44 #include <linux/rtnetlink.h>
45 #include <linux/sockios.h>
46 #include <syslog.h>
48 #define __user
49 #include <linux/wireless.h>
52 From initial port to busybox, removed most of the redundancy by
53 converting implementation of a polymorphic interface to the strict
54 functional style. The main role is run a script when link state
55 changed, other activities like audio signal or detailed reports
56 are on the script itself.
58 One questionable point of the design is netlink usage:
60 We have 1 second timeout by default to poll the link status,
61 it is short enough so that there are no real benefits in
62 using netlink to get "instantaneous" interface creation/deletion
63 notifications. We can check for interface existence by just
64 doing some fast ioctl using its name.
66 Netlink code then can be just dropped (1k or more?)
70 #define IFPLUGD_ENV_PREVIOUS "IFPLUGD_PREVIOUS"
71 #define IFPLUGD_ENV_CURRENT "IFPLUGD_CURRENT"
73 enum {
74 FLAG_NO_AUTO = 1 << 0, // -a, Do not enable interface automatically
75 FLAG_NO_DAEMON = 1 << 1, // -n, Do not daemonize
76 FLAG_NO_SYSLOG = 1 << 2, // -s, Do not use syslog, use stderr instead
77 FLAG_IGNORE_FAIL = 1 << 3, // -f, Ignore detection failure, retry instead (failure is treated as DOWN)
78 FLAG_IGNORE_FAIL_POSITIVE = 1 << 4, // -F, Ignore detection failure, retry instead (failure is treated as UP)
79 FLAG_IFACE = 1 << 5, // -i, Specify ethernet interface
80 FLAG_RUN = 1 << 6, // -r, Specify program to execute
81 FLAG_IGNORE_RETVAL = 1 << 7, // -I, Don't exit on nonzero return value of program executed
82 FLAG_POLL_TIME = 1 << 8, // -t, Specify poll time in seconds
83 FLAG_DELAY_UP = 1 << 9, // -u, Specify delay for configuring interface
84 FLAG_DELAY_DOWN = 1 << 10, // -d, Specify delay for deconfiguring interface
85 FLAG_API_MODE = 1 << 11, // -m, Force API mode (mii, priv, ethtool, wlan, auto)
86 FLAG_NO_STARTUP = 1 << 12, // -p, Don't run script on daemon startup
87 FLAG_NO_SHUTDOWN = 1 << 13, // -q, Don't run script on daemon quit
88 FLAG_INITIAL_DOWN = 1 << 14, // -l, Run "down" script on startup if no cable is detected
89 FLAG_EXTRA_ARG = 1 << 15, // -x, Specify an extra argument for action script
90 FLAG_MONITOR = 1 << 16, // -M, Use interface monitoring
91 #if ENABLE_FEATURE_PIDFILE
92 FLAG_KILL = 1 << 17, // -k, Kill a running daemon
93 #endif
95 #if ENABLE_FEATURE_PIDFILE
96 # define OPTION_STR "+ansfFi:r:It:u:d:m:pqlx:Mk"
97 #else
98 # define OPTION_STR "+ansfFi:r:It:u:d:m:pqlx:M"
99 #endif
101 enum { // interface status
102 IFSTATUS_ERR = -1,
103 IFSTATUS_DOWN = 0,
104 IFSTATUS_UP = 1,
107 enum { // constant fds
108 ioctl_fd = 3,
109 netlink_fd = 4,
112 struct globals {
113 smallint iface_last_status;
114 smallint iface_prev_status;
115 smallint iface_exists;
116 smallint api_method_num;
118 /* Used in getopt32, must have sizeof == sizeof(int) */
119 unsigned poll_time;
120 unsigned delay_up;
121 unsigned delay_down;
123 const char *iface;
124 const char *api_mode;
125 const char *script_name;
126 const char *extra_arg;
128 #define G (*ptr_to_globals)
129 #define INIT_G() do { \
130 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
131 G.iface_last_status = -1; \
132 G.iface_exists = 1; \
133 G.poll_time = 1; \
134 G.delay_down = 5; \
135 G.iface = "eth0"; \
136 G.api_mode = "a"; \
137 G.script_name = "/etc/ifplugd/ifplugd.action"; \
138 } while (0)
141 /* Utility routines */
143 static void set_ifreq_to_ifname(struct ifreq *ifreq)
145 memset(ifreq, 0, sizeof(struct ifreq));
146 strncpy_IFNAMSIZ(ifreq->ifr_name, G.iface);
149 static int network_ioctl(int request, void* data, const char *errmsg)
151 int r = ioctl(ioctl_fd, request, data);
152 if (r < 0 && errmsg)
153 bb_perror_msg("%s failed", errmsg);
154 return r;
157 /* Link detection routines and table */
159 static smallint detect_link_mii(void)
161 /* char buffer instead of bona-fide struct avoids aliasing warning */
162 char buf[sizeof(struct ifreq)];
163 struct ifreq *const ifreq = (void *)buf;
165 struct mii_ioctl_data *mii = (void *)&ifreq->ifr_data;
167 set_ifreq_to_ifname(ifreq);
169 if (network_ioctl(SIOCGMIIPHY, ifreq, "SIOCGMIIPHY") < 0) {
170 return IFSTATUS_ERR;
173 mii->reg_num = 1;
175 if (network_ioctl(SIOCGMIIREG, ifreq, "SIOCGMIIREG") < 0) {
176 return IFSTATUS_ERR;
179 return (mii->val_out & 0x0004) ? IFSTATUS_UP : IFSTATUS_DOWN;
182 static smallint detect_link_priv(void)
184 /* char buffer instead of bona-fide struct avoids aliasing warning */
185 char buf[sizeof(struct ifreq)];
186 struct ifreq *const ifreq = (void *)buf;
188 struct mii_ioctl_data *mii = (void *)&ifreq->ifr_data;
190 set_ifreq_to_ifname(ifreq);
192 if (network_ioctl(SIOCDEVPRIVATE, ifreq, "SIOCDEVPRIVATE") < 0) {
193 return IFSTATUS_ERR;
196 mii->reg_num = 1;
198 if (network_ioctl(SIOCDEVPRIVATE+1, ifreq, "SIOCDEVPRIVATE+1") < 0) {
199 return IFSTATUS_ERR;
202 return (mii->val_out & 0x0004) ? IFSTATUS_UP : IFSTATUS_DOWN;
205 static smallint detect_link_ethtool(void)
207 struct ifreq ifreq;
208 struct ethtool_value edata;
210 set_ifreq_to_ifname(&ifreq);
212 edata.cmd = ETHTOOL_GLINK;
213 ifreq.ifr_data = (void*) &edata;
215 if (network_ioctl(SIOCETHTOOL, &ifreq, "ETHTOOL_GLINK") < 0) {
216 return IFSTATUS_ERR;
219 return edata.data ? IFSTATUS_UP : IFSTATUS_DOWN;
222 static smallint detect_link_iff(void)
224 struct ifreq ifreq;
226 set_ifreq_to_ifname(&ifreq);
228 if (network_ioctl(SIOCGIFFLAGS, &ifreq, "SIOCGIFFLAGS") < 0) {
229 return IFSTATUS_ERR;
232 /* If IFF_UP is not set (interface is down), IFF_RUNNING is never set
233 * regardless of link status. Simply continue to report last status -
234 * no point in reporting spurious link downs if interface is disabled
235 * by admin. When/if it will be brought up,
236 * we'll report real link status.
238 if (!(ifreq.ifr_flags & IFF_UP) && G.iface_last_status != IFSTATUS_ERR)
239 return G.iface_last_status;
241 return (ifreq.ifr_flags & IFF_RUNNING) ? IFSTATUS_UP : IFSTATUS_DOWN;
244 static smallint detect_link_wlan(void)
246 int i;
247 struct iwreq iwrequest;
248 uint8_t mac[ETH_ALEN];
250 memset(&iwrequest, 0, sizeof(iwrequest));
251 strncpy_IFNAMSIZ(iwrequest.ifr_ifrn.ifrn_name, G.iface);
253 if (network_ioctl(SIOCGIWAP, &iwrequest, "SIOCGIWAP") < 0) {
254 return IFSTATUS_ERR;
257 memcpy(mac, &iwrequest.u.ap_addr.sa_data, ETH_ALEN);
259 if (mac[0] == 0xFF || mac[0] == 0x44 || mac[0] == 0x00) {
260 for (i = 1; i < ETH_ALEN; ++i) {
261 if (mac[i] != mac[0])
262 return IFSTATUS_UP;
264 return IFSTATUS_DOWN;
267 return IFSTATUS_UP;
270 enum { // api mode
271 API_ETHTOOL, // 'e'
272 API_MII, // 'm'
273 API_PRIVATE, // 'p'
274 API_WLAN, // 'w'
275 API_IFF, // 'i'
276 API_AUTO, // 'a'
279 static const char api_modes[] ALIGN1 = "empwia";
281 static const struct {
282 const char *name;
283 smallint (*func)(void);
284 } method_table[] = {
285 { "SIOCETHTOOL" , &detect_link_ethtool },
286 { "SIOCGMIIPHY" , &detect_link_mii },
287 { "SIOCDEVPRIVATE" , &detect_link_priv },
288 { "wireless extension", &detect_link_wlan },
289 { "IFF_RUNNING" , &detect_link_iff },
294 static const char *strstatus(int status)
296 if (status == IFSTATUS_ERR)
297 return "error";
298 return "down\0up" + (status * 5);
301 static int run_script(const char *action)
303 char *env_PREVIOUS, *env_CURRENT;
304 char *argv[5];
305 int r;
307 bb_error_msg("executing '%s %s %s'", G.script_name, G.iface, action);
309 argv[0] = (char*) G.script_name;
310 argv[1] = (char*) G.iface;
311 argv[2] = (char*) action;
312 argv[3] = (char*) G.extra_arg;
313 argv[4] = NULL;
315 env_PREVIOUS = xasprintf("%s=%s", IFPLUGD_ENV_PREVIOUS, strstatus(G.iface_prev_status));
316 putenv(env_PREVIOUS);
317 env_CURRENT = xasprintf("%s=%s", IFPLUGD_ENV_CURRENT, strstatus(G.iface_last_status));
318 putenv(env_CURRENT);
320 /* r < 0 - can't exec, 0 <= r < 0x180 - exited, >=0x180 - killed by sig (r-0x180) */
321 r = spawn_and_wait(argv);
323 unsetenv(IFPLUGD_ENV_PREVIOUS);
324 unsetenv(IFPLUGD_ENV_CURRENT);
325 free(env_PREVIOUS);
326 free(env_CURRENT);
328 bb_error_msg("exit code: %d", r & 0xff);
329 return (option_mask32 & FLAG_IGNORE_RETVAL) ? 0 : r;
332 static void up_iface(void)
334 struct ifreq ifrequest;
336 if (!G.iface_exists)
337 return;
339 set_ifreq_to_ifname(&ifrequest);
340 if (network_ioctl(SIOCGIFFLAGS, &ifrequest, "getting interface flags") < 0) {
341 G.iface_exists = 0;
342 return;
345 if (!(ifrequest.ifr_flags & IFF_UP)) {
346 ifrequest.ifr_flags |= IFF_UP;
347 /* Let user know we mess up with interface */
348 bb_error_msg("upping interface");
349 if (network_ioctl(SIOCSIFFLAGS, &ifrequest, "setting interface flags") < 0)
350 xfunc_die();
353 #if 0 /* why do we mess with IP addr? It's not our business */
354 if (network_ioctl(SIOCGIFADDR, &ifrequest, "can't get interface address") < 0) {
355 } else if (ifrequest.ifr_addr.sa_family != AF_INET) {
356 bb_perror_msg("the interface is not IP-based");
357 } else {
358 ((struct sockaddr_in*)(&ifrequest.ifr_addr))->sin_addr.s_addr = INADDR_ANY;
359 network_ioctl(SIOCSIFADDR, &ifrequest, "can't set interface address");
361 network_ioctl(SIOCGIFFLAGS, &ifrequest, "can't get interface flags");
362 #endif
365 static void maybe_up_new_iface(void)
367 if (!(option_mask32 & FLAG_NO_AUTO))
368 up_iface();
370 #if 0 /* bloat */
371 struct ifreq ifrequest;
372 struct ethtool_drvinfo driver_info;
374 set_ifreq_to_ifname(&ifrequest);
375 driver_info.cmd = ETHTOOL_GDRVINFO;
376 ifrequest.ifr_data = &driver_info;
377 if (network_ioctl(SIOCETHTOOL, &ifrequest, NULL) == 0) {
378 char buf[sizeof("/xx:xx:xx:xx:xx:xx")];
380 /* Get MAC */
381 buf[0] = '\0';
382 set_ifreq_to_ifname(&ifrequest);
383 if (network_ioctl(SIOCGIFHWADDR, &ifrequest, NULL) == 0) {
384 sprintf(buf, "/%02X:%02X:%02X:%02X:%02X:%02X",
385 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[0]),
386 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[1]),
387 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[2]),
388 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[3]),
389 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[4]),
390 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[5]));
393 bb_error_msg("using interface %s%s with driver<%s> (version: %s)",
394 G.iface, buf, driver_info.driver, driver_info.version);
396 #endif
397 if (G.api_mode[0] == 'a')
398 G.api_method_num = API_AUTO;
401 static smallint detect_link(void)
403 smallint status;
405 if (!G.iface_exists)
406 return (option_mask32 & FLAG_MONITOR) ? IFSTATUS_DOWN : IFSTATUS_ERR;
408 /* Some drivers can't detect link status when the interface is down.
409 * I imagine detect_link_iff() is the most vulnerable.
410 * That's why -a "noauto" in an option, not a hardwired behavior.
412 if (!(option_mask32 & FLAG_NO_AUTO))
413 up_iface();
415 if (G.api_method_num == API_AUTO) {
416 int i;
417 smallint sv_logmode;
419 sv_logmode = logmode;
420 for (i = 0; i < ARRAY_SIZE(method_table); i++) {
421 logmode = LOGMODE_NONE;
422 status = method_table[i].func();
423 logmode = sv_logmode;
424 if (status != IFSTATUS_ERR) {
425 G.api_method_num = i;
426 bb_error_msg("using %s detection mode", method_table[i].name);
427 break;
430 } else {
431 status = method_table[G.api_method_num].func();
434 if (status == IFSTATUS_ERR) {
435 if (option_mask32 & FLAG_IGNORE_FAIL)
436 status = IFSTATUS_DOWN;
437 else if (option_mask32 & FLAG_IGNORE_FAIL_POSITIVE)
438 status = IFSTATUS_UP;
439 else if (G.api_mode[0] == 'a')
440 bb_error_msg("can't detect link status");
443 if (status != G.iface_last_status) {
444 G.iface_prev_status = G.iface_last_status;
445 G.iface_last_status = status;
448 return status;
451 static NOINLINE int check_existence_through_netlink(void)
453 int iface_len;
454 char replybuf[1024];
456 iface_len = strlen(G.iface);
457 while (1) {
458 struct nlmsghdr *mhdr;
459 ssize_t bytes;
461 bytes = recv(netlink_fd, &replybuf, sizeof(replybuf), MSG_DONTWAIT);
462 if (bytes < 0) {
463 if (errno == EAGAIN)
464 return G.iface_exists;
465 if (errno == EINTR)
466 continue;
468 bb_perror_msg("netlink: recv");
469 return -1;
472 mhdr = (struct nlmsghdr*)replybuf;
473 while (bytes > 0) {
474 if (!NLMSG_OK(mhdr, bytes)) {
475 bb_error_msg("netlink packet too small or truncated");
476 return -1;
479 if (mhdr->nlmsg_type == RTM_NEWLINK || mhdr->nlmsg_type == RTM_DELLINK) {
480 struct rtattr *attr;
481 int attr_len;
483 if (mhdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) {
484 bb_error_msg("netlink packet too small or truncated");
485 return -1;
488 attr = IFLA_RTA(NLMSG_DATA(mhdr));
489 attr_len = IFLA_PAYLOAD(mhdr);
491 while (RTA_OK(attr, attr_len)) {
492 if (attr->rta_type == IFLA_IFNAME) {
493 int len = RTA_PAYLOAD(attr);
494 if (len > IFNAMSIZ)
495 len = IFNAMSIZ;
496 if (iface_len <= len
497 && strncmp(G.iface, RTA_DATA(attr), len) == 0
499 G.iface_exists = (mhdr->nlmsg_type == RTM_NEWLINK);
502 attr = RTA_NEXT(attr, attr_len);
506 mhdr = NLMSG_NEXT(mhdr, bytes);
510 return G.iface_exists;
513 #if ENABLE_FEATURE_PIDFILE
514 static NOINLINE pid_t read_pid(const char *filename)
516 int len;
517 char buf[128];
519 len = open_read_close(filename, buf, 127);
520 if (len > 0) {
521 buf[len] = '\0';
522 /* returns ULONG_MAX on error => -1 */
523 return bb_strtoul(buf, NULL, 10);
525 return 0;
527 #endif
529 int ifplugd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
530 int ifplugd_main(int argc UNUSED_PARAM, char **argv)
532 int iface_status;
533 int delay_time;
534 const char *iface_status_str;
535 struct pollfd netlink_pollfd[1];
536 unsigned opts;
537 const char *api_mode_found;
538 #if ENABLE_FEATURE_PIDFILE
539 char *pidfile_name;
540 pid_t pid_from_pidfile;
541 #endif
543 INIT_G();
545 opt_complementary = "t+:u+:d+";
546 opts = getopt32(argv, OPTION_STR,
547 &G.iface, &G.script_name, &G.poll_time, &G.delay_up,
548 &G.delay_down, &G.api_mode, &G.extra_arg);
549 G.poll_time *= 1000;
551 applet_name = xasprintf("ifplugd(%s)", G.iface);
553 #if ENABLE_FEATURE_PIDFILE
554 pidfile_name = xasprintf(_PATH_VARRUN"ifplugd.%s.pid", G.iface);
555 pid_from_pidfile = read_pid(pidfile_name);
557 if (opts & FLAG_KILL) {
558 if (pid_from_pidfile > 0)
559 kill(pid_from_pidfile, SIGQUIT);
560 return EXIT_SUCCESS;
563 if (pid_from_pidfile > 0 && kill(pid_from_pidfile, 0) == 0)
564 bb_error_msg_and_die("daemon already running");
565 #endif
567 api_mode_found = strchr(api_modes, G.api_mode[0]);
568 if (!api_mode_found)
569 bb_error_msg_and_die("unknown API mode '%s'", G.api_mode);
570 G.api_method_num = api_mode_found - api_modes;
572 if (!(opts & FLAG_NO_DAEMON))
573 bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
575 xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), ioctl_fd);
576 if (opts & FLAG_MONITOR) {
577 struct sockaddr_nl addr;
578 int fd = xsocket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
580 memset(&addr, 0, sizeof(addr));
581 addr.nl_family = AF_NETLINK;
582 addr.nl_groups = RTMGRP_LINK;
583 addr.nl_pid = getpid();
585 xbind(fd, (struct sockaddr*)&addr, sizeof(addr));
586 xmove_fd(fd, netlink_fd);
589 write_pidfile(pidfile_name);
591 /* this can't be moved before socket creation */
592 if (!(opts & FLAG_NO_SYSLOG)) {
593 openlog(applet_name, 0, LOG_DAEMON);
594 logmode |= LOGMODE_SYSLOG;
597 bb_signals(0
598 | (1 << SIGINT )
599 | (1 << SIGTERM)
600 | (1 << SIGQUIT)
601 | (1 << SIGHUP ) /* why we ignore it? */
602 /* | (1 << SIGCHLD) - run_script does not use it anymore */
603 , record_signo);
605 bb_error_msg("started: %s", bb_banner);
607 if (opts & FLAG_MONITOR) {
608 struct ifreq ifrequest;
609 set_ifreq_to_ifname(&ifrequest);
610 G.iface_exists = (network_ioctl(SIOCGIFINDEX, &ifrequest, NULL) == 0);
613 if (G.iface_exists)
614 maybe_up_new_iface();
616 iface_status = detect_link();
617 if (iface_status == IFSTATUS_ERR)
618 goto exiting;
619 iface_status_str = strstatus(iface_status);
621 if (opts & FLAG_MONITOR) {
622 bb_error_msg("interface %s",
623 G.iface_exists ? "exists"
624 : "doesn't exist, waiting");
626 /* else we assume it always exists, but don't mislead user
627 * by potentially lying that it really exists */
629 if (G.iface_exists) {
630 bb_error_msg("link is %s", iface_status_str);
633 if ((!(opts & FLAG_NO_STARTUP)
634 && iface_status == IFSTATUS_UP
636 || (opts & FLAG_INITIAL_DOWN)
638 if (run_script(iface_status_str) != 0)
639 goto exiting;
642 /* Main loop */
643 netlink_pollfd[0].fd = netlink_fd;
644 netlink_pollfd[0].events = POLLIN;
645 delay_time = 0;
646 while (1) {
647 int iface_status_old;
648 int iface_exists_old;
650 switch (bb_got_signal) {
651 case SIGINT:
652 case SIGTERM:
653 bb_got_signal = 0;
654 goto cleanup;
655 case SIGQUIT:
656 bb_got_signal = 0;
657 goto exiting;
658 default:
659 bb_got_signal = 0;
660 break;
663 if (poll(netlink_pollfd,
664 (opts & FLAG_MONITOR) ? 1 : 0,
665 G.poll_time
666 ) < 0
668 if (errno == EINTR)
669 continue;
670 bb_perror_msg("poll");
671 goto exiting;
674 iface_status_old = iface_status;
675 iface_exists_old = G.iface_exists;
677 if ((opts & FLAG_MONITOR)
678 && (netlink_pollfd[0].revents & POLLIN)
680 G.iface_exists = check_existence_through_netlink();
681 if (G.iface_exists < 0) /* error */
682 goto exiting;
683 if (iface_exists_old != G.iface_exists) {
684 bb_error_msg("interface %sappeared",
685 G.iface_exists ? "" : "dis");
686 if (G.iface_exists)
687 maybe_up_new_iface();
691 /* note: if !G.iface_exists, returns DOWN */
692 iface_status = detect_link();
693 if (iface_status == IFSTATUS_ERR) {
694 if (!(opts & FLAG_MONITOR))
695 goto exiting;
696 iface_status = IFSTATUS_DOWN;
698 iface_status_str = strstatus(iface_status);
700 if (iface_status_old != iface_status) {
701 bb_error_msg("link is %s", iface_status_str);
703 if (delay_time) {
704 /* link restored its old status before
705 * we run script. don't run the script: */
706 delay_time = 0;
707 } else {
708 delay_time = monotonic_sec();
709 if (iface_status == IFSTATUS_UP)
710 delay_time += G.delay_up;
711 if (iface_status == IFSTATUS_DOWN)
712 delay_time += G.delay_down;
713 if (delay_time == 0)
714 delay_time++;
718 if (delay_time && (int)(monotonic_sec() - delay_time) >= 0) {
719 delay_time = 0;
720 if (run_script(iface_status_str) != 0)
721 goto exiting;
723 } /* while (1) */
725 cleanup:
726 if (!(opts & FLAG_NO_SHUTDOWN)
727 && (iface_status == IFSTATUS_UP
728 || (iface_status == IFSTATUS_DOWN && delay_time)
731 setenv(IFPLUGD_ENV_PREVIOUS, strstatus(iface_status), 1);
732 setenv(IFPLUGD_ENV_CURRENT, strstatus(-1), 1);
733 run_script("down\0up"); /* reusing string */
736 exiting:
737 remove_pidfile(pidfile_name);
738 bb_error_msg_and_die("exiting");