service: allow freeing the fdstore via cleaning

Now that we have a potentially pinned fdstore let's add a concept for
cleaning it explicitly on user requested. Let's expose this via
"systemctl clean", i.e. the same way as user directories are cleaned.
This commit is contained in:
Lennart Poettering 2023-03-29 22:10:01 +02:00
parent b9c1883a9c
commit 4fb8f1e883
8 changed files with 86 additions and 27 deletions

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>

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

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

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;

View file

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

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;

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

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