busybox/networking/ifupdown.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 *  ifupdown for busybox
   4 *  Copyright (c) 2002 Glenn McGrath
   5 *  Copyright (c) 2003-2004 Erik Andersen <andersen@codepoet.org>
   6 *
   7 *  Based on ifupdown v 0.6.4 by Anthony Towns
   8 *  Copyright (c) 1999 Anthony Towns <aj@azure.humbug.org.au>
   9 *
  10 *  Changes to upstream version
  11 *  Remove checks for kernel version, assume kernel version 2.2.0 or better.
  12 *  Lines in the interfaces file cannot wrap.
  13 *  To adhere to the FHS, the default state file is /var/run/ifstate
  14 *  (defined via CONFIG_IFUPDOWN_IFSTATE_PATH) and can be overridden by build
  15 *  configuration.
  16 *
  17 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  18 */
  19
  20//usage:#define ifup_trivial_usage
  21//usage:       "[-an"IF_FEATURE_IFUPDOWN_MAPPING("m")"vf] [-i FILE] IFACE..."
  22//usage:#define ifup_full_usage "\n\n"
  23//usage:       "        -a      De/configure all interfaces automatically"
  24//usage:     "\n        -i FILE Use FILE for interface definitions"
  25//usage:     "\n        -n      Print out what would happen, but don't do it"
  26//usage:        IF_FEATURE_IFUPDOWN_MAPPING(
  27//usage:     "\n                (note: doesn't disable mappings)"
  28//usage:     "\n        -m      Don't run any mappings"
  29//usage:        )
  30//usage:     "\n        -v      Print out what would happen before doing it"
  31//usage:     "\n        -f      Force de/configuration"
  32//usage:
  33//usage:#define ifdown_trivial_usage
  34//usage:       "[-an"IF_FEATURE_IFUPDOWN_MAPPING("m")"vf] [-i FILE] IFACE..."
  35//usage:#define ifdown_full_usage "\n\n"
  36//usage:       "        -a      De/configure all interfaces automatically"
  37//usage:     "\n        -i FILE Use FILE for interface definitions"
  38//usage:     "\n        -n      Print out what would happen, but don't do it"
  39//usage:        IF_FEATURE_IFUPDOWN_MAPPING(
  40//usage:     "\n                (note: doesn't disable mappings)"
  41//usage:     "\n        -m      Don't run any mappings"
  42//usage:        )
  43//usage:     "\n        -v      Print out what would happen before doing it"
  44//usage:     "\n        -f      Force de/configuration"
  45
  46#include "libbb.h"
  47/* After libbb.h, since it needs sys/types.h on some systems */
  48#include <sys/utsname.h>
  49#include <fnmatch.h>
  50
  51#define MAX_OPT_DEPTH 10
  52#define EUNBALBRACK 10001
  53#define EUNDEFVAR   10002
  54#define EUNBALPER   10000
  55
  56#if ENABLE_FEATURE_IFUPDOWN_MAPPING
  57#define MAX_INTERFACE_LENGTH 10
  58#endif
  59
  60#define UDHCPC_CMD_OPTIONS CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS
  61
  62#define debug_noise(args...) /*fprintf(stderr, args)*/
  63
  64/* Forward declaration */
  65struct interface_defn_t;
  66
  67typedef int execfn(char *command);
  68
  69struct method_t {
  70        const char *name;
  71        int (*up)(struct interface_defn_t *ifd, execfn *e) FAST_FUNC;
  72        int (*down)(struct interface_defn_t *ifd, execfn *e) FAST_FUNC;
  73};
  74
  75struct address_family_t {
  76        const char *name;
  77        int n_methods;
  78        const struct method_t *method;
  79};
  80
  81struct mapping_defn_t {
  82        struct mapping_defn_t *next;
  83
  84        int max_matches;
  85        int n_matches;
  86        char **match;
  87
  88        char *script;
  89
  90        int max_mappings;
  91        int n_mappings;
  92        char **mapping;
  93};
  94
  95struct variable_t {
  96        char *name;
  97        char *value;
  98};
  99
 100struct interface_defn_t {
 101        const struct address_family_t *address_family;
 102        const struct method_t *method;
 103
 104        char *iface;
 105        int max_options;
 106        int n_options;
 107        struct variable_t *option;
 108};
 109
 110struct interfaces_file_t {
 111        llist_t *autointerfaces;
 112        llist_t *ifaces;
 113        struct mapping_defn_t *mappings;
 114};
 115
 116
 117#define OPTION_STR "anvf" IF_FEATURE_IFUPDOWN_MAPPING("m") "i:"
 118enum {
 119        OPT_do_all      = 0x1,
 120        OPT_no_act      = 0x2,
 121        OPT_verbose     = 0x4,
 122        OPT_force       = 0x8,
 123        OPT_no_mappings = 0x10,
 124};
 125#define DO_ALL      (option_mask32 & OPT_do_all)
 126#define NO_ACT      (option_mask32 & OPT_no_act)
 127#define VERBOSE     (option_mask32 & OPT_verbose)
 128#define FORCE       (option_mask32 & OPT_force)
 129#define NO_MAPPINGS (option_mask32 & OPT_no_mappings)
 130
 131
 132struct globals {
 133        char **my_environ;
 134        const char *startup_PATH;
 135        char *shell;
 136} FIX_ALIASING;
 137#define G (*(struct globals*)&bb_common_bufsiz1)
 138#define INIT_G() do { } while (0)
 139
 140
 141#if ENABLE_FEATURE_IFUPDOWN_IPV4 || ENABLE_FEATURE_IFUPDOWN_IPV6
 142
 143static void addstr(char **bufp, const char *str, size_t str_length)
 144{
 145        /* xasprintf trick will be smaller, but we are often
 146         * called with str_length == 1 - don't want to have
 147         * THAT much of malloc/freeing! */
 148        char *buf = *bufp;
 149        int len = (buf ? strlen(buf) : 0);
 150        str_length++;
 151        buf = xrealloc(buf, len + str_length);
 152        /* copies at most str_length-1 chars! */
 153        safe_strncpy(buf + len, str, str_length);
 154        *bufp = buf;
 155}
 156
 157static int strncmpz(const char *l, const char *r, size_t llen)
 158{
 159        int i = strncmp(l, r, llen);
 160
 161        if (i == 0)
 162                return - (unsigned char)r[llen];
 163        return i;
 164}
 165
 166static char *get_var(const char *id, size_t idlen, struct interface_defn_t *ifd)
 167{
 168        int i;
 169
 170        if (strncmpz(id, "iface", idlen) == 0) {
 171                // ubuntu's ifup doesn't do this:
 172                //static char *label_buf;
 173                //char *result;
 174                //free(label_buf);
 175                //label_buf = xstrdup(ifd->iface);
 176                // Remove virtual iface suffix
 177                //result = strchrnul(label_buf, ':');
 178                //*result = '\0';
 179                //return label_buf;
 180
 181                return ifd->iface;
 182        }
 183        if (strncmpz(id, "label", idlen) == 0) {
 184                return ifd->iface;
 185        }
 186        for (i = 0; i < ifd->n_options; i++) {
 187                if (strncmpz(id, ifd->option[i].name, idlen) == 0) {
 188                        return ifd->option[i].value;
 189                }
 190        }
 191        return NULL;
 192}
 193
 194# if ENABLE_FEATURE_IFUPDOWN_IP
 195static int count_netmask_bits(const char *dotted_quad)
 196{
 197//      int result;
 198//      unsigned a, b, c, d;
 199//      /* Found a netmask...  Check if it is dotted quad */
 200//      if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) != 4)
 201//              return -1;
 202//      if ((a|b|c|d) >> 8)
 203//              return -1; /* one of numbers is >= 256 */
 204//      d |= (a << 24) | (b << 16) | (c << 8); /* IP */
 205//      d = ~d; /* 11110000 -> 00001111 */
 206
 207        /* Shorter version */
 208        int result;
 209        struct in_addr ip;
 210        unsigned d;
 211
 212        if (inet_aton(dotted_quad, &ip) == 0)
 213                return -1; /* malformed dotted IP */
 214        d = ntohl(ip.s_addr); /* IP in host order */
 215        d = ~d; /* 11110000 -> 00001111 */
 216        if (d & (d+1)) /* check that it is in 00001111 form */
 217                return -1; /* no it is not */
 218        result = 32;
 219        while (d) {
 220                d >>= 1;
 221                result--;
 222        }
 223        return result;
 224}
 225# endif
 226
 227static char *parse(const char *command, struct interface_defn_t *ifd)
 228{
 229        size_t old_pos[MAX_OPT_DEPTH] = { 0 };
 230        int okay[MAX_OPT_DEPTH] = { 1 };
 231        int opt_depth = 1;
 232        char *result = NULL;
 233
 234        while (*command) {
 235                switch (*command) {
 236                default:
 237                        addstr(&result, command, 1);
 238                        command++;
 239                        break;
 240                case '\\':
 241                        if (command[1]) {
 242                                addstr(&result, command + 1, 1);
 243                                command += 2;
 244                        } else {
 245                                addstr(&result, command, 1);
 246                                command++;
 247                        }
 248                        break;
 249                case '[':
 250                        if (command[1] == '[' && opt_depth < MAX_OPT_DEPTH) {
 251                                old_pos[opt_depth] = result ? strlen(result) : 0;
 252                                okay[opt_depth] = 1;
 253                                opt_depth++;
 254                                command += 2;
 255                        } else {
 256                                addstr(&result, "[", 1);
 257                                command++;
 258                        }
 259                        break;
 260                case ']':
 261                        if (command[1] == ']' && opt_depth > 1) {
 262                                opt_depth--;
 263                                if (!okay[opt_depth]) {
 264                                        result[old_pos[opt_depth]] = '\0';
 265                                }
 266                                command += 2;
 267                        } else {
 268                                addstr(&result, "]", 1);
 269                                command++;
 270                        }
 271                        break;
 272                case '%':
 273                        {
 274                                char *nextpercent;
 275                                char *varvalue;
 276
 277                                command++;
 278                                nextpercent = strchr(command, '%');
 279                                if (!nextpercent) {
 280                                        errno = EUNBALPER;
 281                                        free(result);
 282                                        return NULL;
 283                                }
 284
 285                                varvalue = get_var(command, nextpercent - command, ifd);
 286
 287                                if (varvalue) {
 288# if ENABLE_FEATURE_IFUPDOWN_IP
 289                                        /* "hwaddress <class> <address>":
 290                                         * unlike ifconfig, ip doesnt want <class>
 291                                         * (usually "ether" keyword). Skip it. */
 292                                        if (strncmp(command, "hwaddress", 9) == 0) {
 293                                                varvalue = skip_whitespace(skip_non_whitespace(varvalue));
 294                                        }
 295# endif
 296                                        addstr(&result, varvalue, strlen(varvalue));
 297                                } else {
 298# if ENABLE_FEATURE_IFUPDOWN_IP
 299                                        /* Sigh...  Add a special case for 'ip' to convert from
 300                                         * dotted quad to bit count style netmasks.  */
 301                                        if (strncmp(command, "bnmask", 6) == 0) {
 302                                                unsigned res;
 303                                                varvalue = get_var("netmask", 7, ifd);
 304                                                if (varvalue) {
 305                                                        res = count_netmask_bits(varvalue);
 306                                                        if (res > 0) {
 307                                                                const char *argument = utoa(res);
 308                                                                addstr(&result, argument, strlen(argument));
 309                                                                command = nextpercent + 1;
 310                                                                break;
 311                                                        }
 312                                                }
 313                                        }
 314# endif
 315                                        okay[opt_depth - 1] = 0;
 316                                }
 317
 318                                command = nextpercent + 1;
 319                        }
 320                        break;
 321                }
 322        }
 323
 324        if (opt_depth > 1) {
 325                errno = EUNBALBRACK;
 326                free(result);
 327                return NULL;
 328        }
 329
 330        if (!okay[0]) {
 331                errno = EUNDEFVAR;
 332                free(result);
 333                return NULL;
 334        }
 335
 336        return result;
 337}
 338
 339/* execute() returns 1 for success and 0 for failure */
 340static int execute(const char *command, struct interface_defn_t *ifd, execfn *exec)
 341{
 342        char *out;
 343        int ret;
 344
 345        out = parse(command, ifd);
 346        if (!out) {
 347                /* parse error? */
 348                return 0;
 349        }
 350        /* out == "": parsed ok but not all needed variables known, skip */
 351        ret = out[0] ? (*exec)(out) : 1;
 352
 353        free(out);
 354        if (ret != 1) {
 355                return 0;
 356        }
 357        return 1;
 358}
 359
 360#endif /* FEATURE_IFUPDOWN_IPV4 || FEATURE_IFUPDOWN_IPV6 */
 361
 362
 363#if ENABLE_FEATURE_IFUPDOWN_IPV6
 364
 365static int FAST_FUNC loopback_up6(struct interface_defn_t *ifd, execfn *exec)
 366{
 367# if ENABLE_FEATURE_IFUPDOWN_IP
 368        int result;
 369        result = execute("ip addr add ::1 dev %iface%", ifd, exec);
 370        result += execute("ip link set %iface% up", ifd, exec);
 371        return ((result == 2) ? 2 : 0);
 372# else
 373        return execute("ifconfig %iface% add ::1", ifd, exec);
 374# endif
 375}
 376
 377static int FAST_FUNC loopback_down6(struct interface_defn_t *ifd, execfn *exec)
 378{
 379# if ENABLE_FEATURE_IFUPDOWN_IP
 380        return execute("ip link set %iface% down", ifd, exec);
 381# else
 382        return execute("ifconfig %iface% del ::1", ifd, exec);
 383# endif
 384}
 385
 386static int FAST_FUNC manual_up_down6(struct interface_defn_t *ifd UNUSED_PARAM, execfn *exec UNUSED_PARAM)
 387{
 388        return 1;
 389}
 390
 391static int FAST_FUNC static_up6(struct interface_defn_t *ifd, execfn *exec)
 392{
 393        int result;
 394# if ENABLE_FEATURE_IFUPDOWN_IP
 395        result = execute("ip addr add %address%/%netmask% dev %iface%[[ label %label%]]", ifd, exec);
 396        result += execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd, exec);
 397        /* Was: "[[ ip ....%gateway% ]]". Removed extra spaces w/o checking */
 398        result += execute("[[ip route add ::/0 via %gateway%]]", ifd, exec);
 399# else
 400        result = execute("ifconfig %iface%[[ media %media%]][[ hw %hwaddress%]][[ mtu %mtu%]] up", ifd, exec);
 401        result += execute("ifconfig %iface% add %address%/%netmask%", ifd, exec);
 402        result += execute("[[route -A inet6 add ::/0 gw %gateway%]]", ifd, exec);
 403# endif
 404        return ((result == 3) ? 3 : 0);
 405}
 406
 407static int FAST_FUNC static_down6(struct interface_defn_t *ifd, execfn *exec)
 408{
 409# if ENABLE_FEATURE_IFUPDOWN_IP
 410        return execute("ip link set %iface% down", ifd, exec);
 411# else
 412        return execute("ifconfig %iface% down", ifd, exec);
 413# endif
 414}
 415
 416# if ENABLE_FEATURE_IFUPDOWN_IP
 417static int FAST_FUNC v4tunnel_up(struct interface_defn_t *ifd, execfn *exec)
 418{
 419        int result;
 420        result = execute("ip tunnel add %iface% mode sit remote "
 421                        "%endpoint%[[ local %local%]][[ ttl %ttl%]]", ifd, exec);
 422        result += execute("ip link set %iface% up", ifd, exec);
 423        result += execute("ip addr add %address%/%netmask% dev %iface%", ifd, exec);
 424        result += execute("[[ip route add ::/0 via %gateway%]]", ifd, exec);
 425        return ((result == 4) ? 4 : 0);
 426}
 427
 428static int FAST_FUNC v4tunnel_down(struct interface_defn_t * ifd, execfn * exec)
 429{
 430        return execute("ip tunnel del %iface%", ifd, exec);
 431}
 432# endif
 433
 434static const struct method_t methods6[] = {
 435# if ENABLE_FEATURE_IFUPDOWN_IP
 436        { "v4tunnel" , v4tunnel_up     , v4tunnel_down   , },
 437# endif
 438        { "static"   , static_up6      , static_down6    , },
 439        { "manual"   , manual_up_down6 , manual_up_down6 , },
 440        { "loopback" , loopback_up6    , loopback_down6  , },
 441};
 442
 443static const struct address_family_t addr_inet6 = {
 444        "inet6",
 445        ARRAY_SIZE(methods6),
 446        methods6
 447};
 448
 449#endif /* FEATURE_IFUPDOWN_IPV6 */
 450
 451
 452#if ENABLE_FEATURE_IFUPDOWN_IPV4
 453
 454static int FAST_FUNC loopback_up(struct interface_defn_t *ifd, execfn *exec)
 455{
 456# if ENABLE_FEATURE_IFUPDOWN_IP
 457        int result;
 458        result = execute("ip addr add 127.0.0.1/8 dev %iface%", ifd, exec);
 459        result += execute("ip link set %iface% up", ifd, exec);
 460        return ((result == 2) ? 2 : 0);
 461# else
 462        return execute("ifconfig %iface% 127.0.0.1 up", ifd, exec);
 463# endif
 464}
 465
 466static int FAST_FUNC loopback_down(struct interface_defn_t *ifd, execfn *exec)
 467{
 468# if ENABLE_FEATURE_IFUPDOWN_IP
 469        int result;
 470        result = execute("ip addr flush dev %iface%", ifd, exec);
 471        result += execute("ip link set %iface% down", ifd, exec);
 472        return ((result == 2) ? 2 : 0);
 473# else
 474        return execute("ifconfig %iface% 127.0.0.1 down", ifd, exec);
 475# endif
 476}
 477
 478static int FAST_FUNC static_up(struct interface_defn_t *ifd, execfn *exec)
 479{
 480        int result;
 481# if ENABLE_FEATURE_IFUPDOWN_IP
 482        result = execute("ip addr add %address%/%bnmask%[[ broadcast %broadcast%]] "
 483                        "dev %iface%[[ peer %pointopoint%]][[ label %label%]]", ifd, exec);
 484        result += execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd, exec);
 485        result += execute("[[ip route add default via %gateway% dev %iface%]]", ifd, exec);
 486        return ((result == 3) ? 3 : 0);
 487# else
 488        /* ifconfig said to set iface up before it processes hw %hwaddress%,
 489         * which then of course fails. Thus we run two separate ifconfig */
 490        result = execute("ifconfig %iface%[[ hw %hwaddress%]][[ media %media%]][[ mtu %mtu%]] up",
 491                                ifd, exec);
 492        result += execute("ifconfig %iface% %address% netmask %netmask%"
 493                                "[[ broadcast %broadcast%]][[ pointopoint %pointopoint%]] ",
 494                                ifd, exec);
 495        result += execute("[[route add default gw %gateway% %iface%]]", ifd, exec);
 496        return ((result == 3) ? 3 : 0);
 497# endif
 498}
 499
 500static int FAST_FUNC static_down(struct interface_defn_t *ifd, execfn *exec)
 501{
 502        int result;
 503# if ENABLE_FEATURE_IFUPDOWN_IP
 504        result = execute("ip addr flush dev %iface%", ifd, exec);
 505        result += execute("ip link set %iface% down", ifd, exec);
 506# else
 507        /* result = execute("[[route del default gw %gateway% %iface%]]", ifd, exec); */
 508        /* Bringing the interface down deletes the routes in itself.
 509           Otherwise this fails if we reference 'gateway' when using this from dhcp_down */
 510        result = 1;
 511        result += execute("ifconfig %iface% down", ifd, exec);
 512# endif
 513        return ((result == 2) ? 2 : 0);
 514}
 515
 516# if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
 517struct dhcp_client_t {
 518        const char *name;
 519        const char *startcmd;
 520        const char *stopcmd;
 521};
 522
 523static const struct dhcp_client_t ext_dhcp_clients[] = {
 524        { "dhcpcd",
 525                "dhcpcd[[ -h %hostname%]][[ -i %vendor%]][[ -I %client%]][[ -l %leasetime%]] %iface%",
 526                "dhcpcd -k %iface%",
 527        },
 528        { "dhclient",
 529                "dhclient -pf /var/run/dhclient.%iface%.pid %iface%",
 530                "kill -9 `cat /var/run/dhclient.%iface%.pid` 2>/dev/null",
 531        },
 532        { "pump",
 533                "pump -i %iface%[[ -h %hostname%]][[ -l %leasehours%]]",
 534                "pump -i %iface% -k",
 535        },
 536        { "udhcpc",
 537                "udhcpc " UDHCPC_CMD_OPTIONS " -p /var/run/udhcpc.%iface%.pid -i %iface%[[ -H %hostname%]][[ -c %client%]]"
 538                                "[[ -s %script%]][[ %udhcpc_opts%]]",
 539                "kill `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null",
 540        },
 541};
 542# endif /* FEATURE_IFUPDOWN_EXTERNAL_DHCPC */
 543
 544# if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
 545static int FAST_FUNC dhcp_up(struct interface_defn_t *ifd, execfn *exec)
 546{
 547        unsigned i;
 548#  if ENABLE_FEATURE_IFUPDOWN_IP
 549        /* ip doesn't up iface when it configures it (unlike ifconfig) */
 550        if (!execute("ip link set[[ addr %hwaddress%]] %iface% up", ifd, exec))
 551                return 0;
 552#  else
 553        /* needed if we have hwaddress on dhcp iface */
 554        if (!execute("ifconfig %iface%[[ hw %hwaddress%]] up", ifd, exec))
 555                return 0;
 556#  endif
 557        for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) {
 558                if (exists_execable(ext_dhcp_clients[i].name))
 559                        return execute(ext_dhcp_clients[i].startcmd, ifd, exec);
 560        }
 561        bb_error_msg("no dhcp clients found");
 562        return 0;
 563}
 564# elif ENABLE_UDHCPC
 565static int FAST_FUNC dhcp_up(struct interface_defn_t *ifd, execfn *exec)
 566{
 567#  if ENABLE_FEATURE_IFUPDOWN_IP
 568        /* ip doesn't up iface when it configures it (unlike ifconfig) */
 569        if (!execute("ip link set[[ addr %hwaddress%]] %iface% up", ifd, exec))
 570                return 0;
 571#  else
 572        /* needed if we have hwaddress on dhcp iface */
 573        if (!execute("ifconfig %iface%[[ hw %hwaddress%]] up", ifd, exec))
 574                return 0;
 575#  endif
 576        return execute("udhcpc " UDHCPC_CMD_OPTIONS " -p /var/run/udhcpc.%iface%.pid "
 577                        "-i %iface%[[ -H %hostname%]][[ -c %client%]][[ -s %script%]][[ %udhcpc_opts%]]",
 578                        ifd, exec);
 579}
 580# else
 581static int FAST_FUNC dhcp_up(struct interface_defn_t *ifd UNUSED_PARAM,
 582                execfn *exec UNUSED_PARAM)
 583{
 584        return 0; /* no dhcp support */
 585}
 586# endif
 587
 588# if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
 589static int FAST_FUNC dhcp_down(struct interface_defn_t *ifd, execfn *exec)
 590{
 591        int result = 0;
 592        unsigned i;
 593
 594        for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) {
 595                if (exists_execable(ext_dhcp_clients[i].name)) {
 596                        result = execute(ext_dhcp_clients[i].stopcmd, ifd, exec);
 597                        if (result)
 598                                break;
 599                }
 600        }
 601
 602        if (!result)
 603                bb_error_msg("warning: no dhcp clients found and stopped");
 604
 605        /* Sleep a bit, otherwise static_down tries to bring down interface too soon,
 606           and it may come back up because udhcpc is still shutting down */
 607        usleep(100000);
 608        result += static_down(ifd, exec);
 609        return ((result == 3) ? 3 : 0);
 610}
 611# elif ENABLE_UDHCPC
 612static int FAST_FUNC dhcp_down(struct interface_defn_t *ifd, execfn *exec)
 613{
 614        int result;
 615        result = execute(
 616                "test -f /var/run/udhcpc.%iface%.pid && "
 617                "kill `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null",
 618                ifd, exec);
 619        /* Also bring the hardware interface down since
 620           killing the dhcp client alone doesn't do it.
 621           This enables consecutive ifup->ifdown->ifup */
 622        /* Sleep a bit, otherwise static_down tries to bring down interface too soon,
 623           and it may come back up because udhcpc is still shutting down */
 624        usleep(100000);
 625        result += static_down(ifd, exec);
 626        return ((result == 3) ? 3 : 0);
 627}
 628# else
 629static int FAST_FUNC dhcp_down(struct interface_defn_t *ifd UNUSED_PARAM,
 630                execfn *exec UNUSED_PARAM)
 631{
 632        return 0; /* no dhcp support */
 633}
 634# endif
 635
 636static int FAST_FUNC manual_up_down(struct interface_defn_t *ifd UNUSED_PARAM, execfn *exec UNUSED_PARAM)
 637{
 638        return 1;
 639}
 640
 641static int FAST_FUNC bootp_up(struct interface_defn_t *ifd, execfn *exec)
 642{
 643        return execute("bootpc[[ --bootfile %bootfile%]] --dev %iface%"
 644                        "[[ --server %server%]][[ --hwaddr %hwaddr%]]"
 645                        " --returniffail --serverbcast", ifd, exec);
 646}
 647
 648static int FAST_FUNC ppp_up(struct interface_defn_t *ifd, execfn *exec)
 649{
 650        return execute("pon[[ %provider%]]", ifd, exec);
 651}
 652
 653static int FAST_FUNC ppp_down(struct interface_defn_t *ifd, execfn *exec)
 654{
 655        return execute("poff[[ %provider%]]", ifd, exec);
 656}
 657
 658static int FAST_FUNC wvdial_up(struct interface_defn_t *ifd, execfn *exec)
 659{
 660        return execute("start-stop-daemon --start -x wvdial "
 661                "-p /var/run/wvdial.%iface% -b -m --[[ %provider%]]", ifd, exec);
 662}
 663
 664static int FAST_FUNC wvdial_down(struct interface_defn_t *ifd, execfn *exec)
 665{
 666        return execute("start-stop-daemon --stop -x wvdial "
 667                        "-p /var/run/wvdial.%iface% -s 2", ifd, exec);
 668}
 669
 670static const struct method_t methods[] = {
 671        { "manual"  , manual_up_down, manual_up_down, },
 672        { "wvdial"  , wvdial_up     , wvdial_down   , },
 673        { "ppp"     , ppp_up        , ppp_down      , },
 674        { "static"  , static_up     , static_down   , },
 675        { "bootp"   , bootp_up      , static_down   , },
 676        { "dhcp"    , dhcp_up       , dhcp_down     , },
 677        { "loopback", loopback_up   , loopback_down , },
 678};
 679
 680static const struct address_family_t addr_inet = {
 681        "inet",
 682        ARRAY_SIZE(methods),
 683        methods
 684};
 685
 686#endif  /* FEATURE_IFUPDOWN_IPV4 */
 687
 688
 689/* Returns pointer to the next word, or NULL.
 690 * In 1st case, advances *buf to the word after this one.
 691 */
 692static char *next_word(char **buf)
 693{
 694        unsigned length;
 695        char *word;
 696
 697        /* Skip over leading whitespace */
 698        word = skip_whitespace(*buf);
 699
 700        /* Stop on EOL */
 701        if (*word == '\0')
 702                return NULL;
 703
 704        /* Find the length of this word (can't be 0) */
 705        length = strcspn(word, " \t\n");
 706
 707        /* Unless we are already at NUL, store NUL and advance */
 708        if (word[length] != '\0')
 709                word[length++] = '\0';
 710
 711        *buf = skip_whitespace(word + length);
 712
 713        return word;
 714}
 715
 716static const struct address_family_t *get_address_family(const struct address_family_t *const af[], char *name)
 717{
 718        int i;
 719
 720        if (!name)
 721                return NULL;
 722
 723        for (i = 0; af[i]; i++) {
 724                if (strcmp(af[i]->name, name) == 0) {
 725                        return af[i];
 726                }
 727        }
 728        return NULL;
 729}
 730
 731static const struct method_t *get_method(const struct address_family_t *af, char *name)
 732{
 733        int i;
 734
 735        if (!name)
 736                return NULL;
 737        /* TODO: use index_in_str_array() */
 738        for (i = 0; i < af->n_methods; i++) {
 739                if (strcmp(af->method[i].name, name) == 0) {
 740                        return &af->method[i];
 741                }
 742        }
 743        return NULL;
 744}
 745
 746static struct interfaces_file_t *read_interfaces(const char *filename)
 747{
 748        /* Let's try to be compatible.
 749         *
 750         * "man 5 interfaces" says:
 751         * Lines starting with "#" are ignored. Note that end-of-line
 752         * comments are NOT supported, comments must be on a line of their own.
 753         * A line may be extended across multiple lines by making
 754         * the last character a backslash.
 755         *
 756         * Seen elsewhere in example config file:
 757         * A first non-blank "#" character makes the rest of the line
 758         * be ignored. Blank lines are ignored. Lines may be indented freely.
 759         * A "\" character at the very end of the line indicates the next line
 760         * should be treated as a continuation of the current one.
 761         */
 762#if ENABLE_FEATURE_IFUPDOWN_MAPPING
 763        struct mapping_defn_t *currmap = NULL;
 764#endif
 765        struct interface_defn_t *currif = NULL;
 766        struct interfaces_file_t *defn;
 767        FILE *f;
 768        char *buf;
 769        char *first_word;
 770        char *rest_of_line;
 771        enum { NONE, IFACE, MAPPING } currently_processing = NONE;
 772
 773        defn = xzalloc(sizeof(*defn));
 774        f = xfopen_for_read(filename);
 775
 776        while ((buf = xmalloc_fgetline(f)) != NULL) {
 777#if ENABLE_DESKTOP
 778                /* Trailing "\" concatenates lines */
 779                char *p;
 780                while ((p = last_char_is(buf, '\\')) != NULL) {
 781                        *p = '\0';
 782                        rest_of_line = xmalloc_fgetline(f);
 783                        if (!rest_of_line)
 784                                break;
 785                        p = xasprintf("%s%s", buf, rest_of_line);
 786                        free(buf);
 787                        free(rest_of_line);
 788                        buf = p;
 789                }
 790#endif
 791                rest_of_line = buf;
 792                first_word = next_word(&rest_of_line);
 793                if (!first_word || *first_word == '#') {
 794                        free(buf);
 795                        continue; /* blank/comment line */
 796                }
 797
 798                if (strcmp(first_word, "mapping") == 0) {
 799#if ENABLE_FEATURE_IFUPDOWN_MAPPING
 800                        currmap = xzalloc(sizeof(*currmap));
 801
 802                        while ((first_word = next_word(&rest_of_line)) != NULL) {
 803                                currmap->match = xrealloc_vector(currmap->match, 4, currmap->n_matches);
 804                                currmap->match[currmap->n_matches++] = xstrdup(first_word);
 805                        }
 806                        /*currmap->max_mappings = 0; - done by xzalloc */
 807                        /*currmap->n_mappings = 0;*/
 808                        /*currmap->mapping = NULL;*/
 809                        /*currmap->script = NULL;*/
 810                        {
 811                                struct mapping_defn_t **where = &defn->mappings;
 812                                while (*where != NULL) {
 813                                        where = &(*where)->next;
 814                                }
 815                                *where = currmap;
 816                                /*currmap->next = NULL;*/
 817                        }
 818                        debug_noise("Added mapping\n");
 819#endif
 820                        currently_processing = MAPPING;
 821                } else if (strcmp(first_word, "iface") == 0) {
 822                        static const struct address_family_t *const addr_fams[] = {
 823#if ENABLE_FEATURE_IFUPDOWN_IPV4
 824                                &addr_inet,
 825#endif
 826#if ENABLE_FEATURE_IFUPDOWN_IPV6
 827                                &addr_inet6,
 828#endif
 829                                NULL
 830                        };
 831                        char *iface_name;
 832                        char *address_family_name;
 833                        char *method_name;
 834                        llist_t *iface_list;
 835
 836                        currif = xzalloc(sizeof(*currif));
 837                        iface_name = next_word(&rest_of_line);
 838                        address_family_name = next_word(&rest_of_line);
 839                        method_name = next_word(&rest_of_line);
 840
 841                        if (method_name == NULL)
 842                                bb_error_msg_and_die("too few parameters for line \"%s\"", buf);
 843
 844                        /* ship any trailing whitespace */
 845                        rest_of_line = skip_whitespace(rest_of_line);
 846
 847                        if (rest_of_line[0] != '\0' /* && rest_of_line[0] != '#' */)
 848                                bb_error_msg_and_die("too many parameters \"%s\"", buf);
 849
 850                        currif->iface = xstrdup(iface_name);
 851
 852                        currif->address_family = get_address_family(addr_fams, address_family_name);
 853                        if (!currif->address_family)
 854                                bb_error_msg_and_die("unknown address type \"%s\"", address_family_name);
 855
 856                        currif->method = get_method(currif->address_family, method_name);
 857                        if (!currif->method)
 858                                bb_error_msg_and_die("unknown method \"%s\"", method_name);
 859
 860                        for (iface_list = defn->ifaces; iface_list; iface_list = iface_list->link) {
 861                                struct interface_defn_t *tmp = (struct interface_defn_t *) iface_list->data;
 862                                if ((strcmp(tmp->iface, currif->iface) == 0)
 863                                 && (tmp->address_family == currif->address_family)
 864                                ) {
 865                                        bb_error_msg_and_die("duplicate interface \"%s\"", tmp->iface);
 866                                }
 867                        }
 868                        llist_add_to_end(&(defn->ifaces), (char*)currif);
 869
 870                        debug_noise("iface %s %s %s\n", currif->iface, address_family_name, method_name);
 871                        currently_processing = IFACE;
 872                } else if (strcmp(first_word, "auto") == 0) {
 873                        while ((first_word = next_word(&rest_of_line)) != NULL) {
 874
 875                                /* Check the interface isnt already listed */
 876                                if (llist_find_str(defn->autointerfaces, first_word)) {
 877                                        bb_perror_msg_and_die("interface declared auto twice \"%s\"", buf);
 878                                }
 879
 880                                /* Add the interface to the list */
 881                                llist_add_to_end(&(defn->autointerfaces), xstrdup(first_word));
 882                                debug_noise("\nauto %s\n", first_word);
 883                        }
 884                        currently_processing = NONE;
 885                } else {
 886                        switch (currently_processing) {
 887                        case IFACE:
 888                                if (rest_of_line[0] == '\0')
 889                                        bb_error_msg_and_die("option with empty value \"%s\"", buf);
 890
 891                                if (strcmp(first_word, "up") != 0
 892                                 && strcmp(first_word, "down") != 0
 893                                 && strcmp(first_word, "pre-up") != 0
 894                                 && strcmp(first_word, "post-down") != 0
 895                                ) {
 896                                        int i;
 897                                        for (i = 0; i < currif->n_options; i++) {
 898                                                if (strcmp(currif->option[i].name, first_word) == 0)
 899                                                        bb_error_msg_and_die("duplicate option \"%s\"", buf);
 900                                        }
 901                                }
 902                                if (currif->n_options >= currif->max_options) {
 903                                        currif->max_options += 10;
 904                                        currif->option = xrealloc(currif->option,
 905                                                sizeof(*currif->option) * currif->max_options);
 906                                }
 907                                debug_noise("\t%s=%s\n", first_word, rest_of_line);
 908                                currif->option[currif->n_options].name = xstrdup(first_word);
 909                                currif->option[currif->n_options].value = xstrdup(rest_of_line);
 910                                currif->n_options++;
 911                                break;
 912                        case MAPPING:
 913#if ENABLE_FEATURE_IFUPDOWN_MAPPING
 914                                if (strcmp(first_word, "script") == 0) {
 915                                        if (currmap->script != NULL)
 916                                                bb_error_msg_and_die("duplicate script in mapping \"%s\"", buf);
 917                                        currmap->script = xstrdup(next_word(&rest_of_line));
 918                                } else if (strcmp(first_word, "map") == 0) {
 919                                        if (currmap->n_mappings >= currmap->max_mappings) {
 920                                                currmap->max_mappings = currmap->max_mappings * 2 + 1;
 921                                                currmap->mapping = xrealloc(currmap->mapping,
 922                                                        sizeof(char *) * currmap->max_mappings);
 923                                        }
 924                                        currmap->mapping[currmap->n_mappings] = xstrdup(next_word(&rest_of_line));
 925                                        currmap->n_mappings++;
 926                                } else {
 927                                        bb_error_msg_and_die("misplaced option \"%s\"", buf);
 928                                }
 929#endif
 930                                break;
 931                        case NONE:
 932                        default:
 933                                bb_error_msg_and_die("misplaced option \"%s\"", buf);
 934                        }
 935                }
 936                free(buf);
 937        } /* while (fgets) */
 938
 939        if (ferror(f) != 0) {
 940                /* ferror does NOT set errno! */
 941                bb_error_msg_and_die("%s: I/O error", filename);
 942        }
 943        fclose(f);
 944
 945        return defn;
 946}
 947
 948static char *setlocalenv(const char *format, const char *name, const char *value)
 949{
 950        char *result;
 951        char *dst;
 952        char *src;
 953        char c;
 954
 955        result = xasprintf(format, name, value);
 956
 957        for (dst = src = result; (c = *src) != '=' && c; src++) {
 958                if (c == '-')
 959                        c = '_';
 960                if (c >= 'a' && c <= 'z')
 961                        c -= ('a' - 'A');
 962                if (isalnum(c) || c == '_')
 963                        *dst++ = c;
 964        }
 965        overlapping_strcpy(dst, src);
 966
 967        return result;
 968}
 969
 970static void set_environ(struct interface_defn_t *iface, const char *mode)
 971{
 972        int i;
 973        char **pp;
 974
 975        if (G.my_environ != NULL) {
 976                for (pp = G.my_environ; *pp; pp++) {
 977                        free(*pp);
 978                }
 979                free(G.my_environ);
 980        }
 981
 982        /* note: last element will stay NULL: */
 983        G.my_environ = xzalloc(sizeof(char *) * (iface->n_options + 6));
 984        pp = G.my_environ;
 985
 986        for (i = 0; i < iface->n_options; i++) {
 987                if (strcmp(iface->option[i].name, "up") == 0
 988                 || strcmp(iface->option[i].name, "down") == 0
 989                 || strcmp(iface->option[i].name, "pre-up") == 0
 990                 || strcmp(iface->option[i].name, "post-down") == 0
 991                ) {
 992                        continue;
 993                }
 994                *pp++ = setlocalenv("IF_%s=%s", iface->option[i].name, iface->option[i].value);
 995        }
 996
 997        *pp++ = setlocalenv("%s=%s", "IFACE", iface->iface);
 998        *pp++ = setlocalenv("%s=%s", "ADDRFAM", iface->address_family->name);
 999        *pp++ = setlocalenv("%s=%s", "METHOD", iface->method->name);
1000        *pp++ = setlocalenv("%s=%s", "MODE", mode);
1001        if (G.startup_PATH)
1002                *pp++ = setlocalenv("%s=%s", "PATH", G.startup_PATH);
1003}
1004
1005static int doit(char *str)
1006{
1007        if (option_mask32 & (OPT_no_act|OPT_verbose)) {
1008                puts(str);
1009        }
1010        if (!(option_mask32 & OPT_no_act)) {
1011                pid_t child;
1012                int status;
1013
1014                fflush_all();
1015                child = vfork();
1016                if (child < 0) /* failure */
1017                        return 0;
1018                if (child == 0) { /* child */
1019                        execle(G.shell, G.shell, "-c", str, (char *) NULL, G.my_environ);
1020                        _exit(127);
1021                }
1022                safe_waitpid(child, &status, 0);
1023                if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
1024                        return 0;
1025                }
1026        }
1027        return 1;
1028}
1029
1030static int execute_all(struct interface_defn_t *ifd, const char *opt)
1031{
1032        int i;
1033        char *buf;
1034        for (i = 0; i < ifd->n_options; i++) {
1035                if (strcmp(ifd->option[i].name, opt) == 0) {
1036                        if (!doit(ifd->option[i].value)) {
1037                                return 0;
1038                        }
1039                }
1040        }
1041
1042        buf = xasprintf("run-parts /etc/network/if-%s.d", opt);
1043        /* heh, we don't bother free'ing it */
1044        return doit(buf);
1045}
1046
1047static int check(char *str)
1048{
1049        return str != NULL;
1050}
1051
1052static int iface_up(struct interface_defn_t *iface)
1053{
1054        if (!iface->method->up(iface, check)) return -1;
1055        set_environ(iface, "start");
1056        if (!execute_all(iface, "pre-up")) return 0;
1057        if (!iface->method->up(iface, doit)) return 0;
1058        if (!execute_all(iface, "up")) return 0;
1059        return 1;
1060}
1061
1062static int iface_down(struct interface_defn_t *iface)
1063{
1064        if (!iface->method->down(iface,check)) return -1;
1065        set_environ(iface, "stop");
1066        if (!execute_all(iface, "down")) return 0;
1067        if (!iface->method->down(iface, doit)) return 0;
1068        if (!execute_all(iface, "post-down")) return 0;
1069        return 1;
1070}
1071
1072#if ENABLE_FEATURE_IFUPDOWN_MAPPING
1073static int popen2(FILE **in, FILE **out, char *command, char *param)
1074{
1075        char *argv[3] = { command, param, NULL };
1076        struct fd_pair infd, outfd;
1077        pid_t pid;
1078
1079        xpiped_pair(infd);
1080        xpiped_pair(outfd);
1081
1082        fflush_all();
1083        pid = xvfork();
1084
1085        if (pid == 0) {
1086                /* Child */
1087                /* NB: close _first_, then move fds! */
1088                close(infd.wr);
1089                close(outfd.rd);
1090                xmove_fd(infd.rd, 0);
1091                xmove_fd(outfd.wr, 1);
1092                BB_EXECVP_or_die(argv);
1093        }
1094        /* parent */
1095        close(infd.rd);
1096        close(outfd.wr);
1097        *in = xfdopen_for_write(infd.wr);
1098        *out = xfdopen_for_read(outfd.rd);
1099        return pid;
1100}
1101
1102static char *run_mapping(char *physical, struct mapping_defn_t *map)
1103{
1104        FILE *in, *out;
1105        int i, status;
1106        pid_t pid;
1107
1108        char *logical = xstrdup(physical);
1109
1110        /* Run the mapping script. Never fails. */
1111        pid = popen2(&in, &out, map->script, physical);
1112
1113        /* Write mappings to stdin of mapping script. */
1114        for (i = 0; i < map->n_mappings; i++) {
1115                fprintf(in, "%s\n", map->mapping[i]);
1116        }
1117        fclose(in);
1118        safe_waitpid(pid, &status, 0);
1119
1120        if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
1121                /* If the mapping script exited successfully, try to
1122                 * grab a line of output and use that as the name of the
1123                 * logical interface. */
1124                char *new_logical = xmalloc_fgetline(out);
1125
1126                if (new_logical) {
1127                        /* If we are able to read a line of output from the script,
1128                         * remove any trailing whitespace and use this value
1129                         * as the name of the logical interface. */
1130                        char *pch = new_logical + strlen(new_logical) - 1;
1131
1132                        while (pch >= new_logical && isspace(*pch))
1133                                *(pch--) = '\0';
1134
1135                        free(logical);
1136                        logical = new_logical;
1137                }
1138        }
1139
1140        fclose(out);
1141
1142        return logical;
1143}
1144#endif /* FEATURE_IFUPDOWN_MAPPING */
1145
1146static llist_t *find_iface_state(llist_t *state_list, const char *iface)
1147{
1148        unsigned iface_len = strlen(iface);
1149        llist_t *search = state_list;
1150
1151        while (search) {
1152                if ((strncmp(search->data, iface, iface_len) == 0)
1153                 && (search->data[iface_len] == '=')
1154                ) {
1155                        return search;
1156                }
1157                search = search->link;
1158        }
1159        return NULL;
1160}
1161
1162/* read the previous state from the state file */
1163static llist_t *read_iface_state(void)
1164{
1165        llist_t *state_list = NULL;
1166        FILE *state_fp = fopen_for_read(CONFIG_IFUPDOWN_IFSTATE_PATH);
1167
1168        if (state_fp) {
1169                char *start, *end_ptr;
1170                while ((start = xmalloc_fgets(state_fp)) != NULL) {
1171                        /* We should only need to check for a single character */
1172                        end_ptr = start + strcspn(start, " \t\n");
1173                        *end_ptr = '\0';
1174                        llist_add_to(&state_list, start);
1175                }
1176                fclose(state_fp);
1177        }
1178        return state_list;
1179}
1180
1181
1182int ifupdown_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1183int ifupdown_main(int argc UNUSED_PARAM, char **argv)
1184{
1185        int (*cmds)(struct interface_defn_t *);
1186        struct interfaces_file_t *defn;
1187        llist_t *target_list = NULL;
1188        const char *interfaces = "/etc/network/interfaces";
1189        bool any_failures = 0;
1190
1191        INIT_G();
1192
1193        G.startup_PATH = getenv("PATH");
1194        G.shell = xstrdup(get_shell_name());
1195
1196        cmds = iface_down;
1197        if (applet_name[2] == 'u') {
1198                /* ifup command */
1199                cmds = iface_up;
1200        }
1201
1202        getopt32(argv, OPTION_STR, &interfaces);
1203        argv += optind;
1204        if (argv[0]) {
1205                if (DO_ALL) bb_show_usage();
1206        } else {
1207                if (!DO_ALL) bb_show_usage();
1208        }
1209
1210        debug_noise("reading %s file:\n", interfaces);
1211        defn = read_interfaces(interfaces);
1212        debug_noise("\ndone reading %s\n\n", interfaces);
1213
1214        /* Create a list of interfaces to work on */
1215        if (DO_ALL) {
1216                target_list = defn->autointerfaces;
1217        } else {
1218                llist_add_to_end(&target_list, argv[0]);
1219        }
1220
1221        /* Update the interfaces */
1222        while (target_list) {
1223                llist_t *iface_list;
1224                struct interface_defn_t *currif;
1225                char *iface;
1226                char *liface;
1227                char *pch;
1228                bool okay = 0;
1229                int cmds_ret;
1230
1231                iface = xstrdup(target_list->data);
1232                target_list = target_list->link;
1233
1234                pch = strchr(iface, '=');
1235                if (pch) {
1236                        *pch = '\0';
1237                        liface = xstrdup(pch + 1);
1238                } else {
1239                        liface = xstrdup(iface);
1240                }
1241
1242                if (!FORCE) {
1243                        llist_t *state_list = read_iface_state();
1244                        const llist_t *iface_state = find_iface_state(state_list, iface);
1245
1246                        if (cmds == iface_up) {
1247                                /* ifup */
1248                                if (iface_state) {
1249                                        bb_error_msg("interface %s already configured", iface);
1250                                        goto next;
1251                                }
1252                        } else {
1253                                /* ifdown */
1254                                if (!iface_state) {
1255                                        bb_error_msg("interface %s not configured", iface);
1256                                        goto next;
1257                                }
1258                        }
1259                        llist_free(state_list, free);
1260                }
1261
1262#if ENABLE_FEATURE_IFUPDOWN_MAPPING
1263                if ((cmds == iface_up) && !NO_MAPPINGS) {
1264                        struct mapping_defn_t *currmap;
1265
1266                        for (currmap = defn->mappings; currmap; currmap = currmap->next) {
1267                                int i;
1268                                for (i = 0; i < currmap->n_matches; i++) {
1269                                        if (fnmatch(currmap->match[i], liface, 0) != 0)
1270                                                continue;
1271                                        if (VERBOSE) {
1272                                                printf("Running mapping script %s on %s\n", currmap->script, liface);
1273                                        }
1274                                        liface = run_mapping(iface, currmap);
1275                                        break;
1276                                }
1277                        }
1278                }
1279#endif
1280
1281                iface_list = defn->ifaces;
1282                while (iface_list) {
1283                        currif = (struct interface_defn_t *) iface_list->data;
1284                        if (strcmp(liface, currif->iface) == 0) {
1285                                char *oldiface = currif->iface;
1286
1287                                okay = 1;
1288                                currif->iface = iface;
1289
1290                                debug_noise("\nConfiguring interface %s (%s)\n", liface, currif->address_family->name);
1291
1292                                /* Call the cmds function pointer, does either iface_up() or iface_down() */
1293                                cmds_ret = cmds(currif);
1294                                if (cmds_ret == -1) {
1295                                        bb_error_msg("don't seem to have all the variables for %s/%s",
1296                                                        liface, currif->address_family->name);
1297                                        any_failures = 1;
1298                                } else if (cmds_ret == 0) {
1299                                        any_failures = 1;
1300                                }
1301
1302                                currif->iface = oldiface;
1303                        }
1304                        iface_list = iface_list->link;
1305                }
1306                if (VERBOSE) {
1307                        bb_putchar('\n');
1308                }
1309
1310                if (!okay && !FORCE) {
1311                        bb_error_msg("ignoring unknown interface %s", liface);
1312                        any_failures = 1;
1313                } else if (!NO_ACT) {
1314                        /* update the state file */
1315                        FILE *state_fp;
1316                        llist_t *state;
1317                        llist_t *state_list = read_iface_state();
1318                        llist_t *iface_state = find_iface_state(state_list, iface);
1319
1320                        if (cmds == iface_up) {
1321                                char * const newiface = xasprintf("%s=%s", iface, liface);
1322                                if (iface_state == NULL) {
1323                                        llist_add_to_end(&state_list, newiface);
1324                                } else {
1325                                        free(iface_state->data);
1326                                        iface_state->data = newiface;
1327                                }
1328                        } else {
1329                                /* Remove an interface from state_list */
1330                                llist_unlink(&state_list, iface_state);
1331                                free(llist_pop(&iface_state));
1332                        }
1333
1334                        /* Actually write the new state */
1335                        state_fp = xfopen_for_write(CONFIG_IFUPDOWN_IFSTATE_PATH);
1336                        state = state_list;
1337                        while (state) {
1338                                if (state->data) {
1339                                        fprintf(state_fp, "%s\n", state->data);
1340                                }
1341                                state = state->link;
1342                        }
1343                        fclose(state_fp);
1344                        llist_free(state_list, free);
1345                }
1346 next:
1347                free(iface);
1348                free(liface);
1349        }
1350
1351        return any_failures;
1352}
1353