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 = ...;
<!--property CanFreeze is not documented!-->
+ <!--property CanLiveMount is not documented!-->
+
<!--property SurviveFinalKillSignal is not documented!-->
<!--property OnSuccessJobMode is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="CanFreeze"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="CanLiveMount"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="Job"/>
<variablelist class="dbus-property" generated="True" extra-ref="StopWhenUnneeded"/>
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")
<!--property CleanResult is not documented!-->
+ <!--property LiveMountResult is not documented!-->
+
<!--property USBFunctionDescriptors is not documented!-->
<!--property USBFunctionStrings is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="CleanResult"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="LiveMountResult"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="USBFunctionDescriptors"/>
<variablelist class="dbus-property" generated="True" extra-ref="USBFunctionStrings"/>
<para><function>QueueSignal()</function> was added in version 254.</para>
<para><varname>SurviveFinalKillSignal</varname> was added in version 255.</para>
<para><varname>WantsMountsFor</varname> was added in version 256.</para>
- <para><varname>DebugInvocation</varname> was added in version 257.</para>
+ <para><varname>DebugInvocation</varname>, and
+ <varname>CanLiveMount</varname> were added in version 257.</para>
</refsect2>
<refsect2>
<title>Service Unit Objects</title>
<varname>ExecMainHandoffTimestamp</varname> were added in version 256.</para>
<para><varname>StatusBusError</varname>,
<varname>StatusVarlinkError</varname>,
+ <varname>LiveMountResult</varname>,
<varname>PrivateTmpEx</varname>, and
<varname>ImportCredentialEx</varname> were added in version 257.</para>
</refsect2>
<literal>inactive</literal> or <literal>maintenance</literal> is a white circle ("○"),
<literal>active</literal> is a green dot ("●"), <literal>deactivating</literal> is a white dot,
<literal>failed</literal> or <literal>error</literal> is a red cross ("×"), and
- <literal>reloading</literal> is a green clockwise circle arrow ("↻").</para>
+ <literal>reloading</literal> or <literal>refreshing</literal> is a green clockwise circle arrow
+ ("↻").</para>
<para>The "Loaded:" line in the output will show <literal>loaded</literal> if the unit has been
loaded into memory. Other possible values for "Loaded:" include: <literal>error</literal> if
<refsect1>
<title>Units</title>
- <para>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
+ <para>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
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
- 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.</para>
+ 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.</para>
<para>The following unit types are available:</para>
[UNIT_ACTIVATING] = "activating",
[UNIT_DEACTIVATING] = "deactivating",
[UNIT_MAINTENANCE] = "maintenance",
+ [UNIT_REFRESHING] = "refreshing",
};
DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState);
[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);
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,
UNIT_ACTIVATING,
UNIT_DEACTIVATING,
UNIT_MAINTENANCE,
+ UNIT_REFRESHING,
_UNIT_ACTIVE_STATE_MAX,
_UNIT_ACTIVE_STATE_INVALID = -EINVAL,
} UnitActiveState;
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,
#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"
}
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;
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)
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);
+ 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;
+
+ r = unit_live_mount(u, src, dest, message, flags, options, error);
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 r;
- return sd_bus_reply_method_return(message, NULL);
+ return 1;
}
int bus_service_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error) {
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),
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,
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),
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
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))
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;
#include "alloc-util.h"
#include "async.h"
+#include "bus-common-errors.h"
#include "bus-error.h"
#include "bus-kernel.h"
#include "bus-util.h"
#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"
[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
[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);
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);
}
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) {
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) {
"%sResult: %s\n"
"%sReload Result: %s\n"
"%sClean Result: %s\n"
+ "%sMount Result: %s\n"
"%sPermissionsStartOnly: %s\n"
"%sRootDirectoryStartOnly: %s\n"
"%sRemainAfterExit: %s\n"
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),
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);
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)) {
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));
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;
}
(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)) {
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));
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:
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. */
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 &&
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();
}
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) {
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) {
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);
SERVICE_RELOAD,
SERVICE_RELOAD_SIGNAL,
SERVICE_RELOAD_NOTIFY,
+ SERVICE_MOUNTING,
SERVICE_STOP,
SERVICE_STOP_WATCHDOG,
SERVICE_STOP_SIGTERM,
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));
.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,
ServiceResult result;
ServiceResult reload_result;
ServiceResult clean_result;
+ ServiceResult live_mount_result;
bool main_pid_known:1;
bool main_pid_alien:1;
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) {
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) {
/* 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) {
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) {
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))
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)
(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));
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) {
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",
#include "emergency-action.h"
#include "install.h"
#include "list.h"
+#include "mount-util.h"
#include "pidref.h"
#include "unit-file.h"
} 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) {
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);
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);
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);
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;
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);
char *active_state;
uint32_t job_id;
char *clean_result;
+ char *live_mount_result;
} WaitForItem;
typedef struct BusWaitForUnits {
free(item->bus_path);
free(item->active_state);
free(item->clean_result);
+ free(item->live_mount_result);
return mfree(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;
}
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) },
{}
};
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;
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);
/* 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);
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,
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;
* 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);
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);
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
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);
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);
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) {
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;
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,
"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)
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) {
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(
/* 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);
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);
}
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) {
incoming_path,
src,
dest,
- read_only,
- make_file_or_directory,
- /* is_image = */ true,
+ flags | MOUNT_IN_NAMESPACE_IS_IMAGE,
options,
image_policy);
}
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);
static const UnitActiveState states[] = {
UNIT_ACTIVE,
UNIT_RELOADING,
+ UNIT_REFRESHING,
};
/* According to LSB: 3, "program is not running" */
switch (state) {
case UNIT_ACTIVE:
case UNIT_RELOADING:
+ case UNIT_REFRESHING:
case UNIT_ACTIVATING:
on = ansi_highlight_green();
break;
/* 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 */
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
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",
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;
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);
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)