diff options
Diffstat (limited to 'src/nspawn')
-rw-r--r-- | src/nspawn/nspawn-cgroup.c | 1 | ||||
-rw-r--r-- | src/nspawn/nspawn-mount.c | 17 | ||||
-rw-r--r-- | src/nspawn/nspawn-patch-uid.c | 7 | ||||
-rw-r--r-- | src/nspawn/nspawn-register.c | 10 | ||||
-rw-r--r-- | src/nspawn/nspawn-register.h | 2 | ||||
-rw-r--r-- | src/nspawn/nspawn-seccomp.c | 11 | ||||
-rw-r--r-- | src/nspawn/nspawn-settings.c | 16 | ||||
-rw-r--r-- | src/nspawn/nspawn-setuid.c | 1 | ||||
-rw-r--r-- | src/nspawn/nspawn-stub-pid1.c | 8 | ||||
-rw-r--r-- | src/nspawn/nspawn.c | 295 | ||||
-rw-r--r-- | src/nspawn/test-patch-uid.c | 2 |
11 files changed, 221 insertions, 149 deletions
diff --git a/src/nspawn/nspawn-cgroup.c b/src/nspawn/nspawn-cgroup.c index 0462b46413..f5048d9473 100644 --- a/src/nspawn/nspawn-cgroup.c +++ b/src/nspawn/nspawn-cgroup.c @@ -3,6 +3,7 @@ #include <sys/mount.h> #include "alloc-util.h" +#include "cgroup-setup.h" #include "fd-util.h" #include "fileio.h" #include "format-util.h" diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index 7fae2117a2..30b09e3333 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -636,7 +636,7 @@ int mount_all(const char *dest, if (!tmpfs_tmp && (bool)(mount_table[k].mount_settings & MOUNT_APPLY_TMPFS_TMP)) continue; - r = chase_symlinks(mount_table[k].where, dest, CHASE_NONEXISTENT|CHASE_PREFIX_ROOT, &where); + r = chase_symlinks(mount_table[k].where, dest, CHASE_NONEXISTENT|CHASE_PREFIX_ROOT, &where, NULL); if (r < 0) return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, mount_table[k].where); @@ -706,8 +706,9 @@ static int parse_mount_bind_options(const char *options, unsigned long *mount_fl else if (streq(word, "norbind")) flags &= ~MS_REC; else { - log_error("Invalid bind mount option: %s", word); - return -EINVAL; + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Invalid bind mount option: %s", + word); } } @@ -736,7 +737,7 @@ static int mount_bind(const char *dest, CustomMount *m) { if (stat(m->source, &source_st) < 0) return log_error_errno(errno, "Failed to stat %s: %m", m->source); - r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &where); + r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &where, NULL); if (r < 0) return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, m->destination); if (r > 0) { /* Path exists already? */ @@ -797,7 +798,7 @@ static int mount_tmpfs( assert(dest); assert(m); - r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &where); + r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &where, NULL); if (r < 0) return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, m->destination); if (r == 0) { /* Doesn't exist yet? */ @@ -837,7 +838,7 @@ static int mount_overlay(const char *dest, CustomMount *m) { assert(dest); assert(m); - r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &where); + r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &where, NULL); if (r < 0) return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, m->destination); if (r == 0) { /* Doesn't exist yet? */ @@ -880,7 +881,7 @@ static int mount_inaccessible(const char *dest, CustomMount *m) { assert(dest); assert(m); - r = chase_symlinks_and_stat(m->destination, dest, CHASE_PREFIX_ROOT, &where, &st); + r = chase_symlinks_and_stat(m->destination, dest, CHASE_PREFIX_ROOT, &where, &st, NULL); if (r < 0) { log_full_errno(m->graceful ? LOG_DEBUG : LOG_ERR, r, "Failed to resolve %s/%s: %m", dest, m->destination); return m->graceful ? 0 : r; @@ -908,7 +909,7 @@ static int mount_arbitrary(const char *dest, CustomMount *m) { assert(dest); assert(m); - r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &where); + r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &where, NULL); if (r < 0) return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, m->destination); if (r == 0) { /* Doesn't exist yet? */ diff --git a/src/nspawn/nspawn-patch-uid.c b/src/nspawn/nspawn-patch-uid.c index 4885744cfc..fc591e2725 100644 --- a/src/nspawn/nspawn-patch-uid.c +++ b/src/nspawn/nspawn-patch-uid.c @@ -1,11 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include <fcntl.h> -#include <linux/magic.h> -#if HAVE_ACL -#include <sys/acl.h> -#endif -#include <sys/stat.h> #include <sys/statvfs.h> #include <sys/vfs.h> #include <unistd.h> @@ -14,7 +9,7 @@ #include "dirent-util.h" #include "fd-util.h" #include "fs-util.h" -#include "missing.h" +#include "missing_magic.h" #include "nspawn-def.h" #include "nspawn-patch-uid.h" #include "stat-util.h" diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c index 8e2c329665..9b7ca5e3dd 100644 --- a/src/nspawn/nspawn-register.c +++ b/src/nspawn/nspawn-register.c @@ -209,7 +209,7 @@ int register_machine( return 0; } -int terminate_machine( +int unregister_machine( sd_bus *bus, const char *machine_name) { @@ -223,13 +223,13 @@ int terminate_machine( "org.freedesktop.machine1", "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", - "TerminateMachine", + "UnregisterMachine", &error, NULL, "s", machine_name); if (r < 0) - log_debug("Failed to terminate machine: %s", bus_error_message(&error, r)); + log_debug("Failed to unregister machine: %s", bus_error_message(&error, r)); return 0; } @@ -258,7 +258,7 @@ int allocate_scope( if (r < 0) return log_error_errno(r, "Could not watch job: %m"); - r = unit_name_mangle_with_suffix(machine_name, 0, ".scope", &scope); + r = unit_name_mangle_with_suffix(machine_name, "as machine name", 0, ".scope", &scope); if (r < 0) return log_error_errno(r, "Failed to mangle scope name: %m"); @@ -350,7 +350,7 @@ int terminate_scope( _cleanup_free_ char *scope = NULL; int r; - r = unit_name_mangle_with_suffix(machine_name, 0, ".scope", &scope); + r = unit_name_mangle_with_suffix(machine_name, "to terminate", 0, ".scope", &scope); if (r < 0) return log_error_errno(r, "Failed to mangle scope name: %m"); diff --git a/src/nspawn/nspawn-register.h b/src/nspawn/nspawn-register.h index 65a3ae85a7..07cca7fadc 100644 --- a/src/nspawn/nspawn-register.h +++ b/src/nspawn/nspawn-register.h @@ -8,7 +8,7 @@ #include "nspawn-mount.h" int register_machine(sd_bus *bus, const char *machine_name, pid_t pid, const char *directory, sd_id128_t uuid, int local_ifindex, const char *slice, CustomMount *mounts, unsigned n_mounts, int kill_signal, char **properties, sd_bus_message *properties_message, bool keep_unit, const char *service); -int terminate_machine(sd_bus *bus, const char *machine_name); +int unregister_machine(sd_bus *bus, const char *machine_name); int allocate_scope(sd_bus *bus, const char *machine_name, pid_t pid, const char *slice, CustomMount *mounts, unsigned n_mounts, int kill_signal, char **properties, sd_bus_message *properties_message); int terminate_scope(sd_bus *bus, const char *machine_name); diff --git a/src/nspawn/nspawn-seccomp.c b/src/nspawn/nspawn-seccomp.c index 9222f2bc84..f94f131f22 100644 --- a/src/nspawn/nspawn-seccomp.c +++ b/src/nspawn/nspawn-seccomp.c @@ -123,6 +123,7 @@ static int seccomp_add_default_syscall_filter( * @cpu-emulation * @keyring (NB: keyring is not namespaced!) * @obsolete + * @pkey * @swap * * bpf (NB: bpffs is not namespaced!) @@ -134,18 +135,14 @@ static int seccomp_add_default_syscall_filter( * nfsservctl * open_by_handle_at * perf_event_open - * pkey_alloc - * pkey_free - * pkey_mprotect * quotactl */ }; - int r; - size_t i; char **p; + int r; - for (i = 0; i < ELEMENTSOF(whitelist); i++) { + for (size_t i = 0; i < ELEMENTSOF(whitelist); i++) { if (whitelist[i].capability != 0 && (cap_list_retain & (1ULL << whitelist[i].capability)) == 0) continue; @@ -155,7 +152,7 @@ static int seccomp_add_default_syscall_filter( } STRV_FOREACH(p, syscall_whitelist) { - r = seccomp_add_syscall_filter_item(ctx, *p, SCMP_ACT_ALLOW, syscall_blacklist, false); + r = seccomp_add_syscall_filter_item(ctx, *p, SCMP_ACT_ALLOW, syscall_blacklist, true); if (r < 0) log_warning_errno(r, "Failed to add rule for system call %s on %s, ignoring: %m", *p, seccomp_arch_to_string(arch)); diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c index 3a99736813..5fb5b49bbc 100644 --- a/src/nspawn/nspawn-settings.c +++ b/src/nspawn/nspawn-settings.c @@ -275,13 +275,17 @@ int config_parse_capability( if (r == 0) break; - r = capability_from_name(word); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse capability, ignoring: %s", word); - continue; - } + if (streq(word, "all")) + u = (uint64_t) -1; + else { + r = capability_from_name(word); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse capability, ignoring: %s", word); + continue; + } - u |= UINT64_C(1) << r; + u |= UINT64_C(1) << r; + } } if (u == 0) diff --git a/src/nspawn/nspawn-setuid.c b/src/nspawn/nspawn-setuid.c index 3c302d6487..cb2b2272b6 100644 --- a/src/nspawn/nspawn-setuid.c +++ b/src/nspawn/nspawn-setuid.c @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include <fcntl.h> -#include <grp.h> #include <sys/types.h> #include <unistd.h> diff --git a/src/nspawn/nspawn-stub-pid1.c b/src/nspawn/nspawn-stub-pid1.c index ebf4f0f523..d86dd23185 100644 --- a/src/nspawn/nspawn-stub-pid1.c +++ b/src/nspawn/nspawn-stub-pid1.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include <sys/ioctl.h> #include <sys/reboot.h> #include <sys/wait.h> #include <sys/prctl.h> @@ -9,7 +10,6 @@ #include "exit-status.h" #include "fd-util.h" #include "log.h" -#include "missing.h" #include "nspawn-stub-pid1.h" #include "process-util.h" #include "signal-util.h" @@ -53,6 +53,12 @@ int stub_pid1(sd_id128_t uuid) { assert_se(sigfillset(&fullmask) >= 0); assert_se(sigprocmask(SIG_BLOCK, &fullmask, &oldmask) >= 0); + /* Surrender the terminal this stub may control so that child processes can have a controlling terminal + * without resorting to setsid hacks. */ + r = ioctl(STDIN_FILENO, TIOCNOTTY); + if (r < 0 && errno != ENOTTY) + return log_error_errno(errno, "Failed to surrender controlling terminal: %m"); + pid = fork(); if (pid < 0) return log_error_errno(errno, "Failed to fork child pid: %m"); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 2aec8041f0..873a76596f 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -1,22 +1,15 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #if HAVE_BLKID -#include <blkid.h> #endif #include <errno.h> #include <getopt.h> -#include <grp.h> #include <linux/fs.h> #include <linux/loop.h> -#include <pwd.h> -#include <sched.h> #if HAVE_SELINUX #include <selinux/selinux.h> #endif -#include <signal.h> -#include <stdio.h> #include <stdlib.h> -#include <string.h> #include <sys/file.h> #include <sys/personality.h> #include <sys/prctl.h> @@ -58,7 +51,7 @@ #include "machine-image.h" #include "macro.h" #include "main-func.h" -#include "missing.h" +#include "missing_sched.h" #include "mkdir.h" #include "mount-util.h" #include "mountpoint-util.h" @@ -103,6 +96,7 @@ #include "terminal-util.h" #include "tmpfile-util.h" #include "umask-util.h" +#include "unit-name.h" #include "user-util.h" #include "util.h" @@ -261,6 +255,30 @@ STATIC_DESTRUCTOR_REGISTER(arg_seccomp, seccomp_releasep); STATIC_DESTRUCTOR_REGISTER(arg_cpu_set, cpu_set_reset); STATIC_DESTRUCTOR_REGISTER(arg_sysctl, strv_freep); +static int handle_arg_console(const char *arg) { + if (streq(arg, "help")) { + puts("interactive\n" + "read-only\n" + "passive\n" + "pipe"); + return 0; + } + + if (streq(arg, "interactive")) + arg_console_mode = CONSOLE_INTERACTIVE; + else if (streq(arg, "read-only")) + arg_console_mode = CONSOLE_READ_ONLY; + else if (streq(arg, "passive")) + arg_console_mode = CONSOLE_PASSIVE; + else if (streq(arg, "pipe")) + arg_console_mode = CONSOLE_PIPE; + else + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown console mode: %s", optarg); + + arg_settings_mask |= SETTING_CONSOLE_MODE; + return 1; +} + static int help(void) { _cleanup_free_ char *link = NULL; int r; @@ -272,7 +290,7 @@ static int help(void) { return log_oom(); printf("%1$s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n" - "Spawn a command or OS in a light-weight container.\n\n" + "%5$sSpawn a command or OS in a light-weight container.%6$s\n\n" " -h --help Show this help\n" " --version Print version string\n" " -q --quiet Do not show status information\n" @@ -387,7 +405,9 @@ static int help(void) { "\nSee the %2$s for details.\n" , program_invocation_short_name , link - , ansi_underline(), ansi_normal()); + , ansi_underline(), ansi_normal() + , ansi_highlight(), ansi_normal() + ); return 0; } @@ -412,15 +432,22 @@ static int custom_mount_check_all(void) { } static int detect_unified_cgroup_hierarchy_from_environment(void) { - const char *e; + const char *e, *var = "SYSTEMD_NSPAWN_UNIFIED_HIERARCHY"; int r; /* Allow the user to control whether the unified hierarchy is used */ - e = getenv("UNIFIED_CGROUP_HIERARCHY"); - if (e) { + + e = getenv(var); + if (!e) { + /* $UNIFIED_CGROUP_HIERARCHY has been renamed to $SYSTEMD_NSPAWN_UNIFIED_HIERARCHY. */ + var = "UNIFIED_CGROUP_HIERARCHY"; + e = getenv(var); + } + + if (!isempty(e)) { r = parse_boolean(e); if (r < 0) - return log_error_errno(r, "Failed to parse $UNIFIED_CGROUP_HIERARCHY."); + return log_error_errno(r, "Failed to parse $%s: %m", var); if (r > 0) arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_ALL; else @@ -433,8 +460,8 @@ static int detect_unified_cgroup_hierarchy_from_environment(void) { static int detect_unified_cgroup_hierarchy_from_image(const char *directory) { int r; - /* Let's inherit the mode to use from the host system, but let's take into consideration what systemd in the - * image actually supports. */ + /* Let's inherit the mode to use from the host system, but let's take into consideration what systemd + * in the image actually supports. */ r = cg_all_unified(); if (r < 0) return log_error_errno(r, "Failed to determine whether we are in all unified mode."); @@ -467,58 +494,106 @@ static int detect_unified_cgroup_hierarchy_from_image(const char *directory) { return 0; } -static void parse_share_ns_env(const char *name, unsigned long ns_flag) { +static int parse_capability_spec(const char *spec, uint64_t *ret_mask) { + uint64_t mask = 0; + int r; + + for (;;) { + _cleanup_free_ char *t = NULL; + + r = extract_first_word(&spec, &t, ",", 0); + if (r < 0) + return log_error_errno(r, "Failed to parse capability %s.", t); + if (r == 0) + break; + + if (streq(t, "help")) { + for (int i = 0; i < capability_list_length(); i++) { + const char *name; + + name = capability_to_name(i); + if (name) + puts(name); + } + + return 0; /* quit */ + } + + if (streq(t, "all")) + mask = (uint64_t) -1; + else { + r = capability_from_name(t); + if (r < 0) + return log_error_errno(r, "Failed to parse capability %s.", t); + + mask |= 1ULL << r; + } + } + + *ret_mask = mask; + return 1; /* continue */ +} + +static int parse_share_ns_env(const char *name, unsigned long ns_flag) { int r; r = getenv_bool(name); if (r == -ENXIO) - return; + return 0; if (r < 0) - log_warning_errno(r, "Failed to parse %s from environment, defaulting to false.", name); + return log_error_errno(r, "Failed to parse $%s: %m", name); arg_clone_ns_flags = (arg_clone_ns_flags & ~ns_flag) | (r > 0 ? 0 : ns_flag); arg_settings_mask |= SETTING_CLONE_NS_FLAGS; + return 0; } -static void parse_mount_settings_env(void) { +static int parse_mount_settings_env(void) { const char *e; int r; r = getenv_bool("SYSTEMD_NSPAWN_TMPFS_TMP"); + if (r < 0 && r != -ENXIO) + return log_error_errno(r, "Failed to parse $SYSTEMD_NSPAWN_TMPFS_TMP: %m"); if (r >= 0) SET_FLAG(arg_mount_settings, MOUNT_APPLY_TMPFS_TMP, r > 0); - else if (r != -ENXIO) - log_warning_errno(r, "Failed to parse $SYSTEMD_NSPAWN_TMPFS_TMP, ignoring: %m"); e = getenv("SYSTEMD_NSPAWN_API_VFS_WRITABLE"); - if (!e) - return; - - if (streq(e, "network")) { + if (streq_ptr(e, "network")) arg_mount_settings |= MOUNT_APPLY_APIVFS_RO|MOUNT_APPLY_APIVFS_NETNS; - return; - } - r = parse_boolean(e); - if (r < 0) { - log_warning_errno(r, "Failed to parse SYSTEMD_NSPAWN_API_VFS_WRITABLE from environment, ignoring."); - return; + else if (e) { + r = parse_boolean(e); + if (r < 0) + return log_error_errno(r, "Failed to parse $SYSTEMD_NSPAWN_API_VFS_WRITABLE: %m"); + + SET_FLAG(arg_mount_settings, MOUNT_APPLY_APIVFS_RO, r == 0); + SET_FLAG(arg_mount_settings, MOUNT_APPLY_APIVFS_NETNS, false); } - SET_FLAG(arg_mount_settings, MOUNT_APPLY_APIVFS_RO, r == 0); - SET_FLAG(arg_mount_settings, MOUNT_APPLY_APIVFS_NETNS, false); + return 0; } -static void parse_environment(void) { +static int parse_environment(void) { const char *e; int r; - parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_IPC", CLONE_NEWIPC); - parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_PID", CLONE_NEWPID); - parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_UTS", CLONE_NEWUTS); - parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_SYSTEM", CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS); + r = parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_IPC", CLONE_NEWIPC); + if (r < 0) + return r; + r = parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_PID", CLONE_NEWPID); + if (r < 0) + return r; + r = parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_UTS", CLONE_NEWUTS); + if (r < 0) + return r; + r = parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_SYSTEM", CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS); + if (r < 0) + return r; - parse_mount_settings_env(); + r = parse_mount_settings_env(); + if (r < 0) + return r; /* SYSTEMD_NSPAWN_USE_CGNS=0 can be used to disable CLONE_NEWCGROUP use, * even if it is supported. If not supported, it has no effect. */ @@ -528,7 +603,7 @@ static void parse_environment(void) { r = getenv_bool("SYSTEMD_NSPAWN_USE_CGNS"); if (r < 0) { if (r != -ENXIO) - log_warning_errno(r, "Failed to parse $SYSTEMD_NSPAWN_USE_CGNS, ignoring: %m"); + return log_error_errno(r, "Failed to parse $SYSTEMD_NSPAWN_USE_CGNS: %m"); arg_use_cgns = true; } else { @@ -541,7 +616,7 @@ static void parse_environment(void) { if (e) arg_container_service_name = e; - detect_unified_cgroup_hierarchy_from_environment(); + return detect_unified_cgroup_hierarchy_from_environment(); } static int parse_argv(int argc, char *argv[]) { @@ -662,7 +737,6 @@ static int parse_argv(int argc, char *argv[]) { }; int c, r; - const char *p; uint64_t plus = 0, minus = 0; bool mask_all_settings = false, mask_no_settings = false; @@ -847,13 +921,17 @@ static int parse_argv(int argc, char *argv[]) { arg_settings_mask |= SETTING_MACHINE_ID; break; - case 'S': - r = free_and_strdup(&arg_slice, optarg); + case 'S': { + _cleanup_free_ char *mangled = NULL; + + r = unit_name_mangle_with_suffix(optarg, NULL, UNIT_NAME_MANGLE_WARN, ".slice", &mangled); if (r < 0) return log_oom(); + free_and_replace(arg_slice, mangled); arg_settings_mask |= SETTING_SLICE; break; + } case 'M': if (isempty(optarg)) @@ -900,37 +978,18 @@ static int parse_argv(int argc, char *argv[]) { case ARG_CAPABILITY: case ARG_DROP_CAPABILITY: { - p = optarg; - for (;;) { - _cleanup_free_ char *t = NULL; - - r = extract_first_word(&p, &t, ",", 0); - if (r < 0) - return log_error_errno(r, "Failed to parse capability %s.", t); - if (r == 0) - break; - - if (streq(t, "all")) { - if (c == ARG_CAPABILITY) - plus = (uint64_t) -1; - else - minus = (uint64_t) -1; - } else { - r = capability_from_name(t); - if (r < 0) - return log_error_errno(r, "Failed to parse capability %s.", t); - - if (c == ARG_CAPABILITY) - plus |= 1ULL << r; - else - minus |= 1ULL << r; - } - } + uint64_t m; + r = parse_capability_spec(optarg, &m); + if (r <= 0) + return r; + if (c == ARG_CAPABILITY) + plus |= m; + else + minus |= m; arg_settings_mask |= SETTING_CAPABILITY; break; } - case ARG_NO_NEW_PRIVILEGES: r = parse_boolean(optarg); if (r < 0) @@ -1369,29 +1428,16 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_CONSOLE: - if (streq(optarg, "interactive")) - arg_console_mode = CONSOLE_INTERACTIVE; - else if (streq(optarg, "read-only")) - arg_console_mode = CONSOLE_READ_ONLY; - else if (streq(optarg, "passive")) - arg_console_mode = CONSOLE_PASSIVE; - else if (streq(optarg, "pipe")) - arg_console_mode = CONSOLE_PIPE; - else if (streq(optarg, "help")) - puts("interactive\n" - "read-only\n" - "passive\n" - "pipe"); - else - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown console mode: %s", optarg); - - arg_settings_mask |= SETTING_CONSOLE_MODE; + r = handle_arg_console(optarg); + if (r <= 0) + return r; break; case 'P': case ARG_PIPE: - arg_console_mode = CONSOLE_PIPE; - arg_settings_mask |= SETTING_CONSOLE_MODE; + r = handle_arg_console("pipe"); + if (r <= 0) + return r; break; case ARG_NO_PAGER: @@ -1424,7 +1470,9 @@ static int parse_argv(int argc, char *argv[]) { arg_caps_retain = (arg_caps_retain | plus | (arg_private_network ? UINT64_C(1) << CAP_NET_ADMIN : 0)) & ~minus; /* Make sure to parse environment before we reset the settings mask below */ - parse_environment(); + r = parse_environment(); + if (r < 0) + return r; /* Load all settings from .nspawn files */ if (mask_no_settings) @@ -1440,6 +1488,25 @@ static int parse_argv(int argc, char *argv[]) { static int verify_arguments(void) { int r; + if (arg_start_mode == START_PID2 && arg_unified_cgroup_hierarchy == CGROUP_UNIFIED_UNKNOWN) { + /* If we are running the stub init in the container, we don't need to look at what the init + * in the container supports, because we are not using it. Let's immediately pick the right + * setting based on the host system configuration. + * + * We only do this, if the user didn't use an environment variable to override the detection. + */ + + r = cg_all_unified(); + if (r < 0) + return log_error_errno(r, "Failed to determine whether we are in all unified mode."); + if (r > 0) + arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_ALL; + else if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) > 0) + arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_SYSTEMD; + else + arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_NONE; + } + if (arg_userns_mode != USER_NAMESPACE_NO) arg_mount_settings |= MOUNT_USE_USERNS; @@ -1616,7 +1683,7 @@ static int setup_timezone(const char *dest) { if (m == TIMEZONE_OFF) return 0; - r = chase_symlinks("/etc", dest, CHASE_PREFIX_ROOT, &etc); + r = chase_symlinks("/etc", dest, CHASE_PREFIX_ROOT, &etc, NULL); if (r < 0) { log_warning_errno(r, "Failed to resolve /etc path in container, ignoring: %m"); return 0; @@ -1647,7 +1714,7 @@ static int setup_timezone(const char *dest) { return 0; /* Already pointing to the right place? Then do nothing .. */ check = strjoina(dest, "/usr/share/zoneinfo/", z); - r = chase_symlinks(check, dest, 0, NULL); + r = chase_symlinks(check, dest, 0, NULL, NULL); if (r < 0) log_debug_errno(r, "Timezone %s does not exist (or is not accessible) in container, not creating symlink: %m", z); else { @@ -1674,7 +1741,7 @@ static int setup_timezone(const char *dest) { _cleanup_free_ char *resolved = NULL; int found; - found = chase_symlinks(where, dest, CHASE_NONEXISTENT, &resolved); + found = chase_symlinks(where, dest, CHASE_NONEXISTENT, &resolved, NULL); if (found < 0) { log_warning_errno(found, "Failed to resolve /etc/localtime path in container, ignoring: %m"); return 0; @@ -1780,7 +1847,7 @@ static int setup_resolv_conf(const char *dest) { if (m == RESOLV_CONF_OFF) return 0; - r = chase_symlinks("/etc", dest, CHASE_PREFIX_ROOT, &etc); + r = chase_symlinks("/etc", dest, CHASE_PREFIX_ROOT, &etc, NULL); if (r < 0) { log_warning_errno(r, "Failed to resolve /etc path in container, ignoring: %m"); return 0; @@ -1804,7 +1871,7 @@ static int setup_resolv_conf(const char *dest) { _cleanup_free_ char *resolved = NULL; int found; - found = chase_symlinks(where, dest, CHASE_NONEXISTENT, &resolved); + found = chase_symlinks(where, dest, CHASE_NONEXISTENT, &resolved, NULL); if (found < 0) { log_warning_errno(found, "Failed to resolve /etc/resolv.conf path in container, ignoring: %m"); return 0; @@ -2333,7 +2400,8 @@ static int drop_capabilities(uid_t uid) { /* If we're not using OCI, proceed with mangled capabilities (so we don't error out) * in order to maintain the same behavior as systemd < 242. */ if (capability_quintet_mangle(&q)) - log_warning("Some capabilities will not be set because they are not in the current bounding set."); + log_full(arg_quiet ? LOG_DEBUG : LOG_WARNING, + "Some capabilities will not be set because they are not in the current bounding set."); } @@ -2681,12 +2749,11 @@ static int chase_symlinks_and_update(char **p, unsigned flags) { if (!*p) return 0; - r = chase_symlinks(*p, NULL, flags, &chased); + r = chase_symlinks(*p, NULL, flags, &chased, NULL); if (r < 0) return log_error_errno(r, "Failed to resolve path %s: %m", *p); - free_and_replace(*p, chased); - return r; /* r might be an fd here in case we ever use CHASE_OPEN in flags */ + return free_and_replace(*p, chased); } static int determine_uid_shift(const char *directory) { @@ -3725,6 +3792,7 @@ static int merge_settings(Settings *settings, const char *path) { if ((arg_settings_mask & SETTING_CAPABILITY) == 0) { uint64_t plus, minus; + uint64_t network_minus = 0; /* Note that we copy both the simple plus/minus caps here, and the full quintet from the * Settings structure */ @@ -3736,14 +3804,16 @@ static int merge_settings(Settings *settings, const char *path) { if (settings_private_network(settings)) plus |= UINT64_C(1) << CAP_NET_ADMIN; else - minus |= UINT64_C(1) << CAP_NET_ADMIN; + network_minus |= UINT64_C(1) << CAP_NET_ADMIN; } if (!arg_settings_trusted && plus != 0) { if (settings->capability != 0) log_warning("Ignoring Capability= setting, file %s is not trusted.", path); - } else + } else { + arg_caps_retain &= ~network_minus; arg_caps_retain |= plus; + } arg_caps_retain &= ~minus; @@ -4567,12 +4637,8 @@ static int run_container( } /* Kill if it is not dead yet anyway */ - if (bus) { - if (arg_register) - terminate_machine(bus, arg_machine); - else if (!arg_keep_unit) - terminate_scope(bus, arg_machine); - } + if (!arg_register && !arg_keep_unit && bus) + terminate_scope(bus, arg_machine); /* Normally redundant, but better safe than sorry */ (void) kill(*pid, SIGKILL); @@ -4580,6 +4646,10 @@ static int run_container( r = wait_for_container(*pid, &container_status); *pid = 0; + /* Tell machined that we are gone. */ + if (bus) + (void) unregister_machine(bus, arg_machine); + if (r < 0) /* We failed to wait for the container, or the container exited abnormally. */ return r; @@ -4720,7 +4790,7 @@ static int run(int argc, char *argv[]) { if (r < 0) goto finish; - r = cg_unified_flush(); + r = cg_unified(); if (r < 0) { log_error_errno(r, "Failed to determine whether the unified cgroups hierarchy is used: %m"); goto finish; @@ -4730,9 +4800,8 @@ static int run(int argc, char *argv[]) { if (r < 0) goto finish; - r = detect_unified_cgroup_hierarchy_from_environment(); - if (r < 0) - goto finish; + /* Reapply environment settings. */ + (void) detect_unified_cgroup_hierarchy_from_environment(); /* Ignore SIGPIPE here, because we use splice() on the ptyfwd stuff and that will generate SIGPIPE if * the result is closed. Note that the container payload child will reset signal mask+handler anyway, diff --git a/src/nspawn/test-patch-uid.c b/src/nspawn/test-patch-uid.c index b50f0990d8..a6829629b4 100644 --- a/src/nspawn/test-patch-uid.c +++ b/src/nspawn/test-patch-uid.c @@ -5,8 +5,8 @@ #include "log.h" #include "nspawn-patch-uid.h" #include "user-util.h" +#include "string-util.h" #include "tests.h" -#include "util.h" int main(int argc, char *argv[]) { uid_t shift, range; |