X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fnspawn%2Fnspawn.c;h=7e96efd425425f4f2d196356a38201774ff5b06e;hb=fddbb89c46ea5d39e52e361b390ea34a06cbb67c;hp=bad93a5e2bf2742a8ba4488b685fbf0717519957;hpb=5aa4bb6b5b0b9dcea524d56cf8664b892a5a976a;p=elogind.git diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index bad93a5e2..7e96efd42 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -40,7 +40,6 @@ #include #include #include -#include #include #include #include @@ -84,12 +83,12 @@ #include "def.h" #include "rtnl-util.h" #include "udev-util.h" -#include "eventfd-util.h" #include "blkid-util.h" #include "gpt.h" #include "siphash24.h" #include "copy.h" #include "base-filesystem.h" +#include "barrier.h" #ifdef HAVE_SECCOMP #include "seccomp-util.h" @@ -167,8 +166,7 @@ static unsigned long arg_personality = 0xffffffffLU; static const char *arg_image = NULL; static Volatile arg_volatile = VOLATILE_NO; -static int help(void) { - +static void help(void) { printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n" "Spawn a minimal namespace container for debugging, testing and building.\n\n" " -h --help Show this help\n" @@ -217,8 +215,6 @@ static int help(void) { " the service unit nspawn is running in\n" " --volatile[=MODE] Run the system in volatile mode\n", program_invocation_short_name); - - return 0; } static int parse_argv(int argc, char *argv[]) { @@ -286,12 +282,13 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "+hD:u:bL:M:jS:Z:qi:", options, NULL)) >= 0) { + while ((c = getopt_long(argc, argv, "+hD:u:bL:M:jS:Z:qi:", options, NULL)) >= 0) switch (c) { case 'h': - return help(); + help(); + return 0; case ARG_VERSION: puts(PACKAGE_STRING); @@ -396,7 +393,7 @@ static int parse_argv(int argc, char *argv[]) { case ARG_CAPABILITY: case ARG_DROP_CAPABILITY: { - char *state, *word; + const char *state, *word; size_t length; FOREACH_WORD_SEPARATOR(word, length, optarg, ",", state) { @@ -594,7 +591,6 @@ static int parse_argv(int argc, char *argv[]) { default: assert_not_reached("Unhandled option"); } - } if (arg_share_system) arg_register = false; @@ -679,7 +675,18 @@ static int mount_all(const char *dest) { if (mount_table[k].what && t > 0) continue; - mkdir_p(where, 0755); + t = mkdir_p(where, 0755); + if (t < 0) { + if (mount_table[k].fatal) { + log_error("Failed to create directory %s: %s", where, strerror(-t)); + + if (r == 0) + r = t; + } else + log_warning("Failed to create directory %s: %s", where, strerror(-t)); + + continue; + } #ifdef HAVE_SELINUX if (arg_selinux_apifs_context && @@ -698,13 +705,15 @@ static int mount_all(const char *dest) { where, mount_table[k].type, mount_table[k].flags, - o) < 0 && - mount_table[k].fatal) { + o) < 0) { - log_error("mount(%s) failed: %m", where); + if (mount_table[k].fatal) { + log_error("mount(%s) failed: %m", where); - if (r == 0) - r = -errno; + if (r == 0) + r = -errno; + } else + log_warning("mount(%s) failed: %m", where); } } @@ -747,15 +756,35 @@ static int mount_binds(const char *dest, char **l, bool ro) { /* Create the mount point, but be conservative -- refuse to create block * and char devices. */ - if (S_ISDIR(source_st.st_mode)) - mkdir_label(where, 0755); - else if (S_ISFIFO(source_st.st_mode)) - mkfifo(where, 0644); - else if (S_ISSOCK(source_st.st_mode)) - mknod(where, 0644 | S_IFSOCK, 0); - else if (S_ISREG(source_st.st_mode)) - touch(where); - else { + if (S_ISDIR(source_st.st_mode)) { + r = mkdir_label(where, 0755); + if (r < 0 && errno != EEXIST) { + log_error("Failed to create mount point %s: %s", where, strerror(-r)); + + return r; + } + } else if (S_ISFIFO(source_st.st_mode)) { + r = mkfifo(where, 0644); + if (r < 0 && errno != EEXIST) { + log_error("Failed to create mount point %s: %m", where); + + return -errno; + } + } else if (S_ISSOCK(source_st.st_mode)) { + r = mknod(where, 0644 | S_IFSOCK, 0); + if (r < 0 && errno != EEXIST) { + log_error("Failed to create mount point %s: %m", where); + + return -errno; + } + } else if (S_ISREG(source_st.st_mode)) { + r = touch(where); + if (r < 0) { + log_error("Failed to create mount point %s: %s", where, strerror(-r)); + + return r; + } + } else { log_error("Refusing to create mountpoint for file: %s", *x); return -ENOTSUP; } @@ -782,12 +811,18 @@ static int mount_tmpfs(const char *dest) { STRV_FOREACH_PAIR(i, o, arg_tmpfs) { _cleanup_free_ char *where = NULL; + int r; where = strappend(dest, *i); if (!where) return log_oom(); - mkdir_label(where, 0755); + r = mkdir_label(where, 0755); + if (r < 0) { + log_error("creating mount point for tmpfs %s failed: %s", where, strerror(-r)); + + return r; + } if (mount("tmpfs", where, "tmpfs", MS_NODEV|MS_STRICTATIME, *o) < 0) { log_error("tmpfs mount to %s failed: %m", where); @@ -848,8 +883,19 @@ static int setup_timezone(const char *dest) { if (!what) return log_oom(); - mkdir_parents(where, 0755); - unlink(where); + r = mkdir_parents(where, 0755); + if (r < 0) { + log_error("Failed to create directory for timezone info %s in container: %s", where, strerror(-r)); + + return 0; + } + + r = unlink(where); + if (r < 0 && errno != ENOENT) { + log_error("Failed to remove existing timezone info %s in container: %m", where); + + return 0; + } if (symlink(what, where) < 0) { log_error("Failed to correct timezone of container: %m"); @@ -861,6 +907,7 @@ static int setup_timezone(const char *dest) { static int setup_resolv_conf(const char *dest) { _cleanup_free_ char *where = NULL; + int r; assert(dest); @@ -874,8 +921,19 @@ static int setup_resolv_conf(const char *dest) { /* We don't really care for the results of this really. If it * fails, it fails, but meh... */ - mkdir_parents(where, 0755); - copy_file("/etc/resolv.conf", where, O_TRUNC|O_NOFOLLOW, 0644); + r = mkdir_parents(where, 0755); + if (r < 0) { + log_warning("Failed to create parent directory for resolv.conf %s: %s", where, strerror(-r)); + + return 0; + } + + r = copy_file("/etc/resolv.conf", where, O_TRUNC|O_NOFOLLOW, 0644); + if (r < 0) { + log_warning("Failed to copy /etc/resolv.conf to %s: %s", where, strerror(-r)); + + return 0; + } return 0; } @@ -899,7 +957,11 @@ static int setup_volatile_state(const char *directory) { } p = strappenda(directory, "/var"); - mkdir(p, 0755); + r = mkdir(p, 0755); + if (r < 0 && errno != EEXIST) { + log_error("Failed to create %s: %m", directory); + return -errno; + } if (mount("tmpfs", p, "tmpfs", MS_STRICTATIME, "mode=755") < 0) { log_error("Failed to mount tmpfs to /var: %m"); @@ -939,7 +1001,13 @@ static int setup_volatile(const char *directory) { f = strappenda(directory, "/usr"); t = strappenda(template, "/usr"); - mkdir(t, 0755); + r = mkdir(t, 0755); + if (r < 0 && errno != EEXIST) { + log_error("Failed to create %s: %m", t); + r = -errno; + goto fail; + } + if (mount(f, t, "bind", MS_BIND|MS_REC, NULL) < 0) { log_error("Failed to create /usr bind mount: %m"); r = -errno; @@ -1033,7 +1101,8 @@ static int copy_devnodes(const char *dest) { "full\0" "random\0" "urandom\0" - "tty\0"; + "tty\0" + "net/tun\0"; const char *d; int r = 0; @@ -1064,10 +1133,17 @@ static int copy_devnodes(const char *dest) { log_error("%s is not a char or block device, cannot copy", from); return -EIO; - } else if (mknod(to, st.st_mode, st.st_rdev) < 0) { + } else { + r = mkdir_parents(to, 0775); + if (r < 0) { + log_error("Failed to create parent directory of %s: %s", to, strerror(-r)); + return -r; + } - log_error("mknod(%s) failed: %m", dest); - return -errno; + if (mknod(to, st.st_mode, st.st_rdev) < 0) { + log_error("mknod(%s) failed: %m", dest); + return -errno; + } } } @@ -1213,7 +1289,7 @@ static int setup_hostname(void) { if (arg_share_system) return 0; - if (sethostname(arg_machine, strlen(arg_machine)) < 0) + if (sethostname_idempotent(arg_machine) < 0) return -errno; return 0; @@ -1298,7 +1374,7 @@ static int setup_journal(const char *directory) { r = mkdir_p(q, 0755); if (r < 0) - log_warning("failed to create directory %s: %m", q); + log_warning("Failed to create directory %s: %m", q); return 0; } @@ -1333,7 +1409,7 @@ static int setup_journal(const char *directory) { r = mkdir_p(q, 0755); if (r < 0) - log_warning("failed to create directory %s: %m", q); + log_warning("Failed to create directory %s: %m", q); return 0; } @@ -1390,7 +1466,7 @@ static int drop_capabilities(void) { static int register_machine(pid_t pid, int local_ifindex) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_bus_unref_ sd_bus *bus = NULL; + _cleanup_bus_close_unref_ sd_bus *bus = NULL; int r; if (!arg_register) @@ -1469,7 +1545,7 @@ static int register_machine(pid_t pid, int local_ifindex) { return r; } - r = sd_bus_message_append(m, "(sv)", "DeviceAllow", "a(ss)", 10, + r = sd_bus_message_append(m, "(sv)", "DeviceAllow", "a(ss)", 11, /* Allow the container to * access and create the API * device nodes, so that @@ -1482,6 +1558,7 @@ static int register_machine(pid_t pid, int local_ifindex) { "/dev/random", "rwm", "/dev/urandom", "rwm", "/dev/tty", "rwm", + "/dev/net/tun", "rwm", /* Allow the container * access to ptys. However, * do not permit the @@ -1525,7 +1602,7 @@ static int register_machine(pid_t pid, int local_ifindex) { static int terminate_machine(pid_t pid) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_bus_unref_ sd_bus *bus = NULL; + _cleanup_bus_close_unref_ sd_bus *bus = NULL; const char *path; int r; @@ -1610,9 +1687,10 @@ static int reset_audit_loginuid(void) { return 0; } -#define HASH_KEY SD_ID128_MAKE(c3,c4,f9,19,b5,57,b2,1c,e6,cf,14,27,03,9c,ee,a2) +#define HOST_HASH_KEY SD_ID128_MAKE(1a,37,6f,c7,46,ec,45,0b,ad,a3,d5,31,06,60,5d,b1) +#define CONTAINER_HASH_KEY SD_ID128_MAKE(c3,c4,f9,19,b5,57,b2,1c,e6,cf,14,27,03,9c,ee,a2) -static int get_mac(struct ether_addr *mac) { +static int generate_mac(struct ether_addr *mac, sd_id128_t hash_key) { int r; uint8_t result[8]; @@ -1634,7 +1712,7 @@ static int get_mac(struct ether_addr *mac) { /* Let's hash the host machine ID plus the container name. We * use a fixed, but originally randomly created hash key here. */ - siphash24(result, v, sz, HASH_KEY.bytes); + siphash24(result, v, sz, hash_key.bytes); assert_cc(ETH_ALEN <= sizeof(result)); memcpy(mac->ether_addr_octet, result, ETH_ALEN); @@ -1649,7 +1727,7 @@ static int get_mac(struct ether_addr *mac) { static int setup_veth(pid_t pid, char iface_name[IFNAMSIZ], int *ifi) { _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL; _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL; - struct ether_addr mac; + struct ether_addr mac_host, mac_container; int r, i; if (!arg_private_network) @@ -1660,15 +1738,18 @@ static int setup_veth(pid_t pid, char iface_name[IFNAMSIZ], int *ifi) { /* Use two different interface name prefixes depending whether * we are in bridge mode or not. */ - if (arg_network_bridge) - memcpy(iface_name, "vb-", 3); - else - memcpy(iface_name, "ve-", 3); - strncpy(iface_name+3, arg_machine, IFNAMSIZ - 3); + snprintf(iface_name, IFNAMSIZ - 1, "%s-%s", + arg_network_bridge ? "vb" : "ve", arg_machine); + + r = generate_mac(&mac_container, CONTAINER_HASH_KEY); + if (r < 0) { + log_error("Failed to generate predictable MAC address for container side"); + return r; + } - r = get_mac(&mac); + r = generate_mac(&mac_host, HOST_HASH_KEY); if (r < 0) { - log_error("Failed to generate predictable MAC address for host0"); + log_error("Failed to generate predictable MAC address for host side"); return r; } @@ -1690,6 +1771,12 @@ static int setup_veth(pid_t pid, char iface_name[IFNAMSIZ], int *ifi) { return r; } + r = sd_rtnl_message_append_ether_addr(m, IFLA_ADDRESS, &mac_host); + if (r < 0) { + log_error("Failed to add netlink MAC address: %s", strerror(-r)); + return r; + } + r = sd_rtnl_message_open_container(m, IFLA_LINKINFO); if (r < 0) { log_error("Failed to open netlink container: %s", strerror(-r)); @@ -1714,7 +1801,7 @@ static int setup_veth(pid_t pid, char iface_name[IFNAMSIZ], int *ifi) { return r; } - r = sd_rtnl_message_append_ether_addr(m, IFLA_ADDRESS, &mac); + r = sd_rtnl_message_append_ether_addr(m, IFLA_ADDRESS, &mac_container); if (r < 0) { log_error("Failed to add netlink MAC address: %s", strerror(-r)); return r; @@ -1880,7 +1967,7 @@ static int move_network_interfaces(pid_t pid) { if (ifi < 0) return ifi; - r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, ifi); + r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, ifi); if (r < 0) { log_error("Failed to allocate netlink message: %s", strerror(-r)); return r; @@ -2529,20 +2616,27 @@ static int mount_devices( static void loop_remove(int nr, int *image_fd) { _cleanup_close_ int control = -1; + int r; if (nr < 0) return; if (image_fd && *image_fd >= 0) { - ioctl(*image_fd, LOOP_CLR_FD); + r = ioctl(*image_fd, LOOP_CLR_FD); + if (r < 0) + log_warning("Failed to close loop image: %m"); *image_fd = safe_close(*image_fd); } control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); - if (control < 0) + if (control < 0) { + log_warning("Failed to open /dev/loop-control: %m"); return; + } - ioctl(control, LOOP_CTL_REMOVE, nr); + r = ioctl(control, LOOP_CTL_REMOVE, nr); + if (r < 0) + log_warning("Failed to remove loop %d: %m", nr); } static int spawn_getent(const char *database, const char *key, pid_t *rpid) { @@ -2603,7 +2697,8 @@ static int spawn_getent(const char *database, const char *key, pid_t *rpid) { } static int change_uid_gid(char **_home) { - char line[LINE_MAX], *w, *x, *state, *u, *g, *h; + char line[LINE_MAX], *x, *u, *g, *h; + const char *word, *state; _cleanup_free_ uid_t *uids = NULL; _cleanup_free_ char *home = NULL; _cleanup_fclose_ FILE *f = NULL; @@ -2753,10 +2848,10 @@ static int change_uid_gid(char **_home) { x += strcspn(x, WHITESPACE); x += strspn(x, WHITESPACE); - FOREACH_WORD(w, l, x, state) { + FOREACH_WORD(word, l, x, state) { char c[l+1]; - memcpy(c, w, l); + memcpy(c, word, l); c[l] = 0; if (!GREEDY_REALLOC(uids, sz, n_uids+1)) @@ -2836,33 +2931,30 @@ static int wait_for_container(pid_t pid, ContainerStatus *container) { } switch (status.si_code) { + case CLD_EXITED: r = status.si_status; if (r == 0) { if (!arg_quiet) - log_debug("Container %s exited successfully.", - arg_machine); + log_debug("Container %s exited successfully.", arg_machine); *container = CONTAINER_TERMINATED; - } else { - log_error("Container %s failed with error code %i.", - arg_machine, status.si_status); - } + } else + log_error("Container %s failed with error code %i.", arg_machine, status.si_status); + break; case CLD_KILLED: if (status.si_status == SIGINT) { if (!arg_quiet) - log_info("Container %s has been shut down.", - arg_machine); + log_info("Container %s has been shut down.", arg_machine); *container = CONTAINER_TERMINATED; r = 0; break; } else if (status.si_status == SIGHUP) { if (!arg_quiet) - log_info("Container %s is being rebooted.", - arg_machine); + log_info("Container %s is being rebooted.", arg_machine); *container = CONTAINER_REBOOTED; r = 0; @@ -2871,15 +2963,13 @@ static int wait_for_container(pid_t pid, ContainerStatus *container) { /* CLD_KILLED fallthrough */ case CLD_DUMPED: - log_error("Container %s terminated by signal %s.", - arg_machine, signal_to_string(status.si_status)); - r = -1; + log_error("Container %s terminated by signal %s.", arg_machine, signal_to_string(status.si_status)); + r = -EIO; break; default: - log_error("Container %s failed due to unknown reason.", - arg_machine); - r = -1; + log_error("Container %s failed due to unknown reason.", arg_machine); + r = -EIO; break; } @@ -3064,7 +3154,9 @@ int main(int argc, char *argv[]) { goto finish; } - sd_notify(0, "READY=1"); + sd_notify(false, + "READY=1\n" + "STATUS=Container running."); assert_se(sigemptyset(&mask) == 0); assert_se(sigemptyset(&mask_chld) == 0); @@ -3074,12 +3166,18 @@ int main(int argc, char *argv[]) { for (;;) { ContainerStatus container_status; - int eventfds[2] = { -1, -1 }; + _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL; struct sigaction sa = { .sa_handler = nop_handler, .sa_flags = SA_NOCLDSTOP, }; + r = barrier_create(&barrier); + if (r < 0) { + log_error("Cannot initialize IPC barrier: %s", strerror(-r)); + goto finish; + } + /* Child can be killed before execv(), so handle SIGCHLD * in order to interrupt parent's blocking calls and * give it a chance to call wait() and terminate. */ @@ -3095,9 +3193,9 @@ int main(int argc, char *argv[]) { goto finish; } - pid = clone_with_eventfd(SIGCHLD|CLONE_NEWNS| - (arg_share_system ? 0 : CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS)| - (arg_private_network ? CLONE_NEWNET : 0), eventfds); + pid = syscall(__NR_clone, SIGCHLD|CLONE_NEWNS| + (arg_share_system ? 0 : CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS)| + (arg_private_network ? CLONE_NEWNET : 0), NULL); if (pid < 0) { if (errno == EINVAL) log_error("clone() failed, do you have namespace support enabled in your kernel? (You need UTS, IPC, PID and NET namespacing built in): %m"); @@ -3126,6 +3224,8 @@ int main(int argc, char *argv[]) { }; char **env_use; + barrier_set_role(&barrier, BARRIER_CHILD); + envp[n_env] = strv_find_prefix(environ, "TERM="); if (envp[n_env]) n_env ++; @@ -3139,9 +3239,7 @@ int main(int argc, char *argv[]) { kmsg_socket_pair[0] = safe_close(kmsg_socket_pair[0]); reset_all_signal_handlers(); - - assert_se(sigemptyset(&mask) == 0); - assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0); + reset_signal_mask(); k = open_terminal(console, O_RDWR); if (k != STDIN_FILENO) { @@ -3151,26 +3249,26 @@ int main(int argc, char *argv[]) { } log_error("Failed to open console: %s", strerror(-k)); - goto child_fail; + _exit(EXIT_FAILURE); } if (dup2(STDIN_FILENO, STDOUT_FILENO) != STDOUT_FILENO || dup2(STDIN_FILENO, STDERR_FILENO) != STDERR_FILENO) { log_error("Failed to duplicate console: %m"); - goto child_fail; + _exit(EXIT_FAILURE); } if (setsid() < 0) { log_error("setsid() failed: %m"); - goto child_fail; + _exit(EXIT_FAILURE); } if (reset_audit_loginuid() < 0) - goto child_fail; + _exit(EXIT_FAILURE); if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0) { log_error("PR_SET_PDEATHSIG failed: %m"); - goto child_fail; + _exit(EXIT_FAILURE); } /* Mark everything as slave, so that we still @@ -3178,113 +3276,109 @@ int main(int argc, char *argv[]) { * propagate mounts to the real root. */ if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) { log_error("MS_SLAVE|MS_REC failed: %m"); - goto child_fail; + _exit(EXIT_FAILURE); } if (mount_devices(arg_directory, root_device, root_device_rw, home_device, home_device_rw, srv_device, srv_device_rw) < 0) - goto child_fail; + _exit(EXIT_FAILURE); /* Turn directory into bind mount */ if (mount(arg_directory, arg_directory, "bind", MS_BIND|MS_REC, NULL) < 0) { log_error("Failed to make bind mount: %m"); - goto child_fail; + _exit(EXIT_FAILURE); } r = setup_volatile(arg_directory); if (r < 0) - goto child_fail; + _exit(EXIT_FAILURE); if (setup_volatile_state(arg_directory) < 0) - goto child_fail; + _exit(EXIT_FAILURE); r = base_filesystem_create(arg_directory); if (r < 0) - goto child_fail; + _exit(EXIT_FAILURE); if (arg_read_only) { k = bind_remount_recursive(arg_directory, true); if (k < 0) { log_error("Failed to make tree read-only: %s", strerror(-k)); - goto child_fail; + _exit(EXIT_FAILURE); } } if (mount_all(arg_directory) < 0) - goto child_fail; + _exit(EXIT_FAILURE); if (copy_devnodes(arg_directory) < 0) - goto child_fail; + _exit(EXIT_FAILURE); if (setup_ptmx(arg_directory) < 0) - goto child_fail; + _exit(EXIT_FAILURE); dev_setup(arg_directory); if (setup_seccomp() < 0) - goto child_fail; + _exit(EXIT_FAILURE); if (setup_dev_console(arg_directory, console) < 0) - goto child_fail; + _exit(EXIT_FAILURE); if (setup_kmsg(arg_directory, kmsg_socket_pair[1]) < 0) - goto child_fail; + _exit(EXIT_FAILURE); kmsg_socket_pair[1] = safe_close(kmsg_socket_pair[1]); if (setup_boot_id(arg_directory) < 0) - goto child_fail; + _exit(EXIT_FAILURE); if (setup_timezone(arg_directory) < 0) - goto child_fail; + _exit(EXIT_FAILURE); if (setup_resolv_conf(arg_directory) < 0) - goto child_fail; + _exit(EXIT_FAILURE); if (setup_journal(arg_directory) < 0) - goto child_fail; + _exit(EXIT_FAILURE); if (mount_binds(arg_directory, arg_bind, false) < 0) - goto child_fail; + _exit(EXIT_FAILURE); if (mount_binds(arg_directory, arg_bind_ro, true) < 0) - goto child_fail; + _exit(EXIT_FAILURE); if (mount_tmpfs(arg_directory) < 0) - goto child_fail; + _exit(EXIT_FAILURE); if (setup_kdbus(arg_directory, kdbus_domain) < 0) - goto child_fail; + _exit(EXIT_FAILURE); /* Tell the parent that we are ready, and that * it can cgroupify us to that we lack access * to certain devices and resources. */ - r = eventfd_send_state(eventfds[1], - EVENTFD_CHILD_SUCCEEDED); - eventfds[1] = safe_close(eventfds[1]); - if (r < 0) - goto child_fail; + barrier_place(&barrier); if (chdir(arg_directory) < 0) { log_error("chdir(%s) failed: %m", arg_directory); - goto child_fail; + _exit(EXIT_FAILURE); } if (mount(arg_directory, "/", NULL, MS_MOVE, NULL) < 0) { log_error("mount(MS_MOVE) failed: %m"); - goto child_fail; + _exit(EXIT_FAILURE); } if (chroot(".") < 0) { log_error("chroot() failed: %m"); - goto child_fail; + _exit(EXIT_FAILURE); } if (chdir("/") < 0) { log_error("chdir() failed: %m"); - goto child_fail; + _exit(EXIT_FAILURE); } umask(0022); @@ -3294,18 +3388,18 @@ int main(int argc, char *argv[]) { if (drop_capabilities() < 0) { log_error("drop_capabilities() failed: %m"); - goto child_fail; + _exit(EXIT_FAILURE); } r = change_uid_gid(&home); if (r < 0) - goto child_fail; + _exit(EXIT_FAILURE); if ((asprintf((char**)(envp + n_env++), "HOME=%s", home ? home: "/root") < 0) || (asprintf((char**)(envp + n_env++), "USER=%s", arg_user ? arg_user : "root") < 0) || (asprintf((char**)(envp + n_env++), "LOGNAME=%s", arg_user ? arg_user : "root") < 0)) { log_oom(); - goto child_fail; + _exit(EXIT_FAILURE); } if (!sd_id128_equal(arg_uuid, SD_ID128_NULL)) { @@ -3313,7 +3407,7 @@ int main(int argc, char *argv[]) { if (asprintf((char**)(envp + n_env++), "container_uuid=%s", id128_format_as_uuid(arg_uuid, as_uuid)) < 0) { log_oom(); - goto child_fail; + _exit(EXIT_FAILURE); } } @@ -3321,13 +3415,13 @@ int main(int argc, char *argv[]) { k = fdset_cloexec(fds, false); if (k < 0) { log_error("Failed to unset O_CLOEXEC for file descriptors."); - goto child_fail; + _exit(EXIT_FAILURE); } if ((asprintf((char **)(envp + n_env++), "LISTEN_FDS=%u", n_fd_passed) < 0) || (asprintf((char **)(envp + n_env++), "LISTEN_PID=1") < 0)) { log_oom(); - goto child_fail; + _exit(EXIT_FAILURE); } } @@ -3336,12 +3430,12 @@ int main(int argc, char *argv[]) { if (arg_personality != 0xffffffffLU) { if (personality(arg_personality) < 0) { log_error("personality() failed: %m"); - goto child_fail; + _exit(EXIT_FAILURE); } } else if (secondary) { if (personality(PER_LINUX32) < 0) { log_error("personality() failed: %m"); - goto child_fail; + _exit(EXIT_FAILURE); } } @@ -3349,7 +3443,7 @@ int main(int argc, char *argv[]) { if (arg_selinux_context) if (setexeccon((security_context_t) arg_selinux_context) < 0) { log_error("setexeccon(\"%s\") failed: %m", arg_selinux_context); - goto child_fail; + _exit(EXIT_FAILURE); } #endif @@ -3359,7 +3453,7 @@ int main(int argc, char *argv[]) { n = strv_env_merge(2, envp, arg_setenv); if (!n) { log_oom(); - goto child_fail; + _exit(EXIT_FAILURE); } env_use = n; @@ -3367,10 +3461,8 @@ int main(int argc, char *argv[]) { env_use = (char**) envp; /* Wait until the parent is ready with the setup, too... */ - r = eventfd_parent_succeeded(eventfds[0]); - eventfds[0] = safe_close(eventfds[0]); - if (r < 0) - goto child_fail; + if (!barrier_place_and_sync(&barrier)) + _exit(EXIT_FAILURE); if (arg_boot) { char **a; @@ -3399,29 +3491,15 @@ int main(int argc, char *argv[]) { } log_error("execv() failed: %m"); - - child_fail: - /* Tell the parent that the setup failed, so he - * can clean up resources and terminate. */ - if (eventfds[1] != -1) - eventfd_send_state(eventfds[1], - EVENTFD_CHILD_FAILED); _exit(EXIT_FAILURE); } + barrier_set_role(&barrier, BARRIER_PARENT); fdset_free(fds); fds = NULL; - /* Wait for the child event: - * If EVENTFD_CHILD_FAILED, the child will terminate soon. - * If EVENTFD_CHILD_SUCCEEDED, the child is reporting that - * it is ready with all it needs to do with priviliges. - * After we got the notification we can make the process - * join its cgroup which might limit what it can do */ - r = eventfd_child_succeeded(eventfds[1]); - eventfds[1] = safe_close(eventfds[1]); - - if (r >= 0) { + /* wait for child-setup to be done */ + if (barrier_place_and_sync(&barrier)) { int ifi = 0; r = move_network_interfaces(pid); @@ -3458,10 +3536,7 @@ int main(int argc, char *argv[]) { /* Notify the child that the parent is ready with all * its setup, and that the child can now hand over * control to the code to run inside the container. */ - r = eventfd_send_state(eventfds[0], EVENTFD_PARENT_SUCCEEDED); - eventfds[0] = safe_close(eventfds[0]); - if (r < 0) - goto finish; + barrier_place(&barrier); k = process_pty(master, &mask, arg_boot ? pid : 0, SIGRTMIN+3); if (k < 0) { @@ -3512,6 +3587,10 @@ int main(int argc, char *argv[]) { } finish: + sd_notify(false, + "STOPPING=1\n" + "STATUS=Terminating..."); + loop_remove(loop_nr, &image_fd); if (pid > 0)