X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fservice.c;h=efa52778a66f6342a4fa805704dd6012859a2fdb;hp=202557ac0ba6035097fc241598540f2c5f62de78;hb=f73d93a4d9403d44bf3661bf383ac1385e5b5eb6;hpb=d6c9574fb558d9e304699b1cc7522c3b133adfc9 diff --git a/src/service.c b/src/service.c index 202557ac0..efa52778a 100644 --- a/src/service.c +++ b/src/service.c @@ -95,7 +95,7 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = { [SERVICE_STOP_POST] = UNIT_DEACTIVATING, [SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING, [SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING, - [SERVICE_MAINTENANCE] = UNIT_MAINTENANCE, + [SERVICE_FAILED] = UNIT_FAILED, [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING }; @@ -251,9 +251,17 @@ static int sysv_translate_facility(const char *name, char **_r) { "$time", SPECIAL_RTC_SET_TARGET, /* Debian extensions */ +#ifdef TARGET_DEBIAN "$mail-transport-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET, +#endif "$mail-transfer-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET, - "$x-display-manager", SPECIAL_DISPLAY_MANAGER_SERVICE + "$x-display-manager", SPECIAL_DISPLAY_MANAGER_SERVICE, + +#ifdef TARGET_FEDORA + /* Fedora extensions, lacking the $ prefix */ + "MTA", SPECIAL_MAIL_TRANSFER_AGENT_TARGET, + "smtpdaemon", SPECIAL_MAIL_TRANSFER_AGENT_TARGET +#endif }; unsigned i; @@ -488,7 +496,7 @@ static int service_load_sysv_path(Service *s, const char *path) { t[k-1] = 0; } - if (!(d = strdup(strstrip(t+12)))) { + if (!(d = strappend("LSB: ", strstrip(t+12)))) { r = -ENOMEM; goto finish; } @@ -566,15 +574,21 @@ static int service_load_sysv_path(Service *s, const char *path) { if (unit_name_to_type(m) == UNIT_SERVICE) r = unit_add_name(u, m); - else if (s->sysv_enabled) - r = unit_add_two_dependencies_by_name_inverse(u, UNIT_AFTER, UNIT_WANTS, m, NULL, true); - else - r = unit_add_dependency_by_name_inverse(u, UNIT_AFTER, m, NULL, true); + else { + r = unit_add_dependency_by_name(u, UNIT_BEFORE, m, NULL, true); - free(m); + if (s->sysv_enabled) { + int k; + + if ((k = unit_add_dependency_by_name_inverse(u, UNIT_WANTS, m, NULL, true)) < 0) + r = k; + } + } if (r < 0) - goto finish; + log_error("[%s:%u] Failed to add LSB Provides name %s, ignoring: %s", path, line, m, strerror(-r)); + + free(m); } } else if (startswith_no_case(t, "Required-Start:") || @@ -604,10 +618,11 @@ static int service_load_sysv_path(Service *s, const char *path) { continue; r = unit_add_dependency_by_name(u, startswith_no_case(t, "X-Start-Before:") ? UNIT_BEFORE : UNIT_AFTER, m, NULL, true); - free(m); if (r < 0) - goto finish; + log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s", path, line, m, strerror(-r)); + + free(m); } } else if (startswith_no_case(t, "Default-Start:")) { char *k, *d; @@ -635,7 +650,7 @@ static int service_load_sysv_path(Service *s, const char *path) { state = LSB_DESCRIPTION; - if (!(d = strdup(strstrip(t+12)))) { + if (!(d = strappend("LSB: ", strstrip(t+12)))) { r = -ENOMEM; goto finish; } @@ -648,7 +663,7 @@ static int service_load_sysv_path(Service *s, const char *path) { state = LSB; - if (!(d = strdup(strstrip(t+18)))) { + if (!(d = strappend("LSB: ", strstrip(t+18)))) { r = -ENOMEM; goto finish; } @@ -706,9 +721,11 @@ static int service_load_sysv_path(Service *s, const char *path) { /* Special setting for all SysV services */ s->type = SERVICE_FORKING; - s->valid_no_process = true; + s->remain_after_exit = true; s->restart = SERVICE_ONCE; - s->exec_context.std_output = s->meta.manager->sysv_console ? EXEC_OUTPUT_TTY : EXEC_OUTPUT_NULL; + s->exec_context.std_output = + (s->meta.manager->sysv_console || s->exec_context.std_input == EXEC_INPUT_TTY) + ? EXEC_OUTPUT_TTY : EXEC_OUTPUT_NULL; s->exec_context.kill_mode = KILL_PROCESS_GROUP; u->meta.load_state = UNIT_LOADED; @@ -951,14 +968,14 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { "%sService State: %s\n" "%sPermissionsStartOnly: %s\n" "%sRootDirectoryStartOnly: %s\n" - "%sValidNoProcess: %s\n" + "%sRemainAfterExit: %s\n" "%sType: %s\n" "%sRestart: %s\n" "%sNotifyAccess: %s\n", prefix, service_state_to_string(s->state), prefix, yes_no(s->permissions_start_only), prefix, yes_no(s->root_directory_start_only), - prefix, yes_no(s->valid_no_process), + prefix, yes_no(s->remain_after_exit), prefix, service_type_to_string(s->type), prefix, service_restart_to_string(s->restart), prefix, notify_access_to_string(s->notify_access)); @@ -1186,7 +1203,7 @@ static void service_set_state(Service *s, ServiceState state) { state == SERVICE_STOP_POST || state == SERVICE_FINAL_SIGTERM || state == SERVICE_FINAL_SIGKILL || - state == SERVICE_MAINTENANCE || + state == SERVICE_FAILED || state == SERVICE_AUTO_RESTART) service_notify_sockets_dead(s); @@ -1510,7 +1527,7 @@ static void service_enter_dead(Service *s, bool success, bool allow_restart) { service_set_state(s, SERVICE_AUTO_RESTART); } else - service_set_state(s, s->failure ? SERVICE_MAINTENANCE : SERVICE_DEAD); + service_set_state(s, s->failure ? SERVICE_FAILED : SERVICE_DEAD); s->forbid_restart = false; @@ -1559,7 +1576,8 @@ fail: static void service_enter_signal(Service *s, ServiceState state, bool success) { int r; - bool sent = false; + Set *pid_set = NULL; + bool wait_for_exit = false; assert(s); @@ -1569,38 +1587,53 @@ static void service_enter_signal(Service *s, ServiceState state, bool success) { 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->exec_context.kill_mode == KILL_CONTROL_GROUP) { + if (s->main_pid > 0) { + if (kill(s->exec_context.kill_mode == KILL_PROCESS_GROUP ? + -s->main_pid : + s->main_pid, sig) < 0 && errno != ESRCH) - if ((r = cgroup_bonding_kill_list(s->meta.cgroup_bondings, sig)) < 0) { - if (r != -EAGAIN && r != -ESRCH) - goto fail; - } else - sent = true; + log_warning("Failed to kill main process %li: %m", (long) s->main_pid); + else + wait_for_exit = true; } - if (!sent) { - r = 0; + if (s->control_pid > 0) { + if (kill(s->exec_context.kill_mode == KILL_PROCESS_GROUP ? + -s->control_pid : + s->control_pid, sig) < 0 && errno != ESRCH) - if (s->main_pid > 0) { - if (kill(s->exec_context.kill_mode == KILL_PROCESS ? s->main_pid : -s->main_pid, sig) < 0 && errno != ESRCH) - r = -errno; - else - sent = true; - } + log_warning("Failed to kill control process %li: %m", (long) s->control_pid); + else + wait_for_exit = true; + } - if (s->control_pid > 0) { - if (kill(s->exec_context.kill_mode == KILL_PROCESS ? s->control_pid : -s->control_pid, sig) < 0 && errno != ESRCH) - r = -errno; - else - sent = true; - } + if (s->exec_context.kill_mode == KILL_CONTROL_GROUP) { - if (r < 0) + if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func))) { + r = -ENOMEM; goto fail; + } + + /* Exclude the main/control pids from being killed via the cgroup */ + if (s->main_pid > 0) + if ((r = set_put(pid_set, LONG_TO_PTR(s->main_pid))) < 0) + goto fail; + + if (s->control_pid > 0) + if ((r = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0) + goto fail; + + if ((r = cgroup_bonding_kill_list(s->meta.cgroup_bondings, sig, pid_set)) < 0) { + if (r != -EAGAIN && r != -ESRCH && r != -ENOENT) + log_warning("Failed to kill control group: %s", strerror(-r)); + } else if (r > 0) + wait_for_exit = true; + + set_free(pid_set); } } - if (sent && (s->main_pid > 0 || s->control_pid > 0)) { + if (wait_for_exit) { if (s->timeout_usec > 0) if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0) goto fail; @@ -1620,6 +1653,9 @@ fail: service_enter_stop_post(s, false); else service_enter_dead(s, false, true); + + if (pid_set) + set_free(pid_set); } static void service_enter_stop(Service *s, bool success) { @@ -1669,7 +1705,7 @@ static void service_enter_running(Service *s, bool success) { if ((main_pid_ok > 0 || (main_pid_ok < 0 && cgroup_ok != 0)) && (s->bus_name_good || s->type != SERVICE_DBUS)) service_set_state(s, SERVICE_RUNNING); - else if (s->valid_no_process) + else if (s->remain_after_exit) service_set_state(s, SERVICE_EXITED); else service_enter_stop(s, true); @@ -1811,7 +1847,7 @@ static void service_enter_restart(Service *s) { service_enter_dead(s, true, false); - if ((r = manager_add_job(s->meta.manager, JOB_START, UNIT(s), JOB_FAIL, false, NULL, NULL)) < 0) + if ((r = manager_add_job(s->meta.manager, JOB_START, UNIT(s), JOB_FAIL, false, &error, NULL)) < 0) goto fail; log_debug("%s scheduled restart job.", s->meta.id); @@ -1955,7 +1991,7 @@ static int service_start(Unit *u) { s->state == SERVICE_START_POST) return 0; - assert(s->state == SERVICE_DEAD || s->state == SERVICE_MAINTENANCE || s->state == SERVICE_AUTO_RESTART); + assert(s->state == SERVICE_DEAD || s->state == SERVICE_FAILED || s->state == SERVICE_AUTO_RESTART); /* Make sure we don't enter a busy loop of some kind. */ if (!ratelimit_test(&s->ratelimit)) { @@ -2261,7 +2297,10 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { assert(s); assert(pid >= 0); - success = is_clean_exit(code, status); + if (s->sysv_path) + success = is_clean_exit_lsb(code, status); + else + success = is_clean_exit(code, status); if (s->main_pid == pid) { @@ -2449,6 +2488,9 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { } } } + + /* Notify clients about changed exit status */ + unit_add_to_dbus_queue(u); } static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) { @@ -2503,7 +2545,7 @@ static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) { break; case SERVICE_FINAL_SIGKILL: - log_warning("%s still around after SIGKILL (2). Entering maintenance mode.", u->meta.id); + log_warning("%s still around after SIGKILL (2). Entering failed mode.", u->meta.id); service_enter_dead(s, false, true); break; @@ -2536,6 +2578,20 @@ static void service_cgroup_notify_event(Unit *u) { service_enter_running(s, true); break; + case SERVICE_STOP_SIGTERM: + case SERVICE_STOP_SIGKILL: + if (main_pid_good(s) <= 0 && !control_pid_good(s)) + service_enter_stop_post(s, true); + + break; + + case SERVICE_FINAL_SIGTERM: + case SERVICE_FINAL_SIGKILL: + if (main_pid_good(s) <= 0 && !control_pid_good(s)) + service_enter_dead(s, true, true); + + break; + default: ; } @@ -2605,6 +2661,9 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { } } + + /* Notify clients about changed status or main pid */ + unit_add_to_dbus_queue(u); } static int service_enumerate(Manager *m) { @@ -2612,10 +2671,15 @@ static int service_enumerate(Manager *m) { unsigned i; DIR *d = NULL; char *path = NULL, *fpath = NULL, *name = NULL; + Set *runlevel_services[ELEMENTSOF(rcnd_table)], *shutdown_services = NULL; + Unit *service; + Iterator j; int r; assert(m); + zero(runlevel_services); + STRV_FOREACH(p, m->lookup_paths.sysvrcnd_path) for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) { struct dirent *de; @@ -2638,7 +2702,6 @@ static int service_enumerate(Manager *m) { } while ((de = readdir(d))) { - Unit *service; int a, b; if (ignore_file(de->d_name)) @@ -2682,54 +2745,70 @@ static int service_enumerate(Manager *m) { continue; } - if (de->d_name[0] == 'S' && - (rcnd_table[i].type == RUNLEVEL_UP || rcnd_table[i].type == RUNLEVEL_SYSINIT)) { - SERVICE(service)->sysv_start_priority = - MAX(a*10 + b, SERVICE(service)->sysv_start_priority); - SERVICE(service)->sysv_enabled = true; - } + if (de->d_name[0] == 'S') { - manager_dispatch_load_queue(m); - service = unit_follow_merge(service); + if (rcnd_table[i].type == RUNLEVEL_UP || rcnd_table[i].type == RUNLEVEL_SYSINIT) { + SERVICE(service)->sysv_start_priority = + MAX(a*10 + b, SERVICE(service)->sysv_start_priority); - /* If this is a native service, rely - * on native ways to pull in a - * service, don't pull it in via sysv - * rcN.d links. */ - if (service->meta.fragment_path) - continue; + SERVICE(service)->sysv_enabled = true; + } - if (de->d_name[0] == 'S') { + if ((r = set_ensure_allocated(&runlevel_services[i], trivial_hash_func, trivial_compare_func)) < 0) + goto finish; - if ((r = unit_add_two_dependencies_by_name_inverse(service, UNIT_AFTER, UNIT_WANTS, rcnd_table[i].target, NULL, true)) < 0) + if ((r = set_put(runlevel_services[i], service)) < 0) goto finish; } else if (de->d_name[0] == 'K' && (rcnd_table[i].type == RUNLEVEL_DOWN || rcnd_table[i].type == RUNLEVEL_SYSINIT)) { - /* We honour K links only for - * halt/reboot. For the normal - * runlevels we assume the - * stop jobs will be - * implicitly added by the - * core logic. Also, we don't - * really distuingish here - * between the runlevels 0 and - * 6 and just add them to the - * special shutdown target. On - * SUSE the boot.d/ runlevel - * is also used for shutdown, - * so we add links for that - * too to the shutdown - * target.*/ - - if ((r = unit_add_two_dependencies_by_name_inverse(service, UNIT_AFTER, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true)) < 0) + if ((r = set_ensure_allocated(&shutdown_services, trivial_hash_func, trivial_compare_func)) < 0) + goto finish; + + if ((r = set_put(shutdown_services, service)) < 0) goto finish; } } } + /* Now we loaded all stubs and are aware of the lowest + start-up priority for all services, not let's actually load + the services, this will also tell us which services are + actually native now */ + manager_dispatch_load_queue(m); + + /* If this is a native service, rely on native ways to pull in + * a service, don't pull it in via sysv rcN.d links. */ + for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) + SET_FOREACH(service, runlevel_services[i], j) { + service = unit_follow_merge(service); + + if (service->meta.fragment_path) + continue; + + if ((r = unit_add_two_dependencies_by_name_inverse(service, UNIT_AFTER, UNIT_WANTS, rcnd_table[i].target, NULL, true)) < 0) + goto finish; + } + + /* We honour K links only for halt/reboot. For the normal + * runlevels we assume the stop jobs will be implicitly added + * by the core logic. Also, we don't really distuingish here + * between the runlevels 0 and 6 and just add them to the + * special shutdown target. On SUSE the boot.d/ runlevel is + * also used for shutdown, so we add links for that too to the + * shutdown target.*/ + SET_FOREACH(service, shutdown_services, j) { + service = unit_follow_merge(service); + + if (service->meta.fragment_path) + continue; + + if ((r = unit_add_two_dependencies_by_name_inverse(service, UNIT_AFTER, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true)) < 0) + goto finish; + } + r = 0; finish: @@ -2737,6 +2816,10 @@ finish: free(fpath); free(name); + for (i = 0; i < ELEMENTSOF(rcnd_table); i++) + set_free(runlevel_services[i]); + set_free(shutdown_services); + if (d) closedir(d); @@ -2833,12 +2916,12 @@ int service_set_socket_fd(Service *s, int fd, Socket *sock) { return 0; } -static void service_reset_maintenance(Unit *u) { +static void service_reset_failed(Unit *u) { Service *s = SERVICE(u); assert(s); - if (s->state == SERVICE_MAINTENANCE) + if (s->state == SERVICE_FAILED) service_set_state(s, SERVICE_DEAD); s->failure = false; @@ -2858,7 +2941,7 @@ static const char* const service_state_table[_SERVICE_STATE_MAX] = { [SERVICE_STOP_POST] = "stop-post", [SERVICE_FINAL_SIGTERM] = "final-sigterm", [SERVICE_FINAL_SIGKILL] = "final-sigkill", - [SERVICE_MAINTENANCE] = "maintenance", + [SERVICE_FAILED] = "failed", [SERVICE_AUTO_RESTART] = "auto-restart", }; @@ -2931,7 +3014,7 @@ const UnitVTable service_vtable = { .sigchld_event = service_sigchld_event, .timer_event = service_timer_event, - .reset_maintenance = service_reset_maintenance, + .reset_failed = service_reset_failed, .cgroup_notify_empty = service_cgroup_notify_event, .notify_message = service_notify_message, @@ -2939,7 +3022,9 @@ const UnitVTable service_vtable = { .bus_name_owner_change = service_bus_name_owner_change, .bus_query_pid_done = service_bus_query_pid_done, + .bus_interface = "org.freedesktop.systemd1.Service", .bus_message_handler = bus_service_message_handler, + .bus_invalidating_properties = bus_service_invalidating_properties, .enumerate = service_enumerate };