[lxc-devel] [PATCH 1/3] lxc-init: use lxc_mainloop
Greg Kurz
gkurz at fr.ibm.com
Wed Oct 26 15:13:17 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