Skip to content

Commit 97506e8

Browse files
authored
Merge pull request #4745 from joukewitteveen/notify
Improvements for notify services (including #4212)
2 parents e7330df + 6375bd2 commit 97506e8

File tree

6 files changed

+60
-13
lines changed

6 files changed

+60
-13
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ CHANGES WITH 233 in spe
1717
The 'n' choice for the confirmation spawn prompt has been removed,
1818
because its meaning was confusing.
1919

20+
* Services of Type=notify require a READY=1 notification to be sent
21+
during startup. If no such message is sent, the service now fails,
22+
even if the main process exited with a successful exit code.
23+
2024
CHANGES WITH 232:
2125

2226
* The new RemoveIPC= option can be used to remove IPC objects owned by

man/systemd.exec.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1772,9 +1772,9 @@
17721772
<listitem><para>Only defined for the service unit type, this environment variable is passed to all
17731773
<varname>ExecStop=</varname> and <varname>ExecStopPost=</varname> processes, and encodes the service
17741774
"result". Currently, the following values are defined: <literal>protocol</literal> (in case of a protocol
1775-
violation; if a service did not take the steps required by its configuration), <literal>timeout</literal> (in
1776-
case of an operation timeout), <literal>exit-code</literal> (if a service process exited with a non-zero exit
1777-
code; see <varname>$EXIT_CODE</varname> below for the actual exit code returned), <literal>signal</literal>
1775+
violation; if a service did not take the steps required by its unit configuration), <literal>timeout</literal>
1776+
(in case of an operation timeout), <literal>exit-code</literal> (if a service process exited with a non-zero
1777+
exit code; see <varname>$EXIT_CODE</varname> below for the actual exit code returned), <literal>signal</literal>
17781778
(if a service process was terminated abnormally by a signal; see <varname>$EXIT_CODE</varname> below for the
17791779
actual signal used for the termination), <literal>core-dump</literal> (if a service process terminated
17801780
abnormally and dumped core), <literal>watchdog</literal> (if the watchdog keep-alive ping was enabled for the

man/systemd.service.xml

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -798,11 +798,14 @@
798798
notification socket, as accessible via the
799799
<citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>
800800
call. Takes one of <option>none</option> (the default),
801-
<option>main</option> or <option>all</option>. If
802-
<option>none</option>, no daemon status updates are accepted
803-
from the service processes, all status update messages are
804-
ignored. If <option>main</option>, only service updates sent
805-
from the main process of the service are accepted. If
801+
<option>main</option>, <option>exec</option> or
802+
<option>all</option>. If <option>none</option>, no daemon status
803+
updates are accepted from the service processes, all status
804+
update messages are ignored. If <option>main</option>, only
805+
service updates sent from the main process of the service are
806+
accepted. If <option>exec</option>, only service updates sent
807+
from any of the control processes originating from one of the
808+
<varname>Exec*=</varname> commands are accepted. If
806809
<option>all</option>, all services updates from all members of
807810
the service's control group are accepted. This option should
808811
be set to open access to the notification socket when using

src/core/service.c

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,6 +1179,25 @@ static int service_collect_fds(Service *s, int **fds, char ***fd_names) {
11791179
return rn_fds;
11801180
}
11811181

1182+
static bool service_exec_needs_notify_socket(Service *s, ExecFlags flags) {
1183+
assert(s);
1184+
1185+
/* Notifications are accepted depending on the process and
1186+
* the access setting of the service:
1187+
* process: \ access: NONE MAIN EXEC ALL
1188+
* main no yes yes yes
1189+
* control no no yes yes
1190+
* other (forked) no no no yes */
1191+
1192+
if (flags & EXEC_IS_CONTROL)
1193+
/* A control process */
1194+
return IN_SET(s->notify_access, NOTIFY_EXEC, NOTIFY_ALL);
1195+
1196+
/* We only spawn main processes and control processes, so any
1197+
* process that is not a control process is a main process */
1198+
return s->notify_access != NOTIFY_NONE;
1199+
}
1200+
11821201
static int service_spawn(
11831202
Service *s,
11841203
ExecCommand *c,
@@ -1252,7 +1271,7 @@ static int service_spawn(
12521271
if (!our_env)
12531272
return -ENOMEM;
12541273

1255-
if ((flags & EXEC_IS_CONTROL) ? s->notify_access == NOTIFY_ALL : s->notify_access != NOTIFY_NONE)
1274+
if (service_exec_needs_notify_socket(s, flags))
12561275
if (asprintf(our_env + n_env++, "NOTIFY_SOCKET=%s", UNIT(s)->manager->notify_socket) < 0)
12571276
return -ENOMEM;
12581277

@@ -2579,11 +2598,16 @@ static void service_notify_cgroup_empty_event(Unit *u) {
25792598
* SIGCHLD for. */
25802599

25812600
case SERVICE_START:
2582-
case SERVICE_START_POST:
2583-
if (s->type == SERVICE_NOTIFY)
2601+
if (s->type == SERVICE_NOTIFY) {
25842602
/* No chance of getting a ready notification anymore */
25852603
service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_PROTOCOL);
2586-
else if (s->pid_file_pathspec) {
2604+
break;
2605+
}
2606+
2607+
/* Fall through */
2608+
2609+
case SERVICE_START_POST:
2610+
if (s->pid_file_pathspec) {
25872611
/* Give up hoping for the daemon to write its PID file */
25882612
log_unit_warning(u, "Daemon never wrote its PID file. Failing.");
25892613

@@ -3056,7 +3080,18 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds)
30563080
if (s->main_pid != 0)
30573081
log_unit_warning(u, "Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT, pid, s->main_pid);
30583082
else
3059-
log_unit_debug(u, "Got notification message from PID "PID_FMT", but reception only permitted for main PID which is currently not known", pid);
3083+
log_unit_warning(u, "Got notification message from PID "PID_FMT", but reception only permitted for main PID which is currently not known", pid);
3084+
return;
3085+
} else if (s->notify_access == NOTIFY_EXEC && pid != s->main_pid && pid != s->control_pid) {
3086+
if (s->main_pid != 0 && s->control_pid != 0)
3087+
log_unit_warning(u, "Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT" and control PID "PID_FMT,
3088+
pid, s->main_pid, s->control_pid);
3089+
else if (s->main_pid != 0)
3090+
log_unit_warning(u, "Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT, pid, s->main_pid);
3091+
else if (s->control_pid != 0)
3092+
log_unit_warning(u, "Got notification message from PID "PID_FMT", but reception only permitted for control PID "PID_FMT, pid, s->control_pid);
3093+
else
3094+
log_unit_warning(u, "Got notification message from PID "PID_FMT", but reception only permitted for main PID and control PID which are currently not known", pid);
30603095
return;
30613096
} else
30623097
log_unit_debug(u, "Got notification message from PID "PID_FMT" (%s)", pid, isempty(cc) ? "n/a" : cc);
@@ -3066,6 +3101,8 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds)
30663101
if (e && IN_SET(s->state, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD)) {
30673102
if (parse_pid(e, &pid) < 0)
30683103
log_unit_warning(u, "Failed to parse MAINPID= field in notification message: %s", e);
3104+
else if (pid == s->control_pid)
3105+
log_unit_warning(u, "A control process cannot also be the main process");
30693106
else {
30703107
service_set_main_pid(s, pid);
30713108
unit_watch_pid(UNIT(s), pid);
@@ -3381,6 +3418,7 @@ DEFINE_STRING_TABLE_LOOKUP(service_exec_command, ServiceExecCommand);
33813418
static const char* const notify_access_table[_NOTIFY_ACCESS_MAX] = {
33823419
[NOTIFY_NONE] = "none",
33833420
[NOTIFY_MAIN] = "main",
3421+
[NOTIFY_EXEC] = "exec",
33843422
[NOTIFY_ALL] = "all"
33853423
};
33863424

src/core/service.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ typedef enum NotifyAccess {
6565
NOTIFY_NONE,
6666
NOTIFY_ALL,
6767
NOTIFY_MAIN,
68+
NOTIFY_EXEC,
6869
_NOTIFY_ACCESS_MAX,
6970
_NOTIFY_ACCESS_INVALID = -1
7071
} NotifyAccess;

src/shared/bus-unit-util.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,7 @@ static const struct {
764764
const char *result, *explanation;
765765
} explanations [] = {
766766
{ "resources", "of unavailable resources or another system error" },
767+
{ "protocol", "the service did not take the steps required by its unit configuration" },
767768
{ "timeout", "a timeout was exceeded" },
768769
{ "exit-code", "the control process exited with error code" },
769770
{ "signal", "a fatal signal was delivered to the control process" },

0 commit comments

Comments
 (0)