From 7fa49280bc33ba5135228401fb24dce0de5f9195 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 2 Jan 2023 15:00:05 +0100 Subject: [PATCH 01/10] manager: add one more assert() --- src/core/manager.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/manager.c b/src/core/manager.c index 739933b97c..0aed7de15c 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -809,6 +809,8 @@ static int manager_find_credentials_dirs(Manager *m) { } void manager_set_switching_root(Manager *m, bool switching_root) { + assert(m); + m->switching_root = MANAGER_IS_SYSTEM(m) && switching_root; } From 3bd28bf721dc70722ff1c675026ed0b44ad968a3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 2 Jan 2023 16:49:23 +0100 Subject: [PATCH 02/10] pid1: add new Type=notify-reload service type Fixes: #6162 --- 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 5154638c33..8eb30afd85 100644 --- a/man/org.freedesktop.systemd1.xml +++ b/man/org.freedesktop.systemd1.xml @@ -2578,6 +2578,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { readonly s OOMPolicy = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly a(sst) OpenFile = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i ReloadSignal = ...; readonly t ExecMainStartTimestamp = ...; readonly t ExecMainStartTimestampMonotonic = ...; readonly t ExecMainExitTimestamp = ...; @@ -3177,6 +3179,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + + @@ -3735,6 +3739,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + + 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 0d437afe6b..079cb9b92d 100644 --- a/src/core/dbus-service.c +++ b/src/core/dbus-service.c @@ -258,6 +258,7 @@ const sd_bus_vtable bus_service_vtable[] = { 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("OpenFile", "a(sst)", property_get_open_files, offsetof(Service, open_files), 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), @@ -404,6 +405,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, @@ -612,6 +614,9 @@ static int bus_service_set_transient_property( return 1; } + 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 81eb586cfe..3ea3ca3200 100644 --- a/src/core/load-fragment-gperf.gperf.in +++ b/src/core/load-fragment-gperf.gperf.in @@ -427,6 +427,7 @@ Service.USBFunctionDescriptors, config_parse_unit_path_printf, 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.OpenFile, config_parse_open_file, 0, offsetof(Service, open_files) +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 0efcb9a081..a376b70f42 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) { @@ -767,7 +773,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 @@ -832,7 +838,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), @@ -845,7 +852,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, @@ -1105,7 +1113,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, @@ -1114,7 +1122,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); @@ -1123,7 +1132,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)) { @@ -1139,7 +1148,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)) @@ -1148,7 +1158,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, @@ -1174,6 +1184,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: @@ -1220,7 +1232,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); @@ -1232,7 +1245,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)) { @@ -1247,7 +1260,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)) { @@ -2274,7 +2287,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. */ @@ -2418,7 +2431,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); @@ -2427,6 +2440,7 @@ static void service_enter_reload_by_notify(Service *s) { } static void service_enter_reload(Service *s) { + bool killed = false; int r; assert(s); @@ -2434,6 +2448,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; @@ -2443,17 +2469,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); } @@ -2616,9 +2653,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; } @@ -2651,7 +2687,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) { @@ -2827,6 +2864,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; } @@ -3165,6 +3205,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); @@ -3368,7 +3412,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 */ @@ -3572,17 +3616,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); @@ -3601,7 +3647,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 */ @@ -3694,15 +3740,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; @@ -3780,12 +3824,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: @@ -3888,6 +3942,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; @@ -4113,6 +4169,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; @@ -4131,7 +4188,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) @@ -4160,43 +4217,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= */ @@ -4326,7 +4413,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) { @@ -4533,6 +4622,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, @@ -4655,13 +4746,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 763398e315..fd242a7cbf 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -25,13 +25,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; @@ -218,6 +219,9 @@ struct Service { OOMPolicy oom_policy; LIST_HEAD(OpenFile, open_files); + + 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 0fa2dea2d4..1e95e36678 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -2094,7 +2094,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; From 81e19b6f6585d656e972efad73781e184ca0e7a0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 2 Jan 2023 18:13:27 +0100 Subject: [PATCH 03/10] man: document Type=notify-reload --- man/sd_notify.xml | 36 ++++-- man/systemd.service.xml | 248 ++++++++++++++++++++++------------------ 2 files changed, 162 insertions(+), 122 deletions(-) diff --git a/man/sd_notify.xml b/man/sd_notify.xml index 021cd0384f..0ba191c0ce 100644 --- a/man/sd_notify.xml +++ b/man/sd_notify.xml @@ -102,23 +102,35 @@ READY=1 - Tells the service manager that service startup is finished, or the service finished loading its - configuration. This is only used by systemd if the service definition file has Type=notify - set. Since there is little value in signaling non-readiness, the only value services should send is - READY=1 (i.e. READY=0 is not defined). + Tells the service manager that service startup is finished, or the service finished + re-loading its configuration. This is only used by systemd if the service definition file has + Type=notify or Type=notify-reload set. Since there is little + value in signaling non-readiness, the only value services should send is READY=1 + (i.e. READY=0 is not defined). RELOADING=1 - Tells the service manager that the service is - reloading its configuration. This is useful to allow the - service manager to track the service's internal state, and - present it to the user. Note that a service that sends this - notification must also send a READY=1 - notification when it completed reloading its - configuration. Reloads are propagated in the same way as they - are when initiated by the user. + Tells the service manager that the service is beginning to reload its + configuration. This is useful to allow the service manager to track the service's internal state, and + present it to the user. Note that a service that sends this notification must also send a + READY=1 notification when it completed reloading its configuration. Reloads the + service manager is notified about with this mechanisms are propagated in the same way as they are + when originally initiated through the service manager. This message is particularly relevant for + Type=notify-reload services, to inform the service manager that the request to + reload the service has been received and is now being processed. + + + + MONOTONIC_USEC=… + + A field carrying the monotonic timestamp (as per + CLOCK_MONOTONIC) formatted in decimal in µs, when the notification message was + generated by the client. This is typically used in combination with RELOADING=1, + to allow the service manager to properly synchronize reload cycles. See + systemd.service5 + for details, specifically Type=notify-reload. diff --git a/man/systemd.service.xml b/man/systemd.service.xml index e327f688f4..f123fa2e4c 100644 --- a/man/systemd.service.xml +++ b/man/systemd.service.xml @@ -157,7 +157,7 @@ Configures the process start-up type for this service unit. One of , , , , , - or : + , or : If set to (the default if ExecStart= is @@ -216,14 +216,30 @@ logic thus should be prepared to receive a SIGTERM (or whichever signal is configured in KillSignal=) as result. - Behavior of is similar to ; however, it is - expected that the service sends a notification message via - sd_notify3 or an - equivalent call when it has finished starting up. systemd will proceed with starting follow-up units after - this notification message has been sent. If this option is used, NotifyAccess= (see - below) should be set to open access to the notification socket provided by systemd. If - NotifyAccess= is missing or set to , it will be forcibly set to - . + Behavior of is similar to ; however, + it is expected that the service sends a READY=1 notification message via + sd_notify3 or + an equivalent call when it has finished starting up. systemd will proceed with starting follow-up + units after this notification message has been sent. If this option is used, + NotifyAccess= (see below) should be set to open access to the notification + socket provided by systemd. If NotifyAccess= is missing or set to + , it will be forcibly set to . + + Behavior of is identical to + . However, it extends the logic in one way: the + SIGHUP UNIX process signal is sent to the service's main process when the + service is asked to reload. (The signal to send can be tweaked via + ReloadSignal=, see below.). When + initiating the reload process the service is then expected to reply with a notification message + via sd_notify3 + that contains the RELOADING=1 field in combination with + MONOTONIC_USEC= set to the current monotonic time + (i.e. CLOCK_MONOTONIC in + clock_gettime2) + in µs, formatted as decimal string. Once reloading is complete another notification message must + be sent, containing READY=1. Using this service type and implementing this + reload protocol is an efficient alternative to providing an ExecReload= + command for reloading of the service's configuration. Behavior of is very similar to ; however, actual execution of the service program is delayed until all active jobs are dispatched. This may be used @@ -233,25 +249,27 @@ anyway. - It is generally recommended to use Type= for long-running - services whenever possible, as it is the simplest and fastest option. However, as this service type won't - propagate service start-up failures and doesn't allow ordering of other units against completion of - initialization of the service (which for example is useful if clients need to connect to the service through - some form of IPC, and the IPC channel is only established by the service itself — in contrast to doing this - ahead of time through socket or bus activation or similar), it might not be sufficient for many cases. If so, - or (the latter only in case the service provides a D-Bus - interface) are the preferred options as they allow service program code to precisely schedule when to - consider the service started up successfully and when to proceed with follow-up units. The - service type requires explicit support in the service codebase (as - sd_notify() or an equivalent API needs to be invoked by the service at the appropriate - time) — if it's not supported, then is an alternative: it supports the traditional - UNIX service start-up protocol. Finally, might be an option for cases where it is - enough to ensure the service binary is invoked, and where the service binary itself executes no or little - initialization on its own (and its initialization is unlikely to fail). Note that using any type other than - possibly delays the boot process, as the service manager needs to wait for service - initialization to complete. It is hence recommended not to needlessly use any types other than - . (Also note it is generally not recommended to use or - for long-running services.) + It is generally recommended to use Type= for + long-running services whenever possible, as it is the simplest and fastest option. However, as this + service type won't propagate service start-up failures and doesn't allow ordering of other units + against completion of initialization of the service (which for example is useful if clients need to + connect to the service through some form of IPC, and the IPC channel is only established by the + service itself — in contrast to doing this ahead of time through socket or bus activation or + similar), it might not be sufficient for many cases. If so, , + or (the latter only in case the service + provides a D-Bus interface) are the preferred options as they allow service program code to + precisely schedule when to consider the service started up successfully and when to proceed with + follow-up units. The / service types require + explicit support in the service codebase (as sd_notify() or an equivalent API + needs to be invoked by the service at the appropriate time) — if it's not supported, then + is an alternative: it supports the traditional UNIX service start-up + protocol. Finally, might be an option for cases where it is enough to ensure + the service binary is invoked, and where the service binary itself executes no or little + initialization on its own (and its initialization is unlikely to fail). Note that using any type + other than possibly delays the boot process, as the service manager needs + to wait for service initialization to complete. It is hence recommended not to needlessly use any + types other than . (Also note it is generally not recommended to use + or for long-running services.) @@ -319,9 +337,10 @@ the file may not be a symlink to a file owned by a different user (neither directly nor indirectly), and the PID file must refer to a process already belonging to the service. - Note that PID files should be avoided in modern projects. Use or - where possible, which does not require use of PID files to determine the - main process of a service and avoids needless forking. + Note that PID files should be avoided in modern projects. Use , + or where possible, which does not + require use of PID files to determine the main process of a service and avoids needless + forking. @@ -443,12 +462,13 @@ with a - exit successfully. ExecStartPost= commands are only run after the commands specified in - ExecStart= have been invoked successfully, as determined by Type= - (i.e. the process has been started for Type=simple or Type=idle, the last - ExecStart= process exited successfully for Type=oneshot, the initial - process exited successfully for Type=forking, READY=1 is sent for - Type=notify, or the BusName= has been taken for - Type=dbus). + ExecStart= have been invoked successfully, as determined by + Type= (i.e. the process has been started for Type=simple or + Type=idle, the last ExecStart= process exited successfully for + Type=oneshot, the initial process exited successfully for + Type=forking, READY=1 is sent for + Type=notify/Type=notify-reload, or the + BusName= has been taken for Type=dbus). Note that ExecStartPre= may not be used to start long-running processes. All processes forked @@ -487,30 +507,26 @@ 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 - ExecStart= above. Use of this setting is - optional. Specifier and environment variable substitution is - supported here following the same scheme as for + + Commands to execute to trigger a configuration reload in the service. This argument + takes 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=. - One additional, special environment variable is set: if - known, $MAINPID is set to the main process - of the daemon, and may be used for command lines like the - following: + One additional, special environment variable is set: if known, $MAINPID is + set to the main process of the daemon, and may be used for command lines like the following: ExecReload=kill -HUP $MAINPID - Note however that reloading a daemon by sending a signal - (as with the example line above) is usually not a good choice, - because this is an asynchronous operation and hence not - suitable to order reloads of multiple services against each - other. It is strongly recommended 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 + Note however that reloading a daemon by enqueing 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, dbus-broker1 uses the following: ExecReload=busctl call org.freedesktop.DBus \ @@ -605,12 +621,13 @@ systemd-system.conf5). - If a service of Type=notify sends EXTEND_TIMEOUT_USEC=…, this may cause - the start time to be extended beyond TimeoutStartSec=. The first receipt of this message - must occur before TimeoutStartSec= is exceeded, and once the start time has extended beyond - TimeoutStartSec=, the service manager will allow the service to continue to start, provided - the service repeats EXTEND_TIMEOUT_USEC=… within the interval specified until the service - startup status is finished by READY=1. (see + If a service of Type=notify/Type=notify-reload sends + EXTEND_TIMEOUT_USEC=…, this may cause the start time to be extended beyond + TimeoutStartSec=. The first receipt of this message must occur before + TimeoutStartSec= is exceeded, and once the start time has extended beyond + TimeoutStartSec=, the service manager will allow the service to continue to start, + provided the service repeats EXTEND_TIMEOUT_USEC=… within the interval specified + until the service startup status is finished by READY=1. (see sd_notify3). @@ -633,12 +650,14 @@ systemd-system.conf5). - If a service of Type=notify sends EXTEND_TIMEOUT_USEC=…, this may cause - the stop time to be extended beyond TimeoutStopSec=. The first receipt of this message - must occur before TimeoutStopSec= is exceeded, and once the stop time has extended beyond - TimeoutStopSec=, the service manager will allow the service to continue to stop, provided - the service repeats EXTEND_TIMEOUT_USEC=… within the interval specified, or terminates itself - (see sd_notify3). + If a service of Type=notify/Type=notify-reload sends + EXTEND_TIMEOUT_USEC=…, this may cause the stop time to be extended beyond + TimeoutStopSec=. The first receipt of this message must occur before + TimeoutStopSec= is exceeded, and once the stop time has extended beyond + TimeoutStopSec=, the service manager will allow the service to continue to stop, + provided the service repeats EXTEND_TIMEOUT_USEC=… within the interval specified, + or terminates itself (see + sd_notify3). @@ -661,13 +680,15 @@ systemd-system.conf5). - If a service of Type=notify handles SIGABRT itself (instead of relying - on the kernel to write a core dump) it can send EXTEND_TIMEOUT_USEC=… to - extended the abort time beyond TimeoutAbortSec=. The first receipt of this message - must occur before TimeoutAbortSec= is exceeded, and once the abort time has extended beyond - TimeoutAbortSec=, the service manager will allow the service to continue to abort, provided - the service repeats EXTEND_TIMEOUT_USEC=… within the interval specified, or terminates itself - (see sd_notify3). + If a service of Type=notify/Type=notify-reload handles + SIGABRT itself (instead of relying on the kernel to write a core dump) it can + send EXTEND_TIMEOUT_USEC=… to extended the abort time beyond + TimeoutAbortSec=. The first receipt of this message must occur before + TimeoutAbortSec= is exceeded, and once the abort time has extended beyond + TimeoutAbortSec=, the service manager will allow the service to continue to abort, + provided the service repeats EXTEND_TIMEOUT_USEC=… within the interval specified, + or terminates itself (see + sd_notify3). @@ -710,12 +731,13 @@ activation completed. Pass infinity (the default) to configure no runtime limit. - If a service of Type=notify sends EXTEND_TIMEOUT_USEC=…, this may cause - the runtime to be extended beyond RuntimeMaxSec=. The first receipt of this message - must occur before RuntimeMaxSec= is exceeded, and once the runtime has extended beyond - RuntimeMaxSec=, the service manager will allow the service to continue to run, provided - the service repeats EXTEND_TIMEOUT_USEC=… within the interval specified until the service - shutdown is achieved by STOPPING=1 (or termination). (see + If a service of Type=notify/Type=notify-reload sends + EXTEND_TIMEOUT_USEC=…, this may cause the runtime to be extended beyond + RuntimeMaxSec=. The first receipt of this message must occur before + RuntimeMaxSec= is exceeded, and once the runtime has extended beyond + RuntimeMaxSec=, the service manager will allow the service to continue to run, + provided the service repeats EXTEND_TIMEOUT_USEC=… within the interval specified + until the service shutdown is achieved by STOPPING=1 (or termination). (see sd_notify3). @@ -1023,16 +1045,19 @@ NotifyAccess= Controls access to the service status notification socket, as accessible via the - sd_notify3 call. Takes one - of (the default), , or - . If , no daemon status updates are accepted from the service - processes, all status update messages are ignored. If , only service updates sent from the - main process of the service are accepted. If , only service updates sent from any of the - main or control processes originating from one of the Exec*= commands are accepted. If - , all services updates from all members of the service's control group are accepted. This - option should be set to open access to the notification socket when using Type=notify or - WatchdogSec= (see above). If those options are used but NotifyAccess= is - not configured, it will be implicitly set to . + sd_notify3 + call. Takes one of (the default), , + or . If , no daemon status updates are accepted from the + service processes, all status update messages are ignored. If , only service + updates sent from the main process of the service are accepted. If , only + service updates sent from any of the main or control processes originating from one of the + Exec*= commands are accepted. If , all services updates from + all members of the service's control group are accepted. This option should be set to open access to + the notification socket when using + Type=notify/Type=notify-reload or + WatchdogSec= (see above). If those options are used but + NotifyAccess= is not configured, it will be implicitly set to + . Note that sd_notify() notifications may be attributed to units correctly only if either the sending process is still around at the time PID 1 processes the message, or if the sending process @@ -1156,6 +1181,7 @@ kills, this setting determines the state of the unit after systemd-oomd kills a cgroup associated with it. + OpenFile= Takes an argument of the form path:fd-name:options, @@ -1187,6 +1213,14 @@ If the empty string is assigned, the entire list of open files defined prior to this is reset. + + ReloadSignal= + Configures the UNIX process signal to send to the service's main process when asked + to reload the service's configuration. Defaults to SIGHUP. This option has no + effect unless Type= is used, see + above. + + Check @@ -1350,16 +1384,13 @@ WantedBy=multi-user.target systemd.kill5 for details. - Note that this unit type does not include any type of - notification when a service has completed initialization. For - this, you should use other unit types, such as - Type= if the service - understands systemd's notification protocol, - Type= if the service - can background itself or - Type= if the unit - acquires a DBus name once initialization is complete. See - below. + Note that this unit type does not include any type of notification when a service has completed + initialization. For this, you should use other unit types, such as + Type=/Type= + if the service understands systemd's notification protocol, + Type= if the service can background itself or + Type= if the unit acquires a DBus name once initialization is + complete. See below. @@ -1536,15 +1567,12 @@ SystemdService=simple-dbus-service.service Services that notify systemd about their initialization - Type= services - are really easy to write, but have the major disadvantage of - systemd not being able to tell when initialization of the given - service is complete. For this reason, systemd supports a simple - notification protocol that allows daemons to make systemd aware - that they are done initializing. Use - Type= for this. A - typical service file for such a daemon would look like - this: + Type= services are really easy to write, but have the + major disadvantage of systemd not being able to tell when initialization of the given service is + complete. For this reason, systemd supports a simple notification protocol that allows daemons to make + systemd aware that they are done initializing. Use Type= or + Type= for this. A typical service file for such a + daemon would look like this: [Unit] Description=Simple notifying service From dd0ab174c36492cdcb92cf46844fb0905b1d4a7e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 2 Jan 2023 15:01:56 +0100 Subject: [PATCH 04/10] pid1: make sure we send our calling service manager RELOADING=1 when reloading And send READY=1 again when we are done with it. We do this not only for "daemon-reload" but also for "daemon-reexec" and "switch-root", since from the perspective of an encapsulating service manager these three operations are not that different. --- src/core/main.c | 10 ++++++++++ src/core/manager.c | 12 ++++++++++++ src/core/manager.h | 1 + units/user@.service.in | 2 +- 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/core/main.c b/src/core/main.c index c833050db0..94055f08de 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1961,6 +1961,8 @@ static int invoke_main_loop( LogTarget saved_log_target; int saved_log_level; + manager_send_reloading(m); + log_info("Reloading."); /* First, save any overridden log level/target, then parse the configuration file, @@ -1991,6 +1993,10 @@ static int invoke_main_loop( } case MANAGER_REEXECUTE: + + manager_send_reloading(m); /* From the perspective of the manager calling us this is + * pretty much the same as a reload */ + r = prepare_reexecute(m, &arg_serialization, ret_fds, false); if (r < 0) { *ret_error_message = "Failed to prepare for reexecution"; @@ -2005,6 +2011,10 @@ static int invoke_main_loop( return objective; case MANAGER_SWITCH_ROOT: + + manager_send_reloading(m); /* From the perspective of the manager calling us this is + * pretty much the same as a reload */ + manager_set_switching_root(m, true); if (!m->switch_root_init) { diff --git a/src/core/manager.c b/src/core/manager.c index 0aed7de15c..f8576cca1a 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -3648,6 +3648,18 @@ void manager_check_finished(Manager *m) { manager_invalidate_startup_units(m); } +void manager_send_reloading(Manager *m) { + assert(m); + + /* Let whoever invoked us know that we are now reloading */ + (void) sd_notifyf(/* unset= */ false, + "RELOADING=1\n" + "MONOTONIC_USEC=" USEC_FMT "\n", now(CLOCK_MONOTONIC)); + + /* And ensure that we'll send READY=1 again as soon as we are ready again */ + m->ready_sent = false; +} + static bool generator_path_any(const char* const* paths) { bool found = false; diff --git a/src/core/manager.h b/src/core/manager.h index 4d4b56c3cc..c13de8fe78 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -538,6 +538,7 @@ void manager_send_unit_plymouth(Manager *m, Unit *u); bool manager_unit_inactive_or_pending(Manager *m, const char *name); void manager_check_finished(Manager *m); +void manager_send_reloading(Manager *m); void disable_printk_ratelimit(void); void manager_recheck_dbus(Manager *m); diff --git a/units/user@.service.in b/units/user@.service.in index 1660de0326..1558764909 100644 --- a/units/user@.service.in +++ b/units/user@.service.in @@ -17,7 +17,7 @@ IgnoreOnIsolate=yes [Service] User=%i PAMName=systemd-user -Type=notify +Type=notify-reload ExecStart={{ROOTLIBEXECDIR}}/systemd --user Slice=user-%i.slice KillMode=mixed From 0e07cdb0e77d0322bc866b5e13abbe38e988059d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 2 Jan 2023 17:25:46 +0100 Subject: [PATCH 05/10] networkd: implement Type=notify-reload protocol --- src/network/networkd-manager-bus.c | 13 +------- src/network/networkd-manager.c | 48 ++++++++++++++++++++++++++---- src/network/networkd-manager.h | 2 ++ src/network/networkd.c | 2 -- units/systemd-networkd.service.in | 3 +- 5 files changed, 47 insertions(+), 21 deletions(-) diff --git a/src/network/networkd-manager-bus.c b/src/network/networkd-manager-bus.c index 2ab3aaadc2..67f951df69 100644 --- a/src/network/networkd-manager-bus.c +++ b/src/network/networkd-manager-bus.c @@ -197,7 +197,6 @@ static int bus_method_reconfigure_link(sd_bus_message *message, void *userdata, static int bus_method_reload(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *manager = userdata; - Link *link; int r; r = bus_verify_polkit_async(message, CAP_NET_ADMIN, @@ -209,20 +208,10 @@ static int bus_method_reload(sd_bus_message *message, void *userdata, sd_bus_err if (r == 0) return 1; /* Polkit will call us back */ - r = netdev_load(manager, true); + r = manager_reload(manager); if (r < 0) return r; - r = network_reload(manager); - if (r < 0) - return r; - - HASHMAP_FOREACH(link, manager->links_by_index) { - r = link_reconfigure(link, /* force = */ false); - if (r < 0) - return r; - } - return sd_bus_reply_method_return(message, NULL); } diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 7d067a8c46..f82117cc1e 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -474,6 +474,14 @@ static int signal_restart_callback(sd_event_source *s, const struct signalfd_sig return sd_event_exit(sd_event_source_get_event(s), 0); } +static int signal_reload_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + Manager *m = ASSERT_PTR(userdata); + + manager_reload(m); + + return 0; +} + static int manager_set_keep_configuration(Manager *m) { int r; @@ -508,12 +516,11 @@ int manager_setup(Manager *m) { if (r < 0) return r; - assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, SIGUSR2, -1) >= 0); - (void) sd_event_set_watchdog(m->event, true); - (void) sd_event_add_signal(m->event, NULL, SIGTERM, signal_terminate_callback, m); - (void) sd_event_add_signal(m->event, NULL, SIGINT, signal_terminate_callback, m); - (void) sd_event_add_signal(m->event, NULL, SIGUSR2, signal_restart_callback, m); + (void) sd_event_add_signal(m->event, NULL, SIGTERM | SD_EVENT_SIGNAL_PROCMASK, signal_terminate_callback, m); + (void) sd_event_add_signal(m->event, NULL, SIGINT | SD_EVENT_SIGNAL_PROCMASK, signal_terminate_callback, m); + (void) sd_event_add_signal(m->event, NULL, SIGUSR2 | SD_EVENT_SIGNAL_PROCMASK, signal_restart_callback, m); + (void) sd_event_add_signal(m->event, NULL, SIGHUP | SD_EVENT_SIGNAL_PROCMASK, signal_reload_callback, m); r = sd_event_add_post(m->event, NULL, manager_dirty_handler, m); if (r < 0) @@ -1069,3 +1076,34 @@ int manager_set_timezone(Manager *m, const char *tz) { return 0; } + +int manager_reload(Manager *m) { + Link *link; + int r; + + assert(m); + + (void) sd_notifyf(/* unset= */ false, + "RELOADING=1\n" + "STATUS=Reloading configuration...\n" + "MONOTONIC_USEC=" USEC_FMT, now(CLOCK_MONOTONIC)); + + r = netdev_load(m, /* reload= */ true); + if (r < 0) + goto finish; + + r = network_reload(m); + if (r < 0) + goto finish; + + HASHMAP_FOREACH(link, m->links_by_index) { + r = link_reconfigure(link, /* force = */ false); + if (r < 0) + goto finish; + } + + r = 0; +finish: + (void) sd_notify(/* unset= */ false, NOTIFY_READY); + return r; +} diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index 40e6092f85..e6183af0e4 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -115,4 +115,6 @@ int manager_enumerate(Manager *m); int manager_set_hostname(Manager *m, const char *hostname); int manager_set_timezone(Manager *m, const char *timezone); +int manager_reload(Manager *m); + DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); diff --git a/src/network/networkd.c b/src/network/networkd.c index d61769d9f3..68760e8ff4 100644 --- a/src/network/networkd.c +++ b/src/network/networkd.c @@ -81,8 +81,6 @@ static int run(int argc, char *argv[]) { if (r < 0) log_warning_errno(r, "Could not create runtime directory 'lldp': %m"); - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); - r = manager_new(&m, /* test_mode = */ false); if (r < 0) return log_error_errno(r, "Could not create manager: %m"); diff --git a/units/systemd-networkd.service.in b/units/systemd-networkd.service.in index d15129e7f0..d8b935a358 100644 --- a/units/systemd-networkd.service.in +++ b/units/systemd-networkd.service.in @@ -24,7 +24,6 @@ BusName=org.freedesktop.network1 CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_BROADCAST CAP_NET_RAW DeviceAllow=char-* rw ExecStart=!!{{ROOTLIBEXECDIR}}/systemd-networkd -ExecReload=networkctl reload FileDescriptorStoreMax=512 LockPersonality=yes MemoryDenyWriteExecute=yes @@ -48,7 +47,7 @@ RuntimeDirectoryPreserve=yes SystemCallArchitectures=native SystemCallErrorNumber=EPERM SystemCallFilter=@system-service -Type=notify +Type=notify-reload User=systemd-network {{SERVICE_WATCHDOG}} From f84331539deae28fbeb42d45ad0c8d583b3372a3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 2 Jan 2023 17:21:16 +0100 Subject: [PATCH 06/10] udevd: implement the full Type=notify-reload protocol We are basically already there, just need to add MONOTONIC_USEC= to the RELOADING=1 message, and make sure the message is generated in really all cases. --- src/udev/udevd.c | 47 +++++++++++++++++++--------------- units/systemd-udevd.service.in | 3 +-- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/src/udev/udevd.c b/src/udev/udevd.c index 28b76bfbab..6fb0305e09 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -32,6 +32,7 @@ #include "cgroup-setup.h" #include "cgroup-util.h" #include "cpu-set-util.h" +#include "daemon-util.h" #include "dev-setup.h" #include "device-monitor-private.h" #include "device-private.h" @@ -330,9 +331,7 @@ static void manager_exit(Manager *manager) { manager->exit = true; - sd_notify(false, - "STOPPING=1\n" - "STATUS=Starting shutdown..."); + (void) sd_notify(/* unset= */ false, NOTIFY_STOPPING); /* close sources of new events and discard buffered events */ manager->ctrl = udev_ctrl_unref(manager->ctrl); @@ -350,7 +349,7 @@ static void manager_exit(Manager *manager) { static void notify_ready(void) { int r; - r = sd_notifyf(false, + r = sd_notifyf(/* unset= */ false, "READY=1\n" "STATUS=Processing with %u children at max", arg_children_max); if (r < 0) @@ -375,23 +374,33 @@ static void manager_reload(Manager *manager, bool force) { mac_selinux_maybe_reload(); /* Nothing changed. It is not necessary to reload. */ - if (!udev_rules_should_reload(manager->rules) && !udev_builtin_should_reload()) - return; + if (!udev_rules_should_reload(manager->rules) && !udev_builtin_should_reload()) { - sd_notify(false, - "RELOADING=1\n" - "STATUS=Flushing configuration..."); + if (!force) + return; - manager_kill_workers(manager, false); + /* If we eat this up, then tell our service manager to just continue */ + (void) sd_notifyf(/* unset= */ false, + "RELOADING=1\n" + "STATUS=Skipping configuration reloading, nothing changed.\n" + "MONOTONIC_USEC=" USEC_FMT, now(CLOCK_MONOTONIC)); + } else { + (void) sd_notifyf(/* unset= */ false, + "RELOADING=1\n" + "STATUS=Flushing configuration...\n" + "MONOTONIC_USEC=" USEC_FMT, now(CLOCK_MONOTONIC)); - udev_builtin_exit(); - udev_builtin_init(); + manager_kill_workers(manager, false); - r = udev_rules_load(&rules, arg_resolve_name_timing); - if (r < 0) - log_warning_errno(r, "Failed to read udev rules, using the previously loaded rules, ignoring: %m"); - else - udev_rules_free_and_replace(manager->rules, rules); + udev_builtin_exit(); + udev_builtin_init(); + + r = udev_rules_load(&rules, arg_resolve_name_timing); + if (r < 0) + log_warning_errno(r, "Failed to read udev rules, using the previously loaded rules, ignoring: %m"); + else + udev_rules_free_and_replace(manager->rules, rules); + } notify_ready(); } @@ -1985,9 +1994,7 @@ static int main_loop(Manager *manager) { if (r < 0) log_error_errno(r, "Event loop failed: %m"); - sd_notify(false, - "STOPPING=1\n" - "STATUS=Shutting down..."); + (void) sd_notify(/* unset= */ false, NOTIFY_STOPPING); return r; } diff --git a/units/systemd-udevd.service.in b/units/systemd-udevd.service.in index e9dbe85ef4..dfc2a0e341 100644 --- a/units/systemd-udevd.service.in +++ b/units/systemd-udevd.service.in @@ -18,14 +18,13 @@ ConditionPathIsReadWrite=/sys [Service] CapabilityBoundingSet=~CAP_SYS_TIME CAP_WAKE_ALARM Delegate=pids -Type=notify +Type=notify-reload # Note that udev will reset the value internally for its workers OOMScoreAdjust=-1000 Sockets=systemd-udevd-control.socket systemd-udevd-kernel.socket Restart=always RestartSec=0 ExecStart={{ROOTLIBEXECDIR}}/systemd-udevd -ExecReload=udevadm control --reload --timeout 0 KillMode=mixed TasksMax=infinity PrivateMounts=yes From 5d71e463f49518c7702467f6145484afa31bf8ba Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 2 Jan 2023 17:35:23 +0100 Subject: [PATCH 07/10] logind: implement Type=notify-reload protocol properly So close already. Let's add the two missing notifications too. Fixes: #18484 --- src/login/logind.c | 6 ++++++ units/systemd-logind.service.in | 1 + 2 files changed, 7 insertions(+) diff --git a/src/login/logind.c b/src/login/logind.c index def2f5a442..1feacd3601 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -1020,6 +1020,11 @@ static int manager_dispatch_reload_signal(sd_event_source *s, const struct signa Manager *m = userdata; int r; + (void) sd_notifyf(/* unset= */ false, + "RELOADING=1\n" + "STATUS=Reloading configuration...\n" + "MONOTONIC_USEC=" USEC_FMT, now(CLOCK_MONOTONIC)); + manager_reset_config(m); r = manager_parse_config_file(m); if (r < 0) @@ -1027,6 +1032,7 @@ static int manager_dispatch_reload_signal(sd_event_source *s, const struct signa else log_info("Config file reloaded."); + (void) sd_notify(/* unset= */ false, NOTIFY_READY); return 0; } diff --git a/units/systemd-logind.service.in b/units/systemd-logind.service.in index 042ea75d7a..24f5ddaa17 100644 --- a/units/systemd-logind.service.in +++ b/units/systemd-logind.service.in @@ -58,6 +58,7 @@ StateDirectory=systemd/linger SystemCallArchitectures=native SystemCallErrorNumber=EPERM SystemCallFilter=@system-service +Type=notify-reload {{SERVICE_WATCHDOG}} # Increase the default a bit in order to allow many simultaneous logins since From fd0f4da5457fbf7136f2d1888142d5fea75fd45a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 3 Jan 2023 12:55:50 +0100 Subject: [PATCH 08/10] notify: add --stopping + --reloading switches These wrap RELOADING=1 and STOPPING=1 messages. The former is particularly useful, since we want to insert the MONOTONIC_USEC= field into the message automatically, which is easy from C but harder from shell. --- man/systemd-notify.xml | 87 +++++++++++++++++++++++++----------------- src/notify/notify.c | 39 +++++++++++++++++-- 2 files changed, 86 insertions(+), 40 deletions(-) diff --git a/man/systemd-notify.xml b/man/systemd-notify.xml index 9479fbd759..dd76cc0fb6 100644 --- a/man/systemd-notify.xml +++ b/man/systemd-notify.xml @@ -30,20 +30,18 @@ Description - systemd-notify may be called by daemon - scripts to notify the init system about status changes. It can be - used to send arbitrary information, encoded in an - environment-block-like list of strings. Most importantly, it can be - used for start-up completion notification. + systemd-notify may be called by service scripts to notify the invoking service + manager about status changes. It can be used to send arbitrary information, encoded in an + environment-block-like list of strings. Most importantly, it can be used for start-up completion + notification. - This is mostly just a wrapper around - sd_notify() and makes this functionality + This is mostly just a wrapper around sd_notify() and makes this functionality available to shell scripts. For details see sd_notify3. - The command line may carry a list of environment variables - to send as part of the status update. + The command line may carry a list of environment variables to send as part of the status + update. Note that systemd will refuse reception of status updates from this command unless NotifyAccess= is appropriately set for the service unit this command is called @@ -52,9 +50,9 @@ details. Note that sd_notify() notifications may be attributed to units correctly only - if either the sending process is still around at the time PID 1 processes the message, or if the sending - process is explicitly runtime-tracked by the service manager. The latter is the case if the service - manager originally forked off the process, i.e. on all processes that match + if either the sending process is still around at the time the service manager processes the message, or + if the sending process is explicitly runtime-tracked by the service manager. The latter is the case if + the service manager originally forked off the process, i.e. on all processes that match NotifyAccess= or NotifyAccess=. Conversely, if an auxiliary process of the unit sends an sd_notify() message and immediately exits, the service manager might not be @@ -84,22 +82,42 @@ - Inform the init system about service start-up - completion. This is equivalent to systemd-notify - READY=1. For details about the semantics of this - option see + Inform the invoking service manager about service start-up or configuration reload + completion. This is equivalent to systemd-notify READY=1. For details about the + semantics of this option see + sd_notify3. + + + + + + Inform the invoking service manager about the beginning of a configuration reload + cycle. This is equivalent to systemd-notify RELOADING=1 (but implicitly also sets + a MONOTONIC_USEC= field as required for Type=notify-reload + services, see + systemd.service5, + for details). For details about the semantics of this option see + sd_notify3. + + + + + + Inform the invoking service manager about the beginning of the shutdown phase of the + service. This is equivalent to systemd-notify STOPPING=1. For details about the + semantics of this option see sd_notify3. - Inform the service manager about the main PID of the daemon. Takes a PID as + Inform the service manager about the main PID of the service. Takes a PID as argument. If the argument is specified as auto or omitted, the PID of the process that invoked systemd-notify is used, except if that's the service manager. If the argument is specified as self, the PID of the systemd-notify command itself is used, and if parent is specified the calling process' PID is - used — even if it is the service manager. This is equivalent to systemd-notify + used — even if it is the service manager. The latter is equivalent to systemd-notify MAINPID=$PID. For details about the semantics of this option see sd_notify3. @@ -124,27 +142,26 @@ - Send a free-form status string for the daemon - to the init systemd. This option takes the status string as - argument. This is equivalent to systemd-notify - STATUS=…. For details about the semantics of this - option see - sd_notify3. + Send a free-form human readable status string for the daemon to the service + manager. This option takes the status string as argument. This is equivalent to + systemd-notify STATUS=…. For details about the semantics of this option see + sd_notify3. This + information is shown in + systemctl1's + status output, among other places. - Returns 0 if the system was booted up with - systemd, non-zero otherwise. If this option is passed, no - message is sent. This option is hence unrelated to the other - options. For details about the semantics of this option, see + Returns 0 if the system was booted up with systemd, non-zero otherwise. If this + option is passed, no message is sent. This option is hence unrelated to the other options. For + details about the semantics of this option, see sd_booted3. An alternate way to check for this state is to call - systemctl1 - with the is-system-running command. It will - return offline if the system was not booted - with systemd. + systemctl1 with + the is-system-running command. It will return offline if the + system was not booted with systemd. @@ -176,9 +193,8 @@ Start-up Notification and Status Updates - A simple shell daemon that sends start-up notifications - after having set up its communication channel. During runtime it - sends further status updates to the init system: + A simple shell daemon that sends start-up notifications after having set up its communication + channel. During runtime it sends further status updates to the init system: #!/bin/sh @@ -207,5 +223,4 @@ done sd_booted3 - diff --git a/src/notify/notify.c b/src/notify/notify.c index c4f54920ac..b1348383f5 100644 --- a/src/notify/notify.c +++ b/src/notify/notify.c @@ -23,6 +23,8 @@ #include "user-util.h" static bool arg_ready = false; +static bool arg_reloading = false; +static bool arg_stopping = false; static pid_t arg_pid = 0; static const char *arg_status = NULL; static bool arg_booted = false; @@ -42,7 +44,10 @@ static int help(void) { "\n%sNotify the init system about service status updates.%s\n\n" " -h --help Show this help\n" " --version Show package version\n" - " --ready Inform the init system about service start-up completion\n" + " --ready Inform the service manager about service start-up/reload\n" + " completion\n" + " --reloading Inform the service manager about configuration reloading\n" + " --stopping Inform the service manager about service shutdown\n" " --pid[=PID] Set main PID of daemon\n" " --uid=USER Set user to send from\n" " --status=TEXT Set status text\n" @@ -81,6 +86,8 @@ static int parse_argv(int argc, char *argv[]) { enum { ARG_READY = 0x100, + ARG_RELOADING, + ARG_STOPPING, ARG_VERSION, ARG_PID, ARG_STATUS, @@ -93,6 +100,8 @@ static int parse_argv(int argc, char *argv[]) { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, ARG_VERSION }, { "ready", no_argument, NULL, ARG_READY }, + { "reloading", no_argument, NULL, ARG_RELOADING }, + { "stopping", no_argument, NULL, ARG_STOPPING }, { "pid", optional_argument, NULL, ARG_PID }, { "status", required_argument, NULL, ARG_STATUS }, { "booted", no_argument, NULL, ARG_BOOTED }, @@ -120,6 +129,14 @@ static int parse_argv(int argc, char *argv[]) { arg_ready = true; break; + case ARG_RELOADING: + arg_reloading = true; + break; + + case ARG_STOPPING: + arg_stopping = true; + break; + case ARG_PID: if (isempty(optarg) || streq(optarg, "auto")) { arg_pid = getppid(); @@ -176,6 +193,8 @@ static int parse_argv(int argc, char *argv[]) { if (optind >= argc && !arg_ready && + !arg_stopping && + !arg_reloading && !arg_status && !arg_pid && !arg_booted) { @@ -187,10 +206,10 @@ static int parse_argv(int argc, char *argv[]) { } static int run(int argc, char* argv[]) { - _cleanup_free_ char *status = NULL, *cpid = NULL, *n = NULL; + _cleanup_free_ char *status = NULL, *cpid = NULL, *n = NULL, *monotonic_usec = NULL; _cleanup_strv_free_ char **final_env = NULL; - char* our_env[4]; - unsigned i = 0; + char* our_env[7]; + size_t i = 0; pid_t source_pid; int r; @@ -212,9 +231,21 @@ static int run(int argc, char* argv[]) { return r <= 0; } + if (arg_reloading) { + our_env[i++] = (char*) "RELOADING=1"; + + if (asprintf(&monotonic_usec, "MONOTONIC_USEC=" USEC_FMT, now(CLOCK_MONOTONIC)) < 0) + return log_oom(); + + our_env[i++] = monotonic_usec; + } + if (arg_ready) our_env[i++] = (char*) "READY=1"; + if (arg_stopping) + our_env[i++] = (char*) "STOPPING=1"; + if (arg_status) { status = strjoin("STATUS=", arg_status); if (!status) From ee52bbc68f129cfed833990906c0a0a77ee12c42 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 3 Jan 2023 12:56:53 +0100 Subject: [PATCH 09/10] test: add Type=notify-reload testcase --- test/units/testsuite-59.sh | 53 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/test/units/testsuite-59.sh b/test/units/testsuite-59.sh index ab6b2216fe..db8dc27ab9 100755 --- a/test/units/testsuite-59.sh +++ b/test/units/testsuite-59.sh @@ -104,6 +104,59 @@ sleep 10 systemctl daemon-reload +# Let's now test the notify-reload logic + +cat >/run/notify-reload-test.sh </testok exit 0 From 6fee784964b2763bd4307181335a433078ba977c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 2 Jan 2023 16:48:51 +0100 Subject: [PATCH 10/10] update TODO --- TODO | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/TODO b/TODO index 0208a5445e..899b6a1de1 100644 --- a/TODO +++ b/TODO @@ -763,17 +763,9 @@ Features: and synthesize initrd from it, and measure it. Signing is not necessary, as microcode does that on its own. Pass as first initrd to kernel. -* Add a new service type very similar to Type=notify, that goes one step - further and extends the protocol to cover reloads. Specifically, SIGHUP will - become the official way to reload, and daemon has to respond with sd_notify() - to report when it starts reloading, and when it is complete reloading. Care - must be taken to remove races from this model. I.e. PID 1 needs to take - CLOCK_MONOTONIC, then send SIGHUP, then wait for at least one RELOADING=1 - message that comes with a newer timestamp, then wait for a READY=1 message. - while we are at it, also maybe extend the logic to require handling of some - specific SIGRT signal for setting debug log level, that carries the level via - the sigqueue() data parameter. With that we extended with minimal logic the - service runtime logic quite substantially. +* Maybe extend the service protocol to support handling of some specific SIGRT + signal for setting service log level, that carries the level via the + sigqueue() data parameter. Enable this via unit file setting. * firstboot: maybe just default to C.UTF-8 locale if nothing is set, so that we don't query this unnecessarily in entirely uninitialized @@ -1774,7 +1766,6 @@ Features: * unit files: - allow port=0 in .socket units - maybe introduce ExecRestartPre= - - add ReloadSignal= for configuring a reload signal to use - implement Register= switch in .socket units to enable registration in Avahi, RPC and other socket registration services. - allow Type=simple with PIDFile=