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