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