]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
service: add ability to pin fd store
authorLennart Poettering <lennart@poettering.net>
Wed, 29 Mar 2023 20:07:22 +0000 (22:07 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 13 Apr 2023 04:44:27 +0000 (06:44 +0200)
Oftentimes it is useful to allow the per-service fd store to survive
longer than for a restart. This is useful in various scenarios:

1. An fd to some security relevant object needs to be stashed somewhere,
   that should not be cleaned automatically, because the security
   enforcement would be dropped then.

2. A user namespace fd should be allocated on first invocation and be
   kept around until the user logs out (i.e. systemd --user ends), á la
   #16328 (This does not implement what #16318 asks for, but should
   solve the use-case discussed there.)

3. There's interest in allow a concept of "userspace reboots" where the
   kernel stays running, and userspace is swapped out (i.e. all services
   exit, and the rootfs transitioned into a new version of it) while
   keeping some select resources pinned, very similar to how we
   implement a switch root. Thus it is useful to allow services to exit,
   while leaving their fds around till the very end.

This is exposed through a new FileDescriptorStorePreserve= setting that
is closely modelled after RuntimeDirectoryPreserve= (in fact it reused
the same internal type), since we want similar behaviour in the end, and
quite often they probably want to be used together.

13 files changed:
man/org.freedesktop.systemd1.xml
man/systemd.service.xml
src/basic/unit-def.c
src/basic/unit-def.h
src/core/dbus-execute.c
src/core/dbus-execute.h
src/core/dbus-service.c
src/core/load-fragment-gperf.gperf.in
src/core/load-fragment.c
src/core/load-fragment.h
src/core/service.c
src/core/service.h
src/shared/bus-unit-util.c

index f39893f647a6ceedcdcf846cc24b6ab366591e49..e462c606362c6255817387baf2d47da16e58f9a6 100644 (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"/>
index 1b116b8372aaae7f0c75c326af3ace2c4d156e04..7de1350a5935c2e102761d2427a298c4c46e0d93 100644 (file)
         <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
         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>
         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
index a0fab46a193b3c6e62381425baad30335015398e..86b66e2be0c49d5f9dbe336e8f18df0cb9856868 100644 (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",
 };
index 2fab42e9c7204adb66deca0cc380bea66df69d36..169e1f719ec4ffcd85ae6ef79664c99abd7dc079 100644 (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,
index d77842bdabd480f1bc8cd6dcafa8f176e9edc636..ce20183a7074e532b9dc5b2c929eb02ab9c791c6 100644 (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);
index c53834140e5fb82b294c5d8669b35586ff9372fe..5926bdb4b139a99e52dad887292bddcf0348181a 100644 (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);
index a563624fce915b33d7d9548f8e060546331bae21..ecab3f479d990238180df5b3278ae7dde2662463 100644 (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);
 
index f35c7436550924124d038f35400612316270f0b8..ce0e2f0c5c241f21e9d83652935c7335e51db9d7 100644 (file)
 {{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
index 686a72402d624e6d7e9491bf0404f5bbb2c3c888..cf0096263afcde231035aeb46ccb85428de6fce1 100644 (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");
index ab682ee23e73fe71ed27b3f9b82677312c43f112..98adf5ae056e57877df51686937043fea60f2e48 100644 (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);
index 934500abd65cda95d30bf33bb9fb33da2ff36aac..7130cbf4e66edeea82b6e1b9417a7f618ff07581 100644 (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) {
@@ -1031,8 +1035,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);
@@ -1244,7 +1250,8 @@ 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));
         }
@@ -1351,7 +1358,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);
         }
@@ -1939,6 +1947,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;
@@ -1955,11 +1969,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));
@@ -2023,6 +2037,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);
 
@@ -2701,7 +2719,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)
@@ -2760,7 +2778,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:
@@ -2789,6 +2807,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();
@@ -3397,7 +3416,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;
@@ -3624,6 +3643,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;
 
@@ -4712,7 +4732,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) {
@@ -4749,7 +4769,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;
@@ -4920,14 +4940,19 @@ static void service_release_resources(Unit *u) {
         /* 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))
+        if (!IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_DEAD_RESOURCES_PINNED))
                 return;
 
         log_unit_debug(u, "Releasing resources...");
 
         service_close_socket_fd(s);
         service_release_stdio_fd(s);
-        service_release_fd_store(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] = {
index 03edb18e31e5c99d340c213045f7702fabf50e2a..1d1aa91fc6d9a84008eaa4bc047478b3903b2e6d 100644 (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;
index 1c991ae54f2e609797457207ce5fa2c88fab3fe8..ebbd1f7f28e502b8b848ac3f7577182a4ee6a14c 100644 (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",