diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml
index d445c138fa0..a5c98d3458a 100644
--- a/man/org.freedesktop.systemd1.xml
+++ b/man/org.freedesktop.systemd1.xml
@@ -2046,6 +2046,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
readonly as CanClean = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b CanFreeze = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly b CanLiveMount = ...;
readonly (uo) Job = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b StopWhenUnneeded = ...;
@@ -2178,6 +2180,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
+
+
@@ -2382,6 +2386,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
+
+
@@ -2761,6 +2767,7 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
readonly s Result = '...';
readonly s ReloadResult = '...';
readonly s CleanResult = '...';
+ readonly s LiveMountResult = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s USBFunctionDescriptors = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
@@ -3425,6 +3432,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
+
+
@@ -4055,6 +4064,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
+
+
@@ -12090,7 +12101,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
QueueSignal() was added in version 254.
SurviveFinalKillSignal was added in version 255.
WantsMountsFor was added in version 256.
- DebugInvocation was added in version 257.
+ DebugInvocation, and
+ CanLiveMount were added in version 257.
Service Unit Objects
@@ -12136,6 +12148,7 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
ExecMainHandoffTimestamp were added in version 256.
StatusBusError,
StatusVarlinkError,
+ LiveMountResult,
PrivateTmpEx, and
ImportCredentialEx were added in version 257.
diff --git a/man/systemctl.xml b/man/systemctl.xml
index 8561e169442..11da7a9b73c 100644
--- a/man/systemctl.xml
+++ b/man/systemctl.xml
@@ -291,7 +291,8 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
inactive or maintenance is a white circle ("○"),
active is a green dot ("●"), deactivating is a white dot,
failed or error is a red cross ("×"), and
- reloading is a green clockwise circle arrow ("↻").
+ reloading or refreshing is a green clockwise circle arrow
+ ("↻").
The "Loaded:" line in the output will show loaded if the unit has been
loaded into memory. Other possible values for "Loaded:" include: error if
diff --git a/man/systemd.xml b/man/systemd.xml
index 58e76eecb27..ddd190093eb 100644
--- a/man/systemd.xml
+++ b/man/systemd.xml
@@ -89,28 +89,22 @@
Units
- systemd provides a dependency system between various
- entities called "units" of 11 different types. Units encapsulate
- various objects that are relevant for system boot-up and
- maintenance. The majority of units are configured in unit
- configuration files, whose syntax and basic set of options is
+ systemd provides a dependency system between various entities called "units" of 11 different
+ types. Units encapsulate various objects that are relevant for system boot-up and maintenance. The
+ majority of units are configured in unit configuration files, whose syntax and basic set of options is
described in
systemd.unit5,
- however some are created automatically from other configuration
- files, dynamically from system state or programmatically at runtime.
- Units may be "active" (meaning started, bound, plugged in, …,
- depending on the unit type, see below), or "inactive" (meaning
- stopped, unbound, unplugged, …), as well as in the process of
- being activated or deactivated, i.e. between the two states (these
- states are called "activating", "deactivating"). A special
- "failed" state is available as well, which is very similar to
- "inactive" and is entered when the service failed in some way
- (process returned error code on exit, or crashed, an operation
- timed out, or after too many restarts). If this state is entered,
- the cause will be logged, for later reference. Note that the
- various unit types may have a number of additional substates,
- which are mapped to the five generalized unit states described
- here.
+ however some are created automatically from other configuration files, dynamically from system state or
+ programmatically at runtime. Units may be "active" (meaning started, bound, plugged in, …, depending on
+ the unit type, see below), or "inactive" (meaning stopped, unbound, unplugged, …), as well as in the
+ process of being activated or deactivated, i.e. between the two states (these states are called
+ "activating", "deactivating"). A special "failed" state is available as well, which is very similar to
+ "inactive" and is entered when the service failed in some way (process returned error code on exit, or
+ crashed, an operation timed out, or after too many restarts). If this state is entered, the cause will
+ be logged, for later reference. Units may also be in a special transient state for a time, to indicate
+ that some operation is being performed on them, before reverting to the previous state, such as
+ "maintenance", "reloading" or "refreshing". Note that the various unit types may have a number of
+ additional substates, which are mapped to the five generalized unit states described here.
The following unit types are available:
diff --git a/src/basic/unit-def.c b/src/basic/unit-def.c
index 4dc8ceef865..e3292a8a447 100644
--- a/src/basic/unit-def.c
+++ b/src/basic/unit-def.c
@@ -112,6 +112,7 @@ static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
[UNIT_ACTIVATING] = "activating",
[UNIT_DEACTIVATING] = "deactivating",
[UNIT_MAINTENANCE] = "maintenance",
+ [UNIT_REFRESHING] = "refreshing",
};
DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState);
@@ -234,6 +235,7 @@ static const char* const service_state_table[_SERVICE_STATE_MAX] = {
[SERVICE_AUTO_RESTART] = "auto-restart",
[SERVICE_AUTO_RESTART_QUEUED] = "auto-restart-queued",
[SERVICE_CLEANING] = "cleaning",
+ [SERVICE_MOUNTING] = "mounting",
};
DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState);
@@ -344,6 +346,7 @@ SpecialGlyph unit_active_state_to_glyph(UnitActiveState state) {
static const SpecialGlyph map[_UNIT_ACTIVE_STATE_MAX] = {
[UNIT_ACTIVE] = SPECIAL_GLYPH_BLACK_CIRCLE,
[UNIT_RELOADING] = SPECIAL_GLYPH_CIRCLE_ARROW,
+ [UNIT_REFRESHING] = SPECIAL_GLYPH_CIRCLE_ARROW,
[UNIT_INACTIVE] = SPECIAL_GLYPH_WHITE_CIRCLE,
[UNIT_FAILED] = SPECIAL_GLYPH_MULTIPLICATION_SIGN,
[UNIT_ACTIVATING] = SPECIAL_GLYPH_BLACK_CIRCLE,
diff --git a/src/basic/unit-def.h b/src/basic/unit-def.h
index cf74c9de2f2..d022983bdca 100644
--- a/src/basic/unit-def.h
+++ b/src/basic/unit-def.h
@@ -47,6 +47,7 @@ typedef enum UnitActiveState {
UNIT_ACTIVATING,
UNIT_DEACTIVATING,
UNIT_MAINTENANCE,
+ UNIT_REFRESHING,
_UNIT_ACTIVE_STATE_MAX,
_UNIT_ACTIVE_STATE_INVALID = -EINVAL,
} UnitActiveState;
@@ -137,6 +138,7 @@ typedef enum ServiceState {
SERVICE_RELOAD, /* Reloading via ExecReload= */
SERVICE_RELOAD_SIGNAL, /* Reloading via SIGHUP requested */
SERVICE_RELOAD_NOTIFY, /* Waiting for READY=1 after RELOADING=1 notify */
+ SERVICE_MOUNTING, /* Performing a live mount into the namespace of the service */
SERVICE_STOP, /* No STOP_PRE state, instead just register multiple STOP executables */
SERVICE_STOP_WATCHDOG,
SERVICE_STOP_SIGTERM,
diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c
index 8a144519086..43a8fb06175 100644
--- a/src/core/dbus-service.c
+++ b/src/core/dbus-service.c
@@ -19,7 +19,6 @@
#include "fileio.h"
#include "locale-util.h"
#include "missing_fcntl.h"
-#include "mount-util.h"
#include "open-file.h"
#include "parse-util.h"
#include "path-util.h"
@@ -130,6 +129,7 @@ static int property_get_exit_status_set(
}
static int bus_service_method_mount(sd_bus_message *message, void *userdata, sd_bus_error *error, bool is_image) {
+ MountInNamespaceFlags flags = 0;
Unit *u = ASSERT_PTR(userdata);
int r;
@@ -138,8 +138,9 @@ static int bus_service_method_mount(sd_bus_message *message, void *userdata, sd_
if (!MANAGER_IS_SYSTEM(u->manager))
return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Adding bind mounts at runtime is only supported by system manager");
- if (u->type != UNIT_SERVICE)
- return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unit is not of type .service");
+ r = unit_can_live_mount(u, error);
+ if (r < 0)
+ return r;
r = mac_selinux_unit_access_check(u, message, "start", error);
if (r < 0)
@@ -178,50 +179,18 @@ static int bus_service_method_mount(sd_bus_message *message, void *userdata, sd_
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
- const PidRef *unit_pid = unit_main_pid(u);
- if (!pidref_is_set(unit_pid) || !UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u)))
- return sd_bus_error_set(error, BUS_ERROR_UNIT_INACTIVE, "Unit is not running");
-
- /* The context should always be available, but there's an assert in exec_needs_mount_namespace,
- * so double-check just in case. */
- ExecContext *c = unit_get_exec_context(u);
- if (!c)
- return -ENXIO;
-
- /* 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_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unit not running in private mount namespace, cannot activate bind mount");
-
- if (mount_point_is_credentials(u->manager->prefix[EXEC_DIRECTORY_RUNTIME], dest))
- return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Refusing to bind mount over credential mounts");
-
- /* If it would be dropped at startup time, return an error. */
- if (path_startswith_strv(dest, c->inaccessible_paths))
- return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "%s is not accessible to this unit", dest);
-
- const char *propagate_directory = strjoina("/run/systemd/propagate/", u->id);
if (is_image)
- r = mount_image_in_namespace(
- unit_pid,
- propagate_directory,
- "/run/systemd/incoming/",
- src, dest,
- read_only,
- make_file_or_directory,
- options,
- c->mount_image_policy ?: &image_policy_service);
- else
- 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);
+ flags |= MOUNT_IN_NAMESPACE_IS_IMAGE;
+ if (read_only)
+ flags |= MOUNT_IN_NAMESPACE_READ_ONLY;
+ if (make_file_or_directory)
+ flags |= MOUNT_IN_NAMESPACE_MAKE_FILE_OR_DIRECTORY;
- return sd_bus_reply_method_return(message, NULL);
+ r = unit_live_mount(u, src, dest, message, flags, options, error);
+ if (r < 0)
+ return r;
+
+ return 1;
}
int bus_service_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -362,6 +331,7 @@ const sd_bus_vtable bus_service_vtable[] = {
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Service, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("ReloadResult", "s", property_get_result, offsetof(Service, reload_result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("CleanResult", "s", property_get_result, offsetof(Service, clean_result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("LiveMountResult", "s", property_get_result, offsetof(Service, live_mount_result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("USBFunctionDescriptors", "s", NULL, offsetof(Service, usb_function_descriptors), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("USBFunctionStrings", "s", NULL, offsetof(Service, usb_function_strings), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("UID", "u", bus_property_get_uid, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index f1136a95b78..d7869f115a1 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -89,6 +89,23 @@ static int property_get_can_clean(
return sd_bus_message_close_container(reply);
}
+static int property_get_can_live_mount(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Unit *u = ASSERT_PTR(userdata);
+
+ assert(bus);
+ assert(reply);
+
+ return sd_bus_message_append(reply, "b", unit_can_live_mount(u, /* error= */ NULL) >= 0);
+}
+
static int property_get_names(
sd_bus *bus,
const char *path,
@@ -882,6 +899,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("CanIsolate", "b", property_get_can_isolate, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("CanClean", "as", property_get_can_clean, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("CanFreeze", "b", property_get_can_freeze, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("CanLiveMount", "b", property_get_can_live_mount, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Job", "(uo)", property_get_job, offsetof(Unit, job), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("StopWhenUnneeded", "b", bus_property_get_bool, offsetof(Unit, stop_when_unneeded), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RefuseManualStart", "b", bus_property_get_bool, offsetof(Unit, refuse_manual_start), SD_BUS_VTABLE_PROPERTY_CONST),
diff --git a/src/core/job.c b/src/core/job.c
index 21083497c09..468571ae71a 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -425,13 +425,13 @@ bool job_type_is_redundant(JobType a, UnitActiveState b) {
switch (a) {
case JOB_START:
- return IN_SET(b, UNIT_ACTIVE, UNIT_RELOADING);
+ return IN_SET(b, UNIT_ACTIVE, UNIT_RELOADING, UNIT_REFRESHING);
case JOB_STOP:
return IN_SET(b, UNIT_INACTIVE, UNIT_FAILED);
case JOB_VERIFY_ACTIVE:
- return IN_SET(b, UNIT_ACTIVE, UNIT_RELOADING);
+ return IN_SET(b, UNIT_ACTIVE, UNIT_RELOADING, UNIT_REFRESHING);
case JOB_RELOAD:
return
diff --git a/src/core/manager.c b/src/core/manager.c
index 499ecec88e6..f43bfd214ba 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -1890,6 +1890,7 @@ static bool manager_dbus_is_running(Manager *m, bool deserialized) {
return false;
if (!IN_SET((deserialized ? SERVICE(u)->deserialized_state : SERVICE(u)->state),
SERVICE_RUNNING,
+ SERVICE_MOUNTING,
SERVICE_RELOAD,
SERVICE_RELOAD_NOTIFY,
SERVICE_RELOAD_SIGNAL))
diff --git a/src/core/scope.c b/src/core/scope.c
index cfa2aeb03f6..6e66b56dcba 100644
--- a/src/core/scope.c
+++ b/src/core/scope.c
@@ -356,7 +356,7 @@ static int scope_enter_start_chown(Scope *s) {
if (r < 0)
return r;
- r = unit_fork_helper_process(u, "(sd-chown-cgroup)", &pidref);
+ r = unit_fork_helper_process(u, "(sd-chown-cgroup)", /* into_cgroup= */ true, &pidref);
if (r < 0)
goto fail;
diff --git a/src/core/service.c b/src/core/service.c
index ed3a46ff8c2..663fdb30f2c 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -10,6 +10,7 @@
#include "alloc-util.h"
#include "async.h"
+#include "bus-common-errors.h"
#include "bus-error.h"
#include "bus-kernel.h"
#include "bus-util.h"
@@ -31,6 +32,7 @@
#include "log.h"
#include "manager.h"
#include "missing_audit.h"
+#include "mount-util.h"
#include "open-file.h"
#include "parse-util.h"
#include "path-util.h"
@@ -77,6 +79,7 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
[SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
[SERVICE_AUTO_RESTART_QUEUED] = UNIT_ACTIVATING,
[SERVICE_CLEANING] = UNIT_MAINTENANCE,
+ [SERVICE_MOUNTING] = UNIT_REFRESHING,
};
/* For Type=idle we never want to delay any other jobs, hence we
@@ -107,6 +110,7 @@ static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] =
[SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
[SERVICE_AUTO_RESTART_QUEUED] = UNIT_ACTIVATING,
[SERVICE_CLEANING] = UNIT_MAINTENANCE,
+ [SERVICE_MOUNTING] = UNIT_REFRESHING,
};
static int service_dispatch_inotify_io(sd_event_source *source, int fd, uint32_t events, void *userdata);
@@ -122,6 +126,7 @@ static bool SERVICE_STATE_WITH_MAIN_PROCESS(ServiceState state) {
SERVICE_START, SERVICE_START_POST,
SERVICE_RUNNING,
SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY,
+ SERVICE_MOUNTING,
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL);
}
@@ -133,7 +138,7 @@ static bool SERVICE_STATE_WITH_CONTROL_PROCESS(ServiceState state) {
SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY,
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
- SERVICE_CLEANING);
+ SERVICE_CLEANING, SERVICE_MOUNTING);
}
static void service_init(Unit *u) {
@@ -505,6 +510,8 @@ static void service_done(Unit *u) {
service_release_socket_fd(s);
service_release_stdio_fd(s);
service_release_fd_store(s);
+
+ s->mount_request = sd_bus_message_unref(s->mount_request);
}
static int on_fd_store_io(sd_event_source *e, int fd, uint32_t revents, void *userdata) {
@@ -944,6 +951,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
"%sResult: %s\n"
"%sReload Result: %s\n"
"%sClean Result: %s\n"
+ "%sMount Result: %s\n"
"%sPermissionsStartOnly: %s\n"
"%sRootDirectoryStartOnly: %s\n"
"%sRemainAfterExit: %s\n"
@@ -958,6 +966,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
prefix, service_result_to_string(s->result),
prefix, service_result_to_string(s->reload_result),
prefix, service_result_to_string(s->clean_result),
+ prefix, service_result_to_string(s->live_mount_result),
prefix, yes_no(s->permissions_start_only),
prefix, yes_no(s->root_directory_start_only),
prefix, yes_no(s->remain_after_exit),
@@ -1252,6 +1261,7 @@ static void service_set_state(Service *s, ServiceState state) {
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
SERVICE_AUTO_RESTART,
+ SERVICE_MOUNTING,
SERVICE_CLEANING))
s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source);
@@ -1277,7 +1287,7 @@ static void service_set_state(Service *s, ServiceState state) {
if (state != SERVICE_START)
s->exec_fd_event_source = sd_event_source_disable_unref(s->exec_fd_event_source);
- if (!IN_SET(state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY))
+ if (!IN_SET(state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_MOUNTING))
service_stop_watchdog(s);
if (state == SERVICE_EXITED && !MANAGER_IS_RELOADING(u->manager)) {
@@ -1301,6 +1311,9 @@ static void service_set_state(Service *s, ServiceState state) {
unit_destroy_runtime_data(u, &s->exec_context);
}
+ if (state != SERVICE_MOUNTING) /* Just in case */
+ s->mount_request = sd_bus_message_unref(s->mount_request);
+
if (old_state != state)
log_unit_debug(u, "Changed %s -> %s", service_state_to_string(old_state), service_state_to_string(state));
@@ -1342,6 +1355,9 @@ static usec_t service_coldplug_timeout(Service *s) {
case SERVICE_CLEANING:
return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->exec_context.timeout_clean_usec);
+ case SERVICE_MOUNTING:
+ return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->timeout_start_usec);
+
default:
return USEC_INFINITY;
}
@@ -1386,7 +1402,7 @@ static int service_coldplug(Unit *u) {
(void) unit_setup_exec_runtime(u);
}
- if (IN_SET(s->deserialized_state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY))
+ if (IN_SET(s->deserialized_state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_MOUNTING))
service_start_watchdog(s);
if (UNIT_ISSET(s->accept_socket)) {
@@ -2853,6 +2869,32 @@ static int service_start(Unit *u) {
return 1;
}
+static void service_mount_request_reply(Service *s, bool success, const char *error) {
+ assert(s);
+ assert(error);
+
+ if (!s->mount_request)
+ return;
+
+ if (success) {
+ (void) sd_bus_reply_method_return(s->mount_request, NULL);
+ log_unit_debug(UNIT(s),
+ "'%s' method succeeded",
+ strna(sd_bus_message_get_member(s->mount_request)));
+ } else {
+ (void) sd_bus_reply_method_errorf(s->mount_request, error,
+ "method '%s' for unit '%s' failed",
+ strna(sd_bus_message_get_member(s->mount_request)),
+ UNIT(s)->id);
+ log_unit_debug(UNIT(s),
+ "'%s' method failed: %s",
+ strna(sd_bus_message_get_member(s->mount_request)),
+ error);
+ }
+
+ s->mount_request = sd_bus_message_unref(s->mount_request);
+}
+
static int service_stop(Unit *u) {
Service *s = ASSERT_PTR(SERVICE(u));
@@ -2877,6 +2919,10 @@ static int service_stop(Unit *u) {
service_set_state(s, service_determine_dead_state(s));
return 0;
+ case SERVICE_MOUNTING:
+ service_kill_control_process(s);
+ service_mount_request_reply(s, /* success= */ false, BUS_ERROR_UNIT_INACTIVE);
+ _fallthrough_;
case SERVICE_CONDITION:
case SERVICE_START_PRE:
case SERVICE_START:
@@ -3860,6 +3906,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
case SERVICE_RELOAD:
case SERVICE_RELOAD_SIGNAL:
case SERVICE_RELOAD_NOTIFY:
+ case SERVICE_MOUNTING:
/* If neither main nor control processes are running then the current
* state can never exit cleanly, hence immediately terminate the
* service. */
@@ -3974,7 +4021,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
success,
code, status);
- if (s->state != SERVICE_RELOAD && s->result == SERVICE_SUCCESS)
+ if (!IN_SET(s->state, SERVICE_RELOAD, SERVICE_MOUNTING) && s->result == SERVICE_SUCCESS)
s->result = f;
if (s->control_command &&
@@ -4114,6 +4161,14 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
service_enter_dead(s, SERVICE_SUCCESS, false);
break;
+ case SERVICE_MOUNTING:
+ s->live_mount_result = f;
+
+ service_mount_request_reply(s, f == SERVICE_SUCCESS, SD_BUS_ERROR_FAILED);
+
+ service_enter_running(s, SERVICE_SUCCESS);
+ break;
+
default:
assert_not_reached();
}
@@ -4187,6 +4242,14 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
service_enter_running(s, SERVICE_SUCCESS);
break;
+ case SERVICE_MOUNTING:
+ log_unit_warning(UNIT(s), "Mount operation timed out. Killing mount process.");
+ service_kill_control_process(s);
+ s->live_mount_result = SERVICE_FAILURE_TIMEOUT;
+ service_mount_request_reply(s, /* success= */ false, SD_BUS_ERROR_TIMEOUT);
+ service_enter_running(s, SERVICE_SUCCESS);
+ break;
+
case SERVICE_STOP:
switch (s->timeout_stop_failure_mode) {
@@ -4743,7 +4806,8 @@ static bool pick_up_pid_from_bus_name(Service *s) {
SERVICE_RUNNING,
SERVICE_RELOAD,
SERVICE_RELOAD_SIGNAL,
- SERVICE_RELOAD_NOTIFY);
+ SERVICE_RELOAD_NOTIFY,
+ SERVICE_MOUNTING);
}
static int bus_name_pid_lookup_callback(sd_bus_message *reply, void *userdata, sd_bus_error *ret_error) {
@@ -4892,6 +4956,7 @@ static void service_reset_failed(Unit *u) {
s->result = SERVICE_SUCCESS;
s->reload_result = SERVICE_SUCCESS;
s->clean_result = SERVICE_SUCCESS;
+ s->live_mount_result = SERVICE_SUCCESS;
s->n_restarts = 0;
service_set_debug_invocation(s, /* enable= */ false);
@@ -4928,6 +4993,7 @@ static bool service_needs_console(Unit *u) {
SERVICE_RELOAD,
SERVICE_RELOAD_SIGNAL,
SERVICE_RELOAD_NOTIFY,
+ SERVICE_MOUNTING,
SERVICE_STOP,
SERVICE_STOP_WATCHDOG,
SERVICE_STOP_SIGTERM,
@@ -5035,6 +5101,168 @@ static int service_can_clean(Unit *u, ExecCleanMask *ret) {
return 0;
}
+static int service_live_mount(Unit *u,
+ const char *src,
+ const char *dst,
+ sd_bus_message *message,
+ MountInNamespaceFlags flags,
+ const MountOptions *options,
+ sd_bus_error *error) {
+
+ _cleanup_(pidref_done) PidRef worker = PIDREF_NULL;
+ Service *s = ASSERT_PTR(SERVICE(u));
+ const char *propagate_directory;
+ int r;
+
+ assert(u);
+ assert(u->manager);
+ assert(src);
+ assert(dst);
+ assert(message);
+ assert(!s->mount_request);
+
+ if (s->state != SERVICE_RUNNING || !pidref_is_set(&s->main_pid)) {
+ log_unit_warning(u, "Service is not running, cannot live mount");
+ return sd_bus_error_setf(
+ error,
+ BUS_ERROR_UNIT_INACTIVE,
+ "Live mounting '%s' on '%s' for unit '%s' cannot be scheduled: service not running",
+ src,
+ dst,
+ u->id);
+ }
+
+ if (mount_point_is_credentials(u->manager->prefix[EXEC_DIRECTORY_RUNTIME], dst)) {
+ log_unit_warning(u, "Refusing to live mount over credential mount '%s'", dst);
+ return sd_bus_error_setf(
+ error,
+ SD_BUS_ERROR_INVALID_ARGS,
+ "Live mounting '%s' on '%s' for unit '%s' cannot be scheduled: cannot mount over credential mount",
+ src,
+ dst,
+ u->id);
+ }
+
+ if (path_startswith_strv(dst, s->exec_context.inaccessible_paths)) {
+ log_unit_warning(u, "%s is not accessible to this unit, cannot live mount", dst);
+ return sd_bus_error_setf(
+ error,
+ SD_BUS_ERROR_INVALID_ARGS,
+ "Live mounting '%s' on '%s' for unit '%s' cannot be scheduled: destination is not accessible to this unit",
+ src,
+ dst,
+ u->id);
+ }
+
+ service_unwatch_control_pid(s);
+ s->live_mount_result = SERVICE_SUCCESS;
+ s->control_command = NULL;
+ s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
+
+ r = service_arm_timer(s, /* relative= */ true, s->timeout_start_usec);
+ if (r < 0) {
+ log_unit_warning_errno(u, r, "Failed to install timer: %m");
+ sd_bus_error_set_errnof(
+ error,
+ r,
+ "Live mounting '%s' on '%s' for unit '%s' cannot be scheduled: failed to install timer",
+ src,
+ dst,
+ u->id);
+ goto fail;
+ }
+
+ propagate_directory = strjoina("/run/systemd/propagate/", u->id);
+
+ /* Given we are running from PID1, avoid doing potentially heavy I/O operations like opening images
+ * directly, and instead fork a worker process. We record the D-Bus message, so that we can reply
+ * after the operation has finished. This way callers can wait on the message and know that the new
+ * resource is available (or the operation failed) once they receive the response. */
+ r = unit_fork_helper_process(u, "(sd-mount-in-ns)", /* into_cgroup= */ false, &worker);
+ if (r < 0) {
+ log_unit_warning_errno(
+ u,
+ r,
+ "Failed to fork process to mount '%s' on '%s' in unit's namespace: %m",
+ src,
+ dst);
+ sd_bus_error_set_errnof(
+ error,
+ r,
+ "Live mounting '%s' on '%s' for unit '%s' cannot be scheduled: failed to fork process",
+ src,
+ dst,
+ u->id);
+ goto fail;
+ }
+ if (r == 0) {
+ if (flags & MOUNT_IN_NAMESPACE_IS_IMAGE)
+ r = mount_image_in_namespace(
+ &s->main_pid,
+ propagate_directory,
+ "/run/systemd/incoming/",
+ src, dst,
+ flags,
+ options,
+ s->exec_context.mount_image_policy ?: &image_policy_service);
+ else
+ r = bind_mount_in_namespace(
+ &s->main_pid,
+ propagate_directory,
+ "/run/systemd/incoming/",
+ src, dst,
+ flags);
+ if (r < 0)
+ log_unit_warning_errno(
+ u,
+ r,
+ "Failed to mount '%s' on '%s' in unit's namespace: %m",
+ src,
+ dst);
+ else
+ log_unit_debug(u, "Mounted '%s' on '%s' in unit's namespace", src, dst);
+ _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
+ }
+
+ r = unit_watch_pidref(u, &worker, /* exclusive= */ true);
+ if (r < 0) {
+ sd_bus_error_set_errnof(
+ error,
+ r,
+ "Live mounting '%s' on '%s' for unit '%s' failed: failed to watch worker process",
+ src,
+ dst,
+ u->id);
+ goto fail;
+ }
+
+ s->mount_request = sd_bus_message_ref(message);
+ s->control_pid = TAKE_PIDREF(worker);
+ service_set_state(s, SERVICE_MOUNTING);
+ return 0;
+
+fail:
+ s->live_mount_result = SERVICE_FAILURE_RESOURCES;
+ s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source);
+ return r;
+}
+
+static int service_can_live_mount(const Unit *u, sd_bus_error *error) {
+ assert(u);
+
+ /* Ensure that the unit runs in a private mount namespace */
+ if (!exec_needs_mount_namespace(unit_get_exec_context(u), /* params= */ NULL, unit_get_exec_runtime(u))) {
+ log_unit_debug(u, "Unit not running in private mount namespace, cannot live mount");
+ return sd_bus_error_setf(
+ error,
+ SD_BUS_ERROR_INVALID_ARGS,
+ "Live mounting for unit '%s' cannot be scheduled: unit not running in private mount namespace",
+ u->id);
+ }
+
+ return 0;
+}
+
static const char* service_finished_job(Unit *u, JobType t, JobResult result) {
Service *s = ASSERT_PTR(SERVICE(u));
@@ -5265,6 +5493,9 @@ const UnitVTable service_vtable = {
.clean = service_clean,
.can_clean = service_can_clean,
+ .live_mount = service_live_mount,
+ .can_live_mount = service_can_live_mount,
+
.freezer_action = unit_cgroup_freezer_action,
.serialize = service_serialize,
diff --git a/src/core/service.h b/src/core/service.h
index 4d67174756f..6a0c4929922 100644
--- a/src/core/service.h
+++ b/src/core/service.h
@@ -188,6 +188,7 @@ struct Service {
ServiceResult result;
ServiceResult reload_result;
ServiceResult clean_result;
+ ServiceResult live_mount_result;
bool main_pid_known:1;
bool main_pid_alien:1;
@@ -232,6 +233,9 @@ struct Service {
int reload_signal;
usec_t reload_begin_usec;
+
+ /* The D-Bus request, we will reply once the operation is finished, so that callers can block */
+ sd_bus_message *mount_request;
};
static inline usec_t service_timeout_abort_usec(Service *s) {
diff --git a/src/core/socket.c b/src/core/socket.c
index a1553bcc68b..0701780f038 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -1568,7 +1568,7 @@ static int socket_address_listen_in_cgroup(
if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pair) < 0)
return log_unit_error_errno(UNIT(s), errno, "Failed to create communication channel: %m");
- r = unit_fork_helper_process(UNIT(s), "(sd-listen)", &pid);
+ r = unit_fork_helper_process(UNIT(s), "(sd-listen)", /* into_cgroup= */ true, &pid);
if (r < 0)
return log_unit_error_errno(UNIT(s), r, "Failed to fork off listener stub process: %m");
if (r == 0) {
@@ -1989,7 +1989,7 @@ static int socket_chown(Socket *s, PidRef *ret_pid) {
/* We have to resolve the user names out-of-process, hence
* let's fork here. It's messy, but well, what can we do? */
- r = unit_fork_helper_process(UNIT(s), "(sd-chown)", &pid);
+ r = unit_fork_helper_process(UNIT(s), "(sd-chown)", /* into_cgroup= */ true, &pid);
if (r < 0)
return r;
if (r == 0) {
@@ -3013,7 +3013,7 @@ static int socket_accept_in_cgroup(Socket *s, SocketPort *p, int fd) {
if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pair) < 0)
return log_unit_error_errno(UNIT(s), errno, "Failed to create communication channel: %m");
- r = unit_fork_helper_process(UNIT(s), "(sd-accept)", &pid);
+ r = unit_fork_helper_process(UNIT(s), "(sd-accept)", /* into_cgroup= */ true, &pid);
if (r < 0)
return log_unit_error_errno(UNIT(s), r, "Failed to fork off accept stub process: %m");
if (r == 0) {
diff --git a/src/core/unit.c b/src/core/unit.c
index 66203f27942..4468733a636 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -2561,7 +2561,7 @@ static bool unit_process_job(Job *j, UnitActiveState ns, bool reload_success) {
if (j->state == JOB_RUNNING) {
if (ns == UNIT_ACTIVE)
job_finish_and_invalidate(j, reload_success ? JOB_DONE : JOB_FAILED, true, false);
- else if (!IN_SET(ns, UNIT_ACTIVATING, UNIT_RELOADING)) {
+ else if (!IN_SET(ns, UNIT_ACTIVATING, UNIT_RELOADING, UNIT_REFRESHING)) {
unexpected = true;
if (UNIT_IS_INACTIVE_OR_FAILED(ns))
@@ -5412,21 +5412,25 @@ int unit_set_exec_params(Unit *u, ExecParameters *p) {
return 0;
}
-int unit_fork_helper_process(Unit *u, const char *name, PidRef *ret) {
+int unit_fork_helper_process(Unit *u, const char *name, bool into_cgroup, PidRef *ret) {
+ CGroupRuntime *crt = NULL;
pid_t pid;
int r;
assert(u);
assert(ret);
- /* Forks off a helper process and makes sure it is a member of the unit's cgroup. Returns == 0 in the child,
- * and > 0 in the parent. The pid parameter is always filled in with the child's PID. */
+ /* Forks off a helper process and makes sure it is a member of the unit's cgroup, if configured to
+ * do so. Returns == 0 in the child, and > 0 in the parent. The pid parameter is always filled in
+ * with the child's PID. */
- (void) unit_realize_cgroup(u);
+ if (into_cgroup) {
+ (void) unit_realize_cgroup(u);
- CGroupRuntime *crt = unit_setup_cgroup_runtime(u);
- if (!crt)
- return -ENOMEM;
+ crt = unit_setup_cgroup_runtime(u);
+ if (!crt)
+ return -ENOMEM;
+ }
r = safe_fork(name, FORK_REOPEN_LOG|FORK_DEATHSIG_SIGTERM, &pid);
if (r < 0)
@@ -5450,7 +5454,7 @@ int unit_fork_helper_process(Unit *u, const char *name, PidRef *ret) {
(void) default_signals(SIGNALS_CRASH_HANDLER, SIGNALS_IGNORE);
(void) ignore_signals(SIGPIPE);
- if (crt->cgroup_path) {
+ if (crt && crt->cgroup_path) {
r = cg_attach_everywhere(u->manager->cgroup_supported, crt->cgroup_path, 0);
if (r < 0) {
log_unit_error_errno(u, r, "Failed to join unit cgroup %s: %m", empty_to_root(crt->cgroup_path));
@@ -5468,7 +5472,7 @@ int unit_fork_and_watch_rm_rf(Unit *u, char **paths, PidRef *ret_pid) {
assert(u);
assert(ret_pid);
- r = unit_fork_helper_process(u, "(sd-rmrf)", &pid);
+ r = unit_fork_helper_process(u, "(sd-rmrf)", /* into_cgroup= */ true, &pid);
if (r < 0)
return r;
if (r == 0) {
@@ -6375,6 +6379,82 @@ Condition *unit_find_failed_condition(Unit *u) {
return failed_trigger && !has_succeeded_trigger ? failed_trigger : NULL;
}
+int unit_can_live_mount(const Unit *u, sd_bus_error *error) {
+ assert(u);
+
+ if (!UNIT_VTABLE(u)->live_mount) {
+ log_unit_debug(u, "Live mounting not supported for unit type '%s'", unit_type_to_string(u->type));
+ return sd_bus_error_setf(
+ error,
+ SD_BUS_ERROR_INVALID_ARGS,
+ "Live mounting for unit '%s' cannot be scheduled: live mounting not supported for unit type '%s'",
+ u->id,
+ unit_type_to_string(u->type));
+ }
+
+ if (u->load_state != UNIT_LOADED) {
+ log_unit_debug(u, "Unit not loaded");
+ return sd_bus_error_setf(
+ error,
+ BUS_ERROR_NO_SUCH_UNIT,
+ "Live mounting for unit '%s' cannot be scheduled: unit not loaded",
+ u->id);
+ }
+
+ if (!UNIT_VTABLE(u)->can_live_mount)
+ return 0;
+
+ return UNIT_VTABLE(u)->can_live_mount(u, error);
+}
+
+int unit_live_mount(
+ Unit *u,
+ const char *src,
+ const char *dst,
+ sd_bus_message *message,
+ MountInNamespaceFlags flags,
+ const MountOptions *options,
+ sd_bus_error *error) {
+
+ assert(u);
+ assert(UNIT_VTABLE(u)->live_mount);
+
+ if (!UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) {
+ log_unit_debug(u, "Unit not active");
+ return sd_bus_error_setf(
+ error,
+ BUS_ERROR_UNIT_INACTIVE,
+ "Live mounting '%s' on '%s' for unit '%s' cannot be scheduled: unit not active",
+ src,
+ dst,
+ u->id);
+ }
+
+ if (unit_active_state(u) == UNIT_REFRESHING) {
+ log_unit_debug(u, "Unit already live mounting");
+ return sd_bus_error_setf(
+ error,
+ BUS_ERROR_UNIT_BUSY,
+ "Live mounting '%s' on '%s' for unit '%s' cannot be scheduled: another live mount in progress",
+ src,
+ dst,
+ u->id);
+ }
+
+ if (u->job) {
+ log_unit_debug(u, "Unit already has a job in progress, cannot live mount");
+ return sd_bus_error_setf(
+ error,
+ BUS_ERROR_UNIT_BUSY,
+ "Live mounting '%s' on '%s' for unit '%s' cannot be scheduled: another operation in progress",
+ src,
+ dst,
+ u->id);
+ }
+
+ return UNIT_VTABLE(u)->live_mount(u, src, dst, message, flags, options, error);
+}
+
static const char* const collect_mode_table[_COLLECT_MODE_MAX] = {
[COLLECT_INACTIVE] = "inactive",
[COLLECT_INACTIVE_OR_FAILED] = "inactive-or-failed",
diff --git a/src/core/unit.h b/src/core/unit.h
index ce713354385..8aac5f59992 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -22,6 +22,7 @@ typedef enum UnitMountDependencyType {
#include "emergency-action.h"
#include "install.h"
#include "list.h"
+#include "mount-util.h"
#include "pidref.h"
#include "unit-file.h"
@@ -45,11 +46,11 @@ typedef enum CollectMode {
} CollectMode;
static inline bool UNIT_IS_ACTIVE_OR_RELOADING(UnitActiveState t) {
- return IN_SET(t, UNIT_ACTIVE, UNIT_RELOADING);
+ return IN_SET(t, UNIT_ACTIVE, UNIT_RELOADING, UNIT_REFRESHING);
}
static inline bool UNIT_IS_ACTIVE_OR_ACTIVATING(UnitActiveState t) {
- return IN_SET(t, UNIT_ACTIVE, UNIT_ACTIVATING, UNIT_RELOADING);
+ return IN_SET(t, UNIT_ACTIVE, UNIT_ACTIVATING, UNIT_RELOADING, UNIT_REFRESHING);
}
static inline bool UNIT_IS_INACTIVE_OR_DEACTIVATING(UnitActiveState t) {
@@ -584,6 +585,10 @@ typedef struct UnitVTable {
bool (*can_reload)(Unit *u);
+ /* Add a bind/image mount into the unit namespace while it is running. */
+ int (*live_mount)(Unit *u, const char *src, const char *dst, sd_bus_message *message, MountInNamespaceFlags flags, const MountOptions *options, sd_bus_error *error);
+ int (*can_live_mount)(const Unit *u, sd_bus_error *error);
+
/* Serialize state and file descriptors that should be carried over into the new
* instance after reexecution. */
int (*serialize)(Unit *u, FILE *f, FDSet *fds);
@@ -980,7 +985,7 @@ int unit_acquire_invocation_id(Unit *u);
int unit_set_exec_params(Unit *s, ExecParameters *p);
-int unit_fork_helper_process(Unit *u, const char *name, PidRef *ret);
+int unit_fork_helper_process(Unit *u, const char *name, bool into_cgroup, PidRef *ret);
int unit_fork_and_watch_rm_rf(Unit *u, char **paths, PidRef *ret);
void unit_remove_dependencies(Unit *u, UnitDependencyMask mask);
@@ -1041,6 +1046,9 @@ void unit_next_freezer_state(Unit *u, FreezerAction action, FreezerState *ret_ne
void unit_set_freezer_state(Unit *u, FreezerState state);
void unit_freezer_complete(Unit *u, FreezerState kernel_state);
+int unit_can_live_mount(const Unit *u, sd_bus_error *error);
+int unit_live_mount(Unit *u, const char *src, const char *dst, sd_bus_message *message, MountInNamespaceFlags flags, const MountOptions *options, sd_bus_error *error);
+
Condition *unit_find_failed_condition(Unit *u);
int unit_arm_timer(Unit *u, sd_event_source **source, bool relative, usec_t usec, sd_event_time_handler_t handler);
diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c
index d4c6f1bfe39..366be8fbdeb 100644
--- a/src/machine/machine-dbus.c
+++ b/src/machine/machine-dbus.c
@@ -844,6 +844,7 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
int read_only, make_file_or_directory;
const char *dest, *src, *propagate_directory;
Machine *m = ASSERT_PTR(userdata);
+ MountInNamespaceFlags flags = 0;
uid_t uid;
int r;
@@ -889,14 +890,18 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
if (uid != 0)
return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Can't bind mount on container with user namespacing applied.");
+ if (read_only)
+ flags |= MOUNT_IN_NAMESPACE_READ_ONLY;
+ if (make_file_or_directory)
+ flags |= MOUNT_IN_NAMESPACE_MAKE_FILE_OR_DIRECTORY;
+
propagate_directory = strjoina("/run/systemd/nspawn/propagate/", m->name);
r = bind_mount_in_namespace(
&m->leader,
propagate_directory,
"/run/host/incoming/",
src, dest,
- read_only,
- make_file_or_directory);
+ flags);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to mount %s on %s in machine's namespace: %m", src, dest);
diff --git a/src/shared/bus-wait-for-units.c b/src/shared/bus-wait-for-units.c
index 6ccf822064f..f16fe51e6e1 100644
--- a/src/shared/bus-wait-for-units.c
+++ b/src/shared/bus-wait-for-units.c
@@ -24,6 +24,7 @@ typedef struct WaitForItem {
char *active_state;
uint32_t job_id;
char *clean_result;
+ char *live_mount_result;
} WaitForItem;
typedef struct BusWaitForUnits {
@@ -67,6 +68,7 @@ static WaitForItem *wait_for_item_free(WaitForItem *item) {
free(item->bus_path);
free(item->active_state);
free(item->clean_result);
+ free(item->live_mount_result);
return mfree(item);
}
@@ -178,6 +180,9 @@ static void wait_for_item_check_ready(WaitForItem *item) {
if (item->clean_result && !streq(item->clean_result, "success"))
d->has_failed = true;
+ if (item->live_mount_result && !streq(item->live_mount_result, "success"))
+ d->has_failed = true;
+
if (!item->active_state || streq(item->active_state, "maintenance"))
return;
}
@@ -214,9 +219,10 @@ static int property_map_job_id(
static int wait_for_item_parse_properties(WaitForItem *item, sd_bus_message *m) {
static const struct bus_properties_map map[] = {
- { "ActiveState", "s", NULL, offsetof(WaitForItem, active_state) },
- { "Job", "(uo)", property_map_job_id, offsetof(WaitForItem, job_id) },
- { "CleanResult", "s", NULL, offsetof(WaitForItem, clean_result) },
+ { "ActiveState", "s", NULL, offsetof(WaitForItem, active_state) },
+ { "Job", "(uo)", property_map_job_id, offsetof(WaitForItem, job_id) },
+ { "CleanResult", "s", NULL, offsetof(WaitForItem, clean_result) },
+ { "LiveMountResult", "s", NULL, offsetof(WaitForItem, live_mount_result) },
{}
};
diff --git a/src/shared/mount-util.c b/src/shared/mount-util.c
index aa0b9b40ecf..3992afe8c7d 100644
--- a/src/shared/mount-util.c
+++ b/src/shared/mount-util.c
@@ -854,11 +854,9 @@ static int mount_in_namespace_legacy(
int pidns_fd,
int mntns_fd,
int root_fd,
- bool read_only,
- bool make_file_or_directory,
+ MountInNamespaceFlags flags,
const MountOptions *options,
- const ImagePolicy *image_policy,
- bool is_image) {
+ const ImagePolicy *image_policy) {
_cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR;
char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
@@ -877,7 +875,7 @@ static int mount_in_namespace_legacy(
assert(pidns_fd >= 0);
assert(mntns_fd >= 0);
assert(root_fd >= 0);
- assert(!options || is_image);
+ assert(!options || (flags & MOUNT_IN_NAMESPACE_IS_IMAGE));
p = strjoina(propagate_path, "/");
r = laccess(p, F_OK);
@@ -910,7 +908,7 @@ static int mount_in_namespace_legacy(
/* Second, we mount the source file or directory to a directory inside of our MS_SLAVE playground. */
mount_tmp = strjoina(mount_slave, "/mount");
- if (is_image)
+ if (flags & MOUNT_IN_NAMESPACE_IS_IMAGE)
r = mkdir_p(mount_tmp, 0700);
else
r = make_mount_point_inode_from_stat(chased_src_st, mount_tmp, 0700);
@@ -921,7 +919,7 @@ static int mount_in_namespace_legacy(
mount_tmp_created = true;
- if (is_image)
+ if (flags & MOUNT_IN_NAMESPACE_IS_IMAGE)
r = verity_dissect_and_mount(
chased_src_fd,
chased_src_path,
@@ -943,7 +941,7 @@ static int mount_in_namespace_legacy(
mount_tmp_mounted = true;
/* Third, we remount the new bind mount read-only if requested. */
- if (read_only) {
+ if (flags & MOUNT_IN_NAMESPACE_READ_ONLY) {
r = mount_nofollow_verbose(LOG_DEBUG, NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL);
if (r < 0)
goto finish;
@@ -953,7 +951,7 @@ static int mount_in_namespace_legacy(
* right-away. */
mount_outside = strjoina(propagate_path, "/XXXXXX");
- if (is_image || S_ISDIR(chased_src_st->st_mode))
+ if ((flags & MOUNT_IN_NAMESPACE_IS_IMAGE) || S_ISDIR(chased_src_st->st_mode))
r = mkdtemp(mount_outside) ? 0 : -errno;
else {
r = mkostemp_safe(mount_outside);
@@ -973,7 +971,7 @@ static int mount_in_namespace_legacy(
mount_outside_mounted = true;
mount_tmp_mounted = false;
- if (is_image || S_ISDIR(chased_src_st->st_mode))
+ if ((flags & MOUNT_IN_NAMESPACE_IS_IMAGE) || S_ISDIR(chased_src_st->st_mode))
(void) rmdir(mount_tmp);
else
(void) unlink(mount_tmp);
@@ -999,8 +997,8 @@ static int mount_in_namespace_legacy(
errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
- if (make_file_or_directory) {
- if (!is_image) {
+ if (flags & MOUNT_IN_NAMESPACE_MAKE_FILE_OR_DIRECTORY) {
+ if (!(flags & MOUNT_IN_NAMESPACE_IS_IMAGE)) {
(void) mkdir_parents(dest, 0755);
(void) make_mount_point_inode_from_stat(chased_src_st, dest, 0700);
} else
@@ -1052,7 +1050,7 @@ finish:
if (mount_outside_mounted)
(void) umount_verbose(LOG_DEBUG, mount_outside, UMOUNT_NOFOLLOW);
if (mount_outside_created) {
- if (is_image || S_ISDIR(chased_src_st->st_mode))
+ if ((flags & MOUNT_IN_NAMESPACE_IS_IMAGE) || S_ISDIR(chased_src_st->st_mode))
(void) rmdir(mount_outside);
else
(void) unlink(mount_outside);
@@ -1061,7 +1059,7 @@ finish:
if (mount_tmp_mounted)
(void) umount_verbose(LOG_DEBUG, mount_tmp, UMOUNT_NOFOLLOW);
if (mount_tmp_created) {
- if (is_image || S_ISDIR(chased_src_st->st_mode))
+ if ((flags & MOUNT_IN_NAMESPACE_IS_IMAGE) || S_ISDIR(chased_src_st->st_mode))
(void) rmdir(mount_tmp);
else
(void) unlink(mount_tmp);
@@ -1081,9 +1079,7 @@ static int mount_in_namespace(
const char *incoming_path,
const char *src,
const char *dest,
- bool read_only,
- bool make_file_or_directory,
- bool is_image,
+ MountInNamespaceFlags flags,
const MountOptions *options,
const ImagePolicy *image_policy) {
@@ -1096,7 +1092,7 @@ static int mount_in_namespace(
assert(incoming_path);
assert(src);
assert(dest);
- assert(is_image || (!options && !image_policy));
+ assert((flags & MOUNT_IN_NAMESPACE_IS_IMAGE) || (!options && !image_policy));
if (!pidref_is_set(target))
return -ESRCH;
@@ -1133,18 +1129,16 @@ static int mount_in_namespace(
pidns_fd,
mntns_fd,
root_fd,
- read_only,
- make_file_or_directory,
+ flags,
options,
- image_policy,
- is_image);
+ image_policy);
_cleanup_(dissected_image_unrefp) DissectedImage *img = NULL;
_cleanup_close_ int new_mount_fd = -EBADF;
_cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR;
pid_t child;
- if (is_image) {
+ if (flags & MOUNT_IN_NAMESPACE_IS_IMAGE) {
r = verity_dissect_and_mount(
chased_src_fd,
chased_src_path,
@@ -1173,7 +1167,7 @@ static int mount_in_namespace(
"Failed to open mount source '%s': %m",
chased_src_path);
- if (read_only && mount_setattr(new_mount_fd, "", AT_EMPTY_PATH,
+ if ((flags & MOUNT_IN_NAMESPACE_READ_ONLY) && mount_setattr(new_mount_fd, "", AT_EMPTY_PATH,
&(struct mount_attr) {
.attr_set = MOUNT_ATTR_RDONLY,
}, MOUNT_ATTR_SIZE_VER0) < 0)
@@ -1201,7 +1195,7 @@ static int mount_in_namespace(
if (r == 0) {
errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
- if (make_file_or_directory)
+ if (flags & MOUNT_IN_NAMESPACE_MAKE_FILE_OR_DIRECTORY)
(void) mkdir_parents(dest, 0755);
if (img) {
@@ -1209,10 +1203,10 @@ static int mount_in_namespace(
DISSECT_IMAGE_TRY_ATOMIC_MOUNT_EXCHANGE |
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY;
- if (make_file_or_directory)
+ if (flags & MOUNT_IN_NAMESPACE_MAKE_FILE_OR_DIRECTORY)
f |= DISSECT_IMAGE_MKDIR;
- if (read_only)
+ if (flags & MOUNT_IN_NAMESPACE_READ_ONLY)
f |= DISSECT_IMAGE_READ_ONLY;
r = dissected_image_mount(
@@ -1223,7 +1217,7 @@ static int mount_in_namespace(
/* userns_fd= */ -EBADF,
f);
} else {
- if (make_file_or_directory)
+ if (flags & MOUNT_IN_NAMESPACE_MAKE_FILE_OR_DIRECTORY)
(void) make_mount_point_inode_from_stat(&st, dest, 0700);
r = mount_exchange_graceful(new_mount_fd, dest, /* mount_beneath= */ true);
@@ -1259,17 +1253,14 @@ int bind_mount_in_namespace(
const char *incoming_path,
const char *src,
const char *dest,
- bool read_only,
- bool make_file_or_directory) {
+ MountInNamespaceFlags flags) {
return mount_in_namespace(target,
propagate_path,
incoming_path,
src,
dest,
- read_only,
- make_file_or_directory,
- /* is_image = */ false,
+ flags & ~MOUNT_IN_NAMESPACE_IS_IMAGE,
/* options = */ NULL,
/* image_policy = */ NULL);
}
@@ -1280,8 +1271,7 @@ int mount_image_in_namespace(
const char *incoming_path,
const char *src,
const char *dest,
- bool read_only,
- bool make_file_or_directory,
+ MountInNamespaceFlags flags,
const MountOptions *options,
const ImagePolicy *image_policy) {
@@ -1290,9 +1280,7 @@ int mount_image_in_namespace(
incoming_path,
src,
dest,
- read_only,
- make_file_or_directory,
- /* is_image = */ true,
+ flags | MOUNT_IN_NAMESPACE_IS_IMAGE,
options,
image_policy);
}
diff --git a/src/shared/mount-util.h b/src/shared/mount-util.h
index 8014eb48cff..069378cf4d2 100644
--- a/src/shared/mount-util.h
+++ b/src/shared/mount-util.h
@@ -106,22 +106,26 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(char*, umount_and_unlink_and_free);
int mount_exchange_graceful(int fsmount_fd, const char *dest, bool mount_beneath);
+typedef enum MountInNamespaceFlags {
+ MOUNT_IN_NAMESPACE_READ_ONLY = 1 << 0,
+ MOUNT_IN_NAMESPACE_MAKE_FILE_OR_DIRECTORY = 1 << 1,
+ MOUNT_IN_NAMESPACE_IS_IMAGE = 1 << 2,
+} MountInNamespaceFlags;
+
int bind_mount_in_namespace(
const PidRef *target,
const char *propagate_path,
const char *incoming_path,
const char *src,
const char *dest,
- bool read_only,
- bool make_file_or_directory);
+ MountInNamespaceFlags flags);
int mount_image_in_namespace(
const PidRef *target,
const char *propagate_path,
const char *incoming_path,
const char *src,
const char *dest,
- bool read_only,
- bool make_file_or_directory,
+ MountInNamespaceFlags flags,
const MountOptions *options,
const ImagePolicy *image_policy);
diff --git a/src/systemctl/systemctl-is-active.c b/src/systemctl/systemctl-is-active.c
index 596320a8c61..ae834f1071a 100644
--- a/src/systemctl/systemctl-is-active.c
+++ b/src/systemctl/systemctl-is-active.c
@@ -57,6 +57,7 @@ int verb_is_active(int argc, char *argv[], void *userdata) {
static const UnitActiveState states[] = {
UNIT_ACTIVE,
UNIT_RELOADING,
+ UNIT_REFRESHING,
};
/* According to LSB: 3, "program is not running" */
diff --git a/src/systemctl/systemctl-list-dependencies.c b/src/systemctl/systemctl-list-dependencies.c
index 3df9b7abdff..8c1ebf9d0f7 100644
--- a/src/systemctl/systemctl-list-dependencies.c
+++ b/src/systemctl/systemctl-list-dependencies.c
@@ -22,6 +22,7 @@ static int list_dependencies_print(const char *name, UnitActiveState state, int
switch (state) {
case UNIT_ACTIVE:
case UNIT_RELOADING:
+ case UNIT_REFRESHING:
case UNIT_ACTIVATING:
on = ansi_highlight_green();
break;
diff --git a/src/systemctl/systemctl-list-units.c b/src/systemctl/systemctl-list-units.c
index b4ccc8ebd76..a2f3074358e 100644
--- a/src/systemctl/systemctl-list-units.c
+++ b/src/systemctl/systemctl-list-units.c
@@ -149,7 +149,7 @@ static int output_units_list(const UnitInfo *unit_infos, size_t c) {
/* Here override any load_state highlighting */
on_circle = ansi_highlight_red();
circle = true;
- } else if (STR_IN_SET(u->active_state, "reloading", "activating", "maintenance", "deactivating")) {
+ } else if (STR_IN_SET(u->active_state, "reloading", "activating", "maintenance", "refreshing", "deactivating")) {
on_sub = on_active = ansi_highlight();
if (!circle) { /* Here we let load_state highlighting win */
diff --git a/src/systemctl/systemctl-show.c b/src/systemctl/systemctl-show.c
index 50f30d85658..2f39bc2b12f 100644
--- a/src/systemctl/systemctl-show.c
+++ b/src/systemctl/systemctl-show.c
@@ -301,7 +301,7 @@ static void format_active_state(const char *active_state, const char **active_on
if (streq_ptr(active_state, "failed")) {
*active_on = ansi_highlight_red();
*active_off = ansi_normal();
- } else if (STRPTR_IN_SET(active_state, "active", "reloading")) {
+ } else if (STRPTR_IN_SET(active_state, "active", "reloading", "refreshing")) {
*active_on = ansi_highlight_green();
*active_off = ansi_normal();
} else
@@ -440,10 +440,10 @@ static void print_status_info(
if (!isempty(i->result) && !streq(i->result, "success"))
printf(" (Result: %s)", i->result);
- timestamp = STRPTR_IN_SET(i->active_state, "active", "reloading") ? i->active_enter_timestamp :
- STRPTR_IN_SET(i->active_state, "inactive", "failed") ? i->inactive_enter_timestamp :
- STRPTR_IN_SET(i->active_state, "activating") ? i->inactive_exit_timestamp :
- i->active_exit_timestamp;
+ timestamp = STRPTR_IN_SET(i->active_state, "active", "reloading", "refreshing") ? i->active_enter_timestamp :
+ STRPTR_IN_SET(i->active_state, "inactive", "failed") ? i->inactive_enter_timestamp :
+ STRPTR_IN_SET(i->active_state, "activating") ? i->inactive_exit_timestamp :
+ i->active_exit_timestamp;
if (timestamp_is_set(timestamp)) {
printf(" since %s; %s\n",
@@ -2199,7 +2199,7 @@ static int show_one(
if (show_mode == SYSTEMCTL_SHOW_STATUS) {
print_status_info(bus, &info, ellipsized);
- if (info.active_state && !STR_IN_SET(info.active_state, "active", "reloading"))
+ if (info.active_state && !STR_IN_SET(info.active_state, "active", "reloading", "refreshing"))
return EXIT_PROGRAM_NOT_RUNNING;
return EXIT_PROGRAM_RUNNING_OR_SERVICE_OK;
diff --git a/src/systemctl/systemctl-util.c b/src/systemctl/systemctl-util.c
index f00b2d00229..848012de663 100644
--- a/src/systemctl/systemctl-util.c
+++ b/src/systemctl/systemctl-util.c
@@ -361,7 +361,7 @@ int get_active_triggering_units(sd_bus *bus, const char *unit, bool ignore_maske
if (r < 0)
return r;
- if (!IN_SET(active_state, UNIT_ACTIVE, UNIT_RELOADING))
+ if (!IN_SET(active_state, UNIT_ACTIVE, UNIT_RELOADING, UNIT_REFRESHING))
continue;
r = strv_extend(&active, *i);
diff --git a/test/units/TEST-23-UNIT-FILE.runtime-bind-paths.sh b/test/units/TEST-23-UNIT-FILE.runtime-bind-paths.sh
index 3a78234cdc7..32adcf785f7 100755
--- a/test/units/TEST-23-UNIT-FILE.runtime-bind-paths.sh
+++ b/test/units/TEST-23-UNIT-FILE.runtime-bind-paths.sh
@@ -34,10 +34,14 @@ systemctl bind --mkdir TEST-23-UNIT-FILE-namespaced.service /run/TEST-23-UNIT-FI
timeout 10 bash -xec 'while [[ "$(systemctl show -P SubState TEST-23-UNIT-FILE-namespaced.service)" == running ]]; do sleep .5; done'
systemctl is-active TEST-23-UNIT-FILE-namespaced.service
+test "$(busctl --json=short get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/TEST_2d23_2dUNIT_2dFILE_2dnamespaced_2eservice org.freedesktop.systemd1.Unit CanLiveMount)" = "{\"type\":\"b\",\"data\":true}"
+
# Now test that systemctl bind fails when attempted on a non-namespaced unit
systemctl start TEST-23-UNIT-FILE-non-namespaced.service
(! systemctl bind --mkdir TEST-23-UNIT-FILE-non-namespaced.service /run/TEST-23-UNIT-FILE-marker-runtime /tmp/testfile-marker-runtime)
+test "$(busctl --json=short get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/TEST_2d23_2dUNIT_2dFILE_2dnon_2dnamespaced_2eservice org.freedesktop.systemd1.Unit CanLiveMount)" = "{\"type\":\"b\",\"data\":false}"
+
timeout 10 bash -xec 'while [[ "$(systemctl show -P SubState TEST-23-UNIT-FILE-non-namespaced.service)" == running ]]; do sleep .5; done'
(! systemctl is-active TEST-23-UNIT-FILE-non-namespaced.service)