From: Christian Brauner Date: Mon, 25 Dec 2017 13:53:40 +0000 (+0100) Subject: mainloop: capture output of short-lived init procs X-Git-Tag: lxc-2.0.10~443 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=452badd5cba0c57d27676e26f104c95227395d33;p=thirdparty%2Flxc.git mainloop: capture output of short-lived init procs The handler for the signal fd will detect when the init process of a container has exited and cause the mainloop to close. However, this can happen before the console handlers - or any other events for that matter - are handled. So in the case of init exiting we still need to allow for all buffered input to the console to be handled before exiting. This allows us to capture output from short-lived init processes. This is conceptually equivalent to my implementation of ExecReaderToChannel() https://github.com/lxc/lxd/blob/master/shared/util_linux.go#L527 Closes #1694. Signed-off-by: Christian Brauner --- diff --git a/src/lxc/mainloop.c b/src/lxc/mainloop.c index a7383d632..c102295ef 100644 --- a/src/lxc/mainloop.c +++ b/src/lxc/mainloop.c @@ -20,11 +20,12 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include -#include -#include + #include #include +#include +#include +#include #include #include @@ -49,6 +50,7 @@ int lxc_mainloop(struct lxc_epoll_descr *descr, int timeout_ms) if (nfds < 0) { if (errno == EINTR) continue; + return -1; } @@ -56,14 +58,15 @@ int lxc_mainloop(struct lxc_epoll_descr *descr, int timeout_ms) handler = events[i].data.ptr; /* If the handler returns a positive value, exit the - * mainloop. */ + * mainloop. + */ ret = handler->callback(handler->fd, events[i].events, handler->data, descr); if (ret == LXC_MAINLOOP_CLOSE) return 0; } - if (nfds == 0 && timeout_ms != 0) + if (nfds == 0) return 0; if (lxc_list_empty(&descr->handlers)) diff --git a/src/lxc/start.c b/src/lxc/start.c index 85a349923..eef483cb7 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -290,11 +290,10 @@ static int setup_signal_fd(sigset_t *oldmask) static int signal_handler(int fd, uint32_t events, void *data, struct lxc_epoll_descr *descr) { - struct signalfd_siginfo siginfo; - siginfo_t info; int ret; - pid_t *pid = data; - bool init_died = false; + siginfo_t info; + struct signalfd_siginfo siginfo; + struct lxc_handler *hdlr = data; ret = read(fd, &siginfo, sizeof(siginfo)); if (ret < 0) { @@ -309,34 +308,34 @@ static int signal_handler(int fd, uint32_t events, void *data, /* Check whether init is running. */ info.si_pid = 0; - ret = waitid(P_PID, *pid, &info, WEXITED | WNOWAIT | WNOHANG); - if (ret == 0 && info.si_pid == *pid) - init_died = true; + ret = waitid(P_PID, hdlr->pid, &info, WEXITED | WNOWAIT | WNOHANG); + if (ret == 0 && info.si_pid == hdlr->pid) + hdlr->init_died = true; if (siginfo.ssi_signo != SIGCHLD) { - kill(*pid, siginfo.ssi_signo); - INFO("Forwarded signal %d to pid %d.", siginfo.ssi_signo, *pid); - return init_died ? 1 : 0; + kill(hdlr->pid, siginfo.ssi_signo); + INFO("Forwarded signal %d to pid %d.", siginfo.ssi_signo, hdlr->pid); + return hdlr->init_died ? LXC_MAINLOOP_CLOSE : 0; } if (siginfo.ssi_code == CLD_STOPPED) { INFO("Container init process was stopped."); - return init_died ? 1 : 0; + return hdlr->init_died ? LXC_MAINLOOP_CLOSE : 0; } else if (siginfo.ssi_code == CLD_CONTINUED) { INFO("Container init process was continued."); - return init_died ? 1 : 0; + return hdlr->init_died ? LXC_MAINLOOP_CLOSE : 0; } /* More robustness, protect ourself from a SIGCHLD sent * by a process different from the container init. */ - if (siginfo.ssi_pid != *pid) { - NOTICE("Received SIGCHLD from pid %d instead of container init %d.", siginfo.ssi_pid, *pid); - return init_died ? 1 : 0; + if (siginfo.ssi_pid != hdlr->pid) { + NOTICE("Received SIGCHLD from pid %d instead of container init %d.", siginfo.ssi_pid, hdlr->pid); + return hdlr->init_died ? LXC_MAINLOOP_CLOSE : 0; } - DEBUG("Container init process %d exited.", *pid); - return 1; + DEBUG("Container init process %d exited.", hdlr->pid); + return LXC_MAINLOOP_CLOSE; } static int lxc_serve_state_clients(const char *name, @@ -458,35 +457,39 @@ int lxc_set_state(const char *name, struct lxc_handler *handler, int lxc_poll(const char *name, struct lxc_handler *handler) { + int ret; + struct lxc_epoll_descr descr, descr_console; int sigfd = handler->sigfd; - int pid = handler->pid; - struct lxc_epoll_descr descr; - if (lxc_mainloop_open(&descr)) { - ERROR("Failed to create LXC mainloop."); + ret = lxc_mainloop_open(&descr); + if (ret < 0) { + ERROR("Failed to create mainloop"); goto out_sigfd; } - if (lxc_mainloop_add_handler(&descr, sigfd, signal_handler, &pid)) { - ERROR("Failed to add signal handler with file descriptor %d to LXC mainloop.", sigfd); - goto out_mainloop_open; + ret = lxc_mainloop_open(&descr_console); + if (ret < 0) { + ERROR("Failed to create console mainloop"); + goto out_mainloop; } - if (lxc_console_mainloop_add(&descr, handler->conf)) { - ERROR("Failed to add console handler to LXC mainloop."); - goto out_mainloop_open; + ret = lxc_mainloop_add_handler(&descr, sigfd, signal_handler, handler); + if (ret < 0) { + ERROR("Failed to add signal handler for %d to mainloop", sigfd); + goto out_mainloop_console; } - if (lxc_cmd_mainloop_add(name, &descr, handler)) { - ERROR("Failed to add command handler to LXC mainloop."); - goto out_mainloop_open; + ret = lxc_console_mainloop_add(&descr, handler->conf); + if (ret < 0) { + ERROR("Failed to add console handlers to mainloop"); + goto out_mainloop_console; } if (handler->conf->need_utmp_watch) { #if HAVE_LIBCAP if (lxc_utmp_mainloop_add(&descr, handler)) { ERROR("Failed to add utmp handler to LXC mainloop."); - goto out_mainloop_open; + goto out_mainloop_console; } #else DEBUG("Not starting utmp handler as CAP_SYS_BOOT cannot be dropped without capabilities support."); @@ -494,11 +497,33 @@ int lxc_poll(const char *name, struct lxc_handler *handler) } TRACE("lxc mainloop is ready"); - return lxc_mainloop(&descr, -1); + ret = lxc_console_mainloop_add(&descr_console, handler->conf); + if (ret < 0) { + ERROR("Failed to add console handlers to console mainloop"); + goto out_mainloop_console; + } + + ret = lxc_cmd_mainloop_add(name, &descr, handler); + if (ret < 0) { + ERROR("Failed to add command handler to mainloop"); + goto out_mainloop_console; + } + + TRACE("Mainloop is ready"); -out_mainloop_open: + ret = lxc_mainloop(&descr, -1); + if (ret < 0) + return -1; + if (handler->init_died) + return lxc_mainloop(&descr_console, 0); + return ret; + +out_mainloop: lxc_mainloop_close(&descr); +out_mainloop_console: + lxc_mainloop_close(&descr_console); + out_sigfd: close(sigfd); diff --git a/src/lxc/start.h b/src/lxc/start.h index 19e14a7b1..63706b466 100644 --- a/src/lxc/start.h +++ b/src/lxc/start.h @@ -82,6 +82,9 @@ struct lxc_handler { /* The child's pid. */ pid_t pid; + /* Whether the child has already exited. */ + bool init_died; + /* The signal mask prior to setting up the signal file descriptor. */ sigset_t oldmask;