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