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.
This commit is contained in:
Lennart Poettering 2014-08-21 18:50:42 +02:00
parent 1954ea346d
commit 96fb8242cc
2 changed files with 56 additions and 27 deletions

View File

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

View File

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