start_stop_daemon: NOMMU fixes, round 2 by Alex Landau <landau_alex@yahoo.com>
[oweals/busybox.git] / networking / ifupdown.c
index 93d7bc5207436ee579e1aba04cf1f9d7a696cd6c..040bbe38965cbee39050ac3a3df736aae0475d5b 100644 (file)
  *  Changes to upstream version
  *  Remove checks for kernel version, assume kernel version 2.2.0 or better.
  *  Lines in the interfaces file cannot wrap.
- *  To adhere to the FHS, the default state file is /var/run/ifstate.
+ *  To adhere to the FHS, the default state file is /var/run/ifstate
+ *  (defined via CONFIG_IFUPDOWN_IFSTATE_PATH) and can be overridden by build
+ *  configuration.
  *
  * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
  */
 
-#include "busybox.h"
 #include <sys/utsname.h>
 #include <fnmatch.h>
 #include <getopt.h>
 
+#include "libbb.h"
+
 #define MAX_OPT_DEPTH 10
 #define EUNBALBRACK 10001
 #define EUNDEFVAR   10002
@@ -36,22 +39,19 @@ struct interface_defn_t;
 
 typedef int execfn(char *command);
 
-struct method_t
-{
-       char *name;
+struct method_t {
+       const char *name;
        int (*up)(struct interface_defn_t *ifd, execfn *e);
        int (*down)(struct interface_defn_t *ifd, execfn *e);
 };
 
-struct address_family_t
-{
-       char *name;
+struct address_family_t {
+       const char *name;
        int n_methods;
        const struct method_t *method;
 };
 
-struct mapping_defn_t
-{
+struct mapping_defn_t {
        struct mapping_defn_t *next;
 
        int max_matches;
@@ -65,14 +65,12 @@ struct mapping_defn_t
        char **mapping;
 };
 
-struct variable_t
-{
+struct variable_t {
        char *name;
        char *value;
 };
 
-struct interface_defn_t
-{
+struct interface_defn_t {
        const struct address_family_t *address_family;
        const struct method_t *method;
 
@@ -82,8 +80,7 @@ struct interface_defn_t
        struct variable_t *option;
 };
 
-struct interfaces_file_t
-{
+struct interfaces_file_t {
        llist_t *autointerfaces;
        llist_t *ifaces;
        struct mapping_defn_t *mappings;
@@ -103,37 +100,12 @@ enum {
 #define FORCE (option_mask32 & OPT_force)
 #define NO_MAPPINGS (option_mask32 & OPT_no_mappings)
 
-static char **__myenviron;
+static char **my_environ;
 
-static char *startup_PATH;
+static const char *startup_PATH;
 
 #if ENABLE_FEATURE_IFUPDOWN_IPV4 || ENABLE_FEATURE_IFUPDOWN_IPV6
 
-#if ENABLE_FEATURE_IFUPDOWN_IP
-
-static unsigned count_bits(unsigned a)
-{
-       unsigned result;
-       result = (a & 0x55) + ((a >> 1) & 0x55);
-       result = (result & 0x33) + ((result >> 2) & 0x33);
-       return (result & 0x0F) + ((result >> 4) & 0x0F);
-}
-
-static int count_netmask_bits(char *dotted_quad)
-{
-       unsigned result, a, b, c, d;
-       /* Found a netmask...  Check if it is dotted quad */
-       if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) != 4)
-               return -1;
-       // FIXME: will be confused by e.g. 255.0.255.0
-       result = count_bits(a);
-       result += count_bits(b);
-       result += count_bits(c);
-       result += count_bits(d);
-       return (int)result;
-}
-#endif
-
 static void addstr(char **bufp, const char *str, size_t str_length)
 {
        /* xasprintf trick will be smaller, but we are often
@@ -182,12 +154,45 @@ static char *get_var(const char *id, size_t idlen, struct interface_defn_t *ifd)
        return NULL;
 }
 
+#if ENABLE_FEATURE_IFUPDOWN_IP
+static int count_netmask_bits(const char *dotted_quad)
+{
+//     int result;
+//     unsigned a, b, c, d;
+//     /* Found a netmask...  Check if it is dotted quad */
+//     if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) != 4)
+//             return -1;
+//     if ((a|b|c|d) >> 8)
+//             return -1; /* one of numbers is >= 256 */
+//     d |= (a << 24) | (b << 16) | (c << 8); /* IP */
+//     d = ~d; /* 11110000 -> 00001111 */
+
+       /* Shorter version */
+       int result;
+       struct in_addr ip;
+       unsigned d;
+
+       if (inet_aton(dotted_quad, &ip) == 0)
+               return -1; /* malformed dotted IP */
+       d = ntohl(ip.s_addr); /* IP in host order */
+       d = ~d; /* 11110000 -> 00001111 */
+       if (d & (d+1)) /* check that it is in 00001111 form */
+               return -1; /* no it is not */
+       result = 32;
+       while (d) {
+               d >>= 1;
+               result--;
+       }
+       return result;
+}
+#endif
+
 static char *parse(const char *command, struct interface_defn_t *ifd)
 {
        size_t old_pos[MAX_OPT_DEPTH] = { 0 };
        int okay[MAX_OPT_DEPTH] = { 1 };
        int opt_depth = 1;
-       char *result = xstrdup("");
+       char *result = NULL;
 
        while (*command) {
                switch (*command) {
@@ -206,7 +211,7 @@ static char *parse(const char *command, struct interface_defn_t *ifd)
                        break;
                case '[':
                        if (command[1] == '[' && opt_depth < MAX_OPT_DEPTH) {
-                               old_pos[opt_depth] = strlen(result);
+                               old_pos[opt_depth] = result ? strlen(result) : 0;
                                okay[opt_depth] = 1;
                                opt_depth++;
                                command += 2;
@@ -251,11 +256,14 @@ static char *parse(const char *command, struct interface_defn_t *ifd)
                                        if (strncmp(command, "bnmask", 6) == 0) {
                                                unsigned res;
                                                varvalue = get_var("netmask", 7, ifd);
-                                               if (varvalue && (res = count_netmask_bits(varvalue)) > 0) {
-                                                       const char *argument = utoa(res);
-                                                       addstr(&result, argument, strlen(argument));
-                                                       command = nextpercent + 1;
-                                                       break;
+                                               if (varvalue) {
+                                                       res = count_netmask_bits(varvalue);
+                                                       if (res > 0) {
+                                                               const char *argument = utoa(res);
+                                                               addstr(&result, argument, strlen(argument));
+                                                               command = nextpercent + 1;
+                                                               break;
+                                                       }
                                                }
                                        }
 #endif
@@ -290,10 +298,12 @@ static int execute(const char *command, struct interface_defn_t *ifd, execfn *ex
        int ret;
 
        out = parse(command, ifd);
-       if (!out || !out[0]) {
+       if (!out) {
+               /* parse error? */
                return 0;
        }
-       ret = (*exec)(out);
+       /* out == "": parsed ok but not all needed variables known, skip */
+       ret = out[0] ? (*exec)(out) : 1;
 
        free(out);
        if (ret != 1) {
@@ -378,7 +388,7 @@ static const struct method_t methods6[] = {
 
 static const struct address_family_t addr_inet6 = {
        "inet6",
-       sizeof(methods6) / sizeof(struct method_t),
+       ARRAY_SIZE(methods6),
        methods6
 };
 #endif /* FEATURE_IFUPDOWN_IPV6 */
@@ -425,7 +435,7 @@ static int static_up(struct interface_defn_t *ifd, execfn *exec)
        result += execute("ifconfig %iface% %address% netmask %netmask%"
                                "[[ broadcast %broadcast%]][[ pointopoint %pointopoint%]] ",
                                ifd, exec);
-       result += execute("[[route add default gw %gateway% %iface%]]", ifd, exec);
+       result += execute("[[route add default gw %gateway% %iface%]]", ifd, exec);
        return ((result == 3) ? 3 : 0);
 #endif
 }
@@ -443,7 +453,7 @@ static int static_down(struct interface_defn_t *ifd, execfn *exec)
        return ((result == 2) ? 2 : 0);
 }
 
-#if !ENABLE_APP_UDHCPC
+#if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
 struct dhcp_client_t
 {
        const char *name;
@@ -452,55 +462,69 @@ struct dhcp_client_t
 };
 
 static const struct dhcp_client_t ext_dhcp_clients[] = {
-       { "udhcpc",
-               "udhcpc -R -n -p /var/run/udhcpc.%iface%.pid -i %iface%[[ -H %hostname%]][[ -c %clientid%]][[ -s %script%]]",
-               "kill -TERM `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null",
-       },
-       { "pump",
-               "pump -i %iface%[[ -h %hostname%]][[ -l %leasehours%]]",
-               "pump -i %iface% -k",
+       { "dhcpcd",
+               "dhcpcd[[ -h %hostname%]][[ -i %vendor%]][[ -I %clientid%]][[ -l %leasetime%]] %iface%",
+               "dhcpcd -k %iface%",
        },
        { "dhclient",
                "dhclient -pf /var/run/dhclient.%iface%.pid %iface%",
                "kill -9 `cat /var/run/dhclient.%iface%.pid` 2>/dev/null",
        },
-       { "dhcpcd",
-               "dhcpcd[[ -h %hostname%]][[ -i %vendor%]][[ -I %clientid%]][[ -l %leasetime%]] %iface%",
-               "dhcpcd -k %iface%",
+       { "pump",
+               "pump -i %iface%[[ -h %hostname%]][[ -l %leasehours%]]",
+               "pump -i %iface% -k",
+       },
+       { "udhcpc",
+               "udhcpc -R -n -p /var/run/udhcpc.%iface%.pid -i %iface%[[ -H %hostname%]][[ -c %clientid%]][[ -s %script%]]",
+               "kill `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null",
        },
 };
-#endif
+#endif /* ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCPC */
 
 static int dhcp_up(struct interface_defn_t *ifd, execfn *exec)
 {
-#if ENABLE_APP_UDHCPC
-       return execute("udhcpc -R -n -p /var/run/udhcpc.%iface%.pid "
-                       "-i %iface%[[ -H %hostname%]][[ -c %clientid%]][[ -s %script%]]",
-                       ifd, exec);
-#else
-       int i, nclients = sizeof(ext_dhcp_clients) / sizeof(ext_dhcp_clients[0]);
-       for (i = 0; i < nclients; i++) {
+#if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
+#if ENABLE_FEATURE_IFUPDOWN_IP
+       /* ip doesn't up iface when it configures it (unlike ifconfig) */
+       if (!execute("ip link set %iface% up", ifd, exec))
+               return 0;
+#endif
+       int i;
+       for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) {
                if (exists_execable(ext_dhcp_clients[i].name))
                        return execute(ext_dhcp_clients[i].startcmd, ifd, exec);
        }
        bb_error_msg("no dhcp clients found");
        return 0;
+#elif ENABLE_APP_UDHCPC
+#if ENABLE_FEATURE_IFUPDOWN_IP
+       /* ip doesn't up iface when it configures it (unlike ifconfig) */
+       if (!execute("ip link set %iface% up", ifd, exec))
+               return 0;
+#endif
+       return execute("udhcpc -R -n -p /var/run/udhcpc.%iface%.pid "
+                       "-i %iface%[[ -H %hostname%]][[ -c %clientid%]][[ -s %script%]]",
+                       ifd, exec);
+#else
+       return 0; /* no dhcp support */
 #endif
 }
 
 static int dhcp_down(struct interface_defn_t *ifd, execfn *exec)
 {
-#if ENABLE_APP_UDHCPC
-       return execute("kill -TERM "
-                      "`cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec);
-#else
-       int i, nclients = sizeof(ext_dhcp_clients) / sizeof(ext_dhcp_clients[0]);
-       for (i = 0; i < nclients; i++) {
+#if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
+       int i;
+       for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) {
                if (exists_execable(ext_dhcp_clients[i].name))
                        return execute(ext_dhcp_clients[i].stopcmd, ifd, exec);
        }
        bb_error_msg("no dhcp clients found, using static interface shutdown");
        return static_down(ifd, exec);
+#elif ENABLE_APP_UDHCPC
+       return execute("kill "
+                      "`cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec);
+#else
+       return 0; /* no dhcp support */
 #endif
 }
 
@@ -512,8 +536,8 @@ static int manual_up_down(struct interface_defn_t *ifd, execfn *exec)
 static int bootp_up(struct interface_defn_t *ifd, execfn *exec)
 {
        return execute("bootpc[[ --bootfile %bootfile%]] --dev %iface%"
-                       "[[ --server %server%]][[ --hwaddr %hwaddr%]] "
-                       "--returniffail --serverbcast", ifd, exec);
+                       "[[ --server %server%]][[ --hwaddr %hwaddr%]]"
+                       " --returniffail --serverbcast", ifd, exec);
 }
 
 static int ppp_up(struct interface_defn_t *ifd, execfn *exec)
@@ -550,7 +574,7 @@ static const struct method_t methods[] = {
 
 static const struct address_family_t addr_inet = {
        "inet",
-       sizeof(methods) / sizeof(struct method_t),
+       ARRAY_SIZE(methods),
        methods
 };
 
@@ -833,14 +857,15 @@ static struct interfaces_file_t *read_interfaces(const char *filename)
                free(buf);
        }
        if (ferror(f) != 0) {
-               bb_perror_msg_and_die("%s", filename);
+               /* ferror does NOT set errno! */
+               bb_error_msg_and_die("%s: I/O error", filename);
        }
        fclose(f);
 
        return defn;
 }
 
-static char *setlocalenv(char *format, const char *name, const char *value)
+static char *setlocalenv(const char *format, const char *name, const char *value)
 {
        char *result;
        char *here;
@@ -871,21 +896,22 @@ static void set_environ(struct interface_defn_t *iface, const char *mode)
        const int n_env_entries = iface->n_options + 5;
        char **ppch;
 
-       if (__myenviron != NULL) {
-               for (ppch = __myenviron; *ppch; ppch++) {
+       if (my_environ != NULL) {
+               for (ppch = my_environ; *ppch; ppch++) {
                        free(*ppch);
                        *ppch = NULL;
                }
-               free(__myenviron);
+               free(my_environ);
        }
-       __myenviron = xzalloc(sizeof(char *) * (n_env_entries + 1 /* for final NULL */ ));
-       environend = __myenviron;
+       my_environ = xzalloc(sizeof(char *) * (n_env_entries + 1 /* for final NULL */ ));
+       environend = my_environ;
 
        for (i = 0; i < iface->n_options; i++) {
                if (strcmp(iface->option[i].name, "up") == 0
-                               || strcmp(iface->option[i].name, "down") == 0
-                               || strcmp(iface->option[i].name, "pre-up") == 0
-                               || strcmp(iface->option[i].name, "post-down") == 0) {
+                || strcmp(iface->option[i].name, "down") == 0
+                || strcmp(iface->option[i].name, "pre-up") == 0
+                || strcmp(iface->option[i].name, "post-down") == 0
+               ) {
                        continue;
                }
                *(environend++) = setlocalenv("IF_%s=%s", iface->option[i].name, iface->option[i].value);
@@ -908,11 +934,12 @@ static int doit(char *str)
                int status;
 
                fflush(NULL);
-               switch (child = fork()) {
+               child = fork();
+               switch (child) {
                case -1: /* failure */
                        return 0;
                case 0: /* child */
-                       execle(DEFAULT_SHELL, DEFAULT_SHELL, "-c", str, NULL, __myenviron);
+                       execle(DEFAULT_SHELL, DEFAULT_SHELL, "-c", str, NULL, my_environ);
                        exit(127);
                }
                waitpid(child, &status, 0);
@@ -936,10 +963,8 @@ static int execute_all(struct interface_defn_t *ifd, const char *opt)
        }
 
        buf = xasprintf("run-parts /etc/network/if-%s.d", opt);
-       if (doit(buf) != 1) {
-               return 0;
-       }
-       return 1;
+       /* heh, we don't bother free'ing it */
+       return doit(buf);
 }
 
 static int check(char *str)
@@ -1009,7 +1034,7 @@ static int popen2(FILE **in, FILE **out, char *command, ...)
                close(infd[1]);
                close(outfd[0]);
                close(outfd[1]);
-               execvp(command, argv);
+               BB_EXECVP(command, argv);
                exit(127);
        default:                        /* parent */
                *in = fdopen(infd[1], "w");
@@ -1047,7 +1072,7 @@ static char *run_mapping(char *physical, struct mapping_defn_t * map)
                /* If the mapping script exited successfully, try to
                 * grab a line of output and use that as the name of the
                 * logical interface. */
-               char *new_logical = (char *)xmalloc(MAX_INTERFACE_LENGTH);
+               char *new_logical = xmalloc(MAX_INTERFACE_LENGTH);
 
                if (fgets(new_logical, MAX_INTERFACE_LENGTH, out)) {
                        /* If we are able to read a line of output from the script,
@@ -1079,8 +1104,8 @@ static llist_t *find_iface_state(llist_t *state_list, const char *iface)
        llist_t *search = state_list;
 
        while (search) {
-               if ((strncmp(search->data, iface, iface_len) == 0) &&
-                               (search->data[iface_len] == '=')) {
+               if ((strncmp(search->data, iface, iface_len) == 0)
+                && (search->data[iface_len] == '=')) {
                        return search;
                }
                search = search->link;
@@ -1088,14 +1113,34 @@ static llist_t *find_iface_state(llist_t *state_list, const char *iface)
        return NULL;
 }
 
+/* read the previous state from the state file */
+static llist_t *read_iface_state(void)
+{
+       llist_t *state_list = NULL;
+       FILE *state_fp = fopen(CONFIG_IFUPDOWN_IFSTATE_PATH, "r");
+
+       if (state_fp) {
+               char *start, *end_ptr;
+               while ((start = xmalloc_fgets(state_fp)) != NULL) {
+                       /* We should only need to check for a single character */
+                       end_ptr = start + strcspn(start, " \t\n");
+                       *end_ptr = '\0';
+                       llist_add_to(&state_list, start);
+               }
+               fclose(state_fp);
+       }
+       return state_list;
+}
+
+
+int ifupdown_main(int argc, char **argv);
 int ifupdown_main(int argc, char **argv)
 {
        int (*cmds)(struct interface_defn_t *) = NULL;
        struct interfaces_file_t *defn;
-       llist_t *state_list = NULL;
        llist_t *target_list = NULL;
        const char *interfaces = "/etc/network/interfaces";
-       int any_failures = 0;
+       bool any_failures = 0;
 
        cmds = iface_down;
        if (applet_name[2] == 'u') {
@@ -1123,22 +1168,11 @@ int ifupdown_main(int argc, char **argv)
 
        /* Create a list of interfaces to work on */
        if (DO_ALL) {
-               if (cmds == iface_up) {
-                       target_list = defn->autointerfaces;
-               } else {
-                       /* iface_down */
-                       const llist_t *list = state_list;
-                       while (list) {
-                               llist_add_to_end(&target_list, xstrdup(list->data));
-                               list = list->link;
-                       }
-                       target_list = defn->autointerfaces;
-               }
+               target_list = defn->autointerfaces;
        } else {
                llist_add_to_end(&target_list, argv[optind]);
        }
 
-
        /* Update the interfaces */
        while (target_list) {
                llist_t *iface_list;
@@ -1146,8 +1180,8 @@ int ifupdown_main(int argc, char **argv)
                char *iface;
                char *liface;
                char *pch;
-               int okay = 0;
-               int cmds_ret;
+               bool okay = 0;
+               unsigned cmds_ret;
 
                iface = xstrdup(target_list->data);
                target_list = target_list->link;
@@ -1161,6 +1195,7 @@ int ifupdown_main(int argc, char **argv)
                }
 
                if (!FORCE) {
+                       llist_t *state_list = read_iface_state();
                        const llist_t *iface_state = find_iface_state(state_list, iface);
 
                        if (cmds == iface_up) {
@@ -1171,11 +1206,12 @@ int ifupdown_main(int argc, char **argv)
                                }
                        } else {
                                /* ifdown */
-                               if (iface_state) {
+                               if (!iface_state) {
                                        bb_error_msg("interface %s not configured", iface);
                                        continue;
                                }
                        }
+                       llist_free(state_list, free);
                }
 
 #if ENABLE_FEATURE_IFUPDOWN_MAPPING
@@ -1206,7 +1242,7 @@ int ifupdown_main(int argc, char **argv)
                                okay = 1;
                                currif->iface = iface;
 
-                               debug_noise("\nConfiguring interface %s (%s)\n", liface, currif->address_family->name);
+                               debug_noise("\nConfiguring interface %s (%s)\n", liface, currif->address_family->name);
 
                                /* Call the cmds function pointer, does either iface_up() or iface_down() */
                                cmds_ret = cmds(currif);
@@ -1229,11 +1265,15 @@ int ifupdown_main(int argc, char **argv)
                if (!okay && !FORCE) {
                        bb_error_msg("ignoring unknown interface %s", liface);
                        any_failures = 1;
-               } else {
+               } else if (!NO_ACT) {
+                       /* update the state file */
+                       FILE *state_fp;
+                       llist_t *state;
+                       llist_t *state_list = read_iface_state();
                        llist_t *iface_state = find_iface_state(state_list, iface);
 
                        if (cmds == iface_up) {
-                               char *newiface = xasprintf("%s=%s", iface, liface);
+                               char * const newiface = xasprintf("%s=%s", iface, liface);
                                if (iface_state == NULL) {
                                        llist_add_to_end(&state_list, newiface);
                                } else {
@@ -1241,25 +1281,23 @@ int ifupdown_main(int argc, char **argv)
                                        iface_state->data = newiface;
                                }
                        } else {
-                               /* Remove an interface from the linked list */
+                               /* Remove an interface from state_list */
+                               llist_unlink(&state_list, iface_state);
                                free(llist_pop(&iface_state));
                        }
-               }
-       }
-
-       /* Actually write the new state */
-       if (!NO_ACT) {
-               FILE *state_fp;
 
-               state_fp = xfopen("/var/run/ifstate", "w");
-               while (state_list) {
-                       if (state_list->data) {
-                               fputs(state_list->data, state_fp);
-                               fputc('\n', state_fp);
+                       /* Actually write the new state */
+                       state_fp = xfopen(CONFIG_IFUPDOWN_IFSTATE_PATH, "w");
+                       state = state_list;
+                       while (state) {
+                               if (state->data) {
+                                       fprintf(state_fp, "%s\n", state->data);
+                               }
+                               state = state->link;
                        }
-                       state_list = state_list->link;
+                       fclose(state_fp);
+                       llist_free(state_list, free);
                }
-               fclose(state_fp);
        }
 
        return any_failures;