wget: -O FILE is allowed to overwrite existing file (compat)
[oweals/busybox.git] / networking / ifupdown.c
index 8e750feafb9acab0b2e6b1560522e2fb7cafcf3f..02e13ed2769df764ea97d31611a46853847f24b2 100644 (file)
@@ -1,7 +1,7 @@
 /* vi: set sw=4 ts=4: */
 /*
  *  ifupdown for busybox
- *  Copyright (c) 2002 Glenn McGrath <bug1@iinet.net.au>
+ *  Copyright (c) 2002 Glenn McGrath
  *  Copyright (c) 2003-2004 Erik Andersen <andersen@codepoet.org>
  *
  *  Based on ifupdown v 0.6.4 by Anthony Towns
  *  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
@@ -103,31 +106,6 @@ 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
@@ -176,6 +154,39 @@ 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 };
@@ -245,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
@@ -374,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 */
@@ -439,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;
@@ -448,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
+       int i;
+#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
+       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
 }
 
@@ -508,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)
@@ -546,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
 };
 
@@ -829,7 +857,8 @@ 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);
 
@@ -879,9 +908,10 @@ static void set_environ(struct interface_defn_t *iface, const char *mode)
 
        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);
@@ -1074,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;
@@ -1083,16 +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";
-       FILE *state_fp;
-       int any_failures = 0;
+       bool any_failures = 0;
 
        cmds = iface_down;
        if (applet_name[2] == 'u') {
@@ -1100,7 +1148,7 @@ int ifupdown_main(int argc, char **argv)
                cmds = iface_up;
        }
 
-       getopt32(argc, argv, OPTION_STR, &interfaces);
+       getopt32(argv, OPTION_STR, &interfaces);
        if (argc - optind > 0) {
                if (DO_ALL) bb_show_usage();
        } else {
@@ -1118,32 +1166,9 @@ int ifupdown_main(int argc, char **argv)
        startup_PATH = getenv("PATH");
        if (!startup_PATH) startup_PATH = "";
 
-       /* Read the previous state from the state file */
-       state_fp = fopen_or_warn("/var/run/ifstate", "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);
-       }
-
        /* 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]);
        }
@@ -1155,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;
@@ -1170,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) {
@@ -1185,6 +1211,7 @@ int ifupdown_main(int argc, char **argv)
                                        continue;
                                }
                        }
+                       llist_free(state_list, free);
                }
 
 #if ENABLE_FEATURE_IFUPDOWN_MAPPING
@@ -1232,17 +1259,21 @@ int ifupdown_main(int argc, char **argv)
                        iface_list = iface_list->link;
                }
                if (VERBOSE) {
-                       puts("");
+                       bb_putchar('\n');
                }
 
                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 {
@@ -1254,19 +1285,19 @@ int ifupdown_main(int argc, char **argv)
                                llist_unlink(&state_list, iface_state);
                                free(llist_pop(&iface_state));
                        }
-               }
-       }
 
-       /* Actually write the new state */
-       if (!NO_ACT) {
-               state_fp = xfopen("/var/run/ifstate", "w");
-               while (state_list) {
-                       if (state_list->data) {
-                               fprintf(state_fp, "%s\n", state_list->data);
+                       /* 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;