Simple cleanups.
[oweals/dinit.git] / service.h
1 #include <string>
2 #include <list>
3 #include <vector>
4 #include "ev.h"
5
6 /*
7  * Possible service states
8  *
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
11  * or SVC_STOPPING.
12  *
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
18  *                            completed startup.
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.
23  *
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).
27  */
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.
33
34
35 /* Service types */
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
40                            command to stop */
41
42
43 // Exception loading service
44 class ServiceLoadExc
45 {
46     public:
47     std::string serviceName;
48     
49     ServiceLoadExc(std::string serviceName)
50         : serviceName(serviceName)
51     {
52     }
53 };
54
55 class ServiceNotFound : public ServiceLoadExc
56 {
57     public:
58     ServiceNotFound(std::string serviceName)
59         : ServiceLoadExc(serviceName)
60     {
61     }
62 };
63
64 class ServiceCyclicDependency : public ServiceLoadExc
65 {
66     public:
67     ServiceCyclicDependency(std::string serviceName)
68         : ServiceLoadExc(serviceName)
69     {
70     }
71 };
72
73 class ServiceDescriptionExc : public ServiceLoadExc
74 {
75     public:
76     std::string extraInfo;
77     
78     ServiceDescriptionExc(std::string serviceName, std::string extraInfo)
79         : ServiceLoadExc(serviceName), extraInfo(extraInfo)
80     {
81     }    
82 };
83
84 class ServiceRecord; // forward declaration
85 class ServiceSet; // forward declaration
86
87 /* Service dependency record */
88 class ServiceDep
89 {
90     ServiceRecord * from;
91     ServiceRecord * to;
92     
93     public:
94     /* Whether the 'from' service is waiting for the 'to' service to start */
95     bool waiting_on;
96     
97     ServiceDep(ServiceRecord * from, ServiceRecord * to) : from(from), to(to), waiting_on(false)
98     {  }
99     
100     ServiceRecord * getFrom()
101     {
102         return from;
103     }
104     
105     ServiceRecord * getTo()
106     {
107         return to;
108     }
109 };
110
111
112
113 class ServiceRecord
114 {
115     typedef std::string string;
116     
117     string service_name;
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 */
127     
128     typedef std::list<ServiceRecord *> sr_list;
129     typedef sr_list::iterator sr_iter;
130     
131     // list of soft dependencies
132     typedef std::list<ServiceDep> softdep_list;
133     
134     // list of soft dependents
135     typedef std::list<ServiceDep *> softdpt_list;
136     
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
141     
142     // unsigned wait_count;  /* if we are waiting for dependents/dependencies to
143     //                         start/stop, this is how many we're waiting for */
144     
145     ServiceSet *service_set; // the set this service belongs to
146     
147     // Implementation details
148     
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).
152                    */
153
154     ev_child child_listener;
155     
156     // Move service to STOPPING state. This can only be called once
157     // all dependents have stopped.
158     void stopping();
159     
160     // Service has actually stopped (includes having all dependents
161     // reaching STOPPED state).
162     void stopped();
163     
164     // Service has successfully started
165     void started();
166     
167     // Service failed to start
168     void failed_to_start();
169     
170     // A dependency of this service failed to start.
171     void failed_dependency();
172     
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);
176    
177     // Callback from libev when a child process dies
178     static void process_child_callback(struct ev_loop *loop, struct ev_child *w,
179             int revents);
180     
181     // A dependent has reached STOPPED state
182     void dependentStopped();
183
184     // check if all dependents have stopped
185     bool stopCheckDependents();
186     
187     // issue a stop to all dependents, return true if they are all already stopped
188     bool stopDependents();
189     
190     void forceStop(); // force-stop this service and all dependents
191     
192     public:
193
194     ServiceRecord(ServiceSet *set, string name)
195         : service_state(SVC_STOPPED), desired_state(SVC_STOPPED), force_stop(false), auto_restart(false)
196     {
197         service_set = set;
198         service_name = name;
199         service_type = SVC_DUMMY;
200     }
201     
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)
205     {
206         service_set = set;
207         service_name = name;
208         this->service_type = service_type;
209         program_name = command;
210         this->depends_on = std::move(*pdepends_on);
211
212         for (sr_iter i = pdepends_on->begin(); i != pdepends_on->end(); ++i) {
213             (*i)->dependents.push_back(this);
214         }
215         
216         // Soft dependencies
217         auto b_iter = soft_deps.end();
218         for (sr_iter i = pdepends_soft->begin(); i != pdepends_soft->end(); ++i) {
219             b_iter = soft_deps.emplace(b_iter, this, *i);
220             (*i)->soft_dpts.push_back(&(*b_iter));
221             ++b_iter;
222         }
223     }
224     
225     // Set logfile, should be done before service is started
226     void setLogfile(string logfile)
227     {
228         this->logfile = logfile;
229     }
230     
231     // Set whether this service should automatically restart when it dies
232     void setAutoRestart(bool auto_restart)
233     {
234         this->auto_restart = auto_restart;
235     }
236     
237     const char *getServiceName() const { return service_name.c_str(); }
238     int getState() const { return service_state; }
239     
240     void start();  // start the service
241     void stop();   // stop the service
242     
243     bool isDummy()
244     {
245         return service_type == SVC_DUMMY;
246     }
247 };
248
249
250 class ServiceSet
251 {
252     int active_services;
253     std::list<ServiceRecord *> records;
254     const char *service_dir;  // directory containing service descriptions
255     bool restart_enabled; // whether automatic restart is enabled (allowed)
256     
257     // Private methods
258     
259     // Locate an existing service record.
260     ServiceRecord *findService(std::string name);
261     
262     // Load a service description, and dependencies, if there is no existing
263     // record for the given name.
264     ServiceRecord *loadServiceRecord(const char *name);
265
266     // Public
267     
268     public:
269     ServiceSet(const char *service_dir)
270     {
271         this->service_dir = service_dir;
272         active_services = 0;
273         restart_enabled = true;
274     }
275     
276     // Start the service with the given name. The named service will begin
277     // transition to the 'started' state.
278     //
279     // Throws an exception if the
280     // service description cannot be loaded.
281     void startService(const char *name);
282     
283     // Stop the service with the given name. The named service will begin
284     // transition to the 'stopped' state.
285     void stopService(const std::string &name);
286     
287     // Notification from service that it is active (state != SVC_STOPPED)
288     // Only to be called on the transition from inactive to active.
289     void service_active(ServiceRecord *);
290     
291     // Notification from service that it is inactive (SVC_STOPPED)
292     // Only to be called on the transition from active to inactive.
293     void service_inactive(ServiceRecord *);
294     
295     // Find out how many services are active (starting, running or stopping,
296     // but not stopped).
297     int count_active_services()
298     {
299         return active_services;
300     }
301     
302     void stop_all_services()
303     {
304         restart_enabled = false;
305         for (std::list<ServiceRecord *>::iterator i = records.begin(); i != records.end(); ++i) {
306             (*i)->stop();
307         }
308     }
309     
310     void set_auto_restart(bool restart)
311     {
312         restart_enabled = restart;
313     }
314     
315     bool get_auto_restart()
316     {
317         return restart_enabled;
318     }
319 };