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 <gk...@fr.ibm.com> Signed-off-by: Cedric Le Goater <c...@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); } ------------------------------------------------------------------------------ The demand for IT networking professionals continues to grow, and the demand for specialized networking skills is growing even more rapidly. Take a complimentary Learning@Cisco Self-Assessment and learn about Cisco certifications, training, and career opportunities. http://p.sf.net/sfu/cisco-dev2dev _______________________________________________ Lxc-devel mailing list Lxc-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/lxc-devel