X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Fservice.c;h=694a2658e9744beeb7ecff0031daa8a72d8c91b9;hp=5c6b638650198a1575ef944786e1344570c99a90;hb=bf500566323bbc2240d1fdd1165a8c908faf4098;hpb=336c6e4690ea017a11799aa331ffedd4c59a31ad diff --git a/src/core/service.c b/src/core/service.c index 5c6b63865..694a2658e 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include "manager.h" #include "unit.h" @@ -143,13 +145,7 @@ static void service_init(Unit *u) { s->socket_fd = -1; s->guess_main_pid = true; - exec_context_init(&s->exec_context); - kill_context_init(&s->kill_context); - cgroup_context_init(&s->cgroup_context); - - RATELIMIT_INIT(s->start_limit, - u->manager->default_start_limit_interval, - u->manager->default_start_limit_burst); + RATELIMIT_INIT(s->start_limit, u->manager->default_start_limit_interval, u->manager->default_start_limit_burst); s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; } @@ -226,8 +222,7 @@ static void service_close_socket_fd(Service *s) { if (s->socket_fd < 0) return; - close_nointr_nofail(s->socket_fd); - s->socket_fd = -1; + s->socket_fd = safe_close(s->socket_fd); } static void service_connection_unref(Service *s) { @@ -264,7 +259,12 @@ static void service_start_watchdog(Service *s) { r = sd_event_source_set_enabled(s->watchdog_event_source, SD_EVENT_ONESHOT); } else { - r = sd_event_add_monotonic(UNIT(s)->manager->event, s->watchdog_timestamp.monotonic + s->watchdog_usec, 0, service_dispatch_watchdog, s, &s->watchdog_event_source); + r = sd_event_add_time( + UNIT(s)->manager->event, + &s->watchdog_event_source, + CLOCK_MONOTONIC, + s->watchdog_timestamp.monotonic + s->watchdog_usec, 0, + service_dispatch_watchdog, s); if (r < 0) { log_warning_unit(UNIT(s)->id, "%s failed to add watchdog timer: %s", UNIT(s)->id, strerror(-r)); return; @@ -302,8 +302,9 @@ static void service_done(Unit *u) { free(s->status_text); s->status_text = NULL; - cgroup_context_done(&s->cgroup_context); - exec_context_done(&s->exec_context); + free(s->reboot_arg); + s->reboot_arg = NULL; + s->exec_runtime = exec_runtime_unref(s->exec_runtime); exec_command_free_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX); s->control_command = NULL; @@ -354,14 +355,19 @@ static int service_arm_timer(Service *s, usec_t usec) { return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT); } - return sd_event_add_monotonic(UNIT(s)->manager->event, now(CLOCK_MONOTONIC) + usec, 0, service_dispatch_timer, s, &s->timer_event_source); + return sd_event_add_time( + UNIT(s)->manager->event, + &s->timer_event_source, + CLOCK_MONOTONIC, + now(CLOCK_MONOTONIC) + usec, 0, + service_dispatch_timer, s); } #ifdef HAVE_SYSV_COMPAT static char *sysv_translate_name(const char *name) { char *r; - r = new(char, strlen(name) + sizeof(".service")); + r = new(char, strlen(name) + strlen(".service") + 1); if (!r) return NULL; @@ -663,13 +669,8 @@ static int service_load_sysv_path(Service *s, const char *path) { state = NORMAL; - if (sscanf(t+10, "%15s %i %*i", - runlevels, - &start_priority) != 2) { - - log_warning_unit(u->id, - "[%s:%u] Failed to parse chkconfig line. Ignoring.", - path, line); + if (sscanf(t+10, "%15s %i %*i", runlevels, &start_priority) != 2) { + log_warning_unit(u->id, "[%s:%u] Failed to parse chkconfig line. Ignoring.", path, line); continue; } @@ -677,19 +678,17 @@ static int service_load_sysv_path(Service *s, const char *path) { * symlink farms is preferred over the * data from the LSB header. */ if (start_priority < 0 || start_priority > 99) - log_warning_unit(u->id, - "[%s:%u] Start priority out of range. Ignoring.", - path, line); + log_warning_unit(u->id, "[%s:%u] Start priority out of range. Ignoring.", path, line); else - s->sysv_start_priority = start_priority; + log_debug_unit(u->id, "[%s:%u] Ignoring start priority set in the chkconfig file.", path, line); char_array_0(runlevels); k = delete_chars(runlevels, WHITESPACE "-"); - if (k[0]) { char *d; - if (!(d = strdup(k))) { + d = strdup(k); + if (!d) { r = -ENOMEM; goto finish; } @@ -991,9 +990,9 @@ static int service_load_sysv_path(Service *s, const char *path) { u->description = d; } - /* The priority that has been set in /etc/rcN.d/ hierarchies - * takes precedence over what is stored as default in the LSB - * header */ + /* Initialize the start priority from what has been set in the + * /etc/rcN.d/ hierarchies if we load the unit file as SysV + * init script. */ if (s->sysv_start_priority_from_rcnd >= 0) s->sysv_start_priority = s->sysv_start_priority_from_rcnd; @@ -1203,11 +1202,15 @@ static int service_load(Unit *u) { service_fix_output(s); + r = unit_patch_contexts(u); + if (r < 0) + return r; + r = unit_add_exec_dependencies(u, &s->exec_context); if (r < 0) return r; - r = unit_add_default_slice(u); + r = unit_add_default_slice(u, &s->cgroup_context); if (r < 0) return r; @@ -1217,27 +1220,23 @@ static int service_load(Unit *u) { return r; #endif - if (s->bus_name) { - r = unit_watch_bus_name(u, s->bus_name); - if (r < 0) - return r; - } - if (s->type == SERVICE_NOTIFY && s->notify_access == NOTIFY_NONE) s->notify_access = NOTIFY_MAIN; if (s->watchdog_usec > 0 && s->notify_access == NOTIFY_NONE) s->notify_access = NOTIFY_MAIN; - if (UNIT(s)->default_dependencies) { - r = service_add_default_dependencies(s); + if (s->bus_name) { + r = unit_watch_bus_name(u, s->bus_name); if (r < 0) return r; } - r = unit_exec_context_defaults(u, &s->exec_context); - if (r < 0) - return r; + if (u->default_dependencies) { + r = service_add_default_dependencies(s); + if (r < 0) + return r; + } } return service_verify(s); @@ -1368,11 +1367,10 @@ static int service_load_pid_file(Service *s, bool may_warn) { return r; } - if (kill(pid, 0) < 0 && errno != EPERM) { + if (!pid_is_alive(pid)) { if (may_warn) - log_info_unit(UNIT(s)->id, - "PID "PID_FMT" read from file %s does not exist.", - pid, s->pid_file); + log_info_unit(UNIT(s)->id, "PID "PID_FMT" read from file %s does not exist or is a zombie.", pid, s->pid_file); + return -ESRCH; } @@ -1437,9 +1435,7 @@ static int service_search_main_pid(Service *s) { log_warning_unit(UNIT(s)->id, "Failed to watch PID "PID_FMT" from service %s", pid, UNIT(s)->id); - return r; - - return 0; + return r; } static void service_set_state(Service *s, ServiceState state) { @@ -1565,7 +1561,7 @@ static int service_coldplug(Unit *u) { return r; } - if (pid_valid(s->main_pid) && + if (pid_is_unwaited(s->main_pid) && ((s->deserialized_state == SERVICE_START && IN_SET(s->type, SERVICE_FORKING, SERVICE_DBUS, SERVICE_ONESHOT, SERVICE_NOTIFY)) || IN_SET(s->deserialized_state, SERVICE_START, SERVICE_START_POST, @@ -1578,7 +1574,7 @@ static int service_coldplug(Unit *u) { return r; } - if (pid_valid(s->control_pid) && + if (pid_is_unwaited(s->control_pid) && IN_SET(s->deserialized_state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD, @@ -1773,6 +1769,7 @@ static int service_spawn( UNIT(s)->manager->confirm_spawn, UNIT(s)->manager->cgroup_supported, path, + manager_get_runtime_prefix(UNIT(s)->manager), UNIT(s)->id, s->watchdog_usec, s->type == SERVICE_IDLE ? UNIT(s)->manager->idle_pipe : NULL, @@ -1810,7 +1807,7 @@ static int main_pid_good(Service *s) { /* If it's an alien child let's check if it is still * alive ... */ if (s->main_pid_alien && s->main_pid > 0) - return kill(s->main_pid, 0) >= 0 || errno != ESRCH; + return pid_is_alive(s->main_pid); /* .. otherwise assume we'll get a SIGCHLD for it, * which we really should wait for to collect exit @@ -1843,6 +1840,8 @@ static int cgroup_good(Service *s) { return !r; } +static int service_execute_action(Service *s, FailureAction action, const char *reason, bool log_action_none); + static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) { int r; assert(s); @@ -1852,6 +1851,9 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD); + if (s->result != SERVICE_SUCCESS) + service_execute_action(s, s->failure_action, "failed", false); + if (allow_restart && !s->forbid_restart && (s->restart == SERVICE_RESTART_ALWAYS || @@ -1874,10 +1876,13 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) s->forbid_restart = false; - /* we want fresh tmpdirs in case service is started again immediately */ + /* We want fresh tmpdirs in case service is started again immediately */ exec_runtime_destroy(s->exec_runtime); s->exec_runtime = exec_runtime_unref(s->exec_runtime); + /* Also, remove the runtime directory in */ + exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager)); + /* Try to delete the pid file. At this point it will be * out-of-date, and some software might be confused by it, so * let's remove it. */ @@ -2371,26 +2376,27 @@ fail: service_enter_stop(s, SERVICE_FAILURE_RESOURCES); } -static int service_start_limit_test(Service *s) { +static int service_execute_action(Service *s, FailureAction action, const char *reason, bool log_action_none) { assert(s); - if (ratelimit_test(&s->start_limit)) - return 0; + if (action == SERVICE_FAILURE_ACTION_REBOOT || + action == SERVICE_FAILURE_ACTION_REBOOT_FORCE) + update_reboot_param_file(s->reboot_arg); - switch (s->start_limit_action) { + switch (action) { - case SERVICE_START_LIMIT_NONE: - log_warning_unit(UNIT(s)->id, - "%s start request repeated too quickly, refusing to start.", - UNIT(s)->id); + case SERVICE_FAILURE_ACTION_NONE: + if (log_action_none) + log_warning_unit(UNIT(s)->id, + "%s %s, refusing to start.", UNIT(s)->id, reason); break; - case SERVICE_START_LIMIT_REBOOT: { + case SERVICE_FAILURE_ACTION_REBOOT: { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; int r; log_warning_unit(UNIT(s)->id, - "%s start request repeated too quickly, rebooting.", UNIT(s)->id); + "%s %s, rebooting.", UNIT(s)->id, reason); r = manager_add_job_by_name(UNIT(s)->manager, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE, @@ -2402,28 +2408,44 @@ static int service_start_limit_test(Service *s) { break; } - case SERVICE_START_LIMIT_REBOOT_FORCE: + case SERVICE_FAILURE_ACTION_REBOOT_FORCE: log_warning_unit(UNIT(s)->id, - "%s start request repeated too quickly, forcibly rebooting.", UNIT(s)->id); + "%s %s, forcibly rebooting.", UNIT(s)->id, reason); UNIT(s)->manager->exit_code = MANAGER_REBOOT; break; - case SERVICE_START_LIMIT_REBOOT_IMMEDIATE: + case SERVICE_FAILURE_ACTION_REBOOT_IMMEDIATE: log_warning_unit(UNIT(s)->id, - "%s start request repeated too quickly, rebooting immediately.", UNIT(s)->id); + "%s %s, rebooting immediately.", UNIT(s)->id, reason); sync(); + if (s->reboot_arg) { + log_info("Rebooting with argument '%s'.", s->reboot_arg); + syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, + LINUX_REBOOT_CMD_RESTART2, s->reboot_arg); + } + + log_info("Rebooting."); reboot(RB_AUTOBOOT); break; default: log_error_unit(UNIT(s)->id, - "start limit action=%i", s->start_limit_action); - assert_not_reached("Unknown StartLimitAction."); + "failure action=%i", action); + assert_not_reached("Unknown FailureAction."); } return -ECANCELED; } +static int service_start_limit_test(Service *s) { + assert(s); + + if (ratelimit_test(&s->start_limit)) + return 0; + + return service_execute_action(s, s->start_limit_action, "start request repeated too quickly", true); +} + static int service_start(Unit *u) { Service *s = SERVICE(u); int r; @@ -2683,8 +2705,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, log_debug_unit(u->id, "Failed to parse socket-fd value %s", value); else { - if (s->socket_fd >= 0) - close_nointr_nofail(s->socket_fd); + safe_close(s->socket_fd); s->socket_fd = fdset_remove(fds, fd); } } else if (streq(key, "main-exec-status-pid")) { @@ -3309,7 +3330,10 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us case SERVICE_AUTO_RESTART: log_info_unit(UNIT(s)->id, - "%s holdoff time over, scheduling restart.", UNIT(s)->id); + s->restart_usec > 0 ? + "%s holdoff time over, scheduling restart." : + "%s has no holdoff time, scheduling restart.", + UNIT(s)->id); service_enter_restart(s); break; @@ -3354,7 +3378,7 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { if (s->main_pid != 0) log_warning_unit(u->id, "%s: Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT, u->id, pid, s->main_pid); else - log_warning_unit(u->id, "%s: Got notification message from PID "PID_FMT", but reception only permitted for main PID which is currently not known", u->id, pid); + log_debug_unit(u->id, "%s: Got notification message from PID "PID_FMT", but reception only permitted for main PID which is currently not known", u->id, pid); return; } @@ -3412,7 +3436,7 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { free(t); } - /* Interpet WATCHDOG= */ + /* Interpret WATCHDOG= */ if (strv_find(tags, "WATCHDOG=1")) { log_debug_unit(u->id, "%s: got WATCHDOG=1", u->id); service_reset_watchdog(s); @@ -3808,13 +3832,13 @@ static const char* const service_result_table[_SERVICE_RESULT_MAX] = { DEFINE_STRING_TABLE_LOOKUP(service_result, ServiceResult); -static const char* const start_limit_action_table[_SERVICE_START_LIMIT_MAX] = { - [SERVICE_START_LIMIT_NONE] = "none", - [SERVICE_START_LIMIT_REBOOT] = "reboot", - [SERVICE_START_LIMIT_REBOOT_FORCE] = "reboot-force", - [SERVICE_START_LIMIT_REBOOT_IMMEDIATE] = "reboot-immediate" +static const char* const failure_action_table[_SERVICE_FAILURE_ACTION_MAX] = { + [SERVICE_FAILURE_ACTION_NONE] = "none", + [SERVICE_FAILURE_ACTION_REBOOT] = "reboot", + [SERVICE_FAILURE_ACTION_REBOOT_FORCE] = "reboot-force", + [SERVICE_FAILURE_ACTION_REBOOT_IMMEDIATE] = "reboot-immediate" }; -DEFINE_STRING_TABLE_LOOKUP(start_limit_action, StartLimitAction); +DEFINE_STRING_TABLE_LOOKUP(failure_action, FailureAction); const UnitVTable service_vtable = { .object_size = sizeof(Service),