From: Alban Crequy al...@endocode.com
When a process starts systemd-nspawn with exec*() without fork(),
systemd-nspawn can be the parent process of children processes unknown
to systemd-nspawn. It can then receive the signal SIGCHLD for both the
container leader process and the previously started processes. So it
should distinguish them.
v2:
- correctly check if a child was in a waitable state.
---
src/nspawn/nspawn.c | 55 +++--
1 file changed, 49 insertions(+), 6 deletions(-)
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 05d2c71..71a6239 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -23,6 +23,7 @@
#include sched.h
#include unistd.h
#include sys/types.h
+#include sys/wait.h
#include sys/mount.h
#include stdlib.h
#include string.h
@@ -3511,6 +3512,12 @@ static int change_uid_gid(char **_home) {
return 0;
}
+typedef struct SigChildData {
+pid_t leader_pid;
+siginfo_t leader_status;
+bool terminated;
+} SigChildData;
+
/*
* Return values:
* 0 : wait_for_terminate() failed to get the state of the
@@ -3528,13 +3535,17 @@ static int change_uid_gid(char **_home) {
* That is, success is indicated by a return value of zero, and an
* error is indicated by a non-zero value.
*/
-static int wait_for_container(pid_t pid, ContainerStatus *container) {
+static int wait_for_container(SigChildData *sigchld_ctx, pid_t pid,
ContainerStatus *container) {
siginfo_t status;
int r;
-r = wait_for_terminate(pid, status);
-if (r 0)
-return log_warning_errno(r, Failed to wait for container:
%m);
+if (sigchld_ctx-terminated) {
+status = sigchld_ctx-leader_status;
+} else {
+r = wait_for_terminate(pid, status);
+if (r 0)
+return log_warning_errno(r, Failed to wait for
container: %m);
+}
switch (status.si_code) {
@@ -3594,6 +3605,35 @@ static int on_orderly_shutdown(sd_event_source *s, const
struct signalfd_siginfo
return 0;
}
+static int on_sigchld(sd_event_source *s, const struct signalfd_siginfo *si,
void *userdata) {
+SigChildData *ctx = userdata;
+pid_t leader_pid = ctx-leader_pid;
+
+/* several terminated children could be merged in a single SIGCHLD, so
+ * don't rely on si-ssi_pid. */
+
+while (1) {
+int r;
+siginfo_t status;
+
+zero(status);
+r = waitid(P_ALL, -1, status, WEXITED | WNOHANG);
+if (r 0)
+break;
+
+if (status.si_pid = 0)
+break;
+
+if (status.si_pid == leader_pid) {
+ctx-leader_status = status;
+ctx-terminated = true;
+return sd_event_exit(sd_event_source_get_event(s), 0);
+}
+}
+
+return 0;
+}
+
static int determine_names(void) {
int r;
@@ -3917,6 +3957,7 @@ int main(int argc, char *argv[]) {
.sa_handler = nop_handler,
.sa_flags = SA_NOCLDSTOP,
};
+SigChildData sigchld_ctx = {0,};
r = barrier_create(barrier);
if (r 0) {
@@ -4386,7 +4427,9 @@ int main(int argc, char *argv[]) {
}
/* simply exit on sigchld */
-sd_event_add_signal(event, NULL, SIGCHLD,
NULL, NULL);
+sigchld_ctx.leader_pid = pid;
+sigchld_ctx.terminated = false;
+sd_event_add_signal(event, NULL, SIGCHLD,
on_sigchld , sigchld_ctx);
if (arg_expose_ports) {
r = watch_rtnl(event,
rtnl_socket_pair[0], exposed, rtnl);
@@ -4425,7 +4468,7 @@ int main(int argc, char *argv[]) {
/* Normally redundant, but better safe than sorry */
kill(pid, SIGKILL);
-r = wait_for_container(pid, container_status);
+r = wait_for_container(sigchld_ctx, pid, container_status);
pid = 0;
if (r 0)
--
2.1.4
___
systemd-devel mailing list
systemd-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/systemd-devel