X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=networking%2Fifplugd.c;h=9bc1a075f51bcfeb201ddc4e29a791252e4d622f;hb=ed4393bdc7a4d3b1b59293a3393eb1d6953bac99;hp=ac6607c9155a6b9740a632cb592473a665f76927;hpb=f422a722bb7c0b39a086255380c87eb4ba7af45b;p=oweals%2Fbusybox.git diff --git a/networking/ifplugd.c b/networking/ifplugd.c index ac6607c91..9bc1a075f 100644 --- a/networking/ifplugd.c +++ b/networking/ifplugd.c @@ -1,18 +1,65 @@ /* vi: set sw=4 ts=4: */ /* - * ifplugd for busybox + * ifplugd for busybox, based on ifplugd 0.28 (written by Lennart Poettering). * * Copyright (C) 2009 Maksym Kryzhanovskyy * - * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ +//config:config IFPLUGD +//config: bool "ifplugd (9.9 kb)" +//config: default y +//config: select PLATFORM_LINUX +//config: help +//config: Network interface plug detection daemon. + +//applet:IF_IFPLUGD(APPLET(ifplugd, BB_DIR_USR_SBIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_IFPLUGD) += ifplugd.o + +//usage:#define ifplugd_trivial_usage +//usage: "[OPTIONS]" +//usage:#define ifplugd_full_usage "\n\n" +//usage: "Network interface plug detection daemon\n" +//usage: "\n -n Don't daemonize" +//usage: "\n -s Don't log to syslog" +//usage: "\n -i IFACE Interface" +//usage: "\n -f/-F Treat link detection error as link down/link up" +//usage: "\n (otherwise exit on error)" +//usage: "\n -a Don't up interface at each link probe" +//usage: "\n -M Monitor creation/destruction of interface" +//usage: "\n (otherwise it must exist)" +//usage: "\n -r PROG Script to run" +//usage: "\n -x ARG Extra argument for script" +//usage: "\n -I Don't exit on nonzero exit code from script" +//usage: "\n -p Don't run \"up\" script on startup" +//usage: "\n -q Don't run \"down\" script on exit" +//usage: "\n -l Always run script on startup" +//usage: "\n -t SECS Poll time in seconds" +//usage: "\n -u SECS Delay before running script after link up" +//usage: "\n -d SECS Delay after link down" +//usage: "\n -m MODE API mode (mii, priv, ethtool, wlan, iff, auto)" +//usage: "\n -k Kill running daemon" + #include "libbb.h" #include "fix_u32.h" #include #include #include -#include +#ifdef HAVE_NET_ETHERNET_H +/* musl breakage: + * In file included from /usr/include/net/ethernet.h:10, + * from networking/ifplugd.c:41: + * /usr/include/netinet/if_ether.h:96: error: redefinition of 'struct ethhdr' + * + * Build succeeds without it on musl. Commented it out. + * If on your system you need it, consider removing + * and copy-pasting its definitions here ( is what pulls in + * conflicting definition of struct ethhdr on musl). + */ +/* # include */ +#endif #include #include #include @@ -21,8 +68,16 @@ #define __user #include +#ifndef ETH_ALEN +# define ETH_ALEN 6 +#endif + /* -TODO: describe compat status here. +From initial port to busybox, removed most of the redundancy by +converting implementation of a polymorphic interface to the strict +functional style. The main role is run a script when link state +changed, other activities like audio signal or detailed reports +are on the script itself. One questionable point of the design is netlink usage: @@ -62,20 +117,11 @@ enum { #endif }; #if ENABLE_FEATURE_PIDFILE -# define OPTION_STR "+ansfFi:r:It:u:d:m:pqlx:Mk" +# define OPTION_STR "+ansfFi:r:It:+u:+d:+m:pqlx:Mk" #else -# define OPTION_STR "+ansfFi:r:It:u:d:m:pqlx:M" +# define OPTION_STR "+ansfFi:r:It:+u:+d:+m:pqlx:M" #endif -enum { // api mode - API_AUTO = 'a', - API_ETHTOOL = 'e', - API_MII = 'm', - API_PRIVATE = 'p', - API_WLAN = 'w', - API_IFF = 'i', -}; - enum { // interface status IFSTATUS_ERR = -1, IFSTATUS_DOWN = 0, @@ -89,7 +135,9 @@ enum { // constant fds struct globals { smallint iface_last_status; + smallint iface_prev_status; smallint iface_exists; + smallint api_method_num; /* Used in getopt32, must have sizeof == sizeof(int) */ unsigned poll_time; @@ -100,9 +148,6 @@ struct globals { const char *api_mode; const char *script_name; const char *extra_arg; - - smallint (*detect_link_func)(void); - smallint (*cached_detect_link_func)(void); }; #define G (*ptr_to_globals) #define INIT_G() do { \ @@ -117,106 +162,7 @@ struct globals { } while (0) -static int run_script(const char *action) -{ - char *argv[5]; - int r; - - bb_error_msg("executing '%s %s %s'", G.script_name, G.iface, action); - -#if 1 - - argv[0] = (char*) G.script_name; - argv[1] = (char*) G.iface; - argv[2] = (char*) action; - argv[3] = (char*) G.extra_arg; - argv[4] = NULL; - - /* r < 0 - can't exec, 0 <= r < 1000 - exited, >1000 - killed by sig (r-1000) */ - r = wait4pid(spawn(argv)); - - bb_error_msg("exit code: %d", r); - return (option_mask32 & FLAG_IGNORE_RETVAL) ? 0 : r; - -#else /* insanity */ - - struct fd_pair pipe_pair; - char buf[256]; - int i = 0; - - xpiped_pair(pipe_pair); - - pid = vfork(); - if (pid < 0) { - bb_perror_msg("fork"); - return -1; - } - - /* child */ - if (pid == 0) { - xmove_fd(pipe_pair.wr, 1); - xdup2(1, 2); - if (pipe_pair.rd > 2) - close(pipe_pair.rd); - - // umask(0022); // Set up a sane umask - - execlp(G.script_name, G.script_name, G.iface, action, G.extra_arg, NULL); - _exit(EXIT_FAILURE); - } - - /* parent */ - close(pipe_pair.wr); - - while (1) { - if (bb_got_signal && bb_got_signal != SIGCHLD) { - bb_error_msg("killing child"); - kill(pid, SIGTERM); - bb_got_signal = 0; - break; - } - - r = read(pipe_pair.rd, &buf[i], 1); - - if (buf[i] == '\n' || i == sizeof(buf)-2 || r != 1) { - if (r == 1 && buf[i] != '\n') - i++; - - buf[i] = '\0'; - - if (i > 0) - bb_error_msg("client: %s", buf); - - i = 0; - } else { - i++; - } - - if (r != 1) - break; - } - - close(pipe_pair.rd); - - wait(&r); - - if (!WIFEXITED(r) || WEXITSTATUS(r) != 0) { - bb_error_msg("program execution failed, return value is %i", - WEXITSTATUS(r)); - return option_mask32 & FLAG_IGNORE_RETVAL ? 0 : WEXITSTATUS(r); - } - bb_error_msg("program executed successfully"); - return 0; -#endif -} - -static int network_ioctl(int request, void* data, const char *errmsg) -{ - int r = ioctl(ioctl_fd, request, data); - if (r < 0 && errmsg) - bb_perror_msg(errmsg); - return r; -} +/* Utility routines */ static void set_ifreq_to_ifname(struct ifreq *ifreq) { @@ -224,96 +170,33 @@ static void set_ifreq_to_ifname(struct ifreq *ifreq) strncpy_IFNAMSIZ(ifreq->ifr_name, G.iface); } -static const char *strstatus(int status) -{ - if (status == IFSTATUS_ERR) - return "error"; - return "down\0up" + (status * 5); -} - -static void up_iface(void) +static int network_ioctl(int request, void* data, const char *errmsg) { - struct ifreq ifrequest; - - if (!G.iface_exists) - return; - - set_ifreq_to_ifname(&ifrequest); - if (network_ioctl(SIOCGIFFLAGS, &ifrequest, "can't get interface flags") < 0) { - G.iface_exists = 0; - return; - } - - if (!(ifrequest.ifr_flags & IFF_UP)) { - ifrequest.ifr_flags |= IFF_UP; - /* Let user know we mess up with interface */ - bb_error_msg("upping interface"); - if (network_ioctl(SIOCSIFFLAGS, &ifrequest, "can't set interface flags") < 0) - xfunc_die(); - } - -#if 0 /* why do we mess with IP addr? It's not our business */ - if (network_ioctl(SIOCGIFADDR, &ifrequest, "can't get interface address") < 0) { - } else if (ifrequest.ifr_addr.sa_family != AF_INET) { - bb_perror_msg("the interface is not IP-based"); - } else { - ((struct sockaddr_in*)(&ifrequest.ifr_addr))->sin_addr.s_addr = INADDR_ANY; - network_ioctl(SIOCSIFADDR, &ifrequest, "can't set interface address"); - } - network_ioctl(SIOCGIFFLAGS, &ifrequest, "can't get interface flags"); -#endif + int r = ioctl(ioctl_fd, request, data); + if (r < 0 && errmsg) + bb_perror_msg("%s failed", errmsg); + return r; } -static void maybe_up_new_iface(void) -{ - if (!(option_mask32 & FLAG_NO_AUTO)) - up_iface(); - -#if 0 /* bloat */ - struct ifreq ifrequest; - struct ethtool_drvinfo driver_info; - - set_ifreq_to_ifname(&ifrequest); - driver_info.cmd = ETHTOOL_GDRVINFO; - ifrequest.ifr_data = &driver_info; - if (network_ioctl(SIOCETHTOOL, &ifrequest, NULL) == 0) { - char buf[sizeof("/xx:xx:xx:xx:xx:xx")]; - - /* Get MAC */ - buf[0] = '\0'; - set_ifreq_to_ifname(&ifrequest); - if (network_ioctl(SIOCGIFHWADDR, &ifrequest, NULL) == 0) { - sprintf(buf, "/%02X:%02X:%02X:%02X:%02X:%02X", - (uint8_t)(ifrequest.ifr_hwaddr.sa_data[0]), - (uint8_t)(ifrequest.ifr_hwaddr.sa_data[1]), - (uint8_t)(ifrequest.ifr_hwaddr.sa_data[2]), - (uint8_t)(ifrequest.ifr_hwaddr.sa_data[3]), - (uint8_t)(ifrequest.ifr_hwaddr.sa_data[4]), - (uint8_t)(ifrequest.ifr_hwaddr.sa_data[5])); - } - - bb_error_msg("using interface %s%s with driver<%s> (version: %s)", - G.iface, buf, driver_info.driver, driver_info.version); - } -#endif - - G.cached_detect_link_func = NULL; -} +/* Link detection routines and table */ static smallint detect_link_mii(void) { - struct ifreq ifreq; - struct mii_ioctl_data *mii = (void *)&ifreq.ifr_data; + /* char buffer instead of bona-fide struct avoids aliasing warning */ + char buf[sizeof(struct ifreq)]; + struct ifreq *const ifreq = (void *)buf; - set_ifreq_to_ifname(&ifreq); + struct mii_ioctl_data *mii = (void *)&ifreq->ifr_data; + + set_ifreq_to_ifname(ifreq); - if (network_ioctl(SIOCGMIIPHY, &ifreq, "SIOCGMIIPHY failed") < 0) { + if (network_ioctl(SIOCGMIIPHY, ifreq, "SIOCGMIIPHY") < 0) { return IFSTATUS_ERR; } mii->reg_num = 1; - if (network_ioctl(SIOCGMIIREG, &ifreq, "SIOCGMIIREG failed") < 0) { + if (network_ioctl(SIOCGMIIREG, ifreq, "SIOCGMIIREG") < 0) { return IFSTATUS_ERR; } @@ -322,18 +205,21 @@ static smallint detect_link_mii(void) static smallint detect_link_priv(void) { - struct ifreq ifreq; - struct mii_ioctl_data *mii = (void *)&ifreq.ifr_data; + /* char buffer instead of bona-fide struct avoids aliasing warning */ + char buf[sizeof(struct ifreq)]; + struct ifreq *const ifreq = (void *)buf; - set_ifreq_to_ifname(&ifreq); + struct mii_ioctl_data *mii = (void *)&ifreq->ifr_data; - if (network_ioctl(SIOCDEVPRIVATE, &ifreq, "SIOCDEVPRIVATE failed") < 0) { + set_ifreq_to_ifname(ifreq); + + if (network_ioctl(SIOCDEVPRIVATE, ifreq, "SIOCDEVPRIVATE") < 0) { return IFSTATUS_ERR; } mii->reg_num = 1; - if (network_ioctl(SIOCDEVPRIVATE+1, &ifreq, "SIOCDEVPRIVATE+1 failed") < 0) { + if (network_ioctl(SIOCDEVPRIVATE+1, ifreq, "SIOCDEVPRIVATE+1") < 0) { return IFSTATUS_ERR; } @@ -350,7 +236,7 @@ static smallint detect_link_ethtool(void) edata.cmd = ETHTOOL_GLINK; ifreq.ifr_data = (void*) &edata; - if (network_ioctl(SIOCETHTOOL, &ifreq, "ETHTOOL_GLINK failed") < 0) { + if (network_ioctl(SIOCETHTOOL, &ifreq, "ETHTOOL_GLINK") < 0) { return IFSTATUS_ERR; } @@ -363,7 +249,7 @@ static smallint detect_link_iff(void) set_ifreq_to_ifname(&ifreq); - if (network_ioctl(SIOCGIFFLAGS, &ifreq, "SIOCGIFFLAGS failed") < 0) { + if (network_ioctl(SIOCGIFFLAGS, &ifreq, "SIOCGIFFLAGS") < 0) { return IFSTATUS_ERR; } @@ -381,20 +267,21 @@ static smallint detect_link_iff(void) static smallint detect_link_wlan(void) { + int i; struct iwreq iwrequest; uint8_t mac[ETH_ALEN]; - memset(&iwrequest, 0, sizeof(struct iwreq)); + memset(&iwrequest, 0, sizeof(iwrequest)); strncpy_IFNAMSIZ(iwrequest.ifr_ifrn.ifrn_name, G.iface); - if (network_ioctl(SIOCGIWAP, &iwrequest, "SIOCGIWAP failed") < 0) { + if (network_ioctl(SIOCGIWAP, &iwrequest, "SIOCGIWAP") < 0) { return IFSTATUS_ERR; } - memcpy(mac, &(iwrequest.u.ap_addr.sa_data), ETH_ALEN); + memcpy(mac, &iwrequest.u.ap_addr.sa_data, ETH_ALEN); if (mac[0] == 0xFF || mac[0] == 0x44 || mac[0] == 0x00) { - for (int i = 1; i < ETH_ALEN; ++i) { + for (i = 1; i < ETH_ALEN; ++i) { if (mac[i] != mac[0]) return IFSTATUS_UP; } @@ -404,61 +291,135 @@ static smallint detect_link_wlan(void) return IFSTATUS_UP; } -static smallint detect_link_auto(void) +enum { // api mode + API_ETHTOOL, // 'e' + API_MII, // 'm' + API_PRIVATE, // 'p' + API_WLAN, // 'w' + API_IFF, // 'i' + API_AUTO, // 'a' +}; + +static const char api_modes[] ALIGN1 = "empwia"; + +static const struct { + const char *name; + smallint (*func)(void); +} method_table[] = { + { "SIOCETHTOOL" , &detect_link_ethtool }, + { "SIOCGMIIPHY" , &detect_link_mii }, + { "SIOCDEVPRIVATE" , &detect_link_priv }, + { "wireless extension", &detect_link_wlan }, + { "IFF_RUNNING" , &detect_link_iff }, +}; + +static const char *strstatus(int status) { - const char *method; - smallint iface_status; - smallint sv_logmode; - - if (G.cached_detect_link_func) { - iface_status = G.cached_detect_link_func(); - if (iface_status != IFSTATUS_ERR) - return iface_status; - } + if (status == IFSTATUS_ERR) + return "error"; + return "down\0up" + (status * 5); +} - sv_logmode = logmode; - logmode = LOGMODE_NONE; - - iface_status = detect_link_ethtool(); - if (iface_status != IFSTATUS_ERR) { - G.cached_detect_link_func = detect_link_ethtool; - method = "SIOCETHTOOL"; - found_method: - logmode = sv_logmode; - bb_error_msg("using %s detection mode", method); - return iface_status; - } +static int run_script(const char *action) +{ + char *env_PREVIOUS, *env_CURRENT; + char *argv[5]; + int r; - iface_status = detect_link_mii(); - if (iface_status != IFSTATUS_ERR) { - G.cached_detect_link_func = detect_link_mii; - method = "SIOCGMIIPHY"; - goto found_method; - } + bb_error_msg("executing '%s %s %s'", G.script_name, G.iface, action); - iface_status = detect_link_priv(); - if (iface_status != IFSTATUS_ERR) { - G.cached_detect_link_func = detect_link_priv; - method = "SIOCDEVPRIVATE"; - goto found_method; + argv[0] = (char*) G.script_name; + argv[1] = (char*) G.iface; + argv[2] = (char*) action; + argv[3] = (char*) G.extra_arg; + argv[4] = NULL; + + env_PREVIOUS = xasprintf("%s=%s", IFPLUGD_ENV_PREVIOUS, strstatus(G.iface_prev_status)); + putenv(env_PREVIOUS); + env_CURRENT = xasprintf("%s=%s", IFPLUGD_ENV_CURRENT, strstatus(G.iface_last_status)); + putenv(env_CURRENT); + + /* r < 0 - can't exec, 0 <= r < 0x180 - exited, >=0x180 - killed by sig (r-0x180) */ + r = spawn_and_wait(argv); + + bb_unsetenv_and_free(env_PREVIOUS); + bb_unsetenv_and_free(env_CURRENT); + + bb_error_msg("exit code: %d", r & 0xff); + return (option_mask32 & FLAG_IGNORE_RETVAL) ? 0 : r; +} + +static void up_iface(void) +{ + struct ifreq ifrequest; + + if (!G.iface_exists) + return; + + set_ifreq_to_ifname(&ifrequest); + if (network_ioctl(SIOCGIFFLAGS, &ifrequest, "getting interface flags") < 0) { + G.iface_exists = 0; + return; } - iface_status = detect_link_wlan(); - if (iface_status != IFSTATUS_ERR) { - G.cached_detect_link_func = detect_link_wlan; - method = "wireless extension"; - goto found_method; + if (!(ifrequest.ifr_flags & IFF_UP)) { + ifrequest.ifr_flags |= IFF_UP; + /* Let user know we mess up with interface */ + bb_error_msg("upping interface"); + if (network_ioctl(SIOCSIFFLAGS, &ifrequest, "setting interface flags") < 0) { + if (errno != ENODEV) + xfunc_die(); + G.iface_exists = 0; + return; + } } - iface_status = detect_link_iff(); - if (iface_status != IFSTATUS_ERR) { - G.cached_detect_link_func = detect_link_iff; - method = "IFF_RUNNING"; - goto found_method; +#if 0 /* why do we mess with IP addr? It's not our business */ + if (network_ioctl(SIOCGIFADDR, &ifrequest, "can't get interface address") < 0) { + } else if (ifrequest.ifr_addr.sa_family != AF_INET) { + bb_perror_msg("the interface is not IP-based"); + } else { + ((struct sockaddr_in*)(&ifrequest.ifr_addr))->sin_addr.s_addr = INADDR_ANY; + network_ioctl(SIOCSIFADDR, &ifrequest, "can't set interface address"); } + network_ioctl(SIOCGIFFLAGS, &ifrequest, "can't get interface flags"); +#endif +} + +static void maybe_up_new_iface(void) +{ + if (!(option_mask32 & FLAG_NO_AUTO)) + up_iface(); + +#if 0 /* bloat */ + struct ifreq ifrequest; + struct ethtool_drvinfo driver_info; + + set_ifreq_to_ifname(&ifrequest); + driver_info.cmd = ETHTOOL_GDRVINFO; + ifrequest.ifr_data = &driver_info; + if (network_ioctl(SIOCETHTOOL, &ifrequest, NULL) == 0) { + char buf[sizeof("/xx:xx:xx:xx:xx:xx")]; + + /* Get MAC */ + buf[0] = '\0'; + set_ifreq_to_ifname(&ifrequest); + if (network_ioctl(SIOCGIFHWADDR, &ifrequest, NULL) == 0) { + sprintf(buf, "/%02X:%02X:%02X:%02X:%02X:%02X", + (uint8_t)(ifrequest.ifr_hwaddr.sa_data[0]), + (uint8_t)(ifrequest.ifr_hwaddr.sa_data[1]), + (uint8_t)(ifrequest.ifr_hwaddr.sa_data[2]), + (uint8_t)(ifrequest.ifr_hwaddr.sa_data[3]), + (uint8_t)(ifrequest.ifr_hwaddr.sa_data[4]), + (uint8_t)(ifrequest.ifr_hwaddr.sa_data[5])); + } - logmode = sv_logmode; - return iface_status; /* IFSTATUS_ERR */ + bb_error_msg("using interface %s%s with driver<%s> (version: %s)", + G.iface, buf, driver_info.driver, driver_info.version); + } +#endif + if (G.api_mode[0] == 'a') + G.api_method_num = API_AUTO; } static smallint detect_link(void) @@ -475,24 +436,36 @@ static smallint detect_link(void) if (!(option_mask32 & FLAG_NO_AUTO)) up_iface(); - status = G.detect_link_func(); + if (G.api_method_num == API_AUTO) { + int i; + smallint sv_logmode; + + sv_logmode = logmode; + for (i = 0; i < ARRAY_SIZE(method_table); i++) { + logmode = LOGMODE_NONE; + status = method_table[i].func(); + logmode = sv_logmode; + if (status != IFSTATUS_ERR) { + G.api_method_num = i; + bb_error_msg("using %s detection mode", method_table[i].name); + break; + } + } + } else { + status = method_table[G.api_method_num].func(); + } + if (status == IFSTATUS_ERR) { if (option_mask32 & FLAG_IGNORE_FAIL) status = IFSTATUS_DOWN; - if (option_mask32 & FLAG_IGNORE_FAIL_POSITIVE) + else if (option_mask32 & FLAG_IGNORE_FAIL_POSITIVE) status = IFSTATUS_UP; - } - - if (status == IFSTATUS_ERR - && G.detect_link_func == detect_link_auto - ) { - bb_error_msg("failed to detect link status"); + else if (G.api_mode[0] == 'a') + bb_error_msg("can't detect link status"); } if (status != G.iface_last_status) { -//TODO: is it safe to repeatedly do this? - setenv(IFPLUGD_ENV_PREVIOUS, strstatus(G.iface_last_status), 1); - setenv(IFPLUGD_ENV_CURRENT, strstatus(status), 1); + G.iface_prev_status = G.iface_last_status; G.iface_last_status = status; } @@ -501,57 +474,56 @@ static smallint detect_link(void) static NOINLINE int check_existence_through_netlink(void) { - char replybuf[1024]; + int iface_len; + /* Buffer was 1K, but on linux-3.9.9 it was reported to be too small. + * netlink.h: "limit to 8K to avoid MSG_TRUNC when PAGE_SIZE is very large". + * Note: on error returns (-1) we exit, no need to free replybuf. + */ + enum { BUF_SIZE = 8 * 1024 }; + char *replybuf = xmalloc(BUF_SIZE); + iface_len = strlen(G.iface); while (1) { struct nlmsghdr *mhdr; ssize_t bytes; - bytes = recv(netlink_fd, &replybuf, sizeof(replybuf), MSG_DONTWAIT); + bytes = recv(netlink_fd, replybuf, BUF_SIZE, MSG_DONTWAIT); if (bytes < 0) { if (errno == EAGAIN) - return G.iface_exists; + goto ret; if (errno == EINTR) continue; - bb_perror_msg("netlink: recv"); return -1; } mhdr = (struct nlmsghdr*)replybuf; while (bytes > 0) { - if (!NLMSG_OK(mhdr, bytes) - || bytes < sizeof(struct nlmsghdr) - || bytes < mhdr->nlmsg_len - ) { + if (!NLMSG_OK(mhdr, bytes)) { bb_error_msg("netlink packet too small or truncated"); return -1; } if (mhdr->nlmsg_type == RTM_NEWLINK || mhdr->nlmsg_type == RTM_DELLINK) { struct rtattr *attr; - struct ifinfomsg *imsg; int attr_len; - imsg = NLMSG_DATA(mhdr); - if (mhdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) { bb_error_msg("netlink packet too small or truncated"); return -1; } - attr = (struct rtattr*)((char*)imsg + NLMSG_ALIGN(sizeof(struct ifinfomsg))); - attr_len = NLMSG_PAYLOAD(mhdr, sizeof(struct ifinfomsg)); + attr = IFLA_RTA(NLMSG_DATA(mhdr)); + attr_len = IFLA_PAYLOAD(mhdr); while (RTA_OK(attr, attr_len)) { if (attr->rta_type == IFLA_IFNAME) { - char ifname[IFNAMSIZ + 1]; int len = RTA_PAYLOAD(attr); - if (len > IFNAMSIZ) len = IFNAMSIZ; - memcpy(ifname, RTA_DATA(attr), len); - if (strcmp(G.iface, ifname) == 0) { + if (iface_len <= len + && strncmp(G.iface, RTA_DATA(attr), len) == 0 + ) { G.iface_exists = (mhdr->nlmsg_type == RTM_NEWLINK); } } @@ -563,26 +535,11 @@ static NOINLINE int check_existence_through_netlink(void) } } + ret: + free(replybuf); return G.iface_exists; } -static NOINLINE int netlink_open(void) -{ - int fd; - struct sockaddr_nl addr; - - fd = xsocket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); - - memset(&addr, 0, sizeof(addr)); - addr.nl_family = AF_NETLINK; - addr.nl_groups = RTMGRP_LINK; - addr.nl_pid = getpid(); - - xbind(fd, (struct sockaddr*)&addr, sizeof(addr)); - - return fd; -} - #if ENABLE_FEATURE_PIDFILE static NOINLINE pid_t read_pid(const char *filename) { @@ -607,6 +564,7 @@ int ifplugd_main(int argc UNUSED_PARAM, char **argv) const char *iface_status_str; struct pollfd netlink_pollfd[1]; unsigned opts; + const char *api_mode_found; #if ENABLE_FEATURE_PIDFILE char *pidfile_name; pid_t pid_from_pidfile; @@ -614,7 +572,6 @@ int ifplugd_main(int argc UNUSED_PARAM, char **argv) INIT_G(); - opt_complementary = "t+:u+:d+"; opts = getopt32(argv, OPTION_STR, &G.iface, &G.script_name, &G.poll_time, &G.delay_up, &G.delay_down, &G.api_mode, &G.extra_arg); @@ -623,12 +580,13 @@ int ifplugd_main(int argc UNUSED_PARAM, char **argv) applet_name = xasprintf("ifplugd(%s)", G.iface); #if ENABLE_FEATURE_PIDFILE - pidfile_name = xasprintf(_PATH_VARRUN"ifplugd.%s.pid", G.iface); + pidfile_name = xasprintf(CONFIG_PID_FILE_PATH "/ifplugd.%s.pid", G.iface); pid_from_pidfile = read_pid(pidfile_name); if (opts & FLAG_KILL) { if (pid_from_pidfile > 0) - kill(pid_from_pidfile, SIGQUIT); + /* Upstream tool use SIGINT for -k */ + kill(pid_from_pidfile, SIGINT); return EXIT_SUCCESS; } @@ -636,35 +594,26 @@ int ifplugd_main(int argc UNUSED_PARAM, char **argv) bb_error_msg_and_die("daemon already running"); #endif - switch (G.api_mode[0]) { - case API_AUTO: - G.detect_link_func = detect_link_auto; - break; - case API_ETHTOOL: - G.detect_link_func = detect_link_ethtool; - break; - case API_MII: - G.detect_link_func = detect_link_mii; - break; - case API_PRIVATE: - G.detect_link_func = detect_link_priv; - break; - case API_WLAN: - G.detect_link_func = detect_link_wlan; - break; - case API_IFF: - G.detect_link_func = detect_link_iff; - break; - default: + api_mode_found = strchr(api_modes, G.api_mode[0]); + if (!api_mode_found) bb_error_msg_and_die("unknown API mode '%s'", G.api_mode); - } + G.api_method_num = api_mode_found - api_modes; if (!(opts & FLAG_NO_DAEMON)) bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv); xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), ioctl_fd); if (opts & FLAG_MONITOR) { - xmove_fd(netlink_open(), netlink_fd); + struct sockaddr_nl addr; + int fd = xsocket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + addr.nl_groups = RTMGRP_LINK; + addr.nl_pid = getpid(); + + xbind(fd, (struct sockaddr*)&addr, sizeof(addr)); + xmove_fd(fd, netlink_fd); } write_pidfile(pidfile_name); @@ -726,7 +675,6 @@ int ifplugd_main(int argc UNUSED_PARAM, char **argv) delay_time = 0; while (1) { int iface_status_old; - int iface_exists_old; switch (bb_got_signal) { case SIGINT: @@ -752,12 +700,12 @@ int ifplugd_main(int argc UNUSED_PARAM, char **argv) goto exiting; } - iface_status_old = iface_status; - iface_exists_old = G.iface_exists; - if ((opts & FLAG_MONITOR) && (netlink_pollfd[0].revents & POLLIN) ) { + int iface_exists_old; + + iface_exists_old = G.iface_exists; G.iface_exists = check_existence_through_netlink(); if (G.iface_exists < 0) /* error */ goto exiting; @@ -770,6 +718,7 @@ int ifplugd_main(int argc UNUSED_PARAM, char **argv) } /* note: if !G.iface_exists, returns DOWN */ + iface_status_old = iface_status; iface_status = detect_link(); if (iface_status == IFSTATUS_ERR) { if (!(opts & FLAG_MONITOR)) @@ -783,7 +732,7 @@ int ifplugd_main(int argc UNUSED_PARAM, char **argv) if (delay_time) { /* link restored its old status before - * we run script. don't run the script: */ + * we ran script. don't run the script: */ delay_time = 0; } else { delay_time = monotonic_sec(); @@ -791,15 +740,19 @@ int ifplugd_main(int argc UNUSED_PARAM, char **argv) delay_time += G.delay_up; if (iface_status == IFSTATUS_DOWN) delay_time += G.delay_down; - if (delay_time == 0) - delay_time++; +#if 0 /* if you are back in 1970... */ + if (delay_time == 0) { + sleep(1); + delay_time = 1; + } +#endif } } if (delay_time && (int)(monotonic_sec() - delay_time) >= 0) { - delay_time = 0; if (run_script(iface_status_str) != 0) goto exiting; + delay_time = 0; } } /* while (1) */