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