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