ping: try SOCK_DGRAM if no root privileges
[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         /* Buffer was 1K, but on linux-3.9.9 it was reported to be too small.
455          * netlink.h: "limit to 8K to avoid MSG_TRUNC when PAGE_SIZE is very large".
456          * Note: on error returns (-1) we exit, no need to free replybuf.
457          */
458         enum { BUF_SIZE = 8 * 1024 };
459         char *replybuf = xmalloc(BUF_SIZE);
460
461         iface_len = strlen(G.iface);
462         while (1) {
463                 struct nlmsghdr *mhdr;
464                 ssize_t bytes;
465
466                 bytes = recv(netlink_fd, replybuf, BUF_SIZE, MSG_DONTWAIT);
467                 if (bytes < 0) {
468                         if (errno == EAGAIN)
469                                 goto ret;
470                         if (errno == EINTR)
471                                 continue;
472                         bb_perror_msg("netlink: recv");
473                         return -1;
474                 }
475
476                 mhdr = (struct nlmsghdr*)replybuf;
477                 while (bytes > 0) {
478                         if (!NLMSG_OK(mhdr, bytes)) {
479                                 bb_error_msg("netlink packet too small or truncated");
480                                 return -1;
481                         }
482
483                         if (mhdr->nlmsg_type == RTM_NEWLINK || mhdr->nlmsg_type == RTM_DELLINK) {
484                                 struct rtattr *attr;
485                                 int attr_len;
486
487                                 if (mhdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) {
488                                         bb_error_msg("netlink packet too small or truncated");
489                                         return -1;
490                                 }
491
492                                 attr = IFLA_RTA(NLMSG_DATA(mhdr));
493                                 attr_len = IFLA_PAYLOAD(mhdr);
494
495                                 while (RTA_OK(attr, attr_len)) {
496                                         if (attr->rta_type == IFLA_IFNAME) {
497                                                 int len = RTA_PAYLOAD(attr);
498                                                 if (len > IFNAMSIZ)
499                                                         len = IFNAMSIZ;
500                                                 if (iface_len <= len
501                                                  && strncmp(G.iface, RTA_DATA(attr), len) == 0
502                                                 ) {
503                                                         G.iface_exists = (mhdr->nlmsg_type == RTM_NEWLINK);
504                                                 }
505                                         }
506                                         attr = RTA_NEXT(attr, attr_len);
507                                 }
508                         }
509
510                         mhdr = NLMSG_NEXT(mhdr, bytes);
511                 }
512         }
513
514  ret:
515         free(replybuf);
516         return G.iface_exists;
517 }
518
519 #if ENABLE_FEATURE_PIDFILE
520 static NOINLINE pid_t read_pid(const char *filename)
521 {
522         int len;
523         char buf[128];
524
525         len = open_read_close(filename, buf, 127);
526         if (len > 0) {
527                 buf[len] = '\0';
528                 /* returns ULONG_MAX on error => -1 */
529                 return bb_strtoul(buf, NULL, 10);
530         }
531         return 0;
532 }
533 #endif
534
535 int ifplugd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
536 int ifplugd_main(int argc UNUSED_PARAM, char **argv)
537 {
538         int iface_status;
539         int delay_time;
540         const char *iface_status_str;
541         struct pollfd netlink_pollfd[1];
542         unsigned opts;
543         const char *api_mode_found;
544 #if ENABLE_FEATURE_PIDFILE
545         char *pidfile_name;
546         pid_t pid_from_pidfile;
547 #endif
548
549         INIT_G();
550
551         opt_complementary = "t+:u+:d+";
552         opts = getopt32(argv, OPTION_STR,
553                 &G.iface, &G.script_name, &G.poll_time, &G.delay_up,
554                 &G.delay_down, &G.api_mode, &G.extra_arg);
555         G.poll_time *= 1000;
556
557         applet_name = xasprintf("ifplugd(%s)", G.iface);
558
559 #if ENABLE_FEATURE_PIDFILE
560         pidfile_name = xasprintf(CONFIG_PID_FILE_PATH "/ifplugd.%s.pid", G.iface);
561         pid_from_pidfile = read_pid(pidfile_name);
562
563         if (opts & FLAG_KILL) {
564                 if (pid_from_pidfile > 0)
565                         /* Upstream tool use SIGINT for -k */
566                         kill(pid_from_pidfile, SIGINT);
567                 return EXIT_SUCCESS;
568         }
569
570         if (pid_from_pidfile > 0 && kill(pid_from_pidfile, 0) == 0)
571                 bb_error_msg_and_die("daemon already running");
572 #endif
573
574         api_mode_found = strchr(api_modes, G.api_mode[0]);
575         if (!api_mode_found)
576                 bb_error_msg_and_die("unknown API mode '%s'", G.api_mode);
577         G.api_method_num = api_mode_found - api_modes;
578
579         if (!(opts & FLAG_NO_DAEMON))
580                 bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
581
582         xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), ioctl_fd);
583         if (opts & FLAG_MONITOR) {
584                 struct sockaddr_nl addr;
585                 int fd = xsocket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
586
587                 memset(&addr, 0, sizeof(addr));
588                 addr.nl_family = AF_NETLINK;
589                 addr.nl_groups = RTMGRP_LINK;
590                 addr.nl_pid = getpid();
591
592                 xbind(fd, (struct sockaddr*)&addr, sizeof(addr));
593                 xmove_fd(fd, netlink_fd);
594         }
595
596         write_pidfile(pidfile_name);
597
598         /* this can't be moved before socket creation */
599         if (!(opts & FLAG_NO_SYSLOG)) {
600                 openlog(applet_name, 0, LOG_DAEMON);
601                 logmode |= LOGMODE_SYSLOG;
602         }
603
604         bb_signals(0
605                 | (1 << SIGINT )
606                 | (1 << SIGTERM)
607                 | (1 << SIGQUIT)
608                 | (1 << SIGHUP ) /* why we ignore it? */
609                 /* | (1 << SIGCHLD) - run_script does not use it anymore */
610                 , record_signo);
611
612         bb_error_msg("started: %s", bb_banner);
613
614         if (opts & FLAG_MONITOR) {
615                 struct ifreq ifrequest;
616                 set_ifreq_to_ifname(&ifrequest);
617                 G.iface_exists = (network_ioctl(SIOCGIFINDEX, &ifrequest, NULL) == 0);
618         }
619
620         if (G.iface_exists)
621                 maybe_up_new_iface();
622
623         iface_status = detect_link();
624         if (iface_status == IFSTATUS_ERR)
625                 goto exiting;
626         iface_status_str = strstatus(iface_status);
627
628         if (opts & FLAG_MONITOR) {
629                 bb_error_msg("interface %s",
630                         G.iface_exists ? "exists"
631                         : "doesn't exist, waiting");
632         }
633         /* else we assume it always exists, but don't mislead user
634          * by potentially lying that it really exists */
635
636         if (G.iface_exists) {
637                 bb_error_msg("link is %s", iface_status_str);
638         }
639
640         if ((!(opts & FLAG_NO_STARTUP)
641              && iface_status == IFSTATUS_UP
642             )
643          || (opts & FLAG_INITIAL_DOWN)
644         ) {
645                 if (run_script(iface_status_str) != 0)
646                         goto exiting;
647         }
648
649         /* Main loop */
650         netlink_pollfd[0].fd = netlink_fd;
651         netlink_pollfd[0].events = POLLIN;
652         delay_time = 0;
653         while (1) {
654                 int iface_status_old;
655                 int iface_exists_old;
656
657                 switch (bb_got_signal) {
658                 case SIGINT:
659                 case SIGTERM:
660                         bb_got_signal = 0;
661                         goto cleanup;
662                 case SIGQUIT:
663                         bb_got_signal = 0;
664                         goto exiting;
665                 default:
666                         bb_got_signal = 0;
667                         break;
668                 }
669
670                 if (poll(netlink_pollfd,
671                                 (opts & FLAG_MONITOR) ? 1 : 0,
672                                 G.poll_time
673                         ) < 0
674                 ) {
675                         if (errno == EINTR)
676                                 continue;
677                         bb_perror_msg("poll");
678                         goto exiting;
679                 }
680
681                 iface_status_old = iface_status;
682                 iface_exists_old = G.iface_exists;
683
684                 if ((opts & FLAG_MONITOR)
685                  && (netlink_pollfd[0].revents & POLLIN)
686                 ) {
687                         G.iface_exists = check_existence_through_netlink();
688                         if (G.iface_exists < 0) /* error */
689                                 goto exiting;
690                         if (iface_exists_old != G.iface_exists) {
691                                 bb_error_msg("interface %sappeared",
692                                                 G.iface_exists ? "" : "dis");
693                                 if (G.iface_exists)
694                                         maybe_up_new_iface();
695                         }
696                 }
697
698                 /* note: if !G.iface_exists, returns DOWN */
699                 iface_status = detect_link();
700                 if (iface_status == IFSTATUS_ERR) {
701                         if (!(opts & FLAG_MONITOR))
702                                 goto exiting;
703                         iface_status = IFSTATUS_DOWN;
704                 }
705                 iface_status_str = strstatus(iface_status);
706
707                 if (iface_status_old != iface_status) {
708                         bb_error_msg("link is %s", iface_status_str);
709
710                         if (delay_time) {
711                                 /* link restored its old status before
712                                  * we run script. don't run the script: */
713                                 delay_time = 0;
714                         } else {
715                                 delay_time = monotonic_sec();
716                                 if (iface_status == IFSTATUS_UP)
717                                         delay_time += G.delay_up;
718                                 if (iface_status == IFSTATUS_DOWN)
719                                         delay_time += G.delay_down;
720                                 if (delay_time == 0)
721                                         delay_time++;
722                         }
723                 }
724
725                 if (delay_time && (int)(monotonic_sec() - delay_time) >= 0) {
726                         delay_time = 0;
727                         if (run_script(iface_status_str) != 0)
728                                 goto exiting;
729                 }
730         } /* while (1) */
731
732  cleanup:
733         if (!(opts & FLAG_NO_SHUTDOWN)
734          && (iface_status == IFSTATUS_UP
735              || (iface_status == IFSTATUS_DOWN && delay_time)
736             )
737         ) {
738                 setenv(IFPLUGD_ENV_PREVIOUS, strstatus(iface_status), 1);
739                 setenv(IFPLUGD_ENV_CURRENT, strstatus(-1), 1);
740                 run_script("down\0up"); /* reusing string */
741         }
742
743  exiting:
744         remove_pidfile(pidfile_name);
745         bb_error_msg_and_die("exiting");
746 }