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