-void service_record::emergency_stop() noexcept
-{
- if (! do_auto_restart() && start_explicit) {
- start_explicit = false;
- release();
- }
- forceStop();
- stopDependents();
- stopped();
-}
-
-void process_service::handle_exit_status(int exit_status) noexcept
-{
- bool did_exit = WIFEXITED(exit_status);
- bool was_signalled = WIFSIGNALED(exit_status);
-
- if (exit_status != 0 && service_state != service_state_t::STOPPING) {
- if (did_exit) {
- log(LogLevel::ERROR, "Service ", service_name, " process terminated with exit code ", WEXITSTATUS(exit_status));
- }
- else if (was_signalled) {
- log(LogLevel::ERROR, "Service ", service_name, " terminated due to signal ", WTERMSIG(exit_status));
- }
- }
-
- if (service_state == service_state_t::STARTING) {
- if (did_exit && WEXITSTATUS(exit_status) == 0) {
- started();
- }
- else {
- failed_to_start();
- }
- }
- else if (service_state == service_state_t::STOPPING) {
- // We won't log a non-zero exit status or termination due to signal here -
- // we assume that the process died because we signalled it.
- stopped();
- }
- else if (smooth_recovery && service_state == service_state_t::STARTED && desired_state == service_state_t::STARTED) {
- // TODO if we are pinned-started then we should probably check
- // that dependencies have started before trying to re-start the
- // service process.
- if (! restart_ps_process()) {
- emergency_stop();
- services->processQueues(false);
- }
- return;
- }
- else {
- emergency_stop();
- }
- services->processQueues(false);
-}
-
-void bgproc_service::handle_exit_status(int exit_status) noexcept
-{
- bool did_exit = WIFEXITED(exit_status);
- bool was_signalled = WIFSIGNALED(exit_status);
-
- if (exit_status != 0 && service_state != service_state_t::STOPPING) {
- if (did_exit) {
- log(LogLevel::ERROR, "Service ", service_name, " process terminated with exit code ", WEXITSTATUS(exit_status));
- }
- else if (was_signalled) {
- log(LogLevel::ERROR, "Service ", service_name, " terminated due to signal ", WTERMSIG(exit_status));
- }
- }
-
- if (doing_recovery) {
- // (BGPROCESS only)
- doing_recovery = false;
- bool need_stop = false;
- if ((did_exit && WEXITSTATUS(exit_status) != 0) || was_signalled) {
- need_stop = true;
- }
- else {
- // We need to re-read the PID, since it has now changed.
- if (pid_file.length() != 0) {
- if (! read_pid_file()) {
- need_stop = true;
- }
- }
- }
-
- if (need_stop) {
- // Failed startup: no auto-restart.
- emergency_stop();
- services->processQueues(false);
- }
-
- return;
- }
-
- if (service_state == service_state_t::STARTING) {
- // POSIX requires that if the process exited clearly with a status code of 0,
- // the exit status value will be 0:
- if (exit_status == 0) {
- if (pid_file.length() != 0 && ! read_pid_file()) {
- failed_to_start();
- }
- else {
- started();
- }
- }
- else {
- failed_to_start();
- }
- }
- else if (service_state == service_state_t::STOPPING) {
- // We won't log a non-zero exit status or termination due to signal here -
- // we assume that the process died because we signalled it.
- stopped();
- }
- else if (smooth_recovery && service_state == service_state_t::STARTED && desired_state == service_state_t::STARTED) {
- // TODO if we are pinned-started then we should probably check
- // that dependencies have started before trying to re-start the
- // service process.
- doing_recovery = true;
- if (! restart_ps_process()) {
- emergency_stop();
- services->processQueues();
- }
- return;
- }
- else {
- // we must be STARTED
- if (! do_auto_restart() && start_explicit) {
- start_explicit = false;
- release();
- }
- forceStop();
- stopDependents();
- stopped();
- }
- services->processQueues(false);
-}
-
-void scripted_service::handle_exit_status(int exit_status) noexcept
-{
- bool did_exit = WIFEXITED(exit_status);
- bool was_signalled = WIFSIGNALED(exit_status);
-
- if (service_state == service_state_t::STOPPING) {
- if (did_exit && WEXITSTATUS(exit_status) == 0) {
- stopped();
- }
- else {
- // ??? failed to stop! Let's log it as info:
- if (did_exit) {
- log(LogLevel::INFO, "Service ", service_name, " stop command failed with exit code ", WEXITSTATUS(exit_status));
- }
- else if (was_signalled) {
- log(LogLevel::INFO, "Serivice ", service_name, " stop command terminated due to signal ", WTERMSIG(exit_status));
- }
- // Just assume that we stopped, so that any dependencies
- // can be stopped:
- stopped();
- }
- services->processQueues(false);
- }
- else { // STARTING
- if (exit_status == 0) {
- started();
- }
- else {
- // failed to start
- if (did_exit) {
- log(LogLevel::ERROR, "Service ", service_name, " command failed with exit code ", WEXITSTATUS(exit_status));
- }
- else if (was_signalled) {
- log(LogLevel::ERROR, "Service ", service_name, " command terminated due to signal ", WTERMSIG(exit_status));
- }
- failed_to_start();
- }
- services->processQueues(true);
- }
-}
-
-rearm exec_status_pipe_watcher::fd_event(eventloop_t &loop, int fd, int flags) noexcept
-{
- base_process_service *sr = service;
- sr->waiting_for_execstat = false;
-
- int exec_status;
- int r = read(get_watched_fd(), &exec_status, sizeof(int));
- deregister(loop);
- close(get_watched_fd());
-
- if (r > 0) {
- // We read an errno code; exec() failed, and the service startup failed.
- if (sr->pid != -1) {
- sr->child_listener.deregister(eventLoop, sr->pid);
- }
- sr->pid = -1;
- log(LogLevel::ERROR, sr->service_name, ": execution failed: ", strerror(exec_status));
- if (sr->service_state == service_state_t::STARTING) {
- sr->failed_to_start();
- }
- else if (sr->service_state == service_state_t::STOPPING) {
- // Must be a scripted service. We've logged the failure, but it's probably better
- // not to leave the service in STARTED state:
- sr->stopped();
- }
- }
- else {
- // exec() succeeded.
- if (sr->record_type == service_type::PROCESS) {
- // This could be a smooth recovery (state already STARTED). Even more, the process
- // might be stopped (and killed via a signal) during smooth recovery. We don't to
- // process startup again in either case, so we check for state STARTING:
- if (sr->service_state == service_state_t::STARTING) {
- sr->started();
- }
- }
-
- if (sr->pid == -1) {
- // Somehow the process managed to complete before we even saw the status.
- sr->handle_exit_status(sr->exit_status);
- }
- }
-
- sr->services->processQueues(true);
-
- return rearm::REMOVED;
-}
-