randomconfig fixes
[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 = &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
632         applet_name = xasprintf("ifplugd(%s)", G.iface);
633
634 #if ENABLE_FEATURE_PIDFILE
635         pidfile_name = xasprintf(_PATH_VARRUN"ifplugd.%s.pid", G.iface);
636         pid_from_pidfile = read_pid(pidfile_name);
637
638         if (opts & FLAG_KILL) {
639                 if (pid_from_pidfile > 0)
640                         kill(pid_from_pidfile, SIGQUIT);
641                 return EXIT_SUCCESS;
642         }
643
644         if (pid_from_pidfile > 0 && kill(pid_from_pidfile, 0) == 0)
645                 bb_error_msg_and_die("daemon already running");
646 #endif
647
648         switch (G.api_mode[0]) {
649         case API_AUTO:
650                 G.detect_link_func = detect_link_auto;
651                 break;
652         case API_ETHTOOL:
653                 G.detect_link_func = detect_link_ethtool;
654                 break;
655         case API_MII:
656                 G.detect_link_func = detect_link_mii;
657                 break;
658         case API_PRIVATE:
659                 G.detect_link_func = detect_link_priv;
660                 break;
661         case API_WLAN:
662                 G.detect_link_func = detect_link_wlan;
663                 break;
664         case API_IFF:
665                 G.detect_link_func = detect_link_iff;
666                 break;
667         default:
668                 bb_error_msg_and_die("unknown API mode '%s'", G.api_mode);
669         }
670
671         if (!(opts & FLAG_NO_DAEMON))
672                 bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
673
674         xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), ioctl_fd);
675         if (opts & FLAG_MONITOR) {
676                 xmove_fd(netlink_open(), netlink_fd);
677         }
678
679         write_pidfile(pidfile_name);
680
681         /* this can't be moved before socket creation */
682         if (!(opts & FLAG_NO_SYSLOG)) {
683                 openlog(applet_name, 0, LOG_DAEMON);
684                 logmode |= LOGMODE_SYSLOG;
685         }
686
687         bb_signals(0
688                 | (1 << SIGINT )
689                 | (1 << SIGTERM)
690                 | (1 << SIGQUIT)
691                 | (1 << SIGHUP ) /* why we ignore it? */
692                 /* | (1 << SIGCHLD) - run_script does not use it anymore */
693                 , record_signo);
694
695         bb_error_msg("started: %s", bb_banner);
696
697         if (opts & FLAG_MONITOR) {
698                 struct ifreq ifrequest;
699                 set_ifreq_to_ifname(&ifrequest);
700                 G.iface_exists = (network_ioctl(SIOCGIFINDEX, &ifrequest) == 0);
701         }
702
703         if (G.iface_exists)
704                 maybe_up_new_iface();
705
706         iface_status = detect_link();
707         if (iface_status == IFSTATUS_ERR)
708                 goto exiting;
709         iface_status_str = strstatus(iface_status);
710
711         if (opts & FLAG_MONITOR) {
712                 bb_error_msg("interface %s",
713                         G.iface_exists ? "exists"
714                         : "doesn't exist, waiting");
715         }
716         /* else we assume it always exists, but don't mislead user
717          * by potentially lying that it really exists */
718
719         if (G.iface_exists) {
720                 bb_error_msg("link is %s", iface_status_str);
721         }
722
723         if ((!(opts & FLAG_NO_STARTUP)
724              && iface_status == IFSTATUS_UP
725             )
726          || (opts & FLAG_INITIAL_DOWN)
727         ) {
728                 if (run_script(iface_status_str) != 0)
729                         goto exiting;
730         }
731
732         /* Main loop */
733         netlink_pollfd[0].fd = netlink_fd;
734         netlink_pollfd[0].events = POLLIN;
735         delay_time = 0;
736         while (1) {
737                 int iface_status_old;
738                 int iface_exists_old;
739
740                 switch (bb_got_signal) {
741                 case SIGINT:
742                 case SIGTERM:
743                         bb_got_signal = 0;
744                         goto cleanup;
745                 case SIGQUIT:
746                         bb_got_signal = 0;
747                         goto exiting;
748                 default:
749                         bb_got_signal = 0;
750                         break;
751                 }
752
753                 if (poll(netlink_pollfd,
754                                 (opts & FLAG_MONITOR) ? 1 : 0,
755                                 G.poll_time * 1000
756                         ) < 0
757                 ) {
758                         if (errno == EINTR)
759                                 continue;
760                         bb_perror_msg("poll");
761                         goto exiting;
762                 }
763
764                 iface_status_old = iface_status;
765                 iface_exists_old = G.iface_exists;
766
767                 if ((opts & FLAG_MONITOR)
768                  && (netlink_pollfd[0].revents & POLLIN)
769                 ) {
770                         G.iface_exists = check_existence_through_netlink();
771                         if (G.iface_exists < 0) /* error */
772                                 goto exiting;
773                         if (iface_exists_old != G.iface_exists) {
774                                 bb_error_msg("interface %sappeared",
775                                                 G.iface_exists ? "" : "dis");
776                                 if (G.iface_exists)
777                                         maybe_up_new_iface();
778                         }
779                 }
780
781                 /* note: if !G.iface_exists, returns DOWN */
782                 iface_status = detect_link();
783                 if (iface_status == IFSTATUS_ERR) {
784                         if (!(opts & FLAG_MONITOR))
785                                 goto exiting;
786                         iface_status = IFSTATUS_DOWN;
787                 }
788                 iface_status_str = strstatus(iface_status);
789
790                 if (iface_status_old != iface_status) {
791                         bb_error_msg("link is %s", iface_status_str);
792
793                         if (delay_time) {
794                                 /* link restored its old status before
795                                  * we run script. don't run the script: */
796                                 delay_time = 0;
797                         } else {
798                                 delay_time = monotonic_sec();
799                                 if (iface_status == IFSTATUS_UP)
800                                         delay_time += G.delay_up;
801                                 if (iface_status == IFSTATUS_DOWN)
802                                         delay_time += G.delay_down;
803                                 if (delay_time == 0)
804                                         delay_time++;
805                         }
806                 }
807
808                 if (delay_time && (int)(monotonic_sec() - delay_time) >= 0) {
809                         delay_time = 0;
810                         if (run_script(iface_status_str) != 0)
811                                 goto exiting;
812                 }
813         } /* while (1) */
814
815  cleanup:
816         if (!(opts & FLAG_NO_SHUTDOWN)
817          && (iface_status == IFSTATUS_UP
818              || (iface_status == IFSTATUS_DOWN && delay_time)
819             )
820         ) {
821                 setenv(IFPLUGD_ENV_PREVIOUS, strstatus(iface_status), 1);
822                 setenv(IFPLUGD_ENV_CURRENT, strstatus(-1), 1);
823                 run_script("down\0up"); /* reusing string */
824         }
825
826  exiting:
827         remove_pidfile(pidfile_name);
828         bb_error_msg_and_die("exiting");
829 }