diff options
author | Lennart Poettering <lennart@poettering.net> | 2014-08-21 17:03:15 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2014-08-21 17:24:21 +0200 |
commit | 308d72dc1e2106f94ae637e2ea510e8d466d2af1 (patch) | |
tree | b7cae9d62f78eb64810065245ab39e9657bad21d | |
parent | 70af4d17dafe81acc96f71f4ec06fbea7386bc38 (diff) |
core: allow informing systemd about service status changes with RELOADING=1 and STOPPING=1 sd_notify() messages
-rw-r--r-- | man/sd_notify.xml | 93 | ||||
-rw-r--r-- | src/core/service.c | 105 | ||||
-rw-r--r-- | src/core/service.h | 13 | ||||
-rw-r--r-- | src/test/test-daemon.c | 22 |
4 files changed, 185 insertions, 48 deletions
diff --git a/man/sd_notify.xml b/man/sd_notify.xml index 6bf8230763..fbb882dfd2 100644 --- a/man/sd_notify.xml +++ b/man/sd_notify.xml @@ -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", diff --git a/src/core/service.c b/src/core/service.c index 262a40cc8b..3221938793 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -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", diff --git a/src/core/service.h b/src/core/service.h index 686cf4b0bd..0227321d99 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -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_; diff --git a/src/test/test-daemon.c b/src/test/test-daemon.c index bcc049b325..7e0ac754d1 100644 --- a/src/test/test-daemon.c +++ b/src/test/test-daemon.c @@ -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(5); + + sd_notify(0, + "STATUS=Reloading\n" + "RELOADING=1"); sleep(5); + sd_notify(0, "STATUS=Running\n" "READY=1"); - sleep(10); - sd_notify(0, "STATUS=Quitting"); + sleep(5); + + sd_notify(0, + "STATUS=Quitting\n" + "STOPPING=1"); + sleep(5); return 0; } |