<varlistentry>
<term><varname>ExecReload=</varname></term>
- <listitem><para>Commands to execute to trigger a configuration reload in the service. This argument
- takes multiple command lines, following the same scheme as described for
+ <listitem><para>Commands to execute to trigger a configuration reload in the service. This setting
+ may take multiple command lines, following the same scheme as described for
<varname>ExecStart=</varname> above. Use of this setting is optional. Specifier and environment
variable substitution is supported here following the same scheme as for
<varname>ExecStart=</varname>.</para>
<programlisting>ExecReload=kill -HUP $MAINPID</programlisting>
- <para>Note however that reloading a daemon by enqueuing a signal (as with the example line above) is
- usually not a good choice, because this is an asynchronous operation and hence not suitable when
- ordering reloads of multiple services against each other. It is thus strongly recommended to either
- use <varname>Type=</varname><option>notify-reload</option> in place of
- <varname>ExecReload=</varname>, or to set <varname>ExecReload=</varname> to a command that not only
- triggers a configuration reload of the daemon, but also synchronously waits for it to complete. For
- example, <citerefentry
+ <para>Note however that reloading a daemon by enqueuing a signal without completion notification
+ (as is the case with the example line above) is usually not a good choice, because this is an
+ asynchronous operation and hence not suitable when ordering reloads of multiple services against
+ each other. It is thus strongly recommended to either use <varname>Type=notify-reload</varname>,
+ or to set <varname>ExecReload=</varname> to a command that not only triggers a configuration reload
+ of the daemon, but also synchronously waits for it to complete. For example, <citerefentry
project='mankier'><refentrytitle>dbus-broker</refentrytitle><manvolnum>1</manvolnum></citerefentry>
uses the following:</para>
/org/freedesktop/DBus org.freedesktop.DBus \
ReloadConfig
</programlisting>
+
+ <para>This setting can be combined with <varname>Type=notify-reload</varname>, in which case
+ the service main process is signaled after all specified command lines finish execution. Specially,
+ if <literal>RELOADING=1</literal> notification is received before <varname>ExecReload=</varname>
+ completes, the signaling is skipped and the service manager immediately starts listening for
+ <literal>READY=1</literal>.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>ExecReloadPost=</varname></term>
+
+ <listitem><para>Commands to execute after a successful reload operation. Syntax for this setting
+ is exactly the same as <varname>ExecReload=</varname>.</para>
+
+ <xi:include href="version-info.xml" xpointer="v259"/>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>RootDirectoryStartOnly=</varname></term>
- <listitem><para>Takes a boolean argument. If true, the root
- directory, as configured with the
+ <listitem><para>Takes a boolean argument. If true, the root directory, as configured with the
<varname>RootDirectory=</varname> option (see
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for more information), is only applied to the process started
- with <varname>ExecStart=</varname>, and not to the various
- other <varname>ExecStartPre=</varname>,
- <varname>ExecStartPost=</varname>,
- <varname>ExecReload=</varname>, <varname>ExecStop=</varname>,
- and <varname>ExecStopPost=</varname> commands. If false, the
- setting is applied to all configured commands the same way.
- Defaults to false.</para></listitem>
+ for more information), is only applied to the process started with <varname>ExecStart=</varname>,
+ and not to the various other <varname>ExecStartPre=</varname>, <varname>ExecStartPost=</varname>,
+ <varname>ExecReload=</varname>, <varname>ExecReloadPost=</varname>, <varname>ExecStop=</varname>,
+ and <varname>ExecStopPost=</varname> commands. If false, the setting is applied to all
+ configured commands the same way. Defaults to false.</para></listitem>
</varlistentry>
<varlistentry>
[SERVICE_START_POST] = UNIT_ACTIVATING,
[SERVICE_RUNNING] = UNIT_ACTIVE,
[SERVICE_EXITED] = UNIT_ACTIVE,
+ [SERVICE_REFRESH_EXTENSIONS] = UNIT_REFRESHING,
[SERVICE_RELOAD] = UNIT_RELOADING,
[SERVICE_RELOAD_SIGNAL] = UNIT_RELOADING,
[SERVICE_RELOAD_NOTIFY] = UNIT_RELOADING,
- [SERVICE_REFRESH_EXTENSIONS] = UNIT_REFRESHING,
+ [SERVICE_RELOAD_POST] = UNIT_RELOADING,
[SERVICE_MOUNTING] = UNIT_REFRESHING,
[SERVICE_STOP] = UNIT_DEACTIVATING,
[SERVICE_STOP_WATCHDOG] = UNIT_DEACTIVATING,
[SERVICE_START_POST] = UNIT_ACTIVE,
[SERVICE_RUNNING] = UNIT_ACTIVE,
[SERVICE_EXITED] = UNIT_ACTIVE,
+ [SERVICE_REFRESH_EXTENSIONS] = UNIT_REFRESHING,
[SERVICE_RELOAD] = UNIT_RELOADING,
[SERVICE_RELOAD_SIGNAL] = UNIT_RELOADING,
[SERVICE_RELOAD_NOTIFY] = UNIT_RELOADING,
- [SERVICE_REFRESH_EXTENSIONS] = UNIT_REFRESHING,
+ [SERVICE_RELOAD_POST] = UNIT_RELOADING,
[SERVICE_MOUNTING] = UNIT_REFRESHING,
[SERVICE_STOP] = UNIT_DEACTIVATING,
[SERVICE_STOP_WATCHDOG] = UNIT_DEACTIVATING,
return IN_SET(state,
SERVICE_START, SERVICE_START_POST,
SERVICE_RUNNING,
- SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_REFRESH_EXTENSIONS,
+ SERVICE_REFRESH_EXTENSIONS, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_RELOAD_POST,
SERVICE_MOUNTING,
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL);
return IN_SET(state,
SERVICE_CONDITION,
SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
- SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_REFRESH_EXTENSIONS,
+ SERVICE_REFRESH_EXTENSIONS, SERVICE_RELOAD, SERVICE_RELOAD_POST,
SERVICE_MOUNTING,
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
return IN_SET(state,
SERVICE_START_POST,
SERVICE_RUNNING,
- SERVICE_REFRESH_EXTENSIONS, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY,
+ SERVICE_REFRESH_EXTENSIONS, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_RELOAD_POST,
SERVICE_MOUNTING);
}
if (!IN_SET(state,
SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
SERVICE_RUNNING,
- SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_REFRESH_EXTENSIONS,
+ SERVICE_REFRESH_EXTENSIONS, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_RELOAD_POST,
SERVICE_MOUNTING,
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
case SERVICE_START_PRE:
case SERVICE_START:
case SERVICE_START_POST:
+ case SERVICE_REFRESH_EXTENSIONS:
case SERVICE_RELOAD:
case SERVICE_RELOAD_SIGNAL:
case SERVICE_RELOAD_NOTIFY:
- case SERVICE_REFRESH_EXTENSIONS:
+ case SERVICE_RELOAD_POST:
case SERVICE_MOUNTING:
return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->timeout_start_usec);
log_unit_warning(UNIT(s), "Failed to schedule propagation of reload, ignoring: %s", bus_error_message(&error, r));
}
-static void service_enter_reload_signal_exec(Service *s) {
- bool killed = false;
+static void service_enter_reload_post(Service *s) {
int r;
assert(s);
service_unwatch_control_pid(s);
- usec_t ts = now(CLOCK_MONOTONIC);
-
- if (s->type == SERVICE_NOTIFY_RELOAD && pidref_is_set(&s->main_pid)) {
- r = pidref_kill_and_sigcont(&s->main_pid, s->reload_signal);
- if (r < 0) {
- log_unit_warning_errno(UNIT(s), r, "Failed to send reload signal: %m");
- goto fail;
- }
-
- killed = true;
- }
-
- s->control_command = s->exec_command[SERVICE_EXEC_RELOAD];
+ s->control_command = s->exec_command[SERVICE_EXEC_RELOAD_POST];
if (s->control_command) {
- s->control_command_id = SERVICE_EXEC_RELOAD;
+ s->control_command_id = SERVICE_EXEC_RELOAD_POST;
r = service_spawn(s,
s->control_command,
s->timeout_start_usec,
&s->control_pid);
if (r < 0) {
- log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'reload' task: %m");
- goto fail;
+ log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'reload-post' task: %m");
+ return service_reload_finish(s, SERVICE_FAILURE_RESOURCES);
}
- service_set_state(s, SERVICE_RELOAD);
- } else if (killed) {
+ service_set_state(s, SERVICE_RELOAD_POST);
+ } else
+ service_reload_finish(s, SERVICE_SUCCESS);
+}
+
+static void service_enter_reload_signal(Service *s) {
+ int r;
+
+ assert(s);
+
+ if (s->type != SERVICE_NOTIFY_RELOAD)
+ return service_enter_reload_post(s);
+
+ if (s->state == SERVICE_RELOAD) {
+ /* We executed ExecReload=, and the service has already notified us the result?
+ * Directly transition to next state. */
+ if (s->notify_state == NOTIFY_RELOADING)
+ return service_set_state(s, SERVICE_RELOAD_NOTIFY);
+ if (s->notify_state == NOTIFY_RELOAD_READY)
+ return service_enter_reload_post(s);
+ }
+
+ if (pidref_is_set(&s->main_pid)) {
r = service_arm_timer(s, /* relative= */ true, s->timeout_start_usec);
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to install timer: %m");
goto fail;
}
+ r = pidref_kill_and_sigcont(&s->main_pid, s->reload_signal);
+ if (r < 0) {
+ log_unit_warning_errno(UNIT(s), r, "Failed to send reload signal: %m");
+ goto fail;
+ }
+
service_set_state(s, SERVICE_RELOAD_SIGNAL);
} else
- return service_reload_finish(s, SERVICE_SUCCESS);
+ service_enter_reload_post(s);
+
+ return;
+
+fail:
+ service_reload_finish(s, SERVICE_FAILURE_RESOURCES);
+}
+
+static void service_enter_reload(Service *s) {
+ int r;
+
+ assert(s);
+
+ service_unwatch_control_pid(s);
+
+ if (IN_SET(s->notify_state, NOTIFY_RELOADING, NOTIFY_RELOAD_READY))
+ s->notify_state = _NOTIFY_STATE_INVALID;
/* Store the timestamp when we started reloading: when reloading via SIGHUP we won't leave the reload
* state until we received both RELOADING=1 and READY=1 with MONOTONIC_USEC= set to a value above
* this. Thus we know for sure the reload cycle was executed *after* we requested it, and is not one
* that was already in progress before. */
- s->reload_begin_usec = ts;
- return;
+ s->reload_begin_usec = now(CLOCK_MONOTONIC);
-fail:
- service_reload_finish(s, SERVICE_FAILURE_RESOURCES);
+ s->control_command = s->exec_command[SERVICE_EXEC_RELOAD];
+ if (s->control_command) {
+ s->control_command_id = SERVICE_EXEC_RELOAD;
+
+ r = service_spawn(s,
+ s->control_command,
+ service_exec_flags(s->control_command_id, /* cred_flag = */ 0),
+ s->timeout_start_usec,
+ &s->control_pid);
+ if (r < 0) {
+ log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'reload' task: %m");
+ return service_reload_finish(s, SERVICE_FAILURE_RESOURCES);
+ }
+
+ service_set_state(s, SERVICE_RELOAD);
+ } else
+ service_enter_reload_signal(s);
}
static bool service_should_reload_extensions(Service *s) {
assert(s);
- /* If we don't have extensions to reload, immediately go to the signal step */
+ /* If we don't have extensions to refresh, immediately transition to reload state */
if (!service_should_reload_extensions(s))
- return service_enter_reload_signal_exec(s);
+ return service_enter_reload(s);
service_unwatch_control_pid(s);
s->control_command = NULL;
s->control_command = s->control_command->command_next;
service_unwatch_control_pid(s);
- if (IN_SET(s->state, SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD))
+ if (IN_SET(s->state,
+ SERVICE_CONDITION,
+ SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
+ SERVICE_RUNNING,
+ SERVICE_RELOAD, SERVICE_RELOAD_POST))
timeout = s->timeout_start_usec;
else
timeout = s->timeout_stop_usec;
service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES);
else if (s->state == SERVICE_STOP_POST)
service_enter_dead(s, SERVICE_FAILURE_RESOURCES, /* allow_restart= */ true);
- else if (s->state == SERVICE_RELOAD)
+ else if (IN_SET(s->state, SERVICE_RELOAD, SERVICE_RELOAD_POST))
service_reload_finish(s, SERVICE_FAILURE_RESOURCES);
else
service_enter_stop(s, SERVICE_FAILURE_RESOURCES);
case SERVICE_RELOAD:
case SERVICE_RELOAD_SIGNAL:
case SERVICE_RELOAD_NOTIFY:
+ case SERVICE_RELOAD_POST:
case SERVICE_STOP_WATCHDOG:
/* If there's already something running we go directly into kill mode. */
service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_SUCCESS);
switch (s->state) {
case SERVICE_START_POST:
+ case SERVICE_REFRESH_EXTENSIONS:
case SERVICE_RELOAD:
case SERVICE_RELOAD_SIGNAL:
case SERVICE_RELOAD_NOTIFY:
- case SERVICE_REFRESH_EXTENSIONS:
+ case SERVICE_RELOAD_POST:
case SERVICE_MOUNTING:
/* If neither main nor control processes are running then the current
* state can never exit cleanly, hence immediately terminate the
success,
code, status);
- if (!IN_SET(s->state, SERVICE_RELOAD, SERVICE_MOUNTING) && s->result == SERVICE_SUCCESS)
+ if (!IN_SET(s->state, SERVICE_REFRESH_EXTENSIONS, SERVICE_RELOAD, SERVICE_RELOAD_POST, SERVICE_MOUNTING) &&
+ s->result == SERVICE_SUCCESS)
s->result = f;
if (s->control_command &&
service_enter_running(s, SERVICE_SUCCESS);
break;
- case SERVICE_RELOAD:
- case SERVICE_RELOAD_SIGNAL:
- case SERVICE_RELOAD_NOTIFY:
+ case SERVICE_REFRESH_EXTENSIONS:
if (f == SERVICE_SUCCESS)
- if (service_load_pid_file(s, true) < 0)
- service_search_main_pid(s);
-
- /* If the last notification we received from the service process indicates
- * we are still reloading, then don't leave reloading state just yet, just
- * transition into SERVICE_RELOAD_NOTIFY, to wait for the READY=1 coming,
- * too. */
- if (s->notify_state == NOTIFY_RELOADING) {
- s->reload_result = f;
- service_set_state(s, SERVICE_RELOAD_NOTIFY);
- } else
+ /* Remounting extensions asynchronously done, proceed to reload */
+ service_enter_reload(s);
+ else
service_reload_finish(s, f);
break;
- case SERVICE_REFRESH_EXTENSIONS:
- if (f == SERVICE_SUCCESS)
- /* Remounting extensions asynchronously done, proceed to signal */
- service_enter_reload_signal_exec(s);
- else
+ case SERVICE_RELOAD:
+ if (f != SERVICE_SUCCESS) {
service_reload_finish(s, f);
+ break;
+ }
+
+ if (service_load_pid_file(s, true) < 0)
+ service_search_main_pid(s);
+
+ service_enter_reload_signal(s);
+ break;
+
+ case SERVICE_RELOAD_POST:
+ service_reload_finish(s, f);
break;
case SERVICE_MOUNTING:
service_enter_stop(s, SERVICE_FAILURE_TIMEOUT);
break;
+ case SERVICE_REFRESH_EXTENSIONS:
case SERVICE_RELOAD:
case SERVICE_RELOAD_SIGNAL:
case SERVICE_RELOAD_NOTIFY:
- case SERVICE_REFRESH_EXTENSIONS:
+ case SERVICE_RELOAD_POST:
log_unit_warning(UNIT(s), "Reload operation timed out. Killing reload process.");
service_kill_control_process(s);
service_reload_finish(s, SERVICE_FAILURE_TIMEOUT);
if (strv_contains(tags, "READY=1")) {
- s->notify_state = NOTIFY_READY;
+ if (s->notify_state == NOTIFY_RELOADING)
+ s->notify_state = NOTIFY_RELOAD_READY;
+ else
+ s->notify_state = NOTIFY_READY;
/* Combined RELOADING=1 and READY=1? Then this is indication that the service started and
* immediately finished reloading. */
monotonic_usec != USEC_INFINITY &&
monotonic_usec >= s->reload_begin_usec)
/* Valid Type=notify-reload protocol? Then we're all good. */
- service_reload_finish(s, SERVICE_SUCCESS);
+ service_enter_reload_post(s);
else if (s->state == SERVICE_RUNNING) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
/* Sending READY=1 while we are reloading informs us that the reloading is complete. */
if (s->state == SERVICE_RELOAD_NOTIFY)
- service_reload_finish(s, SERVICE_SUCCESS);
+ service_enter_reload_post(s);
} else if (strv_contains(tags, "RELOADING=1")) {
r = service_notify_message_parse_new_pid(u, tags, fds, &new_main_pid);
if (r > 0 &&
IN_SET(s->state, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING,
- SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_REFRESH_EXTENSIONS,
+ SERVICE_REFRESH_EXTENSIONS, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_RELOAD_POST,
SERVICE_STOP, SERVICE_STOP_SIGTERM) &&
(!s->main_pid_known || !pidref_equal(&new_main_pid, &s->main_pid))) {
SERVICE_START,
SERVICE_START_POST,
SERVICE_RUNNING,
+ SERVICE_REFRESH_EXTENSIONS,
SERVICE_RELOAD,
SERVICE_RELOAD_SIGNAL,
SERVICE_RELOAD_NOTIFY,
- SERVICE_REFRESH_EXTENSIONS,
+ SERVICE_RELOAD_POST,
SERVICE_MOUNTING);
}
SERVICE_START,
SERVICE_START_POST,
SERVICE_RUNNING,
+ SERVICE_REFRESH_EXTENSIONS,
SERVICE_RELOAD,
SERVICE_RELOAD_SIGNAL,
SERVICE_RELOAD_NOTIFY,
- SERVICE_REFRESH_EXTENSIONS,
+ SERVICE_RELOAD_POST,
SERVICE_MOUNTING,
SERVICE_STOP,
SERVICE_STOP_WATCHDOG,
DEFINE_STRING_TABLE_LOOKUP(service_exit_type, ServiceExitType);
static const char* const service_exec_command_table[_SERVICE_EXEC_COMMAND_MAX] = {
- [SERVICE_EXEC_CONDITION] = "ExecCondition",
- [SERVICE_EXEC_START_PRE] = "ExecStartPre",
- [SERVICE_EXEC_START] = "ExecStart",
- [SERVICE_EXEC_START_POST] = "ExecStartPost",
- [SERVICE_EXEC_RELOAD] = "ExecReload",
- [SERVICE_EXEC_STOP] = "ExecStop",
- [SERVICE_EXEC_STOP_POST] = "ExecStopPost",
+ [SERVICE_EXEC_CONDITION] = "ExecCondition",
+ [SERVICE_EXEC_START_PRE] = "ExecStartPre",
+ [SERVICE_EXEC_START] = "ExecStart",
+ [SERVICE_EXEC_START_POST] = "ExecStartPost",
+ [SERVICE_EXEC_RELOAD] = "ExecReload",
+ [SERVICE_EXEC_RELOAD_POST] = "ExecReloadPost",
+ [SERVICE_EXEC_STOP] = "ExecStop",
+ [SERVICE_EXEC_STOP_POST] = "ExecStopPost",
};
DEFINE_STRING_TABLE_LOOKUP(service_exec_command, ServiceExecCommand);
static const char* const service_exec_ex_command_table[_SERVICE_EXEC_COMMAND_MAX] = {
- [SERVICE_EXEC_CONDITION] = "ExecConditionEx",
- [SERVICE_EXEC_START_PRE] = "ExecStartPreEx",
- [SERVICE_EXEC_START] = "ExecStartEx",
- [SERVICE_EXEC_START_POST] = "ExecStartPostEx",
- [SERVICE_EXEC_RELOAD] = "ExecReloadEx",
- [SERVICE_EXEC_STOP] = "ExecStopEx",
- [SERVICE_EXEC_STOP_POST] = "ExecStopPostEx",
+ [SERVICE_EXEC_CONDITION] = "ExecConditionEx",
+ [SERVICE_EXEC_START_PRE] = "ExecStartPreEx",
+ [SERVICE_EXEC_START] = "ExecStartEx",
+ [SERVICE_EXEC_START_POST] = "ExecStartPostEx",
+ [SERVICE_EXEC_RELOAD] = "ExecReloadEx",
+ [SERVICE_EXEC_RELOAD_POST] = "ExecReloadPostEx",
+ [SERVICE_EXEC_STOP] = "ExecStopEx",
+ [SERVICE_EXEC_STOP_POST] = "ExecStopPostEx",
};
DEFINE_STRING_TABLE_LOOKUP(service_exec_ex_command, ServiceExecCommand);
static const char* const notify_state_table[_NOTIFY_STATE_MAX] = {
- [NOTIFY_READY] = "ready",
- [NOTIFY_RELOADING] = "reloading",
- [NOTIFY_STOPPING] = "stopping",
+ [NOTIFY_READY] = "ready",
+ [NOTIFY_RELOADING] = "reloading",
+ [NOTIFY_RELOAD_READY] = "reload-ready",
+ [NOTIFY_STOPPING] = "stopping",
};
DEFINE_STRING_TABLE_LOOKUP(notify_state, NotifyState);