// Must deregister now since handle_exit_status might result in re-launch:
deregister(loop, child);
- sr->handle_exit_status();
+ sr->handle_exit_status(status);
return rearm::REMOVED;
}
return false;
}
-void ServiceRecord::handle_exit_status() noexcept
+void ServiceRecord::handle_exit_status(int exit_status) noexcept
{
bool did_exit = WIFEXITED(exit_status);
bool was_signalled = WIFSIGNALED(exit_status);
log(LogLevel::ERROR, "Service ", service_name, " terminated due to signal ", WTERMSIG(exit_status));
}
}
+
+ // BGPROCESS and PROCESS override this method; we must be a SCRIPTED service.
+ if (service_state == ServiceState::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();
+ }
+ service_set->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();
+ }
+ service_set->processQueues(true);
+ }
+}
+
+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 != ServiceState::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 == ServiceState::STARTING) {
+ if (did_exit && WEXITSTATUS(exit_status) == 0) {
+ started();
+ }
+ else {
+ failed_to_start();
+ }
+ }
+ else if (service_state == ServiceState::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 == ServiceState::STARTED && desired_state == ServiceState::STARTED) {
+ // TODO ensure a minimum time between restarts
+ // TODO if we are pinned-started then we should probably check
+ // that dependencies have started before trying to re-start the
+ // service process.
+ start_ps_process();
+ return;
+ }
+ else {
+ if (! do_auto_restart()) desired_state = ServiceState::STOPPED;
+ forceStop();
+ }
+ service_set->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 != ServiceState::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)
}
}
}
-
+
if (need_stop) {
// Failed startup: no auto-restart.
desired_state = ServiceState::STOPPED;
forceStop();
service_set->processQueues(false);
}
-
+
return;
}
-
- if (service_type == ServiceType::PROCESS || service_type == ServiceType::BGPROCESS) {
- if (service_state == ServiceState::STARTING) {
- // (only applies to BGPROCESS)
- if (did_exit && WEXITSTATUS(exit_status) == 0) {
- started();
- }
- else {
- failed_to_start();
- }
- }
- else if (service_state == ServiceState::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 == ServiceState::STARTED && desired_state == ServiceState::STARTED) {
- // TODO ensure a minimum time between restarts
- // 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 = (service_type == ServiceType::BGPROCESS);
- start_ps_process();
- return;
+
+ if (service_state == ServiceState::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) {
+ started();
}
else {
- if (! do_auto_restart()) desired_state = ServiceState::STOPPED;
- forceStop();
+ failed_to_start();
}
- service_set->processQueues(false);
}
- else { // SCRIPTED
- if (service_state == ServiceState::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();
- }
- service_set->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();
- }
- service_set->processQueues(true);
- }
+ else if (service_state == ServiceState::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 == ServiceState::STARTED && desired_state == ServiceState::STARTED) {
+ // TODO ensure a minimum time between restarts
+ // 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;
+ start_ps_process();
+ return;
+ }
+ else {
+ if (! do_auto_restart()) desired_state = ServiceState::STOPPED;
+ forceStop();
}
+ service_set->processQueues(false);
}
rearm ServiceIoWatcher::fd_event(EventLoop_t &loop, int fd, int flags) noexcept
if (sr->pid == -1) {
// Somehow the process managed to complete before we even saw the status.
- sr->handle_exit_status();
+ sr->handle_exit_status(sr->exit_status);
}
}
* in the STARTING state when waiting for dependencies to start or for the exec() call in
* the forked child to complete and return a status.
*
- * Aquisition/release:
+ * Acquisition/release:
* ------------------
* Each service has a dependent-count ("required_by"). This starts at 0, adds 1 if the
* service has explicitly been started (i.e. "start_explicit" is true), and adds 1 for
friend class ServiceChildWatcher;
friend class ServiceIoWatcher;
+ protected:
typedef std::string string;
string service_name;
bool pinned_started : 1;
bool waiting_for_deps : 1; // if STARTING, whether we are waiting for dependencies (inc console) to start
bool waiting_for_execstat : 1; // if we are waiting for exec status after fork()
- bool doing_recovery : 1; // if we are currently recovering a BGPROCESS (restarting process, while
- // holding STARTED service state)
bool start_explicit : 1; // whether we are are explictly required to be started
bool prop_require : 1; // require must be propagated
string socket_path; // path to the socket for socket-activation service
int socket_perms; // socket permissions ("mode")
uid_t socket_uid = -1; // socket user id or -1
- gid_t socket_gid = -1; // sockget group id or -1
+ gid_t socket_gid = -1; // socket group id or -1
// Implementation details
ServiceRecord *next_in_stop_queue = nullptr;
- private:
+ protected:
// All dependents have stopped.
void allDepsStopped();
static void process_child_callback(EventLoop_t *loop, ServiceChildWatcher *w,
int revents) noexcept;
- void handle_exit_status() noexcept;
+ virtual void handle_exit_status(int exit_status) noexcept;
// A dependency has reached STARTED state
void dependencyStarted() noexcept;
ServiceRecord(ServiceSet *set, string name)
: service_state(ServiceState::STOPPED), desired_state(ServiceState::STOPPED), auto_restart(false),
pinned_stopped(false), pinned_started(false), waiting_for_deps(false),
- waiting_for_execstat(false), doing_recovery(false),
- start_explicit(false), prop_require(false), prop_release(false), prop_failure(false),
+ waiting_for_execstat(false), start_explicit(false),
+ prop_require(false), prop_release(false), prop_failure(false),
force_stop(false), child_listener(this), child_status_listener(this)
{
service_set = set;
}
}
- // TODO write a destructor
+ virtual ~ServiceRecord() noexcept
+ {
+ }
// begin transition from stopped to started state or vice versa depending on current and desired state
void execute_transition() noexcept;
}
};
+class process_service : public ServiceRecord
+{
+ virtual void handle_exit_status(int exit_status) noexcept override;
+
+ public:
+ process_service(ServiceSet *sset, string name, string &&command,
+ std::list<std::pair<unsigned,unsigned>> &command_offsets,
+ sr_list * pdepends_on, sr_list * pdepends_soft)
+ : ServiceRecord(sset, name, ServiceType::PROCESS, std::move(command), command_offsets,
+ pdepends_on, pdepends_soft)
+ {
+ }
+
+ ~process_service() noexcept
+ {
+ }
+};
+
+class bgproc_service : public ServiceRecord
+{
+ virtual void handle_exit_status(int exit_status) noexcept override;
+
+ bool doing_recovery : 1; // if we are currently recovering a BGPROCESS (restarting process, while
+ // holding STARTED service state)
+
+ public:
+ bgproc_service(ServiceSet *sset, string name, string &&command,
+ std::list<std::pair<unsigned,unsigned>> &command_offsets,
+ sr_list * pdepends_on, sr_list * pdepends_soft)
+ : ServiceRecord(sset, name, ServiceType::BGPROCESS, std::move(command), command_offsets,
+ pdepends_on, pdepends_soft)
+ {
+ doing_recovery = false;
+ }
+
+ ~bgproc_service() noexcept
+ {
+ }
+};
+
/*
* A ServiceSet, as the name suggests, manages a set of services.
*