From 96fb8242cc1ef6b0e28f6c86a4f57950095dd7f1 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Thu, 21 Aug 2014 18:50:42 +0200
Subject: [PATCH] service: allow services of Type=oneshot that specify no
ExecStart= commands
This is useful for services that simply want to run something on
shutdown, but not at bootup. They should only set ExecStop= but leave
ExecStart= unset.
---
man/systemd.service.xml | 44 +++++++++++++++++++++++++++-----------------
src/core/service.c | 39 +++++++++++++++++++++++++++++----------
2 files changed, 56 insertions(+), 27 deletions(-)
diff --git a/man/systemd.service.xml b/man/systemd.service.xml
index 5c4bd6569f..e584a1f006 100644
--- a/man/systemd.service.xml
+++ b/man/systemd.service.xml
@@ -139,9 +139,10 @@
<para>If set to
<option>simple</option> (the default
- value if neither
+ if neither
<varname>Type=</varname> nor
- <varname>BusName=</varname> are
+ <varname>BusName=</varname>, but
+ <varname>ExecStart=</varname> are
specified), it is expected that the
process configured with
<varname>ExecStart=</varname> is the
@@ -177,13 +178,17 @@
exits.</para>
<para>Behavior of
- <option>oneshot</option> is similar
- to <option>simple</option>; however,
- it is expected that the process has to
+ <option>oneshot</option> is similar to
+ <option>simple</option>; however, it
+ is expected that the process has to
exit before systemd starts follow-up
units. <varname>RemainAfterExit=</varname>
is particularly useful for this type
- of service.</para>
+ of service. This is the implied
+ default if neither
+ <varname>Type=</varname> or
+ <varname>ExecStart=</varname> are
+ specified.</para>
<para>Behavior of
<option>dbus</option> is similar to
@@ -313,22 +318,27 @@
<para>When <varname>Type</varname> is
not <option>oneshot</option>, only one
- command may be given. When
+ command may and must be given. When
<varname>Type=oneshot</varname> is
- used, more than one command may be
- specified. Multiple command lines may
- be concatenated in a single directive
- by separating them with semicolons
- (these semicolons must be passed as
- separate words). Alternatively, this
- directive may be specified more than
- once with the same effect.
- Lone semicolons may be escaped as
+ used, none or more than one command
+ may be specified. Multiple command
+ lines may be concatenated in a single
+ directive by separating them with
+ semicolons (these semicolons must be
+ passed as separate
+ words). Alternatively, this directive
+ may be specified more than once with
+ the same effect. Lone semicolons may
+ be escaped as
<literal>\;</literal>. If the empty
string is assigned to this option, the
list of commands to start is reset,
prior assignments of this option will
- have no effect.</para>
+ have no effect. If no
+ <varname>ExecStart=</varname> is
+ specified, then the service must have
+ <varname>RemainAfterExit=yes</varname>
+ set.</para>
<para>Each command line is split on
whitespace, with the first item being
diff --git a/src/core/service.c b/src/core/service.c
index 7d6ea73e05..1b864c4c8c 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -313,14 +313,23 @@ static int service_verify(Service *s) {
if (UNIT(s)->load_state != UNIT_LOADED)
return 0;
- if (!s->exec_command[SERVICE_EXEC_START]) {
- log_error_unit(UNIT(s)->id, "%s lacks ExecStart setting. Refusing.", UNIT(s)->id);
+ if (!s->exec_command[SERVICE_EXEC_START] && !s->exec_command[SERVICE_EXEC_STOP]) {
+ log_error_unit(UNIT(s)->id, "%s lacks both ExecStart= and ExecStop= setting. Refusing.", UNIT(s)->id);
return -EINVAL;
}
- if (s->type != SERVICE_ONESHOT &&
- s->exec_command[SERVICE_EXEC_START]->command_next) {
- log_error_unit(UNIT(s)->id, "%s has more than one ExecStart setting, which is only allowed for Type=oneshot services. Refusing.", UNIT(s)->id);
+ if (s->type != SERVICE_ONESHOT && !s->exec_command[SERVICE_EXEC_START]) {
+ log_error_unit(UNIT(s)->id, "%s has no ExecStart= setting, which is only allowed for Type=oneshot services. Refusing.", UNIT(s)->id);
+ return -EINVAL;
+ }
+
+ if (!s->remain_after_exit && !s->exec_command[SERVICE_EXEC_START]) {
+ log_error_unit(UNIT(s)->id, "%s has no ExecStart= setting, which is only allowed for RemainAfterExit=yes services. Refusing.", UNIT(s)->id);
+ return -EINVAL;
+ }
+
+ if (s->type != SERVICE_ONESHOT && s->exec_command[SERVICE_EXEC_START]->command_next) {
+ log_error_unit(UNIT(s)->id, "%s has more than one ExecStart= setting, which is only allowed for Type=oneshot services. Refusing.", UNIT(s)->id);
return -EINVAL;
}
@@ -410,8 +419,15 @@ static int service_load(Unit *u) {
if (r < 0)
return r;
- if (s->type == _SERVICE_TYPE_INVALID)
- s->type = s->bus_name ? SERVICE_DBUS : SERVICE_SIMPLE;
+ if (s->type == _SERVICE_TYPE_INVALID) {
+ /* Figure out a type automatically */
+ if (s->bus_name)
+ s->type = SERVICE_DBUS;
+ else if (s->exec_command[SERVICE_EXEC_START])
+ s->type = SERVICE_SIMPLE;
+ else
+ s->type = SERVICE_ONESHOT;
+ }
/* Oneshot services have disabled start timeout by default */
if (s->type == SERVICE_ONESHOT && !s->start_timeout_defined)
@@ -1309,9 +1325,6 @@ static void service_enter_start(Service *s) {
assert(s);
- assert(s->exec_command[SERVICE_EXEC_START]);
- assert(!s->exec_command[SERVICE_EXEC_START]->command_next || s->type == SERVICE_ONESHOT);
-
service_unwatch_control_pid(s);
service_unwatch_main_pid(s);
@@ -1332,6 +1345,12 @@ static void service_enter_start(Service *s) {
c = s->main_command = s->exec_command[SERVICE_EXEC_START];
}
+ if (!c) {
+ assert(s->type == SERVICE_ONESHOT);
+ service_enter_start_post(s);
+ return;
+ }
+
r = service_spawn(s,
c,
IN_SET(s->type, SERVICE_FORKING, SERVICE_DBUS, SERVICE_NOTIFY, SERVICE_ONESHOT) ? s->timeout_start_usec : 0,