diff --git a/man/systemctl.xml b/man/systemctl.xml
index 1a881d1049..705ed3e238 100644
--- a/man/systemctl.xml
+++ b/man/systemctl.xml
@@ -538,12 +538,16 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
StateDirectory=, CacheDirectory=,
LogsDirectory= and RuntimeDirectory=, see
systemd.exec5
- 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
+ FileDescriptorStoreMax=, see
+ systemd.service5
+ for details. For timer units this may be used to clear out the persistent timestamp data if
Persistent= is used and is selected, see
systemd.timer5. This
command only applies to units that use either of these settings. If 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).
+ 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.
@@ -2193,13 +2197,17 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
Select what type of per-unit resources to remove when the clean command is
- invoked, see below. Takes one of configuration, state,
- cache, logs, runtime 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 all as a shortcut for
- specifying all five resource types. If this option is not specified defaults to the combination of
- cache and runtime, i.e. the two kinds of resources that
- are generally considered to be redundant and can be reconstructed on next invocation.
+ invoked, see above. Takes one of configuration, state,
+ cache, logs, runtime,
+ fdstore 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
+ all as a shortcut for specifying all six resource types. If this option is not
+ specified defaults to the combination of cache, runtime
+ and fdstore, 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
+ fdstore resource type is only useful if the
+ FileDescriptorStorePreserve= option is enabled, since the file descriptor store
+ is otherwise cleaned automatically when the unit is stopped.
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index 3f083a8174..c01f41cb44 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -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);
diff --git a/src/core/execute.c b/src/core/execute.c
index 8b09794089..879f1345b0 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -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);
diff --git a/src/core/execute.h b/src/core/execute.h
index 123fc1ec60..d2f5507405 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -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;
diff --git a/src/core/service.c b/src/core/service.c
index 7130cbf4e6..dcd62fabdc 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -4857,22 +4857,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;
@@ -4899,10 +4916,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) {
diff --git a/src/core/timer.c b/src/core/timer.c
index 371e42e0aa..419416b325 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -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;
diff --git a/src/systemctl/systemctl-clean-or-freeze.c b/src/systemctl/systemctl-clean-or-freeze.c
index 5c15a9fba0..40d5f6d557 100644
--- a/src/systemctl/systemctl-clean-or-freeze.c
+++ b/src/systemctl/systemctl-clean-or-freeze.c
@@ -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();
}
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index df4cf68435..21e09536a6 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -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;
}