Add an event listener interface to services.
authorDavin McCall <davmac@davmac.org>
Sun, 27 Dec 2015 11:56:21 +0000 (11:56 +0000)
committerDavin McCall <davmac@davmac.org>
Sun, 27 Dec 2015 11:56:21 +0000 (11:56 +0000)
service-listener.h [new file with mode: 0644]
service.cc
service.h

diff --git a/service-listener.h b/service-listener.h
new file mode 100644 (file)
index 0000000..b443602
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef SERVICE_LISTENER_H
+#define SERVICE_LISTENER_H
+
+class ServiceRecord;
+
+enum class ServiceEvent {
+    STARTED,           // Service was started (reached STARTED state)
+    STOPPED,           // Service was stopped (reached STOPPED state)
+    FAILEDSTART,       // Service failed to start (possibly due to dependency failing)
+    STARTCANCELLED,    // Service was set to be started but a stop was requested
+    STOPCANCELLED      // Service was set to be stopped but a start was requested
+};
+
+// Interface for listening to services
+class ServiceListener
+{
+    public:
+    
+    // An event occurred on the service being observed.
+    // Listeners must not be added or removed during event notification.
+    virtual void serviceEvent(ServiceRecord * service, ServiceEvent event) noexcept = 0;
+};
+
+#endif
index dcda52d6f4e6cb8c8adcdde12b273759057a2556..5f0b3b45ce5f6189cf8905d48b39a4d1533e8e6d 100644 (file)
@@ -62,8 +62,8 @@ void ServiceRecord::stopped() noexcept
     }
 
     service_set->service_inactive(this);
+    notifyListeners(ServiceEvent::STOPPED);
     
-    // TODO inform listeners.
     if (desired_state == ServiceState::STARTED) {
         // Desired state is "started".
         start();
@@ -126,8 +126,7 @@ void ServiceRecord::start(bool unpinned) noexcept
             && 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
-        //      its cancellation
+        notifyListeners(ServiceEvent::STOPCANCELLED);
     }
 
     if (desired_state == ServiceState::STARTED && !unpinned) return;
@@ -236,7 +235,7 @@ void ServiceRecord::started()
 {
     logServiceStarted(service_name);
     service_state = ServiceState::STARTED;
-    // TODO - inform listeners
+    notifyListeners(ServiceEvent::STARTED);
 
     if (onstart_flags.release_console) {
         log_to_console = false;
@@ -271,6 +270,8 @@ void ServiceRecord::failed_to_start()
     service_state = ServiceState::STOPPED;
     desired_state = ServiceState::STOPPED;
     service_set->service_inactive(this);
+    notifyListeners(ServiceEvent::FAILEDSTART);
+    
     // failure to start
     // Cancel start of dependents:
     for (sr_iter i = dependents.begin(); i != dependents.end(); i++) {
@@ -453,7 +454,7 @@ void ServiceRecord::stop(bool unpinned) noexcept
             && 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
+        notifyListeners(ServiceEvent::STARTCANCELLED);
     }
     
     if (desired_state == ServiceState::STOPPED && !unpinned) return;
index 1c8963f1b2de2c103d469b96396e44fee0fd2ec0..202f01a216e46282bded094f8b897d7dae7c3150 100644 (file)
--- a/service.h
+++ b/service.h
@@ -1,9 +1,14 @@
+#ifndef SERVICE_H
+#define SERVICE_H
+
 #include <string>
 #include <list>
 #include <vector>
 #include <csignal>
+#include <unordered_set>
 #include "ev.h"
 #include "control.h"
+#include "service-listener.h"
 
 /*
  * Possible service states
@@ -157,6 +162,7 @@ static const char ** separate_args(std::string &s, std::list<std::pair<unsigned,
     return r;
 }
 
+
 class ServiceRecord
 {
     typedef std::string string;
@@ -199,6 +205,8 @@ class ServiceRecord
     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.
+    
+    std::unordered_set<ServiceListener *> listeners;
 
     int term_signal = -1;  // signal to use for process termination
 
@@ -254,6 +262,14 @@ class ServiceRecord
     
     void forceStop() noexcept; // force-stop this service and all dependents
     
+    void notifyListeners(ServiceEvent event)
+    {
+        for (auto l : listeners) {
+            l->serviceEvent(this, event);
+        }
+    }
+    
+    
     public:
 
     ServiceRecord(ServiceSet *set, string name)
@@ -331,6 +347,20 @@ class ServiceRecord
     {
         return service_type == ServiceType::DUMMY;
     }
+    
+    // Add a listener. A listener must only be added once. May throw std::bad_alloc.
+    void addListener(ServiceListener * listener)
+    {
+        listeners.insert(listener);
+    }
+    
+    // Remove a listener.    
+    void removeListener(ServiceListener * listener) noexcept
+    {
+        listeners.erase(listener);
+    }
+    
+    // TODO notify listeners when service events occur
 };
 
 
@@ -430,3 +460,5 @@ class ServiceSet
         }
     }
 };
+
+#endif