X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Fservice.c;h=ca71788d220a55b40349025b65298237a7bb57a7;hp=1ec2e51fc2a927136f551e1ec8873186224b7fb3;hb=454f7158c6cecd18555c5e7dd556e3d544301b52;hpb=ebc2259da1d1579347b86fc2ebca9f96334b6f22 diff --git a/src/core/service.c b/src/core/service.c index 1ec2e51fc..ca71788d2 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -264,7 +264,7 @@ 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_monotonic(UNIT(s)->manager->event, &s->watchdog_event_source, 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; @@ -354,7 +354,7 @@ 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_monotonic(UNIT(s)->manager->event, &s->timer_event_source, now(CLOCK_MONOTONIC) + usec, 0, service_dispatch_timer, s); } #ifdef HAVE_SYSV_COMPAT @@ -1368,11 +1368,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; } @@ -1485,6 +1484,9 @@ static void service_set_state(Service *s, ServiceState state) { s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; } + if (IN_SET(state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART)) + unit_unwatch_all_pids(UNIT(s)); + if (!IN_SET(state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD, @@ -1562,7 +1564,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, @@ -1575,7 +1577,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, @@ -1587,6 +1589,9 @@ static int service_coldplug(Unit *u) { return r; } + if (!IN_SET(s->deserialized_state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART)) + unit_watch_all_pids(UNIT(s)); + if (IN_SET(s->deserialized_state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD)) service_start_watchdog(s); @@ -1804,7 +1809,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 @@ -1895,6 +1900,7 @@ static void service_enter_stop_post(Service *s, ServiceResult f) { s->result = f; service_unwatch_control_pid(s); + unit_watch_all_pids(UNIT(s)); s->control_command = s->exec_command[SERVICE_EXEC_STOP_POST]; if (s->control_command) { @@ -1913,10 +1919,9 @@ static void service_enter_stop_post(Service *s, ServiceResult f) { if (r < 0) goto fail; - service_set_state(s, SERVICE_STOP_POST); } else - service_enter_dead(s, SERVICE_SUCCESS, true); + service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_SUCCESS); return; @@ -1935,6 +1940,8 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f if (f != SERVICE_SUCCESS) s->result = f; + unit_watch_all_pids(UNIT(s)); + r = unit_kill_context( UNIT(s), &s->kill_context, @@ -1942,6 +1949,7 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f s->main_pid, s->control_pid, s->main_pid_alien); + if (r < 0) goto fail; @@ -1953,8 +1961,12 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f } service_set_state(s, state); - } else if (state == SERVICE_STOP_SIGTERM || state == SERVICE_STOP_SIGKILL) + } else if (state == SERVICE_STOP_SIGTERM) + service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_SUCCESS); + else if (state == SERVICE_STOP_SIGKILL) service_enter_stop_post(s, SERVICE_SUCCESS); + else if (state == SERVICE_FINAL_SIGTERM) + service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_SUCCESS); else service_enter_dead(s, SERVICE_SUCCESS, true); @@ -1979,6 +1991,7 @@ static void service_enter_stop(Service *s, ServiceResult f) { s->result = f; service_unwatch_control_pid(s); + unit_watch_all_pids(UNIT(s)); s->control_command = s->exec_command[SERVICE_EXEC_STOP]; if (s->control_command) { @@ -2861,6 +2874,62 @@ fail: return 0; } +static void service_notify_cgroup_empty_event(Unit *u) { + Service *s = SERVICE(u); + + assert(u); + + log_debug_unit(u->id, "%s: cgroup is empty", u->id); + + switch (s->state) { + + /* Waiting for SIGCHLD is usually more interesting, + * because it includes return codes/signals. Which is + * why we ignore the cgroup events for most cases, + * except when we don't know pid which to expect the + * SIGCHLD for. */ + + case SERVICE_START: + case SERVICE_START_POST: + /* If we were hoping for the daemon to write its PID file, + * we can give up now. */ + if (s->pid_file_pathspec) { + log_warning_unit(u->id, + "%s never wrote its PID file. Failing.", UNIT(s)->id); + service_unwatch_pid_file(s); + if (s->state == SERVICE_START) + service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); + else + service_enter_stop(s, SERVICE_FAILURE_RESOURCES); + } + break; + + case SERVICE_RUNNING: + /* service_enter_running() will figure out what to do */ + service_enter_running(s, SERVICE_SUCCESS); + break; + + case SERVICE_STOP_SIGTERM: + case SERVICE_STOP_SIGKILL: + + if (main_pid_good(s) <= 0 && !control_pid_good(s)) + service_enter_stop_post(s, SERVICE_SUCCESS); + + break; + + case SERVICE_STOP_POST: + case SERVICE_FINAL_SIGTERM: + case SERVICE_FINAL_SIGKILL: + if (main_pid_good(s) <= 0 && !control_pid_good(s)) + service_enter_dead(s, SERVICE_SUCCESS, true); + + break; + + default: + ; + } +} + static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { Service *s = SERVICE(u); ServiceResult f; @@ -3138,6 +3207,18 @@ 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); + + /* We got one SIGCHLD for the service, let's watch all + * processes that are now running of the service, and watch + * that. Among the PIDs we then watch will be children + * reassigned to us, which hopefully allows us to identify + * when all children are gone */ + unit_tidy_watch_pids(u, s->main_pid, s->control_pid); + unit_watch_all_pids(u); + + /* If the PID set is empty now, then let's finish this off */ + if (set_isempty(u->pids)) + service_notify_cgroup_empty_event(u); } static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) { @@ -3227,7 +3308,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; @@ -3250,62 +3334,6 @@ static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void return 0; } -static void service_notify_cgroup_empty_event(Unit *u) { - Service *s = SERVICE(u); - - assert(u); - - log_debug_unit(u->id, "%s: cgroup is empty", u->id); - - switch (s->state) { - - /* Waiting for SIGCHLD is usually more interesting, - * because it includes return codes/signals. Which is - * why we ignore the cgroup events for most cases, - * except when we don't know pid which to expect the - * SIGCHLD for. */ - - case SERVICE_START: - case SERVICE_START_POST: - /* If we were hoping for the daemon to write its PID file, - * we can give up now. */ - if (s->pid_file_pathspec) { - log_warning_unit(u->id, - "%s never wrote its PID file. Failing.", UNIT(s)->id); - service_unwatch_pid_file(s); - if (s->state == SERVICE_START) - service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); - else - service_enter_stop(s, SERVICE_FAILURE_RESOURCES); - } - break; - - case SERVICE_RUNNING: - /* service_enter_running() will figure out what to do */ - service_enter_running(s, SERVICE_SUCCESS); - break; - - case SERVICE_STOP_SIGTERM: - case SERVICE_STOP_SIGKILL: - - if (main_pid_good(s) <= 0 && !control_pid_good(s)) - service_enter_stop_post(s, SERVICE_SUCCESS); - - break; - - case SERVICE_STOP_POST: - case SERVICE_FINAL_SIGTERM: - case SERVICE_FINAL_SIGKILL: - if (main_pid_good(s) <= 0 && !control_pid_good(s)) - service_enter_dead(s, SERVICE_SUCCESS, true); - - break; - - default: - ; - } -} - static void service_notify_message(Unit *u, pid_t pid, char **tags) { Service *s = SERVICE(u); const char *e; @@ -3323,10 +3351,12 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { return; } - if (s->notify_access == NOTIFY_MAIN && s->main_pid != 0 && pid != s->main_pid) { - log_warning_unit(u->id, - "%s: Got notification message from PID "PID_FMT", but reception only permitted for PID "PID_FMT, - u->id, pid, s->main_pid); + if (s->notify_access == NOTIFY_MAIN && pid != s->main_pid) { + + 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_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; }