]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
mainloop: capture output of short-lived init procs
authorChristian Brauner <christian.brauner@ubuntu.com>
Mon, 25 Dec 2017 13:53:40 +0000 (14:53 +0100)
committerChristian Brauner <christian.brauner@ubuntu.com>
Tue, 2 Jan 2018 00:15:00 +0000 (01:15 +0100)
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 <christian.brauner@ubuntu.com>
src/lxc/mainloop.c
src/lxc/start.c
src/lxc/start.h

index a7383d632f40dcc0273ba96335bdb40c3476b73f..c102295ef3afdb4578da0b21222fcb0b0c33163a 100644 (file)
  * 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 <stdio.h>
-#include <string.h>
-#include <stdlib.h>
+
 #include <errno.h>
 #include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 #include <sys/epoll.h>
 
@@ -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))
index 85a349923e3f7a6724f87e8ad78372ecac44cb45..eef483cb75cec0ce437b3c29fca10d08331c6fd6 100644 (file)
@@ -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);
 
index 19e14a7b144678f2a2118ac5a847129ccb8f32b7..63706b466d7033e7e9fc4a18dc1aa426a8e01700 100644 (file)
@@ -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;