Merge pull request #27135 from poettering/pin-fdstore

Allow the per-service fdstore to be "pinned", i.e. preserved as long as the unit info remains in memory
This commit is contained in:
Lennart Poettering
2023-04-13 07:16:24 +02:00
committed by GitHub
32 changed files with 451 additions and 104 deletions
-2
View File
@@ -1479,8 +1479,6 @@ Features:
* maybe rework get_user_creds() to query the user database if $SHELL is used
for root, but only then.
* be stricter with fds we receive for the fdstore: close them asynchronously
* calenderspec: add support for week numbers and day numbers within a
year. This would allow us to define "bi-weekly" triggers safely.
+6
View File
@@ -2619,6 +2619,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
readonly u FileDescriptorStoreMax = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly u NFileDescriptorStore = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly s FileDescriptorStorePreserve = '...';
readonly s StatusText = '...';
readonly i StatusErrno = ...;
readonly s Result = '...';
@@ -3244,6 +3246,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<!--property NFileDescriptorStore is not documented!-->
<!--property FileDescriptorStorePreserve is not documented!-->
<!--property StatusErrno is not documented!-->
<!--property ReloadResult is not documented!-->
@@ -3830,6 +3834,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<variablelist class="dbus-property" generated="True" extra-ref="NFileDescriptorStore"/>
<variablelist class="dbus-property" generated="True" extra-ref="FileDescriptorStorePreserve"/>
<variablelist class="dbus-property" generated="True" extra-ref="StatusText"/>
<variablelist class="dbus-property" generated="True" extra-ref="StatusErrno"/>
+18 -10
View File
@@ -538,12 +538,16 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<varname>StateDirectory=</varname>, <varname>CacheDirectory=</varname>,
<varname>LogsDirectory=</varname> and <varname>RuntimeDirectory=</varname>, see
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details. For timer units this may be used to clear out the persistent timestamp data if
for details. It may also be used to clear the file decriptor store as enabled via
<varname>FileDescriptorStoreMax=</varname>, see
<citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details. For timer units this may be used to clear out the persistent timestamp data if
<varname>Persistent=</varname> is used and <option>--what=state</option> is selected, see
<citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>. This
command only applies to units that use either of these settings. If <option>--what=</option> is
not specified, both the cache and runtime data are removed (as these two types of data are
generally redundant and reproducible on the next invocation of the unit).</para>
not specified, the cache and runtime data as well as the file descriptor store are removed (as
these three types of resources are generally redundant and reproducible on the next invocation of
the unit). Note that the specified units must be stopped to invoke this operation.</para>
</listitem>
</varlistentry>
<varlistentry>
@@ -2193,13 +2197,17 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<listitem>
<para>Select what type of per-unit resources to remove when the <command>clean</command> command is
invoked, see below. Takes one of <constant>configuration</constant>, <constant>state</constant>,
<constant>cache</constant>, <constant>logs</constant>, <constant>runtime</constant> to select the
type of resource. This option may be specified more than once, in which case all specified resource
types are removed. Also accepts the special value <constant>all</constant> as a shortcut for
specifying all five resource types. If this option is not specified defaults to the combination of
<constant>cache</constant> and <constant>runtime</constant>, i.e. the two kinds of resources that
are generally considered to be redundant and can be reconstructed on next invocation.</para>
invoked, see above. Takes one of <constant>configuration</constant>, <constant>state</constant>,
<constant>cache</constant>, <constant>logs</constant>, <constant>runtime</constant>,
<constant>fdstore</constant> to select the type of resource. This option may be specified more than
once, in which case all specified resource types are removed. Also accepts the special value
<constant>all</constant> as a shortcut for specifying all six resource types. If this option is not
specified defaults to the combination of <constant>cache</constant>, <constant>runtime</constant>
and <constant>fdstore</constant>, i.e. the three kinds of resources that are generally considered
to be redundant and can be reconstructed on next invocation. Note that the explicit removal of the
<constant>fdstore</constant> resource type is only useful if the
<varname>FileDescriptorStorePreserve=</varname> option is enabled, since the file descriptor store
is otherwise cleaned automatically when the unit is stopped.</para>
</listitem>
</varlistentry>
+19 -2
View File
@@ -1066,7 +1066,7 @@
<literal>FDSTORE=1</literal> messages. This is useful for implementing services that can restart
after an explicit request or a crash without losing state. Any open sockets and other file
descriptors which should not be closed during the restart may be stored this way. Application state
can either be serialized to a file in <filename>/run/</filename>, or better, stored in a
can either be serialized to a file in <varname>RuntimeDirectory=</varname>, or stored in a
<citerefentry><refentrytitle>memfd_create</refentrytitle><manvolnum>2</manvolnum></citerefentry>
memory file descriptor. Defaults to 0, i.e. no file descriptors may be stored in the service
manager. All file descriptors passed to the service manager from a specific service are passed back
@@ -1075,7 +1075,8 @@
details about the precise protocol used and the order in which the file descriptors are passed). Any
file descriptors passed to the service manager are automatically closed when
<constant>POLLHUP</constant> or <constant>POLLERR</constant> is seen on them, or when the service is
fully stopped and no job is queued or being executed for it. If this option is used,
fully stopped and no job is queued or being executed for it (the latter can be tweaked with
<varname>FileDescriptorStorePreserve=</varname>, see below). If this option is used,
<varname>NotifyAccess=</varname> (see above) should be set to open access to the notification socket
provided by systemd. If <varname>NotifyAccess=</varname> is not set, it will be implicitly set to
<option>main</option>.</para>
@@ -1097,6 +1098,22 @@
details.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>FileDescriptorStorePreserve=</varname></term>
<listitem><para>Takes one of <constant>no</constant>, <constant>yes</constant>,
<constant>restart</constant> and controls when to release the service's file descriptor store
(i.e. when to close the contained file descriptors, if any). If set to <constant>no</constant> the
file descriptor store is automatically released when the service is stopped; if
<constant>restart</constant> (the default) it is kept around as long as the unit is neither inactive
nor failed, or a job is queued for the service, or the service is expected to be restarted. If
<constant>yes</constant> the file descriptor store is kept around until the unit is removed from
memory (i.e. is not referenced anymore and inactive). The latter is useful to keep entries in the
file descriptor store pinned until the service manage exits.</para>
<para>Use <command>systemctl clean --what=fdstore …</command> to release the file descriptor store
explicitly.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>USBFunctionDescriptors=</varname></term>
<listitem><para>Configure the location of a file containing
+1
View File
@@ -201,6 +201,7 @@ static const char* const service_state_table[_SERVICE_STATE_MAX] = {
[SERVICE_FAILED] = "failed",
[SERVICE_DEAD_BEFORE_AUTO_RESTART] = "dead-before-auto-restart",
[SERVICE_FAILED_BEFORE_AUTO_RESTART] = "failed-before-auto-restart",
[SERVICE_DEAD_RESOURCES_PINNED] = "dead-resources-pinned",
[SERVICE_AUTO_RESTART] = "auto-restart",
[SERVICE_CLEANING] = "cleaning",
};
+1
View File
@@ -146,6 +146,7 @@ typedef enum ServiceState {
SERVICE_FAILED,
SERVICE_DEAD_BEFORE_AUTO_RESTART,
SERVICE_FAILED_BEFORE_AUTO_RESTART,
SERVICE_DEAD_RESOURCES_PINNED, /* Like SERVICE_DEAD, but with pinned resources */
SERVICE_AUTO_RESTART,
SERVICE_CLEANING,
_SERVICE_STATE_MAX,
+4 -4
View File
@@ -49,7 +49,7 @@
BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_exec_output, exec_output, ExecOutput);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_input, exec_input, ExecInput);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_preserve_mode, exec_preserve_mode, ExecPreserveMode);
BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_exec_preserve_mode, exec_preserve_mode, ExecPreserveMode);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_keyring_mode, exec_keyring_mode, ExecKeyringMode);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_protect_proc, protect_proc, ProtectProc);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_proc_subset, proc_subset, ProcSubset);
@@ -1318,7 +1318,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("LockPersonality", "b", bus_property_get_bool, offsetof(ExecContext, lock_personality), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RestrictAddressFamilies", "(bas)", property_get_address_families, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RuntimeDirectorySymlink", "a(sst)", bus_property_get_exec_dir_symlink, offsetof(ExecContext, directories[EXEC_DIRECTORY_RUNTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RuntimeDirectoryPreserve", "s", property_get_exec_preserve_mode, offsetof(ExecContext, runtime_directory_preserve_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RuntimeDirectoryPreserve", "s", bus_property_get_exec_preserve_mode, offsetof(ExecContext, runtime_directory_preserve_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RuntimeDirectoryMode", "u", bus_property_get_mode, offsetof(ExecContext, directories[EXEC_DIRECTORY_RUNTIME].mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RuntimeDirectory", "as", bus_property_get_exec_dir, offsetof(ExecContext, directories[EXEC_DIRECTORY_RUNTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StateDirectorySymlink", "a(sst)", bus_property_get_exec_dir_symlink, offsetof(ExecContext, directories[EXEC_DIRECTORY_STATE]), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -1695,7 +1695,7 @@ static BUS_DEFINE_SET_TRANSIENT_PARSE(protect_home, ProtectHome, protect_home_fr
static BUS_DEFINE_SET_TRANSIENT_PARSE(keyring_mode, ExecKeyringMode, exec_keyring_mode_from_string);
static BUS_DEFINE_SET_TRANSIENT_PARSE(protect_proc, ProtectProc, protect_proc_from_string);
static BUS_DEFINE_SET_TRANSIENT_PARSE(proc_subset, ProcSubset, proc_subset_from_string);
static BUS_DEFINE_SET_TRANSIENT_PARSE(preserve_mode, ExecPreserveMode, exec_preserve_mode_from_string);
BUS_DEFINE_SET_TRANSIENT_PARSE(exec_preserve_mode, ExecPreserveMode, exec_preserve_mode_from_string);
static BUS_DEFINE_SET_TRANSIENT_PARSE_PTR(personality, unsigned long, parse_personality);
static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(secure_bits, "i", int32_t, int, "%" PRIi32, secure_bits_to_string_alloc_with_check);
static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(capability, "t", uint64_t, uint64_t, "%" PRIu64, capability_set_to_string);
@@ -2047,7 +2047,7 @@ int bus_exec_context_set_transient_property(
return bus_set_transient_proc_subset(u, name, &c->proc_subset, message, flags, error);
if (streq(name, "RuntimeDirectoryPreserve"))
return bus_set_transient_preserve_mode(u, name, &c->runtime_directory_preserve_mode, message, flags, error);
return bus_set_transient_exec_preserve_mode(u, name, &c->runtime_directory_preserve_mode, message, flags, error);
if (streq(name, "UMask"))
return bus_set_transient_mode_t(u, name, &c->umask, message, flags, error);
+2
View File
@@ -28,6 +28,8 @@ int bus_property_get_exec_output(sd_bus *bus, const char *path, const char *inte
int bus_property_get_exec_command(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error);
int bus_property_get_exec_command_list(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error);
int bus_property_get_exec_ex_command_list(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error);
int bus_property_get_exec_preserve_mode(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error);
int bus_exec_context_set_transient_property(Unit *u, ExecContext *c, const char *name, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
int bus_set_transient_exec_command(Unit *u, const char *name, ExecCommand **exec_command, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
int bus_set_transient_exec_preserve_mode(Unit *u, const char *name, ExecPreserveMode *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
+4
View File
@@ -349,6 +349,7 @@ const sd_bus_vtable bus_service_vtable[] = {
SD_BUS_PROPERTY("BusName", "s", NULL, offsetof(Service, bus_name), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("FileDescriptorStoreMax", "u", bus_property_get_unsigned, offsetof(Service, n_fd_store_max), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("NFileDescriptorStore", "u", property_get_size_as_uint32, offsetof(Service, n_fd_store), 0),
SD_BUS_PROPERTY("FileDescriptorStorePreserve", "s", bus_property_get_exec_preserve_mode, offsetof(Service, fd_store_preserve_mode), 0),
SD_BUS_PROPERTY("StatusText", "s", NULL, offsetof(Service, status_text), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("StatusErrno", "i", bus_property_get_int, offsetof(Service, status_errno), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Service, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
@@ -599,6 +600,9 @@ static int bus_service_set_transient_property(
if (streq(name, "FileDescriptorStoreMax"))
return bus_set_transient_unsigned(u, name, &s->n_fd_store_max, message, flags, error);
if (streq(name, "FileDescriptorStorePreserve"))
return bus_set_transient_exec_preserve_mode(u, name, &s->fd_store_preserve_mode, message, flags, error);
if (streq(name, "NotifyAccess"))
return bus_set_transient_notify_access(u, name, &s->notify_access, message, flags, error);
+11 -10
View File
@@ -90,6 +90,12 @@ static int property_get_can_clean(
return r;
}
if (FLAGS_SET(mask, EXEC_CLEAN_FDSTORE)) {
r = sd_bus_message_append(reply, "s", "fdstore");
if (r < 0)
return r;
}
return sd_bus_message_close_container(reply);
}
@@ -696,6 +702,7 @@ int bus_unit_method_clean(sd_bus_message *message, void *userdata, sd_bus_error
return r;
for (;;) {
ExecCleanMask m;
const char *i;
r = sd_bus_message_read(message, "s", &i);
@@ -704,17 +711,11 @@ int bus_unit_method_clean(sd_bus_message *message, void *userdata, sd_bus_error
if (r == 0)
break;
if (streq(i, "all"))
mask |= EXEC_CLEAN_ALL;
else {
ExecDirectoryType t;
m = exec_clean_mask_from_string(i);
if (m < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid resource type: %s", i);
t = exec_resource_type_from_string(i);
if (t < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid resource type: %s", i);
mask |= 1U << t;
}
mask |= m;
}
r = sd_bus_message_exit_container(message);
+17
View File
@@ -7582,6 +7582,23 @@ void exec_directory_sort(ExecDirectory *d) {
}
}
ExecCleanMask exec_clean_mask_from_string(const char *s) {
ExecDirectoryType t;
assert(s);
if (streq(s, "all"))
return EXEC_CLEAN_ALL;
if (streq(s, "fdstore"))
return EXEC_CLEAN_FDSTORE;
t = exec_resource_type_from_string(s);
if (t < 0)
return (ExecCleanMask) t;
return 1U << t;
}
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(exec_set_credential_hash_ops, char, string_hash_func, string_compare_func, ExecSetCredential, exec_set_credential_free);
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(exec_load_credential_hash_ops, char, string_hash_func, string_compare_func, ExecLoadCredential, exec_load_credential_free);
+4 -1
View File
@@ -161,8 +161,9 @@ typedef enum ExecCleanMask {
EXEC_CLEAN_CACHE = 1U << EXEC_DIRECTORY_CACHE,
EXEC_CLEAN_LOGS = 1U << EXEC_DIRECTORY_LOGS,
EXEC_CLEAN_CONFIGURATION = 1U << EXEC_DIRECTORY_CONFIGURATION,
EXEC_CLEAN_FDSTORE = 1U << _EXEC_DIRECTORY_TYPE_MAX,
EXEC_CLEAN_NONE = 0,
EXEC_CLEAN_ALL = (1U << _EXEC_DIRECTORY_TYPE_MAX) - 1,
EXEC_CLEAN_ALL = (1U << (_EXEC_DIRECTORY_TYPE_MAX+1)) - 1,
_EXEC_CLEAN_MASK_INVALID = -EINVAL,
} ExecCleanMask;
@@ -520,6 +521,8 @@ void exec_directory_done(ExecDirectory *d);
int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink);
void exec_directory_sort(ExecDirectory *d);
ExecCleanMask exec_clean_mask_from_string(const char *s);
extern const struct hash_ops exec_set_credential_hash_ops;
extern const struct hash_ops exec_load_credential_hash_ops;
+2 -1
View File
@@ -136,7 +136,7 @@
{{type}}.MountFlags, config_parse_exec_mount_propagation_flag, 0, offsetof({{type}}, exec_context.mount_propagation_flag)
{{type}}.MountAPIVFS, config_parse_exec_mount_apivfs, 0, offsetof({{type}}, exec_context)
{{type}}.Personality, config_parse_personality, 0, offsetof({{type}}, exec_context.personality)
{{type}}.RuntimeDirectoryPreserve, config_parse_runtime_preserve_mode, 0, offsetof({{type}}, exec_context.runtime_directory_preserve_mode)
{{type}}.RuntimeDirectoryPreserve, config_parse_exec_preserve_mode, 0, offsetof({{type}}, exec_context.runtime_directory_preserve_mode)
{{type}}.RuntimeDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_RUNTIME].mode)
{{type}}.RuntimeDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_RUNTIME])
{{type}}.StateDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_STATE].mode)
@@ -433,6 +433,7 @@ Service.SysVStartPriority, config_parse_warn_compat,
Service.NonBlocking, config_parse_bool, 0, offsetof(Service, exec_context.non_blocking)
Service.BusName, config_parse_bus_name, 0, offsetof(Service, bus_name)
Service.FileDescriptorStoreMax, config_parse_unsigned, 0, offsetof(Service, n_fd_store_max)
Service.FileDescriptorStorePreserve, config_parse_exec_preserve_mode, 0, offsetof(Service, fd_store_preserve_mode)
Service.NotifyAccess, config_parse_notify_access, 0, offsetof(Service, notify_access)
Service.Sockets, config_parse_service_sockets, 0, 0
Service.BusPolicy, config_parse_warn_compat, DISABLED_LEGACY, 0
+1 -1
View File
@@ -138,7 +138,7 @@ DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to pa
DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_home, protect_home, ProtectHome, "Failed to parse protect home value");
DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_system, protect_system, ProtectSystem, "Failed to parse protect system value");
DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode, exec_preserve_mode, ExecPreserveMode, "Failed to parse runtime directory preserve mode");
DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_preserve_mode, exec_preserve_mode, ExecPreserveMode, "Failed to parse resource preserve mode");
DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
DEFINE_CONFIG_PARSE_ENUM(config_parse_service_exit_type, service_exit_type, ServiceExitType, "Failed to parse service exit type");
DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
+1 -1
View File
@@ -101,7 +101,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_exec_selinux_context);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_apparmor_profile);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_smack_process_label);
CONFIG_PARSER_PROTOTYPE(config_parse_address_families);
CONFIG_PARSER_PROTOTYPE(config_parse_runtime_preserve_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_preserve_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_directories);
CONFIG_PARSER_PROTOTYPE(config_parse_set_credential);
CONFIG_PARSER_PROTOTYPE(config_parse_load_credential);
+23
View File
@@ -1250,6 +1250,26 @@ static unsigned manager_dispatch_cleanup_queue(Manager *m) {
return n;
}
static unsigned manager_dispatch_release_resources_queue(Manager *m) {
unsigned n = 0;
Unit *u;
assert(m);
while ((u = m->release_resources_queue)) {
assert(u->in_release_resources_queue);
LIST_REMOVE(release_resources_queue, m->release_resources_queue, u);
u->in_release_resources_queue = false;
n++;
unit_release_resources(u);
}
return n;
}
enum {
GC_OFFSET_IN_PATH, /* This one is on the path we were traveling */
GC_OFFSET_UNSURE, /* No clue */
@@ -3158,6 +3178,9 @@ int manager_loop(Manager *m) {
if (manager_dispatch_stop_when_unneeded_queue(m) > 0)
continue;
if (manager_dispatch_release_resources_queue(m) > 0)
continue;
if (manager_dispatch_dbus_queue(m) > 0)
continue;
+3
View File
@@ -195,6 +195,9 @@ struct Manager {
/* Units that have BindsTo= another unit, and might need to be shutdown because the bound unit is not active. */
LIST_HEAD(Unit, stop_when_bound_queue);
/* Units that have resources open, and where it might be good to check if they can be released now */
LIST_HEAD(Unit, release_resources_queue);
sd_event *event;
/* This maps PIDs we care about to units that are interested in. We allow multiple units to be interested in
+119 -48
View File
@@ -69,6 +69,7 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
[SERVICE_FAILED] = UNIT_FAILED,
[SERVICE_DEAD_BEFORE_AUTO_RESTART] = UNIT_INACTIVE,
[SERVICE_FAILED_BEFORE_AUTO_RESTART] = UNIT_FAILED,
[SERVICE_DEAD_RESOURCES_PINNED] = UNIT_INACTIVE,
[SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
[SERVICE_CLEANING] = UNIT_MAINTENANCE,
};
@@ -97,6 +98,7 @@ static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] =
[SERVICE_FAILED] = UNIT_FAILED,
[SERVICE_DEAD_BEFORE_AUTO_RESTART] = UNIT_INACTIVE,
[SERVICE_FAILED_BEFORE_AUTO_RESTART] = UNIT_FAILED,
[SERVICE_DEAD_RESOURCES_PINNED] = UNIT_INACTIVE,
[SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
[SERVICE_CLEANING] = UNIT_MAINTENANCE,
};
@@ -139,6 +141,8 @@ static void service_init(Unit *u) {
s->oom_policy = _OOM_POLICY_INVALID;
s->reload_begin_usec = USEC_INFINITY;
s->reload_signal = SIGHUP;
s->fd_store_preserve_mode = EXEC_PRESERVE_RESTART;
}
static void service_unwatch_control_pid(Service *s) {
@@ -196,9 +200,14 @@ static int service_set_main_pid(Service *s, pid_t pid) {
return 0;
}
void service_close_socket_fd(Service *s) {
void service_release_socket_fd(Service *s) {
assert(s);
if (s->socket_fd < 0 && !UNIT_ISSET(s->accept_socket) && !s->socket_peer)
return;
log_unit_debug(UNIT(s), "Closing connection socket.");
/* Undo the effect of service_set_socket_fd(). */
s->socket_fd = asynchronous_close(s->socket_fd);
@@ -380,41 +389,36 @@ static void service_fd_store_unlink(ServiceFDStore *fs) {
sd_event_source_disable_unref(fs->event_source);
free(fs->fdname);
safe_close(fs->fd);
asynchronous_close(fs->fd);
free(fs);
}
static void service_release_fd_store(Service *s) {
assert(s);
if (!s->fd_store)
return;
log_unit_debug(UNIT(s), "Releasing all stored fds");
while (s->fd_store)
service_fd_store_unlink(s->fd_store);
assert(s->n_fd_store == 0);
}
static void service_release_resources(Unit *u) {
Service *s = SERVICE(u);
static void service_release_stdio_fd(Service *s) {
assert(s);
/* Don't release resources if this is a transitionary failed/dead state */
if (IN_SET(s->state, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART))
if (s->stdin_fd < 0 && s->stdout_fd < 0 && s->stdout_fd < 0)
return;
if (!s->fd_store && s->stdin_fd < 0 && s->stdout_fd < 0 && s->stderr_fd < 0)
return;
log_unit_debug(UNIT(s), "Releasing stdin/stdout/stderr file descriptors.");
log_unit_debug(u, "Releasing resources.");
s->stdin_fd = safe_close(s->stdin_fd);
s->stdout_fd = safe_close(s->stdout_fd);
s->stderr_fd = safe_close(s->stderr_fd);
service_release_fd_store(s);
s->stdin_fd = asynchronous_close(s->stdin_fd);
s->stdout_fd = asynchronous_close(s->stdout_fd);
s->stderr_fd = asynchronous_close(s->stderr_fd);
}
static void service_done(Unit *u) {
Service *s = SERVICE(u);
@@ -450,8 +454,6 @@ static void service_done(Unit *u) {
s->usb_function_descriptors = mfree(s->usb_function_descriptors);
s->usb_function_strings = mfree(s->usb_function_strings);
service_close_socket_fd(s);
service_stop_watchdog(s);
s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source);
@@ -459,7 +461,9 @@ static void service_done(Unit *u) {
s->bus_name_pid_lookup_slot = sd_bus_slot_unref(s->bus_name_pid_lookup_slot);
service_release_resources(u);
service_release_socket_fd(s);
service_release_stdio_fd(s);
service_release_fd_store(s);
}
static int on_fd_store_io(sd_event_source *e, int fd, uint32_t revents, void *userdata) {
@@ -477,6 +481,7 @@ static int on_fd_store_io(sd_event_source *e, int fd, uint32_t revents, void *us
}
static int service_add_fd_store(Service *s, int fd, const char *name, bool do_poll) {
struct stat st;
ServiceFDStore *fs;
int r;
@@ -485,17 +490,23 @@ static int service_add_fd_store(Service *s, int fd, const char *name, bool do_po
assert(s);
assert(fd >= 0);
if (fstat(fd, &st) < 0)
return -errno;
log_unit_debug(UNIT(s), "Trying to stash fd for dev=" DEVNUM_FORMAT_STR "/inode=%" PRIu64, DEVNUM_FORMAT_VAL(st.st_dev), (uint64_t) st.st_ino);
if (s->n_fd_store >= s->n_fd_store_max)
return -EXFULL; /* Our store is full.
* Use this errno rather than E[NM]FILE to distinguish from
* the case where systemd itself hits the file limit. */
/* Our store is full. Use this errno rather than E[NM]FILE to distinguish from the case
* where systemd itself hits the file limit. */
return log_unit_debug_errno(UNIT(s), SYNTHETIC_ERRNO(EXFULL), "Hit fd store limit.");
LIST_FOREACH(fd_store, i, s->fd_store) {
r = same_fd(i->fd, fd);
if (r < 0)
return r;
if (r > 0) {
safe_close(fd);
log_unit_debug(UNIT(s), "Suppressing duplicate fd in fd store.");
asynchronous_close(fd);
return 0; /* fd already included */
}
}
@@ -538,7 +549,7 @@ static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name, bo
assert(s);
while (fdset_size(fds) > 0) {
_cleanup_close_ int fd = -EBADF;
_cleanup_(asynchronous_closep) int fd = -EBADF;
fd = fdset_steal_first(fds);
if (fd < 0)
@@ -553,7 +564,8 @@ static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name, bo
return log_unit_error_errno(UNIT(s), r, "Failed to add fd to store: %m");
if (r > 0)
log_unit_debug(UNIT(s), "Added fd %i (%s) to fd store.", fd, strna(name));
fd = -EBADF;
TAKE_FD(fd);
}
return 0;
@@ -1030,8 +1042,10 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
if (s->n_fd_store_max > 0)
fprintf(f,
"%sFile Descriptor Store Max: %u\n"
"%sFile Descriptor Store Pin: %s\n"
"%sFile Descriptor Store Current: %zu\n",
prefix, s->n_fd_store_max,
prefix, exec_preserve_mode_to_string(s->fd_store_preserve_mode),
prefix, s->n_fd_store);
service_dump_fdstore(s, f, prefix);
@@ -1243,20 +1257,12 @@ static void service_set_state(Service *s, ServiceState state) {
if (IN_SET(state,
SERVICE_DEAD, SERVICE_FAILED,
SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART, SERVICE_AUTO_RESTART)) {
SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART, SERVICE_AUTO_RESTART,
SERVICE_DEAD_RESOURCES_PINNED)) {
unit_unwatch_all_pids(UNIT(s));
unit_dequeue_rewatch_pids(UNIT(s));
}
if (!IN_SET(state,
SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
SERVICE_RUNNING,
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) &&
!(state == SERVICE_DEAD && UNIT(s)->job))
service_close_socket_fd(s);
if (state != SERVICE_START)
s->exec_fd_event_source = sd_event_source_disable_unref(s->exec_fd_event_source);
@@ -1359,7 +1365,8 @@ static int service_coldplug(Unit *u) {
if (!IN_SET(s->deserialized_state,
SERVICE_DEAD, SERVICE_FAILED,
SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART, SERVICE_AUTO_RESTART,
SERVICE_CLEANING)) {
SERVICE_CLEANING,
SERVICE_DEAD_RESOURCES_PINNED)) {
(void) unit_enqueue_rewatch_pids(u);
(void) unit_setup_exec_runtime(u);
}
@@ -1947,6 +1954,12 @@ static bool service_will_restart(Unit *u) {
return unit_will_restart_default(u);
}
static ServiceState service_determine_dead_state(Service *s) {
assert(s);
return s->fd_store && s->fd_store_preserve_mode == EXEC_PRESERVE_YES ? SERVICE_DEAD_RESOURCES_PINNED : SERVICE_DEAD;
}
static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) {
ServiceState end_state, restart_state;
int r;
@@ -1963,11 +1976,11 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
if (s->result == SERVICE_SUCCESS) {
unit_log_success(UNIT(s));
end_state = SERVICE_DEAD;
end_state = service_determine_dead_state(s);
restart_state = SERVICE_DEAD_BEFORE_AUTO_RESTART;
} else if (s->result == SERVICE_SKIP_CONDITION) {
unit_log_skip(UNIT(s), service_result_to_string(s->result));
end_state = SERVICE_DEAD;
end_state = service_determine_dead_state(s);
restart_state = SERVICE_DEAD_BEFORE_AUTO_RESTART;
} else {
unit_log_failure(UNIT(s), service_result_to_string(s->result));
@@ -2031,6 +2044,10 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
/* Also, remove the runtime directory */
unit_destroy_runtime_data(UNIT(s), &s->exec_context);
/* Also get rid of the fd store, if that's configured. */
if (s->fd_store_preserve_mode == EXEC_PRESERVE_NO)
service_release_fd_store(s);
/* Get rid of the IPC bits of the user */
unit_unref_uid_gid(UNIT(s), true);
@@ -2709,7 +2726,7 @@ static int service_start(Unit *u) {
if (IN_SET(s->state, SERVICE_AUTO_RESTART, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART))
return -EAGAIN;
assert(IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED));
assert(IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_DEAD_RESOURCES_PINNED));
r = unit_acquire_invocation_id(u);
if (r < 0)
@@ -2768,7 +2785,7 @@ static int service_stop(Unit *u) {
case SERVICE_AUTO_RESTART:
/* A restart will be scheduled or is in progress. */
service_set_state(s, SERVICE_DEAD);
service_set_state(s, service_determine_dead_state(s));
return 0;
case SERVICE_CONDITION:
@@ -2797,6 +2814,7 @@ static int service_stop(Unit *u) {
case SERVICE_FAILED_BEFORE_AUTO_RESTART:
case SERVICE_DEAD:
case SERVICE_FAILED:
case SERVICE_DEAD_RESOURCES_PINNED:
default:
/* Unknown state, or unit_stop() should already have handled these */
assert_not_reached();
@@ -3405,7 +3423,7 @@ static bool service_may_gc(Unit *u) {
/* Only allow collection of actually dead services, i.e. not those that are in the transitionary
* SERVICE_DEAD_BEFORE_AUTO_RESTART/SERVICE_FAILED_BEFORE_AUTO_RESTART states. */
if (!IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED))
if (!IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_DEAD_RESOURCES_PINNED))
return false;
return true;
@@ -3632,6 +3650,7 @@ static void service_notify_cgroup_empty_event(Unit *u) {
case SERVICE_DEAD_BEFORE_AUTO_RESTART:
case SERVICE_FAILED_BEFORE_AUTO_RESTART:
case SERVICE_AUTO_RESTART:
case SERVICE_DEAD_RESOURCES_PINNED:
unit_prune_cgroup(u);
break;
@@ -4720,7 +4739,7 @@ int service_set_socket_fd(
assert(!s->socket_peer);
if (s->state != SERVICE_DEAD)
if (!IN_SET(s->state, SERVICE_DEAD, SERVICE_DEAD_RESOURCES_PINNED))
return -EAGAIN;
if (getpeername_pretty(fd, true, &peer_text) >= 0) {
@@ -4757,7 +4776,7 @@ static void service_reset_failed(Unit *u) {
assert(s);
if (s->state == SERVICE_FAILED)
service_set_state(s, SERVICE_DEAD);
service_set_state(s, service_determine_dead_state(s));
s->result = SERVICE_SUCCESS;
s->reload_result = SERVICE_SUCCESS;
@@ -4845,22 +4864,39 @@ static const char* service_status_text(Unit *u) {
static int service_clean(Unit *u, ExecCleanMask mask) {
_cleanup_strv_free_ char **l = NULL;
bool may_clean_fdstore = false;
Service *s = SERVICE(u);
int r;
assert(s);
assert(mask != 0);
if (s->state != SERVICE_DEAD)
if (!IN_SET(s->state, SERVICE_DEAD, SERVICE_DEAD_RESOURCES_PINNED))
return -EBUSY;
/* Determine if there's anything we could potentially clean */
r = exec_context_get_clean_directories(&s->exec_context, u->manager->prefix, mask, &l);
if (r < 0)
return r;
if (strv_isempty(l))
return -EUNATCH;
if (mask & EXEC_CLEAN_FDSTORE)
may_clean_fdstore = s->n_fd_store > 0 || s->n_fd_store_max > 0;
if (strv_isempty(l) && !may_clean_fdstore)
return -EUNATCH; /* Nothing to potentially clean */
/* Let's clean the stuff we can clean quickly */
if (may_clean_fdstore)
service_release_fd_store(s);
/* If we are done, leave quickly */
if (strv_isempty(l)) {
if (s->state == SERVICE_DEAD_RESOURCES_PINNED && !s->fd_store)
service_set_state(s, SERVICE_DEAD);
return 0;
}
/* We need to clean disk stuff. This is slow, hence do it out of process, and change state */
service_unwatch_control_pid(s);
s->clean_result = SERVICE_SUCCESS;
s->control_command = NULL;
@@ -4887,10 +4923,21 @@ fail:
static int service_can_clean(Unit *u, ExecCleanMask *ret) {
Service *s = SERVICE(u);
ExecCleanMask mask = 0;
int r;
assert(s);
assert(ret);
return exec_context_get_clean_mask(&s->exec_context, ret);
r = exec_context_get_clean_mask(&s->exec_context, &mask);
if (r < 0)
return r;
if (s->n_fd_store_max > 0)
mask |= EXEC_CLEAN_FDSTORE;
*ret = mask;
return 0;
}
static const char *service_finished_job(Unit *u, JobType t, JobResult result) {
@@ -4919,6 +4966,30 @@ static int service_can_start(Unit *u) {
return 1;
}
static void service_release_resources(Unit *u) {
Service *s = SERVICE(ASSERT_PTR(u));
/* Invoked by the unit state engine, whenever it realizes that unit is dead and there's no job
* anymore for it, and it hence is a good idea to release resources */
/* Don't release resources if this is a transitionary failed/dead state
* (i.e. SERVICE_DEAD_BEFORE_AUTO_RESTART/SERVICE_FAILED_BEFORE_AUTO_RESTART), insist on a permanent
* failure state. */
if (!IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_DEAD_RESOURCES_PINNED))
return;
log_unit_debug(u, "Releasing resources...");
service_release_socket_fd(s);
service_release_stdio_fd(s);
if (s->fd_store_preserve_mode != EXEC_PRESERVE_YES)
service_release_fd_store(s);
if (s->state == SERVICE_DEAD_RESOURCES_PINNED && !s->fd_store)
service_set_state(s, SERVICE_DEAD);
}
static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
[SERVICE_RESTART_NO] = "no",
[SERVICE_RESTART_ON_SUCCESS] = "on-success",
+2 -1
View File
@@ -206,6 +206,7 @@ struct Service {
ServiceFDStore *fd_store;
size_t n_fd_store;
unsigned n_fd_store_max;
ExecPreserveMode fd_store_preserve_mode;
char *usb_function_descriptors;
char *usb_function_strings;
@@ -243,7 +244,7 @@ static inline usec_t service_get_watchdog_usec(Service *s) {
extern const UnitVTable service_vtable;
int service_set_socket_fd(Service *s, int fd, struct Socket *socket, struct SocketPeer *peer, bool selinux_context_net);
void service_close_socket_fd(Service *s);
void service_release_socket_fd(Service *s);
usec_t service_restart_usec_next(Service *s);
+1 -1
View File
@@ -2393,7 +2393,7 @@ static void socket_enter_running(Socket *s, int cfd_in) {
if (r < 0) {
/* We failed to activate the new service, but it still exists. Let's make sure the
* service closes and forgets the connection fd again, immediately. */
service_close_socket_fd(SERVICE(service));
service_release_socket_fd(SERVICE(service));
goto fail;
}
+1
View File
@@ -884,6 +884,7 @@ static int timer_can_clean(Unit *u, ExecCleanMask *ret) {
Timer *t = TIMER(u);
assert(t);
assert(ret);
*ret = t->persistent ? EXEC_CLEAN_STATE : 0;
return 0;
+80 -19
View File
@@ -394,37 +394,59 @@ static bool unit_success_failure_handler_has_jobs(Unit *unit) {
return false;
}
void unit_release_resources(Unit *u) {
UnitActiveState state;
ExecContext *ec;
assert(u);
if (u->job || u->nop_job)
return;
if (u->perpetual)
return;
state = unit_active_state(u);
if (!IN_SET(state, UNIT_INACTIVE, UNIT_FAILED))
return;
if (unit_will_restart(u))
return;
ec = unit_get_exec_context(u);
if (ec && ec->runtime_directory_preserve_mode == EXEC_PRESERVE_RESTART)
exec_context_destroy_runtime_directory(ec, u->manager->prefix[EXEC_DIRECTORY_RUNTIME]);
if (UNIT_VTABLE(u)->release_resources)
UNIT_VTABLE(u)->release_resources(u);
}
bool unit_may_gc(Unit *u) {
UnitActiveState state;
int r;
assert(u);
/* Checks whether the unit is ready to be unloaded for garbage collection.
* Returns true when the unit may be collected, and false if there's some
* reason to keep it loaded.
/* Checks whether the unit is ready to be unloaded for garbage collection. Returns true when the
* unit may be collected, and false if there's some reason to keep it loaded.
*
* References from other units are *not* checked here. Instead, this is done
* in unit_gc_sweep(), but using markers to properly collect dependency loops.
* References from other units are *not* checked here. Instead, this is done in unit_gc_sweep(), but
* using markers to properly collect dependency loops.
*/
if (u->job || u->nop_job)
return false;
state = unit_active_state(u);
/* If the unit is inactive and failed and no job is queued for it, then release its runtime resources */
if (UNIT_IS_INACTIVE_OR_FAILED(state) &&
UNIT_VTABLE(u)->release_resources)
UNIT_VTABLE(u)->release_resources(u);
if (u->perpetual)
return false;
if (sd_bus_track_count(u->bus_track) > 0)
return false;
/* But we keep the unit object around for longer when it is referenced or configured to not be gc'ed */
state = unit_active_state(u);
/* But we keep the unit object around for longer when it is referenced or configured to not be
* gc'ed */
switch (u->collect_mode) {
case COLLECT_INACTIVE:
@@ -458,10 +480,10 @@ bool unit_may_gc(Unit *u) {
return false;
}
if (UNIT_VTABLE(u)->may_gc && !UNIT_VTABLE(u)->may_gc(u))
return false;
if (!UNIT_VTABLE(u)->may_gc)
return true;
return true;
return UNIT_VTABLE(u)->may_gc(u);
}
void unit_add_to_load_queue(Unit *u) {
@@ -565,6 +587,40 @@ void unit_submit_to_stop_when_bound_queue(Unit *u) {
u->in_stop_when_bound_queue = true;
}
static bool unit_can_release_resources(Unit *u) {
ExecContext *ec;
assert(u);
if (UNIT_VTABLE(u)->release_resources)
return true;
ec = unit_get_exec_context(u);
if (ec && ec->runtime_directory_preserve_mode == EXEC_PRESERVE_RESTART)
return true;
return false;
}
void unit_submit_to_release_resources_queue(Unit *u) {
assert(u);
if (u->in_release_resources_queue)
return;
if (u->job || u->nop_job)
return;
if (u->perpetual)
return;
if (!unit_can_release_resources(u))
return;
LIST_PREPEND(release_resources_queue, u->manager->release_resources_queue, u);
u->in_release_resources_queue = true;
}
static void unit_clear_dependencies(Unit *u) {
assert(u);
@@ -781,6 +837,9 @@ Unit* unit_free(Unit *u) {
if (u->in_stop_when_bound_queue)
LIST_REMOVE(stop_when_bound_queue, u->manager->stop_when_bound_queue, u);
if (u->in_release_resources_queue)
LIST_REMOVE(release_resources_queue, u->manager->release_resources_queue, u);
bpf_firewall_close(u);
hashmap_free(u->bpf_foreign_by_key);
@@ -2567,7 +2626,6 @@ static bool unit_process_job(Job *j, UnitActiveState ns, UnitNotifyFlags flags)
assert(j);
if (j->state == JOB_WAITING)
/* So we reached a different state for this job. Let's see if we can run it now if it failed previously
* due to EAGAIN. */
job_add_to_run_queue(j);
@@ -2771,6 +2829,9 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag
/* Maybe the unit should be GC'ed now? */
unit_add_to_gc_queue(u);
/* Maybe we can release some resources now? */
unit_submit_to_release_resources_queue(u);
}
if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) {
@@ -5822,8 +5883,8 @@ void unit_destroy_runtime_data(Unit *u, const ExecContext *context) {
assert(u);
assert(context);
if (context->runtime_directory_preserve_mode == EXEC_PRESERVE_NO ||
(context->runtime_directory_preserve_mode == EXEC_PRESERVE_RESTART && !unit_will_restart(u)))
/* EXEC_PRESERVE_RESTART is handled via unit_release_resources()! */
if (context->runtime_directory_preserve_mode == EXEC_PRESERVE_NO)
exec_context_destroy_runtime_directory(context, u->manager->prefix[EXEC_DIRECTORY_RUNTIME]);
exec_context_destroy_credentials(context, u->manager->prefix[EXEC_DIRECTORY_RUNTIME], u->id);
+7
View File
@@ -317,6 +317,9 @@ typedef struct Unit {
/* Queue of units that have a BindTo= dependency on some other unit, and should possibly be shut down */
LIST_FIELDS(Unit, stop_when_bound_queue);
/* Queue of units that should be checked if they can release resources now */
LIST_FIELDS(Unit, release_resources_queue);
/* PIDs we keep an eye on. Note that a unit might have many
* more, but these are the ones we care enough about to
* process SIGCHLD for */
@@ -479,6 +482,7 @@ typedef struct Unit {
bool in_stop_when_unneeded_queue:1;
bool in_start_when_upheld_queue:1;
bool in_stop_when_bound_queue:1;
bool in_release_resources_queue:1;
bool sent_dbus_new_signal:1;
@@ -832,6 +836,8 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c);
int unit_choose_id(Unit *u, const char *name);
int unit_set_description(Unit *u, const char *description);
void unit_release_resources(Unit *u);
bool unit_may_gc(Unit *u);
static inline bool unit_is_extrinsic(Unit *u) {
@@ -853,6 +859,7 @@ void unit_add_to_target_deps_queue(Unit *u);
void unit_submit_to_stop_when_unneeded_queue(Unit *u);
void unit_submit_to_start_when_upheld_queue(Unit *u);
void unit_submit_to_stop_when_bound_queue(Unit *u);
void unit_submit_to_release_resources_queue(Unit *u);
int unit_merge(Unit *u, Unit *other);
int unit_merge_by_name(Unit *u, const char *other);
+2 -1
View File
@@ -2198,7 +2198,8 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con
"USBFunctionStrings",
"OOMPolicy",
"TimeoutStartFailureMode",
"TimeoutStopFailureMode"))
"TimeoutStopFailureMode",
"FileDescriptorStorePreserve"))
return bus_append_string(m, field, eq);
if (STR_IN_SET(field, "PermissionsStartOnly",
+1 -1
View File
@@ -21,7 +21,7 @@ int verb_clean_or_freeze(int argc, char *argv[], void *userdata) {
polkit_agent_open_maybe();
if (!arg_clean_what) {
arg_clean_what = strv_new("cache", "runtime");
arg_clean_what = strv_new("cache", "runtime", "fdstore");
if (!arg_clean_what)
return log_oom();
}
+2 -1
View File
@@ -937,7 +937,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
"state\n"
"cache\n"
"logs\n"
"configuration");
"configuration\n"
"fdstore");
return 0;
}
@@ -0,0 +1,8 @@
[Service]
Type=notify
NotifyAccess=all
FileDescriptorStoreMax=10
FileDescriptorStorePreserve=restart
ExecStart=/usr/lib/systemd/tests/testdata/testsuite-80.units/fdstore-pin.sh 0
StandardOutput=journal+console
StandardError=journal+console
@@ -0,0 +1,8 @@
[Service]
Type=notify
NotifyAccess=all
FileDescriptorStoreMax=10
FileDescriptorStorePreserve=yes
ExecStart=/usr/lib/systemd/tests/testdata/testsuite-80.units/fdstore-pin.sh 1
StandardOutput=journal+console
StandardError=journal+console
+47
View File
@@ -0,0 +1,47 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
PINNED="$1"
COUNTER="/tmp/fdstore-invoked.$PINNED"
FILE="/tmp/fdstore-data.$PINNED"
# This script is called six times: thrice from a service unit where the fdstore
# is pinned, and thrice where it isn't. The second iteration of each series is
# a restart, the third a stop followed by a start
if [ -e "$COUNTER" ] ; then
read -r N < "$COUNTER"
else
N=0
fi
echo "Invocation #$N with PINNED=$PINNED."
if [ "$N" -eq 0 ] ; then
# First iteration
test "${LISTEN_FDS:-0}" -eq 0
test ! -e "$FILE"
echo waldi > "$FILE"
systemd-notify --fd=3 --fdname="fd-$N-$PINNED" 3< "$FILE"
elif [ "$N" -eq 1 ] || { [ "$N" -eq 2 ] && [ "$PINNED" -eq 1 ]; } ; then
# Second iteration, or iteration with pinning on
test "${LISTEN_FDS:-0}" -eq 1
# We reopen fd #3 here, so that the read offset is at zero each time (hence no <&3 here…)
read -r word < /proc/self/fd/3
test "$word" = "waldi"
else
test "${LISTEN_FDS:-0}" -eq 0
test -e "$FILE"
fi
if [ "$N" -ge 2 ] ; then
rm "$COUNTER" "$FILE"
else
echo $((N + 1)) > "$COUNTER"
fi
systemd-notify --ready --status="Ready"
exec sleep infinity
@@ -0,0 +1,3 @@
[Unit]
After=fdstore-pin.service fdstore-nopin.service
Wants=fdstore-pin.service fdstore-nopin.service
+2
View File
@@ -6,3 +6,5 @@ Description=TEST-80-NOTIFYACCESS
ExecStartPre=rm -f /failed /testok
ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
Type=oneshot
StandardOutput=journal+console
StandardError=journal+console
+51
View File
@@ -48,6 +48,8 @@ assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "all"
rm /tmp/syncfifo1 /tmp/syncfifo2
# Now test basic fdstore behaviour
MYSCRIPT="/tmp/myscript$RANDOM.sh"
cat >> "$MYSCRIPT" <<'EOF'
#!/usr/bin/env bash
@@ -74,6 +76,55 @@ systemd-analyze fdstore "$MYUNIT" --json=short | grep -P -q '\[{"fdname":"quux",
systemctl stop "$MYUNIT"
rm "$MYSCRIPT"
systemd-analyze log-level debug
# Test fdstore pinning (this will pull in fdstore-pin.service fdstore-nopin.service)
systemctl start fdstore-pin.target
assert_eq "$(systemctl show fdstore-pin.service -P FileDescriptorStorePreserve)" yes
assert_eq "$(systemctl show fdstore-nopin.service -P FileDescriptorStorePreserve)" restart
assert_eq "$(systemctl show fdstore-pin.service -P SubState)" running
assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" running
assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1
assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 1
# The file descriptor store should survive service restarts
systemctl restart fdstore-pin.service fdstore-nopin.service
assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1
assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 1
assert_eq "$(systemctl show fdstore-pin.service -P SubState)" running
assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" running
# It should not survive the service stop plus a later start (unless pinned)
systemctl stop fdstore-pin.service fdstore-nopin.service
assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1
assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0
assert_eq "$(systemctl show fdstore-pin.service -P SubState)" dead-resources-pinned
assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" dead
systemctl start fdstore-pin.service fdstore-nopin.service
assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1
assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0
assert_eq "$(systemctl show fdstore-pin.service -P SubState)" running
assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" running
systemctl stop fdstore-pin.service fdstore-nopin.service
assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1
assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0
assert_eq "$(systemctl show fdstore-pin.service -P SubState)" dead-resources-pinned
assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" dead
systemctl clean fdstore-pin.service --what=fdstore
assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 0
assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0
assert_eq "$(systemctl show fdstore-pin.service -P SubState)" dead
assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" dead
touch /testok
rm /failed