]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core/socket: stop hardcoding every service inactive state 33888/head
authorMike Yuan <me@yhndnzj.com>
Wed, 31 Jul 2024 18:02:05 +0000 (20:02 +0200)
committerMike Yuan <me@yhndnzj.com>
Wed, 31 Jul 2024 19:56:28 +0000 (21:56 +0200)
History (c068650fcfc69aebb35be1c71f35dbc25b22030a,
941a12dcba57f6673230a9c413738c51374d2998) has proven
that we're not good at keeping socket and service states
in sync. Instead, let's query the high-level unit_active_state()
first, and only hardcode the two special auto-restart
service states.

Additionally, allow returning to listening state on SERVICE_CLEANING.

src/core/socket.c

index 58c3a11a491e63c345e4b0d0de924a7e286be37c..5042e4b0784a9a2d86e054253749430696f6f7a0 100644 (file)
@@ -91,6 +91,26 @@ static bool SOCKET_STATE_WITH_PROCESS(SocketState state) {
                       SOCKET_CLEANING);
 }
 
+static bool SOCKET_SERVICE_IS_ACTIVE(Service *s, bool allow_finalize) {
+        assert(s);
+
+        /* If unit_active_state() reports inactive/failed then it's all good, otherwise we need to
+         * manually exclude SERVICE_AUTO_RESTART and SERVICE_AUTO_RESTART_QUEUED, in which cases
+         * the start job hasn't been enqueued/run, but are only placeholders in order to allow
+         * canceling auto restart. */
+
+        if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(UNIT(s))))
+                return false;
+
+        if (IN_SET(s->state, SERVICE_AUTO_RESTART, SERVICE_AUTO_RESTART_QUEUED))
+                return false;
+
+        if (allow_finalize && IN_SET(s->state, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL, SERVICE_CLEANING))
+                return false;
+
+        return true;
+}
+
 static void socket_init(Unit *u) {
         Socket *s = SOCKET(u);
 
@@ -2469,19 +2489,14 @@ static int socket_start(Unit *u) {
 
         /* Cannot run this without the service being around */
         if (UNIT_ISSET(s->service)) {
-                Service *service;
-
-                service = SERVICE(UNIT_DEREF(s->service));
+                Service *service = ASSERT_PTR(SERVICE(UNIT_DEREF(s->service)));
 
                 if (UNIT(service)->load_state != UNIT_LOADED)
                         return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOENT),
                                                     "Socket service %s not loaded, refusing.", UNIT(service)->id);
 
-                /* If the service is already active we cannot start the
-                 * socket */
-                if (!IN_SET(service->state,
-                            SERVICE_DEAD, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_DEAD_RESOURCES_PINNED, SERVICE_FAILED, SERVICE_FAILED_BEFORE_AUTO_RESTART,
-                            SERVICE_AUTO_RESTART, SERVICE_AUTO_RESTART_QUEUED))
+                /* If the service is already active we cannot start the socket */
+                if (SOCKET_SERVICE_IS_ACTIVE(service, /* allow_finalize = */ false))
                         return log_unit_error_errno(u, SYNTHETIC_ERRNO(EBUSY),
                                                     "Socket service %s already active, refusing.", UNIT(service)->id);
         }
@@ -3346,7 +3361,8 @@ static void socket_trigger_notify(Unit *u, Unit *other) {
 
         /* Filter out invocations with bogus state */
         assert(UNIT_IS_LOAD_COMPLETE(other->load_state));
-        assert(other->type == UNIT_SERVICE);
+
+        Service *service = ASSERT_PTR(SERVICE(other));
 
         /* Don't propagate state changes from the service if we are already down */
         if (!IN_SET(s->state, SOCKET_RUNNING, SOCKET_LISTENING))
@@ -3366,11 +3382,8 @@ static void socket_trigger_notify(Unit *u, Unit *other) {
         if (other->job)
                 return;
 
-        if (IN_SET(SERVICE(other)->state,
-                   SERVICE_DEAD, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_DEAD_RESOURCES_PINNED, SERVICE_FAILED, SERVICE_FAILED_BEFORE_AUTO_RESTART,
-                   SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
-                   SERVICE_AUTO_RESTART, SERVICE_AUTO_RESTART_QUEUED))
-               socket_enter_listening(s);
+        if (!SOCKET_SERVICE_IS_ACTIVE(service, /* allow_finalize = */ true))
+                socket_enter_listening(s);
 
         if (SERVICE(other)->state == SERVICE_RUNNING)
                 socket_set_state(s, SOCKET_RUNNING);