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