diff --git a/libcontainer/nsenter/nsexec.c b/libcontainer/nsenter/nsexec.c index 307cef45da1..b7a7b3ff007 100644 --- a/libcontainer/nsenter/nsexec.c +++ b/libcontainer/nsenter/nsexec.c @@ -33,6 +33,7 @@ enum sync_t { SYNC_USERMAP_ACK = 0x41, /* Mapping finished by the parent. */ SYNC_RECVPID_PLS = 0x42, /* Tell parent we're sending the PID. */ SYNC_RECVPID_ACK = 0x43, /* PID was correctly received by parent. */ + SYNC_CHILD_READY = 0x44, /* The grandchild is ready to return. */ /* XXX: This doesn't help with segfaults and other such issues. */ SYNC_ERR = 0xFF, /* Fatal error, no turning back. The error code follows. */ @@ -500,7 +501,7 @@ void nsexec(void) * process. */ case JUMP_PARENT: { - int len; + int len, ready = 0; pid_t child; char buf[JSON_MAX]; @@ -512,8 +513,14 @@ void nsexec(void) if (child < 0) bail("unable to fork: child_func"); - /* State machine for synchronisation with the children. */ - while (true) { + /* + * State machine for synchronisation with the children. + * + * Father only return when both child and grandchild are + * ready, so we can receive all possible error codes + * generated by children. + */ + while (ready < 2) { enum sync_t s; /* This doesn't need to be global, we're in the parent. */ @@ -571,17 +578,22 @@ void nsexec(void) } } - /* Leave the loop. */ - goto out; + ready++; + break; case SYNC_RECVPID_ACK: /* We should _never_ receive acks. */ kill(child, SIGKILL); bail("failed to sync with child: unexpected SYNC_RECVPID_ACK"); break; + case SYNC_CHILD_READY: + ready++; + break; + default: + bail("unexpected sync value"); + break; } } - out: /* Send the init_func pid back to our parent. */ len = snprintf(buf, JSON_MAX, "{\"pid\": %d}\n", child); if (len < 0) { @@ -711,6 +723,7 @@ void nsexec(void) * start_child() code after forking in the parent. */ int consolefd = config.consolefd; + enum sync_t s; /* We're in a child and thus need to tell the parent if we die. */ syncfd = syncpipe[0]; @@ -741,6 +754,10 @@ void nsexec(void) bail("failed to dup stderr"); } + s = SYNC_CHILD_READY; + if (write(syncfd, &s, sizeof(s)) != sizeof(s)) + bail("failed to sync with patent: write(SYNC_CHILD_READY)"); + /* Close sync pipes. */ close(syncpipe[0]); close(syncpipe[1]);