Skip to content

Commit

Permalink
core/service: destroy runtime data when Type=oneshot services exit
Browse files Browse the repository at this point in the history
Currently, we have a bunch of Type=oneshot + RemainAfterExit=yes
services that make use of credentials. When those exits, the cred mounts
remain established, which is pointless and quite annoying. Let's
instead destroy the runtime data on SERVICE_EXITED, if no process
will be spawned for the unit again.

(cherry picked from commit c26948c)

Resolves: RHEL-55734
  • Loading branch information
YHNdnzj authored and github-actions[bot] committed Aug 22, 2024
1 parent dee8164 commit 45d3266
Showing 1 changed file with 26 additions and 11 deletions.
37 changes: 26 additions & 11 deletions src/core/service.c
Original file line number Diff line number Diff line change
Expand Up @@ -1206,13 +1206,12 @@ static void service_search_main_pid(Service *s) {
}

static void service_set_state(Service *s, ServiceState state) {
Unit *u = UNIT(ASSERT_PTR(s));
ServiceState old_state;
const UnitActiveState *table;

assert(s);

if (s->state != state)
bus_unit_send_pending_change_signal(UNIT(s), false);
bus_unit_send_pending_change_signal(u, false);

table = s->type == SERVICE_IDLE ? state_translation_table_idle : state_translation_table;

Expand Down Expand Up @@ -1246,8 +1245,8 @@ static void service_set_state(Service *s, ServiceState state) {
SERVICE_DEAD, SERVICE_FAILED,
SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART, SERVICE_AUTO_RESTART, SERVICE_AUTO_RESTART_QUEUED,
SERVICE_DEAD_RESOURCES_PINNED)) {
unit_unwatch_all_pids(UNIT(s));
unit_dequeue_rewatch_pids(UNIT(s));
unit_unwatch_all_pids(u);
unit_dequeue_rewatch_pids(u);
}

if (state != SERVICE_START)
Expand All @@ -1256,15 +1255,31 @@ static void service_set_state(Service *s, ServiceState state) {
if (!IN_SET(state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY))
service_stop_watchdog(s);

/* For the inactive states unit_notify() will trim the cgroup,
* but for exit we have to do that ourselves... */
if (state == SERVICE_EXITED && !MANAGER_IS_RELOADING(UNIT(s)->manager))
unit_prune_cgroup(UNIT(s));
if (state == SERVICE_EXITED && !MANAGER_IS_RELOADING(u->manager)) {
/* For the inactive states unit_notify() will trim the cgroup. But for exit we have to
* do that ourselves... */
unit_prune_cgroup(u);

/* If none of ExecReload= and ExecStop*= is used, we can safely destroy runtime data
* as soon as the service enters SERVICE_EXITED. This saves us from keeping the credential mount
* for the whole duration of the oneshot service while no processes are actually running,
* among other things. */

bool start_only = true;
for (ServiceExecCommand c = SERVICE_EXEC_RELOAD; c < _SERVICE_EXEC_COMMAND_MAX; c++)
if (s->exec_command[c]) {
start_only = false;
break;
}

if (start_only)
unit_destroy_runtime_data(u, &s->exec_context);
}

if (old_state != state)
log_unit_debug(UNIT(s), "Changed %s -> %s", service_state_to_string(old_state), service_state_to_string(state));
log_unit_debug(u, "Changed %s -> %s", service_state_to_string(old_state), service_state_to_string(state));

unit_notify(UNIT(s), table[old_state], table[state], s->reload_result == SERVICE_SUCCESS);
unit_notify(u, table[old_state], table[state], s->reload_result == SERVICE_SUCCESS);
}

static usec_t service_coldplug_timeout(Service *s) {
Expand Down

0 comments on commit 45d3266

Please sign in to comment.