*: remove a few more cases of argc usage. -89 bytes.
[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         pid_t pid;
123         int r;
124
125         bb_error_msg("executing '%s %s %s'", G.script_name, G.iface, action);
126
127 #if 1
128         pid = vfork();
129         if (pid < 0) {
130                 bb_perror_msg("fork");
131                 return -1;
132         }
133
134         if (pid == 0) {
135                 /* child */
136                 execlp(G.script_name, G.script_name, G.iface, action, G.extra_arg, NULL);
137                 bb_perror_msg_and_die("can't execute '%s'", G.script_name);
138         }
139
140         /* parent */
141         wait(&r);
142         r = WEXITSTATUS(r);
143
144         bb_error_msg("exit code: %u", r);
145         return (option_mask32 & FLAG_IGNORE_RETVAL) ? 0 : r;
146
147 #else /* insanity */
148
149         struct fd_pair pipe_pair;
150         char buf[256];
151         int i = 0;
152
153         xpiped_pair(pipe_pair);
154
155         pid = vfork();
156         if (pid < 0) {
157                 bb_perror_msg("fork");
158                 return -1;
159         }
160
161         /* child */
162         if (pid == 0) {
163                 xmove_fd(pipe_pair.wr, 1);
164                 xdup2(1, 2);
165                 if (pipe_pair.rd > 2)
166                         close(pipe_pair.rd);
167
168                 // umask(0022); // Set up a sane umask
169
170                 execlp(G.script_name, G.script_name, G.iface, action, G.extra_arg, NULL);
171                 _exit(EXIT_FAILURE);
172         }
173
174         /* parent */
175         close(pipe_pair.wr);
176
177         while (1) {
178                 if (bb_got_signal && bb_got_signal != SIGCHLD) {
179                         bb_error_msg("killing child");
180                         kill(pid, SIGTERM);
181                         bb_got_signal = 0;
182                         break;
183                 }
184
185                 r = read(pipe_pair.rd, &buf[i], 1);
186
187                 if (buf[i] == '\n' || i == sizeof(buf)-2 || r != 1) {
188                         if (r == 1 && buf[i] != '\n')
189                                 i++;
190
191                         buf[i] = '\0';
192
193                         if (i > 0)
194                                 bb_error_msg("client: %s", buf);
195
196                         i = 0;
197                 } else {
198                         i++;
199                 }
200
201                 if (r != 1)
202                         break;
203         }
204
205         close(pipe_pair.rd);
206
207         wait(&r);
208
209         if (!WIFEXITED(r) || WEXITSTATUS(r) != 0) {
210                 bb_error_msg("program execution failed, return value is %i",
211                         WEXITSTATUS(r));
212                 return option_mask32 & FLAG_IGNORE_RETVAL ? 0 : WEXITSTATUS(r);
213         }
214         bb_error_msg("program executed successfully");
215         return 0;
216 #endif
217 }
218
219 static int network_ioctl(int request, void* data)
220 {
221         return ioctl(ioctl_fd, request, data);
222 }
223
224 static void set_ifreq_to_ifname(struct ifreq *ifreq)
225 {
226         memset(ifreq, 0, sizeof(struct ifreq));
227         strncpy_IFNAMSIZ(ifreq->ifr_name, G.iface);
228 }
229
230 static const char *strstatus(int status)
231 {
232         if (status == IFSTATUS_ERR)
233                 return "error";
234         return "down\0up" + (status * 5);
235 }
236
237 static void up_iface(void)
238 {
239         struct ifreq ifrequest;
240
241         if (!G.iface_exists)
242                 return;
243
244         set_ifreq_to_ifname(&ifrequest);
245         if (network_ioctl(SIOCGIFFLAGS, &ifrequest) < 0) {
246                 bb_perror_msg("can't %cet interface flags", 'g');
247                 G.iface_exists = 0;
248                 return;
249         }
250
251         if (!(ifrequest.ifr_flags & IFF_UP)) {
252                 ifrequest.ifr_flags |= IFF_UP;
253                 /* Let user know we mess up with interface */
254                 bb_error_msg("upping interface");
255                 if (network_ioctl(SIOCSIFFLAGS, &ifrequest) < 0)
256                         bb_perror_msg_and_die("can't %cet interface flags", 's');
257         }
258
259 #if 0 /* why do we mess with IP addr? It's not our business */
260         if (network_ioctl(SIOCGIFADDR, &ifrequest) < 0) {
261                 bb_error_msg("can't get interface address");
262         } else if (ifrequest.ifr_addr.sa_family != AF_INET) {
263                 bb_perror_msg("the interface is not IP-based");
264         } else {
265                 ((struct sockaddr_in*)(&ifrequest.ifr_addr))->sin_addr.s_addr = INADDR_ANY;
266                 if (network_ioctl(SIOCSIFADDR, &ifrequest) < 0)
267                         bb_perror_msg("can't set interface address");
268         }
269         if (network_ioctl(SIOCGIFFLAGS, &ifrequest) < 0) {
270                 bb_perror_msg("can't get interface flags");
271                 return;
272         }
273 #endif
274 }
275
276 static void maybe_up_new_iface(void)
277 {
278         if (!(option_mask32 & FLAG_NO_AUTO))
279                 up_iface();
280
281 #if 0 /* bloat */
282         struct ifreq ifrequest;
283         struct ethtool_drvinfo driver_info;
284
285         set_ifreq_to_ifname(&ifrequest);
286         driver_info.cmd = ETHTOOL_GDRVINFO;
287         ifrequest.ifr_data = &driver_info;
288         if (network_ioctl(SIOCETHTOOL, &ifrequest) == 0) {
289                 char buf[sizeof("/xx:xx:xx:xx:xx:xx")];
290
291                 /* Get MAC */
292                 buf[0] = '\0';
293                 set_ifreq_to_ifname(&ifrequest);
294                 if (network_ioctl(SIOCGIFHWADDR, &ifrequest) == 0) {
295                         sprintf(buf, "/%02X:%02X:%02X:%02X:%02X:%02X",
296                                 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[0]),
297                                 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[1]),
298                                 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[2]),
299                                 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[3]),
300                                 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[4]),
301                                 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[5]));
302                 }
303
304                 bb_error_msg("using interface %s%s with driver<%s> (version: %s)",
305                         G.iface, buf, driver_info.driver, driver_info.version);
306         }
307 #endif
308
309         G.cached_detect_link_func = NULL;
310 }
311
312 static smallint detect_link_mii(void)
313 {
314         struct ifreq ifreq;
315         struct mii_ioctl_data *mii = (void *)&ifreq.ifr_data;
316
317         set_ifreq_to_ifname(&ifreq);
318
319         if (network_ioctl(SIOCGMIIPHY, &ifreq) < 0) {
320                 bb_perror_msg("SIOCGMIIPHY failed");
321                 return IFSTATUS_ERR;
322         }
323
324         mii->reg_num = 1;
325
326         if (network_ioctl(SIOCGMIIREG, &ifreq) < 0) {
327                 bb_perror_msg("SIOCGMIIREG failed");
328                 return IFSTATUS_ERR;
329         }
330
331         return (mii->val_out & 0x0004) ? IFSTATUS_UP : IFSTATUS_DOWN;
332 }
333
334 static smallint detect_link_priv(void)
335 {
336         struct ifreq ifreq;
337         struct mii_ioctl_data *mii = (void *)&ifreq.ifr_data;
338
339         set_ifreq_to_ifname(&ifreq);
340
341         if (network_ioctl(SIOCDEVPRIVATE, &ifreq) < 0) {
342                 bb_perror_msg("SIOCDEVPRIVATE failed");
343                 return IFSTATUS_ERR;
344         }
345
346         mii->reg_num = 1;
347
348         if (network_ioctl(SIOCDEVPRIVATE+1, &ifreq) < 0) {
349                 bb_perror_msg("SIOCDEVPRIVATE+1 failed");
350                 return IFSTATUS_ERR;
351         }
352
353         return (mii->val_out & 0x0004) ? IFSTATUS_UP : IFSTATUS_DOWN;
354 }
355
356 static smallint detect_link_ethtool(void)
357 {
358         struct ifreq ifreq;
359         struct ethtool_value edata;
360
361         set_ifreq_to_ifname(&ifreq);
362
363         edata.cmd = ETHTOOL_GLINK;
364         ifreq.ifr_data = (void*) &edata;
365
366         if (network_ioctl(SIOCETHTOOL, &ifreq) < 0) {
367                 bb_perror_msg("ETHTOOL_GLINK failed");
368                 return IFSTATUS_ERR;
369         }
370
371         return edata.data ? IFSTATUS_UP : IFSTATUS_DOWN;
372 }
373
374 static smallint detect_link_iff(void)
375 {
376         struct ifreq ifreq;
377
378         set_ifreq_to_ifname(&ifreq);
379
380         if (network_ioctl(SIOCGIFFLAGS, &ifreq) < 0) {
381                 bb_perror_msg("SIOCGIFFLAGS failed");
382                 return IFSTATUS_ERR;
383         }
384
385         return (ifreq.ifr_flags & IFF_RUNNING) ? IFSTATUS_UP : IFSTATUS_DOWN;
386 }
387
388 static smallint detect_link_wlan(void)
389 {
390         struct iwreq iwrequest;
391         uint8_t mac[ETH_ALEN];
392
393         memset(&iwrequest, 0, sizeof(struct iwreq));
394         strncpy_IFNAMSIZ(iwrequest.ifr_ifrn.ifrn_name, G.iface);
395
396         if (network_ioctl(SIOCGIWAP, &iwrequest) < 0) {
397                 bb_perror_msg("SIOCGIWAP failed");
398                 return IFSTATUS_ERR;
399         }
400
401         memcpy(mac, &(iwrequest.u.ap_addr.sa_data), ETH_ALEN);
402
403         if (mac[0] == 0xFF || mac[0] == 0x44 || mac[0] == 0x00) {
404                 for (int i = 1; i < ETH_ALEN; ++i) {
405                         if (mac[i] != mac[0])
406                                 return IFSTATUS_UP;
407                 }
408                 return IFSTATUS_DOWN;
409         }
410
411         return IFSTATUS_UP;
412 }
413
414 static smallint detect_link_auto(void)
415 {
416         const char *method;
417         smallint iface_status;
418         smallint sv_logmode;
419
420         if (G.cached_detect_link_func) {
421                 iface_status = G.cached_detect_link_func();
422                 if (iface_status != IFSTATUS_ERR)
423                         return iface_status;
424         }
425
426         sv_logmode = logmode;
427         logmode = LOGMODE_NONE;
428
429         iface_status = detect_link_ethtool();
430         if (iface_status != IFSTATUS_ERR) {
431                 G.cached_detect_link_func = detect_link_ethtool;
432                 method = "SIOCETHTOOL";
433  found_method:
434                 logmode = sv_logmode;
435                 bb_error_msg("using %s detection mode", method);
436                 return iface_status;
437         }
438
439         iface_status = detect_link_mii();
440         if (iface_status != IFSTATUS_ERR) {
441                 G.cached_detect_link_func = detect_link_mii;
442                 method = "SIOCGMIIPHY";
443                 goto found_method;
444         }
445
446         iface_status = detect_link_priv();
447         if (iface_status != IFSTATUS_ERR) {
448                 G.cached_detect_link_func = detect_link_priv;
449                 method = "SIOCDEVPRIVATE";
450                 goto found_method;
451         }
452
453         iface_status = detect_link_wlan();
454         if (iface_status != IFSTATUS_ERR) {
455                 G.cached_detect_link_func = detect_link_wlan;
456                 method = "wireless extension";
457                 goto found_method;
458         }
459
460         iface_status = detect_link_iff();
461         if (iface_status != IFSTATUS_ERR) {
462                 G.cached_detect_link_func = detect_link_iff;
463                 method = "IFF_RUNNING";
464                 goto found_method;
465         }
466
467         logmode = sv_logmode;
468         return iface_status; /* IFSTATUS_ERR */
469 }
470
471 static smallint detect_link(void)
472 {
473         smallint status;
474
475         if (!G.iface_exists)
476                 return (option_mask32 & FLAG_MONITOR) ? IFSTATUS_DOWN : IFSTATUS_ERR;
477
478 #if 0
479 /* Why? This behavior makes it hard to temporary down the iface.
480  * It makes a bit more sense to do only in maybe_up_new_iface.
481  * OTOH, maybe detect_link_wlan needs this. Then it should be done
482  * _only_ there.
483  */
484         if (!(option_mask32 & FLAG_NO_AUTO))
485                 up_iface();
486 #endif
487
488         status = G.detect_link_func();
489         if (status == IFSTATUS_ERR) {
490                 if (option_mask32 & FLAG_IGNORE_FAIL)
491                         status = IFSTATUS_DOWN;
492                 if (option_mask32 & FLAG_IGNORE_FAIL_POSITIVE)
493                         status = IFSTATUS_UP;
494         }
495
496         if (status == IFSTATUS_ERR
497          && G.detect_link_func == detect_link_auto
498         ) {
499                 bb_error_msg("failed to detect link status");
500         }
501
502         if (status != G.iface_last_status) {
503 //TODO: is it safe to repeatedly do this?
504                 setenv(IFPLUGD_ENV_PREVIOUS, strstatus(G.iface_last_status), 1);
505                 setenv(IFPLUGD_ENV_CURRENT, strstatus(status), 1);
506                 G.iface_last_status = status;
507         }
508
509         return status;
510 }
511
512 static NOINLINE int check_existence_through_netlink(void)
513 {
514         char replybuf[1024];
515
516         while (1) {
517                 struct nlmsghdr *mhdr;
518                 ssize_t bytes;
519
520                 bytes = recv(netlink_fd, &replybuf, sizeof(replybuf), MSG_DONTWAIT);
521                 if (bytes < 0) {
522                         if (errno == EAGAIN)
523                                 return G.iface_exists;
524                         if (errno == EINTR)
525                                 continue;
526
527                         bb_perror_msg("netlink: recv");
528                         return -1;
529                 }
530
531                 mhdr = (struct nlmsghdr*)replybuf;
532                 while (bytes > 0) {
533                         if (!NLMSG_OK(mhdr, bytes)
534                          || bytes < sizeof(struct nlmsghdr)
535                          || bytes < mhdr->nlmsg_len
536                         ) {
537                                 bb_error_msg("netlink packet too small or truncated");
538                                 return -1;
539                         }
540
541                         if (mhdr->nlmsg_type == RTM_NEWLINK || mhdr->nlmsg_type == RTM_DELLINK) {
542                                 struct rtattr *attr;
543                                 struct ifinfomsg *imsg;
544                                 int attr_len;
545
546                                 imsg = NLMSG_DATA(mhdr);
547
548                                 if (mhdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) {
549                                         bb_error_msg("netlink packet too small or truncated");
550                                         return -1;
551                                 }
552
553                                 attr = (struct rtattr*)((char*)imsg + NLMSG_ALIGN(sizeof(struct ifinfomsg)));
554                                 attr_len = NLMSG_PAYLOAD(mhdr, sizeof(struct ifinfomsg));
555
556                                 while (RTA_OK(attr, attr_len)) {
557                                         if (attr->rta_type == IFLA_IFNAME) {
558                                                 char ifname[IFNAMSIZ + 1];
559                                                 int len = RTA_PAYLOAD(attr);
560
561                                                 if (len > IFNAMSIZ)
562                                                         len = IFNAMSIZ;
563                                                 memcpy(ifname, RTA_DATA(attr), len);
564                                                 if (strcmp(G.iface, ifname) == 0) {
565                                                         G.iface_exists = (mhdr->nlmsg_type == RTM_NEWLINK);
566                                                 }
567                                         }
568                                         attr = RTA_NEXT(attr, attr_len);
569                                 }
570                         }
571
572                         mhdr = NLMSG_NEXT(mhdr, bytes);
573                 }
574         }
575
576         return G.iface_exists;
577 }
578
579 static NOINLINE int netlink_open(void)
580 {
581         int fd;
582         struct sockaddr_nl addr;
583
584         fd = xsocket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
585
586         memset(&addr, 0, sizeof(addr));
587         addr.nl_family = AF_NETLINK;
588         addr.nl_groups = RTMGRP_LINK;
589         addr.nl_pid = getpid();
590
591         xbind(fd, (struct sockaddr*)&addr, sizeof(addr));
592
593         return fd;
594 }
595
596 #if ENABLE_FEATURE_PIDFILE
597 static NOINLINE pid_t read_pid(const char *filename)
598 {
599         int len;
600         char buf[128];
601
602         len = open_read_close(filename, buf, 127);
603         if (len > 0) {
604                 buf[len] = '\0';
605                 /* returns ULONG_MAX on error => -1 */
606                 return bb_strtoul(buf, NULL, 10);
607         }
608         return 0;
609 }
610 #endif
611
612 int ifplugd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
613 int ifplugd_main(int argc UNUSED_PARAM, char **argv)
614 {
615         int iface_status;
616         int delay_time;
617         const char *iface_status_str;
618         struct pollfd netlink_pollfd[1];
619         unsigned opts;
620 #if ENABLE_FEATURE_PIDFILE
621         char *pidfile_name;
622         pid_t pid_from_pidfile;
623 #endif
624
625         INIT_G();
626
627         opt_complementary = "t+:u+:d+";
628         opts = getopt32(argv, OPTION_STR,
629                 &G.iface, &G.script_name, &G.poll_time, &G.delay_up,
630                 &G.delay_down, &G.api_mode, &G.extra_arg);
631         G.poll_time *= 1000;
632
633         applet_name = xasprintf("ifplugd(%s)", G.iface);
634
635 #if ENABLE_FEATURE_PIDFILE
636         pidfile_name = xasprintf(_PATH_VARRUN"ifplugd.%s.pid", G.iface);
637         pid_from_pidfile = read_pid(pidfile_name);
638
639         if (opts & FLAG_KILL) {
640                 if (pid_from_pidfile > 0)
641                         kill(pid_from_pidfile, SIGQUIT);
642                 return EXIT_SUCCESS;
643         }
644
645         if (pid_from_pidfile > 0 && kill(pid_from_pidfile, 0) == 0)
646                 bb_error_msg_and_die("daemon already running");
647 #endif
648
649         switch (G.api_mode[0]) {
650         case API_AUTO:
651                 G.detect_link_func = detect_link_auto;
652                 break;
653         case API_ETHTOOL:
654                 G.detect_link_func = detect_link_ethtool;
655                 break;
656         case API_MII:
657                 G.detect_link_func = detect_link_mii;
658                 break;
659         case API_PRIVATE:
660                 G.detect_link_func = detect_link_priv;
661                 break;
662         case API_WLAN:
663                 G.detect_link_func = detect_link_wlan;
664                 break;
665         case API_IFF:
666                 G.detect_link_func = detect_link_iff;
667                 break;
668         default:
669                 bb_error_msg_and_die("unknown API mode '%s'", G.api_mode);
670         }
671
672         if (!(opts & FLAG_NO_DAEMON))
673                 bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
674
675         xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), ioctl_fd);
676         if (opts & FLAG_MONITOR) {
677                 xmove_fd(netlink_open(), netlink_fd);
678         }
679
680         write_pidfile(pidfile_name);
681
682         /* this can't be moved before socket creation */
683         if (!(opts & FLAG_NO_SYSLOG)) {
684                 openlog(applet_name, 0, LOG_DAEMON);
685                 logmode |= LOGMODE_SYSLOG;
686         }
687
688         bb_signals(0
689                 | (1 << SIGINT )
690                 | (1 << SIGTERM)
691                 | (1 << SIGQUIT)
692                 | (1 << SIGHUP ) /* why we ignore it? */
693                 /* | (1 << SIGCHLD) - run_script does not use it anymore */
694                 , record_signo);
695
696         bb_error_msg("started: %s", bb_banner);
697
698         if (opts & FLAG_MONITOR) {
699                 struct ifreq ifrequest;
700                 set_ifreq_to_ifname(&ifrequest);
701                 G.iface_exists = (network_ioctl(SIOCGIFINDEX, &ifrequest) == 0);
702         }
703
704         if (G.iface_exists)
705                 maybe_up_new_iface();
706
707         iface_status = detect_link();
708         if (iface_status == IFSTATUS_ERR)
709                 goto exiting;
710         iface_status_str = strstatus(iface_status);
711
712         if (opts & FLAG_MONITOR) {
713                 bb_error_msg("interface %s",
714                         G.iface_exists ? "exists"
715                         : "doesn't exist, waiting");
716         }
717         /* else we assume it always exists, but don't mislead user
718          * by potentially lying that it really exists */
719
720         if (G.iface_exists) {
721                 bb_error_msg("link is %s", iface_status_str);
722         }
723
724         if ((!(opts & FLAG_NO_STARTUP)
725              && iface_status == IFSTATUS_UP
726             )
727          || (opts & FLAG_INITIAL_DOWN)
728         ) {
729                 if (run_script(iface_status_str) != 0)
730                         goto exiting;
731         }
732
733         /* Main loop */
734         netlink_pollfd[0].fd = netlink_fd;
735         netlink_pollfd[0].events = POLLIN;
736         delay_time = 0;
737         while (1) {
738                 int iface_status_old;
739                 int iface_exists_old;
740
741                 switch (bb_got_signal) {
742                 case SIGINT:
743                 case SIGTERM:
744                         bb_got_signal = 0;
745                         goto cleanup;
746                 case SIGQUIT:
747                         bb_got_signal = 0;
748                         goto exiting;
749                 default:
750                         bb_got_signal = 0;
751                         break;
752                 }
753
754                 if (poll(netlink_pollfd,
755                                 (opts & FLAG_MONITOR) ? 1 : 0,
756                                 G.poll_time
757                         ) < 0
758                 ) {
759                         if (errno == EINTR)
760                                 continue;
761                         bb_perror_msg("poll");
762                         goto exiting;
763                 }
764
765                 iface_status_old = iface_status;
766                 iface_exists_old = G.iface_exists;
767
768                 if ((opts & FLAG_MONITOR)
769                  && (netlink_pollfd[0].revents & POLLIN)
770                 ) {
771                         G.iface_exists = check_existence_through_netlink();
772                         if (G.iface_exists < 0) /* error */
773                                 goto exiting;
774                         if (iface_exists_old != G.iface_exists) {
775                                 bb_error_msg("interface %sappeared",
776                                                 G.iface_exists ? "" : "dis");
777                                 if (G.iface_exists)
778                                         maybe_up_new_iface();
779                         }
780                 }
781
782                 /* note: if !G.iface_exists, returns DOWN */
783                 iface_status = detect_link();
784                 if (iface_status == IFSTATUS_ERR) {
785                         if (!(opts & FLAG_MONITOR))
786                                 goto exiting;
787                         iface_status = IFSTATUS_DOWN;
788                 }
789                 iface_status_str = strstatus(iface_status);
790
791                 if (iface_status_old != iface_status) {
792                         bb_error_msg("link is %s", iface_status_str);
793
794                         if (delay_time) {
795                                 /* link restored its old status before
796                                  * we run script. don't run the script: */
797                                 delay_time = 0;
798                         } else {
799                                 delay_time = monotonic_sec();
800                                 if (iface_status == IFSTATUS_UP)
801                                         delay_time += G.delay_up;
802                                 if (iface_status == IFSTATUS_DOWN)
803                                         delay_time += G.delay_down;
804                                 if (delay_time == 0)
805                                         delay_time++;
806                         }
807                 }
808
809                 if (delay_time && (int)(monotonic_sec() - delay_time) >= 0) {
810                         delay_time = 0;
811                         if (run_script(iface_status_str) != 0)
812                                 goto exiting;
813                 }
814         } /* while (1) */
815
816  cleanup:
817         if (!(opts & FLAG_NO_SHUTDOWN)
818          && (iface_status == IFSTATUS_UP
819              || (iface_status == IFSTATUS_DOWN && delay_time)
820             )
821         ) {
822                 setenv(IFPLUGD_ENV_PREVIOUS, strstatus(iface_status), 1);
823                 setenv(IFPLUGD_ENV_CURRENT, strstatus(-1), 1);
824                 run_script("down\0up"); /* reusing string */
825         }
826
827  exiting:
828         remove_pidfile(pidfile_name);
829         bb_error_msg_and_die("exiting");
830 }