typo fix in comment
[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                         if (errno != ENODEV)
363                                 xfunc_die();
364                         G.iface_exists = 0;
365                         return;
366                 }
367         }
368
369 #if 0 /* why do we mess with IP addr? It's not our business */
370         if (network_ioctl(SIOCGIFADDR, &ifrequest, "can't get interface address") < 0) {
371         } else if (ifrequest.ifr_addr.sa_family != AF_INET) {
372                 bb_perror_msg("the interface is not IP-based");
373         } else {
374                 ((struct sockaddr_in*)(&ifrequest.ifr_addr))->sin_addr.s_addr = INADDR_ANY;
375                 network_ioctl(SIOCSIFADDR, &ifrequest, "can't set interface address");
376         }
377         network_ioctl(SIOCGIFFLAGS, &ifrequest, "can't get interface flags");
378 #endif
379 }
380
381 static void maybe_up_new_iface(void)
382 {
383         if (!(option_mask32 & FLAG_NO_AUTO))
384                 up_iface();
385
386 #if 0 /* bloat */
387         struct ifreq ifrequest;
388         struct ethtool_drvinfo driver_info;
389
390         set_ifreq_to_ifname(&ifrequest);
391         driver_info.cmd = ETHTOOL_GDRVINFO;
392         ifrequest.ifr_data = &driver_info;
393         if (network_ioctl(SIOCETHTOOL, &ifrequest, NULL) == 0) {
394                 char buf[sizeof("/xx:xx:xx:xx:xx:xx")];
395
396                 /* Get MAC */
397                 buf[0] = '\0';
398                 set_ifreq_to_ifname(&ifrequest);
399                 if (network_ioctl(SIOCGIFHWADDR, &ifrequest, NULL) == 0) {
400                         sprintf(buf, "/%02X:%02X:%02X:%02X:%02X:%02X",
401                                 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[0]),
402                                 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[1]),
403                                 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[2]),
404                                 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[3]),
405                                 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[4]),
406                                 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[5]));
407                 }
408
409                 bb_error_msg("using interface %s%s with driver<%s> (version: %s)",
410                         G.iface, buf, driver_info.driver, driver_info.version);
411         }
412 #endif
413         if (G.api_mode[0] == 'a')
414                 G.api_method_num = API_AUTO;
415 }
416
417 static smallint detect_link(void)
418 {
419         smallint status;
420
421         if (!G.iface_exists)
422                 return (option_mask32 & FLAG_MONITOR) ? IFSTATUS_DOWN : IFSTATUS_ERR;
423
424         /* Some drivers can't detect link status when the interface is down.
425          * I imagine detect_link_iff() is the most vulnerable.
426          * That's why -a "noauto" in an option, not a hardwired behavior.
427          */
428         if (!(option_mask32 & FLAG_NO_AUTO))
429                 up_iface();
430
431         if (G.api_method_num == API_AUTO) {
432                 int i;
433                 smallint sv_logmode;
434
435                 sv_logmode = logmode;
436                 for (i = 0; i < ARRAY_SIZE(method_table); i++) {
437                         logmode = LOGMODE_NONE;
438                         status = method_table[i].func();
439                         logmode = sv_logmode;
440                         if (status != IFSTATUS_ERR) {
441                                 G.api_method_num = i;
442                                 bb_error_msg("using %s detection mode", method_table[i].name);
443                                 break;
444                         }
445                 }
446         } else {
447                 status = method_table[G.api_method_num].func();
448         }
449
450         if (status == IFSTATUS_ERR) {
451                 if (option_mask32 & FLAG_IGNORE_FAIL)
452                         status = IFSTATUS_DOWN;
453                 else if (option_mask32 & FLAG_IGNORE_FAIL_POSITIVE)
454                         status = IFSTATUS_UP;
455                 else if (G.api_mode[0] == 'a')
456                         bb_error_msg("can't detect link status");
457         }
458
459         if (status != G.iface_last_status) {
460                 G.iface_prev_status = G.iface_last_status;
461                 G.iface_last_status = status;
462         }
463
464         return status;
465 }
466
467 static NOINLINE int check_existence_through_netlink(void)
468 {
469         int iface_len;
470         /* Buffer was 1K, but on linux-3.9.9 it was reported to be too small.
471          * netlink.h: "limit to 8K to avoid MSG_TRUNC when PAGE_SIZE is very large".
472          * Note: on error returns (-1) we exit, no need to free replybuf.
473          */
474         enum { BUF_SIZE = 8 * 1024 };
475         char *replybuf = xmalloc(BUF_SIZE);
476
477         iface_len = strlen(G.iface);
478         while (1) {
479                 struct nlmsghdr *mhdr;
480                 ssize_t bytes;
481
482                 bytes = recv(netlink_fd, replybuf, BUF_SIZE, MSG_DONTWAIT);
483                 if (bytes < 0) {
484                         if (errno == EAGAIN)
485                                 goto ret;
486                         if (errno == EINTR)
487                                 continue;
488                         bb_perror_msg("netlink: recv");
489                         return -1;
490                 }
491
492                 mhdr = (struct nlmsghdr*)replybuf;
493                 while (bytes > 0) {
494                         if (!NLMSG_OK(mhdr, bytes)) {
495                                 bb_error_msg("netlink packet too small or truncated");
496                                 return -1;
497                         }
498
499                         if (mhdr->nlmsg_type == RTM_NEWLINK || mhdr->nlmsg_type == RTM_DELLINK) {
500                                 struct rtattr *attr;
501                                 int attr_len;
502
503                                 if (mhdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) {
504                                         bb_error_msg("netlink packet too small or truncated");
505                                         return -1;
506                                 }
507
508                                 attr = IFLA_RTA(NLMSG_DATA(mhdr));
509                                 attr_len = IFLA_PAYLOAD(mhdr);
510
511                                 while (RTA_OK(attr, attr_len)) {
512                                         if (attr->rta_type == IFLA_IFNAME) {
513                                                 int len = RTA_PAYLOAD(attr);
514                                                 if (len > IFNAMSIZ)
515                                                         len = IFNAMSIZ;
516                                                 if (iface_len <= len
517                                                  && strncmp(G.iface, RTA_DATA(attr), len) == 0
518                                                 ) {
519                                                         G.iface_exists = (mhdr->nlmsg_type == RTM_NEWLINK);
520                                                 }
521                                         }
522                                         attr = RTA_NEXT(attr, attr_len);
523                                 }
524                         }
525
526                         mhdr = NLMSG_NEXT(mhdr, bytes);
527                 }
528         }
529
530  ret:
531         free(replybuf);
532         return G.iface_exists;
533 }
534
535 #if ENABLE_FEATURE_PIDFILE
536 static NOINLINE pid_t read_pid(const char *filename)
537 {
538         int len;
539         char buf[128];
540
541         len = open_read_close(filename, buf, 127);
542         if (len > 0) {
543                 buf[len] = '\0';
544                 /* returns ULONG_MAX on error => -1 */
545                 return bb_strtoul(buf, NULL, 10);
546         }
547         return 0;
548 }
549 #endif
550
551 int ifplugd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
552 int ifplugd_main(int argc UNUSED_PARAM, char **argv)
553 {
554         int iface_status;
555         int delay_time;
556         const char *iface_status_str;
557         struct pollfd netlink_pollfd[1];
558         unsigned opts;
559         const char *api_mode_found;
560 #if ENABLE_FEATURE_PIDFILE
561         char *pidfile_name;
562         pid_t pid_from_pidfile;
563 #endif
564
565         INIT_G();
566
567         opts = getopt32(argv, OPTION_STR,
568                 &G.iface, &G.script_name, &G.poll_time, &G.delay_up,
569                 &G.delay_down, &G.api_mode, &G.extra_arg);
570         G.poll_time *= 1000;
571
572         applet_name = xasprintf("ifplugd(%s)", G.iface);
573
574 #if ENABLE_FEATURE_PIDFILE
575         pidfile_name = xasprintf(CONFIG_PID_FILE_PATH "/ifplugd.%s.pid", G.iface);
576         pid_from_pidfile = read_pid(pidfile_name);
577
578         if (opts & FLAG_KILL) {
579                 if (pid_from_pidfile > 0)
580                         /* Upstream tool use SIGINT for -k */
581                         kill(pid_from_pidfile, SIGINT);
582                 return EXIT_SUCCESS;
583         }
584
585         if (pid_from_pidfile > 0 && kill(pid_from_pidfile, 0) == 0)
586                 bb_error_msg_and_die("daemon already running");
587 #endif
588
589         api_mode_found = strchr(api_modes, G.api_mode[0]);
590         if (!api_mode_found)
591                 bb_error_msg_and_die("unknown API mode '%s'", G.api_mode);
592         G.api_method_num = api_mode_found - api_modes;
593
594         if (!(opts & FLAG_NO_DAEMON))
595                 bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
596
597         xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), ioctl_fd);
598         if (opts & FLAG_MONITOR) {
599                 struct sockaddr_nl addr;
600                 int fd = xsocket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
601
602                 memset(&addr, 0, sizeof(addr));
603                 addr.nl_family = AF_NETLINK;
604                 addr.nl_groups = RTMGRP_LINK;
605                 addr.nl_pid = getpid();
606
607                 xbind(fd, (struct sockaddr*)&addr, sizeof(addr));
608                 xmove_fd(fd, netlink_fd);
609         }
610
611         write_pidfile(pidfile_name);
612
613         /* this can't be moved before socket creation */
614         if (!(opts & FLAG_NO_SYSLOG)) {
615                 openlog(applet_name, 0, LOG_DAEMON);
616                 logmode |= LOGMODE_SYSLOG;
617         }
618
619         bb_signals(0
620                 | (1 << SIGINT )
621                 | (1 << SIGTERM)
622                 | (1 << SIGQUIT)
623                 | (1 << SIGHUP ) /* why we ignore it? */
624                 /* | (1 << SIGCHLD) - run_script does not use it anymore */
625                 , record_signo);
626
627         bb_error_msg("started: %s", bb_banner);
628
629         if (opts & FLAG_MONITOR) {
630                 struct ifreq ifrequest;
631                 set_ifreq_to_ifname(&ifrequest);
632                 G.iface_exists = (network_ioctl(SIOCGIFINDEX, &ifrequest, NULL) == 0);
633         }
634
635         if (G.iface_exists)
636                 maybe_up_new_iface();
637
638         iface_status = detect_link();
639         if (iface_status == IFSTATUS_ERR)
640                 goto exiting;
641         iface_status_str = strstatus(iface_status);
642
643         if (opts & FLAG_MONITOR) {
644                 bb_error_msg("interface %s",
645                         G.iface_exists ? "exists"
646                         : "doesn't exist, waiting");
647         }
648         /* else we assume it always exists, but don't mislead user
649          * by potentially lying that it really exists */
650
651         if (G.iface_exists) {
652                 bb_error_msg("link is %s", iface_status_str);
653         }
654
655         if ((!(opts & FLAG_NO_STARTUP)
656              && iface_status == IFSTATUS_UP
657             )
658          || (opts & FLAG_INITIAL_DOWN)
659         ) {
660                 if (run_script(iface_status_str) != 0)
661                         goto exiting;
662         }
663
664         /* Main loop */
665         netlink_pollfd[0].fd = netlink_fd;
666         netlink_pollfd[0].events = POLLIN;
667         delay_time = 0;
668         while (1) {
669                 int iface_status_old;
670
671                 switch (bb_got_signal) {
672                 case SIGINT:
673                 case SIGTERM:
674                         bb_got_signal = 0;
675                         goto cleanup;
676                 case SIGQUIT:
677                         bb_got_signal = 0;
678                         goto exiting;
679                 default:
680                         bb_got_signal = 0;
681                         break;
682                 }
683
684                 if (poll(netlink_pollfd,
685                                 (opts & FLAG_MONITOR) ? 1 : 0,
686                                 G.poll_time
687                         ) < 0
688                 ) {
689                         if (errno == EINTR)
690                                 continue;
691                         bb_perror_msg("poll");
692                         goto exiting;
693                 }
694
695                 if ((opts & FLAG_MONITOR)
696                  && (netlink_pollfd[0].revents & POLLIN)
697                 ) {
698                         int iface_exists_old;
699
700                         iface_exists_old = G.iface_exists;
701                         G.iface_exists = check_existence_through_netlink();
702                         if (G.iface_exists < 0) /* error */
703                                 goto exiting;
704                         if (iface_exists_old != G.iface_exists) {
705                                 bb_error_msg("interface %sappeared",
706                                                 G.iface_exists ? "" : "dis");
707                                 if (G.iface_exists)
708                                         maybe_up_new_iface();
709                         }
710                 }
711
712                 /* note: if !G.iface_exists, returns DOWN */
713                 iface_status_old = iface_status;
714                 iface_status = detect_link();
715                 if (iface_status == IFSTATUS_ERR) {
716                         if (!(opts & FLAG_MONITOR))
717                                 goto exiting;
718                         iface_status = IFSTATUS_DOWN;
719                 }
720                 iface_status_str = strstatus(iface_status);
721
722                 if (iface_status_old != iface_status) {
723                         bb_error_msg("link is %s", iface_status_str);
724
725                         if (delay_time) {
726                                 /* link restored its old status before
727                                  * we ran script. don't run the script: */
728                                 delay_time = 0;
729                         } else {
730                                 delay_time = monotonic_sec();
731                                 if (iface_status == IFSTATUS_UP)
732                                         delay_time += G.delay_up;
733                                 if (iface_status == IFSTATUS_DOWN)
734                                         delay_time += G.delay_down;
735 #if 0  /* if you are back in 1970... */
736                                 if (delay_time == 0) {
737                                         sleep(1);
738                                         delay_time = 1;
739                                 }
740 #endif
741                         }
742                 }
743
744                 if (delay_time && (int)(monotonic_sec() - delay_time) >= 0) {
745                         if (run_script(iface_status_str) != 0)
746                                 goto exiting;
747                         delay_time = 0;
748                 }
749         } /* while (1) */
750
751  cleanup:
752         if (!(opts & FLAG_NO_SHUTDOWN)
753          && (iface_status == IFSTATUS_UP
754              || (iface_status == IFSTATUS_DOWN && delay_time)
755             )
756         ) {
757                 setenv(IFPLUGD_ENV_PREVIOUS, strstatus(iface_status), 1);
758                 setenv(IFPLUGD_ENV_CURRENT, strstatus(-1), 1);
759                 run_script("down\0up"); /* reusing string */
760         }
761
762  exiting:
763         remove_pidfile(pidfile_name);
764         bb_error_msg_and_die("exiting");
765 }