[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,
[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,
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) {
/* 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
"%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),
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,
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,
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);
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)) {
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))
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,
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:
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);
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)) {
(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)) {
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. */
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);
}
static void service_enter_reload(Service *s) {
+ bool killed = false;
int r;
assert(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;
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);
}
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;
}
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) {
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;
}
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);
* 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 */
} 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);
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 */
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;
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:
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;
Service *s = SERVICE(u);
bool notify_dbus = false;
+ usec_t monotonic_usec = USEC_INFINITY;
const char *e;
int r;
/* 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)
}
}
- /* 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= */
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) {
SERVICE_START_POST,
SERVICE_RUNNING,
SERVICE_RELOAD,
+ SERVICE_RELOAD_SIGNAL,
+ SERVICE_RELOAD_NOTIFY,
SERVICE_STOP,
SERVICE_STOP_WATCHDOG,
SERVICE_STOP_SIGTERM,
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);