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