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"
"\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;
}
e = getenv(var);
if (!e) {
- static bool warned = false;
-
+ /* $UNIFIED_CGROUP_HIERARCHY has been renamed to $SYSTEMD_NSPAWN_UNIFIED_HIERARCHY. */
var = "UNIFIED_CGROUP_HIERARCHY";
e = getenv(var);
- if (e && !warned) {
- log_info("$UNIFIED_CGROUP_HIERARCHY has been renamed to $SYSTEMD_NSPAWN_UNIFIED_HIERARCHY.");
- warned = true;
- }
}
if (!isempty(e)) {
return 0;
}
+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;
};
int c, r;
- const char *p;
uint64_t plus = 0, minus = 0;
bool mask_all_settings = false, mask_no_settings = false;
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Network interface name not valid: %s", optarg);
+ r = test_network_interface_initialized(optarg);
+ if (r < 0)
+ return r;
+
if (strv_extend(&arg_network_interfaces, optarg) < 0)
return log_oom();
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"MACVLAN network interface name not valid: %s", optarg);
+ r = test_network_interface_initialized(optarg);
+ if (r < 0)
+ return r;
+
if (strv_extend(&arg_network_macvlan, optarg) < 0)
return log_oom();
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"IPVLAN network interface name not valid: %s", optarg);
+ r = test_network_interface_initialized(optarg);
+ if (r < 0)
+ return r;
+
if (strv_extend(&arg_network_ipvlan, optarg) < 0)
return log_oom();
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)
if (arg_userns_chown && arg_volatile_mode != VOLATILE_NO)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--volatile= and --private-users-chown may not be combined.");
- /* If --network-namespace-path is given with any other network-related option, we need to error out,
- * to avoid conflicts between different network options. */
+ /* If --network-namespace-path is given with any other network-related option (except --private-network),
+ * we need to error out, to avoid conflicts between different network options. */
if (arg_network_namespace_path &&
(arg_network_interfaces || arg_network_macvlan ||
arg_network_ipvlan || arg_network_veth_extra ||
arg_network_bridge || arg_network_zone ||
- arg_network_veth || arg_private_network))
+ arg_network_veth))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--network-namespace-path= cannot be combined with other network options.");
if (arg_network_bridge && arg_network_zone)
static int setup_journal(const char *directory) {
_cleanup_free_ char *d = NULL;
+ char id[SD_ID128_STRING_MAX];
const char *dirname, *p, *q;
sd_id128_t this_id;
- char id[33];
bool try;
int r;
FDSet *fds) {
_cleanup_free_ char *home = NULL;
- char as_uuid[37];
+ char as_uuid[ID128_UUID_STRING_MAX];
size_t n_env = 1;
const char *envp[] = {
"PATH=" DEFAULT_PATH_COMPAT,
"/",
arg_custom_mounts,
arg_n_custom_mounts,
- false,
- 0,
0,
arg_selinux_apifs_context,
- true);
+ MOUNT_NON_ROOT_ONLY | MOUNT_IN_USERNS);
if (r < 0)
return r;
int netns_fd) {
_cleanup_close_ int fd = -1;
+ const char *p;
pid_t pid;
ssize_t l;
int r;
return r;
directory = "/run/systemd/nspawn-root";
-
- } else if (!dissected_image) {
- /* Turn directory into bind mount (we need that so that we can move the bind mount to root
- * later on). */
- r = mount_verbose(LOG_ERR, directory, directory, NULL, MS_BIND|MS_REC, NULL);
- if (r < 0)
- return r;
}
r = setup_pivot_root(
r = setup_volatile_mode(
directory,
arg_volatile_mode,
- arg_userns_mode != USER_NAMESPACE_NO,
arg_uid_shift,
- arg_uid_range,
arg_selinux_apifs_context);
if (r < 0)
return r;
+ r = mount_custom(
+ directory,
+ arg_custom_mounts,
+ arg_n_custom_mounts,
+ arg_uid_shift,
+ arg_selinux_apifs_context,
+ MOUNT_ROOT_ONLY);
+ if (r < 0)
+ return r;
+
+ /* Make sure we always have a mount that we can move to root later on. */
+ if (!path_is_mount_point(directory, NULL, 0)) {
+ r = mount_verbose(LOG_ERR, directory, directory, NULL, MS_BIND|MS_REC, NULL);
+ if (r < 0)
+ return r;
+ }
+
if (dissected_image) {
/* Now we know the uid shift, let's now mount everything else that might be in the image. */
r = dissected_image_mount(dissected_image, directory, arg_uid_shift,
* inside the container that create a new mount namespace.
* See https://github.com/systemd/systemd/issues/3860
* Further submounts (such as /dev) done after this will inherit the
- * shared propagation mode. */
+ * shared propagation mode.
+ *
+ * IMPORTANT: Do not overmount the root directory anymore from now on to
+ * enable moving the root directory mount to root later on.
+ * https://github.com/systemd/systemd/issues/3847#issuecomment-562735251
+ */
r = mount_verbose(LOG_ERR, NULL, directory, NULL, MS_SHARED|MS_REC, NULL);
if (r < 0)
return r;
return r;
(void) dev_setup(directory, arg_uid_shift, arg_uid_shift);
- (void) make_inaccessible_nodes(directory, arg_uid_shift, arg_uid_shift);
+
+ p = prefix_roota(directory, "/run/systemd");
+ (void) make_inaccessible_nodes(p, arg_uid_shift, arg_uid_shift);
r = setup_pts(directory);
if (r < 0)
directory,
arg_custom_mounts,
arg_n_custom_mounts,
- arg_userns_mode != USER_NAMESPACE_NO,
arg_uid_shift,
- arg_uid_range,
arg_selinux_apifs_context,
- false);
+ MOUNT_NON_ROOT_ONLY);
if (r < 0)
return r;
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 */
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;
goto finish;
}
- r = loop_device_make_by_path(arg_image, arg_read_only ? O_RDONLY : O_RDWR, &loop);
+ r = loop_device_make_by_path(arg_image, arg_read_only ? O_RDONLY : O_RDWR, LO_FLAGS_PARTSCAN, &loop);
if (r < 0) {
log_error_errno(r, "Failed to set up loopback block device: %m");
goto finish;