X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fcore%2Fservice.c;h=694a2658e9744beeb7ecff0031daa8a72d8c91b9;hb=9ae702110457a555ffc6ff7b59e203ead067db66;hp=41b95ab0f3e6d036855344e867179f3da64031a6;hpb=f8294e4175918117ca6c131720bcf287eadcd029;p=elogind.git diff --git a/src/core/service.c b/src/core/service.c index 41b95ab0f..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,12 +145,6 @@ 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); - - unit_cgroup_context_init_defaults(u, &s->cgroup_context); - 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_event_source, s->watchdog_timestamp.monotonic + s->watchdog_usec, 0, service_dispatch_watchdog, s); + 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,7 +355,12 @@ 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, &s->timer_event_source, now(CLOCK_MONOTONIC) + usec, 0, service_dispatch_timer, s); + 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 @@ -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_patch_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); @@ -1841,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); @@ -1850,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 || @@ -2372,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, @@ -2403,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; @@ -2684,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")) { @@ -3416,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); @@ -3812,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),