From: Davin McCall Date: Mon, 7 Sep 2015 17:12:51 +0000 (+0100) Subject: Set dependents desired state to STOPPED immediately X-Git-Tag: v0.01~148 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=7e31f68e71fe0d29dbc0628d888f13b59309a8db;p=oweals%2Fdinit.git Set dependents desired state to STOPPED immediately when stop of dependency requested. Previously, if the state was STARTING when the stop was issued, dependencies were not stopped. --- diff --git a/service.cc b/service.cc index 047a1b6..a040deb 100644 --- a/service.cc +++ b/service.cc @@ -284,7 +284,7 @@ bool ServiceRecord::start_ps_process(const std::vector &pargs) // Tokenize the command, and add additional arguments from pargs: vector progAndArgs = tokenize(program_name); progAndArgs.insert(progAndArgs.end(), pargs.begin(), pargs.end()); - + const char * pname = progAndArgs[0].c_str(); const char ** args = new const char *[progAndArgs.size() + 1]; @@ -367,15 +367,8 @@ void ServiceRecord::failed_dependency() void ServiceRecord::dependentStopped() { if (service_state != SVC_STOPPED && (desired_state == SVC_STOPPED || force_stop)) { - bool all_deps_stopped = true; - for (sr_iter i = dependents.begin(); i != dependents.end(); ++i) { - if ((*i)->service_state != SVC_STOPPED) { - all_deps_stopped = false; - break; - } - } - - if (all_deps_stopped) { + // Check the other dependents before we stop. + if (stopCheckDependents()) { stopping(); } } @@ -390,33 +383,58 @@ void ServiceRecord::stop() // TODO inform listeners waiting for start of cancellation } + if (desired_state == SVC_STOPPED) return; + desired_state = SVC_STOPPED; if (service_state != SVC_STARTED) { + if (service_state == SVC_STARTING) { + // Well this is awkward: we're going to have to continue + // starting, but we don't want any dependents to think that + // they are still waiting to start. + // Make sure they remain stopped: + stopDependents(); + } + // If we're starting we need to wait for that to complete. // If we're already stopping/stopped there's nothing to do. return; } - - // Make sure all dependents have stopped. + // If we get here, we are in STARTED state; stop all dependents. + if (stopCheckDependents()) { + stopping(); + } +} + +bool ServiceRecord::stopCheckDependents() +{ bool all_deps_stopped = true; for (sr_iter i = dependents.begin(); i != dependents.end(); ++i) { if ((*i)->service_state != SVC_STOPPED) { all_deps_stopped = false; - (*i)->stop(); + break; } } - if (! all_deps_stopped) { - // The dependents will notify this service once they've stopped. - return; + return all_deps_stopped; +} + +bool ServiceRecord::stopDependents() +{ + bool all_deps_stopped = true; + for (sr_iter i = dependents.begin(); i != dependents.end(); ++i) { + if ((*i)->service_state != SVC_STOPPED) { + all_deps_stopped = false; + (*i)->stop(); + } } - // Ok, dependents have stopped. We can stop ourselves. - stopping(); + return all_deps_stopped; } + + // Dependency stopped or is stopping; we must stop too. void ServiceRecord::stopping() { diff --git a/service.h b/service.h index 011d24b..5dea8a8 100644 --- a/service.h +++ b/service.h @@ -3,12 +3,33 @@ #include #include "ev.h" -/* Possible service states */ +/* + * Possible service states + * + * Services have both a current state and a desired state. The desired state can be + * either SVC_STARTED or SVC_STOPPED. The current state can also be SVC_STARTING + * or SVC_STOPPING. + * + * The total state is a combination of the two, current and desired: + * SVC_STOPPED/SVC_STOPPED : stopped and will remain stopped + * SVC_STOPPED/SVC_STARTED : stopped and will be started; waiting for dependencies to start. + * SVC_STARTING/SVC_STARTED : starting, but not yet started. All dependencies have started already. + * SVC_STARTING/SVC_STOPPED : as above, but the service will be stopped again as soon as it has + * completed startup. + * SVC_STARTED/SVC_STARTED : running and will continue running. + * SVC_STARTED/SVC_STOPPED : running but will stop; waiting for dependents to stop. + * SVC_STOPPING/SVC_STOPPED : stopping and will stop. All dependents have stopped. + * SVC_STOPPING/SVC_STARTED : as above, but the service will be re-started again once it stops. + * + * A scripted service is in the STARTING/STOPPING states during the script execution. + * A process service is in the STOPPING state when it has been signalled to stop (and is never + * in the STARTING state; it moves directly from STOPPED to STARTED). + */ // TODO can we use typesafe enum? constexpr static int SVC_STOPPED = 0; // service is not running -constexpr static int SVC_STARTING = 1; // service script is running with "start" -constexpr static int SVC_STARTED = 2; // service is running; start script finished. -constexpr static int SVC_STOPPING = 3; // service script is running with "stop" +constexpr static int SVC_STARTING = 1; // service is starting, and will start (or fail to start) in time. All dependencies have started. +constexpr static int SVC_STARTED = 2; // service is running +constexpr static int SVC_STOPPING = 3; // service script is stopping and will stop. /* Service types */ @@ -123,7 +144,14 @@ class ServiceRecord static void process_child_callback(struct ev_loop *loop, struct ev_child *w, int revents); - void dependentStopped(); // called when a dependent stopped + // A dependent has reached STOPPED state + void dependentStopped(); + + // check if all dependents have stopped + bool stopCheckDependents(); + + // issue a stop to all dependents, return true if they are all already stopped + bool stopDependents(); void forceStop(); // force-stop this service and all dependents