]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
lxc_init: add custom argument parser
authorRicardoSanchezA <ricardo.sanchez@utexas.edu>
Wed, 6 Dec 2017 15:30:44 +0000 (09:30 -0600)
committerChristian Brauner <christian.brauner@ubuntu.com>
Sun, 17 Dec 2017 15:23:38 +0000 (16:23 +0100)
lxc_init.c should not depend on tools/arguments.{c,h}, thus it needs its own custom argument parser

Signed-off-by: RicardoSanchezA <ricardo.sanchez@utexas.edu>
src/lxc/Makefile.am
src/lxc/lxc_init.c

index a3ff3c955cd2a1a4c3a17d8ff5db181ec1c31aeb..f5a6b42d86b534727b84da112190a903a25a233e 100644 (file)
@@ -261,7 +261,7 @@ lxc_device_SOURCES = tools/lxc_device.c tools/arguments.c
 lxc_execute_SOURCES = tools/lxc_execute.c tools/arguments.c
 lxc_freeze_SOURCES = tools/lxc_freeze.c tools/arguments.c
 lxc_info_SOURCES = tools/lxc_info.c tools/arguments.c
-init_lxc_SOURCES = lxc_init.c tools/arguments.c
+init_lxc_SOURCES = lxc_init.c
 lxc_monitor_SOURCES = tools/lxc_monitor.c tools/arguments.c
 lxc_ls_SOURCES = tools/lxc_ls.c tools/arguments.c
 lxc_copy_SOURCES = tools/lxc_copy.c tools/arguments.c
index 823dc18508a5ecb6eac8a571f788b178010897bf..bc613f7e231c0fd658006ea84db2cfd6af30b3c8 100644 (file)
  */
 
 #define _GNU_SOURCE
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
 #include <errno.h>
-#include <signal.h>
+#include <getopt.h>
 #include <libgen.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/wait.h>
-#include <getopt.h>
+#include <ctype.h>
 
 #include <lxc/lxccontainer.h>
 
-#include "log.h"
 #include "error.h"
 #include "initutils.h"
+#include "log.h"
+#include "utils.h"
+#include "version.h"
 
-lxc_log_define(lxc_init, lxc);
-
-static int quiet;
+/* option keys for long only options */
+#define OPT_USAGE 0x1000
+#define OPT_VERSION OPT_USAGE - 1
 
-static const struct option options[] = {
-       { "name",        required_argument, NULL, 'n' },
-       { "logpriority", required_argument, NULL, 'l' },
-       { "quiet",       no_argument,       NULL, 'q' },
-       { "lxcpath",     required_argument, NULL, 'P' },
-       { 0, 0, 0, 0 },
-};
+lxc_log_define(lxc_init, lxc);
 
 static sig_atomic_t was_interrupted = 0;
 
@@ -59,114 +57,137 @@ static void interrupt_handler(int sig)
                was_interrupted = sig;
 }
 
-static void usage(void) {
-       fprintf(stderr, "Usage: lxc-init [OPTION]...\n\n"
-               "Common options :\n"
-               "  -n, --name=NAME          NAME of the container\n"
-               "  -l, --logpriority=LEVEL  Set log priority to LEVEL\n"
-               "  -q, --quiet              Don't produce any output\n"
-               "  -P, --lxcpath=PATH       Use specified container path\n"
-               "  -?, --help               Give this help list\n"
-               "\n"
-               "Mandatory or optional arguments to long options are also mandatory or optional\n"
-               "for any corresponding short options.\n"
-               "\n"
-               "NOTE: lxc-init is intended for use by lxc internally\n"
-               "      and does not need to be run by hand\n\n");
-}
+static struct option long_options[] = {
+           { "name",        required_argument, 0, 'n'         },
+           { "help",        no_argument,       0, 'h'         },
+           { "usage",       no_argument,       0, OPT_USAGE   },
+           { "version",     no_argument,       0, OPT_VERSION },
+           { "quiet",       no_argument,       0, 'q'         },
+           { "logfile",     required_argument, 0, 'o'         },
+           { "logpriority", required_argument, 0, 'l'         },
+           { "lxcpath",     required_argument, 0, 'P'         },
+           { 0,             0,                 0, 0           }
+       };
+static char short_options[] = "n:hqo:l:P:";
+
+struct arguments {
+       const struct option *options;
+       const char *shortopts;
+
+       const char *name;
+       char *log_file;
+       char *log_priority;
+       bool quiet;
+       const char *lxcpath;
+
+       /* remaining arguments */
+       char *const *argv;
+       int argc;
+};
+
+static int arguments_parse(struct arguments *my_args, int argc,
+                              char *const argv[]);
+
+static struct arguments my_args = {
+       .options   = long_options,
+       .shortopts = short_options
+};
 
 int main(int argc, char *argv[])
 {
+       int i, ret;
        pid_t pid, sid;
-       int err;
-       char **aargv;
-       sigset_t mask, omask;
        struct sigaction act;
-       int i, have_status = 0, shutdown = 0;
-       int opt;
-       char *lxcpath = NULL, *name = NULL, *logpriority = NULL;
        struct lxc_log log;
+       sigset_t mask, omask;
+       int have_status = 0, shutdown = 0;
 
-       while ((opt = getopt_long(argc, argv, "n:l:qP:", options, NULL)) != -1) {
-               switch(opt) {
-               case 'n':
-                       name = optarg;
-                       break;
-               case 'l':
-                       logpriority = optarg;
-                       break;
-               case 'q':
-                       quiet = 1;
-                       break;
-               case 'P':
-                       lxcpath = optarg;
-                       break;
-               default: /* '?' */
-                       usage();
-                       exit(EXIT_FAILURE);
-               }
-       }
+       if (arguments_parse(&my_args, argc, argv))
+               exit(EXIT_FAILURE);
 
-       log.name = name;
-       log.file = name ? NULL : "none";
-       log.level = logpriority;
-       log.prefix = basename(argv[0]);
-       log.quiet = quiet;
-       log.lxcpath = lxcpath;
+       log.prefix = "lxc-init";
+       log.name = my_args.name;
+       log.file = my_args.log_file;
+       log.level = my_args.log_priority;
+       log.quiet = my_args.quiet;
+       log.lxcpath = my_args.lxcpath;
 
-       err = lxc_log_init(&log);
-       if (err < 0)
+       ret = lxc_log_init(&log);
+       if (ret < 0)
                exit(EXIT_FAILURE);
        lxc_log_options_no_override();
 
-       if (!argv[optind]) {
-               ERROR("Missing command to launch");
+       if (!my_args.argc) {
+               ERROR("Please specify a command to execute");
                exit(EXIT_FAILURE);
        }
 
-       aargv = &argv[optind];
-
-       /*
-        * mask all the signals so we are safe to install a
-        * signal handler and to fork
+       /* Mask all the signals so we are safe to install a signal handler and
+        * to fork.
         */
-       if (sigfillset(&mask) ||
-           sigdelset(&mask, SIGILL) ||
-           sigdelset(&mask, SIGSEGV) ||
-           sigdelset(&mask, SIGBUS) ||
-           sigprocmask(SIG_SETMASK, &mask, &omask)) {
-               SYSERROR("Failed to set signal mask");
+       ret = sigfillset(&mask);
+       if (ret < 0)
                exit(EXIT_FAILURE);
-       }
 
-       if (sigfillset(&act.sa_mask) ||
-           sigdelset(&act.sa_mask, SIGILL) ||
-           sigdelset(&act.sa_mask, SIGSEGV) ||
-           sigdelset(&act.sa_mask, SIGBUS) ||
-           sigdelset(&act.sa_mask, SIGSTOP) ||
-           sigdelset(&act.sa_mask, SIGKILL)) {
-               ERROR("Failed to set signal");
+       ret = sigdelset(&mask, SIGILL);
+       if (ret < 0)
                exit(EXIT_FAILURE);
-       }
+
+       ret = sigdelset(&mask, SIGSEGV);
+       if (ret < 0)
+               exit(EXIT_FAILURE);
+
+       ret = sigdelset(&mask, SIGBUS);
+       if (ret < 0)
+               exit(EXIT_FAILURE);
+
+       ret = sigprocmask(SIG_SETMASK, &mask, &omask);
+       if (ret < 0)
+               exit(EXIT_FAILURE);
+
+       ret = sigfillset(&act.sa_mask);
+       if (ret < 0)
+               exit(EXIT_FAILURE);
+
+       ret = sigdelset(&act.sa_mask, SIGILL);
+       if (ret < 0)
+               exit(EXIT_FAILURE);
+
+       ret = sigdelset(&act.sa_mask, SIGSEGV);
+       if (ret < 0)
+               exit(EXIT_FAILURE);
+
+       ret = sigdelset(&act.sa_mask, SIGBUS);
+       if (ret < 0)
+               exit(EXIT_FAILURE);
+
+       ret = sigdelset(&act.sa_mask, SIGSTOP);
+       if (ret < 0)
+               exit(EXIT_FAILURE);
+
+       ret = sigdelset(&act.sa_mask, SIGKILL);
+       if (ret < 0)
+               exit(EXIT_FAILURE);
+
        act.sa_flags = 0;
        act.sa_handler = interrupt_handler;
 
        for (i = 1; i < NSIG; i++) {
-               /* Exclude some signals: ILL, SEGV and BUS are likely to
-                * reveal a bug and we want a core. STOP and KILL cannot be
-                * handled anyway: they're here for documentation. 32 and 33
-                * are not defined.
+               /* Exclude some signals: ILL, SEGV and BUS are likely to reveal
+                * a bug and we want a core. STOP and KILL cannot be handled
+                * anyway: they're here for documentation. 32 and 33 are not
+                * defined.
                 */
-               if (i == SIGILL ||
-                   i == SIGSEGV ||
-                   i == SIGBUS ||
-                   i == SIGSTOP ||
-                   i == SIGKILL ||
-                   i == 32 || i == 33)
+               if (i == SIGILL || i == SIGSEGV || i == SIGBUS ||
+                   i == SIGSTOP || i == SIGKILL || i == 32 || i == 33)
                        continue;
 
-               if (sigaction(i, &act, NULL) && errno != EINVAL) {
-                       SYSERROR("Failed to sigaction");
+               ret = sigaction(i, &act, NULL);
+               if (ret < 0) {
+                       if (errno == EINVAL)
+                               continue;
+
+                       SYSERROR("Failed to change signal action");
                        exit(EXIT_FAILURE);
                }
        }
@@ -178,13 +199,19 @@ int main(int argc, char *argv[])
                exit(EXIT_FAILURE);
 
        if (!pid) {
-               int ret;
-
                /* restore default signal handlers */
-               for (i = 1; i < NSIG; i++)
-                       signal(i, SIG_DFL);
+               for (i = 1; i < NSIG; i++) {
+                       sighandler_t sigerr;
+                       sigerr = signal(i, SIG_DFL);
+                       if (sigerr == SIG_ERR) {
+                               DEBUG("%s - Failed to reset to default action "
+                                     "for signal \"%d\": %d", strerror(errno),
+                                     i, pid);
+                       }
+               }
 
-               if (sigprocmask(SIG_SETMASK, &omask, NULL)) {
+               ret = sigprocmask(SIG_SETMASK, &omask, NULL);
+               if (ret < 0) {
                        SYSERROR("Failed to set signal mask");
                        exit(EXIT_FAILURE);
                }
@@ -196,23 +223,31 @@ int main(int argc, char *argv[])
                 if (ioctl(STDIN_FILENO, TIOCSCTTY, 0) < 0)
                         DEBUG("Failed to set controlling terminal");
 
-               ret = execvp(aargv[0], aargv);
-               ERROR("Failed to exec: '%s' : %s", aargv[0], strerror(errno));
+               NOTICE("Exec'ing \"%s\"", my_args.argv[0]);
+
+               ret = execvp(my_args.argv[0], my_args.argv);
+               ERROR("%s - Failed to exec \"%s\"", strerror(errno), my_args.argv[0]);
                exit(ret);
        }
 
-       /* let's process the signals now */
-       if (sigdelset(&omask, SIGALRM) ||
-           sigprocmask(SIG_SETMASK, &omask, NULL)) {
+       INFO("Attempting to set proc title to \"init\"");
+       setproctitle("init");
+
+       /* Let's process the signals now. */
+       ret = sigdelset(&omask, SIGALRM);
+       if (ret < 0)
+               exit(EXIT_FAILURE);
+
+       ret = sigprocmask(SIG_SETMASK, &omask, NULL);
+       if (ret < 0) {
                SYSERROR("Failed to set signal mask");
                exit(EXIT_FAILURE);
        }
 
-       /* no need of other inherited fds but stderr */
-       close(fileno(stdin));
-       close(fileno(stdout));
+       /* No need of other inherited fds but stderr. */
+       close(STDIN_FILENO);
+       close(STDOUT_FILENO);
 
-       err = EXIT_SUCCESS;
        for (;;) {
                int status;
                pid_t waited_pid;
@@ -224,47 +259,157 @@ int main(int argc, char *argv[])
                case SIGTERM:
                        if (!shutdown) {
                                shutdown = 1;
-                               kill(-1, SIGTERM);
+                               ret = kill(-1, SIGTERM);
+                               if (ret < 0)
+                                       DEBUG("%s - Failed to send SIGTERM to "
+                                             "all children", strerror(errno));
                                alarm(1);
                        }
                        break;
                case SIGALRM:
-                       kill(-1, SIGKILL);
+                       ret = kill(-1, SIGKILL);
+                       if (ret < 0)
+                               DEBUG("%s - Failed to send SIGKILL to all "
+                                     "children", strerror(errno));
                        break;
                default:
-                       kill(pid, was_interrupted);
+                       ret = kill(pid, was_interrupted);
+                       if (ret < 0)
+                               DEBUG("%s - Failed to send signal \"%d\" to "
+                                     "%d", strerror(errno), was_interrupted, pid);
                        break;
                }
+               ret = EXIT_SUCCESS;
 
                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));
+                       ERROR("%s - Failed to wait on child %d",
+                             strerror(errno), pid);
                        goto out;
                }
 
-               /* reset timer each time a process exited */
+               /* Reset timer each time a process exited. */
                if (shutdown)
                        alarm(1);
 
-               /*
-                * keep the exit code of started application
-                * (not wrapped pid) and continue to wait for
-                * the end of the orphan group.
+               /* Keep the exit code of the started application (not wrapped
+                * pid) and continue to wait for the end of the orphan group.
                 */
                if (waited_pid == pid && !have_status) {
-                       err = lxc_error_set_and_log(waited_pid, status);
+                       ret = lxc_error_set_and_log(waited_pid, status);
                        have_status = 1;
                }
        }
 out:
-       if (err < 0)
+       if (ret < 0)
                exit(EXIT_FAILURE);
-       exit(err);
+       exit(ret);
+}
+
+
+
+static void print_usage(const struct option longopts[])
+
+{
+       fprintf(stderr, "Usage: lxc-init [-n|--name=NAME] [-h|--help] [--usage] [--version] \n\
+               [-q|--quiet] [-o|--logfile=LOGFILE] [-l|--logpriority=LOGPRIORITY] [-P|--lxcpath=LXCPATH]\n");
+       exit(0);
+}
+
+static void print_version()
+{
+       printf("%s%s\n", LXC_VERSION, LXC_DEVEL ? "-devel" : "");
+       exit(0);
+}
+
+static void print_help()
+{
+       fprintf(stderr, "\
+Usage: lxc-init --name=NAME -- COMMAND\n\
+\n\
+  lxc-init start a COMMAND as PID 2 inside a container\n\
+\n\
+Options :\n\
+  -n, --name=NAME                  NAME of the container\n\
+  -o, --logfile=FILE               Output log to FILE instead of stderr\n\
+  -l, --logpriority=LEVEL          Set log priority to LEVEL\n\
+  -q, --quiet                      Don't produce any output\n\
+  -P, --lxcpath=PATH               Use specified container path\n\
+  -?, --help                       Give this help list\n\
+      --usage                      Give a short usage message\n\
+      --version                    Print the version number\n\
+\n\
+Mandatory or optional arguments to long options are also mandatory or optional\n\
+for any corresponding short options.\n\
+\n\
+See the lxc-init man page for further information.\n\n");
+
+}
+
+static int arguments_parse(struct arguments *args, int argc,
+                              char *const argv[])
+{
+       while (true) {
+               int c;
+               int index = 0;
+
+               c = getopt_long(argc, argv, args->shortopts, args->options, &index);
+               if (c == -1)
+                       break;
+               switch (c) {
+               case 'n':
+                       args->name = optarg;
+                       break;
+               case 'o':
+                       args->log_file = optarg;
+                       break;
+               case 'l':
+                       args->log_priority = optarg;
+                       break;
+               case 'q':
+                       args->quiet = true;
+                       break;
+               case 'P':
+                       remove_trailing_slashes(optarg);
+                       args->lxcpath = optarg;
+                       break;
+               case OPT_USAGE:
+                       print_usage(args->options);
+               case OPT_VERSION:
+                       print_version();
+               case '?':
+                       print_help();
+                       exit(EXIT_FAILURE);
+               case 'h':
+                       print_help();
+                       exit(EXIT_SUCCESS);
+               }
+       }
+
+       /*
+        * Reclaim the remaining command arguments
+        */
+       args->argv = &argv[optind];
+       args->argc = argc - optind;
+
+       /* If no lxcpath was given, use default */
+       if (!args->lxcpath) {
+               args->lxcpath = lxc_global_config_value("lxc.lxcpath");
+       }
+
+       /* Check the command options */
+       if (!args->name) {
+               if(!args->quiet)
+                       fprintf(stderr, "lxc-init: missing container name, use --name option\n");
+               return -1;
+       }
+
+       return 0;
 }