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