--- /dev/null
+#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
}
service_set->service_inactive(this);
+ notifyListeners(ServiceEvent::STOPPED);
- // TODO inform listeners.
if (desired_state == ServiceState::STARTED) {
// Desired state is "started".
start();
&& 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;
{
logServiceStarted(service_name);
service_state = ServiceState::STARTED;
- // TODO - inform listeners
+ notifyListeners(ServiceEvent::STARTED);
if (onstart_flags.release_console) {
log_to_console = false;
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++) {
&& 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;
+#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
return r;
}
+
class ServiceRecord
{
typedef std::string string;
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
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)
{
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
};
}
}
};
+
+#endif