]> 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, 26 Dec 2017 10:47:15 +0000 (11:47 +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 d70764fa1ccd890477830c3f67fcc70c8ffaacfa..e4f8fb976f6d3f12da6dd7780b3f5fe4387fa3de 100644 (file)
@@ -299,11 +299,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) {
@@ -318,34 +317,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,
@@ -467,37 +466,61 @@ 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;
        }
 
-       TRACE("lxc mainloop is ready");
+       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;
+       }
 
-       return lxc_mainloop(&descr, -1);
+       ret = lxc_cmd_mainloop_add(name, &descr, handler);
+       if (ret < 0) {
+               ERROR("Failed to add command handler to mainloop");
+               goto out_mainloop_console;
+       }
 
-out_mainloop_open:
+       TRACE("Mainloop is ready");
+
+       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 0e01c3cf0f6c9dd3d730bd9efe09b3e451a1a4bb..bfa4c9062c855b1608715b918e15e4042da8398f 100644 (file)
@@ -85,6 +85,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;