summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nspawn/nspawn.c35
1 files changed, 26 insertions, 9 deletions
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 3a39b4a6f5..60e1b5dbf1 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -2928,6 +2928,7 @@ static int outer_child(
assert(uuid_socket >= 0);
assert(notify_socket >= 0);
assert(kmsg_socket >= 0);
+ assert(cgroup_socket);
if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0)
return log_error_errno(errno, "PR_SET_PDEATHSIG failed: %m");
@@ -3170,20 +3171,18 @@ static int outer_child(
* less privileged mountns (and using userns causes the mountns to be less privileged). */
if (!arg_use_cgns) {
/* If !use_cgns, then cgroup_mount_mounts() needs to look at /proc/pid/cgroup; but because we've
- * already chroot()ed, we don't have access to /proc. So the parent opens the file and sends it to
- * us. */
+ * already chroot()ed, we don't have access to /proc. So the parent opens the file for us and then
+ * sends it to us. */
int cgfd;
_cleanup_fclose_ FILE *cgfile = NULL;
- assert(cgroup_socket);
-
cgfd = receive_one_fd(cgroup_socket, 0);
if (cgfd < 0)
return log_error_errno(cgfd, "Failed to recv cgroup fd: %m");
cgfile = fdopen(cgfd, "re");
if (!cgfile) {
- r = -errno; /* in case safe_close sets errno */
+ r = -errno; /* in case safe_close() sets errno */
cgfd = safe_close(cgfd);
return log_error_errno(r, "Failed to create a stream object for cgroup fd: %m");
}
@@ -3194,6 +3193,15 @@ static int outer_child(
arg_selinux_apifs_context);
if (r < 0)
return r;
+ } else {
+ /* We're not doing anything else, but wait for a bit of data anyway; as a synchronization point, to
+ * make sure that our SIGCHLD doesn't EINTR anything important. */
+ char c;
+ l = recv(cgroup_socket, &c, sizeof(c), 0);
+ if (l < 0)
+ return log_error_errno(errno, "Failed to recv synchronization byte: %m");
+ if (l != sizeof(c))
+ return log_error_errno(errno, "Short read while receiving synchronization byte: %m");
}
pid_socket = safe_close(pid_socket);
@@ -3685,9 +3693,8 @@ static int run(int master,
if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uid_shift_socket_pair) < 0)
return log_error_errno(errno, "Failed to create uid shift socket pair: %m");
- if (!arg_use_cgns)
- if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, cgroup_socket_pair) < 0)
- return log_error_errno(errno, "Failed to create cgroup socket pair: %m");
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, cgroup_socket_pair) < 0)
+ return log_error_errno(errno, "Failed to create cgroup socket pair: %m");
/* 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. */
@@ -3900,7 +3907,6 @@ static int run(int master,
return r;
if (!arg_use_cgns) {
- /* helper_pid won't exit until this happens */
const char *fs;
_cleanup_close_ int fd;
@@ -3912,6 +3918,17 @@ static int run(int master,
r = send_one_fd(cgroup_socket_pair[0], fd, 0);
if (r < 0)
return log_error_errno(r, "Failed to send cgroup fd: %m");
+ } else {
+ /* we don't have any data to send, but sending something here is important because it acts as a
+ * synchronization point. Otherwise the outer child could exit earlier, and the resulting SIGCHLD
+ * could interrupt something like register_machine() above. We could use a barrier for this, but we
+ * have a perfectly good socket here already. */
+ char c = '\0';
+ l = send(cgroup_socket_pair[0], &c, sizeof(c), MSG_NOSIGNAL);
+ if (l < 0)
+ return log_error_errno(errno, "Failed to send synchronization byte: %m");
+ if (l != sizeof(c))
+ return log_error_errno(errno, "Short write while sending synchronization byte: %m");
}
/* Wait for the outer child. */