Fix compile failure in ll_proto.c
[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(_PATH_VARRUN"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                         kill(pid_from_pidfile, SIGQUIT);
560                 return EXIT_SUCCESS;
561         }
562
563         if (pid_from_pidfile > 0 && kill(pid_from_pidfile, 0) == 0)
564                 bb_error_msg_and_die("daemon already running");
565 #endif
566
567         api_mode_found = strchr(api_modes, G.api_mode[0]);
568         if (!api_mode_found)
569                 bb_error_msg_and_die("unknown API mode '%s'", G.api_mode);
570         G.api_method_num = api_mode_found - api_modes;
571
572         if (!(opts & FLAG_NO_DAEMON))
573                 bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
574
575         xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), ioctl_fd);
576         if (opts & FLAG_MONITOR) {
577                 struct sockaddr_nl addr;
578                 int fd = xsocket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
579
580                 memset(&addr, 0, sizeof(addr));
581                 addr.nl_family = AF_NETLINK;
582                 addr.nl_groups = RTMGRP_LINK;
583                 addr.nl_pid = getpid();
584
585                 xbind(fd, (struct sockaddr*)&addr, sizeof(addr));
586                 xmove_fd(fd, netlink_fd);
587         }
588
589         write_pidfile(pidfile_name);
590
591         /* this can't be moved before socket creation */
592         if (!(opts & FLAG_NO_SYSLOG)) {
593                 openlog(applet_name, 0, LOG_DAEMON);
594                 logmode |= LOGMODE_SYSLOG;
595         }
596
597         bb_signals(0
598                 | (1 << SIGINT )
599                 | (1 << SIGTERM)
600                 | (1 << SIGQUIT)
601                 | (1 << SIGHUP ) /* why we ignore it? */
602                 /* | (1 << SIGCHLD) - run_script does not use it anymore */
603                 , record_signo);
604
605         bb_error_msg("started: %s", bb_banner);
606
607         if (opts & FLAG_MONITOR) {
608                 struct ifreq ifrequest;
609                 set_ifreq_to_ifname(&ifrequest);
610                 G.iface_exists = (network_ioctl(SIOCGIFINDEX, &ifrequest, NULL) == 0);
611         }
612
613         if (G.iface_exists)
614                 maybe_up_new_iface();
615
616         iface_status = detect_link();
617         if (iface_status == IFSTATUS_ERR)
618                 goto exiting;
619         iface_status_str = strstatus(iface_status);
620
621         if (opts & FLAG_MONITOR) {
622                 bb_error_msg("interface %s",
623                         G.iface_exists ? "exists"
624                         : "doesn't exist, waiting");
625         }
626         /* else we assume it always exists, but don't mislead user
627          * by potentially lying that it really exists */
628
629         if (G.iface_exists) {
630                 bb_error_msg("link is %s", iface_status_str);
631         }
632
633         if ((!(opts & FLAG_NO_STARTUP)
634              && iface_status == IFSTATUS_UP
635             )
636          || (opts & FLAG_INITIAL_DOWN)
637         ) {
638                 if (run_script(iface_status_str) != 0)
639                         goto exiting;
640         }
641
642         /* Main loop */
643         netlink_pollfd[0].fd = netlink_fd;
644         netlink_pollfd[0].events = POLLIN;
645         delay_time = 0;
646         while (1) {
647                 int iface_status_old;
648                 int iface_exists_old;
649
650                 switch (bb_got_signal) {
651                 case SIGINT:
652                 case SIGTERM:
653                         bb_got_signal = 0;
654                         goto cleanup;
655                 case SIGQUIT:
656                         bb_got_signal = 0;
657                         goto exiting;
658                 default:
659                         bb_got_signal = 0;
660                         break;
661                 }
662
663                 if (poll(netlink_pollfd,
664                                 (opts & FLAG_MONITOR) ? 1 : 0,
665                                 G.poll_time
666                         ) < 0
667                 ) {
668                         if (errno == EINTR)
669                                 continue;
670                         bb_perror_msg("poll");
671                         goto exiting;
672                 }
673
674                 iface_status_old = iface_status;
675                 iface_exists_old = G.iface_exists;
676
677                 if ((opts & FLAG_MONITOR)
678                  && (netlink_pollfd[0].revents & POLLIN)
679                 ) {
680                         G.iface_exists = check_existence_through_netlink();
681                         if (G.iface_exists < 0) /* error */
682                                 goto exiting;
683                         if (iface_exists_old != G.iface_exists) {
684                                 bb_error_msg("interface %sappeared",
685                                                 G.iface_exists ? "" : "dis");
686                                 if (G.iface_exists)
687                                         maybe_up_new_iface();
688                         }
689                 }
690
691                 /* note: if !G.iface_exists, returns DOWN */
692                 iface_status = detect_link();
693                 if (iface_status == IFSTATUS_ERR) {
694                         if (!(opts & FLAG_MONITOR))
695                                 goto exiting;
696                         iface_status = IFSTATUS_DOWN;
697                 }
698                 iface_status_str = strstatus(iface_status);
699
700                 if (iface_status_old != iface_status) {
701                         bb_error_msg("link is %s", iface_status_str);
702
703                         if (delay_time) {
704                                 /* link restored its old status before
705                                  * we run script. don't run the script: */
706                                 delay_time = 0;
707                         } else {
708                                 delay_time = monotonic_sec();
709                                 if (iface_status == IFSTATUS_UP)
710                                         delay_time += G.delay_up;
711                                 if (iface_status == IFSTATUS_DOWN)
712                                         delay_time += G.delay_down;
713                                 if (delay_time == 0)
714                                         delay_time++;
715                         }
716                 }
717
718                 if (delay_time && (int)(monotonic_sec() - delay_time) >= 0) {
719                         delay_time = 0;
720                         if (run_script(iface_status_str) != 0)
721                                 goto exiting;
722                 }
723         } /* while (1) */
724
725  cleanup:
726         if (!(opts & FLAG_NO_SHUTDOWN)
727          && (iface_status == IFSTATUS_UP
728              || (iface_status == IFSTATUS_DOWN && delay_time)
729             )
730         ) {
731                 setenv(IFPLUGD_ENV_PREVIOUS, strstatus(iface_status), 1);
732                 setenv(IFPLUGD_ENV_CURRENT, strstatus(-1), 1);
733                 run_script("down\0up"); /* reusing string */
734         }
735
736  exiting:
737         remove_pidfile(pidfile_name);
738         bb_error_msg_and_die("exiting");
739 }