X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Fservice.c;h=7c79dcdd148c8f1a1d7432c48b5a82d43906a2ce;hp=e598f9b2793cb8332a33a549d12a46ef3b7f3eb7;hb=67445f4e22ad924394acdd4fd49e6f238244a5ca;hpb=e201a0384da94f57f65c951a97d8ddaf990d6240 diff --git a/src/core/service.c b/src/core/service.c index e598f9b27..7c79dcdd1 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -33,6 +33,7 @@ #include "log.h" #include "strv.h" #include "unit-name.h" +#include "unit-printf.h" #include "dbus-service.h" #include "special.h" #include "bus-errors.h" @@ -130,12 +131,12 @@ static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] = static void service_init(Unit *u) { Service *s = SERVICE(u); - int i; 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; @@ -150,9 +151,7 @@ static void service_init(Unit *u) { s->guess_main_pid = true; exec_context_init(&s->exec_context); - for (i = 0; i < RLIMIT_NLIMITS; i++) - if (UNIT(s)->manager->rlimit[i]) - s->exec_context.rlimit[i] = newdup(struct rlimit, UNIT(s)->manager->rlimit[i], 1); + kill_context_init(&s->kill_context); RATELIMIT_INIT(s->start_limit, 10*USEC_PER_SEC, 5); @@ -296,6 +295,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); @@ -567,7 +576,6 @@ static int service_load_sysv_path(Service *s, const char *path) { goto finish; } - zero(st); if (fstat(fileno(f), &st) < 0) { r = -errno; goto finish; @@ -722,7 +730,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); @@ -879,7 +887,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); @@ -918,9 +926,12 @@ 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; @@ -928,7 +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; - 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. */ @@ -1001,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; @@ -1023,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; @@ -1038,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; @@ -1164,7 +1175,7 @@ static int service_verify(Service *s) { 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->exec_context.kill_mode != KILL_CONTROL_GROUP) { + 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; } @@ -1181,12 +1192,12 @@ static int service_add_default_dependencies(Service *s) { * majority of services. */ /* First, pull in base system */ - if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) { + if (UNIT(s)->manager->running_as == SYSTEMD_SYSTEM) { if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_BASIC_TARGET, NULL, true)) < 0) return r; - } else if (UNIT(s)->manager->running_as == MANAGER_USER) { + } else if (UNIT(s)->manager->running_as == SYSTEMD_USER) { if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SOCKETS_TARGET, NULL, true)) < 0) return r; @@ -1245,9 +1256,9 @@ static int service_load(Unit *u) { if (s->type == _SERVICE_TYPE_INVALID) s->type = s->bus_name ? SERVICE_DBUS : SERVICE_SIMPLE; - /* Oneshot services have disabled timeout by default */ - if (s->type == SERVICE_ONESHOT && !s->timeout_defined) - s->timeout_usec = 0; + /* 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); @@ -1282,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); @@ -1347,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++) { @@ -1602,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; @@ -1752,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); @@ -1888,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; @@ -1962,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) @@ -1979,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; } @@ -2008,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) @@ -2253,10 +2278,12 @@ 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; @@ -2270,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; @@ -2463,20 +2494,21 @@ static int service_start(Unit *u) { return 0; /* A service that will be restarted must be stopped first to - * trigger BindTo and/or OnFailure dependencies. If a user + * 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. */ - if (s->state == SERVICE_AUTO_RESTART) { - log_warning("%s automatic restart is pending, must be stopped before issuing start request.", UNIT(s)->id); - return -ECANCELED; - } + * 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); if (r < 0) { - service_notify_sockets_dead(s, true); + service_enter_dead(s, SERVICE_FAILURE_START_LIMIT, false); return r; } @@ -2509,7 +2541,7 @@ static int service_stop(Unit *u) { /* A restart will be scheduled or is in progress. */ if (s->state == SERVICE_AUTO_RESTART) { - service_enter_dead(s, SERVICE_SUCCESS, false); + service_set_state(s, SERVICE_DEAD); return 0; } @@ -2857,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; @@ -2928,12 +2961,10 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { else service_enter_signal(s, SERVICE_FINAL_SIGTERM, f); break; - } else { - assert(s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY); - - /* Fall through */ } + /* Fall through */ + case SERVICE_RUNNING: service_enter_running(s, f); break; @@ -3003,7 +3034,9 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { break; case SERVICE_START: - assert(s->type == SERVICE_FORKING); + if (s->type != SERVICE_FORKING) + /* Maybe spurious event due to a reload that changed the type? */ + break; if (f != SERVICE_SUCCESS) { service_enter_signal(s, SERVICE_FINAL_SIGTERM, f); @@ -3132,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 { @@ -3157,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 { @@ -3396,7 +3429,7 @@ static int service_enumerate(Manager *m) { assert(m); - if (m->running_as != MANAGER_SYSTEM) + if (m->running_as != SYSTEMD_SYSTEM) return 0; zero(runlevel_services); @@ -3406,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; @@ -3441,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; @@ -3654,9 +3687,11 @@ static void service_reset_failed(Unit *u) { s->result = SERVICE_SUCCESS; s->reload_result = SERVICE_SUCCESS; + + 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; @@ -3683,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; } + } + q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, false, pid_set, NULL); - if (q < 0) - if (q != -EAGAIN && q != -ESRCH && q != -ENOENT) - r = q; + if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT) + r = q; } finish: @@ -3781,7 +3821,8 @@ static const char* const service_result_table[_SERVICE_RESULT_MAX] = { [SERVICE_FAILURE_EXIT_CODE] = "exit-code", [SERVICE_FAILURE_SIGNAL] = "signal", [SERVICE_FAILURE_CORE_DUMP] = "core-dump", - [SERVICE_FAILURE_WATCHDOG] = "watchdog" + [SERVICE_FAILURE_WATCHDOG] = "watchdog", + [SERVICE_FAILURE_START_LIMIT] = "start-limit" }; DEFINE_STRING_TABLE_LOOKUP(service_result, ServiceResult); @@ -3795,8 +3836,9 @@ 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), + .exec_context_offset = offsetof(Service, exec_context), + .sections = "Unit\0" "Service\0"