1
1
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:
Lennart Poettering 2014-08-21 17:03:15 +02:00
parent 70af4d17da
commit 308d72dc1e
4 changed files with 185 additions and 48 deletions

View File

@ -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",

View File

@ -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",

View File

@ -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_;

View File

@ -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;
}