Skip to content

Commit 308d72d

Browse files
committed
core: allow informing systemd about service status changes with RELOADING=1 and STOPPING=1 sd_notify() messages
1 parent 70af4d1 commit 308d72d

File tree

4 files changed

+185
-48
lines changed

4 files changed

+185
-48
lines changed

man/sd_notify.xml

Lines changed: 61 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
<refnamediv>
4747
<refname>sd_notify</refname>
4848
<refname>sd_notifyf</refname>
49-
<refpurpose>Notify service manager about start-up completion and other daemon status changes</refpurpose>
49+
<refpurpose>Notify service manager about start-up completion and other service status changes</refpurpose>
5050
</refnamediv>
5151

5252
<refsynopsisdiv>
@@ -70,12 +70,12 @@
7070

7171
<refsect1>
7272
<title>Description</title>
73-
<para><function>sd_notify()</function> shall be called
74-
by a daemon to notify the init system about status
75-
changes. It can be used to send arbitrary information,
76-
encoded in an environment-block-like string. Most
77-
importantly it can be used for start-up completion
78-
notification.</para>
73+
<para><function>sd_notify()</function> may be called
74+
by a service to notify the service manager about
75+
state changes. It can be used to send arbitrary
76+
information, encoded in an environment-block-like
77+
string. Most importantly it can be used for start-up
78+
completion notification.</para>
7979

8080
<para>If the <parameter>unset_environment</parameter>
8181
parameter is non-zero, <function>sd_notify()</function>
@@ -99,58 +99,87 @@
9999
<varlistentry>
100100
<term>READY=1</term>
101101

102-
<listitem><para>Tells the init system
103-
that daemon startup is finished. This
104-
is only used by systemd if the service
105-
definition file has Type=notify
106-
set. The passed argument is a boolean
107-
"1" or "0". Since there is little
102+
<listitem><para>Tells the service
103+
manager that service startup is
104+
finished. This is only used by systemd
105+
if the service definition file has
106+
Type=notify set. Since there is little
108107
value in signaling non-readiness, the
109-
only value daemons should send is
110-
"READY=1".</para></listitem>
108+
only value services should send is
109+
<literal>READY=1</literal>
110+
(i.e. <literal>READY=0</literal> is
111+
not defined).</para></listitem>
112+
</varlistentry>
113+
114+
<varlistentry>
115+
<term>RELOADING=1</term>
116+
117+
<listitem><para>Tells the service manager
118+
that the service is reloading its
119+
configuration. This is useful to allow
120+
the service manager to track the service's
121+
internal state, and present it to the
122+
user. Note that a service that sends
123+
this notification must also send a
124+
<literal>READY=1</literal>
125+
notification when it completed
126+
reloading its
127+
configuration.</para></listitem>
128+
</varlistentry>
129+
130+
<varlistentry>
131+
<term>STOPPING=1</term>
132+
133+
<listitem><para>Tells the service manager
134+
that the service is beginning its
135+
shutdown. This is useful to allow the
136+
service manager to track the service's
137+
internal state, and present it to the
138+
user.</para></listitem>
111139
</varlistentry>
112140

113141
<varlistentry>
114142
<term>STATUS=...</term>
115143

116144
<listitem><para>Passes a single-line
117-
status string back to the init system
118-
that describes the daemon state. This
145+
UTF-8 status string back to the service manager
146+
that describes the service state. This
119147
is free-form and can be used for
120148
various purposes: general state
121149
feedback, fsck-like programs could
122150
pass completion percentages and
123151
failing programs could pass a human
124152
readable error message. Example:
125-
"STATUS=Completed 66% of file system
126-
check..."</para></listitem>
153+
<literal>STATUS=Completed 66% of file
154+
system
155+
check...</literal></para></listitem>
127156
</varlistentry>
128157

129158
<varlistentry>
130159
<term>ERRNO=...</term>
131160

132-
<listitem><para>If a daemon fails, the
161+
<listitem><para>If a service fails, the
133162
errno-style error code, formatted as
134-
string. Example: "ERRNO=2" for
163+
string. Example: <literal>ERRNO=2</literal> for
135164
ENOENT.</para></listitem>
136165
</varlistentry>
137166

138167
<varlistentry>
139168
<term>BUSERROR=...</term>
140169

141-
<listitem><para>If a daemon fails, the
170+
<listitem><para>If a service fails, the
142171
D-Bus error-style error code. Example:
143-
"BUSERROR=org.freedesktop.DBus.Error.TimedOut"</para></listitem>
172+
<literal>BUSERROR=org.freedesktop.DBus.Error.TimedOut</literal></para></listitem>
144173
</varlistentry>
145174

146175
<varlistentry>
147176
<term>MAINPID=...</term>
148177

149178
<listitem><para>The main pid of the
150-
daemon, in case the init system did
179+
service, in case the service manager did
151180
not fork off the process
152181
itself. Example:
153-
"MAINPID=4711"</para></listitem>
182+
<literal>MAINPID=4711</literal></para></listitem>
154183
</varlistentry>
155184

156185
<varlistentry>
@@ -183,7 +212,7 @@
183212
clashes.</para>
184213

185214
<para>Note that systemd will accept status data sent
186-
from a daemon only if the
215+
from a service only if the
187216
<varname>NotifyAccess=</varname> option is correctly
188217
set in the service definition file. See
189218
<citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
@@ -222,7 +251,7 @@
222251
<varname>$NOTIFY_SOCKET</varname> is <literal>@</literal>, the string is
223252
understood as Linux abstract namespace socket. The
224253
datagram is accompanied by the process credentials of
225-
the sending daemon, using SCM_CREDENTIALS.</para>
254+
the sending service, using SCM_CREDENTIALS.</para>
226255
</refsect1>
227256

228257
<refsect1>
@@ -232,7 +261,7 @@
232261
<varlistentry>
233262
<term><varname>$NOTIFY_SOCKET</varname></term>
234263

235-
<listitem><para>Set by the init system
264+
<listitem><para>Set by the service manager
236265
for supervised processes for status
237266
and start-up completion
238267
notification. This environment variable
@@ -249,17 +278,17 @@
249278
<example>
250279
<title>Start-up Notification</title>
251280

252-
<para>When a daemon finished starting up, it
281+
<para>When a service finished starting up, it
253282
might issue the following call to notify
254-
the init system:</para>
283+
the service manager:</para>
255284

256285
<programlisting>sd_notify(0, "READY=1");</programlisting>
257286
</example>
258287

259288
<example>
260289
<title>Extended Start-up Notification</title>
261290

262-
<para>A daemon could send the following after
291+
<para>A service could send the following after
263292
completing initialization:</para>
264293

265294
<programlisting>sd_notifyf(0, "READY=1\n"
@@ -271,7 +300,7 @@
271300
<example>
272301
<title>Error Cause Notification</title>
273302

274-
<para>A daemon could send the following shortly before exiting, on failure</para>
303+
<para>A service could send the following shortly before exiting, on failure</para>
275304

276305
<programlisting>sd_notifyf(0, "STATUS=Failed to start up: %s\n"
277306
"ERRNO=%i",

src/core/service.c

Lines changed: 92 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
9292
static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void *userdata);
9393

9494
static void service_enter_signal(Service *s, ServiceState state, ServiceResult f);
95+
static void service_enter_reload_by_notify(Service *s);
9596

9697
static void service_init(Unit *u) {
9798
Service *s = SERVICE(u);
@@ -473,7 +474,8 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
473474
"%sGuessMainPID: %s\n"
474475
"%sType: %s\n"
475476
"%sRestart: %s\n"
476-
"%sNotifyAccess: %s\n",
477+
"%sNotifyAccess: %s\n"
478+
"%sNotifyState: %s\n",
477479
prefix, service_state_to_string(s->state),
478480
prefix, service_result_to_string(s->result),
479481
prefix, service_result_to_string(s->reload_result),
@@ -483,7 +485,8 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
483485
prefix, yes_no(s->guess_main_pid),
484486
prefix, service_type_to_string(s->type),
485487
prefix, service_restart_to_string(s->restart),
486-
prefix, notify_access_to_string(s->notify_access));
488+
prefix, notify_access_to_string(s->notify_access),
489+
prefix, notify_state_to_string(s->notify_state));
487490

488491
if (s->control_pid > 0)
489492
fprintf(f,
@@ -1176,6 +1179,17 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f
11761179
service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true);
11771180
}
11781181

1182+
static void service_enter_stop_by_notify(Service *s) {
1183+
assert(s);
1184+
1185+
unit_watch_all_pids(UNIT(s));
1186+
1187+
if (s->timeout_stop_usec > 0)
1188+
service_arm_timer(s, s->timeout_stop_usec);
1189+
1190+
service_set_state(s, SERVICE_STOP);
1191+
}
1192+
11791193
static void service_enter_stop(Service *s, ServiceResult f) {
11801194
int r;
11811195

@@ -1226,9 +1240,18 @@ static void service_enter_running(Service *s, ServiceResult f) {
12261240
cgroup_ok = cgroup_good(s);
12271241

12281242
if ((main_pid_ok > 0 || (main_pid_ok < 0 && cgroup_ok != 0)) &&
1229-
(s->bus_name_good || s->type != SERVICE_DBUS))
1230-
service_set_state(s, SERVICE_RUNNING);
1231-
else if (s->remain_after_exit)
1243+
(s->bus_name_good || s->type != SERVICE_DBUS)) {
1244+
1245+
/* If there are any queued up sd_notify()
1246+
* notifications, process them now */
1247+
if (s->notify_state == NOTIFY_RELOADING)
1248+
service_enter_reload_by_notify(s);
1249+
else if (s->notify_state == NOTIFY_STOPPING)
1250+
service_enter_stop_by_notify(s);
1251+
else
1252+
service_set_state(s, SERVICE_RUNNING);
1253+
1254+
} else if (s->remain_after_exit)
12321255
service_set_state(s, SERVICE_EXITED);
12331256
else
12341257
service_enter_stop(s, SERVICE_SUCCESS);
@@ -1433,12 +1456,19 @@ static void service_enter_restart(Service *s) {
14331456
return;
14341457

14351458
fail:
1436-
log_warning_unit(UNIT(s)->id,
1437-
"%s failed to schedule restart job: %s",
1438-
UNIT(s)->id, bus_error_message(&error, -r));
1459+
log_warning_unit(UNIT(s)->id, "%s failed to schedule restart job: %s", UNIT(s)->id, bus_error_message(&error, -r));
14391460
service_enter_dead(s, SERVICE_FAILURE_RESOURCES, false);
14401461
}
14411462

1463+
static void service_enter_reload_by_notify(Service *s) {
1464+
assert(s);
1465+
1466+
if (s->timeout_start_usec > 0)
1467+
service_arm_timer(s, s->timeout_start_usec);
1468+
1469+
service_set_state(s, SERVICE_RELOAD);
1470+
}
1471+
14421472
static void service_enter_reload(Service *s) {
14431473
int r;
14441474

@@ -1667,6 +1697,8 @@ static int service_start(Unit *u) {
16671697
s->status_text = NULL;
16681698
s->status_errno = 0;
16691699

1700+
s->notify_state = NOTIFY_UNKNOWN;
1701+
16701702
service_enter_start_pre(s);
16711703
return 0;
16721704
}
@@ -2504,13 +2536,15 @@ static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void
25042536

25052537
static void service_notify_message(Unit *u, pid_t pid, char **tags) {
25062538
Service *s = SERVICE(u);
2507-
const char *e;
2539+
_cleanup_free_ char *cc = NULL;
25082540
bool notify_dbus = false;
2541+
const char *e;
25092542

25102543
assert(u);
25112544

2512-
log_debug_unit(u->id, "%s: Got notification message from PID "PID_FMT" (%s...)",
2513-
u->id, pid, tags && *tags ? tags[0] : "(empty)");
2545+
cc = strv_join(tags, ", ");
2546+
log_debug_unit(u->id, "%s: Got notification message from PID "PID_FMT" (%s)",
2547+
u->id, pid, isempty(cc) ? "n/a" : cc);
25142548

25152549
if (s->notify_access == NOTIFY_NONE) {
25162550
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) {
25392573
}
25402574
}
25412575

2576+
/* Interpret RELOADING= */
2577+
if (strv_find(tags, "RELOADING=1")) {
2578+
2579+
log_debug_unit(u->id, "%s: got RELOADING=1", u->id);
2580+
s->notify_state = NOTIFY_RELOADING;
2581+
2582+
if (s->state == SERVICE_RUNNING)
2583+
service_enter_reload_by_notify(s);
2584+
2585+
notify_dbus = true;
2586+
}
2587+
25422588
/* Interpret READY= */
2543-
if (s->type == SERVICE_NOTIFY && s->state == SERVICE_START && strv_find(tags, "READY=1")) {
2589+
if (strv_find(tags, "READY=1")) {
2590+
25442591
log_debug_unit(u->id, "%s: got READY=1", u->id);
2545-
service_enter_start_post(s);
2592+
s->notify_state = NOTIFY_READY;
2593+
2594+
/* Type=notify services inform us about completed
2595+
* initialization with READY=1 */
2596+
if (s->type == SERVICE_NOTIFY && s->state == SERVICE_START)
2597+
service_enter_start_post(s);
2598+
2599+
/* Sending READY=1 while we are reloading informs us
2600+
* that the reloading is complete */
2601+
if (s->state == SERVICE_RELOAD && s->control_pid == 0)
2602+
service_enter_running(s, SERVICE_SUCCESS);
2603+
2604+
notify_dbus = true;
2605+
}
2606+
2607+
/* Interpret STOPPING= */
2608+
if (strv_find(tags, "STOPPING=1")) {
2609+
2610+
log_debug_unit(u->id, "%s: got STOPPING=1", u->id);
2611+
s->notify_state = NOTIFY_STOPPING;
2612+
2613+
if (s->state == SERVICE_RUNNING)
2614+
service_enter_stop_by_notify(s);
2615+
25462616
notify_dbus = true;
25472617
}
25482618

@@ -2798,6 +2868,15 @@ static const char* const notify_access_table[_NOTIFY_ACCESS_MAX] = {
27982868

27992869
DEFINE_STRING_TABLE_LOOKUP(notify_access, NotifyAccess);
28002870

2871+
static const char* const notify_state_table[_NOTIFY_STATE_MAX] = {
2872+
[NOTIFY_UNKNOWN] = "unknown",
2873+
[NOTIFY_READY] = "ready",
2874+
[NOTIFY_RELOADING] = "reloading",
2875+
[NOTIFY_STOPPING] = "stopping",
2876+
};
2877+
2878+
DEFINE_STRING_TABLE_LOOKUP(notify_state, NotifyState);
2879+
28012880
static const char* const service_result_table[_SERVICE_RESULT_MAX] = {
28022881
[SERVICE_SUCCESS] = "success",
28032882
[SERVICE_FAILURE_RESOURCES] = "resources",

0 commit comments

Comments
 (0)