X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Fservice.c;h=f540752b61f6d044d594514fe3a8138cf2dc3825;hp=bf2e0a9d98f99979b8881f6209a20f3727635b72;hb=96342de68d0d6de71a062d984dafd2a0905ed9fe;hpb=b30e2f4c18ad81b04e4314fd191a5d458553773c diff --git a/src/core/service.c b/src/core/service.c index bf2e0a9d9..f540752b6 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -6,16 +6,16 @@ Copyright 2010 Lennart Poettering systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. systemd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. + Lesser General Public License for more details. - You should have received a copy of the GNU General Public License + You should have received a copy of the GNU Lesser General Public License along with systemd; If not, see . ***/ @@ -38,6 +38,7 @@ #include "bus-errors.h" #include "exit-status.h" #include "def.h" +#include "path-util.h" #include "util.h" #include "utf8.h" @@ -107,14 +108,36 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = { [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING }; +/* For Type=idle we never want to delay any other jobs, hence we + * consider idle jobs active as soon as we start working on them */ +static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] = { + [SERVICE_DEAD] = UNIT_INACTIVE, + [SERVICE_START_PRE] = UNIT_ACTIVE, + [SERVICE_START] = UNIT_ACTIVE, + [SERVICE_START_POST] = UNIT_ACTIVE, + [SERVICE_RUNNING] = UNIT_ACTIVE, + [SERVICE_EXITED] = UNIT_ACTIVE, + [SERVICE_RELOAD] = UNIT_RELOADING, + [SERVICE_STOP] = UNIT_DEACTIVATING, + [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING, + [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING, + [SERVICE_STOP_POST] = UNIT_DEACTIVATING, + [SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING, + [SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING, + [SERVICE_FAILED] = UNIT_FAILED, + [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING +}; + static void service_init(Unit *u) { Service *s = SERVICE(u); assert(u); assert(u->load_state == UNIT_STUB); - s->timeout_usec = DEFAULT_TIMEOUT_USEC; + s->timeout_start_usec = DEFAULT_TIMEOUT_USEC; + s->timeout_stop_usec = DEFAULT_TIMEOUT_USEC; s->restart_usec = DEFAULT_RESTART_USEC; + s->type = _SERVICE_TYPE_INVALID; s->watchdog_watch.type = WATCH_INVALID; @@ -127,6 +150,7 @@ static void service_init(Unit *u) { s->guess_main_pid = true; exec_context_init(&s->exec_context); + kill_context_init(&s->kill_context); RATELIMIT_INIT(s->start_limit, 10*USEC_PER_SEC, 5); @@ -258,9 +282,6 @@ static void service_done(Unit *u) { s->pid_file = NULL; #ifdef HAVE_SYSV_COMPAT - free(s->sysv_path); - s->sysv_path = NULL; - free(s->sysv_runlevels); s->sysv_runlevels = NULL; #endif @@ -273,6 +294,16 @@ static void service_done(Unit *u) { s->control_command = NULL; s->main_command = NULL; + set_free(s->restart_ignore_status.code); + s->restart_ignore_status.code = NULL; + set_free(s->restart_ignore_status.signal); + s->restart_ignore_status.signal = NULL; + + set_free(s->success_status.code); + s->success_status.code = NULL; + set_free(s->success_status.signal); + s->success_status.signal = NULL; + /* This will leak a process, but at least no memory or any of * our resources */ service_unwatch_main_pid(s); @@ -498,17 +529,21 @@ static int sysv_exec_commands(Service *s) { ExecCommand *c; assert(s); - assert(s->sysv_path); + assert(s->is_sysv); + assert(UNIT(s)->source_path); - if (!(c = exec_command_new(s->sysv_path, "start"))) + c = exec_command_new(UNIT(s)->source_path, "start"); + if (!c) return -ENOMEM; exec_command_append_list(s->exec_command+SERVICE_EXEC_START, c); - if (!(c = exec_command_new(s->sysv_path, "stop"))) + c = exec_command_new(UNIT(s)->source_path, "stop"); + if (!c) return -ENOMEM; exec_command_append_list(s->exec_command+SERVICE_EXEC_STOP, c); - if (!(c = exec_command_new(s->sysv_path, "reload"))) + c = exec_command_new(UNIT(s)->source_path, "reload"); + if (!c) return -ENOMEM; exec_command_append_list(s->exec_command+SERVICE_EXEC_RELOAD, c); @@ -534,24 +569,24 @@ static int service_load_sysv_path(Service *s, const char *path) { u = UNIT(s); - if (!(f = fopen(path, "re"))) { + f = fopen(path, "re"); + if (!f) { r = errno == ENOENT ? 0 : -errno; goto finish; } - zero(st); if (fstat(fileno(f), &st) < 0) { r = -errno; goto finish; } - free(s->sysv_path); - if (!(s->sysv_path = strdup(path))) { + free(u->source_path); + u->source_path = strdup(path); + if (!u->source_path) { r = -ENOMEM; goto finish; } - - s->sysv_mtime = timespec_load(&st.st_mtim); + u->source_mtime = timespec_load(&st.st_mtim); if (null_or_empty(&st)) { u->load_state = UNIT_MASKED; @@ -559,6 +594,8 @@ static int service_load_sysv_path(Service *s, const char *path) { goto finish; } + s->is_sysv = true; + while (!feof(f)) { char l[LINE_MAX], *t; @@ -692,7 +729,7 @@ static int service_load_sysv_path(Service *s, const char *path) { char *d = NULL; if (chkconfig_description) - d = join(chkconfig_description, " ", j, NULL); + d = strjoin(chkconfig_description, " ", j, NULL); else d = strdup(j); @@ -721,7 +758,7 @@ static int service_load_sysv_path(Service *s, const char *path) { goto finish; } - r = sysv_translate_facility(n, file_name_from_path(path), &m); + r = sysv_translate_facility(n, path_get_file_name(path), &m); free(n); if (r < 0) @@ -771,7 +808,7 @@ static int service_load_sysv_path(Service *s, const char *path) { goto finish; } - r = sysv_translate_facility(n, file_name_from_path(path), &m); + r = sysv_translate_facility(n, path_get_file_name(path), &m); if (r < 0) { log_error("[%s:%u] Failed to translate LSB dependency %s, ignoring: %s", path, line, n, strerror(-r)); @@ -849,7 +886,7 @@ static int service_load_sysv_path(Service *s, const char *path) { char *d = NULL; if (long_description) - d = join(long_description, " ", t, NULL); + d = strjoin(long_description, " ", t, NULL); else d = strdup(j); @@ -888,9 +925,13 @@ static int service_load_sysv_path(Service *s, const char *path) { UNIT(s)->default_dependencies = false; /* Don't timeout special services during boot (like fsck) */ - s->timeout_usec = 0; - } else - s->timeout_usec = DEFAULT_SYSV_TIMEOUT_USEC; + s->timeout_start_usec = 0; + s->timeout_stop_usec = 0; + } else { + s->timeout_start_usec = DEFAULT_SYSV_TIMEOUT_USEC; + s->timeout_stop_usec = DEFAULT_SYSV_TIMEOUT_USEC; + } + /* Special setting for all SysV services */ s->type = SERVICE_FORKING; @@ -898,11 +939,7 @@ static int service_load_sysv_path(Service *s, const char *path) { s->guess_main_pid = false; s->restart = SERVICE_RESTART_NO; s->exec_context.ignore_sigpipe = false; - - if (UNIT(s)->manager->sysv_console) - s->exec_context.std_output = EXEC_OUTPUT_JOURNAL_AND_CONSOLE; - - s->exec_context.kill_mode = KILL_PROCESS; + s->kill_context.kill_mode = KILL_PROCESS; /* We use the long description only if * no short description is set. */ @@ -975,7 +1012,7 @@ static int service_load_sysv_name(Service *s, const char *name) { char *path; int r; - path = join(*p, "/", name, NULL); + path = strjoin(*p, "/", name, NULL); if (!path) return -ENOMEM; @@ -997,7 +1034,7 @@ static int service_load_sysv_name(Service *s, const char *name) { if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) { /* Try SUSE style boot.* init scripts */ - path = join(*p, "/boot.", name, NULL); + path = strjoin(*p, "/boot.", name, NULL); if (!path) return -ENOMEM; @@ -1012,7 +1049,7 @@ static int service_load_sysv_name(Service *s, const char *name) { if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) { /* Try Frugalware style rc.* init scripts */ - path = join(*p, "/rc.", name, NULL); + path = strjoin(*p, "/rc.", name, NULL); if (!path) return -ENOMEM; @@ -1135,7 +1172,10 @@ static int service_verify(Service *s) { return -EINVAL; } - if (s->exec_context.pam_name && s->exec_context.kill_mode != KILL_CONTROL_GROUP) { + if (s->bus_name && s->type != SERVICE_DBUS) + log_warning("%s has a D-Bus service name specified, but is not of type dbus. Ignoring.", UNIT(s)->id); + + if (s->exec_context.pam_name && s->kill_context.kill_mode != KILL_CONTROL_GROUP) { log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(s)->id); return -EINVAL; } @@ -1213,6 +1253,13 @@ static int service_load(Unit *u) { /* This is a new unit? Then let's add in some extras */ if (u->load_state == UNIT_LOADED) { + if (s->type == _SERVICE_TYPE_INVALID) + s->type = s->bus_name ? SERVICE_DBUS : SERVICE_SIMPLE; + + /* Oneshot services have disabled start timeout by default */ + if (s->type == SERVICE_ONESHOT && !s->start_timeout_defined) + s->timeout_start_usec = 0; + service_fix_output(s); if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0) @@ -1246,6 +1293,10 @@ static int service_load(Unit *u) { if (UNIT(s)->default_dependencies) if ((r = service_add_default_dependencies(s)) < 0) return r; + + r = unit_exec_context_defaults(u, &s->exec_context); + if (r < 0) + return r; } return service_verify(s); @@ -1311,6 +1362,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { prefix, s->bus_name, prefix, yes_no(s->bus_name_good)); + kill_context_dump(&s->kill_context, f, prefix); exec_context_dump(&s->exec_context, f, prefix); for (c = 0; c < _SERVICE_EXEC_COMMAND_MAX; c++) { @@ -1325,12 +1377,10 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { } #ifdef HAVE_SYSV_COMPAT - if (s->sysv_path) + if (s->is_sysv) fprintf(f, - "%sSysV Init Script Path: %s\n" "%sSysV Init Script has LSB Header: %s\n" "%sSysVEnabled: %s\n", - prefix, s->sysv_path, prefix, yes_no(s->sysv_has_lsb), prefix, yes_no(s->sysv_enabled)); @@ -1457,8 +1507,11 @@ static void service_notify_sockets_dead(Service *s, bool failed_permanent) { static void service_set_state(Service *s, ServiceState state) { ServiceState old_state; + const UnitActiveState *table; assert(s); + table = s->type == SERVICE_IDLE ? state_translation_table_idle : state_translation_table; + old_state = s->state; s->state = state; @@ -1541,7 +1594,7 @@ static void service_set_state(Service *s, ServiceState state) { if (old_state != state) log_debug("%s changed %s -> %s", UNIT(s)->id, service_state_to_string(old_state), service_state_to_string(state)); - unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], s->reload_result == SERVICE_SUCCESS); + unit_notify(UNIT(s), table[old_state], table[state], s->reload_result == SERVICE_SUCCESS); s->reload_result = SERVICE_SUCCESS; } @@ -1565,11 +1618,10 @@ static int service_coldplug(Unit *u) { s->deserialized_state == SERVICE_FINAL_SIGTERM || s->deserialized_state == SERVICE_FINAL_SIGKILL || s->deserialized_state == SERVICE_AUTO_RESTART) { - - if (s->deserialized_state == SERVICE_AUTO_RESTART || s->timeout_usec > 0) { + if (s->deserialized_state == SERVICE_AUTO_RESTART || s->timeout_start_usec > 0) { usec_t k; - k = s->deserialized_state == SERVICE_AUTO_RESTART ? s->restart_usec : s->timeout_usec; + k = s->deserialized_state == SERVICE_AUTO_RESTART ? s->restart_usec : s->timeout_start_usec; if ((r = unit_watch_timer(UNIT(s), k, &s->timer_watch)) < 0) return r; @@ -1686,6 +1738,7 @@ static int service_spawn( bool apply_chroot, bool apply_tty_stdin, bool set_notify_socket, + bool is_control, pid_t *_pid) { pid_t pid; @@ -1714,8 +1767,9 @@ static int service_spawn( } } - if (timeout && s->timeout_usec) { - if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0) + if (timeout && s->timeout_start_usec) { + r = unit_watch_timer(UNIT(s), s->timeout_start_usec, &s->timer_watch); + if (r < 0) goto fail; } else unit_unwatch_timer(UNIT(s), &s->timer_watch); @@ -1767,6 +1821,9 @@ static int service_spawn( UNIT(s)->manager->confirm_spawn, UNIT(s)->cgroup_bondings, UNIT(s)->cgroup_attributes, + is_control ? "control" : NULL, + UNIT(s)->id, + s->type == SERVICE_IDLE ? UNIT(s)->manager->idle_pipe : NULL, &pid); if (r < 0) @@ -1847,21 +1904,27 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) if (f != SERVICE_SUCCESS) s->result = f; + service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD); + if (allow_restart && !s->forbid_restart && (s->restart == SERVICE_RESTART_ALWAYS || (s->restart == SERVICE_RESTART_ON_SUCCESS && s->result == SERVICE_SUCCESS) || (s->restart == SERVICE_RESTART_ON_FAILURE && s->result != SERVICE_SUCCESS) || (s->restart == SERVICE_RESTART_ON_ABORT && (s->result == SERVICE_FAILURE_SIGNAL || - s->result == SERVICE_FAILURE_CORE_DUMP)))) { + s->result == SERVICE_FAILURE_CORE_DUMP))) && + (s->result != SERVICE_FAILURE_EXIT_CODE || + !set_contains(s->restart_ignore_status.code, INT_TO_PTR(s->main_exec_status.status))) && + (s->result != SERVICE_FAILURE_SIGNAL || + !set_contains(s->restart_ignore_status.signal, INT_TO_PTR(s->main_exec_status.status))) + ) { r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch); if (r < 0) goto fail; service_set_state(s, SERVICE_AUTO_RESTART); - } else - service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD); + } s->forbid_restart = false; @@ -1886,15 +1949,17 @@ static void service_enter_stop_post(Service *s, ServiceResult f) { if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP_POST])) { s->control_command_id = SERVICE_EXEC_STOP_POST; - if ((r = service_spawn(s, - s->control_command, - true, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - true, - false, - &s->control_pid)) < 0) + r = service_spawn(s, + s->control_command, + true, + false, + !s->permissions_start_only, + !s->root_directory_start_only, + true, + false, + true, + &s->control_pid); + if (r < 0) goto fail; @@ -1919,8 +1984,8 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f if (f != SERVICE_SUCCESS) s->result = f; - if (s->exec_context.kill_mode != KILL_NONE) { - int sig = (state == SERVICE_STOP_SIGTERM || state == SERVICE_FINAL_SIGTERM) ? s->exec_context.kill_signal : SIGKILL; + if (s->kill_context.kill_mode != KILL_NONE) { + int sig = (state == SERVICE_STOP_SIGTERM || state == SERVICE_FINAL_SIGTERM) ? s->kill_context.kill_signal : SIGKILL; if (s->main_pid > 0) { if (kill_and_sigcont(s->main_pid, sig) < 0 && errno != ESRCH) @@ -1936,9 +2001,10 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f wait_for_exit = true; } - if (s->exec_context.kill_mode == KILL_CONTROL_GROUP) { + if (s->kill_context.kill_mode == KILL_CONTROL_GROUP) { - if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func))) { + pid_set = set_new(trivial_hash_func, trivial_compare_func); + if (!pid_set) { r = -ENOMEM; goto fail; } @@ -1952,7 +2018,8 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f if ((r = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0) goto fail; - if ((r = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, sig, true, pid_set)) < 0) { + r = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, sig, true, false, pid_set, NULL); + if (r < 0) { if (r != -EAGAIN && r != -ESRCH && r != -ENOENT) log_warning("Failed to kill control group: %s", strerror(-r)); } else if (r > 0) @@ -1964,9 +2031,11 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f } if (wait_for_exit) { - if (s->timeout_usec > 0) - if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0) + if (s->timeout_stop_usec > 0) { + r = unit_watch_timer(UNIT(s), s->timeout_stop_usec, &s->timer_watch); + if (r < 0) goto fail; + } service_set_state(s, state); } else if (state == SERVICE_STOP_SIGTERM || state == SERVICE_STOP_SIGKILL) @@ -2001,15 +2070,17 @@ static void service_enter_stop(Service *s, ServiceResult f) { if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP])) { s->control_command_id = SERVICE_EXEC_STOP; - if ((r = service_spawn(s, - s->control_command, - true, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - false, - false, - &s->control_pid)) < 0) + r = service_spawn(s, + s->control_command, + true, + false, + !s->permissions_start_only, + !s->root_directory_start_only, + false, + false, + true, + &s->control_pid); + if (r < 0) goto fail; service_set_state(s, SERVICE_STOP); @@ -2054,15 +2125,17 @@ static void service_enter_start_post(Service *s) { if ((s->control_command = s->exec_command[SERVICE_EXEC_START_POST])) { s->control_command_id = SERVICE_EXEC_START_POST; - if ((r = service_spawn(s, - s->control_command, - true, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - false, - false, - &s->control_pid)) < 0) + r = service_spawn(s, + s->control_command, + true, + false, + !s->permissions_start_only, + !s->root_directory_start_only, + false, + false, + true, + &s->control_pid); + if (r < 0) goto fail; service_set_state(s, SERVICE_START_POST); @@ -2094,7 +2167,7 @@ static void service_enter_start(Service *s) { /* We want to ensure that nobody leaks processes from * START_PRE here, so let's go on a killing spree, People * should not spawn long running processes from START_PRE. */ - cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, NULL); + cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, true, NULL, "control"); if (s->type == SERVICE_FORKING) { s->control_command_id = SERVICE_EXEC_START; @@ -2108,18 +2181,20 @@ static void service_enter_start(Service *s) { c = s->main_command = s->exec_command[SERVICE_EXEC_START]; } - if ((r = service_spawn(s, - c, - s->type == SERVICE_FORKING || s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY, - true, - true, - true, - true, - s->notify_access != NOTIFY_NONE, - &pid)) < 0) + r = service_spawn(s, + c, + s->type == SERVICE_FORKING || s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY || s->type == SERVICE_ONESHOT, + true, + true, + true, + true, + s->notify_access != NOTIFY_NONE, + false, + &pid); + if (r < 0) goto fail; - if (s->type == SERVICE_SIMPLE) { + if (s->type == SERVICE_SIMPLE || s->type == SERVICE_IDLE) { /* For simple services we immediately start * the START_POST binaries. */ @@ -2168,19 +2243,21 @@ static void service_enter_start_pre(Service *s) { /* Before we start anything, let's clear up what might * be left from previous runs. */ - cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, NULL); + cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, true, NULL, "control"); s->control_command_id = SERVICE_EXEC_START_PRE; - if ((r = service_spawn(s, - s->control_command, - true, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - true, - false, - &s->control_pid)) < 0) + r = service_spawn(s, + s->control_command, + true, + false, + !s->permissions_start_only, + !s->root_directory_start_only, + true, + false, + true, + &s->control_pid); + if (r < 0) goto fail; service_set_state(s, SERVICE_START_PRE); @@ -2201,11 +2278,15 @@ static void service_enter_restart(Service *s) { assert(s); dbus_error_init(&error); - if (UNIT(s)->job) { - log_info("Job pending for unit, delaying automatic restart."); + if (UNIT(s)->job && UNIT(s)->job->type == JOB_STOP) { + /* Don't restart things if we are going down anyway */ + log_info("Stop job pending for unit, delaying automatic restart."); - if ((r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch)) < 0) + r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch); + if (r < 0) goto fail; + + return; } /* Any units that are bound to this service must also be @@ -2216,6 +2297,10 @@ static void service_enter_restart(Service *s) { if (r < 0) goto fail; + /* Note that we stay in the SERVICE_AUTO_RESTART state here, + * it will be canceled as part of the service_stop() call that + * is executed as part of JOB_RESTART. */ + log_debug("%s scheduled restart job.", UNIT(s)->id); return; @@ -2236,15 +2321,17 @@ static void service_enter_reload(Service *s) { if ((s->control_command = s->exec_command[SERVICE_EXEC_RELOAD])) { s->control_command_id = SERVICE_EXEC_RELOAD; - if ((r = service_spawn(s, - s->control_command, - true, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - false, - false, - &s->control_pid)) < 0) + r = service_spawn(s, + s->control_command, + true, + false, + !s->permissions_start_only, + !s->root_directory_start_only, + false, + false, + true, + &s->control_pid); + if (r < 0) goto fail; service_set_state(s, SERVICE_RELOAD); @@ -2271,16 +2358,18 @@ static void service_run_next_control(Service *s) { s->control_command = s->control_command->command_next; service_unwatch_control_pid(s); - if ((r = service_spawn(s, - s->control_command, - true, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - s->control_command_id == SERVICE_EXEC_START_PRE || - s->control_command_id == SERVICE_EXEC_STOP_POST, - false, - &s->control_pid)) < 0) + r = service_spawn(s, + s->control_command, + true, + false, + !s->permissions_start_only, + !s->root_directory_start_only, + s->control_command_id == SERVICE_EXEC_START_PRE || + s->control_command_id == SERVICE_EXEC_STOP_POST, + false, + true, + &s->control_pid); + if (r < 0) goto fail; return; @@ -2313,15 +2402,17 @@ static void service_run_next_main(Service *s) { s->main_command = s->main_command->command_next; service_unwatch_main_pid(s); - if ((r = service_spawn(s, - s->main_command, - false, - true, - true, - true, - true, - s->notify_access != NOTIFY_NONE, - &pid)) < 0) + r = service_spawn(s, + s->main_command, + true, + true, + true, + true, + true, + s->notify_access != NOTIFY_NONE, + false, + &pid); + if (r < 0) goto fail; service_set_main_pid(s, pid); @@ -2402,7 +2493,17 @@ static int service_start(Unit *u) { s->state == SERVICE_START_POST) return 0; - assert(s->state == SERVICE_DEAD || s->state == SERVICE_FAILED || s->state == SERVICE_AUTO_RESTART); + /* A service that will be restarted must be stopped first to + * trigger BindsTo and/or OnFailure dependencies. If a user + * does not want to wait for the holdoff time to elapse, the + * service should be manually restarted, not started. We + * simply return EAGAIN here, so that any start jobs stay + * queued, and assume that the auto restart timer will + * eventually trigger the restart. */ + if (s->state == SERVICE_AUTO_RESTART) + return -EAGAIN; + + assert(s->state == SERVICE_DEAD || s->state == SERVICE_FAILED); /* Make sure we don't enter a busy loop of some kind. */ r = service_start_limit_test(s); @@ -2426,8 +2527,7 @@ static int service_stop(Unit *u) { assert(s); - /* This is a user request, so don't do restarts on this - * shutdown. */ + /* Don't create restart jobs from here. */ s->forbid_restart = true; /* Already on it */ @@ -2439,7 +2539,7 @@ static int service_stop(Unit *u) { s->state == SERVICE_FINAL_SIGKILL) return 0; - /* Don't allow a restart */ + /* A restart will be scheduled or is in progress. */ if (s->state == SERVICE_AUTO_RESTART) { service_set_state(s, SERVICE_DEAD); return 0; @@ -2650,9 +2750,13 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, } static UnitActiveState service_active_state(Unit *u) { + const UnitActiveState *table; + assert(u); - return state_translation_table[SERVICE(u)->state]; + table = SERVICE(u)->type == SERVICE_IDLE ? state_translation_table_idle : state_translation_table; + + return table[SERVICE(u)->state]; } static const char *service_sub_state_to_string(Unit *u) { @@ -2674,7 +2778,7 @@ static bool service_check_gc(Unit *u) { return true; #ifdef HAVE_SYSV_COMPAT - if (s->sysv_path) + if (s->is_sysv) return true; #endif @@ -2785,7 +2889,8 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { assert(s); assert(pid >= 0); - if (UNIT(s)->fragment_path ? is_clean_exit(code, status) : is_clean_exit_lsb(code, status)) + if (UNIT(s)->fragment_path ? is_clean_exit(code, status, &s->success_status) : + is_clean_exit_lsb(code, status, &s->success_status)) f = SERVICE_SUCCESS; else if (code == CLD_EXITED) f = SERVICE_FAILURE_EXIT_CODE; @@ -2897,6 +3002,11 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { if (f != SERVICE_SUCCESS) s->result = f; + /* Immediately get rid of the cgroup, so that the + * kernel doesn't delay the cgroup empty messages for + * the service cgroup any longer than necessary */ + cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, true, NULL, "control"); + if (s->control_command && s->control_command->command_next && f == SERVICE_SUCCESS) { @@ -3055,7 +3165,7 @@ static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) { break; case SERVICE_STOP_SIGTERM: - if (s->exec_context.send_sigkill) { + if (s->kill_context.send_sigkill) { log_warning("%s stopping timed out. Killing.", u->id); service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_FAILURE_TIMEOUT); } else { @@ -3080,7 +3190,7 @@ static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) { break; case SERVICE_FINAL_SIGTERM: - if (s->exec_context.send_sigkill) { + if (s->kill_context.send_sigkill) { log_warning("%s stopping timed out (2). Killing.", u->id); service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_FAILURE_TIMEOUT); } else { @@ -3150,7 +3260,7 @@ static void service_cgroup_notify_event(Unit *u) { case SERVICE_FINAL_SIGTERM: case SERVICE_FINAL_SIGKILL: if (main_pid_good(s) <= 0 && !control_pid_good(s)) - service_enter_dead(s, SERVICE_SUCCESS, SERVICE_SUCCESS); + service_enter_dead(s, SERVICE_SUCCESS, true); break; @@ -3329,7 +3439,7 @@ static int service_enumerate(Manager *m) { struct dirent *de; free(path); - path = join(*p, "/", rcnd_table[i].path, NULL); + path = strjoin(*p, "/", rcnd_table[i].path, NULL); if (!path) { r = -ENOMEM; goto finish; @@ -3364,7 +3474,7 @@ static int service_enumerate(Manager *m) { continue; free(fpath); - fpath = join(path, "/", de->d_name, NULL); + fpath = strjoin(path, "/", de->d_name, NULL); if (!fpath) { r = -ENOMEM; goto finish; @@ -3577,32 +3687,11 @@ static void service_reset_failed(Unit *u) { s->result = SERVICE_SUCCESS; s->reload_result = SERVICE_SUCCESS; -} -static bool service_need_daemon_reload(Unit *u) { - Service *s = SERVICE(u); - - assert(s); - -#ifdef HAVE_SYSV_COMPAT - if (s->sysv_path) { - struct stat st; - - zero(st); - if (stat(s->sysv_path, &st) < 0) - /* What, cannot access this anymore? */ - return true; - - if (s->sysv_mtime > 0 && - timespec_load(&st.st_mtim) != s->sysv_mtime) - return true; - } -#endif - - return false; + RATELIMIT_RESET(s->start_limit); } -static int service_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusError *error) { +static int service_kill(Unit *u, KillWho who, int signo, DBusError *error) { Service *s = SERVICE(u); int r = 0; Set *pid_set = NULL; @@ -3629,28 +3718,33 @@ static int service_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusErro if (kill(s->main_pid, signo) < 0) r = -errno; - if (who == KILL_ALL && mode == KILL_CONTROL_GROUP) { + if (who == KILL_ALL) { int q; - if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func))) + pid_set = set_new(trivial_hash_func, trivial_compare_func); + if (!pid_set) return -ENOMEM; /* Exclude the control/main pid from being killed via the cgroup */ - if (s->control_pid > 0) - if ((q = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0) { + if (s->control_pid > 0) { + q = set_put(pid_set, LONG_TO_PTR(s->control_pid)); + if (q < 0) { r = q; goto finish; } + } - if (s->main_pid > 0) - if ((q = set_put(pid_set, LONG_TO_PTR(s->main_pid))) < 0) { + if (s->main_pid > 0) { + q = set_put(pid_set, LONG_TO_PTR(s->main_pid)); + if (q < 0) { r = q; goto finish; } + } - if ((q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, pid_set)) < 0) - if (q != -EAGAIN && q != -ESRCH && q != -ENOENT) - r = q; + q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, false, pid_set, NULL); + if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT) + r = q; } finish: @@ -3695,7 +3789,8 @@ static const char* const service_type_table[_SERVICE_TYPE_MAX] = { [SERVICE_FORKING] = "forking", [SERVICE_ONESHOT] = "oneshot", [SERVICE_DBUS] = "dbus", - [SERVICE_NOTIFY] = "notify" + [SERVICE_NOTIFY] = "notify", + [SERVICE_IDLE] = "idle" }; DEFINE_STRING_TABLE_LOOKUP(service_type, ServiceType); @@ -3740,13 +3835,11 @@ static const char* const start_limit_action_table[_SERVICE_START_LIMIT_MAX] = { DEFINE_STRING_TABLE_LOOKUP(start_limit_action, StartLimitAction); const UnitVTable service_vtable = { - .suffix = ".service", .object_size = sizeof(Service), .sections = "Unit\0" "Service\0" "Install\0", - .show_status = true, .init = service_init, .done = service_done, @@ -3779,8 +3872,6 @@ const UnitVTable service_vtable = { .reset_failed = service_reset_failed, - .need_daemon_reload = service_need_daemon_reload, - .cgroup_notify_empty = service_cgroup_notify_event, .notify_message = service_notify_message, @@ -3792,6 +3883,23 @@ const UnitVTable service_vtable = { .bus_invalidating_properties = bus_service_invalidating_properties, #ifdef HAVE_SYSV_COMPAT - .enumerate = service_enumerate + .enumerate = service_enumerate, #endif + .status_message_formats = { + .starting_stopping = { + [0] = "Starting %s...", + [1] = "Stopping %s...", + }, + .finished_start_job = { + [JOB_DONE] = "Started %s.", + [JOB_FAILED] = "Failed to start %s.", + [JOB_DEPENDENCY] = "Dependency failed for %s.", + [JOB_TIMEOUT] = "Timed out starting %s.", + }, + .finished_stop_job = { + [JOB_DONE] = "Stopped %s.", + [JOB_FAILED] = "Stopped (with error) %s.", + [JOB_TIMEOUT] = "Timed out stopping %s.", + }, + }, };