mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-10-27 18:55:09 +03:00
core: allow informing systemd about service status changes with RELOADING=1 and STOPPING=1 sd_notify() messages
This commit is contained in:
parent
70af4d17da
commit
308d72dc1e
@ -46,7 +46,7 @@
|
||||
<refnamediv>
|
||||
<refname>sd_notify</refname>
|
||||
<refname>sd_notifyf</refname>
|
||||
<refpurpose>Notify service manager about start-up completion and other daemon status changes</refpurpose>
|
||||
<refpurpose>Notify service manager about start-up completion and other service status changes</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
@ -70,12 +70,12 @@
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
<para><function>sd_notify()</function> shall be called
|
||||
by a daemon to notify the init system about status
|
||||
changes. It can be used to send arbitrary information,
|
||||
encoded in an environment-block-like string. Most
|
||||
importantly it can be used for start-up completion
|
||||
notification.</para>
|
||||
<para><function>sd_notify()</function> may be called
|
||||
by a service to notify the service manager about
|
||||
state changes. It can be used to send arbitrary
|
||||
information, encoded in an environment-block-like
|
||||
string. Most importantly it can be used for start-up
|
||||
completion notification.</para>
|
||||
|
||||
<para>If the <parameter>unset_environment</parameter>
|
||||
parameter is non-zero, <function>sd_notify()</function>
|
||||
@ -99,58 +99,87 @@
|
||||
<varlistentry>
|
||||
<term>READY=1</term>
|
||||
|
||||
<listitem><para>Tells the init system
|
||||
that daemon startup is finished. This
|
||||
is only used by systemd if the service
|
||||
definition file has Type=notify
|
||||
set. The passed argument is a boolean
|
||||
"1" or "0". Since there is little
|
||||
<listitem><para>Tells the service
|
||||
manager that service startup is
|
||||
finished. 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 daemons should send is
|
||||
"READY=1".</para></listitem>
|
||||
only value services should send is
|
||||
<literal>READY=1</literal>
|
||||
(i.e. <literal>READY=0</literal> is
|
||||
not defined).</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>RELOADING=1</term>
|
||||
|
||||
<listitem><para>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
|
||||
<literal>READY=1</literal>
|
||||
notification when it completed
|
||||
reloading its
|
||||
configuration.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>STOPPING=1</term>
|
||||
|
||||
<listitem><para>Tells the service manager
|
||||
that the service is beginning its
|
||||
shutdown. This is useful to allow the
|
||||
service manager to track the service's
|
||||
internal state, and present it to the
|
||||
user.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>STATUS=...</term>
|
||||
|
||||
<listitem><para>Passes a single-line
|
||||
status string back to the init system
|
||||
that describes the daemon state. This
|
||||
UTF-8 status string back to the service manager
|
||||
that describes the service state. This
|
||||
is free-form and can be used for
|
||||
various purposes: general state
|
||||
feedback, fsck-like programs could
|
||||
pass completion percentages and
|
||||
failing programs could pass a human
|
||||
readable error message. Example:
|
||||
"STATUS=Completed 66% of file system
|
||||
check..."</para></listitem>
|
||||
<literal>STATUS=Completed 66% of file
|
||||
system
|
||||
check...</literal></para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>ERRNO=...</term>
|
||||
|
||||
<listitem><para>If a daemon fails, the
|
||||
<listitem><para>If a service fails, the
|
||||
errno-style error code, formatted as
|
||||
string. Example: "ERRNO=2" for
|
||||
string. Example: <literal>ERRNO=2</literal> for
|
||||
ENOENT.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>BUSERROR=...</term>
|
||||
|
||||
<listitem><para>If a daemon fails, the
|
||||
<listitem><para>If a service fails, the
|
||||
D-Bus error-style error code. Example:
|
||||
"BUSERROR=org.freedesktop.DBus.Error.TimedOut"</para></listitem>
|
||||
<literal>BUSERROR=org.freedesktop.DBus.Error.TimedOut</literal></para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>MAINPID=...</term>
|
||||
|
||||
<listitem><para>The main pid of the
|
||||
daemon, in case the init system did
|
||||
service, in case the service manager did
|
||||
not fork off the process
|
||||
itself. Example:
|
||||
"MAINPID=4711"</para></listitem>
|
||||
<literal>MAINPID=4711</literal></para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
@ -183,7 +212,7 @@
|
||||
clashes.</para>
|
||||
|
||||
<para>Note that systemd will accept status data sent
|
||||
from a daemon only if the
|
||||
from a service only if the
|
||||
<varname>NotifyAccess=</varname> option is correctly
|
||||
set in the service definition file. See
|
||||
<citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
@ -222,7 +251,7 @@
|
||||
<varname>$NOTIFY_SOCKET</varname> is <literal>@</literal>, the string is
|
||||
understood as Linux abstract namespace socket. The
|
||||
datagram is accompanied by the process credentials of
|
||||
the sending daemon, using SCM_CREDENTIALS.</para>
|
||||
the sending service, using SCM_CREDENTIALS.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
@ -232,7 +261,7 @@
|
||||
<varlistentry>
|
||||
<term><varname>$NOTIFY_SOCKET</varname></term>
|
||||
|
||||
<listitem><para>Set by the init system
|
||||
<listitem><para>Set by the service manager
|
||||
for supervised processes for status
|
||||
and start-up completion
|
||||
notification. This environment variable
|
||||
@ -249,9 +278,9 @@
|
||||
<example>
|
||||
<title>Start-up Notification</title>
|
||||
|
||||
<para>When a daemon finished starting up, it
|
||||
<para>When a service finished starting up, it
|
||||
might issue the following call to notify
|
||||
the init system:</para>
|
||||
the service manager:</para>
|
||||
|
||||
<programlisting>sd_notify(0, "READY=1");</programlisting>
|
||||
</example>
|
||||
@ -259,7 +288,7 @@
|
||||
<example>
|
||||
<title>Extended Start-up Notification</title>
|
||||
|
||||
<para>A daemon could send the following after
|
||||
<para>A service could send the following after
|
||||
completing initialization:</para>
|
||||
|
||||
<programlisting>sd_notifyf(0, "READY=1\n"
|
||||
@ -271,7 +300,7 @@
|
||||
<example>
|
||||
<title>Error Cause Notification</title>
|
||||
|
||||
<para>A daemon could send the following shortly before exiting, on failure</para>
|
||||
<para>A service could send the following shortly before exiting, on failure</para>
|
||||
|
||||
<programlisting>sd_notifyf(0, "STATUS=Failed to start up: %s\n"
|
||||
"ERRNO=%i",
|
||||
|
@ -92,6 +92,7 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
|
||||
static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void *userdata);
|
||||
|
||||
static void service_enter_signal(Service *s, ServiceState state, ServiceResult f);
|
||||
static void service_enter_reload_by_notify(Service *s);
|
||||
|
||||
static void service_init(Unit *u) {
|
||||
Service *s = SERVICE(u);
|
||||
@ -473,7 +474,8 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
"%sGuessMainPID: %s\n"
|
||||
"%sType: %s\n"
|
||||
"%sRestart: %s\n"
|
||||
"%sNotifyAccess: %s\n",
|
||||
"%sNotifyAccess: %s\n"
|
||||
"%sNotifyState: %s\n",
|
||||
prefix, service_state_to_string(s->state),
|
||||
prefix, service_result_to_string(s->result),
|
||||
prefix, service_result_to_string(s->reload_result),
|
||||
@ -483,7 +485,8 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
prefix, yes_no(s->guess_main_pid),
|
||||
prefix, service_type_to_string(s->type),
|
||||
prefix, service_restart_to_string(s->restart),
|
||||
prefix, notify_access_to_string(s->notify_access));
|
||||
prefix, notify_access_to_string(s->notify_access),
|
||||
prefix, notify_state_to_string(s->notify_state));
|
||||
|
||||
if (s->control_pid > 0)
|
||||
fprintf(f,
|
||||
@ -1176,6 +1179,17 @@ fail:
|
||||
service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true);
|
||||
}
|
||||
|
||||
static void service_enter_stop_by_notify(Service *s) {
|
||||
assert(s);
|
||||
|
||||
unit_watch_all_pids(UNIT(s));
|
||||
|
||||
if (s->timeout_stop_usec > 0)
|
||||
service_arm_timer(s, s->timeout_stop_usec);
|
||||
|
||||
service_set_state(s, SERVICE_STOP);
|
||||
}
|
||||
|
||||
static void service_enter_stop(Service *s, ServiceResult f) {
|
||||
int r;
|
||||
|
||||
@ -1226,9 +1240,18 @@ static void service_enter_running(Service *s, ServiceResult f) {
|
||||
cgroup_ok = cgroup_good(s);
|
||||
|
||||
if ((main_pid_ok > 0 || (main_pid_ok < 0 && cgroup_ok != 0)) &&
|
||||
(s->bus_name_good || s->type != SERVICE_DBUS))
|
||||
service_set_state(s, SERVICE_RUNNING);
|
||||
else if (s->remain_after_exit)
|
||||
(s->bus_name_good || s->type != SERVICE_DBUS)) {
|
||||
|
||||
/* If there are any queued up sd_notify()
|
||||
* notifications, process them now */
|
||||
if (s->notify_state == NOTIFY_RELOADING)
|
||||
service_enter_reload_by_notify(s);
|
||||
else if (s->notify_state == NOTIFY_STOPPING)
|
||||
service_enter_stop_by_notify(s);
|
||||
else
|
||||
service_set_state(s, SERVICE_RUNNING);
|
||||
|
||||
} else if (s->remain_after_exit)
|
||||
service_set_state(s, SERVICE_EXITED);
|
||||
else
|
||||
service_enter_stop(s, SERVICE_SUCCESS);
|
||||
@ -1433,12 +1456,19 @@ static void service_enter_restart(Service *s) {
|
||||
return;
|
||||
|
||||
fail:
|
||||
log_warning_unit(UNIT(s)->id,
|
||||
"%s failed to schedule restart job: %s",
|
||||
UNIT(s)->id, bus_error_message(&error, -r));
|
||||
log_warning_unit(UNIT(s)->id, "%s failed to schedule restart job: %s", UNIT(s)->id, bus_error_message(&error, -r));
|
||||
service_enter_dead(s, SERVICE_FAILURE_RESOURCES, false);
|
||||
}
|
||||
|
||||
static void service_enter_reload_by_notify(Service *s) {
|
||||
assert(s);
|
||||
|
||||
if (s->timeout_start_usec > 0)
|
||||
service_arm_timer(s, s->timeout_start_usec);
|
||||
|
||||
service_set_state(s, SERVICE_RELOAD);
|
||||
}
|
||||
|
||||
static void service_enter_reload(Service *s) {
|
||||
int r;
|
||||
|
||||
@ -1667,6 +1697,8 @@ static int service_start(Unit *u) {
|
||||
s->status_text = NULL;
|
||||
s->status_errno = 0;
|
||||
|
||||
s->notify_state = NOTIFY_UNKNOWN;
|
||||
|
||||
service_enter_start_pre(s);
|
||||
return 0;
|
||||
}
|
||||
@ -2504,13 +2536,15 @@ static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void
|
||||
|
||||
static void service_notify_message(Unit *u, pid_t pid, char **tags) {
|
||||
Service *s = SERVICE(u);
|
||||
const char *e;
|
||||
_cleanup_free_ char *cc = NULL;
|
||||
bool notify_dbus = false;
|
||||
const char *e;
|
||||
|
||||
assert(u);
|
||||
|
||||
log_debug_unit(u->id, "%s: Got notification message from PID "PID_FMT" (%s...)",
|
||||
u->id, pid, tags && *tags ? tags[0] : "(empty)");
|
||||
cc = strv_join(tags, ", ");
|
||||
log_debug_unit(u->id, "%s: Got notification message from PID "PID_FMT" (%s)",
|
||||
u->id, pid, isempty(cc) ? "n/a" : cc);
|
||||
|
||||
if (s->notify_access == NOTIFY_NONE) {
|
||||
log_warning_unit(u->id, "%s: Got notification message from PID "PID_FMT", but reception is disabled.", u->id, pid);
|
||||
@ -2539,10 +2573,46 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Interpret RELOADING= */
|
||||
if (strv_find(tags, "RELOADING=1")) {
|
||||
|
||||
log_debug_unit(u->id, "%s: got RELOADING=1", u->id);
|
||||
s->notify_state = NOTIFY_RELOADING;
|
||||
|
||||
if (s->state == SERVICE_RUNNING)
|
||||
service_enter_reload_by_notify(s);
|
||||
|
||||
notify_dbus = true;
|
||||
}
|
||||
|
||||
/* Interpret READY= */
|
||||
if (s->type == SERVICE_NOTIFY && s->state == SERVICE_START && strv_find(tags, "READY=1")) {
|
||||
if (strv_find(tags, "READY=1")) {
|
||||
|
||||
log_debug_unit(u->id, "%s: got READY=1", u->id);
|
||||
service_enter_start_post(s);
|
||||
s->notify_state = NOTIFY_READY;
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Interpret STOPPING= */
|
||||
if (strv_find(tags, "STOPPING=1")) {
|
||||
|
||||
log_debug_unit(u->id, "%s: got STOPPING=1", u->id);
|
||||
s->notify_state = NOTIFY_STOPPING;
|
||||
|
||||
if (s->state == SERVICE_RUNNING)
|
||||
service_enter_stop_by_notify(s);
|
||||
|
||||
notify_dbus = true;
|
||||
}
|
||||
|
||||
@ -2798,6 +2868,15 @@ static const char* const notify_access_table[_NOTIFY_ACCESS_MAX] = {
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(notify_access, NotifyAccess);
|
||||
|
||||
static const char* const notify_state_table[_NOTIFY_STATE_MAX] = {
|
||||
[NOTIFY_UNKNOWN] = "unknown",
|
||||
[NOTIFY_READY] = "ready",
|
||||
[NOTIFY_RELOADING] = "reloading",
|
||||
[NOTIFY_STOPPING] = "stopping",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(notify_state, NotifyState);
|
||||
|
||||
static const char* const service_result_table[_SERVICE_RESULT_MAX] = {
|
||||
[SERVICE_SUCCESS] = "success",
|
||||
[SERVICE_FAILURE_RESOURCES] = "resources",
|
||||
|
@ -91,6 +91,15 @@ typedef enum NotifyAccess {
|
||||
_NOTIFY_ACCESS_INVALID = -1
|
||||
} NotifyAccess;
|
||||
|
||||
typedef enum NotifyState {
|
||||
NOTIFY_UNKNOWN,
|
||||
NOTIFY_READY,
|
||||
NOTIFY_RELOADING,
|
||||
NOTIFY_STOPPING,
|
||||
_NOTIFY_STATE_MAX,
|
||||
_NOTIFY_STATE_INVALID = -1
|
||||
} NotifyState;
|
||||
|
||||
typedef enum ServiceResult {
|
||||
SERVICE_SUCCESS,
|
||||
SERVICE_FAILURE_RESOURCES,
|
||||
@ -196,6 +205,7 @@ struct Service {
|
||||
PathSpec *pid_file_pathspec;
|
||||
|
||||
NotifyAccess notify_access;
|
||||
NotifyState notify_state;
|
||||
};
|
||||
|
||||
extern const UnitVTable service_vtable;
|
||||
@ -219,6 +229,9 @@ ServiceExecCommand service_exec_command_from_string(const char *s) _pure_;
|
||||
const char* notify_access_to_string(NotifyAccess i) _const_;
|
||||
NotifyAccess notify_access_from_string(const char *s) _pure_;
|
||||
|
||||
const char* notify_state_to_string(NotifyState i) _const_;
|
||||
NotifyState notify_state_from_string(const char *s) _pure_;
|
||||
|
||||
const char* service_result_to_string(ServiceResult i) _const_;
|
||||
ServiceResult service_result_from_string(const char *s) _pure_;
|
||||
|
||||
|
@ -25,13 +25,29 @@
|
||||
|
||||
int main(int argc, char*argv[]) {
|
||||
|
||||
sd_notify(0, "STATUS=Starting up");
|
||||
sd_notify(0,
|
||||
"STATUS=Starting up");
|
||||
sleep(5);
|
||||
|
||||
sd_notify(0,
|
||||
"STATUS=Running\n"
|
||||
"READY=1");
|
||||
sleep(10);
|
||||
sd_notify(0, "STATUS=Quitting");
|
||||
sleep(5);
|
||||
|
||||
sd_notify(0,
|
||||
"STATUS=Reloading\n"
|
||||
"RELOADING=1");
|
||||
sleep(5);
|
||||
|
||||
sd_notify(0,
|
||||
"STATUS=Running\n"
|
||||
"READY=1");
|
||||
sleep(5);
|
||||
|
||||
sd_notify(0,
|
||||
"STATUS=Quitting\n"
|
||||
"STOPPING=1");
|
||||
sleep(5);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user