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