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 = '...';
<!--property NFileDescriptorStore is not documented!-->
+ <!--property FileDescriptorStorePreserve is not documented!-->
+
<!--property StatusErrno is not documented!-->
<!--property ReloadResult is not documented!-->
<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"/>
<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
[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",
};
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,
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);
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),
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);
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);
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);
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),
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);
{{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)
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
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");
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);
[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,
};
[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,
};
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) {
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);
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(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);
}
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;
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));
/* 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);
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)
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:
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();
/* 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;
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;
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) {
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;
/* 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] = {
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;
"USBFunctionStrings",
"OOMPolicy",
"TimeoutStartFailureMode",
- "TimeoutStopFailureMode"))
+ "TimeoutStopFailureMode",
+ "FileDescriptorStorePreserve"))
return bus_append_string(m, field, eq);
if (STR_IN_SET(field, "PermissionsStartOnly",