using namespace std;
ServiceRecord *record = loadServiceRecord(name);
- record->start();
+ record->start(false);
}
void ServiceSet::stopService(const std::string & name) noexcept
service_state = ServiceState::STOPPED;
force_stop = false;
- // Stop any dependencies whose desired state is STOPPED:
- for (sr_iter i = depends_on.begin(); i != depends_on.end(); i++) {
- (*i)->dependentStopped();
- }
-
- service_set->service_inactive(this);
notifyListeners(ServiceEvent::STOPPED);
if (desired_state == ServiceState::STARTED) {
// Desired state is "started".
- start();
+ do_start();
}
- else if (socket_fd != -1) {
- close(socket_fd);
- socket_fd = -1;
+ else {
+ if (socket_fd != -1) {
+ close(socket_fd);
+ socket_fd = -1;
+ }
+
+ service_set->service_inactive(this);
+
+ // Stop any dependencies whose desired state is STOPPED:
+ for (auto i = depends_on.begin(); i != depends_on.end(); i++) {
+ (*i)->dependentStopped();
+ }
+ for (auto i = soft_deps.begin(); i != soft_deps.end(); i++) {
+ i->getTo()->dependentStopped();
+ }
}
}
void ServiceRecord::process_child_callback(struct ev_loop *loop, ev_child *w, int revents) noexcept
-{
+{
ServiceRecord *sr = (ServiceRecord *) w->data;
sr->pid = -1;
if (doing_recovery) {
// (BGPROCESS only)
doing_recovery = false;
- bool do_stop = false;
+ bool need_stop = false;
if (exit_status != 0) {
- do_stop = true;
+ need_stop = true;
}
else {
// We need to re-read the PID, since it has now changed.
if (service_type == ServiceType::BGPROCESS && pid_file.length() != 0) {
if (! read_pid_file()) {
- do_stop = true;
+ need_stop = true;
}
}
}
- if (do_stop) {
- stop();
- if (auto_restart && service_set->get_auto_restart()) {
- start();
- }
+ if (need_stop) {
+ do_stop();
}
return;
else {
forceStop();
}
-
- if (auto_restart && service_set->get_auto_restart()) {
- start();
- }
}
else { // SCRIPTED
if (service_state == ServiceState::STOPPING) {
}
}
-void ServiceRecord::start() noexcept
+void ServiceRecord::start(bool transitive) noexcept
{
if ((service_state == ServiceState::STARTING || service_state == ServiceState::STARTED)
&& desired_state == ServiceState::STOPPED) {
notifyListeners(ServiceEvent::STOPCANCELLED);
}
+ if (transitive) {
+ started_deps++;
+ }
+ else {
+ start_explicit = true;
+ }
+
if (desired_state == ServiceState::STARTED && service_state != ServiceState::STOPPED) return;
desired_state = ServiceState::STARTED;
-
+ service_set->service_active(this);
+ do_start();
+}
+
+void ServiceRecord::do_start() noexcept
+{
if (pinned_stopped) return;
if (service_state != ServiceState::STOPPED) {
// We're STOPPING, and that can be interrupted. Our dependencies might be STOPPING,
// but if so they are waiting (for us), so they too can be instantly returned to
// STARTING state.
+ notifyListeners(ServiceEvent::STOPCANCELLED);
}
service_state = ServiceState::STARTING;
- service_set->service_active(this);
waiting_for_deps = true;
if ((*i)->service_state != ServiceState::STARTED) {
if (start_deps) {
all_deps_started = false;
- (*i)->start();
+ (*i)->start(true);
}
else {
return false;
ServiceRecord * to = i->getTo();
if (start_deps) {
if (to->service_state != ServiceState::STARTED) {
- to->start();
+ to->start(true);
i->waiting_on = true;
all_deps_started = false;
}
if (force_stop || desired_state == ServiceState::STOPPED) {
// We must now stop.
- bool do_restart = (desired_state != ServiceState::STOPPED);
- stop();
- if (do_restart) {
- start();
- }
+ do_stop();
return;
}
service_set->service_inactive(this);
notifyListeners(ServiceEvent::FAILEDSTART);
+ // Stop any dependencies whose desired state is STOPPED:
+ for (auto i = depends_on.begin(); i != depends_on.end(); i++) {
+ (*i)->dependentStopped();
+ }
+ for (auto i = soft_deps.begin(); i != soft_deps.end(); i++) {
+ i->getTo()->dependentStopped();
+ }
+
// Cancel start of dependents:
for (sr_iter i = dependents.begin(); i != dependents.end(); i++) {
if ((*i)->service_state == ServiceState::STARTING) {
for (sr_iter i = dependents.begin(); i != dependents.end(); i++) {
(*i)->forceStop();
}
- stop();
+
+ if (service_state == ServiceState::STARTED) {
+ do_stop();
+ }
// We don't want to force stop soft dependencies, however.
}
void ServiceRecord::dependentStopped() noexcept
{
+ started_deps--;
if (service_state == ServiceState::STOPPING) {
// Check the other dependents before we stop.
if (stopCheckDependents()) {
allDepsStopped();
}
}
+ else if (started_deps == 0 && !start_explicit) {
+ desired_state = ServiceState::STOPPED;
+ service_state = ServiceState::STOPPING;
+ allDepsStopped();
+ }
}
void ServiceRecord::stop() noexcept
{
+ if (desired_state == ServiceState::STOPPED && service_state != ServiceState::STARTED) return;
+
+ start_explicit = false;
+ if (started_deps != 0) {
+ return;
+ }
+
if ((service_state == ServiceState::STOPPING || service_state == ServiceState::STOPPED)
&& desired_state == ServiceState::STARTED) {
// The service *was* stopped/stopping, but it was going to restart.
notifyListeners(ServiceEvent::STARTCANCELLED);
}
- if (desired_state == ServiceState::STOPPED && service_state != ServiceState::STARTED) return;
-
desired_state = ServiceState::STOPPED;
+ do_stop();
+}
+
+void ServiceRecord::do_stop() noexcept
+{
if (pinned_started) return;
if (service_state != ServiceState::STARTED) {
stopDependents();
return;
}
+
+ // We must have had desired_state == STARTED.
+ notifyListeners(ServiceEvent::STARTCANCELLED);
// Reaching this point, we have can_interrupt_start() == true. So,
// we can stop. Dependents might be starting, but they must be
// waiting on us, so they should also be immediately stoppable.
- // Fall through to below.
+ // Fall through to below,.
}
else {
// If we're starting we need to wait for that to complete.
waiting_for_deps = true;
// If we get here, we are in STARTED state; stop all dependents.
- if (stopDependents()) {
+ if (stopCheckDependents()) {
allDepsStopped();
}
}
void ServiceRecord::pinStart() noexcept
{
- start();
+ start(false);
pinned_started = true;
}
if (pinned_stopped) {
pinned_stopped = false;
if (desired_state == ServiceState::STARTED) {
- start();
+ do_start();
}
}
}
OnstartFlags onstart_flags;
string logfile; // log file name, empty string specifies /dev/null
+
bool auto_restart : 1; // whether to restart this (process) if it dies unexpectedly
bool smooth_recovery : 1; // whether the service process can restart without bringing down service
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
+ int started_deps = 0; // number of dependents that require this service to be started
typedef std::list<ServiceRecord *> sr_list;
typedef sr_list::iterator sr_iter;
// Release console (console must be currently held by this service)
void releaseConsole() noexcept;
+ bool get_start_flag(bool transitive)
+ {
+ return transitive ? started_deps != 0 : start_explicit;
+ }
+
public:
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), force_stop(false)
+ waiting_for_execstat(false), doing_recovery(false),
+ start_explicit(false), force_stop(false)
{
service_set = set;
service_name = name;
const char *getServiceName() const noexcept { return service_name.c_str(); }
ServiceState getState() const noexcept { return service_state; }
- void start() noexcept; // start the service
+ void start(bool transitive = false) noexcept; // start the service
void stop() noexcept; // stop the service
+ void do_start() noexcept;
+ void do_stop() noexcept;
void pinStart() noexcept; // start the service and pin it
void pinStop() noexcept; // stop the service and pin it