From b03e1b09af7cf305c762db94c779f78cd85bca80 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Sun, 19 Oct 2025 23:23:17 +0200 Subject: [PATCH] core/service: rework ExecReload= + Type=notify-reload interaction, add ExecReloadPost= When Type=notify-reload got introduced, it wasn't intended to be mutually exclusive with ExecReload=. However, currently ExecReload= is immediately forked off after the service main process is signaled, leaving states in between essentially undefined. Given so broken it is I doubt any sane user is using this setup, hence I took a stab to rework everything: 1. Extensions are refreshed (unchanged) 2. ExecReload= is forked off without signaling the process 3a. If RELOADING=1 is sent during the ExecReload= invocation, we'd refrain from signaling the process again, instead just transition to SERVICE_RELOAD_NOTIFY directly and wait for READY=1 3b. If not, signal the process after ExecReload= finishes (from now on the same as Type=notify-reload w/o ExecReload=) 4. To accomodate the use case of performing post-reload tasks, ExecReloadPost= is introduced which executes after READY=1 The new model greatly simplifies things, as no control processes will be around in SERVICE_RELOAD_SIGNAL and SERVICE_RELOAD_NOTIFY states. See also: https://github.com/systemd/systemd/issues/37515#issuecomment-2891229652 --- man/org.freedesktop.systemd1.xml | 24 ++- man/systemd.service.xml | 49 +++--- src/basic/unit-def.c | 3 +- src/basic/unit-def.h | 3 +- src/core/dbus-service.c | 2 + src/core/load-fragment-gperf.gperf.in | 3 +- src/core/service.c | 223 ++++++++++++++++---------- src/core/service.h | 2 + src/shared/bus-unit-util.c | 2 + src/systemctl/systemctl-show.c | 2 + 10 files changed, 204 insertions(+), 109 deletions(-) diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml index 127ff766776..f1f433bbac9 100644 --- a/man/org.freedesktop.systemd1.xml +++ b/man/org.freedesktop.systemd1.xml @@ -2856,6 +2856,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") readonly a(sasasttttuii) ExecReloadEx = [...]; @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sasbttttuii) ExecReloadPost = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sasasttttuii) ExecReloadPostEx = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") readonly a(sasbttttuii) ExecStop = [...]; @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") readonly a(sasasttttuii) ExecStopEx = [...]; @@ -3537,6 +3541,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + + @@ -4209,6 +4215,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + + + + @@ -4839,10 +4849,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { last watchdog ping received from the service, or 0 if none was ever received. ExecStartPre, ExecStart, ExecStartPost, - ExecReload, ExecStop, and ExecStopPost are arrays - of structures where each struct contains: the binary path to execute; an array with all arguments to - pass to the executed command, starting with argument 0; a boolean whether it should be considered a - failure if the process exits uncleanly; two pairs of + ExecReload, ExecReloadPost, ExecStop, and + ExecStopPost are arrays of structures where each struct contains: the binary path + to execute; an array with all arguments to pass to the executed command, starting with argument 0; + a boolean whether it should be considered a failure if the process exits uncleanly; two pairs of CLOCK_REALTIME/CLOCK_MONOTONIC microsecond timestamps when the process began and finished running the last time, or 0 if it never ran or never finished running; the PID of the process, or 0 if it has not run yet; the exit code and status of the last run. This @@ -12540,8 +12550,10 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \ LogsDirectoryAccounting, and KillSubgroup() were added in version 258. UserNamespacePath, - OOMKills, and - ManagedOOMKills were added in 259. + OOMKills, + ManagedOOMKills, + ExecReloadPost, and + ExecReloadPostEx were added in version 259. Socket Unit Objects diff --git a/man/systemd.service.xml b/man/systemd.service.xml index 8c00a093f87..2ddeeafcec8 100644 --- a/man/systemd.service.xml +++ b/man/systemd.service.xml @@ -475,8 +475,8 @@ ExecReload= - Commands to execute to trigger a configuration reload in the service. This argument - takes multiple command lines, following the same scheme as described for + Commands to execute to trigger a configuration reload in the service. This setting + may take multiple command lines, following the same scheme as described for ExecStart= above. Use of this setting is optional. Specifier and environment variable substitution is supported here following the same scheme as for ExecStart=. @@ -486,13 +486,12 @@ ExecReload=kill -HUP $MAINPID - Note however that reloading a daemon by enqueuing a signal (as with the example line above) is - usually not a good choice, because this is an asynchronous operation and hence not suitable when - ordering reloads of multiple services against each other. It is thus strongly recommended to either - use Type= in place of - ExecReload=, or to set ExecReload= to a command that not only - triggers a configuration reload of the daemon, but also synchronously waits for it to complete. For - example, Note however that reloading a daemon by enqueuing a signal without completion notification + (as is the case with the example line above) is usually not a good choice, because this is an + asynchronous operation and hence not suitable when ordering reloads of multiple services against + each other. It is thus strongly recommended to either use Type=notify-reload, + or to set ExecReload= to a command that not only triggers a configuration reload + of the daemon, but also synchronously waits for it to complete. For example, dbus-broker1 uses the following: @@ -500,6 +499,22 @@ /org/freedesktop/DBus org.freedesktop.DBus \ ReloadConfig + + This setting can be combined with Type=notify-reload, in which case + the service main process is signaled after all specified command lines finish execution. Specially, + if RELOADING=1 notification is received before ExecReload= + completes, the signaling is skipped and the service manager immediately starts listening for + READY=1. + + + + + ExecReloadPost= + + Commands to execute after a successful reload operation. Syntax for this setting + is exactly the same as ExecReload=. + + @@ -1072,18 +1087,14 @@ RootDirectoryStartOnly= - Takes a boolean argument. If true, the root - directory, as configured with the + Takes a boolean argument. If true, the root directory, as configured with the RootDirectory= option (see systemd.exec5 - for more information), is only applied to the process started - with ExecStart=, and not to the various - other ExecStartPre=, - ExecStartPost=, - ExecReload=, ExecStop=, - and ExecStopPost= commands. If false, the - setting is applied to all configured commands the same way. - Defaults to false. + for more information), is only applied to the process started with ExecStart=, + and not to the various other ExecStartPre=, ExecStartPost=, + ExecReload=, ExecReloadPost=, ExecStop=, + and ExecStopPost= commands. If false, the setting is applied to all + configured commands the same way. Defaults to false. diff --git a/src/basic/unit-def.c b/src/basic/unit-def.c index a2b2f9d6f05..9179897f3cc 100644 --- a/src/basic/unit-def.c +++ b/src/basic/unit-def.c @@ -224,10 +224,11 @@ static const char* const service_state_table[_SERVICE_STATE_MAX] = { [SERVICE_START_POST] = "start-post", [SERVICE_RUNNING] = "running", [SERVICE_EXITED] = "exited", + [SERVICE_REFRESH_EXTENSIONS] = "refresh-extensions", [SERVICE_RELOAD] = "reload", [SERVICE_RELOAD_SIGNAL] = "reload-signal", [SERVICE_RELOAD_NOTIFY] = "reload-notify", - [SERVICE_REFRESH_EXTENSIONS] = "refresh-extensions", + [SERVICE_RELOAD_POST] = "reload-post", [SERVICE_STOP] = "stop", [SERVICE_STOP_WATCHDOG] = "stop-watchdog", [SERVICE_STOP_SIGTERM] = "stop-sigterm", diff --git a/src/basic/unit-def.h b/src/basic/unit-def.h index f42198e08e6..37539ef0189 100644 --- a/src/basic/unit-def.h +++ b/src/basic/unit-def.h @@ -131,10 +131,11 @@ typedef enum ServiceState { SERVICE_START_POST, SERVICE_RUNNING, SERVICE_EXITED, /* Nothing is running anymore, but RemainAfterExit is true hence this is OK */ + SERVICE_REFRESH_EXTENSIONS, /* Refreshing extensions for a reload request */ SERVICE_RELOAD, /* Reloading via ExecReload= */ SERVICE_RELOAD_SIGNAL, /* Reloading via SIGHUP requested */ SERVICE_RELOAD_NOTIFY, /* Waiting for READY=1 after RELOADING=1 notify */ - SERVICE_REFRESH_EXTENSIONS, /* Refreshing extensions for a reload request */ + SERVICE_RELOAD_POST, SERVICE_MOUNTING, /* Performing a live mount into the namespace of the service */ SERVICE_STOP, /* No STOP_PRE state, instead just register multiple STOP executables */ SERVICE_STOP_WATCHDOG, diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c index 2ff6272bd4c..aa6a587c16d 100644 --- a/src/core/dbus-service.c +++ b/src/core/dbus-service.c @@ -383,6 +383,8 @@ const sd_bus_vtable bus_service_vtable[] = { BUS_EXEC_EX_COMMAND_LIST_VTABLE("ExecStartPostEx", offsetof(Service, exec_command[SERVICE_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_LIST_VTABLE("ExecReload", offsetof(Service, exec_command[SERVICE_EXEC_RELOAD]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_EX_COMMAND_LIST_VTABLE("ExecReloadEx", offsetof(Service, exec_command[SERVICE_EXEC_RELOAD]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + BUS_EXEC_COMMAND_LIST_VTABLE("ExecReloadPost", offsetof(Service, exec_command[SERVICE_EXEC_RELOAD_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + BUS_EXEC_EX_COMMAND_LIST_VTABLE("ExecReloadPostEx", offsetof(Service, exec_command[SERVICE_EXEC_RELOAD_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStop", offsetof(Service, exec_command[SERVICE_EXEC_STOP]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_EX_COMMAND_LIST_VTABLE("ExecStopEx", offsetof(Service, exec_command[SERVICE_EXEC_STOP]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPost", offsetof(Service, exec_command[SERVICE_EXEC_STOP_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in index 5b83c95e4c9..95ef5081051 100644 --- a/src/core/load-fragment-gperf.gperf.in +++ b/src/core/load-fragment-gperf.gperf.in @@ -430,8 +430,9 @@ Service.PIDFile, config_parse_pid_file, Service.ExecCondition, config_parse_exec, SERVICE_EXEC_CONDITION, offsetof(Service, exec_command) Service.ExecStartPre, config_parse_exec, SERVICE_EXEC_START_PRE, offsetof(Service, exec_command) Service.ExecStart, config_parse_exec, SERVICE_EXEC_START, offsetof(Service, exec_command) -Service.ExecReload, config_parse_exec, SERVICE_EXEC_RELOAD, offsetof(Service, exec_command) Service.ExecStartPost, config_parse_exec, SERVICE_EXEC_START_POST, offsetof(Service, exec_command) +Service.ExecReload, config_parse_exec, SERVICE_EXEC_RELOAD, offsetof(Service, exec_command) +Service.ExecReloadPost, config_parse_exec, SERVICE_EXEC_RELOAD_POST, offsetof(Service, exec_command) Service.ExecStop, config_parse_exec, SERVICE_EXEC_STOP, offsetof(Service, exec_command) Service.ExecStopPost, config_parse_exec, SERVICE_EXEC_STOP_POST, offsetof(Service, exec_command) Service.RestartSec, config_parse_sec, 0, offsetof(Service, restart_usec) diff --git a/src/core/service.c b/src/core/service.c index 8b2158c6775..5c1e6189f58 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -68,10 +68,11 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = { [SERVICE_START_POST] = UNIT_ACTIVATING, [SERVICE_RUNNING] = UNIT_ACTIVE, [SERVICE_EXITED] = UNIT_ACTIVE, + [SERVICE_REFRESH_EXTENSIONS] = UNIT_REFRESHING, [SERVICE_RELOAD] = UNIT_RELOADING, [SERVICE_RELOAD_SIGNAL] = UNIT_RELOADING, [SERVICE_RELOAD_NOTIFY] = UNIT_RELOADING, - [SERVICE_REFRESH_EXTENSIONS] = UNIT_REFRESHING, + [SERVICE_RELOAD_POST] = UNIT_RELOADING, [SERVICE_MOUNTING] = UNIT_REFRESHING, [SERVICE_STOP] = UNIT_DEACTIVATING, [SERVICE_STOP_WATCHDOG] = UNIT_DEACTIVATING, @@ -100,10 +101,11 @@ static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] = [SERVICE_START_POST] = UNIT_ACTIVE, [SERVICE_RUNNING] = UNIT_ACTIVE, [SERVICE_EXITED] = UNIT_ACTIVE, + [SERVICE_REFRESH_EXTENSIONS] = UNIT_REFRESHING, [SERVICE_RELOAD] = UNIT_RELOADING, [SERVICE_RELOAD_SIGNAL] = UNIT_RELOADING, [SERVICE_RELOAD_NOTIFY] = UNIT_RELOADING, - [SERVICE_REFRESH_EXTENSIONS] = UNIT_REFRESHING, + [SERVICE_RELOAD_POST] = UNIT_RELOADING, [SERVICE_MOUNTING] = UNIT_REFRESHING, [SERVICE_STOP] = UNIT_DEACTIVATING, [SERVICE_STOP_WATCHDOG] = UNIT_DEACTIVATING, @@ -136,7 +138,7 @@ static bool SERVICE_STATE_WITH_MAIN_PROCESS(ServiceState state) { return IN_SET(state, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, - SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_REFRESH_EXTENSIONS, + SERVICE_REFRESH_EXTENSIONS, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_RELOAD_POST, SERVICE_MOUNTING, SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL); @@ -146,7 +148,7 @@ static bool SERVICE_STATE_WITH_CONTROL_PROCESS(ServiceState state) { return IN_SET(state, SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, - SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_REFRESH_EXTENSIONS, + SERVICE_REFRESH_EXTENSIONS, SERVICE_RELOAD, SERVICE_RELOAD_POST, SERVICE_MOUNTING, SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL, @@ -157,7 +159,7 @@ static bool SERVICE_STATE_WITH_WATCHDOG(ServiceState state) { return IN_SET(state, SERVICE_START_POST, SERVICE_RUNNING, - SERVICE_REFRESH_EXTENSIONS, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, + SERVICE_REFRESH_EXTENSIONS, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_RELOAD_POST, SERVICE_MOUNTING); } @@ -1302,7 +1304,7 @@ static void service_set_state(Service *s, ServiceState state) { if (!IN_SET(state, SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, - SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_REFRESH_EXTENSIONS, + SERVICE_REFRESH_EXTENSIONS, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_RELOAD_POST, SERVICE_MOUNTING, SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL, @@ -1372,10 +1374,11 @@ static usec_t service_coldplug_timeout(Service *s) { case SERVICE_START_PRE: case SERVICE_START: case SERVICE_START_POST: + case SERVICE_REFRESH_EXTENSIONS: case SERVICE_RELOAD: case SERVICE_RELOAD_SIGNAL: case SERVICE_RELOAD_NOTIFY: - case SERVICE_REFRESH_EXTENSIONS: + case SERVICE_RELOAD_POST: case SERVICE_MOUNTING: return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->timeout_start_usec); @@ -2716,29 +2719,16 @@ static void service_enter_reload_by_notify(Service *s) { log_unit_warning(UNIT(s), "Failed to schedule propagation of reload, ignoring: %s", bus_error_message(&error, r)); } -static void service_enter_reload_signal_exec(Service *s) { - bool killed = false; +static void service_enter_reload_post(Service *s) { int r; assert(s); service_unwatch_control_pid(s); - usec_t ts = now(CLOCK_MONOTONIC); - - if (s->type == SERVICE_NOTIFY_RELOAD && pidref_is_set(&s->main_pid)) { - r = pidref_kill_and_sigcont(&s->main_pid, s->reload_signal); - if (r < 0) { - log_unit_warning_errno(UNIT(s), r, "Failed to send reload signal: %m"); - goto fail; - } - - killed = true; - } - - s->control_command = s->exec_command[SERVICE_EXEC_RELOAD]; + s->control_command = s->exec_command[SERVICE_EXEC_RELOAD_POST]; if (s->control_command) { - s->control_command_id = SERVICE_EXEC_RELOAD; + s->control_command_id = SERVICE_EXEC_RELOAD_POST; r = service_spawn(s, s->control_command, @@ -2746,31 +2736,88 @@ static void service_enter_reload_signal_exec(Service *s) { s->timeout_start_usec, &s->control_pid); if (r < 0) { - log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'reload' task: %m"); - goto fail; + log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'reload-post' task: %m"); + return service_reload_finish(s, SERVICE_FAILURE_RESOURCES); } - service_set_state(s, SERVICE_RELOAD); - } else if (killed) { + service_set_state(s, SERVICE_RELOAD_POST); + } else + service_reload_finish(s, SERVICE_SUCCESS); +} + +static void service_enter_reload_signal(Service *s) { + int r; + + assert(s); + + if (s->type != SERVICE_NOTIFY_RELOAD) + return service_enter_reload_post(s); + + if (s->state == SERVICE_RELOAD) { + /* We executed ExecReload=, and the service has already notified us the result? + * Directly transition to next state. */ + if (s->notify_state == NOTIFY_RELOADING) + return service_set_state(s, SERVICE_RELOAD_NOTIFY); + if (s->notify_state == NOTIFY_RELOAD_READY) + return service_enter_reload_post(s); + } + + if (pidref_is_set(&s->main_pid)) { r = service_arm_timer(s, /* relative= */ true, s->timeout_start_usec); if (r < 0) { log_unit_warning_errno(UNIT(s), r, "Failed to install timer: %m"); goto fail; } + r = pidref_kill_and_sigcont(&s->main_pid, s->reload_signal); + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to send reload signal: %m"); + goto fail; + } + service_set_state(s, SERVICE_RELOAD_SIGNAL); } else - return service_reload_finish(s, SERVICE_SUCCESS); + service_enter_reload_post(s); + + return; + +fail: + service_reload_finish(s, SERVICE_FAILURE_RESOURCES); +} + +static void service_enter_reload(Service *s) { + int r; + + assert(s); + + service_unwatch_control_pid(s); + + if (IN_SET(s->notify_state, NOTIFY_RELOADING, NOTIFY_RELOAD_READY)) + s->notify_state = _NOTIFY_STATE_INVALID; /* Store the timestamp when we started reloading: when reloading via SIGHUP we won't leave the reload * state until we received both RELOADING=1 and READY=1 with MONOTONIC_USEC= set to a value above * this. Thus we know for sure the reload cycle was executed *after* we requested it, and is not one * that was already in progress before. */ - s->reload_begin_usec = ts; - return; + s->reload_begin_usec = now(CLOCK_MONOTONIC); -fail: - service_reload_finish(s, SERVICE_FAILURE_RESOURCES); + s->control_command = s->exec_command[SERVICE_EXEC_RELOAD]; + if (s->control_command) { + s->control_command_id = SERVICE_EXEC_RELOAD; + + r = service_spawn(s, + s->control_command, + service_exec_flags(s->control_command_id, /* cred_flag = */ 0), + s->timeout_start_usec, + &s->control_pid); + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'reload' task: %m"); + return service_reload_finish(s, SERVICE_FAILURE_RESOURCES); + } + + service_set_state(s, SERVICE_RELOAD); + } else + service_enter_reload_signal(s); } static bool service_should_reload_extensions(Service *s) { @@ -2807,9 +2854,9 @@ static void service_enter_refresh_extensions(Service *s) { assert(s); - /* If we don't have extensions to reload, immediately go to the signal step */ + /* If we don't have extensions to refresh, immediately transition to reload state */ if (!service_should_reload_extensions(s)) - return service_enter_reload_signal_exec(s); + return service_enter_reload(s); service_unwatch_control_pid(s); s->control_command = NULL; @@ -2891,7 +2938,11 @@ static void service_run_next_control(Service *s) { s->control_command = s->control_command->command_next; service_unwatch_control_pid(s); - if (IN_SET(s->state, SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD)) + if (IN_SET(s->state, + SERVICE_CONDITION, + SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, + SERVICE_RUNNING, + SERVICE_RELOAD, SERVICE_RELOAD_POST)) timeout = s->timeout_start_usec; else timeout = s->timeout_stop_usec; @@ -2908,7 +2959,7 @@ static void service_run_next_control(Service *s) { service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES); else if (s->state == SERVICE_STOP_POST) service_enter_dead(s, SERVICE_FAILURE_RESOURCES, /* allow_restart= */ true); - else if (s->state == SERVICE_RELOAD) + else if (IN_SET(s->state, SERVICE_RELOAD, SERVICE_RELOAD_POST)) service_reload_finish(s, SERVICE_FAILURE_RESOURCES); else service_enter_stop(s, SERVICE_FAILURE_RESOURCES); @@ -3076,6 +3127,7 @@ static int service_stop(Unit *u) { case SERVICE_RELOAD: case SERVICE_RELOAD_SIGNAL: case SERVICE_RELOAD_NOTIFY: + case SERVICE_RELOAD_POST: case SERVICE_STOP_WATCHDOG: /* If there's already something running we go directly into kill mode. */ service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_SUCCESS); @@ -4128,10 +4180,11 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { switch (s->state) { case SERVICE_START_POST: + case SERVICE_REFRESH_EXTENSIONS: case SERVICE_RELOAD: case SERVICE_RELOAD_SIGNAL: case SERVICE_RELOAD_NOTIFY: - case SERVICE_REFRESH_EXTENSIONS: + case SERVICE_RELOAD_POST: case SERVICE_MOUNTING: /* If neither main nor control processes are running then the current * state can never exit cleanly, hence immediately terminate the @@ -4247,7 +4300,8 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { success, code, status); - if (!IN_SET(s->state, SERVICE_RELOAD, SERVICE_MOUNTING) && s->result == SERVICE_SUCCESS) + if (!IN_SET(s->state, SERVICE_REFRESH_EXTENSIONS, SERVICE_RELOAD, SERVICE_RELOAD_POST, SERVICE_MOUNTING) && + s->result == SERVICE_SUCCESS) s->result = f; if (s->control_command && @@ -4334,30 +4388,28 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { service_enter_running(s, SERVICE_SUCCESS); break; - case SERVICE_RELOAD: - case SERVICE_RELOAD_SIGNAL: - case SERVICE_RELOAD_NOTIFY: + case SERVICE_REFRESH_EXTENSIONS: if (f == SERVICE_SUCCESS) - if (service_load_pid_file(s, true) < 0) - service_search_main_pid(s); - - /* If the last notification we received from the service process indicates - * we are still reloading, then don't leave reloading state just yet, just - * transition into SERVICE_RELOAD_NOTIFY, to wait for the READY=1 coming, - * too. */ - if (s->notify_state == NOTIFY_RELOADING) { - s->reload_result = f; - service_set_state(s, SERVICE_RELOAD_NOTIFY); - } else + /* Remounting extensions asynchronously done, proceed to reload */ + service_enter_reload(s); + else service_reload_finish(s, f); break; - case SERVICE_REFRESH_EXTENSIONS: - if (f == SERVICE_SUCCESS) - /* Remounting extensions asynchronously done, proceed to signal */ - service_enter_reload_signal_exec(s); - else + case SERVICE_RELOAD: + if (f != SERVICE_SUCCESS) { service_reload_finish(s, f); + break; + } + + if (service_load_pid_file(s, true) < 0) + service_search_main_pid(s); + + service_enter_reload_signal(s); + break; + + case SERVICE_RELOAD_POST: + service_reload_finish(s, f); break; case SERVICE_MOUNTING: @@ -4455,10 +4507,11 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us service_enter_stop(s, SERVICE_FAILURE_TIMEOUT); break; + case SERVICE_REFRESH_EXTENSIONS: case SERVICE_RELOAD: case SERVICE_RELOAD_SIGNAL: case SERVICE_RELOAD_NOTIFY: - case SERVICE_REFRESH_EXTENSIONS: + case SERVICE_RELOAD_POST: log_unit_warning(UNIT(s), "Reload operation timed out. Killing reload process."); service_kill_control_process(s); service_reload_finish(s, SERVICE_FAILURE_TIMEOUT); @@ -4790,7 +4843,10 @@ static void service_notify_message_process_state(Service *s, char * const *tags) if (strv_contains(tags, "READY=1")) { - s->notify_state = NOTIFY_READY; + if (s->notify_state == NOTIFY_RELOADING) + s->notify_state = NOTIFY_RELOAD_READY; + else + s->notify_state = NOTIFY_READY; /* Combined RELOADING=1 and READY=1? Then this is indication that the service started and * immediately finished reloading. */ @@ -4799,7 +4855,7 @@ static void service_notify_message_process_state(Service *s, char * const *tags) monotonic_usec != USEC_INFINITY && monotonic_usec >= s->reload_begin_usec) /* Valid Type=notify-reload protocol? Then we're all good. */ - service_reload_finish(s, SERVICE_SUCCESS); + service_enter_reload_post(s); else if (s->state == SERVICE_RUNNING) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -4820,7 +4876,7 @@ static void service_notify_message_process_state(Service *s, char * const *tags) /* Sending READY=1 while we are reloading informs us that the reloading is complete. */ if (s->state == SERVICE_RELOAD_NOTIFY) - service_reload_finish(s, SERVICE_SUCCESS); + service_enter_reload_post(s); } else if (strv_contains(tags, "RELOADING=1")) { @@ -4872,7 +4928,7 @@ static void service_notify_message( r = service_notify_message_parse_new_pid(u, tags, fds, &new_main_pid); if (r > 0 && IN_SET(s->state, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, - SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_REFRESH_EXTENSIONS, + SERVICE_REFRESH_EXTENSIONS, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_RELOAD_POST, SERVICE_STOP, SERVICE_STOP_SIGTERM) && (!s->main_pid_known || !pidref_equal(&new_main_pid, &s->main_pid))) { @@ -5140,10 +5196,11 @@ static bool pick_up_pid_from_bus_name(Service *s) { SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, + SERVICE_REFRESH_EXTENSIONS, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, - SERVICE_REFRESH_EXTENSIONS, + SERVICE_RELOAD_POST, SERVICE_MOUNTING); } @@ -5325,10 +5382,11 @@ static bool service_needs_console(Unit *u) { SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, + SERVICE_REFRESH_EXTENSIONS, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, - SERVICE_REFRESH_EXTENSIONS, + SERVICE_RELOAD_POST, SERVICE_MOUNTING, SERVICE_STOP, SERVICE_STOP_WATCHDOG, @@ -5761,33 +5819,36 @@ static const char* const service_exit_type_table[_SERVICE_EXIT_TYPE_MAX] = { DEFINE_STRING_TABLE_LOOKUP(service_exit_type, ServiceExitType); static const char* const service_exec_command_table[_SERVICE_EXEC_COMMAND_MAX] = { - [SERVICE_EXEC_CONDITION] = "ExecCondition", - [SERVICE_EXEC_START_PRE] = "ExecStartPre", - [SERVICE_EXEC_START] = "ExecStart", - [SERVICE_EXEC_START_POST] = "ExecStartPost", - [SERVICE_EXEC_RELOAD] = "ExecReload", - [SERVICE_EXEC_STOP] = "ExecStop", - [SERVICE_EXEC_STOP_POST] = "ExecStopPost", + [SERVICE_EXEC_CONDITION] = "ExecCondition", + [SERVICE_EXEC_START_PRE] = "ExecStartPre", + [SERVICE_EXEC_START] = "ExecStart", + [SERVICE_EXEC_START_POST] = "ExecStartPost", + [SERVICE_EXEC_RELOAD] = "ExecReload", + [SERVICE_EXEC_RELOAD_POST] = "ExecReloadPost", + [SERVICE_EXEC_STOP] = "ExecStop", + [SERVICE_EXEC_STOP_POST] = "ExecStopPost", }; DEFINE_STRING_TABLE_LOOKUP(service_exec_command, ServiceExecCommand); static const char* const service_exec_ex_command_table[_SERVICE_EXEC_COMMAND_MAX] = { - [SERVICE_EXEC_CONDITION] = "ExecConditionEx", - [SERVICE_EXEC_START_PRE] = "ExecStartPreEx", - [SERVICE_EXEC_START] = "ExecStartEx", - [SERVICE_EXEC_START_POST] = "ExecStartPostEx", - [SERVICE_EXEC_RELOAD] = "ExecReloadEx", - [SERVICE_EXEC_STOP] = "ExecStopEx", - [SERVICE_EXEC_STOP_POST] = "ExecStopPostEx", + [SERVICE_EXEC_CONDITION] = "ExecConditionEx", + [SERVICE_EXEC_START_PRE] = "ExecStartPreEx", + [SERVICE_EXEC_START] = "ExecStartEx", + [SERVICE_EXEC_START_POST] = "ExecStartPostEx", + [SERVICE_EXEC_RELOAD] = "ExecReloadEx", + [SERVICE_EXEC_RELOAD_POST] = "ExecReloadPostEx", + [SERVICE_EXEC_STOP] = "ExecStopEx", + [SERVICE_EXEC_STOP_POST] = "ExecStopPostEx", }; DEFINE_STRING_TABLE_LOOKUP(service_exec_ex_command, ServiceExecCommand); static const char* const notify_state_table[_NOTIFY_STATE_MAX] = { - [NOTIFY_READY] = "ready", - [NOTIFY_RELOADING] = "reloading", - [NOTIFY_STOPPING] = "stopping", + [NOTIFY_READY] = "ready", + [NOTIFY_RELOADING] = "reloading", + [NOTIFY_RELOAD_READY] = "reload-ready", + [NOTIFY_STOPPING] = "stopping", }; DEFINE_STRING_TABLE_LOOKUP(notify_state, NotifyState); diff --git a/src/core/service.h b/src/core/service.h index e27b382e802..06fafb54822 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -46,6 +46,7 @@ typedef enum ServiceExecCommand { SERVICE_EXEC_START, SERVICE_EXEC_START_POST, SERVICE_EXEC_RELOAD, + SERVICE_EXEC_RELOAD_POST, SERVICE_EXEC_STOP, SERVICE_EXEC_STOP_POST, _SERVICE_EXEC_COMMAND_MAX, @@ -55,6 +56,7 @@ typedef enum ServiceExecCommand { typedef enum NotifyState { NOTIFY_READY, NOTIFY_RELOADING, + NOTIFY_RELOAD_READY, NOTIFY_STOPPING, _NOTIFY_STATE_MAX, _NOTIFY_STATE_INVALID = -EINVAL, diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 30a029716dc..3d49cf5415b 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -2649,6 +2649,8 @@ static const BusProperty service_properties[] = { { "ExecStartPostEx", bus_append_exec_command }, /* compat */ { "ExecReload", bus_append_exec_command }, { "ExecReloadEx", bus_append_exec_command }, /* compat */ + { "ExecReloadPost", bus_append_exec_command }, + { "ExecReloadPostEx", bus_append_exec_command }, /* compat */ { "ExecStop", bus_append_exec_command }, { "ExecStopEx", bus_append_exec_command }, /* compat */ { "ExecStopPost", bus_append_exec_command }, diff --git a/src/systemctl/systemctl-show.c b/src/systemctl/systemctl-show.c index 79e1908a236..f1c1ab0eff0 100644 --- a/src/systemctl/systemctl-show.c +++ b/src/systemctl/systemctl-show.c @@ -2284,6 +2284,8 @@ static int show_one( { "ExecStartPostEx", "a(sasasttttuii)", map_exec, 0 }, { "ExecReload", "a(sasbttttuii)", map_exec, 0 }, { "ExecReloadEx", "a(sasasttttuii)", map_exec, 0 }, + { "ExecReloadPost", "a(sasbttttuii)", map_exec, 0 }, + { "ExecReloadPostEx", "a(sasasttttuii)", map_exec, 0 }, { "ExecStopPre", "a(sasbttttuii)", map_exec, 0 }, { "ExecStop", "a(sasbttttuii)", map_exec, 0 }, { "ExecStopEx", "a(sasasttttuii)", map_exec, 0 }, -- 2.47.3