]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
pid1: add new Type=notify-reload service type
authorLennart Poettering <lennart@poettering.net>
Mon, 2 Jan 2023 15:49:23 +0000 (16:49 +0100)
committerLennart Poettering <lennart@poettering.net>
Tue, 10 Jan 2023 17:28:38 +0000 (18:28 +0100)
Fixes: #6162
man/org.freedesktop.systemd1.xml
src/basic/unit-def.c
src/basic/unit-def.h
src/core/dbus-service.c
src/core/load-fragment-gperf.gperf.in
src/core/service.c
src/core/service.h
src/shared/bus-unit-util.c

index 5154638c337e4e4b34e3a984c493281edf546f2d..8eb30afd8599bca753b2c827e1efda2b35530fe4 100644 (file)
@@ -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 {
 
     <!--property OpenFile is not documented!-->
 
+    <!--property ReloadSignal is not documented!-->
+
     <!--property ExecCondition is not documented!-->
 
     <!--property ExecConditionEx is not documented!-->
@@ -3735,6 +3739,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <variablelist class="dbus-property" generated="True" extra-ref="OpenFile"/>
 
+    <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"/>
index 94cd603e32c020b3a8e425898d4d52bfcf43756a..bdb1860246fecba7c269a12912d2f893df6e6eff 100644 (file)
@@ -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",
index 5fcd51c095df67311e18beaa2cf9a1c5c6013736..bae132ea097c185eca57a6576c35b2a089d1fd58 100644 (file)
@@ -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,
index 0d437afe6b6fe55f7cd66d2ff8970364c15d0df7..079cb9b92d2bef92ea6776e6edf2c9ea9f890aa9 100644 (file)
@@ -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;
 }
 
index 81eb586cfee86f1e2f14cba259f006dcf309f546..3ea3ca3200a09d46345f5596ec119d830ab7e016 100644 (file)
@@ -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') }}
index 0efcb9a081718f5685afc8622e54bcb63d3fc1b6..a376b70f4271d19f00bb33e5dee2d5252fbcdcae 100644 (file)
@@ -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);
index 763398e31503d96a45ea9fe829b89ce1eae66f2d..fd242a7cbfd9126621a77ff85bf6bcdf29220d9b 100644 (file)
@@ -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) {
index 0fa2dea2d4e153165cd357e28ebb17c3ee05f51e..1e95e3667874cc73cbcf05dd5a655006c5138e68 100644 (file)
@@ -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;