1
0
mirror of https://github.com/systemd/systemd.git synced 2024-12-22 17:35:35 +03:00

core: do BindMount/MountImage operations in async control process

These operations might require slow I/O, and thus might block PID1's main
loop for an undeterminated amount of time. Instead of performing them
inline, fork a worker process and stash away the D-Bus message, and reply
once we get a SIGCHILD indicating they have completed. That way we don't
break compatibility and callers can continue to rely on the fact that when
they get the method reply the operation either succeeded or failed.

To keep backward compatibility, unlike reload control processes, these
are ran inside init.scope and not the target cgroup. Unlike ExecReload,
this is under our control and is not defined by the unit. This is necessary
because previously the operation also wasn't ran from the target cgroup,
so suddenly forking a copy-on-write copy of pid1 into the target cgroup
will make memory usage spike, and if there is a MemoryMax= or MemoryHigh=
set and the cgroup is already close to the limit, it will cause an OOM
kill, where previously it would have worked fine.
This commit is contained in:
Luca Boccassi 2024-04-29 16:14:12 +01:00 committed by Luca Boccassi
parent 5121f7c45b
commit 5162829ec8
25 changed files with 480 additions and 146 deletions

View File

@ -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 {
<!--property CanFreeze is not documented!-->
<!--property CanLiveMount is not documented!-->
<!--property SurviveFinalKillSignal is not documented!-->
<!--property OnSuccessJobMode is not documented!-->
@ -2382,6 +2386,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<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"/>
@ -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 {
<!--property CleanResult is not documented!-->
<!--property LiveMountResult is not documented!-->
<!--property USBFunctionDescriptors is not documented!-->
<!--property USBFunctionStrings is not documented!-->
@ -4055,6 +4064,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<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"/>
@ -12090,7 +12101,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
<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>
@ -12136,6 +12148,7 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
<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>

View File

@ -291,7 +291,8 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<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

View File

@ -89,28 +89,22 @@
<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>

View File

@ -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,

View File

@ -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,

View File

@ -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),

View File

@ -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),

View File

@ -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

View File

@ -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))

View File

@ -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;

View File

@ -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,

View File

@ -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) {

View File

@ -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) {

View File

@ -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",

View File

@ -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);

View File

@ -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);

View File

@ -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) },
{}
};

View File

@ -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);
}

View File

@ -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);

View File

@ -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" */

View File

@ -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;

View File

@ -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 */

View File

@ -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;

View File

@ -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);

View File

@ -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)