7 * Possible service states
9 * Services have both a current state and a desired state. The desired state can be
10 * either SVC_STARTED or SVC_STOPPED. The current state can also be SVC_STARTING
13 * The total state is a combination of the two, current and desired:
14 * SVC_STOPPED/SVC_STOPPED : stopped and will remain stopped
15 * SVC_STOPPED/SVC_STARTED : stopped and will be started; waiting for dependencies to start.
16 * SVC_STARTING/SVC_STARTED : starting, but not yet started. All dependencies have started already.
17 * SVC_STARTING/SVC_STOPPED : as above, but the service will be stopped again as soon as it has
19 * SVC_STARTED/SVC_STARTED : running and will continue running.
20 * SVC_STARTED/SVC_STOPPED : running but will stop; waiting for dependents to stop.
21 * SVC_STOPPING/SVC_STOPPED : stopping and will stop. All dependents have stopped.
22 * SVC_STOPPING/SVC_STARTED : as above, but the service will be re-started again once it stops.
24 * A scripted service is in the STARTING/STOPPING states during the script execution.
25 * A process service is in the STOPPING state when it has been signalled to stop (and is never
26 * in the STARTING state; it moves directly from STOPPED to STARTED).
28 // TODO can we use typesafe enum?
29 constexpr static int SVC_STOPPED = 0; // service is not running
30 constexpr static int SVC_STARTING = 1; // service is starting, and will start (or fail to start) in time. All dependencies have started.
31 constexpr static int SVC_STARTED = 2; // service is running
32 constexpr static int SVC_STOPPING = 3; // service script is stopping and will stop.
36 #define SVC_DUMMY 0 /* dummy service, used to detect cyclic dependencies */
37 #define SVC_PROCESS 1 /* service runs as a process, and can be stopped
38 by sending the process a signal */
39 #define SVC_SCRIPTED 2 /* service requires a command to start, and another
43 // Exception loading service
47 std::string serviceName;
49 ServiceLoadExc(std::string serviceName)
50 : serviceName(serviceName)
55 class ServiceNotFound : public ServiceLoadExc
58 ServiceNotFound(std::string serviceName)
59 : ServiceLoadExc(serviceName)
64 class ServiceCyclicDependency : public ServiceLoadExc
67 ServiceCyclicDependency(std::string serviceName)
68 : ServiceLoadExc(serviceName)
73 class ServiceDescriptionExc : public ServiceLoadExc
76 std::string extraInfo;
78 ServiceDescriptionExc(std::string serviceName, std::string extraInfo)
79 : ServiceLoadExc(serviceName), extraInfo(extraInfo)
84 class ServiceRecord; // forward declaration
85 class ServiceSet; // forward declaration
87 /* Service dependency record */
94 /* Whether the 'from' service is waiting for the 'to' service to start */
97 ServiceDep(ServiceRecord * from, ServiceRecord * to) : from(from), to(to), waiting_on(false)
100 ServiceRecord * getFrom()
105 ServiceRecord * getTo()
115 typedef std::string string;
118 int service_type; /* SVC_DAEMON or SVC_SCRIPTED */
119 int service_state; /* SVC_STOPPED, _STARTING, _STARTED, _STOPPING */
120 int desired_state; /* SVC_STOPPED / SVC_STARTED */
121 bool force_stop; // true if the service must actually stop. This is the
122 // case if for example the process dies; the service,
123 // and all its dependencies, MUST be stopped.
124 string program_name; /* executable program or script */
125 string logfile; /* log file name, empty string specifies /dev/null */
126 bool auto_restart; /* whether to restart this (process) if it dies unexpectedly */
128 typedef std::list<ServiceRecord *> sr_list;
129 typedef sr_list::iterator sr_iter;
131 // list of soft dependencies
132 typedef std::list<ServiceDep> softdep_list;
134 // list of soft dependents
135 typedef std::list<ServiceDep *> softdpt_list;
137 sr_list depends_on; // services this one depends on
138 sr_list dependents; // services depending on this one
139 softdep_list soft_deps; // services this one depends on via a soft dependency
140 softdpt_list soft_dpts; // services depending on this one via a soft dependency
142 // unsigned wait_count; /* if we are waiting for dependents/dependencies to
143 // start/stop, this is how many we're waiting for */
145 ServiceSet *service_set; // the set this service belongs to
147 // Implementation details
149 pid_t pid; /* PID of the process. If state is STARTING or STOPPING,
150 this is PID of the service script; otherwise it is the
151 PID of the process itself (process service).
154 ev_child child_listener;
156 // Move service to STOPPING state. This can only be called once
157 // all dependents have stopped.
160 // Service has actually stopped (includes having all dependents
161 // reaching STOPPED state).
164 // Service has successfully started
167 // Service failed to start
168 void failed_to_start();
170 // A dependency of this service failed to start.
171 void failed_dependency();
173 // For process services, start the process, return true on success
174 bool start_ps_process();
175 bool start_ps_process(const std::vector<std::string> &args);
177 // Callback from libev when a child process dies
178 static void process_child_callback(struct ev_loop *loop, struct ev_child *w,
181 // A dependent has reached STOPPED state
182 void dependentStopped();
184 // check if all dependents have stopped
185 bool stopCheckDependents();
187 // issue a stop to all dependents, return true if they are all already stopped
188 bool stopDependents();
190 void forceStop(); // force-stop this service and all dependents
194 ServiceRecord(ServiceSet *set, string name)
195 : service_state(SVC_STOPPED), desired_state(SVC_STOPPED), force_stop(false), auto_restart(false)
199 service_type = SVC_DUMMY;
202 ServiceRecord(ServiceSet *set, string name, int service_type, string command,
203 sr_list * pdepends_on, sr_list * pdepends_soft)
204 : service_state(SVC_STOPPED), desired_state(SVC_STOPPED), force_stop(false), auto_restart(false)
208 this->service_type = service_type;
209 program_name = command;
210 // TODO splice the contents from the depends_on parameter
211 // rather than duplicating the list.
212 this->depends_on = *pdepends_on;
213 //this->depends_on.insert(this->depends_on.end(), pdepends_soft->begin(), pdepends_soft->end());
215 for (sr_iter i = pdepends_on->begin(); i != pdepends_on->end(); ++i) {
216 (*i)->dependents.push_back(this);
220 auto b_iter = soft_deps.end();
221 for (sr_iter i = pdepends_soft->begin(); i != pdepends_soft->end(); ++i) {
222 b_iter = soft_deps.emplace(b_iter, this, *i);
223 (*i)->soft_dpts.push_back(&(*b_iter));
228 // For each dependency, add us as a dependent.
229 for (sr_iter i = pdepends_on->begin(); i != pdepends_on->end(); ++i) {
230 (*i)->dependents.push_back(this);
232 for (sr_iter i = pdepends_soft->begin(); i != pdepends_soft->end(); ++i) {
233 (*i)->soft_dependents.push_back(this);
238 // Set logfile, should be done before service is started
239 void setLogfile(string logfile)
241 this->logfile = logfile;
244 // Set whether this service should automatically restart when it dies
245 void setAutoRestart(bool auto_restart)
247 this->auto_restart = auto_restart;
250 const char *getServiceName() const { return service_name.c_str(); }
251 int getState() const { return service_state; }
253 void start(); // start the service
254 void stop(); // stop the service
258 return service_type == SVC_DUMMY;
266 std::list<ServiceRecord *> records;
267 const char *service_dir; // directory containing service descriptions
268 bool restart_enabled; // whether automatic restart is enabled (allowed)
272 // Locate an existing service record.
273 ServiceRecord *findService(std::string name);
275 // Load a service description, and dependencies, if there is no existing
276 // record for the given name.
277 ServiceRecord *loadServiceRecord(const char *name);
282 ServiceSet(const char *service_dir)
284 this->service_dir = service_dir;
286 restart_enabled = true;
289 // Start the service with the given name. The named service will begin
290 // transition to the 'started' state.
292 // Throws an exception if the
293 // service description cannot be loaded.
294 void startService(const char *name);
296 // Stop the service with the given name. The named service will begin
297 // transition to the 'stopped' state.
298 void stopService(const std::string &name);
300 // Notification from service that it is active (state != SVC_STOPPED)
301 // Only to be called on the transition from inactive to active.
302 void service_active(ServiceRecord *);
304 // Notification from service that it is inactive (SVC_STOPPED)
305 // Only to be called on the transition from active to inactive.
306 void service_inactive(ServiceRecord *);
308 // Find out how many services are active (starting, running or stopping,
310 int count_active_services()
312 return active_services;
315 void stop_all_services()
317 restart_enabled = false;
318 for (std::list<ServiceRecord *>::iterator i = records.begin(); i != records.end(); ++i) {
323 void set_auto_restart(bool restart)
325 restart_enabled = restart;
328 bool get_auto_restart()
330 return restart_enabled;