Unverified Commit 0e1a161f by RicardoSanchezA Committed by Christian Brauner

lxc_init: add custom argument parser

lxc_init.c should not depend on tools/arguments.{c,h}, thus it needs its own custom argument parser Signed-off-by: 's avatarRicardoSanchezA <ricardo.sanchez@utexas.edu>
parent 228aeaca
...@@ -261,7 +261,7 @@ lxc_device_SOURCES = tools/lxc_device.c tools/arguments.c ...@@ -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_execute_SOURCES = tools/lxc_execute.c tools/arguments.c
lxc_freeze_SOURCES = tools/lxc_freeze.c tools/arguments.c lxc_freeze_SOURCES = tools/lxc_freeze.c tools/arguments.c
lxc_info_SOURCES = tools/lxc_info.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_monitor_SOURCES = tools/lxc_monitor.c tools/arguments.c
lxc_ls_SOURCES = tools/lxc_ls.c tools/arguments.c lxc_ls_SOURCES = tools/lxc_ls.c tools/arguments.c
lxc_copy_SOURCES = tools/lxc_copy.c tools/arguments.c lxc_copy_SOURCES = tools/lxc_copy.c tools/arguments.c
......
...@@ -22,34 +22,32 @@ ...@@ -22,34 +22,32 @@
*/ */
#define _GNU_SOURCE #define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h> #include <errno.h>
#include <signal.h> #include <getopt.h>
#include <libgen.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/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <getopt.h> #include <ctype.h>
#include <lxc/lxccontainer.h> #include <lxc/lxccontainer.h>
#include "log.h"
#include "error.h" #include "error.h"
#include "initutils.h" #include "initutils.h"
#include "log.h"
#include "utils.h"
#include "version.h"
lxc_log_define(lxc_init, lxc); /* option keys for long only options */
#define OPT_USAGE 0x1000
static int quiet; #define OPT_VERSION OPT_USAGE - 1
static const struct option options[] = { lxc_log_define(lxc_init, lxc);
{ "name", required_argument, NULL, 'n' },
{ "logpriority", required_argument, NULL, 'l' },
{ "quiet", no_argument, NULL, 'q' },
{ "lxcpath", required_argument, NULL, 'P' },
{ 0, 0, 0, 0 },
};
static sig_atomic_t was_interrupted = 0; static sig_atomic_t was_interrupted = 0;
...@@ -59,114 +57,137 @@ static void interrupt_handler(int sig) ...@@ -59,114 +57,137 @@ static void interrupt_handler(int sig)
was_interrupted = sig; was_interrupted = sig;
} }
static void usage(void) { static struct option long_options[] = {
fprintf(stderr, "Usage: lxc-init [OPTION]...\n\n" { "name", required_argument, 0, 'n' },
"Common options :\n" { "help", no_argument, 0, 'h' },
" -n, --name=NAME NAME of the container\n" { "usage", no_argument, 0, OPT_USAGE },
" -l, --logpriority=LEVEL Set log priority to LEVEL\n" { "version", no_argument, 0, OPT_VERSION },
" -q, --quiet Don't produce any output\n" { "quiet", no_argument, 0, 'q' },
" -P, --lxcpath=PATH Use specified container path\n" { "logfile", required_argument, 0, 'o' },
" -?, --help Give this help list\n" { "logpriority", required_argument, 0, 'l' },
"\n" { "lxcpath", required_argument, 0, 'P' },
"Mandatory or optional arguments to long options are also mandatory or optional\n" { 0, 0, 0, 0 }
"for any corresponding short options.\n" };
"\n" static char short_options[] = "n:hqo:l:P:";
"NOTE: lxc-init is intended for use by lxc internally\n"
" and does not need to be run by hand\n\n"); 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 main(int argc, char *argv[])
{ {
int i, ret;
pid_t pid, sid; pid_t pid, sid;
int err;
char **aargv;
sigset_t mask, omask;
struct sigaction act; struct sigaction act;
int i, have_status = 0, shutdown = 0;
int opt;
char *lxcpath = NULL, *name = NULL, *logpriority = NULL;
struct lxc_log log; 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) { if (arguments_parse(&my_args, argc, argv))
switch(opt) { exit(EXIT_FAILURE);
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);
}
}
log.name = name; log.prefix = "lxc-init";
log.file = name ? NULL : "none"; log.name = my_args.name;
log.level = logpriority; log.file = my_args.log_file;
log.prefix = basename(argv[0]); log.level = my_args.log_priority;
log.quiet = quiet; log.quiet = my_args.quiet;
log.lxcpath = lxcpath; log.lxcpath = my_args.lxcpath;
err = lxc_log_init(&log); ret = lxc_log_init(&log);
if (err < 0) if (ret < 0)
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
lxc_log_options_no_override(); lxc_log_options_no_override();
if (!argv[optind]) { if (!my_args.argc) {
ERROR("Missing command to launch"); ERROR("Please specify a command to execute");
exit(EXIT_FAILURE); 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) || ret = sigfillset(&mask);
sigdelset(&mask, SIGILL) || if (ret < 0)
sigdelset(&mask, SIGSEGV) ||
sigdelset(&mask, SIGBUS) ||
sigprocmask(SIG_SETMASK, &mask, &omask)) {
SYSERROR("Failed to set signal mask");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
}
if (sigfillset(&act.sa_mask) || ret = sigdelset(&mask, SIGILL);
sigdelset(&act.sa_mask, SIGILL) || if (ret < 0)
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");
exit(EXIT_FAILURE); 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_flags = 0;
act.sa_handler = interrupt_handler; act.sa_handler = interrupt_handler;
for (i = 1; i < NSIG; i++) { for (i = 1; i < NSIG; i++) {
/* Exclude some signals: ILL, SEGV and BUS are likely to /* Exclude some signals: ILL, SEGV and BUS are likely to reveal
* reveal a bug and we want a core. STOP and KILL cannot be * a bug and we want a core. STOP and KILL cannot be handled
* handled anyway: they're here for documentation. 32 and 33 * anyway: they're here for documentation. 32 and 33 are not
* are not defined. * defined.
*/ */
if (i == SIGILL || if (i == SIGILL || i == SIGSEGV || i == SIGBUS ||
i == SIGSEGV || i == SIGSTOP || i == SIGKILL || i == 32 || i == 33)
i == SIGBUS ||
i == SIGSTOP ||
i == SIGKILL ||
i == 32 || i == 33)
continue; continue;
if (sigaction(i, &act, NULL) && errno != EINVAL) { ret = sigaction(i, &act, NULL);
SYSERROR("Failed to sigaction"); if (ret < 0) {
if (errno == EINVAL)
continue;
SYSERROR("Failed to change signal action");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
...@@ -178,13 +199,19 @@ int main(int argc, char *argv[]) ...@@ -178,13 +199,19 @@ int main(int argc, char *argv[])
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
if (!pid) { if (!pid) {
int ret;
/* restore default signal handlers */ /* restore default signal handlers */
for (i = 1; i < NSIG; i++) for (i = 1; i < NSIG; i++) {
signal(i, SIG_DFL); 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"); SYSERROR("Failed to set signal mask");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -196,23 +223,31 @@ int main(int argc, char *argv[]) ...@@ -196,23 +223,31 @@ int main(int argc, char *argv[])
if (ioctl(STDIN_FILENO, TIOCSCTTY, 0) < 0) if (ioctl(STDIN_FILENO, TIOCSCTTY, 0) < 0)
DEBUG("Failed to set controlling terminal"); DEBUG("Failed to set controlling terminal");
ret = execvp(aargv[0], aargv); NOTICE("Exec'ing \"%s\"", my_args.argv[0]);
ERROR("Failed to exec: '%s' : %s", aargv[0], strerror(errno));
ret = execvp(my_args.argv[0], my_args.argv);
ERROR("%s - Failed to exec \"%s\"", strerror(errno), my_args.argv[0]);
exit(ret); exit(ret);
} }
/* let's process the signals now */ INFO("Attempting to set proc title to \"init\"");
if (sigdelset(&omask, SIGALRM) || setproctitle("init");
sigprocmask(SIG_SETMASK, &omask, NULL)) {
/* 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"); SYSERROR("Failed to set signal mask");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
/* no need of other inherited fds but stderr */ /* No need of other inherited fds but stderr. */
close(fileno(stdin)); close(STDIN_FILENO);
close(fileno(stdout)); close(STDOUT_FILENO);
err = EXIT_SUCCESS;
for (;;) { for (;;) {
int status; int status;
pid_t waited_pid; pid_t waited_pid;
...@@ -224,47 +259,157 @@ int main(int argc, char *argv[]) ...@@ -224,47 +259,157 @@ int main(int argc, char *argv[])
case SIGTERM: case SIGTERM:
if (!shutdown) { if (!shutdown) {
shutdown = 1; 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); alarm(1);
} }
break; break;
case SIGALRM: case SIGALRM:
kill(-1, SIGKILL); ret = kill(-1, SIGKILL);
if (ret < 0)
DEBUG("%s - Failed to send SIGKILL to all "
"children", strerror(errno));
break; break;
default: 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; break;
} }
ret = EXIT_SUCCESS;
was_interrupted = 0; was_interrupted = 0;
waited_pid = wait(&status); waited_pid = wait(&status);
if (waited_pid < 0) { if (waited_pid < 0) {
if (errno == ECHILD) if (errno == ECHILD)
goto out; goto out;
if (errno == EINTR) if (errno == EINTR)
continue; continue;
ERROR("Failed to wait child : %s", ERROR("%s - Failed to wait on child %d",
strerror(errno)); strerror(errno), pid);
goto out; goto out;
} }
/* reset timer each time a process exited */ /* Reset timer each time a process exited. */
if (shutdown) if (shutdown)
alarm(1); alarm(1);
/* /* Keep the exit code of the started application (not wrapped
* keep the exit code of started application * pid) and continue to wait for the end of the orphan group.
* (not wrapped pid) and continue to wait for
* the end of the orphan group.
*/ */
if (waited_pid == pid && !have_status) { 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; have_status = 1;
} }
} }
out: out:
if (err < 0) if (ret < 0)
exit(EXIT_FAILURE); 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;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment