diff -Nru ifupdown-0.7.54ubuntu1/config.c ifupdown-0.7.54ubuntu2/config.c --- ifupdown-0.7.54ubuntu1/config.c 2015-06-06 23:50:23.000000000 +0200 +++ ifupdown-0.7.54ubuntu2/config.c 2015-11-10 11:29:14.000000000 +0100 @@ -59,7 +59,7 @@ pos -= first; } while ((*result)[0] == '#'); - while ((*result)[pos - 1] == '\\') { + while (pos && (*result)[pos - 1] == '\\') { (*result)[--pos] = '\0'; do { @@ -91,7 +91,7 @@ assert((*result)[pos] == '\0'); } - while (isspace((*result)[pos - 1])) /* remove trailing whitespace */ + while (pos && isspace((*result)[pos - 1])) /* remove trailing whitespace */ pos--; (*result)[pos] = '\0'; diff -Nru ifupdown-0.7.54ubuntu1/debian/changelog ifupdown-0.7.54ubuntu2/debian/changelog --- ifupdown-0.7.54ubuntu1/debian/changelog 2015-07-10 06:51:40.000000000 +0200 +++ ifupdown-0.7.54ubuntu2/debian/changelog 2015-11-10 11:30:21.000000000 +0100 @@ -1,3 +1,9 @@ +ifupdown (0.7.54ubuntu2) xenial; urgency=medium + + * Per-interface hierarchical locking. (LP: #1337873) + + -- Dariusz Gadomski Thu, 10 Nov 2015 11:30:14 +0200 + ifupdown (0.7.54ubuntu1) wily; urgency=medium * Merge with Debian unstable. Remaining Ubuntu changes: diff -Nru ifupdown-0.7.54ubuntu1/execute.c ifupdown-0.7.54ubuntu2/execute.c --- ifupdown-0.7.54ubuntu1/execute.c 2015-06-06 22:35:12.000000000 +0200 +++ ifupdown-0.7.54ubuntu2/execute.c 2015-11-10 11:29:14.000000000 +0100 @@ -10,12 +10,27 @@ #include "header.h" -static char **environ = NULL; +extern char **environ; +static char **localenv = NULL; static int check(const char *str) { return str != NULL; } +static char *setlocalenv_nomangle(char *format, char *name, char *value) { + char *result; + + result = malloc(strlen(format) + strlen(name) + strlen(value) + 1); /* -4 for the two %s's */ + if (!result) { + perror("malloc"); + exit(1); + } + + sprintf(result, format, name, value); + + return result; +} + static char *setlocalenv(char *format, char *name, char *value) { char *result; @@ -48,17 +63,22 @@ } static void set_environ(interface_defn *iface, char *mode, char *phase) { - if (environ != NULL) { - for (char **ppch = environ; *ppch; ppch++) + if (localenv != NULL) { + for (char **ppch = localenv; *ppch; ppch++) free(*ppch); - free(environ); + free(localenv); } - const int n_env_entries = iface->n_options + 8; - environ = malloc(sizeof *environ * (n_env_entries + 1 /* for final NULL */ )); + int n_recursion = 0; + for(char **envp = environ; *envp; envp++) + if(strncmp(*envp, "IFUPDOWN_", 9) == 0) + n_recursion++; + + const int n_env_entries = iface->n_options + 10 + n_recursion; + localenv = malloc(sizeof *localenv * (n_env_entries + 1 /* for final NULL */ )); - char **ppch = environ; + char **ppch = localenv; for (int i = 0; i < iface->n_options; i++) { if (strcmp(iface->option[i].name, "pre-up") == 0 || strcmp(iface->option[i].name, "up") == 0 || strcmp(iface->option[i].name, "down") == 0 || strcmp(iface->option[i].name, "post-down") == 0) @@ -67,6 +87,20 @@ *ppch++ = setlocalenv("IF_%s=%s", iface->option[i].name, iface->option[i].value ? iface->option[i].value : ""); } + for(char **envp = environ; *envp; envp++) + if(strncmp(*envp, "IFUPDOWN_", 9) == 0) + *ppch++ = strdup(*envp); + + char piface[80]; + strncpy(piface, iface->real_iface, sizeof piface); + piface[sizeof piface - 1] = '\0'; + char *pch = strchr(piface, '.'); + if (pch) { + *pch = '\0'; + *ppch++ = setlocalenv_nomangle("IFUPDOWN_%s=%s", piface, "parent-lock"); + } + + *ppch++ = setlocalenv_nomangle("IFUPDOWN_%s=%s", iface->real_iface, phase); *ppch++ = setlocalenv("%s=%s", "IFACE", iface->real_iface); *ppch++ = setlocalenv("%s=%s", "LOGICAL", iface->logical_iface); *ppch++ = setlocalenv("%s=%s", "ADDRFAM", iface->address_family->name); @@ -102,7 +136,7 @@ return 0; case 0: /* child */ - execle("/bin/sh", "/bin/sh", "-c", str, NULL, environ); + execle("/bin/sh", "/bin/sh", "-c", str, NULL, localenv); exit(127); default: /* parent */ diff -Nru ifupdown-0.7.54ubuntu1/main.c ifupdown-0.7.54ubuntu2/main.c --- ifupdown-0.7.54ubuntu1/main.c 2015-06-07 00:20:44.000000000 +0200 +++ ifupdown-0.7.54ubuntu2/main.c 2015-11-10 11:29:14.000000000 +0100 @@ -150,6 +150,59 @@ return buf; } +static FILE *lock_interface(const char *argv0, const char *iface, char **state) { + char filename[sizeof statefile + strlen(iface) + 2]; + snprintf(filename, sizeof filename, "%s.%s", statefile, iface); + + FILE *lock_fp = fopen(filename, no_act ? "r" : "a+"); + + if (lock_fp == NULL) { + if (!no_act) { + fprintf(stderr, "%s: failed to open lockfile %s: %s\n", argv0, filename, strerror(errno)); + exit(1); + } else { + return NULL; + } + } + + int flags = fcntl(fileno(lock_fp), F_GETFD); + + if (flags < 0 || fcntl(fileno(lock_fp), F_SETFD, flags | FD_CLOEXEC) < 0) { + fprintf(stderr, "%s: failed to set FD_CLOEXEC on lockfile %s: %s\n", argv0, filename, strerror(errno)); + exit(1); + } + + struct flock lock = {.l_type = F_WRLCK, .l_whence = SEEK_SET}; + + if (fcntl(fileno(lock_fp), F_SETLK, &lock) < 0) { + if (errno == EACCES || errno == EAGAIN) { + fprintf(stderr, "%s: waiting for lock on %s\n", argv0, filename); + if (fcntl(fileno(lock_fp), F_SETLKW, &lock) < 0) { + if (!no_act) { + fprintf(stderr, "%s: failed to lock lockfile %s: %s\n", argv0, filename, strerror(errno)); + exit(1); + } + } + } else if (!no_act) { + fprintf(stderr, "%s: failed to lock lockfile %s: %s\n", argv0, filename, strerror(errno)); + exit(1); + } + } + + if (state) { + char buf[80]; + char *p = fgets(buf, sizeof buf, lock_fp); + if(p) { + p = strip(buf); + *state = *p ? strdup(p) : NULL; + } else { + *state = NULL; + } + } + + return lock_fp; +} + static const char *read_state(const char *argv0, const char *iface) { char *ret = NULL; @@ -247,8 +300,15 @@ fclose(lock_fp); } -static void update_state(const char *argv0, const char *iface, const char *state) { - FILE *lock_fp = lock_state(argv0); +static void update_state(const char *argv0, const char *iface, const char *state, FILE *lock_fp) { + if (lock_fp) { + rewind(lock_fp); + ftruncate(fileno(lock_fp), 0); + fprintf(lock_fp, "%s\n", state ? state : ""); + fflush(lock_fp); + } + + lock_fp = lock_state(argv0); FILE *state_fp = fopen(statefile, no_act ? "r" : "a+"); if (state_fp == NULL) { @@ -629,9 +689,17 @@ } } + FILE *lock = NULL; + FILE *plock = NULL; + for (int i = 0; i < n_target_ifaces; i++) { + if(lock) { + fclose(lock); + lock = NULL; + } + char iface[80], liface[80]; - const char *current_state; + char *current_state; strncpy(iface, target_iface[i], sizeof(iface)); iface[sizeof(iface) - 1] = '\0'; @@ -647,7 +715,33 @@ liface[sizeof(liface) - 1] = '\0'; } - current_state = read_state(argv[0], iface); + // Bail out if we are being called recursively on the same interface. + char envname[160]; + snprintf(envname, sizeof envname, "IFUPDOWN_%s", iface); + char *envval = getenv(envname); + if(envval) { + fprintf(stderr, "%s: recursion detected for interface %s in %s phase\n", argv[0], iface, envval); + continue; + } + + /* Are we configuring a VLAN interface? If so, lock the parent interface as well. */ + + char piface[80]; + strncpy(piface, iface, sizeof piface); + if ((pch = strchr(piface, '.'))) { + *pch = '\0'; + snprintf(envname, sizeof envname, "IFUPDOWN_%s", piface); + char *envval = getenv(envname); + if(envval) { + fprintf(stderr, "%s: recursion detected for parent interface %s in %s phase\n", argv[0], piface, envval); + return false; + } + + plock = lock_interface(argv[0], piface, NULL); + } + + lock = lock_interface(argv[0], iface, ¤t_state); + if (!force) { if (cmds == iface_up) { if (current_state != NULL) { @@ -729,15 +823,15 @@ if ((current_state == NULL) || (no_act)) { if (failed == 1) { printf("Failed to bring up %s.\n", liface); - update_state(argv[0], iface, NULL); + update_state(argv[0], iface, NULL, lock); } else { - update_state(argv[0], iface, liface); + update_state(argv[0], iface, liface, lock); } } else { - update_state(argv[0], iface, liface); + update_state(argv[0], iface, liface, lock); } } else if (cmds == iface_down) { - update_state(argv[0], iface, NULL); + update_state(argv[0], iface, NULL, lock); } else if (!(cmds == iface_list) && !(cmds == iface_query)) { assert(0); } @@ -928,27 +1022,37 @@ if (!okay && !force) { fprintf(stderr, "Ignoring unknown interface %s=%s.\n", iface, liface); - update_state(argv[0], iface, NULL); + update_state(argv[0], iface, NULL, lock); } else { if (cmds == iface_up) { if ((current_state == NULL) || (no_act)) { if (failed == true) { printf("Failed to bring up %s.\n", liface); - update_state(argv[0], iface, NULL); + update_state(argv[0], iface, NULL, lock); } else { - update_state(argv[0], iface, liface); + update_state(argv[0], iface, liface, lock); } } else { - update_state(argv[0], iface, liface); + update_state(argv[0], iface, liface, lock); } } else if (cmds == iface_down) { - update_state(argv[0], iface, NULL); + update_state(argv[0], iface, NULL, lock); } else if (!(cmds == iface_list) && !(cmds == iface_query)) { assert(0); } } } + if(lock) { + fclose(lock); + lock = NULL; + } + + if(plock) { + fclose(plock); + plock = NULL; + } + if (do_all) { int okay = true;