service.c revision 13b84ec7df103ce388910a2b868fe1668c1e27ef
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen This file is part of systemd.
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen Copyright 2010 Lennart Poettering
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen systemd is free software; you can redistribute it and/or modify it
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen under the terms of the GNU Lesser General Public License as published by
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen the Free Software Foundation; either version 2.1 of the License, or
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen (at your option) any later version.
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen systemd is distributed in the hope that it will be useful, but
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen Lesser General Public License for more details.
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen You should have received a copy of the GNU Lesser General Public License
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#define DEFAULT_SYSV_TIMEOUT_USEC (5*USEC_PER_MINUTE)
ed942a9eb22d50f667909ad6184b45015d28d054Tom Gundersenstatic const struct {
ed942a9eb22d50f667909ad6184b45015d28d054Tom Gundersen /* Standard SysV runlevels for start-up */
ed942a9eb22d50f667909ad6184b45015d28d054Tom Gundersen { "rc1.d", SPECIAL_RESCUE_TARGET, RUNLEVEL_UP },
bd8f65387673e29f46136a4ed172097035002c23Tom Gundersen { "rc2.d", SPECIAL_RUNLEVEL2_TARGET, RUNLEVEL_UP },
bd8f65387673e29f46136a4ed172097035002c23Tom Gundersen { "rc3.d", SPECIAL_RUNLEVEL3_TARGET, RUNLEVEL_UP },
bd8f65387673e29f46136a4ed172097035002c23Tom Gundersen { "rc4.d", SPECIAL_RUNLEVEL4_TARGET, RUNLEVEL_UP },
bd8f65387673e29f46136a4ed172097035002c23Tom Gundersen { "rc5.d", SPECIAL_RUNLEVEL5_TARGET, RUNLEVEL_UP },
bd8f65387673e29f46136a4ed172097035002c23Tom Gundersen /* Standard SysV runlevels for shutdown */
bd8f65387673e29f46136a4ed172097035002c23Tom Gundersen { "rc0.d", SPECIAL_POWEROFF_TARGET, RUNLEVEL_DOWN },
bd8f65387673e29f46136a4ed172097035002c23Tom Gundersen { "rc6.d", SPECIAL_REBOOT_TARGET, RUNLEVEL_DOWN }
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen /* Note that the order here matters, as we read the
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen directories in this order, and we want to make sure that
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen sysv_start_priority is known when we first load the
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen unit. And that value we only know from S links. Hence
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen UP must be read before DOWN */
edb85f0d8d0a84f27308a3728f3fde3c52b9dce2Susant Sahanistatic const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
eb27aeca247a4cf8816fffc4c0dbcab55ead3864Tom Gundersen/* For Type=idle we never want to delay any other jobs, hence we
e1ea665edac17d75fce01b72dadfa3211b60df2cEugene Yakubovich * consider idle jobs active as soon as we start working on them */
84b5b79a8f7b423c5b7cad4170eb68d57fe5e26cAngus Gibsonstatic const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] = {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen RATELIMIT_INIT(s->start_limit, 10*USEC_PER_SEC, 5);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
0dd25fb9f005d8ab7ac4bc10a609d00569f8c56aLennart Poetteringstatic void service_unwatch_control_pid(Service *s) {
46b0c76e2c355c0d0cc4792abb98cde07b28bc53Emil Renner Berthingstatic void service_unwatch_main_pid(Service *s) {
11bf3cced13c885ca215c108cb0bdb7a148520d6Lennart Poetteringstatic void service_unwatch_pid_file(Service *s) {
11bf3cced13c885ca215c108cb0bdb7a148520d6Lennart Poettering log_debug_unit(UNIT(s)->id, "Stopping watch for %s's PID file %s",
11bf3cced13c885ca215c108cb0bdb7a148520d6Lennart Poettering UNIT(s)->id, s->pid_file_pathspec->path);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen path_spec_unwatch(s->pid_file_pathspec, UNIT(s));
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersenstatic int service_set_main_pid(Service *s, pid_t pid) {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen if (get_parent_of_pid(pid, &ppid) >= 0 && ppid != getpid()) {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen "%s: Supervising process %lu which is not our child. We'll most likely not notice when it exits.",
3bef724f7e7f7eaca69881548b06e221b77d7031Tom Gundersenstatic void service_close_socket_fd(Service *s) {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersenstatic void service_connection_unref(Service *s) {
505f8da7325591defe5f751f328bd26915267602Tom Gundersen socket_connection_unref(SOCKET(UNIT_DEREF(s->accept_socket)));
69a93e7db47addc4495a6ec9dc0fc74092a6ebeeTom Gundersenstatic void service_stop_watchdog(Service *s) {
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen unit_unwatch_timer(UNIT(s), &s->watchdog_watch);
6192b846ca0d15602e94ddb5da4420b7c60d64a5Tom Gundersenstatic void service_enter_signal(Service *s, ServiceState state, ServiceResult f);
6192b846ca0d15602e94ddb5da4420b7c60d64a5Tom Gundersenstatic void service_handle_watchdog(Service *s) {
7951dea20911969287878e6897b3eca348721adeSusant Sahani offset = now(CLOCK_MONOTONIC) - s->watchdog_timestamp.monotonic;
7951dea20911969287878e6897b3eca348721adeSusant Sahani log_error_unit(UNIT(s)->id, "%s watchdog timeout!", UNIT(s)->id);
7951dea20911969287878e6897b3eca348721adeSusant Sahani service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_FAILURE_WATCHDOG);
7951dea20911969287878e6897b3eca348721adeSusant Sahani r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->watchdog_usec - offset, &s->watchdog_watch);
7951dea20911969287878e6897b3eca348721adeSusant Sahani "%s failed to install watchdog timer: %s",
7951dea20911969287878e6897b3eca348721adeSusant Sahanistatic void service_reset_watchdog(Service *s) {
5c1d3fc93d91384bbac29adf01074fa4375317eaUmut Tezduyar Lindskog cgroup_context_done(&s->cgroup_context);
5c1d3fc93d91384bbac29adf01074fa4375317eaUmut Tezduyar Lindskog exec_context_done(&s->exec_context, manager_is_reloading_or_reexecuting(u->manager));
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen exec_command_free_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX);
5d8e593dce074bff966fc0a46579c61b4f3bc33aSusant Sahani /* This will leak a process, but at least no memory or any of
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen * our resources */
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersenstatic char *sysv_translate_name(const char *name) {
ed942a9eb22d50f667909ad6184b45015d28d054Tom Gundersen r = new(char, strlen(name) + sizeof(".service"));
ed942a9eb22d50f667909ad6184b45015d28d054Tom Gundersen /* Drop .sh suffix */
bd8f65387673e29f46136a4ed172097035002c23Tom Gundersen /* Normal init script name */
bd8f65387673e29f46136a4ed172097035002c23Tom Gundersenstatic int sysv_translate_facility(const char *name, const char *filename, char **_r) {
bd8f65387673e29f46136a4ed172097035002c23Tom Gundersen /* We silently ignore the $ prefix here. According to the LSB
11bf3cced13c885ca215c108cb0bdb7a148520d6Lennart Poettering * spec it simply indicates whether something is a
11bf3cced13c885ca215c108cb0bdb7a148520d6Lennart Poettering * standardized name or a distribution-specific one. Since we
0dd25fb9f005d8ab7ac4bc10a609d00569f8c56aLennart Poettering * just follow what already exists and do not introduce new
0dd25fb9f005d8ab7ac4bc10a609d00569f8c56aLennart Poettering * uses or names we don't care who introduced a new name. */
11bf3cced13c885ca215c108cb0bdb7a148520d6Lennart Poettering static const char * const table[] = {
11bf3cced13c885ca215c108cb0bdb7a148520d6Lennart Poettering /* LSB defined facilities */
return log_oom();
goto finish;
if (!unit_prefix_is_valid(n))
return -EINVAL;
r = sysv_translate_name(n);
return -ENOMEM;
*_r = r;
assert(s);
if (s->sysv_start_priority < 0)
Service *t;
if (t->sysv_start_priority < 0)
d = UNIT_AFTER;
d = UNIT_BEFORE;
d = UNIT_AFTER;
d = UNIT_BEFORE;
ExecCommand *c;
return NULL;
free(c);
return NULL;
free(c);
return NULL;
ExecCommand *c;
assert(s);
return -ENOMEM;
return -ENOMEM;
if (supports_reload) {
return -ENOMEM;
FILE *f;
Unit *u;
unsigned line = 0;
LSB,
char *short_description = NULL, *long_description = NULL, *chkconfig_description = NULL, *description;
bool supports_reload = false;
assert(s);
u = UNIT(s);
goto finish;
r = -errno;
goto finish;
if (!u->source_path) {
r = -ENOMEM;
goto finish;
goto finish;
s->is_sysv = true;
while (!feof(f)) {
char l[LINE_MAX], *t;
if (!fgets(l, sizeof(l), f)) {
if (feof(f))
r = -errno;
goto finish;
line++;
t = strstrip(l);
if (usage_contains_reload(t)) {
supports_reload = true;
s->sysv_has_lsb = true;
int start_priority;
if (!(d = strdup(k))) {
r = -ENOMEM;
goto finish;
s->sysv_runlevels = d;
if (!(d = strdup(j))) {
r = -ENOMEM;
goto finish;
d = NULL;
char *fn;
r = -ENOMEM;
goto finish;
if ((j = strstrip(t)) && *j) {
char *d = NULL;
d = strdup(j);
r = -ENOMEM;
goto finish;
size_t z;
if (!(n = strndup(w, z))) {
r = -ENOMEM;
goto finish;
free(n);
goto finish;
r = unit_merge_by_name(u, m);
free(m);
size_t z;
if (!(n = strndup(w, z))) {
r = -ENOMEM;
goto finish;
free(n);
free(n);
r = unit_add_dependency_by_name(u, startswith_no_case(t, "X-Start-Before:") ? UNIT_BEFORE : UNIT_AFTER, m, NULL, true);
free(m);
if (!(d = strdup(k))) {
r = -ENOMEM;
goto finish;
s->sysv_runlevels = d;
if (!(d = strdup(j))) {
r = -ENOMEM;
goto finish;
d = NULL;
long_description = d;
if (!(d = strdup(j))) {
r = -ENOMEM;
goto finish;
d = NULL;
short_description = d;
if ((j = strstrip(t)) && *j) {
char *d = NULL;
if (long_description)
d = strdup(j);
r = -ENOMEM;
goto finish;
long_description = d;
goto finish;
s->timeout_start_usec = 0;
s->timeout_stop_usec = 0;
s->guess_main_pid = false;
if (short_description)
else if (chkconfig_description)
else if (long_description)
if (description) {
r = -ENOMEM;
goto finish;
u->description = d;
if (s->sysv_start_priority_from_rcnd >= 0)
fclose(f);
assert(s);
return -ENOENT;
char *path;
if (!path)
return -ENOMEM;
Iterator i;
assert(s);
if ((r = service_load_sysv_name(s, t)) < 0)
if ((r = service_load_sysv_name(s, t)) < 0)
assert(s);
if (s->fsck_passno <= 0)
Service *t;
if (t->fsck_passno <= 0)
d = UNIT_AFTER;
d = UNIT_BEFORE;
assert(s);
return -EINVAL;
"%s has more than one ExecStart setting, which is only allowed for Type=oneshot services. Refusing.", UNIT(s)->id);
return -EINVAL;
"%s has Restart setting other than no, which isn't allowed for Type=oneshot services. Refusing.", UNIT(s)->id);
return -EINVAL;
return -EINVAL;
return -EINVAL;
assert(s);
assert(s);
assert(s);
r = unit_load_fragment(u);
#ifdef HAVE_SYSV_COMPAT
r = service_load_sysv(s);
return -ENOENT;
r = unit_load_dropin(u);
s->timeout_start_usec = 0;
r = unit_add_default_slice(u);
#ifdef HAVE_SYSV_COMPAT
r = sysv_fix_order(s);
r = fsck_fix_order(s);
if (s->bus_name)
r = service_add_default_dependencies(s);
return service_verify(s);
const char *prefix2;
assert(s);
fprintf(f,
if (s->control_pid > 0)
fprintf(f,
if (s->main_pid > 0)
fprintf(f,
if (s->pid_file)
fprintf(f,
if (s->bus_name)
fprintf(f,
for (c = 0; c < _SERVICE_EXEC_COMMAND_MAX; c++) {
if (!s->exec_command[c])
#ifdef HAVE_SYSV_COMPAT
if (s->is_sysv)
fprintf(f,
if (s->sysv_start_priority >= 0)
fprintf(f,
if (s->sysv_runlevels)
if (s->fsck_passno > 0)
fprintf(f,
if (s->status_text)
assert(s);
if (!s->pid_file)
return -ENOENT;
if (may_warn)
if (may_warn)
if (may_warn)
return -ESRCH;
if (s->main_pid_known) {
s->main_pid_known = false;
assert(s);
if (s->main_pid_known)
if (!s->guess_main_pid)
if (pid <= 0)
return -ENOENT;
assert(s);
assert(s);
usec_t k;
if (s->main_pid > 0) {
if (s->control_pid > 0) {
Iterator i;
unsigned rn_fds = 0;
Unit *u;
assert(s);
if (s->socket_fd >= 0)
int *cfds;
unsigned cn_fds;
goto fail;
if (!cfds)
if (!rfds) {
r = -ENOMEM;
goto fail;
rfds = t;
fail:
static int service_spawn(
Service *s,
ExecCommand *c,
bool timeout,
bool pass_fds,
bool apply_permissions,
bool apply_chroot,
bool apply_tty_stdin,
bool set_notify_socket,
bool is_control,
_cleanup_strv_free_ char
const char *path;
assert(s);
assert(c);
if (pass_fds ||
if (s->socket_fd >= 0) {
goto fail;
goto fail;
goto fail;
if (!our_env) {
r = -ENOMEM;
goto fail;
if (set_notify_socket)
r = -ENOMEM;
goto fail;
if (s->main_pid > 0)
r = -ENOMEM;
goto fail;
if (s->watchdog_usec > 0)
r = -ENOMEM;
goto fail;
r = -ENOMEM;
goto fail;
if (!final_env) {
r = -ENOMEM;
goto fail;
r = exec_spawn(c,
argv,
&s->exec_context,
path,
&pid);
goto fail;
goto fail;
fail:
if (timeout)
assert(s);
if (s->main_pid_known) {
return s->main_pid > 0;
return -EAGAIN;
assert(s);
return s->control_pid > 0;
assert(s);
assert(s);
if (f != SERVICE_SUCCESS)
s->result = f;
if (allow_restart &&
!s->forbid_restart &&
goto fail;
s->forbid_restart = false;
if (s->pid_file)
fail:
assert(s);
if (f != SERVICE_SUCCESS)
s->result = f;
if (s->control_command) {
r = service_spawn(s,
s->control_command,
&s->control_pid);
goto fail;
fail:
assert(s);
if (f != SERVICE_SUCCESS)
s->result = f;
r = unit_kill_context(
UNIT(s),
&s->kill_context,
s->main_pid,
s->control_pid,
s->main_pid_alien);
goto fail;
if (s->timeout_stop_usec > 0) {
goto fail;
fail:
assert(s);
if (f != SERVICE_SUCCESS)
s->result = f;
if (s->control_command) {
r = service_spawn(s,
s->control_command,
&s->control_pid);
goto fail;
fail:
assert(s);
if (f != SERVICE_SUCCESS)
s->result = f;
else if (s->remain_after_exit)
assert(s);
if (s->watchdog_usec > 0)
if (s->control_command) {
r = service_spawn(s,
s->control_command,
&s->control_pid);
goto fail;
fail:
ExecCommand *c;
assert(s);
r = service_spawn(s,
&pid);
goto fail;
fail:
assert(s);
if (s->control_command) {
r = service_spawn(s,
s->control_command,
&s->control_pid);
goto fail;
fail:
assert(s);
goto fail;
goto fail;
fail:
assert(s);
if (s->control_command) {
r = service_spawn(s,
s->control_command,
&s->control_pid);
goto fail;
fail:
assert(s);
r = service_spawn(s,
s->control_command,
&s->control_pid);
goto fail;
fail:
assert(s);
r = service_spawn(s,
s->main_command,
&pid);
goto fail;
fail:
assert(s);
switch (s->start_limit_action) {
case SERVICE_START_LIMIT_NONE:
case SERVICE_START_LIMIT_REBOOT: {
sync();
return -ECANCELED;
assert(s);
return -EAGAIN;
return -EAGAIN;
r = service_start_limit_test(s);
s->main_pid_known = false;
s->main_pid_alien = false;
s->forbid_restart = false;
assert(s);
s->forbid_restart = true;
assert(s);
assert(s);
assert(u);
assert(f);
if (s->control_pid > 0)
(unsigned long) s->control_pid);
if (s->status_text)
if (s->control_command_id >= 0)
if (s->socket_fd >= 0) {
int copy;
return copy;
&s->watchdog_timestamp);
assert(u);
if (state < 0)
else if (f != SERVICE_SUCCESS)
s->result = f;
else if (f != SERVICE_SUCCESS)
s->reload_result = f;
s->main_pid_known = b;
log_oom();
s->status_text = t;
if (id < 0)
int fd;
if (s->socket_fd >= 0)
return log_oom();
return log_oom();
assert(u);
assert(u);
assert(s);
if (cgroup_good(s) > 0 ||
main_pid_good(s) > 0 ||
control_pid_good(s) > 0)
#ifdef HAVE_SYSV_COMPAT
if (s->is_sysv)
assert(s);
return !s->got_socket_fd;
r = service_load_pid_file(s, false);
goto fail;
fail:
if (!ps)
return -ENOMEM;
return -ENOMEM;
return service_watch_pid_file(s);
assert(s);
goto fail;
if (service_retry_pid_file(s) == 0)
if (service_watch_pid_file(s) < 0)
goto fail;
fail:
assert(s);
f = SERVICE_SUCCESS;
if (service_load_pid_file(s, false) == 0)
s->main_pid = 0;
if (s->main_command) {
f = SERVICE_SUCCESS;
f = SERVICE_SUCCESS;
u->id,
NULL);
if (f != SERVICE_SUCCESS)
s->result = f;
if (s->main_command &&
f == SERVICE_SUCCESS) {
switch (s->state) {
case SERVICE_START_POST:
case SERVICE_RELOAD:
case SERVICE_STOP:
case SERVICE_START:
if (f == SERVICE_SUCCESS)
case SERVICE_RUNNING:
service_enter_running(s, f);
case SERVICE_STOP_SIGTERM:
case SERVICE_STOP_SIGKILL:
if (!control_pid_good(s))
service_enter_stop_post(s, f);
s->control_pid = 0;
if (s->control_command) {
f = SERVICE_SUCCESS;
if (f != SERVICE_SUCCESS)
s->result = f;
if (s->control_command &&
f == SERVICE_SUCCESS) {
switch (s->state) {
case SERVICE_START_PRE:
if (f == SERVICE_SUCCESS)
case SERVICE_START:
if (f != SERVICE_SUCCESS) {
if (s->pid_file) {
bool has_start_post;
if (!has_start_post && r < 0) {
r = service_demand_pid_file(s);
if (r < 0 || !cgroup_good(s))
case SERVICE_START_POST:
if (f != SERVICE_SUCCESS) {
service_enter_stop(s, f);
if (s->pid_file) {
r = service_load_pid_file(s, true);
r = service_demand_pid_file(s);
if (r < 0 || !cgroup_good(s))
case SERVICE_RELOAD:
if (f == SERVICE_SUCCESS) {
service_load_pid_file(s, true);
s->reload_result = f;
case SERVICE_STOP:
case SERVICE_STOP_SIGTERM:
case SERVICE_STOP_SIGKILL:
if (main_pid_good(s) <= 0)
service_enter_stop_post(s, f);
case SERVICE_STOP_POST:
case SERVICE_FINAL_SIGTERM:
case SERVICE_FINAL_SIGKILL:
service_enter_dead(s, f, true);
assert(s);
if (w == &s->watchdog_watch) {
switch (s->state) {
case SERVICE_START_PRE:
case SERVICE_START:
case SERVICE_START_POST:
case SERVICE_RELOAD:
case SERVICE_STOP:
case SERVICE_STOP_SIGTERM:
case SERVICE_STOP_SIGKILL:
case SERVICE_STOP_POST:
case SERVICE_FINAL_SIGTERM:
u->id);
case SERVICE_FINAL_SIGKILL:
case SERVICE_AUTO_RESTART:
assert(u);
switch (s->state) {
case SERVICE_START:
case SERVICE_START_POST:
if (s->pid_file_pathspec) {
case SERVICE_RUNNING:
case SERVICE_STOP_SIGTERM:
case SERVICE_STOP_SIGKILL:
case SERVICE_FINAL_SIGTERM:
case SERVICE_FINAL_SIGKILL:
assert(u);
s->status_text = t;
#ifdef HAVE_SYSV_COMPAT
Iterator j;
assert(m);
if (!path) {
r = -ENOMEM;
goto finish;
closedir(d);
if (!fpath) {
r = -ENOMEM;
goto finish;
if (!name) {
r = log_oom();
goto finish;
goto finish;
goto finish;
goto finish;
goto finish;
goto finish;
goto finish;
static void service_bus_name_owner_change(
Unit *u,
const char *name,
const char *old_owner,
const char *new_owner) {
assert(s);
else if (old_owner)
} else if (new_owner &&
s->main_pid <= 0 &&
static void service_bus_query_pid_done(
Unit *u,
const char *name,
assert(s);
if (s->main_pid <= 0 &&
assert(s);
return -EINVAL;
if (s->socket_fd >= 0)
return -EBUSY;
return -EAGAIN;
s->got_socket_fd = true;
assert(s);
.sections =
.can_transient = true,
#ifdef HAVE_SYSV_COMPAT
.starting_stopping = {
.finished_start_job = {
.finished_stop_job = {