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