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