source: branches/3.2/mindi-busybox/networking/ifplugd.c @ 3232

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