diff options
-rw-r--r-- | src/core/load-fragment.c | 13 | ||||
-rw-r--r-- | src/libsystemd-bus/bus-container.c | 149 | ||||
-rw-r--r-- | src/libsystemd-bus/bus-container.h | 3 | ||||
-rw-r--r-- | src/libsystemd-bus/sd-bus.c | 69 | ||||
-rw-r--r-- | src/machine/machinectl.c | 41 | ||||
-rw-r--r-- | src/shared/util.c | 73 | ||||
-rw-r--r-- | src/shared/util.h | 5 |
7 files changed, 266 insertions, 87 deletions
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 99cf20dcce..183c43d58f 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -2680,31 +2680,28 @@ int unit_load_fragment(Unit *u) { /* Look for a template */ if (u->load_state == UNIT_STUB && u->instance) { - char *k; + _cleanup_free_ char *k; k = unit_name_template(u->id); if (!k) return -ENOMEM; r = load_from_path(u, k); - free(k); - if (r < 0) return r; if (u->load_state == UNIT_STUB) SET_FOREACH(t, u->names, i) { + _cleanup_free_ char *z = NULL; if (t == u->id) continue; - k = unit_name_template(t); - if (!k) + z = unit_name_template(t); + if (!z) return -ENOMEM; - r = load_from_path(u, k); - free(k); - + r = load_from_path(u, z); if (r < 0) return r; diff --git a/src/libsystemd-bus/bus-container.c b/src/libsystemd-bus/bus-container.c index 33478c02de..5d31f5afa7 100644 --- a/src/libsystemd-bus/bus-container.c +++ b/src/libsystemd-bus/bus-container.c @@ -28,51 +28,23 @@ #include "bus-socket.h" #include "bus-container.h" -int bus_container_connect(sd_bus *b) { - _cleanup_free_ char *s = NULL, *ns = NULL, *root = NULL, *class = NULL; +int bus_container_connect_socket(sd_bus *b) { _cleanup_close_ int nsfd = -1, rootfd = -1; - char *p; - siginfo_t si; pid_t leader, child; + siginfo_t si; int r; assert(b); assert(b->input_fd < 0); assert(b->output_fd < 0); - p = strappenda("/run/systemd/machines/", b->machine); - r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL); - if (r == -ENOENT) - return -EHOSTDOWN; + r = container_get_leader(b->machine, &leader); if (r < 0) return r; - if (!s) - return -EIO; - - if (!streq_ptr(class, "container")) - return -EIO; - r = parse_pid(s, &leader); + r = namespace_open(leader, &nsfd, &rootfd); if (r < 0) return r; - if (leader <= 1) - return -EIO; - - r = asprintf(&ns, "/proc/%lu/ns/mnt", (unsigned long) leader); - if (r < 0) - return -ENOMEM; - - nsfd = open(ns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (nsfd < 0) - return -errno; - - r = asprintf(&root, "/proc/%lu/root", (unsigned long) leader); - if (r < 0) - return -ENOMEM; - - rootfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (rootfd < 0) - return -errno; b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (b->input_fd < 0) @@ -89,14 +61,9 @@ int bus_container_connect(sd_bus *b) { return -errno; if (child == 0) { - r = setns(nsfd, CLONE_NEWNS); - if (r < 0) - _exit(255); - - if (fchdir(rootfd) < 0) - _exit(255); - if (chroot(".") < 0) + r = namespace_enter(nsfd, rootfd); + if (r < 0) _exit(255); r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size); @@ -107,7 +74,7 @@ int bus_container_connect(sd_bus *b) { _exit(255); } - _exit(0); + _exit(EXIT_SUCCESS); } r = wait_for_terminate(child, &si); @@ -120,8 +87,108 @@ int bus_container_connect(sd_bus *b) { if (si.si_status == 1) return 1; - if (si.si_status != 0) + if (si.si_status != EXIT_SUCCESS) return -EIO; return bus_socket_start_auth(b); } + +int bus_container_connect_kernel(sd_bus *b) { + _cleanup_close_pipe_ int pair[2] = { -1, -1 }; + _cleanup_close_ int nsfd = -1, rootfd = -1; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(int))]; + } control = {}; + struct msghdr mh = { + .msg_control = &control, + .msg_controllen = sizeof(control), + }; + struct cmsghdr *cmsg; + pid_t leader, child; + siginfo_t si; + int r; + _cleanup_close_ int fd = -1; + + assert(b); + assert(b->input_fd < 0); + assert(b->output_fd < 0); + + r = container_get_leader(b->machine, &leader); + if (r < 0) + return r; + + r = namespace_open(leader, &nsfd, &rootfd); + if (r < 0) + return r; + + if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0) + return -errno; + + child = fork(); + if (child < 0) + return -errno; + + if (child == 0) { + close_nointr_nofail(pair[0]); + pair[0] = -1; + + r = namespace_enter(nsfd, rootfd); + if (r < 0) + _exit(EXIT_FAILURE); + + fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + _exit(EXIT_FAILURE); + + cmsg = CMSG_FIRSTHDR(&mh); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); + + mh.msg_controllen = cmsg->cmsg_len; + + if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0) + _exit(EXIT_FAILURE); + + _exit(EXIT_SUCCESS); + } + + close_nointr_nofail(pair[1]); + pair[1] = -1; + + if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0) + return -errno; + + for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { + int *fds; + unsigned n_fds; + + fds = (int*) CMSG_DATA(cmsg); + n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); + + if (n_fds != 1) { + close_many(fds, n_fds); + return -EIO; + } + + fd = fds[0]; + } + + r = wait_for_terminate(child, &si); + if (r < 0) + return r; + + if (si.si_code != CLD_EXITED) + return -EIO; + + if (si.si_status != EXIT_SUCCESS) + return -EIO; + + b->input_fd = b->output_fd = fd; + fd = -1; + + return bus_kernel_take_fd(b); +} diff --git a/src/libsystemd-bus/bus-container.h b/src/libsystemd-bus/bus-container.h index 65f43ab4fd..c6f757a99b 100644 --- a/src/libsystemd-bus/bus-container.h +++ b/src/libsystemd-bus/bus-container.h @@ -23,4 +23,5 @@ #include "sd-bus.h" -int bus_container_connect(sd_bus *b); +int bus_container_connect_socket(sd_bus *b); +int bus_container_connect_kernel(sd_bus *b); diff --git a/src/libsystemd-bus/sd-bus.c b/src/libsystemd-bus/sd-bus.c index 932bf226c5..4eaceeff04 100644 --- a/src/libsystemd-bus/sd-bus.c +++ b/src/libsystemd-bus/sd-bus.c @@ -740,7 +740,7 @@ static int parse_kernel_address(sd_bus *b, const char **p, char **guid) { return 0; } -static int parse_container_address(sd_bus *b, const char **p, char **guid) { +static int parse_container_unix_address(sd_bus *b, const char **p, char **guid) { _cleanup_free_ char *machine = NULL; int r; @@ -782,6 +782,49 @@ static int parse_container_address(sd_bus *b, const char **p, char **guid) { return 0; } +static int parse_container_kernel_address(sd_bus *b, const char **p, char **guid) { + _cleanup_free_ char *machine = NULL; + int r; + + assert(b); + assert(p); + assert(*p); + assert(guid); + + while (**p != 0 && **p != ';') { + r = parse_address_key(p, "guid", guid); + if (r < 0) + return r; + else if (r > 0) + continue; + + r = parse_address_key(p, "machine", &machine); + if (r < 0) + return r; + else if (r > 0) + continue; + + skip_address_key(p); + } + + if (!machine) + return -EINVAL; + + if (!filename_is_safe(machine)) + return -EINVAL; + + free(b->machine); + b->machine = machine; + machine = NULL; + + free(b->kernel); + b->kernel = strdup("/dev/kdbus/0-system/bus"); + if (!b->kernel) + return -ENOMEM; + + return 0; +} + static void bus_reset_parsed_address(sd_bus *b) { assert(b); @@ -855,10 +898,18 @@ static int bus_parse_next_address(sd_bus *b) { return r; break; - } else if (startswith(a, "x-container:")) { + } else if (startswith(a, "x-container-unix:")) { + + a += 17; + r = parse_container_unix_address(b, &a, &guid); + if (r < 0) + return r; + + break; + } else if (startswith(a, "x-container-kernel:")) { - a += 12; - r = parse_container_address(b, &a, &guid); + a += 19; + r = parse_container_kernel_address(b, &a, &guid); if (r < 0) return r; @@ -892,10 +943,12 @@ static int bus_start_address(sd_bus *b) { if (b->exec_path) r = bus_socket_exec(b); + else if (b->machine && b->kernel) + r = bus_container_connect_kernel(b); + else if (b->machine && b->sockaddr.sa.sa_family != AF_UNSPEC) + r = bus_container_connect_socket(b); else if (b->kernel) r = bus_kernel_connect(b); - else if (b->machine) - r = bus_container_connect(b); else if (b->sockaddr.sa.sa_family != AF_UNSPEC) r = bus_socket_connect(b); else @@ -1144,9 +1197,9 @@ _public_ int sd_bus_open_system_container(const char *machine, sd_bus **ret) { return -ENOMEM; #ifdef ENABLE_KDBUS - p = strjoin("kernel:path=/dev/kdbus/ns/machine-", e, "/0-system/bus;x-container:machine=", e, NULL); + p = strjoin("x-container-kernel:machine=", e, ";x-container-unix:machine=", e, NULL); #else - p = strjoin("x-container:machine=", e, NULL); + p = strjoin("x-container-unix:machine=", e, NULL); #endif if (!p) return -ENOMEM; diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 7bb7086056..f5485b3d42 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -398,8 +398,8 @@ static int terminate_machine(sd_bus *bus, char **args, unsigned n) { } static int openpt_in_namespace(pid_t pid, int flags) { + _cleanup_close_pipe_ int pair[2] = { -1, -1 }; _cleanup_close_ int nsfd = -1, rootfd = -1; - _cleanup_close_pipe_ int sock[2] = { -1, -1 }; union { struct cmsghdr cmsghdr; uint8_t buf[CMSG_SPACE(sizeof(int))]; @@ -410,23 +410,14 @@ static int openpt_in_namespace(pid_t pid, int flags) { }; struct cmsghdr *cmsg; int master = -1, r; - char *ns, *root; pid_t child; siginfo_t si; - ns = procfs_file_alloca(pid, "ns/mnt"); - - nsfd = open(ns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (nsfd < 0) - return -errno; - - root = procfs_file_alloca(pid, "root"); - - rootfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (rootfd < 0) - return -errno; + r = namespace_open(pid, &nsfd, &rootfd); + if (r < 0) + return r; - if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sock) < 0) + if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0) return -errno; child = fork(); @@ -434,19 +425,13 @@ static int openpt_in_namespace(pid_t pid, int flags) { return -errno; if (child == 0) { - close_nointr_nofail(sock[0]); - sock[0] = -1; + close_nointr_nofail(pair[0]); + pair[0] = -1; - r = setns(nsfd, CLONE_NEWNS); + r = namespace_enter(nsfd, rootfd); if (r < 0) _exit(EXIT_FAILURE); - if (fchdir(rootfd) < 0) - _exit(EXIT_FAILURE); - - if (chroot(".") < 0) - _exit(EXIT_FAILURE); - master = posix_openpt(flags); if (master < 0) _exit(EXIT_FAILURE); @@ -459,18 +444,16 @@ static int openpt_in_namespace(pid_t pid, int flags) { mh.msg_controllen = cmsg->cmsg_len; - r = sendmsg(sock[1], &mh, MSG_NOSIGNAL); - close_nointr_nofail(master); - if (r < 0) + if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0) _exit(EXIT_FAILURE); _exit(EXIT_SUCCESS); } - close_nointr_nofail(sock[1]); - sock[1] = -1; + close_nointr_nofail(pair[1]); + pair[1] = -1; - if (recvmsg(sock[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0) + if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0) return -errno; for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) diff --git a/src/shared/util.c b/src/shared/util.c index 1c35edfbb1..66276aa54e 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -5951,3 +5951,76 @@ int proc_cmdline(char **ret) { return 1; } + +int container_get_leader(const char *machine, pid_t *pid) { + _cleanup_free_ char *s = NULL, *class = NULL; + const char *p; + pid_t leader; + int r; + + assert(machine); + assert(pid); + + p = strappenda("/run/systemd/machines/", machine); + r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL); + if (r == -ENOENT) + return -EHOSTDOWN; + if (r < 0) + return r; + if (!s) + return -EIO; + + if (!streq_ptr(class, "container")) + return -EIO; + + r = parse_pid(s, &leader); + if (r < 0) + return r; + if (leader <= 1) + return -EIO; + + *pid = leader; + return 0; +} + +int namespace_open(pid_t pid, int *namespace_fd, int *root_fd) { + _cleanup_close_ int nsfd = -1; + const char *ns, *root; + int rfd; + + assert(pid >= 0); + assert(namespace_fd); + assert(root_fd); + + ns = procfs_file_alloca(pid, "ns/mnt"); + nsfd = open(ns, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (nsfd < 0) + return -errno; + + root = procfs_file_alloca(pid, "root"); + rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); + if (rfd < 0) + return -errno; + + *namespace_fd = nsfd; + *root_fd = rfd; + nsfd = -1; + + return 0; +} + +int namespace_enter(int namespace_fd, int root_fd) { + assert(namespace_fd >= 0); + assert(root_fd >= 0); + + if (setns(namespace_fd, CLONE_NEWNS) < 0) + return -errno; + + if (fchdir(root_fd) < 0) + return -errno; + + if (chroot(".") < 0) + return -errno; + + return 0; +} diff --git a/src/shared/util.h b/src/shared/util.h index 504f63a28f..d5fa81c6a5 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -789,3 +789,8 @@ static inline void qsort_safe(void *base, size_t nmemb, size_t size, } int proc_cmdline(char **ret); + +int container_get_leader(const char *machine, pid_t *pid); + +int namespace_open(pid_t pid, int *namespace_fd, int *root_fd); +int namespace_enter(int namespace_fd, int root_fd); |