typo fixes
[oweals/busybox.git] / networking / ifplugd.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * ifplugd for busybox, based on ifplugd 0.28 (written by Lennart Poettering).
4  *
5  * Copyright (C) 2009 Maksym Kryzhanovskyy <xmaks@email.cz>
6  *
7  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8  */
9
10 //usage:#define ifplugd_trivial_usage
11 //usage:       "[OPTIONS]"
12 //usage:#define ifplugd_full_usage "\n\n"
13 //usage:       "Network interface plug detection daemon\n"
14 //usage:     "\n        -n              Don't daemonize"
15 //usage:     "\n        -s              Don't log to syslog"
16 //usage:     "\n        -i IFACE        Interface"
17 //usage:     "\n        -f/-F           Treat link detection error as link down/link up"
18 //usage:     "\n                        (otherwise exit on error)"
19 //usage:     "\n        -a              Don't up interface at each link probe"
20 //usage:     "\n        -M              Monitor creation/destruction of interface"
21 //usage:     "\n                        (otherwise it must exist)"
22 //usage:     "\n        -r PROG         Script to run"
23 //usage:     "\n        -x ARG          Extra argument for script"
24 //usage:     "\n        -I              Don't exit on nonzero exit code from script"
25 //usage:     "\n        -p              Don't run \"up\" script on startup"
26 //usage:     "\n        -q              Don't run \"down\" script on exit"
27 //usage:     "\n        -l              Always run script on startup"
28 //usage:     "\n        -t SECS         Poll time in seconds"
29 //usage:     "\n        -u SECS         Delay before running script after link up"
30 //usage:     "\n        -d SECS         Delay after link down"
31 //usage:     "\n        -m MODE         API mode (mii, priv, ethtool, wlan, iff, auto)"
32 //usage:     "\n        -k              Kill running daemon"
33
34 #include "libbb.h"
35
36 #include "fix_u32.h"
37 #include <linux/if.h>
38 #include <linux/mii.h>
39 #include <linux/ethtool.h>
40 #ifdef HAVE_NET_ETHERNET_H
41 /* musl breakage:
42  * In file included from /usr/include/net/ethernet.h:10,
43  *                  from networking/ifplugd.c:41:
44  * /usr/include/netinet/if_ether.h:96: error: redefinition of 'struct ethhdr'
45  *
46  * Build succeeds without it on musl. Commented it out.
47  * If on your system you need it, consider removing <linux/ethtool.h>
48  * and copy-pasting its definitions here (<linux/ethtool.h> is what pulls in
49  * conflicting definition of struct ethhdr on musl).
50  */
51 /* # include <net/ethernet.h> */
52 #endif
53 #include <linux/netlink.h>
54 #include <linux/rtnetlink.h>
55 #include <linux/sockios.h>
56 #include <syslog.h>
57
58 #define __user
59 #include <linux/wireless.h>
60
61 #ifndef ETH_ALEN
62 # define ETH_ALEN  6
63 #endif
64
65 /*
66 From initial port to busybox, removed most of the redundancy by
67 converting implementation of a polymorphic interface to the strict
68 functional style. The main role is run a script when link state
69 changed, other activities like audio signal or detailed reports
70 are on the script itself.
71
72 One questionable point of the design is netlink usage:
73
74 We have 1 second timeout by default to poll the link status,
75 it is short enough so that there are no real benefits in
76 using netlink to get "instantaneous" interface creation/deletion
77 notifications. We can check for interface existence by just
78 doing some fast ioctl using its name.
79
80 Netlink code then can be just dropped (1k or more?)
81 */
82
83
84 #define IFPLUGD_ENV_PREVIOUS "IFPLUGD_PREVIOUS"
85 #define IFPLUGD_ENV_CURRENT "IFPLUGD_CURRENT"
86
87 enum {
88         FLAG_NO_AUTO                    = 1 <<  0, // -a, Do not enable interface automatically
89         FLAG_NO_DAEMON                  = 1 <<  1, // -n, Do not daemonize
90         FLAG_NO_SYSLOG                  = 1 <<  2, // -s, Do not use syslog, use stderr instead
91         FLAG_IGNORE_FAIL                = 1 <<  3, // -f, Ignore detection failure, retry instead (failure is treated as DOWN)
92         FLAG_IGNORE_FAIL_POSITIVE       = 1 <<  4, // -F, Ignore detection failure, retry instead (failure is treated as UP)
93         FLAG_IFACE                      = 1 <<  5, // -i, Specify ethernet interface
94         FLAG_RUN                        = 1 <<  6, // -r, Specify program to execute
95         FLAG_IGNORE_RETVAL              = 1 <<  7, // -I, Don't exit on nonzero return value of program executed
96         FLAG_POLL_TIME                  = 1 <<  8, // -t, Specify poll time in seconds
97         FLAG_DELAY_UP                   = 1 <<  9, // -u, Specify delay for configuring interface
98         FLAG_DELAY_DOWN                 = 1 << 10, // -d, Specify delay for deconfiguring interface
99         FLAG_API_MODE                   = 1 << 11, // -m, Force API mode (mii, priv, ethtool, wlan, auto)
100         FLAG_NO_STARTUP                 = 1 << 12, // -p, Don't run script on daemon startup
101         FLAG_NO_SHUTDOWN                = 1 << 13, // -q, Don't run script on daemon quit
102         FLAG_INITIAL_DOWN               = 1 << 14, // -l, Run "down" script on startup if no cable is detected
103         FLAG_EXTRA_ARG                  = 1 << 15, // -x, Specify an extra argument for action script
104         FLAG_MONITOR                    = 1 << 16, // -M, Use interface monitoring
105 #if ENABLE_FEATURE_PIDFILE
106         FLAG_KILL                       = 1 << 17, // -k, Kill a running daemon
107 #endif
108 };
109 #if ENABLE_FEATURE_PIDFILE
110 # define OPTION_STR "+ansfFi:r:It:+u:+d:+m:pqlx:Mk"
111 #else
112 # define OPTION_STR "+ansfFi:r:It:+u:+d:+m:pqlx:M"
113 #endif
114
115 enum { // interface status
116         IFSTATUS_ERR = -1,
117         IFSTATUS_DOWN = 0,
118         IFSTATUS_UP = 1,
119 };
120
121 enum { // constant fds
122         ioctl_fd = 3,
123         netlink_fd = 4,
124 };
125
126 struct globals {
127         smallint iface_last_status;
128         smallint iface_prev_status;
129         smallint iface_exists;
130         smallint api_method_num;
131
132         /* Used in getopt32, must have sizeof == sizeof(int) */
133         unsigned poll_time;
134         unsigned delay_up;
135         unsigned delay_down;
136
137         const char *iface;
138         const char *api_mode;
139         const char *script_name;
140         const char *extra_arg;
141 };
142 #define G (*ptr_to_globals)
143 #define INIT_G() do { \
144         SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
145         G.iface_last_status = -1; \
146         G.iface_exists   = 1; \
147         G.poll_time      = 1; \
148         G.delay_down     = 5; \
149         G.iface          = "eth0"; \
150         G.api_mode       = "a"; \
151         G.script_name    = "/etc/ifplugd/ifplugd.action"; \
152 } while (0)
153
154
155 /* Utility routines */
156
157 static void set_ifreq_to_ifname(struct ifreq *ifreq)
158 {
159         memset(ifreq, 0, sizeof(struct ifreq));
160         strncpy_IFNAMSIZ(ifreq->ifr_name, G.iface);
161 }
162
163 static int network_ioctl(int request, void* data, const char *errmsg)
164 {
165         int r = ioctl(ioctl_fd, request, data);
166         if (r < 0 && errmsg)
167                 bb_perror_msg("%s failed", errmsg);
168         return r;
169 }
170
171 /* Link detection routines and table */
172
173 static smallint detect_link_mii(void)
174 {
175         /* char buffer instead of bona-fide struct avoids aliasing warning */
176         char buf[sizeof(struct ifreq)];
177         struct ifreq *const ifreq = (void *)buf;
178
179         struct mii_ioctl_data *mii = (void *)&ifreq->ifr_data;
180
181         set_ifreq_to_ifname(ifreq);
182
183         if (network_ioctl(SIOCGMIIPHY, ifreq, "SIOCGMIIPHY") < 0) {
184                 return IFSTATUS_ERR;
185         }
186
187         mii->reg_num = 1;
188
189         if (network_ioctl(SIOCGMIIREG, ifreq, "SIOCGMIIREG") < 0) {
190                 return IFSTATUS_ERR;
191         }
192
193         return (mii->val_out & 0x0004) ? IFSTATUS_UP : IFSTATUS_DOWN;
194 }
195
196 static smallint detect_link_priv(void)
197 {
198         /* char buffer instead of bona-fide struct avoids aliasing warning */
199         char buf[sizeof(struct ifreq)];
200         struct ifreq *const ifreq = (void *)buf;
201
202         struct mii_ioctl_data *mii = (void *)&ifreq->ifr_data;
203
204         set_ifreq_to_ifname(ifreq);
205
206         if (network_ioctl(SIOCDEVPRIVATE, ifreq, "SIOCDEVPRIVATE") < 0) {
207                 return IFSTATUS_ERR;
208         }
209
210         mii->reg_num = 1;
211
212         if (network_ioctl(SIOCDEVPRIVATE+1, ifreq, "SIOCDEVPRIVATE+1") < 0) {
213                 return IFSTATUS_ERR;
214         }
215
216         return (mii->val_out & 0x0004) ? IFSTATUS_UP : IFSTATUS_DOWN;
217 }
218
219 static smallint detect_link_ethtool(void)
220 {
221         struct ifreq ifreq;
222         struct ethtool_value edata;
223
224         set_ifreq_to_ifname(&ifreq);
225
226         edata.cmd = ETHTOOL_GLINK;
227         ifreq.ifr_data = (void*) &edata;
228
229         if (network_ioctl(SIOCETHTOOL, &ifreq, "ETHTOOL_GLINK") < 0) {
230                 return IFSTATUS_ERR;
231         }
232
233         return edata.data ? IFSTATUS_UP : IFSTATUS_DOWN;
234 }
235
236 static smallint detect_link_iff(void)
237 {
238         struct ifreq ifreq;
239
240         set_ifreq_to_ifname(&ifreq);
241
242         if (network_ioctl(SIOCGIFFLAGS, &ifreq, "SIOCGIFFLAGS") < 0) {
243                 return IFSTATUS_ERR;
244         }
245
246         /* If IFF_UP is not set (interface is down), IFF_RUNNING is never set
247          * regardless of link status. Simply continue to report last status -
248          * no point in reporting spurious link downs if interface is disabled
249          * by admin. When/if it will be brought up,
250          * we'll report real link status.
251          */
252         if (!(ifreq.ifr_flags & IFF_UP) && G.iface_last_status != IFSTATUS_ERR)
253                 return G.iface_last_status;
254
255         return (ifreq.ifr_flags & IFF_RUNNING) ? IFSTATUS_UP : IFSTATUS_DOWN;
256 }
257
258 static smallint detect_link_wlan(void)
259 {
260         int i;
261         struct iwreq iwrequest;
262         uint8_t mac[ETH_ALEN];
263
264         memset(&iwrequest, 0, sizeof(iwrequest));
265         strncpy_IFNAMSIZ(iwrequest.ifr_ifrn.ifrn_name, G.iface);
266
267         if (network_ioctl(SIOCGIWAP, &iwrequest, "SIOCGIWAP") < 0) {
268                 return IFSTATUS_ERR;
269         }
270
271         memcpy(mac, &iwrequest.u.ap_addr.sa_data, ETH_ALEN);
272
273         if (mac[0] == 0xFF || mac[0] == 0x44 || mac[0] == 0x00) {
274                 for (i = 1; i < ETH_ALEN; ++i) {
275                         if (mac[i] != mac[0])
276                                 return IFSTATUS_UP;
277                 }
278                 return IFSTATUS_DOWN;
279         }
280
281         return IFSTATUS_UP;
282 }
283
284 enum { // api mode
285         API_ETHTOOL, // 'e'
286         API_MII,     // 'm'
287         API_PRIVATE, // 'p'
288         API_WLAN,    // 'w'
289         API_IFF,     // 'i'
290         API_AUTO,    // 'a'
291 };
292
293 static const char api_modes[] ALIGN1 = "empwia";
294
295 static const struct {
296         const char *name;
297         smallint (*func)(void);
298 } method_table[] = {
299         { "SIOCETHTOOL"       , &detect_link_ethtool },
300         { "SIOCGMIIPHY"       , &detect_link_mii     },
301         { "SIOCDEVPRIVATE"    , &detect_link_priv    },
302         { "wireless extension", &detect_link_wlan    },
303         { "IFF_RUNNING"       , &detect_link_iff     },
304 };
305
306 static const char *strstatus(int status)
307 {
308         if (status == IFSTATUS_ERR)
309                 return "error";
310         return "down\0up" + (status * 5);
311 }
312
313 static int run_script(const char *action)
314 {
315         char *env_PREVIOUS, *env_CURRENT;
316         char *argv[5];
317         int r;
318
319         bb_error_msg("executing '%s %s %s'", G.script_name, G.iface, action);
320
321         argv[0] = (char*) G.script_name;
322         argv[1] = (char*) G.iface;
323         argv[2] = (char*) action;
324         argv[3] = (char*) G.extra_arg;
325         argv[4] = NULL;
326
327         env_PREVIOUS = xasprintf("%s=%s", IFPLUGD_ENV_PREVIOUS, strstatus(G.iface_prev_status));
328         putenv(env_PREVIOUS);
329         env_CURRENT = xasprintf("%s=%s", IFPLUGD_ENV_CURRENT, strstatus(G.iface_last_status));
330         putenv(env_CURRENT);
331
332         /* r < 0 - can't exec, 0 <= r < 0x180 - exited, >=0x180 - killed by sig (r-0x180) */
333         r = spawn_and_wait(argv);
334
335         unsetenv(IFPLUGD_ENV_PREVIOUS);
336         unsetenv(IFPLUGD_ENV_CURRENT);
337         free(env_PREVIOUS);
338         free(env_CURRENT);
339
340         bb_error_msg("exit code: %d", r & 0xff);
341         return (option_mask32 & FLAG_IGNORE_RETVAL) ? 0 : r;
342 }
343
344 static void up_iface(void)
345 {
346         struct ifreq ifrequest;
347
348         if (!G.iface_exists)
349                 return;
350
351         set_ifreq_to_ifname(&ifrequest);
352         if (network_ioctl(SIOCGIFFLAGS, &ifrequest, "getting interface flags") < 0) {
353                 G.iface_exists = 0;
354                 return;
355         }
356
357         if (!(ifrequest.ifr_flags & IFF_UP)) {
358                 ifrequest.ifr_flags |= IFF_UP;
359                 /* Let user know we mess up with interface */
360                 bb_error_msg("upping interface");
361                 if (network_ioctl(SIOCSIFFLAGS, &ifrequest, "setting interface flags") < 0)
362                         xfunc_die();
363         }
364
365 #if 0 /* why do we mess with IP addr? It's not our business */
366         if (network_ioctl(SIOCGIFADDR, &ifrequest, "can't get interface address") < 0) {
367         } else if (ifrequest.ifr_addr.sa_family != AF_INET) {
368                 bb_perror_msg("the interface is not IP-based");
369         } else {
370                 ((struct sockaddr_in*)(&ifrequest.ifr_addr))->sin_addr.s_addr = INADDR_ANY;
371                 network_ioctl(SIOCSIFADDR, &ifrequest, "can't set interface address");
372         }
373         network_ioctl(SIOCGIFFLAGS, &ifrequest, "can't get interface flags");
374 #endif
375 }
376
377 static void maybe_up_new_iface(void)
378 {
379         if (!(option_mask32 & FLAG_NO_AUTO))
380                 up_iface();
381
382 #if 0 /* bloat */
383         struct ifreq ifrequest;
384         struct ethtool_drvinfo driver_info;
385
386         set_ifreq_to_ifname(&ifrequest);
387         driver_info.cmd = ETHTOOL_GDRVINFO;
388         ifrequest.ifr_data = &driver_info;
389         if (network_ioctl(SIOCETHTOOL, &ifrequest, NULL) == 0) {
390                 char buf[sizeof("/xx:xx:xx:xx:xx:xx")];
391
392                 /* Get MAC */
393                 buf[0] = '\0';
394                 set_ifreq_to_ifname(&ifrequest);
395                 if (network_ioctl(SIOCGIFHWADDR, &ifrequest, NULL) == 0) {
396                         sprintf(buf, "/%02X:%02X:%02X:%02X:%02X:%02X",
397                                 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[0]),
398                                 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[1]),
399                                 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[2]),
400                                 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[3]),
401                                 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[4]),
402                                 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[5]));
403                 }
404
405                 bb_error_msg("using interface %s%s with driver<%s> (version: %s)",
406                         G.iface, buf, driver_info.driver, driver_info.version);
407         }
408 #endif
409         if (G.api_mode[0] == 'a')
410                 G.api_method_num = API_AUTO;
411 }
412
413 static smallint detect_link(void)
414 {
415         smallint status;
416
417         if (!G.iface_exists)
418                 return (option_mask32 & FLAG_MONITOR) ? IFSTATUS_DOWN : IFSTATUS_ERR;
419
420         /* Some drivers can't detect link status when the interface is down.
421          * I imagine detect_link_iff() is the most vulnerable.
422          * That's why -a "noauto" in an option, not a hardwired behavior.
423          */
424         if (!(option_mask32 & FLAG_NO_AUTO))
425                 up_iface();
426
427         if (G.api_method_num == API_AUTO) {
428                 int i;
429                 smallint sv_logmode;
430
431                 sv_logmode = logmode;
432                 for (i = 0; i < ARRAY_SIZE(method_table); i++) {
433                         logmode = LOGMODE_NONE;
434                         status = method_table[i].func();
435                         logmode = sv_logmode;
436                         if (status != IFSTATUS_ERR) {
437                                 G.api_method_num = i;
438                                 bb_error_msg("using %s detection mode", method_table[i].name);
439                                 break;
440                         }
441                 }
442         } else {
443                 status = method_table[G.api_method_num].func();
444         }
445
446         if (status == IFSTATUS_ERR) {
447                 if (option_mask32 & FLAG_IGNORE_FAIL)
448                         status = IFSTATUS_DOWN;
449                 else if (option_mask32 & FLAG_IGNORE_FAIL_POSITIVE)
450                         status = IFSTATUS_UP;
451                 else if (G.api_mode[0] == 'a')
452                         bb_error_msg("can't detect link status");
453         }
454
455         if (status != G.iface_last_status) {
456                 G.iface_prev_status = G.iface_last_status;
457                 G.iface_last_status = status;
458         }
459
460         return status;
461 }
462
463 static NOINLINE int check_existence_through_netlink(void)
464 {
465         int iface_len;
466         /* Buffer was 1K, but on linux-3.9.9 it was reported to be too small.
467          * netlink.h: "limit to 8K to avoid MSG_TRUNC when PAGE_SIZE is very large".
468          * Note: on error returns (-1) we exit, no need to free replybuf.
469          */
470         enum { BUF_SIZE = 8 * 1024 };
471         char *replybuf = xmalloc(BUF_SIZE);
472
473         iface_len = strlen(G.iface);
474         while (1) {
475                 struct nlmsghdr *mhdr;
476                 ssize_t bytes;
477
478                 bytes = recv(netlink_fd, replybuf, BUF_SIZE, MSG_DONTWAIT);
479                 if (bytes < 0) {
480                         if (errno == EAGAIN)
481                                 goto ret;
482                         if (errno == EINTR)
483                                 continue;
484                         bb_perror_msg("netlink: recv");
485                         return -1;
486                 }
487
488                 mhdr = (struct nlmsghdr*)replybuf;
489                 while (bytes > 0) {
490                         if (!NLMSG_OK(mhdr, bytes)) {
491                                 bb_error_msg("netlink packet too small or truncated");
492                                 return -1;
493                         }
494
495                         if (mhdr->nlmsg_type == RTM_NEWLINK || mhdr->nlmsg_type == RTM_DELLINK) {
496                                 struct rtattr *attr;
497                                 int attr_len;
498
499                                 if (mhdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) {
500                                         bb_error_msg("netlink packet too small or truncated");
501                                         return -1;
502                                 }
503
504                                 attr = IFLA_RTA(NLMSG_DATA(mhdr));
505                                 attr_len = IFLA_PAYLOAD(mhdr);
506
507                                 while (RTA_OK(attr, attr_len)) {
508                                         if (attr->rta_type == IFLA_IFNAME) {
509                                                 int len = RTA_PAYLOAD(attr);
510                                                 if (len > IFNAMSIZ)
511                                                         len = IFNAMSIZ;
512                                                 if (iface_len <= len
513                                                  && strncmp(G.iface, RTA_DATA(attr), len) == 0
514                                                 ) {
515                                                         G.iface_exists = (mhdr->nlmsg_type == RTM_NEWLINK);
516                                                 }
517                                         }
518                                         attr = RTA_NEXT(attr, attr_len);
519                                 }
520                         }
521
522                         mhdr = NLMSG_NEXT(mhdr, bytes);
523                 }
524         }
525
526  ret:
527         free(replybuf);
528         return G.iface_exists;
529 }
530
531 #if ENABLE_FEATURE_PIDFILE
532 static NOINLINE pid_t read_pid(const char *filename)
533 {
534         int len;
535         char buf[128];
536
537         len = open_read_close(filename, buf, 127);
538         if (len > 0) {
539                 buf[len] = '\0';
540                 /* returns ULONG_MAX on error => -1 */
541                 return bb_strtoul(buf, NULL, 10);
542         }
543         return 0;
544 }
545 #endif
546
547 int ifplugd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
548 int ifplugd_main(int argc UNUSED_PARAM, char **argv)
549 {
550         int iface_status;
551         int delay_time;
552         const char *iface_status_str;
553         struct pollfd netlink_pollfd[1];
554         unsigned opts;
555         const char *api_mode_found;
556 #if ENABLE_FEATURE_PIDFILE
557         char *pidfile_name;
558         pid_t pid_from_pidfile;
559 #endif
560
561         INIT_G();
562
563         opts = getopt32(argv, OPTION_STR,
564                 &G.iface, &G.script_name, &G.poll_time, &G.delay_up,
565                 &G.delay_down, &G.api_mode, &G.extra_arg);
566         G.poll_time *= 1000;
567
568         applet_name = xasprintf("ifplugd(%s)", G.iface);
569
570 #if ENABLE_FEATURE_PIDFILE
571         pidfile_name = xasprintf(CONFIG_PID_FILE_PATH "/ifplugd.%s.pid", G.iface);
572         pid_from_pidfile = read_pid(pidfile_name);
573
574         if (opts & FLAG_KILL) {
575                 if (pid_from_pidfile > 0)
576                         /* Upstream tool use SIGINT for -k */
577                         kill(pid_from_pidfile, SIGINT);
578                 return EXIT_SUCCESS;
579         }
580
581         if (pid_from_pidfile > 0 && kill(pid_from_pidfile, 0) == 0)
582                 bb_error_msg_and_die("daemon already running");
583 #endif
584
585         api_mode_found = strchr(api_modes, G.api_mode[0]);
586         if (!api_mode_found)
587                 bb_error_msg_and_die("unknown API mode '%s'", G.api_mode);
588         G.api_method_num = api_mode_found - api_modes;
589
590         if (!(opts & FLAG_NO_DAEMON))
591                 bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
592
593         xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), ioctl_fd);
594         if (opts & FLAG_MONITOR) {
595                 struct sockaddr_nl addr;
596                 int fd = xsocket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
597
598                 memset(&addr, 0, sizeof(addr));
599                 addr.nl_family = AF_NETLINK;
600                 addr.nl_groups = RTMGRP_LINK;
601                 addr.nl_pid = getpid();
602
603                 xbind(fd, (struct sockaddr*)&addr, sizeof(addr));
604                 xmove_fd(fd, netlink_fd);
605         }
606
607         write_pidfile(pidfile_name);
608
609         /* this can't be moved before socket creation */
610         if (!(opts & FLAG_NO_SYSLOG)) {
611                 openlog(applet_name, 0, LOG_DAEMON);
612                 logmode |= LOGMODE_SYSLOG;
613         }
614
615         bb_signals(0
616                 | (1 << SIGINT )
617                 | (1 << SIGTERM)
618                 | (1 << SIGQUIT)
619                 | (1 << SIGHUP ) /* why we ignore it? */
620                 /* | (1 << SIGCHLD) - run_script does not use it anymore */
621                 , record_signo);
622
623         bb_error_msg("started: %s", bb_banner);
624
625         if (opts & FLAG_MONITOR) {
626                 struct ifreq ifrequest;
627                 set_ifreq_to_ifname(&ifrequest);
628                 G.iface_exists = (network_ioctl(SIOCGIFINDEX, &ifrequest, NULL) == 0);
629         }
630
631         if (G.iface_exists)
632                 maybe_up_new_iface();
633
634         iface_status = detect_link();
635         if (iface_status == IFSTATUS_ERR)
636                 goto exiting;
637         iface_status_str = strstatus(iface_status);
638
639         if (opts & FLAG_MONITOR) {
640                 bb_error_msg("interface %s",
641                         G.iface_exists ? "exists"
642                         : "doesn't exist, waiting");
643         }
644         /* else we assume it always exists, but don't mislead user
645          * by potentially lying that it really exists */
646
647         if (G.iface_exists) {
648                 bb_error_msg("link is %s", iface_status_str);
649         }
650
651         if ((!(opts & FLAG_NO_STARTUP)
652              && iface_status == IFSTATUS_UP
653             )
654          || (opts & FLAG_INITIAL_DOWN)
655         ) {
656                 if (run_script(iface_status_str) != 0)
657                         goto exiting;
658         }
659
660         /* Main loop */
661         netlink_pollfd[0].fd = netlink_fd;
662         netlink_pollfd[0].events = POLLIN;
663         delay_time = 0;
664         while (1) {
665                 int iface_status_old;
666
667                 switch (bb_got_signal) {
668                 case SIGINT:
669                 case SIGTERM:
670                         bb_got_signal = 0;
671                         goto cleanup;
672                 case SIGQUIT:
673                         bb_got_signal = 0;
674                         goto exiting;
675                 default:
676                         bb_got_signal = 0;
677                         break;
678                 }
679
680                 if (poll(netlink_pollfd,
681                                 (opts & FLAG_MONITOR) ? 1 : 0,
682                                 G.poll_time
683                         ) < 0
684                 ) {
685                         if (errno == EINTR)
686                                 continue;
687                         bb_perror_msg("poll");
688                         goto exiting;
689                 }
690
691                 if ((opts & FLAG_MONITOR)
692                  && (netlink_pollfd[0].revents & POLLIN)
693                 ) {
694                         int iface_exists_old;
695
696                         iface_exists_old = G.iface_exists;
697                         G.iface_exists = check_existence_through_netlink();
698                         if (G.iface_exists < 0) /* error */
699                                 goto exiting;
700                         if (iface_exists_old != G.iface_exists) {
701                                 bb_error_msg("interface %sappeared",
702                                                 G.iface_exists ? "" : "dis");
703                                 if (G.iface_exists)
704                                         maybe_up_new_iface();
705                         }
706                 }
707
708                 /* note: if !G.iface_exists, returns DOWN */
709                 iface_status_old = iface_status;
710                 iface_status = detect_link();
711                 if (iface_status == IFSTATUS_ERR) {
712                         if (!(opts & FLAG_MONITOR))
713                                 goto exiting;
714                         iface_status = IFSTATUS_DOWN;
715                 }
716                 iface_status_str = strstatus(iface_status);
717
718                 if (iface_status_old != iface_status) {
719                         bb_error_msg("link is %s", iface_status_str);
720
721                         if (delay_time) {
722                                 /* link restored its old status before
723                                  * we ran script. don't run the script: */
724                                 delay_time = 0;
725                         } else {
726                                 delay_time = monotonic_sec();
727                                 if (iface_status == IFSTATUS_UP)
728                                         delay_time += G.delay_up;
729                                 if (iface_status == IFSTATUS_DOWN)
730                                         delay_time += G.delay_down;
731 #if 0  /* if you are back in 1970... */
732                                 if (delay_time == 0) {
733                                         sleep(1);
734                                         delay_time = 1;
735                                 }
736 #endif
737                         }
738                 }
739
740                 if (delay_time && (int)(monotonic_sec() - delay_time) >= 0) {
741                         if (run_script(iface_status_str) != 0)
742                                 goto exiting;
743                         delay_time = 0;
744                 }
745         } /* while (1) */
746
747  cleanup:
748         if (!(opts & FLAG_NO_SHUTDOWN)
749          && (iface_status == IFSTATUS_UP
750              || (iface_status == IFSTATUS_DOWN && delay_time)
751             )
752         ) {
753                 setenv(IFPLUGD_ENV_PREVIOUS, strstatus(iface_status), 1);
754                 setenv(IFPLUGD_ENV_CURRENT, strstatus(-1), 1);
755                 run_script("down\0up"); /* reusing string */
756         }
757
758  exiting:
759         remove_pidfile(pidfile_name);
760         bb_error_msg_and_die("exiting");
761 }