From 4c523d62d2641dfa64a8d73be24ca5a4eefaeb14 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Wed, 31 Jul 2024 20:02:05 +0200 Subject: [PATCH] core/socket: stop hardcoding every service inactive state 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 | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/src/core/socket.c b/src/core/socket.c index 58c3a11a491..5042e4b0784 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -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); -- 2.47.3