From 96fb8242cc1ef6b0e28f6c86a4f57950095dd7f1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering 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 5c4bd6569..e584a1f00 100644 --- a/man/systemd.service.xml +++ b/man/systemd.service.xml @@ -139,9 +139,10 @@ If set to (the default - value if neither + if neither Type= nor - BusName= are + BusName=, but + ExecStart= are specified), it is expected that the process configured with ExecStart= is the @@ -177,13 +178,17 @@ exits. Behavior of - is similar - to ; however, - it is expected that the process has to + is similar to + ; however, it + is expected that the process has to exit before systemd starts follow-up units. RemainAfterExit= is particularly useful for this type - of service. + of service. This is the implied + default if neither + Type= or + ExecStart= are + specified. Behavior of is similar to @@ -313,22 +318,27 @@ When Type is not , only one - command may be given. When + command may and must be given. When Type=oneshot 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 \;. 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. + have no effect. If no + ExecStart= is + specified, then the service must have + RemainAfterExit=yes + set. 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 7d6ea73e0..1b864c4c8 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, -- 2.30.2