]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core/service: destroy runtime data when Type=oneshot services exit 33443/head
authorMike Yuan <me@yhndnzj.com>
Sat, 22 Jun 2024 10:03:50 +0000 (12:03 +0200)
committerLuca Boccassi <bluca@debian.org>
Sun, 21 Jul 2024 11:43:51 +0000 (12:43 +0100)
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.

src/core/service.c

index 9af10df0cd80a6b6a33fb951cd81cbac3155884d..09bb8d61795155dd7b5b88a49d4e296be3931ba5 100644 (file)
@@ -1228,13 +1228,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;
 
@@ -1268,8 +1267,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)
@@ -1278,15 +1277,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) {