[lxc-devel] [PATCH 1/4] lxc-init: use lxc_mainloop

Greg Kurz gkurz at fr.ibm.com
Tue Nov 8 11:49:03 UTC 2011


The goal here is to be able to multiplex several event sources in
lxc-init. It will be a lot easier to add I/O driven features: for
example, a rexec-like service to start extra commands in a
container.

Signed-off-by: Greg Kurz <gkurz at fr.ibm.com>
Signed-off-by: Cedric Le Goater <clg at fr.ibm.com>
---

 src/lxc/error.c    |   23 ++++++
 src/lxc/error.h    |    3 +
 src/lxc/lxc_init.c |  206 ++++++++++++++++++++++++++++------------------------
 3 files changed, 137 insertions(+), 95 deletions(-)

diff --git a/src/lxc/error.c b/src/lxc/error.c
index 5ecfbac..e0ba96a 100644
--- a/src/lxc/error.c
+++ b/src/lxc/error.c
@@ -57,3 +57,26 @@ extern int  lxc_error_set_and_log(int pid, int status)
 
 	return ret;
 }
+
+int lxc_error_set_and_log_siginfo(siginfo_t *siginfo)
+{
+	int ret = 0;
+
+	if (siginfo->si_code == CLD_EXITED) {
+		if (siginfo->si_status) {
+			INFO("child <%d> ended on error (%d)",
+			     siginfo->si_pid, siginfo->si_status);
+			ret = siginfo->si_status;
+		}
+	} else if (siginfo->si_code == CLD_KILLED) {
+		INFO("child <%d> ended on signal (%d)",
+		     siginfo->si_pid, siginfo->si_signo);
+		ret = siginfo->si_signo + 128;
+	} else if (siginfo->si_code == CLD_DUMPED) {
+		INFO("child <%d> dumped core on signal (%d)",
+		     siginfo->si_pid, siginfo->si_signo);
+		ret = siginfo->si_signo + 128;
+	}
+
+	return ret;
+}
diff --git a/src/lxc/error.h b/src/lxc/error.h
index ef25fc3..4684ab5 100644
--- a/src/lxc/error.h
+++ b/src/lxc/error.h
@@ -23,6 +23,9 @@
 #ifndef __lxc_error_h
 #define __lxc_error_h
 
+#include <signal.h>
+
 extern int  lxc_error_set_and_log(int pid, int status);
+extern int  lxc_error_set_and_log_siginfo(siginfo_t *siginfo);
 
 #endif
diff --git a/src/lxc/lxc_init.c b/src/lxc/lxc_init.c
index a534b51..17704cd 100644
--- a/src/lxc/lxc_init.c
+++ b/src/lxc/lxc_init.c
@@ -30,6 +30,7 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <sys/signalfd.h>
 #define _GNU_SOURCE
 #include <getopt.h>
 
@@ -37,6 +38,7 @@
 #include "caps.h"
 #include "error.h"
 #include "utils.h"
+#include "mainloop.h"
 
 lxc_log_define(lxc_init, lxc);
 
@@ -47,23 +49,91 @@ static struct option options[] = {
 	{ 0, 0, 0, 0 },
 };
 
-static	int was_interrupted = 0;
+static pid_t child_pid;
+static int shutting_down;
+static int exit_status;
+static int orphan;
 
-int main(int argc, char *argv[])
+static int handle_child(void)
 {
+	for(;;) {
+		siginfo_t siginfo;
+
+		siginfo.si_pid = 0;
+
+		if (waitid(P_ALL, -1, &siginfo, WEXITED|WNOHANG) < 0) {
+			/* ECHILD means we're the last process */
+			if (errno != ECHILD)
+				ERROR("failed to wait child : %s",
+				      strerror(errno));
+			return 1;
+		}
+
+		/* No process exited */
+		if (!siginfo.si_pid)
+			return 0;
+
+		/* reset timer each time a process exited */
+		if (shutting_down)
+			alarm(1);
+
+		/*
+		 * keep the exit code of started application
+		 * (not wrapped pid) and continue to wait for
+		 * the end of the orphan group.
+		 */
+		if ((siginfo.si_pid != child_pid) || orphan)
+			continue;
+
+		orphan = 1;
 
-	void interrupt_handler(int sig)
-	{
-		if (!was_interrupted)
-			was_interrupted = sig;
+		exit_status =
+			lxc_error_set_and_log_siginfo(&siginfo);
 	}
+}
 
-	pid_t pid;
+static int handle_signal(int fd, void* data, struct lxc_epoll_descr *descr)
+{
+	struct signalfd_siginfo siginfo;
+
+	/* If we get woken up, we have at least one siginfo to read: no need
+	 * to check for errors.
+	 */
+	read(fd, &siginfo, sizeof(siginfo));
+
+	switch (siginfo.ssi_signo) {
+	case SIGTERM:
+		if (!shutting_down) {
+			shutting_down = 1;
+			kill(-1, SIGTERM);
+			alarm(1);
+		}
+		break;
+
+	case SIGALRM:
+		kill(-1, SIGKILL);
+		break;
+
+	case SIGCHLD:
+		return handle_child();
+		break;
+
+	default:
+		kill(child_pid, siginfo.ssi_signo);
+		break;
+	}
+
+	return 0;
+}
+
+int main(int argc, char *argv[])
+{
 	int nbargs = 0;
 	int err = -1;
 	char **aargv;
-	sigset_t mask, omask;
-	int i, shutdown = 0;
+	sigset_t mask;
+	struct lxc_epoll_descr mainloop_descr;
+	int signal_fd;
 
 	while (1) {
 		int ret = getopt_long_only(argc, argv, "", options, NULL);
@@ -90,44 +160,18 @@ int main(int argc, char *argv[])
 	aargv = &argv[optind];
 	argc -= nbargs;
 
-        /*
-	 * mask all the signals so we are safe to install a
-	 * signal handler and to fork
-	 */
-	sigfillset(&mask);
-	sigprocmask(SIG_SETMASK, &mask, &omask);
-
-	for (i = 1; i < NSIG; i++) {
-		struct sigaction act;
-
-		sigfillset(&act.sa_mask);
-		sigdelset(&mask, SIGILL);
-		sigdelset(&mask, SIGSEGV);
-		sigdelset(&mask, SIGBUS);
-		act.sa_flags = 0;
-		act.sa_handler = interrupt_handler;
-		sigaction(i, &act, NULL);
-	}
-
 	if (lxc_setup_fs())
 		exit(err);
 
 	if (lxc_caps_reset())
 		exit(err);
 
-	pid = fork();
+	child_pid = fork();
 
-	if (pid < 0)
+	if (child_pid < 0)
 		exit(err);
 
-	if (!pid) {
-
-		/* restore default signal handlers */
-		for (i = 1; i < NSIG; i++)
-			signal(i, SIG_DFL);
-
-		sigprocmask(SIG_SETMASK, &omask, NULL);
-
+	if (!child_pid) {
 		NOTICE("about to exec '%s'", aargv[0]);
 
 		execvp(aargv[0], aargv);
@@ -135,69 +179,41 @@ int main(int argc, char *argv[])
 		exit(err);
 	}
 
-	/* let's process the signals now */
-	sigdelset(&omask, SIGALRM);
-	sigprocmask(SIG_SETMASK, &omask, NULL);
-
 	/* no need of other inherited fds but stderr */
 	close(fileno(stdin));
 	close(fileno(stdout));
 
-	err = 0;
-	for (;;) {
-		int status;
-		int orphan = 0;
-		pid_t waited_pid;
-
-		switch (was_interrupted) {
-
-		case 0:
-			break;
-
-		case SIGTERM:
-			if (!shutdown) {
-				shutdown = 1;
-				kill(-1, SIGTERM);
-				alarm(1);
-			}
-			break;
-
-		case SIGALRM:
-			kill(-1, SIGKILL);
-			break;
+	/* All signals except the ones that are related to a really serious error
+	 * or the uncatchable ones will be handled by signalfd() and forwarded
+	 * to the container.
+	 */
+	sigfillset(&mask);
+	sigdelset(&mask, SIGILL);
+	sigdelset(&mask, SIGSEGV);
+	sigdelset(&mask, SIGBUS);
+	sigdelset(&mask, SIGSTOP);
+	sigdelset(&mask, SIGKILL);
+	sigprocmask(SIG_SETMASK, &mask, NULL);
+
+	signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
+	if (signal_fd < 0) {
+		SYSERROR("failed to create signal fd");
+		exit(err);
+	}
 
-		default:
-			kill(pid, was_interrupted);
-			break;
-		}
+	if (lxc_mainloop_open(&mainloop_descr)) {
+		ERROR("failed to create mainloop");
+		exit(err);
+	}
 
-		was_interrupted = 0;
-		waited_pid = wait(&status);
-		if (waited_pid < 0) {
-			if (errno == ECHILD)
-				goto out;
-			if (errno == EINTR)
-				continue;
-
-			ERROR("failed to wait child : %s",
-			      strerror(errno));
-			goto out;
-		}
+	if (lxc_mainloop_add_handler(&mainloop_descr, signal_fd, handle_signal,
+				     NULL)) {
+		ERROR("failed to install signal handler");
+		exit(err);
+	}
 
-		/* reset timer each time a process exited */
-		if (shutdown)
-			alarm(1);
+	if (lxc_mainloop(&mainloop_descr))
+		exit(err);
 
-		/*
-		 * keep the exit code of started application
-		 * (not wrapped pid) and continue to wait for
-		 * the end of the orphan group.
-		 */
-		if ((waited_pid != pid) || (orphan ==1))
-			continue;
-		orphan = 1;
-		err = lxc_error_set_and_log(waited_pid, status);
-	}
-out:
-	return err;
+	exit(exit_status);
 }





More information about the lxc-devel mailing list