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