diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml index 78fd0b3378d..90d0d664147 100644 --- a/man/org.freedesktop.systemd1.xml +++ b/man/org.freedesktop.systemd1.xml @@ -116,6 +116,11 @@ node /org/freedesktop/systemd1 { SetUnitProperties(in s name, in b runtime, in a(sv) properties); + BindMountUnit(in s name, + in s source, + in s destination, + in b read_only, + in b mkdir); RefUnit(in s name); UnrefUnit(in s name); StartTransientUnit(in s name, @@ -767,6 +772,8 @@ node /org/freedesktop/systemd1 { + + @@ -1156,6 +1163,9 @@ node /org/freedesktop/systemd1 { the "Try" flavor is used in which case a service that isn't running is not affected by the restart. The "ReloadOrRestart" flavors attempt a reload if the unit supports it and use a restart otherwise. + BindMountUnit() can be used to bind mount new files or directories into + a running service mount namespace. + KillUnit() may be used to kill (i.e. send a signal to) all processes of a unit. It takes the unit name, an enum who and a UNIX signal number to send. The who enum is one of @@ -2193,6 +2203,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { interface org.freedesktop.systemd1.Service { methods: + BindMount(in s source, + in s destination, + in b read_only, + in b mkdir); GetProcesses(out a(sus) processes); AttachProcesses(in s subcgroup, in au pids); @@ -3252,6 +3266,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + + @@ -3810,6 +3826,17 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + + Methods + + BindMount() implements the same operation as the respective method on the + Manager object (see above). However, this method operates on the service + object and hence does not take a unit name parameter. Invoking the methods directly on the Manager + object has the advantage of not requiring a GetUnit() call to get the unit object + for a specific unit name. Calling the methods on the Manager object is hence a round trip + optimization. + + Properties diff --git a/man/systemctl.xml b/man/systemctl.xml index bb702cb078b..a954e8727b6 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -550,6 +550,23 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err + + bind UNIT PATH [PATH] + + Bind mounts a file or directory from the host into the specified unit's view. The first path + argument is the source file or directory on the host, the second path argument is the destination file or + directory in the unit's view. When the latter is omitted, the destination path in the unit's view is the same as + the source path on the host. When combined with the switch, a ready-only bind + mount is created. When combined with the switch, the destination path is first created + before the mount is applied. Note that this option is currently only supported for units that run within a mount + namespace (e.g.: with , , etc.). This command supports bind + mounting directories, regular files, device nodes, AF_UNIX socket nodes, as well as FIFOs. + The bind mount is ephemeral, and it is undone as soon as the current unit process exists. + Note that the namespace mentioned here, where the bind mount will be added to, is the one where the main service + process runs, as other processes run in distinct namespaces (e.g.: , + , etc.) + + service-log-level SERVICE [LEVEL] @@ -2246,6 +2263,21 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err + + + + When used with bind, creates the destination file or directory before + applying the bind mount. Note that even though the name of this option suggests that it is suitable only for + directories, this option also creates the destination file node to mount over if the object to mount is not + a directory, but a regular file, device node, socket or FIFO. + + + + + + When used with bind, creates a read-only bind mount. + + diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 568839e0d9e..9adb6a298e8 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -285,6 +285,10 @@ the service with a private, minimal version of /dev/, combine this option with PrivateDevices=. + In order to allow propagating mounts at runtime in a safe manner, /run/systemd/propagate + on the host will be used to set up new mounts, and /run/host/incoming/ in the private namespace + will be used as an intermediate step to store them before being moved to the final mount point. + diff --git a/meson.build b/meson.build index e360ec95c9a..2caf069602c 100644 --- a/meson.build +++ b/meson.build @@ -2198,6 +2198,8 @@ public_programs += executable( 'src/systemctl/systemctl-log-setting.h', 'src/systemctl/systemctl-logind.c', 'src/systemctl/systemctl-logind.h', + 'src/systemctl/systemctl-mount.c', + 'src/systemctl/systemctl-mount.h', 'src/systemctl/systemctl-preset-all.c', 'src/systemctl/systemctl-preset-all.h', 'src/systemctl/systemctl-reset-failed.c', diff --git a/shell-completion/bash/systemctl.in b/shell-completion/bash/systemctl.in index 4bd80948d64..7e99ef4bf39 100644 --- a/shell-completion/bash/systemctl.in +++ b/shell-completion/bash/systemctl.in @@ -214,7 +214,7 @@ _systemctl () { list-timers list-units list-unit-files poweroff reboot rescue show-environment suspend get-default is-system-running preset-all' - [FILE]='link switch-root' + [FILE]='link switch-root bind' [TARGETS]='set-default' [MACHINES]='list-machines' [LOG_LEVEL]='log-level' diff --git a/shell-completion/zsh/_systemctl.in b/shell-completion/zsh/_systemctl.in index 10af6d5121e..c4ea78b3c10 100644 --- a/shell-completion/zsh/_systemctl.in +++ b/shell-completion/zsh/_systemctl.in @@ -31,6 +31,7 @@ "reset-failed:Reset failed state for all, one, or more units" "list-dependencies:Show unit dependency tree" "clean:Remove configuration, state, cache, logs or runtime data of units" + "bind:Bind mount a path from the host into a unit's namespace" ) local -a machine_commands=( @@ -378,6 +379,10 @@ done _files } +(( $+functions[_systemctl_bind] )) || _systemctl_bind() { + _files + } + # no systemctl completion for: # [STANDALONE]='daemon-reexec daemon-reload default # emergency exit halt kexec list-jobs list-units diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 3e1d609aa37..4b88f0d9f07 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -16,6 +16,7 @@ #include "dbus-job.h" #include "dbus-manager.h" #include "dbus-scope.h" +#include "dbus-service.h" #include "dbus-unit.h" #include "dbus.h" #include "env-util.h" @@ -725,6 +726,11 @@ static int method_set_unit_properties(sd_bus_message *message, void *userdata, s return method_generic_unit_operation(message, userdata, error, bus_unit_method_set_properties, GENERIC_UNIT_LOAD|GENERIC_UNIT_VALIDATE_LOADED); } +static int method_bind_mount_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { + /* Only add mounts on fully loaded units */ + return method_generic_unit_operation(message, userdata, error, bus_service_method_bind_mount, GENERIC_UNIT_VALIDATE_LOADED); +} + static int method_ref_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { /* Only allow reffing of fully loaded units, and make sure reffing a unit loads it. */ return method_generic_unit_operation(message, userdata, error, bus_unit_method_ref, GENERIC_UNIT_LOAD|GENERIC_UNIT_VALIDATE_LOADED); @@ -2760,6 +2766,16 @@ const sd_bus_vtable bus_manager_vtable[] = { NULL,, method_set_unit_properties, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("BindMountUnit", + "sssbb", + SD_BUS_PARAM(name) + SD_BUS_PARAM(source) + SD_BUS_PARAM(destination) + SD_BUS_PARAM(read_only) + SD_BUS_PARAM(mkdir), + NULL,, + method_bind_mount_unit, + SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD_WITH_NAMES("RefUnit", "s", SD_BUS_PARAM(name), diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c index 64f9d4ab362..6df93e44a43 100644 --- a/src/core/dbus-service.c +++ b/src/core/dbus-service.c @@ -11,11 +11,15 @@ #include "dbus-manager.h" #include "dbus-service.h" #include "dbus-util.h" +#include "execute.h" #include "exit-status.h" #include "fd-util.h" #include "fileio.h" +#include "locale-util.h" +#include "mount-util.h" #include "parse-util.h" #include "path-util.h" +#include "selinux-access.h" #include "service.h" #include "signal-util.h" #include "string-util.h" @@ -91,6 +95,79 @@ static int property_get_exit_status_set( return sd_bus_message_close_container(reply); } +int bus_service_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error) { + int read_only, make_file_or_directory; + const char *dest, *src, *propagate_directory; + Unit *u = userdata; + ExecContext *c; + pid_t unit_pid; + int r; + + assert(message); + assert(u); + + if (!MANAGER_IS_SYSTEM(u->manager)) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Adding bind mounts at runtime is only supported for system managers."); + + r = mac_selinux_unit_access_check(u, message, "start", error); + if (r < 0) + return r; + + r = sd_bus_message_read(message, "ssbb", &src, &dest, &read_only, &make_file_or_directory); + if (r < 0) + return r; + + if (!path_is_absolute(src) || !path_is_normalized(src)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and normalized."); + + if (isempty(dest)) + dest = src; + else if (!path_is_absolute(dest) || !path_is_normalized(dest)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and normalized."); + + r = bus_verify_manage_units_async_full( + u, + "bind-mount", + CAP_SYS_ADMIN, + N_("Authentication is required to bind mount on '$(unit)'."), + true, + message, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + if (u->type != UNIT_SERVICE) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit is not of type .service"); + + /* If it would be dropped at startup time, return an error. The context should always be available, but + * there's an assert in exec_needs_mount_namespace, so double-check just in case. */ + c = unit_get_exec_context(u); + if (!c) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot access unit execution context"); + if (path_startswith_strv(dest, c->inaccessible_paths)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s is not accessible to this unit", dest); + + /* Ensure that the unit was started in a private mount namespace */ + if (!exec_needs_mount_namespace(c, NULL, unit_get_exec_runtime(u))) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit not running in private mount namespace, cannot activate bind mount"); + + unit_pid = unit_main_pid(u); + if (unit_pid == 0 || !UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit is not running"); + + propagate_directory = strjoina("/run/systemd/propagate/", u->id); + r = bind_mount_in_namespace(unit_pid, + propagate_directory, + "/run/systemd/incoming/", + src, dest, read_only, make_file_or_directory); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to mount %s on %s in unit's namespace: %m", src, dest); + + return sd_bus_reply_method_return(message, NULL); +} + const sd_bus_vtable bus_service_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Service, type), SD_BUS_VTABLE_PROPERTY_CONST), @@ -146,6 +223,16 @@ const sd_bus_vtable bus_service_vtable[] = { BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPost", offsetof(Service, exec_command[SERVICE_EXEC_STOP_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_EX_COMMAND_LIST_VTABLE("ExecStopPostEx", offsetof(Service, exec_command[SERVICE_EXEC_STOP_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + SD_BUS_METHOD_WITH_NAMES("BindMount", + "ssbb", + SD_BUS_PARAM(source) + SD_BUS_PARAM(destination) + SD_BUS_PARAM(read_only) + SD_BUS_PARAM(mkdir), + NULL,, + bus_service_method_bind_mount, + SD_BUS_VTABLE_UNPRIVILEGED), + /* The following four are obsolete, and thus marked hidden here. They moved into the Unit interface */ SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_ratelimit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_ratelimit.burst), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), diff --git a/src/core/dbus-service.h b/src/core/dbus-service.h index 69311675c9b..5b7b7b757be 100644 --- a/src/core/dbus-service.h +++ b/src/core/dbus-service.h @@ -9,4 +9,5 @@ extern const sd_bus_vtable bus_service_vtable[]; int bus_service_set_property(Unit *u, const char *name, sd_bus_message *i, UnitWriteFlags flags, sd_bus_error *error); +int bus_service_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_service_commit_properties(Unit *u); diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 427152a757b..67cc58ee9e0 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -323,38 +323,6 @@ static int property_get_load_error( return sd_bus_message_append(reply, "(ss)", NULL, NULL); } -static int bus_verify_manage_units_async_full( - Unit *u, - const char *verb, - int capability, - const char *polkit_message, - bool interactive, - sd_bus_message *call, - sd_bus_error *error) { - - const char *details[9] = { - "unit", u->id, - "verb", verb, - }; - - if (polkit_message) { - details[4] = "polkit.message"; - details[5] = polkit_message; - details[6] = "polkit.gettext_domain"; - details[7] = GETTEXT_PACKAGE; - } - - return bus_verify_polkit_async( - call, - capability, - "org.freedesktop.systemd1.manage-units", - details, - interactive, - UID_INVALID, - &u->manager->polkit_registry, - error); -} - static const char *const polkit_message_for_job[_JOB_TYPE_MAX] = { [JOB_START] = N_("Authentication is required to start '$(unit)'."), [JOB_STOP] = N_("Authentication is required to stop '$(unit)'."), diff --git a/src/core/dbus-util.c b/src/core/dbus-util.c index d6223db305c..2d22bc699a3 100644 --- a/src/core/dbus-util.c +++ b/src/core/dbus-util.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "bus-polkit.h" #include "bus-util.h" #include "dbus-util.h" #include "parse-util.h" @@ -153,3 +154,35 @@ int bus_set_transient_usec_internal( return 1; } + +int bus_verify_manage_units_async_full( + Unit *u, + const char *verb, + int capability, + const char *polkit_message, + bool interactive, + sd_bus_message *call, + sd_bus_error *error) { + + const char *details[9] = { + "unit", u->id, + "verb", verb, + }; + + if (polkit_message) { + details[4] = "polkit.message"; + details[5] = polkit_message; + details[6] = "polkit.gettext_domain"; + details[7] = GETTEXT_PACKAGE; + } + + return bus_verify_polkit_async( + call, + capability, + "org.freedesktop.systemd1.manage-units", + details, + interactive, + UID_INVALID, + &u->manager->polkit_registry, + error); +} diff --git a/src/core/dbus-util.h b/src/core/dbus-util.h index 4e7c68e8439..e35c632d378 100644 --- a/src/core/dbus-util.h +++ b/src/core/dbus-util.h @@ -248,3 +248,4 @@ static inline int bus_set_transient_usec(Unit *u, const char *name, usec_t *p, s static inline int bus_set_transient_usec_fix_0(Unit *u, const char *name, usec_t *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error) { return bus_set_transient_usec_internal(u, name, p, true, message, flags, error); } +int bus_verify_manage_units_async_full(Unit *u, const char *verb, int capability, const char *polkit_message, bool interactive, sd_bus_message *call, sd_bus_error *error); diff --git a/src/core/execute.c b/src/core/execute.c index ee5f082783b..5f170db8d17 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -1987,13 +1987,12 @@ static int build_pass_environment(const ExecContext *c, char ***ret) { return 0; } -static bool exec_needs_mount_namespace( +bool exec_needs_mount_namespace( const ExecContext *context, const ExecParameters *params, const ExecRuntime *runtime) { assert(context); - assert(params); if (context->root_image) return true; @@ -2035,7 +2034,7 @@ static bool exec_needs_mount_namespace( return true; for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) { - if (!params->prefix[t]) + if (params && !params->prefix[t]) continue; if (!strv_isempty(context->directories[t].paths)) @@ -3115,7 +3114,7 @@ static int apply_mount_namespace( _cleanup_strv_free_ char **empty_directories = NULL; const char *tmp_dir = NULL, *var_tmp_dir = NULL; const char *root_dir = NULL, *root_image = NULL; - _cleanup_free_ char *creds_path = NULL; + _cleanup_free_ char *creds_path = NULL, *incoming_dir = NULL, *propagate_dir = NULL; NamespaceInfo ns_info; bool needs_sandboxing; BindMount *bind_mounts = NULL; @@ -3192,6 +3191,15 @@ static int apply_mount_namespace( } } + if (MANAGER_IS_SYSTEM(u->manager)) { + propagate_dir = path_join("/run/systemd/propagate/", u->id); + if (!propagate_dir) + return -ENOMEM; + incoming_dir = strdup("/run/systemd/incoming"); + if (!incoming_dir) + return -ENOMEM; + } + r = setup_namespace(root_dir, root_image, context->root_image_options, &ns_info, context->read_write_paths, needs_sandboxing ? context->read_only_paths : NULL, @@ -3211,6 +3219,8 @@ static int apply_mount_namespace( context->root_hash, context->root_hash_size, context->root_hash_path, context->root_hash_sig, context->root_hash_sig_size, context->root_hash_sig_path, context->root_verity, + propagate_dir, + incoming_dir, DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK, error_path); diff --git a/src/core/execute.h b/src/core/execute.h index da8d6ae2729..2da4699df18 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -471,3 +471,5 @@ ExecDirectoryType exec_directory_type_from_string(const char *s) _pure_; const char* exec_resource_type_to_string(ExecDirectoryType i) _const_; ExecDirectoryType exec_resource_type_from_string(const char *s) _pure_; + +bool exec_needs_mount_namespace(const ExecContext *context, const ExecParameters *params, const ExecRuntime *runtime); diff --git a/src/core/namespace.c b/src/core/namespace.c index 73a8fa73a4d..4b5519e11b2 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -1301,7 +1301,8 @@ static size_t namespace_calculate_mounts( const char* tmp_dir, const char* var_tmp_dir, const char *creds_path, - const char* log_namespace) { + const char* log_namespace, + bool setup_propagate) { size_t protect_home_cnt; size_t protect_system_cnt = @@ -1328,6 +1329,7 @@ static size_t namespace_calculate_mounts( n_bind_mounts + n_mount_images + n_temporary_filesystems + + (setup_propagate ? 1 : 0) + /* /run/systemd/incoming */ ns_info->private_dev + (ns_info->protect_kernel_tunables ? ELEMENTSOF(protect_kernel_tunables_table) : 0) + (ns_info->protect_kernel_modules ? ELEMENTSOF(protect_kernel_modules_table) : 0) + @@ -1487,6 +1489,8 @@ int setup_namespace( size_t root_hash_sig_size, const char *root_hash_sig_path, const char *verity_data_path, + const char *propagate_dir, + const char *incoming_dir, DissectImageFlags dissect_image_flags, char **error_path) { @@ -1495,13 +1499,16 @@ int setup_namespace( _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL; _cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT; MountEntry *m = NULL, *mounts = NULL; - bool require_prefix = false; + bool require_prefix = false, setup_propagate = false; const char *root; size_t n_mounts; int r; assert(ns_info); + if (!isempty(propagate_dir) && !isempty(incoming_dir)) + setup_propagate = true; + if (mount_flags == 0) mount_flags = MS_SHARED; @@ -1585,7 +1592,8 @@ int setup_namespace( n_mount_images, tmp_dir, var_tmp_dir, creds_path, - log_namespace); + log_namespace, + setup_propagate); if (n_mounts > 0) { m = mounts = new0(MountEntry, n_mounts); @@ -1754,6 +1762,15 @@ int setup_namespace( }; } + /* Will be used to add bind mounts at runtime */ + if (setup_propagate) + *(m++) = (MountEntry) { + .source_const = propagate_dir, + .path_const = incoming_dir, + .mode = BIND_MOUNT, + .read_only = true, + }; + assert(mounts + n_mounts == m); /* Prepend the root directory where that's necessary */ @@ -1778,6 +1795,10 @@ int setup_namespace( goto finish; } + /* Create the source directory to allow runtime propagation of mounts */ + if (setup_propagate) + (void) mkdir_p(propagate_dir, 0600); + /* Remount / as SLAVE so that nothing now mounted in the namespace * shows up in the parent */ if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) { @@ -1919,6 +1940,16 @@ int setup_namespace( goto finish; } + /* bind_mount_in_namespace() will MS_MOVE into that directory, and that's only + * supported for non-shared mounts. This needs to happen after remounting / or it will fail. */ + if (setup_propagate) { + r = mount(NULL, incoming_dir, NULL, MS_SLAVE, NULL); + if (r < 0) { + log_error_errno(r, "Failed to remount %s with MS_SLAVE: %m", incoming_dir); + goto finish; + } + } + r = 0; finish: diff --git a/src/core/namespace.h b/src/core/namespace.h index da0861c4061..91ee44cd517 100644 --- a/src/core/namespace.h +++ b/src/core/namespace.h @@ -127,6 +127,8 @@ int setup_namespace( size_t root_hash_sig_size, const char *root_hash_sig_path, const char *root_verity, + const char *propagate_dir, + const char *incoming_dir, DissectImageFlags dissected_image_flags, char **error_path); diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf index 8b32379835d..0cea4d2b024 100644 --- a/src/core/org.freedesktop.systemd1.conf +++ b/src/core/org.freedesktop.systemd1.conf @@ -222,6 +222,10 @@ send_interface="org.freedesktop.systemd1.Manager" send_member="ReloadOrTryRestartUnit"/> + + @@ -392,6 +396,10 @@ send_interface="org.freedesktop.systemd1.Service" send_member="AttachProcesses"/> + + /run/testservice-57-fixed +mkdir -p /run/inaccessible + +systemctl start testsuite-57-namespaced.service + +# Ensure that inaccessible paths aren't bypassed by the runtime setup +set +e +systemctl bind --mkdir testsuite-57-namespaced.service /run/testservice-57-fixed /run/inaccessible/testfile_fixed && exit 1 +set -e + +echo "MARKER_RUNTIME" > /run/testservice-57-runtime + +systemctl bind --mkdir testsuite-57-namespaced.service /run/testservice-57-runtime /tmp/testfile_runtime + +while systemctl show -P SubState testsuite-57-namespaced.service | grep -q running +do + sleep 0.1 +done + +systemctl is-active testsuite-57-namespaced.service + +# Now test that systemctl bind fails when attempted on a non-namespaced unit +systemctl start testsuite-57-non-namespaced.service + +set +e +systemctl bind --mkdir testsuite-57-non-namespaced.service /run/testservice-57-runtime /tmp/testfile_runtime && exit 1 +set -e + +while systemctl show -P SubState testsuite-57-non-namespaced.service | grep -q running +do + sleep 0.1 +done + +set +e +systemctl is-active testsuite-57-non-namespaced.service && exit 1 +set -e + +echo OK > /testok + +exit 0