Use C++11 typesafe enums for service type (ServiceType) and state
authorDavin McCall <davmac@davmac.org>
Mon, 16 Nov 2015 00:28:41 +0000 (00:28 +0000)
committerDavin McCall <davmac@davmac.org>
Mon, 16 Nov 2015 00:28:41 +0000 (00:28 +0000)
(ServiceState).

load_service.cc
service.cc
service.h

index 95fd5b0791188eb6d293ee953d3687403ea1ed75..67c2702ec6f2cd44c79ca0a0d726b807ead627e0 100644 (file)
@@ -215,7 +215,7 @@ ServiceRecord * ServiceSet::loadServiceRecord(const char * name)
     const char ** commands = nullptr;
     int num_args = 0;
 
-    int service_type = SVC_PROCESS;
+    ServiceType service_type = ServiceType::PROCESS;
     std::list<ServiceRecord *> depends_on;
     std::list<ServiceRecord *> depends_soft;
     string logfile;
@@ -280,13 +280,13 @@ ServiceRecord * ServiceSet::loadServiceRecord(const char * name)
             else if (setting == "type") {
                 string type_str = read_setting_value(i, end);
                 if (type_str == "scripted") {
-                    service_type = SVC_SCRIPTED;
+                    service_type = ServiceType::SCRIPTED;
                 }
                 else if (type_str == "process") {
-                    service_type = SVC_PROCESS;
+                    service_type = ServiceType::PROCESS;
                 }
                 else if (type_str == "internal") {
-                    service_type = SVC_INTERNAL;
+                    service_type = ServiceType::INTERNAL;
                 }
                 else {
                     throw ServiceDescriptionExc(name, "Service type must be \"scripted\""
index 6bc34e1052ea1c3d7453aada02bc3bd91f155285..60f1b7ffc74a051215c4a3e7fd440c0892c563f1 100644 (file)
@@ -48,10 +48,10 @@ void ServiceSet::stopService(const std::string & name)
 // Called when a service has actually stopped.
 void ServiceRecord::stopped()
 {
-    service_state = SVC_STOPPED;
+    service_state = ServiceState::STOPPED;
     force_stop = false;
     
-    // Stop any dependencies whose desired state is SVC_STOPPED:
+    // Stop any dependencies whose desired state is STOPPED:
     for (sr_iter i = depends_on.begin(); i != depends_on.end(); i++) {
         (*i)->dependentStopped();
     }
@@ -59,7 +59,7 @@ void ServiceRecord::stopped()
     service_set->service_inactive(this);
     
     // TODO inform listeners.
-    if (desired_state == SVC_STARTED) {
+    if (desired_state == ServiceState::STARTED) {
         // Desired state is "started".
         start();
     }
@@ -74,12 +74,12 @@ void ServiceRecord::process_child_callback(struct ev_loop *loop, ev_child *w, in
     
     // Ok, for a process service, any process death which we didn't rig
     // ourselves is a bit... unexpected. Probably, the child died because
-    // we asked it to (sr->service_state == SVC_STOPPING). But even if
+    // we asked it to (sr->service_state == STOPPING). But even if
     // we didn't, there's not much we can do.
     
-    if (sr->service_type == SVC_PROCESS) {
+    if (sr->service_type == ServiceType::PROCESS) {
         // TODO log non-zero rstatus?
-        if (sr->service_state == SVC_STOPPING) {
+        if (sr->service_state == ServiceState::STOPPING) {
             sr->stopped();
         }
         else {
@@ -90,8 +90,8 @@ void ServiceRecord::process_child_callback(struct ev_loop *loop, ev_child *w, in
             sr->start();
         }
     }
-    else {  // SVC_SCRIPTED
-        if (sr->service_state == SVC_STOPPING) {
+    else {  // SCRIPTED
+        if (sr->service_state == ServiceState::STOPPING) {
             if (w->rstatus == 0) {
                 sr->stopped();
             }
@@ -103,7 +103,7 @@ void ServiceRecord::process_child_callback(struct ev_loop *loop, ev_child *w, in
                 sr->stopped();
             }
         }
-        else { // SVC_STARTING
+        else { // STARTING
             if (w->rstatus == 0) {
                 sr->started();
             }
@@ -117,8 +117,8 @@ void ServiceRecord::process_child_callback(struct ev_loop *loop, ev_child *w, in
 
 void ServiceRecord::start()
 {
-    if ((service_state == SVC_STARTING || service_state == SVC_STARTED)
-            && desired_state == SVC_STOPPED) {
+    if ((service_state == ServiceState::STARTING || service_state == ServiceState::STARTED)
+            && desired_state == ServiceState::STOPPED) {
         // This service was starting, or started, but was set to be stopped.
         // Cancel the stop (and continue starting/running).
         // TODO any listeners waiting for stop should be notified of
@@ -126,9 +126,9 @@ void ServiceRecord::start()
     }
 
     auto old_desired_state = desired_state;
-    desired_state = SVC_STARTED;
+    desired_state = ServiceState::STARTED;
     
-    if (service_state == SVC_STARTED || service_state == SVC_STARTING) {
+    if (service_state == ServiceState::STARTED || service_state == ServiceState::STARTING) {
         // We couldn't be started or starting unless all dependencies have
         // already started: so there's nothing left to do.
         return;
@@ -141,17 +141,17 @@ void ServiceRecord::start()
     for (sr_iter i = depends_on.begin(); i != depends_on.end(); ++i) {
         // Note, we cannot treat a dependency as started if its force_stop
         // flag is set.
-        if ((*i)->service_state != SVC_STARTED || (*i)->force_stop) {
+        if ((*i)->service_state != ServiceState::STARTED || (*i)->force_stop) {
             all_deps_started = false;
             (*i)->start();
         }
     }
     
-    if (old_desired_state != SVC_STARTED) {
+    if (old_desired_state != ServiceState::STARTED) {
         // This is a fresh start, so we mark all soft dependencies as 'waiting on' and ask them
         // to start:
         for (auto i = soft_deps.begin(); i != soft_deps.end(); ++i) {
-            if (i->getTo()->service_state != SVC_STARTED) {
+            if (i->getTo()->service_state != ServiceState::STARTED) {
                 all_deps_started = false;
                 i->getTo()->start();
                 i->waiting_on = true;
@@ -164,7 +164,7 @@ void ServiceRecord::start()
         for (auto i = soft_deps.begin(); i != soft_deps.end(); ++i) {
             ServiceRecord * to = i->getTo();
             if (i->waiting_on) {
-                if ((to->desired_state != SVC_STARTED && to->service_state != SVC_STARTING) || to->service_state == SVC_STARTED) {
+                if ((to->desired_state != ServiceState::STARTED && to->service_state != ServiceState::STARTING) || to->service_state == ServiceState::STARTED) {
                     // Service has either started or is no longer starting
                     i->waiting_on = false;
                 }
@@ -181,10 +181,10 @@ void ServiceRecord::start()
     }
     
     // Actually start this service.
-    service_state = SVC_STARTING;
+    service_state = ServiceState::STARTING;
     service_set->service_active(this);
     
-    if (service_type == SVC_PROCESS) {
+    if (service_type == ServiceType::PROCESS) {
         bool start_success = start_ps_process();
         if (start_success) {
             started();
@@ -193,7 +193,7 @@ void ServiceRecord::start()
             failed_to_start();
         }
     }
-    else if (service_type == SVC_SCRIPTED) {
+    else if (service_type == ServiceType::SCRIPTED) {
         // Script-controlled service
         bool start_success = start_ps_process(std::vector<std::string>(1, "start"));
         if (! start_success) {
@@ -208,18 +208,18 @@ void ServiceRecord::start()
 
 void ServiceRecord::started()
 {
-    service_state = SVC_STARTED;
+    service_state = ServiceState::STARTED;
     // TODO - inform listeners
 
-    if (desired_state == SVC_STARTED) {
-        // Start any dependents whose desired state is SVC_STARTED:
+    if (desired_state == ServiceState::STARTED) {
+        // Start any dependents whose desired state is STARTED:
         for (auto i = dependents.begin(); i != dependents.end(); i++) {
-            if ((*i)->desired_state == SVC_STARTED) {
+            if ((*i)->desired_state == ServiceState::STARTED) {
                 (*i)->start();
             }
         }
         for (auto i = soft_dpts.begin(); i != soft_dpts.end(); i++) {
-            if ((*i)->getFrom()->desired_state == SVC_STARTED) {
+            if ((*i)->getFrom()->desired_state == ServiceState::STARTED) {
                 (*i)->getFrom()->start();
             }
         }
@@ -231,18 +231,18 @@ void ServiceRecord::started()
 
 void ServiceRecord::failed_to_start()
 {
-    service_state = SVC_STOPPED;
-    desired_state = SVC_STOPPED;
+    service_state = ServiceState::STOPPED;
+    desired_state = ServiceState::STOPPED;
     service_set->service_inactive(this);
     // failure to start
     // Cancel start of dependents:
     for (sr_iter i = dependents.begin(); i != dependents.end(); i++) {
-        if ((*i)->desired_state == SVC_STARTED) {
+        if ((*i)->desired_state == ServiceState::STARTED) {
             (*i)->failed_dependency();
         }
     }    
     for (auto i = soft_dpts.begin(); i != soft_dpts.end(); i++) {
-        if ((*i)->getFrom()->desired_state == SVC_STARTED) {
+        if ((*i)->getFrom()->desired_state == ServiceState::STARTED) {
             // We can send 'start', because this is only a soft dependency.
             // Our startup failure means that they don't have to wait for us.
             (*i)->getFrom()->start();
@@ -385,19 +385,19 @@ void ServiceRecord::forceStop()
 // A dependency of this service failed to start.
 void ServiceRecord::failed_dependency()
 {
-    desired_state = SVC_STOPPED;
+    desired_state = ServiceState::STOPPED;
     
     // Presumably, we were starting. So now we're not.
-    service_state = SVC_STOPPED;
+    service_state = ServiceState::STOPPED;
     
     // Notify dependents of this service also
     for (auto i = dependents.begin(); i != dependents.end(); i++) {
-        if ((*i)->desired_state == SVC_STARTED) {
+        if ((*i)->desired_state == ServiceState::STARTED) {
             (*i)->failed_dependency();
         }
     }
     for (auto i = soft_dpts.begin(); i != soft_dpts.end(); i++) {
-        if ((*i)->getFrom()->desired_state == SVC_STARTED) {
+        if ((*i)->getFrom()->desired_state == ServiceState::STARTED) {
             // It's a soft dependency, so send them 'started' rather than
             // 'failed dep'.
             (*i)->getFrom()->started();
@@ -407,7 +407,7 @@ void ServiceRecord::failed_dependency()
 
 void ServiceRecord::dependentStopped()
 {
-    if (service_state != SVC_STOPPED && (desired_state == SVC_STOPPED || force_stop)) {
+    if (service_state != ServiceState::STOPPED && (desired_state == ServiceState::STOPPED || force_stop)) {
         // Check the other dependents before we stop.
         if (stopCheckDependents()) {
             stopping();
@@ -417,19 +417,19 @@ void ServiceRecord::dependentStopped()
 
 void ServiceRecord::stop()
 {
-    if ((service_state == SVC_STOPPING || service_state == SVC_STOPPED)
-            && desired_state == SVC_STARTED) {
+    if ((service_state == ServiceState::STOPPING || service_state == ServiceState::STOPPED)
+            && desired_state == ServiceState::STARTED) {
         // The service *was* stopped/stopping, but it was going to restart.
         // Now, we'll cancel the restart.
         // TODO inform listeners waiting for start of cancellation
     }
     
-    if (desired_state == SVC_STOPPED) return;
+    if (desired_state == ServiceState::STOPPED) return;
     
-    desired_state = SVC_STOPPED;
+    desired_state = ServiceState::STOPPED;
 
-    if (service_state != SVC_STARTED) {
-        if (service_state == SVC_STARTING) {
+    if (service_state != ServiceState::STARTED) {
+        if (service_state == ServiceState::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.
@@ -452,7 +452,7 @@ bool ServiceRecord::stopCheckDependents()
 {
     bool all_deps_stopped = true;
     for (sr_iter i = dependents.begin(); i != dependents.end(); ++i) {
-        if ((*i)->service_state != SVC_STOPPED) {
+        if ((*i)->service_state != ServiceState::STOPPED) {
             all_deps_stopped = false;
             break;
         }
@@ -465,7 +465,7 @@ bool ServiceRecord::stopDependents()
 {
     bool all_deps_stopped = true;
     for (sr_iter i = dependents.begin(); i != dependents.end(); ++i) {
-        if ((*i)->service_state != SVC_STOPPED) {
+        if ((*i)->service_state != ServiceState::STOPPED) {
             all_deps_stopped = false;
             (*i)->stop();
         }
@@ -479,9 +479,9 @@ bool ServiceRecord::stopDependents()
 // Dependency stopped or is stopping; we must stop too.
 void ServiceRecord::stopping()
 {
-    service_state = SVC_STOPPING;
+    service_state = ServiceState::STOPPING;
 
-    if (service_type == SVC_PROCESS) {
+    if (service_type == ServiceType::PROCESS) {
         if (pid != -1) {
           // The process is still kicking on - must actually kill it.
           kill(pid, SIGTERM);
index f47f73a612f684eb57ed3fb4f4150d4b679ac425..2a55c7111f75aa24ff4f26261d4e2ab8731f211c 100644 (file)
--- a/service.h
+++ b/service.h
@@ -7,40 +7,44 @@
  * 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.
+ * either STARTED or STOPPED. The current state can also be STARTING or 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.
+ *      STOPPED/STOPPED  : stopped and will remain stopped
+ *      STOPPED/STARTED  : stopped and will be started; waiting for dependencies to start.
+ *      STARTING/STARTED : starting, but not yet started. All dependencies have started already.
+ *      STARTING/STOPPED : as above, but the service will be stopped again as soon as it has
+ *                         completed startup.
+ *      STARTED/STARTED  : running and will continue running.
+ *      STARTED/STOPPED  : running but will stop; waiting for dependents to stop.
+ *      STOPPING/STOPPED : stopping and will stop. All dependents have stopped.
+ *      STOPPING/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 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.
+enum class ServiceState {
+    STOPPED,    // service is not running.
+    STARTING,   // service is starting, and will start (or fail to start) in time. All dependencies have started.
+    STARTED,    // service is running,
+    STOPPING    // service script is stopping and will stop.
+};
+
 
 
 /* Service types */
-#define SVC_DUMMY    0  /* dummy service, used to detect cyclic dependencies */
-#define SVC_PROCESS  1  /* service runs as a process, and can be stopped
-                           by sending the process a signal */
-#define SVC_SCRIPTED 2  /* service requires a command to start, and another
-                           command to stop */
-#define SVC_INTERNAL 3  /* internal service, runs no external process */
+enum class ServiceType {
+    DUMMY,      // dummy service, used to detect cyclice dependencies
+    PROCESS,    // service runs as a process, and can be stopped by
+                // sending the process a signal (SIGTERM)
+    SCRIPTED,   // service requires an external command to start,
+                // and a second command to stop
+    INTERNAL    // internal service, runs no external process
+};
+
 
-// Exception loading service
+// Exception while loading a service
 class ServiceLoadExc
 {
     public:
@@ -115,9 +119,9 @@ class ServiceRecord
     typedef std::string string;
     
     string service_name;
-    int service_type;  /* SVC_DAEMON or SVC_SCRIPTED */
-    int service_state; /* SVC_STOPPED, _STARTING, _STARTED, _STOPPING */
-    int desired_state; /* SVC_STOPPED / SVC_STARTED */
+    ServiceType service_type;  /* ServiceType::DUMMY, PROCESS, SCRIPTED, INTERNAL */
+    ServiceState service_state; /* ServiceState::STOPPED, STARTING, STARTED, STOPPING */
+    ServiceState desired_state; /* ServiceState::STOPPED / STARTED */
     bool force_stop; // true if the service must actually stop. This is the
                      // case if for example the process dies; the service,
                      // and all its dependencies, MUST be stopped.
@@ -196,16 +200,16 @@ class ServiceRecord
     public:
 
     ServiceRecord(ServiceSet *set, string name)
-        : service_state(SVC_STOPPED), desired_state(SVC_STOPPED), force_stop(false), auto_restart(false)
+        : service_state(ServiceState::STOPPED), desired_state(ServiceState::STOPPED), force_stop(false), auto_restart(false)
     {
         service_set = set;
         service_name = name;
-        service_type = SVC_DUMMY;
+        service_type = ServiceType::DUMMY;
     }
     
-    ServiceRecord(ServiceSet *set, string name, int service_type, string command, const char ** commands,
+    ServiceRecord(ServiceSet *set, string name, ServiceType service_type, string command, const char ** commands,
             int num_argsx, sr_list * pdepends_on, sr_list * pdepends_soft)
-        : service_state(SVC_STOPPED), desired_state(SVC_STOPPED), force_stop(false), auto_restart(false)
+        : service_state(ServiceState::STOPPED), desired_state(ServiceState::STOPPED), force_stop(false), auto_restart(false)
     {
         service_set = set;
         service_name = name;
@@ -244,14 +248,14 @@ class ServiceRecord
     }
     
     const char *getServiceName() const { return service_name.c_str(); }
-    int getState() const { return service_state; }
+    ServiceState getState() const { return service_state; }
     
     void start();  // start the service
     void stop();   // stop the service
     
     bool isDummy()
     {
-        return service_type == SVC_DUMMY;
+        return service_type == ServiceType::DUMMY;
     }
 };
 
@@ -293,11 +297,11 @@ class ServiceSet
     // transition to the 'stopped' state.
     void stopService(const std::string &name);
     
-    // Notification from service that it is active (state != SVC_STOPPED)
+    // Notification from service that it is active (state != STOPPED)
     // Only to be called on the transition from inactive to active.
     void service_active(ServiceRecord *);
     
-    // Notification from service that it is inactive (SVC_STOPPED)
+    // Notification from service that it is inactive (STOPPED)
     // Only to be called on the transition from active to inactive.
     void service_inactive(ServiceRecord *);