/* * CORE * Copyright (c)2010-2012 the Boeing Company. * See the LICENSE file included in this distribution. * * author: Tom Goff * * vnoded_main.c * * vnoded daemon runs as PID 1 in the Linux namespace container and receives * and executes commands via a control channel. * */ #include #include #include #include #include #include #include "version.h" #include "vnode_server.h" #include "myerr.h" int verbose; static vnode_server_t *vnodeserver; struct option longopts[] = { {"version", no_argument, NULL, 'V'}, {"help", no_argument, NULL, 'h'}, { 0 } }; static void usage(int status, char *fmt, ...) { extern const char *__progname; va_list ap; FILE *output; va_start(ap, fmt); output = status ? stderr : stdout; fprintf(output, "\n"); if (fmt != NULL) { vfprintf(output, fmt, ap); fprintf(output, "\n\n"); } fprintf(output, "Usage: %s [-h|-V] [-v] [-n] [-C ] [-l ] " "[-p ] -c \n\n" "Linux namespace container server daemon runs as PID 1 in the " "container. \nNormally this process is launched automatically by the " "CORE daemon.\n\nOptions:\n" " -h, --help show this help message and exit\n" " -V, --version show version number and exit\n" " -v enable verbose logging\n" " -n do not create and run daemon within a new network namespace " "(for debug)\n" " -C change to the specified directory\n" " -l log output to the specified file\n" " -p write process id to the specified file\n" " -c establish the specified for receiving " "control commands\n", __progname); va_end(ap); exit(0); } static void sigexit(int signum) { WARNX("exiting due to signal: %d", signum); exit(0); return; } static void cleanup_sigchld(int signum) { /* nothing */ } static void cleanup() { static int incleanup = 0; if (incleanup) return; incleanup = 1; if (vnodeserver) { struct ev_loop *loop = vnodeserver->loop; vnode_delserver(vnodeserver); if (loop) ev_unloop(loop, EVUNLOOP_ALL); } /* don't use SIG_IGN here because receiving SIGCHLD is needed to * interrupt the sleep below in order to avoid long delays */ if (signal(SIGCHLD, cleanup_sigchld) == SIG_ERR) WARN("signal() failed"); if (getpid() == 1) { struct timespec delay = { .tv_sec = 2, .tv_nsec = 0, }; /* try to gracefully terminate all processes in this namespace * first */ kill(-1, SIGTERM); /* wait for child processes to terminate */ for (;;) { pid_t pid; int err; struct timespec rem; pid = waitpid(-1, NULL, WNOHANG); if (pid == -1) break; /* an error occurred */ if (pid != 0) continue; /* a child was reaped */ err = nanosleep(&delay, &rem); if (err == -1 && errno == EINTR) { delay = rem; continue; } /* force termination after delay */ kill(-1, SIGKILL); break; } } return; } int main(int argc, char *argv[]) { int newnetns = 1; char *ctrlchnlname = NULL, *logfilename = NULL, *chdirname = NULL; char *pidfilename = NULL; extern const char *__progname; for (;;) { int opt; if ((opt = getopt_long(argc, argv, "c:C:l:nvVhp:", longopts, NULL)) == -1) break; switch (opt) { case 'c': ctrlchnlname = optarg; break; case 'C': chdirname = optarg; break; case 'l': logfilename = optarg; break; case 'n': newnetns = 0; break; case 'p': pidfilename = optarg; break; case 'v': verbose++; break; case 'V': printf("%s version %s\n", __progname, CORE_VERSION); exit(0); case 'h': /* pass through */ default: usage(0, NULL); } } argc -= optind; argv += optind; if (ctrlchnlname == NULL) usage(1, "no control channel given"); for (; argc; argc--, argv++) WARNX("ignoring command line argument: '%s'", *argv); if (atexit(cleanup)) ERR(1, "atexit() failed"); if (signal(SIGTERM, sigexit) == SIG_ERR) ERR(1, "signal() failed"); if (signal(SIGINT, sigexit) == SIG_ERR) ERR(1, "signal() failed"); /* XXX others? */ vnodeserver = vnoded(newnetns, ctrlchnlname, logfilename, pidfilename, chdirname); if (vnodeserver == NULL) ERRX(1, "vnoded() failed"); ev_loop(vnodeserver->loop, 0); exit(0); }