From: Christian Brauner Date: Tue, 10 Oct 2017 14:53:25 +0000 (+0200) Subject: init: rework dumb init X-Git-Tag: lxc-3.0.0.beta1~217^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a6f151a7ce1c67f75663e9f23a1ae6e691443d15;p=thirdparty%2Flxc.git init: rework dumb init Signed-off-by: Christian Brauner --- diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index 9a429b8cf..b71992d75 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -298,7 +298,8 @@ endif if HAVE_STATIC_LIBCAP sbin_PROGRAMS += init.lxc.static -init_lxc_static_SOURCES = tools/lxc_init.c error.c log.c initutils.c caps.c +init_lxc_static_SOURCES = tools/lxc_init.c error.c log.c initutils.c caps.c \ + arguments.c if !HAVE_GETLINE if HAVE_FGETLN diff --git a/src/lxc/execute.c b/src/lxc/execute.c index ddfd9fadb..2f7f13747 100644 --- a/src/lxc/execute.c +++ b/src/lxc/execute.c @@ -64,33 +64,36 @@ static int execute_start(struct lxc_handler *handler, void* data) initpath = choose_init(NULL); if (!initpath) { - ERROR("Failed to find an lxc-init or init.lxc"); + ERROR("Failed to find an init.lxc or init.lxc.static"); goto out2; } argv[i++] = initpath; + + argv[i++] = "-n"; + argv[i++] = (char *)handler->name; + + if (lxc_log_has_valid_level()) { + argv[i++] = "-l"; + argv[i++] = (char *)lxc_log_priority_to_string(lxc_log_get_level()); + } + if (my_args->quiet) argv[i++] = "--quiet"; + if (!handler->conf->rootfs.path) { - argv[i++] = "--name"; - argv[i++] = (char *)handler->name; - argv[i++] = "--lxcpath"; + argv[i++] = "-P"; argv[i++] = (char *)handler->lxcpath; - - if (lxc_log_has_valid_level()) { - argv[i++] = "--logpriority"; - argv[i++] = (char *) - lxc_log_priority_to_string(lxc_log_get_level()); - } } + argv[i++] = "--"; for (j = 0; j < argc; j++) argv[i++] = my_args->argv[j]; argv[i++] = NULL; - NOTICE("exec'ing '%s'", my_args->argv[0]); + NOTICE("Exec'ing \"%s\"", my_args->argv[0]); execvp(argv[0], argv); - SYSERROR("failed to exec %s", argv[0]); + SYSERROR("Failed to exec %s", argv[0]); free(initpath); out2: free(argv); diff --git a/src/lxc/initutils.c b/src/lxc/initutils.c index c190d6d50..12c33630b 100644 --- a/src/lxc/initutils.c +++ b/src/lxc/initutils.c @@ -21,6 +21,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include + #include "initutils.h" #include "log.h" @@ -296,3 +298,107 @@ FILE *fopen_cloexec(const char *path, const char *mode) errno = saved_errno; return ret; } + +/* + * Sets the process title to the specified title. Note that this may fail if + * the kernel doesn't support PR_SET_MM_MAP (kernels <3.18). + */ +int setproctitle(char *title) +{ + static char *proctitle = NULL; + char buf[2048], *tmp; + FILE *f; + int i, len, ret = 0; + + /* We don't really need to know all of this stuff, but unfortunately + * PR_SET_MM_MAP requires us to set it all at once, so we have to + * figure it out anyway. + */ + unsigned long start_data, end_data, start_brk, start_code, end_code, + start_stack, arg_start, arg_end, env_start, env_end, + brk_val; + struct prctl_mm_map prctl_map; + + f = fopen_cloexec("/proc/self/stat", "r"); + if (!f) { + return -1; + } + + tmp = fgets(buf, sizeof(buf), f); + fclose(f); + if (!tmp) { + return -1; + } + + /* Skip the first 25 fields, column 26-28 are start_code, end_code, + * and start_stack */ + tmp = strchr(buf, ' '); + for (i = 0; i < 24; i++) { + if (!tmp) + return -1; + tmp = strchr(tmp+1, ' '); + } + if (!tmp) + return -1; + + i = sscanf(tmp, "%lu %lu %lu", &start_code, &end_code, &start_stack); + if (i != 3) + return -1; + + /* Skip the next 19 fields, column 45-51 are start_data to arg_end */ + for (i = 0; i < 19; i++) { + if (!tmp) + return -1; + tmp = strchr(tmp+1, ' '); + } + + if (!tmp) + return -1; + + i = sscanf(tmp, "%lu %lu %lu %*u %*u %lu %lu", + &start_data, + &end_data, + &start_brk, + &env_start, + &env_end); + if (i != 5) + return -1; + + /* Include the null byte here, because in the calculations below we + * want to have room for it. */ + len = strlen(title) + 1; + + proctitle = realloc(proctitle, len); + if (!proctitle) + return -1; + + arg_start = (unsigned long) proctitle; + arg_end = arg_start + len; + + brk_val = syscall(__NR_brk, 0); + + prctl_map = (struct prctl_mm_map) { + .start_code = start_code, + .end_code = end_code, + .start_stack = start_stack, + .start_data = start_data, + .end_data = end_data, + .start_brk = start_brk, + .brk = brk_val, + .arg_start = arg_start, + .arg_end = arg_end, + .env_start = env_start, + .env_end = env_end, + .auxv = NULL, + .auxv_size = 0, + .exe_fd = -1, + }; + + ret = prctl(PR_SET_MM, PR_SET_MM_MAP, (long) &prctl_map, sizeof(prctl_map), 0); + if (ret == 0) + strcpy((char*)arg_start, title); + else + INFO("setting cmdline failed - %s", strerror(errno)); + + return ret; +} diff --git a/src/lxc/initutils.h b/src/lxc/initutils.h index c021fd617..ec44554e3 100644 --- a/src/lxc/initutils.h +++ b/src/lxc/initutils.h @@ -25,17 +25,16 @@ #define __LXC_INITUTILS_H #include -#include +#include +#include #include +#include +#include +#include +#include +#include #include #include -#include -#include -#include -#include -#include -#include - #include "config.h" @@ -44,11 +43,37 @@ #define DEFAULT_ZFSROOT "lxc" #define DEFAULT_RBDPOOL "lxc" +#ifndef PR_SET_MM +#define PR_SET_MM 35 +#endif + +#ifndef PR_SET_MM_MAP +#define PR_SET_MM_MAP 14 + +struct prctl_mm_map { + uint64_t start_code; + uint64_t end_code; + uint64_t start_data; + uint64_t end_data; + uint64_t start_brk; + uint64_t brk; + uint64_t start_stack; + uint64_t arg_start; + uint64_t arg_end; + uint64_t env_start; + uint64_t env_end; + uint64_t *auxv; + uint32_t auxv_size; + uint32_t exe_fd; +}; +#endif + extern void lxc_setup_fs(void); extern const char *lxc_global_config_value(const char *option_name); /* open a file with O_CLOEXEC */ extern void remove_trailing_slashes(char *p); -FILE *fopen_cloexec(const char *path, const char *mode); +extern FILE *fopen_cloexec(const char *path, const char *mode); +extern int setproctitle(char *title); #endif /* __LXC_INITUTILS_H */ diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 5e8ad00f9..fa144c55b 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -50,6 +50,7 @@ #include "confile_utils.h" #include "console.h" #include "criu.h" +#include "initutils.h" #include "log.h" #include "lxc.h" #include "lxccontainer.h" diff --git a/src/lxc/tools/lxc_init.c b/src/lxc/tools/lxc_init.c index 3aab2397e..56cc7e084 100644 --- a/src/lxc/tools/lxc_init.c +++ b/src/lxc/tools/lxc_init.c @@ -22,35 +22,27 @@ */ #define _GNU_SOURCE -#include -#include -#include #include -#include +#include #include +#include +#include +#include +#include +#include #include #include #include -#include #include -#include "log.h" +#include "arguments.h" #include "error.h" #include "initutils.h" +#include "log.h" lxc_log_define(lxc_init, lxc); -static int quiet; - -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 }, -}; - static sig_atomic_t was_interrupted = 0; static void interrupt_handler(int sig) @@ -59,114 +51,124 @@ 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 const struct option my_longopts[] = { + LXC_COMMON_OPTIONS +}; + +static int my_parser(struct lxc_arguments *args, int c, char *arg) +{ + return 0; } +static struct lxc_arguments my_args = { + .progname = "lxc-init", + .help = "\ +--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\ +", + .options = my_longopts, + .parser = my_parser, +}; + 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 (lxc_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.name = my_args.name; + log.file = my_args.log_file; + log.level = my_args.log_priority; + log.prefix = my_args.progname; + log.quiet = my_args.quiet; + log.lxcpath = my_args.lxcpath[0]; - 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 +180,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); } @@ -193,25 +201,31 @@ int main(int argc, char *argv[]) if (sid < 0) DEBUG("Failed to make child session leader"); - NOTICE("About to exec '%s'", aargv[0]); + NOTICE("Exec'ing \"%s\"", my_args.argv[0]); - ret = execvp(aargv[0], aargv); - 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); } - /* 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; @@ -223,47 +237,56 @@ 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); } diff --git a/src/lxc/utils.c b/src/lxc/utils.c index c9a60979a..4c886cadd 100644 --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -49,31 +49,6 @@ #include "namespace.h" #include "utils.h" -#ifndef PR_SET_MM -#define PR_SET_MM 35 -#endif - -#ifndef PR_SET_MM_MAP -#define PR_SET_MM_MAP 14 - -struct prctl_mm_map { - uint64_t start_code; - uint64_t end_code; - uint64_t start_data; - uint64_t end_data; - uint64_t start_brk; - uint64_t brk; - uint64_t start_stack; - uint64_t arg_start; - uint64_t arg_end; - uint64_t env_start; - uint64_t env_end; - uint64_t *auxv; - uint32_t auxv_size; - uint32_t exe_fd; -}; -#endif - #ifndef O_PATH #define O_PATH 010000000 #endif @@ -1438,110 +1413,6 @@ char *get_template_path(const char *t) return tpath; } -/* - * Sets the process title to the specified title. Note that this may fail if - * the kernel doesn't support PR_SET_MM_MAP (kernels <3.18). - */ -int setproctitle(char *title) -{ - static char *proctitle = NULL; - char buf[2048], *tmp; - FILE *f; - int i, len, ret = 0; - - /* We don't really need to know all of this stuff, but unfortunately - * PR_SET_MM_MAP requires us to set it all at once, so we have to - * figure it out anyway. - */ - unsigned long start_data, end_data, start_brk, start_code, end_code, - start_stack, arg_start, arg_end, env_start, env_end, - brk_val; - struct prctl_mm_map prctl_map; - - f = fopen_cloexec("/proc/self/stat", "r"); - if (!f) { - return -1; - } - - tmp = fgets(buf, sizeof(buf), f); - fclose(f); - if (!tmp) { - return -1; - } - - /* Skip the first 25 fields, column 26-28 are start_code, end_code, - * and start_stack */ - tmp = strchr(buf, ' '); - for (i = 0; i < 24; i++) { - if (!tmp) - return -1; - tmp = strchr(tmp+1, ' '); - } - if (!tmp) - return -1; - - i = sscanf(tmp, "%lu %lu %lu", &start_code, &end_code, &start_stack); - if (i != 3) - return -1; - - /* Skip the next 19 fields, column 45-51 are start_data to arg_end */ - for (i = 0; i < 19; i++) { - if (!tmp) - return -1; - tmp = strchr(tmp+1, ' '); - } - - if (!tmp) - return -1; - - i = sscanf(tmp, "%lu %lu %lu %*u %*u %lu %lu", - &start_data, - &end_data, - &start_brk, - &env_start, - &env_end); - if (i != 5) - return -1; - - /* Include the null byte here, because in the calculations below we - * want to have room for it. */ - len = strlen(title) + 1; - - proctitle = realloc(proctitle, len); - if (!proctitle) - return -1; - - arg_start = (unsigned long) proctitle; - arg_end = arg_start + len; - - brk_val = syscall(__NR_brk, 0); - - prctl_map = (struct prctl_mm_map) { - .start_code = start_code, - .end_code = end_code, - .start_stack = start_stack, - .start_data = start_data, - .end_data = end_data, - .start_brk = start_brk, - .brk = brk_val, - .arg_start = arg_start, - .arg_end = arg_end, - .env_start = env_start, - .env_end = env_end, - .auxv = NULL, - .auxv_size = 0, - .exe_fd = -1, - }; - - ret = prctl(PR_SET_MM, PR_SET_MM_MAP, (long) &prctl_map, sizeof(prctl_map), 0); - if (ret == 0) - strcpy((char*)arg_start, title); - else - INFO("setting cmdline failed - %s", strerror(errno)); - - return ret; -} - /* * @path: a pathname where / replaced with '\0'. * @offsetp: pointer to int showing which path segment was last seen. diff --git a/src/lxc/utils.h b/src/lxc/utils.h index 833ec4416..41c43827a 100644 --- a/src/lxc/utils.h +++ b/src/lxc/utils.h @@ -340,7 +340,6 @@ extern int print_to_file(const char *file, const char *content); extern bool switch_to_ns(pid_t pid, const char *ns); extern int is_dir(const char *path); extern char *get_template_path(const char *t); -extern int setproctitle(char *title); extern int safe_mount(const char *src, const char *dest, const char *fstype, unsigned long flags, const void *data, const char *rootfs);