You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
689 lines
36 KiB
689 lines
36 KiB
7 months ago
|
From f64d331351e33199c4096b2ae4a4b9d24d127661 Mon Sep 17 00:00:00 2001
|
||
|
From: Lennart Poettering <lennart@poettering.net>
|
||
|
Date: Mon, 2 Jan 2023 16:49:23 +0100
|
||
|
Subject: [PATCH] pid1: add new Type=notify-reload service type
|
||
|
|
||
|
Fixes: #6162
|
||
|
(cherry picked from commit 3bd28bf721dc70722ff1c675026ed0b44ad968a3)
|
||
|
|
||
|
Resolves: RHEL-6090
|
||
|
---
|
||
|
man/org.freedesktop.systemd1.xml | 6 +
|
||
|
src/basic/unit-def.c | 2 +
|
||
|
src/basic/unit-def.h | 4 +-
|
||
|
src/core/dbus-service.c | 5 +
|
||
|
src/core/load-fragment-gperf.gperf.in | 1 +
|
||
|
src/core/service.c | 226 ++++++++++++++++++--------
|
||
|
src/core/service.h | 18 +-
|
||
|
src/shared/bus-unit-util.c | 3 +-
|
||
|
8 files changed, 189 insertions(+), 76 deletions(-)
|
||
|
|
||
|
diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml
|
||
|
index 13a84af747..c18428a092 100644
|
||
|
--- a/man/org.freedesktop.systemd1.xml
|
||
|
+++ b/man/org.freedesktop.systemd1.xml
|
||
|
@@ -2570,6 +2570,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||
|
readonly u NRestarts = ...;
|
||
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||
|
readonly s OOMPolicy = '...';
|
||
|
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||
|
+ readonly i ReloadSignal = ...;
|
||
|
readonly t ExecMainStartTimestamp = ...;
|
||
|
readonly t ExecMainStartTimestampMonotonic = ...;
|
||
|
readonly t ExecMainExitTimestamp = ...;
|
||
|
@@ -3163,6 +3165,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||
|
|
||
|
<!--property OOMPolicy is not documented!-->
|
||
|
|
||
|
+ <!--property ReloadSignal is not documented!-->
|
||
|
+
|
||
|
<!--property ExecCondition is not documented!-->
|
||
|
|
||
|
<!--property ExecConditionEx is not documented!-->
|
||
|
@@ -3715,6 +3719,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||
|
|
||
|
<variablelist class="dbus-property" generated="True" extra-ref="OOMPolicy"/>
|
||
|
|
||
|
+ <variablelist class="dbus-property" generated="True" extra-ref="ReloadSignal"/>
|
||
|
+
|
||
|
<variablelist class="dbus-property" generated="True" extra-ref="ExecMainStartTimestamp"/>
|
||
|
|
||
|
<variablelist class="dbus-property" generated="True" extra-ref="ExecMainStartTimestampMonotonic"/>
|
||
|
diff --git a/src/basic/unit-def.c b/src/basic/unit-def.c
|
||
|
index 94cd603e32..bdb1860246 100644
|
||
|
--- a/src/basic/unit-def.c
|
||
|
+++ b/src/basic/unit-def.c
|
||
|
@@ -188,6 +188,8 @@ static const char* const service_state_table[_SERVICE_STATE_MAX] = {
|
||
|
[SERVICE_RUNNING] = "running",
|
||
|
[SERVICE_EXITED] = "exited",
|
||
|
[SERVICE_RELOAD] = "reload",
|
||
|
+ [SERVICE_RELOAD_SIGNAL] = "reload-signal",
|
||
|
+ [SERVICE_RELOAD_NOTIFY] = "reload-notify",
|
||
|
[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 5fcd51c095..bae132ea09 100644
|
||
|
--- a/src/basic/unit-def.h
|
||
|
+++ b/src/basic/unit-def.h
|
||
|
@@ -132,7 +132,9 @@ typedef enum ServiceState {
|
||
|
SERVICE_START_POST,
|
||
|
SERVICE_RUNNING,
|
||
|
SERVICE_EXITED, /* Nothing is running anymore, but RemainAfterExit is true hence this is OK */
|
||
|
- SERVICE_RELOAD,
|
||
|
+ SERVICE_RELOAD, /* Reloading via ExecReload= */
|
||
|
+ SERVICE_RELOAD_SIGNAL, /* Reloading via SIGHUP requested */
|
||
|
+ SERVICE_RELOAD_NOTIFY, /* Waiting for READY=1 after RELOADING=1 notify */
|
||
|
SERVICE_STOP, /* No STOP_PRE state, instead just register multiple STOP executables */
|
||
|
SERVICE_STOP_WATCHDOG,
|
||
|
SERVICE_STOP_SIGTERM,
|
||
|
diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c
|
||
|
index 6e4bc0bd1a..3d130db66a 100644
|
||
|
--- a/src/core/dbus-service.c
|
||
|
+++ b/src/core/dbus-service.c
|
||
|
@@ -228,6 +228,7 @@ const sd_bus_vtable bus_service_vtable[] = {
|
||
|
SD_BUS_PROPERTY("GID", "u", bus_property_get_gid, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||
|
SD_BUS_PROPERTY("NRestarts", "u", bus_property_get_unsigned, offsetof(Service, n_restarts), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||
|
SD_BUS_PROPERTY("OOMPolicy", "s", bus_property_get_oom_policy, offsetof(Service, oom_policy), SD_BUS_VTABLE_PROPERTY_CONST),
|
||
|
+ SD_BUS_PROPERTY("ReloadSignal", "i", bus_property_get_int, offsetof(Service, reload_signal), SD_BUS_VTABLE_PROPERTY_CONST),
|
||
|
|
||
|
BUS_EXEC_STATUS_VTABLE("ExecMain", offsetof(Service, main_exec_status), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||
|
BUS_EXEC_COMMAND_LIST_VTABLE("ExecCondition", offsetof(Service, exec_command[SERVICE_EXEC_CONDITION]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
|
||
|
@@ -374,6 +375,7 @@ static BUS_DEFINE_SET_TRANSIENT_PARSE(service_restart, ServiceRestart, service_r
|
||
|
static BUS_DEFINE_SET_TRANSIENT_PARSE(oom_policy, OOMPolicy, oom_policy_from_string);
|
||
|
static BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(bus_name, sd_bus_service_name_is_valid);
|
||
|
static BUS_DEFINE_SET_TRANSIENT_PARSE(timeout_failure_mode, ServiceTimeoutFailureMode, service_timeout_failure_mode_from_string);
|
||
|
+static BUS_DEFINE_SET_TRANSIENT_TO_STRING(reload_signal, "i", int32_t, int, "%" PRIi32, signal_to_string_with_check);
|
||
|
|
||
|
static int bus_service_set_transient_property(
|
||
|
Service *s,
|
||
|
@@ -532,6 +534,9 @@ static int bus_service_set_transient_property(
|
||
|
if (streq(name, "StandardErrorFileDescriptor"))
|
||
|
return bus_set_transient_std_fd(u, name, &s->stderr_fd, &s->exec_context.stdio_as_fds, message, flags, error);
|
||
|
|
||
|
+ if (streq(name, "ReloadSignal"))
|
||
|
+ return bus_set_transient_reload_signal(u, name, &s->reload_signal, message, flags, error);
|
||
|
+
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in
|
||
|
index 81a5971339..53089d5590 100644
|
||
|
--- a/src/core/load-fragment-gperf.gperf.in
|
||
|
+++ b/src/core/load-fragment-gperf.gperf.in
|
||
|
@@ -424,6 +424,7 @@ Service.BusPolicy, config_parse_warn_compat,
|
||
|
Service.USBFunctionDescriptors, config_parse_unit_path_printf, 0, offsetof(Service, usb_function_descriptors)
|
||
|
Service.USBFunctionStrings, config_parse_unit_path_printf, 0, offsetof(Service, usb_function_strings)
|
||
|
Service.OOMPolicy, config_parse_oom_policy, 0, offsetof(Service, oom_policy)
|
||
|
+Service.ReloadSignal, config_parse_signal, 0, offsetof(Service, reload_signal)
|
||
|
{{ EXEC_CONTEXT_CONFIG_ITEMS('Service') }}
|
||
|
{{ CGROUP_CONTEXT_CONFIG_ITEMS('Service') }}
|
||
|
{{ KILL_CONTEXT_CONFIG_ITEMS('Service') }}
|
||
|
diff --git a/src/core/service.c b/src/core/service.c
|
||
|
index aa76b4ad9a..902948905f 100644
|
||
|
--- a/src/core/service.c
|
||
|
+++ b/src/core/service.c
|
||
|
@@ -54,6 +54,8 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
|
||
|
[SERVICE_RUNNING] = UNIT_ACTIVE,
|
||
|
[SERVICE_EXITED] = UNIT_ACTIVE,
|
||
|
[SERVICE_RELOAD] = UNIT_RELOADING,
|
||
|
+ [SERVICE_RELOAD_SIGNAL] = UNIT_RELOADING,
|
||
|
+ [SERVICE_RELOAD_NOTIFY] = UNIT_RELOADING,
|
||
|
[SERVICE_STOP] = UNIT_DEACTIVATING,
|
||
|
[SERVICE_STOP_WATCHDOG] = UNIT_DEACTIVATING,
|
||
|
[SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING,
|
||
|
@@ -78,6 +80,8 @@ static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] =
|
||
|
[SERVICE_RUNNING] = UNIT_ACTIVE,
|
||
|
[SERVICE_EXITED] = UNIT_ACTIVE,
|
||
|
[SERVICE_RELOAD] = UNIT_RELOADING,
|
||
|
+ [SERVICE_RELOAD_SIGNAL] = UNIT_RELOADING,
|
||
|
+ [SERVICE_RELOAD_NOTIFY] = UNIT_RELOADING,
|
||
|
[SERVICE_STOP] = UNIT_DEACTIVATING,
|
||
|
[SERVICE_STOP_WATCHDOG] = UNIT_DEACTIVATING,
|
||
|
[SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING,
|
||
|
@@ -124,6 +128,8 @@ static void service_init(Unit *u) {
|
||
|
s->watchdog_original_usec = USEC_INFINITY;
|
||
|
|
||
|
s->oom_policy = _OOM_POLICY_INVALID;
|
||
|
+ s->reload_begin_usec = USEC_INFINITY;
|
||
|
+ s->reload_signal = SIGHUP;
|
||
|
}
|
||
|
|
||
|
static void service_unwatch_control_pid(Service *s) {
|
||
|
@@ -765,7 +771,7 @@ static int service_add_extras(Service *s) {
|
||
|
|
||
|
/* If the service needs the notify socket, let's enable it automatically. */
|
||
|
if (s->notify_access == NOTIFY_NONE &&
|
||
|
- (s->type == SERVICE_NOTIFY || s->watchdog_usec > 0 || s->n_fd_store_max > 0))
|
||
|
+ (IN_SET(s->type, SERVICE_NOTIFY, SERVICE_NOTIFY_RELOAD) || s->watchdog_usec > 0 || s->n_fd_store_max > 0))
|
||
|
s->notify_access = NOTIFY_MAIN;
|
||
|
|
||
|
/* If no OOM policy was explicitly set, then default to the configure default OOM policy. Except when
|
||
|
@@ -830,7 +836,8 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
|
||
|
"%sRestart: %s\n"
|
||
|
"%sNotifyAccess: %s\n"
|
||
|
"%sNotifyState: %s\n"
|
||
|
- "%sOOMPolicy: %s\n",
|
||
|
+ "%sOOMPolicy: %s\n"
|
||
|
+ "%sReloadSignal: %s\n",
|
||
|
prefix, service_state_to_string(s->state),
|
||
|
prefix, service_result_to_string(s->result),
|
||
|
prefix, service_result_to_string(s->reload_result),
|
||
|
@@ -843,7 +850,8 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
|
||
|
prefix, service_restart_to_string(s->restart),
|
||
|
prefix, notify_access_to_string(s->notify_access),
|
||
|
prefix, notify_state_to_string(s->notify_state),
|
||
|
- prefix, oom_policy_to_string(s->oom_policy));
|
||
|
+ prefix, oom_policy_to_string(s->oom_policy),
|
||
|
+ prefix, signal_to_string(s->reload_signal));
|
||
|
|
||
|
if (s->control_pid > 0)
|
||
|
fprintf(f,
|
||
|
@@ -1088,7 +1096,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, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY,
|
||
|
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
|
||
|
SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
|
||
|
SERVICE_AUTO_RESTART,
|
||
|
@@ -1097,7 +1105,8 @@ static void service_set_state(Service *s, ServiceState state) {
|
||
|
|
||
|
if (!IN_SET(state,
|
||
|
SERVICE_START, SERVICE_START_POST,
|
||
|
- SERVICE_RUNNING, SERVICE_RELOAD,
|
||
|
+ SERVICE_RUNNING,
|
||
|
+ SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY,
|
||
|
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
|
||
|
SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
|
||
|
service_unwatch_main_pid(s);
|
||
|
@@ -1106,7 +1115,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_RELOAD,
|
||
|
+ SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY,
|
||
|
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
|
||
|
SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
|
||
|
SERVICE_CLEANING)) {
|
||
|
@@ -1122,7 +1131,8 @@ 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_RUNNING,
|
||
|
+ SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY,
|
||
|
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
|
||
|
SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL) &&
|
||
|
!(state == SERVICE_DEAD && UNIT(s)->job))
|
||
|
@@ -1131,7 +1141,7 @@ static void service_set_state(Service *s, ServiceState state) {
|
||
|
if (state != SERVICE_START)
|
||
|
s->exec_fd_event_source = sd_event_source_disable_unref(s->exec_fd_event_source);
|
||
|
|
||
|
- if (!IN_SET(state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD))
|
||
|
+ 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,
|
||
|
@@ -1157,6 +1167,8 @@ static usec_t service_coldplug_timeout(Service *s) {
|
||
|
case SERVICE_START:
|
||
|
case SERVICE_START_POST:
|
||
|
case SERVICE_RELOAD:
|
||
|
+ case SERVICE_RELOAD_SIGNAL:
|
||
|
+ case SERVICE_RELOAD_NOTIFY:
|
||
|
return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->timeout_start_usec);
|
||
|
|
||
|
case SERVICE_RUNNING:
|
||
|
@@ -1203,7 +1215,8 @@ static int service_coldplug(Unit *u) {
|
||
|
pid_is_unwaited(s->main_pid) &&
|
||
|
(IN_SET(s->deserialized_state,
|
||
|
SERVICE_START, SERVICE_START_POST,
|
||
|
- SERVICE_RUNNING, SERVICE_RELOAD,
|
||
|
+ SERVICE_RUNNING,
|
||
|
+ SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY,
|
||
|
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
|
||
|
SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))) {
|
||
|
r = unit_watch_pid(UNIT(s), s->main_pid, false);
|
||
|
@@ -1215,7 +1228,7 @@ static int service_coldplug(Unit *u) {
|
||
|
pid_is_unwaited(s->control_pid) &&
|
||
|
IN_SET(s->deserialized_state,
|
||
|
SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
|
||
|
- SERVICE_RELOAD,
|
||
|
+ SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY,
|
||
|
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
|
||
|
SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
|
||
|
SERVICE_CLEANING)) {
|
||
|
@@ -1230,7 +1243,7 @@ static int service_coldplug(Unit *u) {
|
||
|
(void) unit_setup_exec_runtime(u);
|
||
|
}
|
||
|
|
||
|
- if (IN_SET(s->deserialized_state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD))
|
||
|
+ if (IN_SET(s->deserialized_state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY))
|
||
|
service_start_watchdog(s);
|
||
|
|
||
|
if (UNIT_ISSET(s->accept_socket)) {
|
||
|
@@ -2255,7 +2268,7 @@ static void service_enter_start(Service *s) {
|
||
|
s->control_pid = pid;
|
||
|
service_set_state(s, SERVICE_START);
|
||
|
|
||
|
- } else if (IN_SET(s->type, SERVICE_ONESHOT, SERVICE_DBUS, SERVICE_NOTIFY, SERVICE_EXEC)) {
|
||
|
+ } else if (IN_SET(s->type, SERVICE_ONESHOT, SERVICE_DBUS, SERVICE_NOTIFY, SERVICE_NOTIFY_RELOAD, SERVICE_EXEC)) {
|
||
|
|
||
|
/* For oneshot services we wait until the start process exited, too, but it is our main process. */
|
||
|
|
||
|
@@ -2399,7 +2412,7 @@ static void service_enter_reload_by_notify(Service *s) {
|
||
|
assert(s);
|
||
|
|
||
|
service_arm_timer(s, /* relative= */ true, s->timeout_start_usec);
|
||
|
- service_set_state(s, SERVICE_RELOAD);
|
||
|
+ service_set_state(s, SERVICE_RELOAD_NOTIFY);
|
||
|
|
||
|
/* service_enter_reload_by_notify is never called during a reload, thus no loops are possible. */
|
||
|
r = manager_propagate_reload(UNIT(s)->manager, UNIT(s), JOB_FAIL, &error);
|
||
|
@@ -2408,6 +2421,7 @@ static void service_enter_reload_by_notify(Service *s) {
|
||
|
}
|
||
|
|
||
|
static void service_enter_reload(Service *s) {
|
||
|
+ bool killed = false;
|
||
|
int r;
|
||
|
|
||
|
assert(s);
|
||
|
@@ -2415,6 +2429,18 @@ static void service_enter_reload(Service *s) {
|
||
|
service_unwatch_control_pid(s);
|
||
|
s->reload_result = SERVICE_SUCCESS;
|
||
|
|
||
|
+ usec_t ts = now(CLOCK_MONOTONIC);
|
||
|
+
|
||
|
+ if (s->type == SERVICE_NOTIFY_RELOAD && s->main_pid > 0) {
|
||
|
+ r = 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];
|
||
|
if (s->control_command) {
|
||
|
s->control_command_id = SERVICE_EXEC_RELOAD;
|
||
|
@@ -2424,17 +2450,28 @@ static void service_enter_reload(Service *s) {
|
||
|
s->timeout_start_usec,
|
||
|
EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_CONTROL_CGROUP,
|
||
|
&s->control_pid);
|
||
|
- if (r < 0)
|
||
|
+ if (r < 0) {
|
||
|
+ log_unit_warning_errno(UNIT(s), r, "Failed to run 'reload' task: %m");
|
||
|
goto fail;
|
||
|
+ }
|
||
|
|
||
|
service_set_state(s, SERVICE_RELOAD);
|
||
|
- } else
|
||
|
+ } else if (killed) {
|
||
|
+ service_arm_timer(s, /* relative= */ true, s->timeout_start_usec);
|
||
|
+ service_set_state(s, SERVICE_RELOAD_SIGNAL);
|
||
|
+ } else {
|
||
|
service_enter_running(s, SERVICE_SUCCESS);
|
||
|
+ return;
|
||
|
+ }
|
||
|
|
||
|
+ /* 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;
|
||
|
|
||
|
fail:
|
||
|
- log_unit_warning_errno(UNIT(s), r, "Failed to run 'reload' task: %m");
|
||
|
s->reload_result = SERVICE_FAILURE_RESOURCES;
|
||
|
service_enter_running(s, SERVICE_SUCCESS);
|
||
|
}
|
||
|
@@ -2597,9 +2634,8 @@ static int service_stop(Unit *u) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
- /* If there's already something running we go directly into
|
||
|
- * kill mode. */
|
||
|
- if (IN_SET(s->state, SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD, SERVICE_STOP_WATCHDOG)) {
|
||
|
+ /* If there's already something running we go directly into kill mode. */
|
||
|
+ if (IN_SET(s->state, SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_STOP_WATCHDOG)) {
|
||
|
service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_SUCCESS);
|
||
|
return 0;
|
||
|
}
|
||
|
@@ -2632,7 +2668,8 @@ _pure_ static bool service_can_reload(Unit *u) {
|
||
|
|
||
|
assert(s);
|
||
|
|
||
|
- return !!s->exec_command[SERVICE_EXEC_RELOAD];
|
||
|
+ return s->exec_command[SERVICE_EXEC_RELOAD] ||
|
||
|
+ s->type == SERVICE_NOTIFY_RELOAD;
|
||
|
}
|
||
|
|
||
|
static unsigned service_exec_command_index(Unit *u, ServiceExecCommand id, ExecCommand *current) {
|
||
|
@@ -2808,6 +2845,9 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
|
||
|
if (s->watchdog_original_usec != USEC_INFINITY)
|
||
|
(void) serialize_item_format(f, "watchdog-original-usec", USEC_FMT, s->watchdog_original_usec);
|
||
|
|
||
|
+ if (s->reload_begin_usec != USEC_INFINITY)
|
||
|
+ (void) serialize_item_format(f, "reload-begin-usec", USEC_FMT, s->reload_begin_usec);
|
||
|
+
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@@ -3146,6 +3186,10 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
|
||
|
log_unit_debug_errno(u, r, "Failed to parse serialized flush restart counter setting '%s': %m", value);
|
||
|
else
|
||
|
s->flush_n_restarts = r;
|
||
|
+ } else if (streq(key, "reload-begin-usec")) {
|
||
|
+ r = deserialize_usec(value, &s->reload_begin_usec);
|
||
|
+ if (r < 0)
|
||
|
+ log_unit_debug_errno(u, r, "Failed to parse serialized reload begin timestamp '%s', ignoring: %m", value);
|
||
|
} else
|
||
|
log_unit_debug(u, "Unknown serialization key: %s", key);
|
||
|
|
||
|
@@ -3349,7 +3393,7 @@ static void service_notify_cgroup_empty_event(Unit *u) {
|
||
|
* SIGCHLD for. */
|
||
|
|
||
|
case SERVICE_START:
|
||
|
- if (s->type == SERVICE_NOTIFY &&
|
||
|
+ if (IN_SET(s->type, SERVICE_NOTIFY, SERVICE_NOTIFY_RELOAD) &&
|
||
|
main_pid_good(s) == 0 &&
|
||
|
control_pid_good(s) == 0) {
|
||
|
/* No chance of getting a ready notification anymore */
|
||
|
@@ -3553,17 +3597,19 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
||
|
} else {
|
||
|
s->main_command = NULL;
|
||
|
|
||
|
- /* Services with ExitType=cgroup do not act on main PID exiting,
|
||
|
- * unless the cgroup is already empty */
|
||
|
+ /* Services with ExitType=cgroup do not act on main PID exiting, unless the cgroup is
|
||
|
+ * already empty */
|
||
|
if (s->exit_type == SERVICE_EXIT_MAIN || cgroup_good(s) <= 0) {
|
||
|
/* The service exited, so the service is officially gone. */
|
||
|
switch (s->state) {
|
||
|
|
||
|
case SERVICE_START_POST:
|
||
|
case SERVICE_RELOAD:
|
||
|
- /* If neither main nor control processes are running then
|
||
|
- * the current state can never exit cleanly, hence immediately
|
||
|
- * terminate the service. */
|
||
|
+ case SERVICE_RELOAD_SIGNAL:
|
||
|
+ case SERVICE_RELOAD_NOTIFY:
|
||
|
+ /* If neither main nor control processes are running then the current
|
||
|
+ * state can never exit cleanly, hence immediately terminate the
|
||
|
+ * service. */
|
||
|
if (control_pid_good(s) <= 0)
|
||
|
service_enter_stop(s, f);
|
||
|
|
||
|
@@ -3582,7 +3628,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
||
|
else
|
||
|
service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
|
||
|
break;
|
||
|
- } else if (s->type == SERVICE_NOTIFY) {
|
||
|
+ } else if (IN_SET(s->type, SERVICE_NOTIFY, SERVICE_NOTIFY_RELOAD)) {
|
||
|
/* Only enter running through a notification, so that the
|
||
|
* SERVICE_START state signifies that no ready notification
|
||
|
* has been received */
|
||
|
@@ -3675,15 +3721,13 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
||
|
s->control_command->command_next &&
|
||
|
f == SERVICE_SUCCESS) {
|
||
|
|
||
|
- /* There is another command to *
|
||
|
- * execute, so let's do that. */
|
||
|
+ /* There is another command to * execute, so let's do that. */
|
||
|
|
||
|
log_unit_debug(u, "Running next control command for state %s.", service_state_to_string(s->state));
|
||
|
service_run_next_control(s);
|
||
|
|
||
|
} else {
|
||
|
- /* No further commands for this step, so let's
|
||
|
- * figure out what to do next */
|
||
|
+ /* No further commands for this step, so let's figure out what to do next */
|
||
|
|
||
|
s->control_command = NULL;
|
||
|
s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
|
||
|
@@ -3761,12 +3805,22 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
||
|
break;
|
||
|
|
||
|
case SERVICE_RELOAD:
|
||
|
+ case SERVICE_RELOAD_SIGNAL:
|
||
|
+ case SERVICE_RELOAD_NOTIFY:
|
||
|
if (f == SERVICE_SUCCESS)
|
||
|
if (service_load_pid_file(s, true) < 0)
|
||
|
service_search_main_pid(s);
|
||
|
|
||
|
s->reload_result = f;
|
||
|
- service_enter_running(s, SERVICE_SUCCESS);
|
||
|
+
|
||
|
+ /* If the last notification we received from the service process indiciates
|
||
|
+ * 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)
|
||
|
+ service_set_state(s, SERVICE_RELOAD_NOTIFY);
|
||
|
+ else
|
||
|
+ service_enter_running(s, SERVICE_SUCCESS);
|
||
|
break;
|
||
|
|
||
|
case SERVICE_STOP:
|
||
|
@@ -3869,6 +3923,8 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
|
||
|
break;
|
||
|
|
||
|
case SERVICE_RELOAD:
|
||
|
+ case SERVICE_RELOAD_SIGNAL:
|
||
|
+ case SERVICE_RELOAD_NOTIFY:
|
||
|
log_unit_warning(UNIT(s), "Reload operation timed out. Killing reload process.");
|
||
|
service_kill_control_process(s);
|
||
|
s->reload_result = SERVICE_FAILURE_TIMEOUT;
|
||
|
@@ -4094,6 +4150,7 @@ static void service_notify_message(
|
||
|
|
||
|
Service *s = SERVICE(u);
|
||
|
bool notify_dbus = false;
|
||
|
+ usec_t monotonic_usec = USEC_INFINITY;
|
||
|
const char *e;
|
||
|
int r;
|
||
|
|
||
|
@@ -4112,7 +4169,7 @@ static void service_notify_message(
|
||
|
|
||
|
/* Interpret MAINPID= */
|
||
|
e = strv_find_startswith(tags, "MAINPID=");
|
||
|
- if (e && IN_SET(s->state, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD)) {
|
||
|
+ if (e && IN_SET(s->state, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY)) {
|
||
|
pid_t new_main_pid;
|
||
|
|
||
|
if (parse_pid(e, &new_main_pid) < 0)
|
||
|
@@ -4141,43 +4198,73 @@ static void service_notify_message(
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- /* Interpret READY=/STOPPING=/RELOADING=. Last one wins. */
|
||
|
- STRV_FOREACH_BACKWARDS(i, tags) {
|
||
|
+ /* Parse MONOTONIC_USEC= */
|
||
|
+ e = strv_find_startswith(tags, "MONOTONIC_USEC=");
|
||
|
+ if (e) {
|
||
|
+ r = safe_atou64(e, &monotonic_usec);
|
||
|
+ if (r < 0)
|
||
|
+ log_unit_warning_errno(u, r, "Failed to parse MONOTONIC_USEC= field in notification message, ignoring: %s", e);
|
||
|
+ }
|
||
|
|
||
|
- if (streq(*i, "READY=1")) {
|
||
|
- s->notify_state = NOTIFY_READY;
|
||
|
+ /* Interpret READY=/STOPPING=/RELOADING=. STOPPING= wins over the others, and READY= over RELOADING= */
|
||
|
+ if (strv_contains(tags, "STOPPING=1")) {
|
||
|
+ s->notify_state = NOTIFY_STOPPING;
|
||
|
|
||
|
- /* Type=notify services inform us about completed
|
||
|
- * initialization with READY=1 */
|
||
|
- if (s->type == SERVICE_NOTIFY && s->state == SERVICE_START)
|
||
|
- service_enter_start_post(s);
|
||
|
+ if (IN_SET(s->state, SERVICE_RUNNING, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY))
|
||
|
+ service_enter_stop_by_notify(s);
|
||
|
|
||
|
- /* Sending READY=1 while we are reloading informs us
|
||
|
- * that the reloading is complete */
|
||
|
- if (s->state == SERVICE_RELOAD && s->control_pid == 0)
|
||
|
- service_enter_running(s, SERVICE_SUCCESS);
|
||
|
+ notify_dbus = true;
|
||
|
|
||
|
- notify_dbus = true;
|
||
|
- break;
|
||
|
+ } else if (strv_contains(tags, "READY=1")) {
|
||
|
|
||
|
- } else if (streq(*i, "RELOADING=1")) {
|
||
|
- s->notify_state = NOTIFY_RELOADING;
|
||
|
+ s->notify_state = NOTIFY_READY;
|
||
|
|
||
|
- if (s->state == SERVICE_RUNNING)
|
||
|
- service_enter_reload_by_notify(s);
|
||
|
+ /* Type=notify services inform us about completed initialization with READY=1 */
|
||
|
+ if (IN_SET(s->type, SERVICE_NOTIFY, SERVICE_NOTIFY_RELOAD) &&
|
||
|
+ s->state == SERVICE_START)
|
||
|
+ service_enter_start_post(s);
|
||
|
|
||
|
- notify_dbus = true;
|
||
|
- break;
|
||
|
+ /* Sending READY=1 while we are reloading informs us that the reloading is complete. */
|
||
|
+ if (s->state == SERVICE_RELOAD_NOTIFY)
|
||
|
+ service_enter_running(s, SERVICE_SUCCESS);
|
||
|
|
||
|
- } else if (streq(*i, "STOPPING=1")) {
|
||
|
- s->notify_state = NOTIFY_STOPPING;
|
||
|
+ /* Combined RELOADING=1 and READY=1? Then this is indication that the service started and
|
||
|
+ * immediately finished reloading. */
|
||
|
+ if (s->state == SERVICE_RELOAD_SIGNAL &&
|
||
|
+ strv_contains(tags, "RELOADING=1") &&
|
||
|
+ monotonic_usec != USEC_INFINITY &&
|
||
|
+ monotonic_usec >= s->reload_begin_usec) {
|
||
|
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||
|
|
||
|
- if (s->state == SERVICE_RUNNING)
|
||
|
- service_enter_stop_by_notify(s);
|
||
|
+ /* Propagate a reload explicitly */
|
||
|
+ r = manager_propagate_reload(UNIT(s)->manager, UNIT(s), JOB_FAIL, &error);
|
||
|
+ if (r < 0)
|
||
|
+ log_unit_warning(UNIT(s), "Failed to schedule propagation of reload, ignoring: %s", bus_error_message(&error, r));
|
||
|
|
||
|
- notify_dbus = true;
|
||
|
- break;
|
||
|
+ service_enter_running(s, SERVICE_SUCCESS);
|
||
|
}
|
||
|
+
|
||
|
+ notify_dbus = true;
|
||
|
+
|
||
|
+ } else if (strv_contains(tags, "RELOADING=1")) {
|
||
|
+
|
||
|
+ s->notify_state = NOTIFY_RELOADING;
|
||
|
+
|
||
|
+ /* Sending RELOADING=1 after we send SIGHUP to request a reload will transition
|
||
|
+ * things to "reload-notify" state, where we'll wait for READY=1 to let us know the
|
||
|
+ * reload is done. Note that we insist on a timestamp being sent along here, so that
|
||
|
+ * we know for sure this is a reload cycle initiated *after* we sent the signal */
|
||
|
+ if (s->state == SERVICE_RELOAD_SIGNAL &&
|
||
|
+ monotonic_usec != USEC_INFINITY &&
|
||
|
+ monotonic_usec >= s->reload_begin_usec)
|
||
|
+ /* Note, we don't call service_enter_reload_by_notify() here, because we
|
||
|
+ * don't need reload propagation nor do we want to restart the time-out. */
|
||
|
+ service_set_state(s, SERVICE_RELOAD_NOTIFY);
|
||
|
+
|
||
|
+ if (s->state == SERVICE_RUNNING)
|
||
|
+ service_enter_reload_by_notify(s);
|
||
|
+
|
||
|
+ notify_dbus = true;
|
||
|
}
|
||
|
|
||
|
/* Interpret STATUS= */
|
||
|
@@ -4307,7 +4394,9 @@ static bool pick_up_pid_from_bus_name(Service *s) {
|
||
|
SERVICE_START,
|
||
|
SERVICE_START_POST,
|
||
|
SERVICE_RUNNING,
|
||
|
- SERVICE_RELOAD);
|
||
|
+ SERVICE_RELOAD,
|
||
|
+ SERVICE_RELOAD_SIGNAL,
|
||
|
+ SERVICE_RELOAD_NOTIFY);
|
||
|
}
|
||
|
|
||
|
static int bus_name_pid_lookup_callback(sd_bus_message *reply, void *userdata, sd_bus_error *ret_error) {
|
||
|
@@ -4514,6 +4603,8 @@ static bool service_needs_console(Unit *u) {
|
||
|
SERVICE_START_POST,
|
||
|
SERVICE_RUNNING,
|
||
|
SERVICE_RELOAD,
|
||
|
+ SERVICE_RELOAD_SIGNAL,
|
||
|
+ SERVICE_RELOAD_NOTIFY,
|
||
|
SERVICE_STOP,
|
||
|
SERVICE_STOP_WATCHDOG,
|
||
|
SERVICE_STOP_SIGTERM,
|
||
|
@@ -4636,13 +4727,14 @@ static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
|
||
|
DEFINE_STRING_TABLE_LOOKUP(service_restart, ServiceRestart);
|
||
|
|
||
|
static const char* const service_type_table[_SERVICE_TYPE_MAX] = {
|
||
|
- [SERVICE_SIMPLE] = "simple",
|
||
|
- [SERVICE_FORKING] = "forking",
|
||
|
- [SERVICE_ONESHOT] = "oneshot",
|
||
|
- [SERVICE_DBUS] = "dbus",
|
||
|
- [SERVICE_NOTIFY] = "notify",
|
||
|
- [SERVICE_IDLE] = "idle",
|
||
|
- [SERVICE_EXEC] = "exec",
|
||
|
+ [SERVICE_SIMPLE] = "simple",
|
||
|
+ [SERVICE_FORKING] = "forking",
|
||
|
+ [SERVICE_ONESHOT] = "oneshot",
|
||
|
+ [SERVICE_DBUS] = "dbus",
|
||
|
+ [SERVICE_NOTIFY] = "notify",
|
||
|
+ [SERVICE_NOTIFY_RELOAD] = "notify-reload",
|
||
|
+ [SERVICE_IDLE] = "idle",
|
||
|
+ [SERVICE_EXEC] = "exec",
|
||
|
};
|
||
|
|
||
|
DEFINE_STRING_TABLE_LOOKUP(service_type, ServiceType);
|
||
|
diff --git a/src/core/service.h b/src/core/service.h
|
||
|
index 91e02e6d7e..194067f0e1 100644
|
||
|
--- a/src/core/service.h
|
||
|
+++ b/src/core/service.h
|
||
|
@@ -24,13 +24,14 @@ typedef enum ServiceRestart {
|
||
|
} ServiceRestart;
|
||
|
|
||
|
typedef enum ServiceType {
|
||
|
- SERVICE_SIMPLE, /* we fork and go on right-away (i.e. modern socket activated daemons) */
|
||
|
- SERVICE_FORKING, /* forks by itself (i.e. traditional daemons) */
|
||
|
- SERVICE_ONESHOT, /* we fork and wait until the program finishes (i.e. programs like fsck which run and need to finish before we continue) */
|
||
|
- SERVICE_DBUS, /* we fork and wait until a specific D-Bus name appears on the bus */
|
||
|
- SERVICE_NOTIFY, /* we fork and wait until a daemon sends us a ready message with sd_notify() */
|
||
|
- SERVICE_IDLE, /* much like simple, but delay exec() until all jobs are dispatched. */
|
||
|
- SERVICE_EXEC, /* we fork and wait until we execute exec() (this means our own setup is waited for) */
|
||
|
+ SERVICE_SIMPLE, /* we fork and go on right-away (i.e. modern socket activated daemons) */
|
||
|
+ SERVICE_FORKING, /* forks by itself (i.e. traditional daemons) */
|
||
|
+ SERVICE_ONESHOT, /* we fork and wait until the program finishes (i.e. programs like fsck which run and need to finish before we continue) */
|
||
|
+ SERVICE_DBUS, /* we fork and wait until a specific D-Bus name appears on the bus */
|
||
|
+ SERVICE_NOTIFY, /* we fork and wait until a daemon sends us a ready message with sd_notify() */
|
||
|
+ SERVICE_NOTIFY_RELOAD, /* just like SERVICE_NOTIFY, but also implements a reload protocol via SIGHUP */
|
||
|
+ SERVICE_IDLE, /* much like simple, but delay exec() until all jobs are dispatched. */
|
||
|
+ SERVICE_EXEC, /* we fork and wait until we execute exec() (this means our own setup is waited for) */
|
||
|
_SERVICE_TYPE_MAX,
|
||
|
_SERVICE_TYPE_INVALID = -EINVAL,
|
||
|
} ServiceType;
|
||
|
@@ -215,6 +216,9 @@ struct Service {
|
||
|
bool flush_n_restarts;
|
||
|
|
||
|
OOMPolicy oom_policy;
|
||
|
+
|
||
|
+ int reload_signal;
|
||
|
+ usec_t reload_begin_usec;
|
||
|
};
|
||
|
|
||
|
static inline usec_t service_timeout_abort_usec(Service *s) {
|
||
|
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
|
||
|
index 922011eccd..a9844e1cc3 100644
|
||
|
--- a/src/shared/bus-unit-util.c
|
||
|
+++ b/src/shared/bus-unit-util.c
|
||
|
@@ -2065,7 +2065,8 @@ static int bus_append_kill_property(sd_bus_message *m, const char *field, const
|
||
|
if (STR_IN_SET(field, "KillSignal",
|
||
|
"RestartKillSignal",
|
||
|
"FinalKillSignal",
|
||
|
- "WatchdogSignal"))
|
||
|
+ "WatchdogSignal",
|
||
|
+ "ReloadSignal"))
|
||
|
return bus_append_signal_from_string(m, field, eq);
|
||
|
|
||
|
return 0;
|