f398cca22bd48f0c12703d588d5f1e9c62394701
[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         char replybuf[1024];
489
490         while (1) {
491                 struct nlmsghdr *mhdr;
492                 ssize_t bytes;
493
494                 bytes = recv(netlink_fd, &replybuf, sizeof(replybuf), MSG_DONTWAIT);
495                 if (bytes < 0) {
496                         if (errno == EAGAIN)
497                                 return G.iface_exists;
498                         if (errno == EINTR)
499                                 continue;
500
501                         bb_perror_msg("netlink: recv");
502                         return -1;
503                 }
504
505                 mhdr = (struct nlmsghdr*)replybuf;
506                 while (bytes > 0) {
507                         if (!NLMSG_OK(mhdr, bytes)) {
508                                 bb_error_msg("netlink packet too small or truncated");
509                                 return -1;
510                         }
511
512                         if (mhdr->nlmsg_type == RTM_NEWLINK || mhdr->nlmsg_type == RTM_DELLINK) {
513                                 struct rtattr *attr;
514                                 int attr_len;
515
516                                 if (mhdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) {
517                                         bb_error_msg("netlink packet too small or truncated");
518                                         return -1;
519                                 }
520
521                                 attr = IFLA_RTA(NLMSG_DATA(mhdr));
522                                 attr_len = IFLA_PAYLOAD(mhdr);
523
524                                 while (RTA_OK(attr, attr_len)) {
525                                         if (attr->rta_type == IFLA_IFNAME) {
526                                                 int len = RTA_PAYLOAD(attr);
527                                                 if (len > IFNAMSIZ)
528                                                         len = IFNAMSIZ;
529
530                                                 if (strncmp(G.iface, RTA_DATA(attr), len) == 0) {
531                                                         G.iface_exists = (mhdr->nlmsg_type == RTM_NEWLINK);
532                                                 }
533                                         }
534                                         attr = RTA_NEXT(attr, attr_len);
535                                 }
536                         }
537
538                         mhdr = NLMSG_NEXT(mhdr, bytes);
539                 }
540         }
541
542         return G.iface_exists;
543 }
544
545 static NOINLINE int netlink_open(void)
546 {
547         int fd;
548         struct sockaddr_nl addr;
549
550         fd = xsocket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
551
552         memset(&addr, 0, sizeof(addr));
553         addr.nl_family = AF_NETLINK;
554         addr.nl_groups = RTMGRP_LINK;
555         addr.nl_pid = getpid();
556
557         xbind(fd, (struct sockaddr*)&addr, sizeof(addr));
558
559         return fd;
560 }
561
562 #if ENABLE_FEATURE_PIDFILE
563 static NOINLINE pid_t read_pid(const char *filename)
564 {
565         int len;
566         char buf[128];
567
568         len = open_read_close(filename, buf, 127);
569         if (len > 0) {
570                 buf[len] = '\0';
571                 /* returns ULONG_MAX on error => -1 */
572                 return bb_strtoul(buf, NULL, 10);
573         }
574         return 0;
575 }
576 #endif
577
578 int ifplugd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
579 int ifplugd_main(int argc UNUSED_PARAM, char **argv)
580 {
581         int iface_status;
582         int delay_time;
583         const char *iface_status_str;
584         struct pollfd netlink_pollfd[1];
585         unsigned opts;
586 #if ENABLE_FEATURE_PIDFILE
587         char *pidfile_name;
588         pid_t pid_from_pidfile;
589 #endif
590
591         INIT_G();
592
593         opt_complementary = "t+:u+:d+";
594         opts = getopt32(argv, OPTION_STR,
595                 &G.iface, &G.script_name, &G.poll_time, &G.delay_up,
596                 &G.delay_down, &G.api_mode, &G.extra_arg);
597         G.poll_time *= 1000;
598
599         applet_name = xasprintf("ifplugd(%s)", G.iface);
600
601 #if ENABLE_FEATURE_PIDFILE
602         pidfile_name = xasprintf(_PATH_VARRUN"ifplugd.%s.pid", G.iface);
603         pid_from_pidfile = read_pid(pidfile_name);
604
605         if (opts & FLAG_KILL) {
606                 if (pid_from_pidfile > 0)
607                         kill(pid_from_pidfile, SIGQUIT);
608                 return EXIT_SUCCESS;
609         }
610
611         if (pid_from_pidfile > 0 && kill(pid_from_pidfile, 0) == 0)
612                 bb_error_msg_and_die("daemon already running");
613 #endif
614
615         switch (G.api_mode[0]) {
616         case API_AUTO:
617                 G.detect_link_func = detect_link_auto;
618                 break;
619         case API_ETHTOOL:
620                 G.detect_link_func = detect_link_ethtool;
621                 break;
622         case API_MII:
623                 G.detect_link_func = detect_link_mii;
624                 break;
625         case API_PRIVATE:
626                 G.detect_link_func = detect_link_priv;
627                 break;
628         case API_WLAN:
629                 G.detect_link_func = detect_link_wlan;
630                 break;
631         case API_IFF:
632                 G.detect_link_func = detect_link_iff;
633                 break;
634         default:
635                 bb_error_msg_and_die("unknown API mode '%s'", G.api_mode);
636         }
637
638         if (!(opts & FLAG_NO_DAEMON))
639                 bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
640
641         xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), ioctl_fd);
642         if (opts & FLAG_MONITOR) {
643                 xmove_fd(netlink_open(), netlink_fd);
644         }
645
646         write_pidfile(pidfile_name);
647
648         /* this can't be moved before socket creation */
649         if (!(opts & FLAG_NO_SYSLOG)) {
650                 openlog(applet_name, 0, LOG_DAEMON);
651                 logmode |= LOGMODE_SYSLOG;
652         }
653
654         bb_signals(0
655                 | (1 << SIGINT )
656                 | (1 << SIGTERM)
657                 | (1 << SIGQUIT)
658                 | (1 << SIGHUP ) /* why we ignore it? */
659                 /* | (1 << SIGCHLD) - run_script does not use it anymore */
660                 , record_signo);
661
662         bb_error_msg("started: %s", bb_banner);
663
664         if (opts & FLAG_MONITOR) {
665                 struct ifreq ifrequest;
666                 set_ifreq_to_ifname(&ifrequest);
667                 G.iface_exists = (network_ioctl(SIOCGIFINDEX, &ifrequest, NULL) == 0);
668         }
669
670         if (G.iface_exists)
671                 maybe_up_new_iface();
672
673         iface_status = detect_link();
674         if (iface_status == IFSTATUS_ERR)
675                 goto exiting;
676         iface_status_str = strstatus(iface_status);
677
678         if (opts & FLAG_MONITOR) {
679                 bb_error_msg("interface %s",
680                         G.iface_exists ? "exists"
681                         : "doesn't exist, waiting");
682         }
683         /* else we assume it always exists, but don't mislead user
684          * by potentially lying that it really exists */
685
686         if (G.iface_exists) {
687                 bb_error_msg("link is %s", iface_status_str);
688         }
689
690         if ((!(opts & FLAG_NO_STARTUP)
691              && iface_status == IFSTATUS_UP
692             )
693          || (opts & FLAG_INITIAL_DOWN)
694         ) {
695                 if (run_script(iface_status_str) != 0)
696                         goto exiting;
697         }
698
699         /* Main loop */
700         netlink_pollfd[0].fd = netlink_fd;
701         netlink_pollfd[0].events = POLLIN;
702         delay_time = 0;
703         while (1) {
704                 int iface_status_old;
705                 int iface_exists_old;
706
707                 switch (bb_got_signal) {
708                 case SIGINT:
709                 case SIGTERM:
710                         bb_got_signal = 0;
711                         goto cleanup;
712                 case SIGQUIT:
713                         bb_got_signal = 0;
714                         goto exiting;
715                 default:
716                         bb_got_signal = 0;
717                         break;
718                 }
719
720                 if (poll(netlink_pollfd,
721                                 (opts & FLAG_MONITOR) ? 1 : 0,
722                                 G.poll_time
723                         ) < 0
724                 ) {
725                         if (errno == EINTR)
726                                 continue;
727                         bb_perror_msg("poll");
728                         goto exiting;
729                 }
730
731                 iface_status_old = iface_status;
732                 iface_exists_old = G.iface_exists;
733
734                 if ((opts & FLAG_MONITOR)
735                  && (netlink_pollfd[0].revents & POLLIN)
736                 ) {
737                         G.iface_exists = check_existence_through_netlink();
738                         if (G.iface_exists < 0) /* error */
739                                 goto exiting;
740                         if (iface_exists_old != G.iface_exists) {
741                                 bb_error_msg("interface %sappeared",
742                                                 G.iface_exists ? "" : "dis");
743                                 if (G.iface_exists)
744                                         maybe_up_new_iface();
745                         }
746                 }
747
748                 /* note: if !G.iface_exists, returns DOWN */
749                 iface_status = detect_link();
750                 if (iface_status == IFSTATUS_ERR) {
751                         if (!(opts & FLAG_MONITOR))
752                                 goto exiting;
753                         iface_status = IFSTATUS_DOWN;
754                 }
755                 iface_status_str = strstatus(iface_status);
756
757                 if (iface_status_old != iface_status) {
758                         bb_error_msg("link is %s", iface_status_str);
759
760                         if (delay_time) {
761                                 /* link restored its old status before
762                                  * we run script. don't run the script: */
763                                 delay_time = 0;
764                         } else {
765                                 delay_time = monotonic_sec();
766                                 if (iface_status == IFSTATUS_UP)
767                                         delay_time += G.delay_up;
768                                 if (iface_status == IFSTATUS_DOWN)
769                                         delay_time += G.delay_down;
770                                 if (delay_time == 0)
771                                         delay_time++;
772                         }
773                 }
774
775                 if (delay_time && (int)(monotonic_sec() - delay_time) >= 0) {
776                         delay_time = 0;
777                         if (run_script(iface_status_str) != 0)
778                                 goto exiting;
779                 }
780         } /* while (1) */
781
782  cleanup:
783         if (!(opts & FLAG_NO_SHUTDOWN)
784          && (iface_status == IFSTATUS_UP
785              || (iface_status == IFSTATUS_DOWN && delay_time)
786             )
787         ) {
788                 setenv(IFPLUGD_ENV_PREVIOUS, strstatus(iface_status), 1);
789                 setenv(IFPLUGD_ENV_CURRENT, strstatus(-1), 1);
790                 run_script("down\0up"); /* reusing string */
791         }
792
793  exiting:
794         remove_pidfile(pidfile_name);
795         bb_error_msg_and_die("exiting");
796 }