Clean up exception throwing/handling during service loading, and fix
[oweals/dinit.git] / service.h
1 #include <string>
2 #include <list>
3 #include <vector>
4 #include "ev.h"
5
6 /* Possible service states */
7 // TODO can we use typesafe enum?
8 constexpr static int SVC_STOPPED  = 0;  // service is not running
9 constexpr static int SVC_STARTING = 1;  // service script is running with "start"
10 constexpr static int SVC_STARTED  = 2;  // service is running; start script finished.
11 constexpr static int SVC_STOPPING = 3;  // service script is running with "stop"
12
13
14 /* Service types */
15 #define SVC_DUMMY    0  /* dummy service, used to detect cyclic dependencies */
16 #define SVC_PROCESS  1  /* service runs as a process, and can be stopped
17                            by sending the process a signal */
18 #define SVC_SCRIPTED 2  /* service requires a command to start, and another
19                            command to stop */
20
21
22 // Exception loading service
23 class ServiceLoadExc
24 {
25     public:
26     std::string serviceName;
27     
28     ServiceLoadExc(std::string serviceName)
29         : serviceName(serviceName)
30     {
31     }
32 };
33
34 class ServiceNotFound : public ServiceLoadExc
35 {
36     public:
37     ServiceNotFound(std::string serviceName)
38         : ServiceLoadExc(serviceName)
39     {
40     }
41 };
42
43 class ServiceCyclicDependency : public ServiceLoadExc
44 {
45     public:
46     ServiceCyclicDependency(std::string serviceName)
47         : ServiceLoadExc(serviceName)
48     {
49     }
50 };
51
52 class ServiceDescriptionExc : public ServiceLoadExc
53 {
54     public:
55     std::string extraInfo;
56     
57     ServiceDescriptionExc(std::string serviceName, std::string extraInfo)
58         : ServiceLoadExc(serviceName), extraInfo(extraInfo)
59     {
60     }    
61 };
62
63
64 class ServiceSet; // forward declaration
65
66 class ServiceRecord
67 {
68     typedef std::string string;
69     
70     string service_name;
71     int service_type;  /* SVC_DAEMON or SVC_SCRIPTED */
72     int service_state; /* SVC_STOPPED, _STARTING, _STARTED, _STOPPING */
73     int desired_state; /* SVC_STOPPED / SVC_STARTED */
74     bool force_stop; // true if the service must actually stop. This is the
75                      // case if for example the process dies; the service,
76                      // and all its dependencies, MUST be stopped.
77     string program_name;  /* executable program or script */
78     string logfile; /* log file name, empty string specifies /dev/null */
79     bool auto_restart; /* whether to restart this (process) if it dies */
80     
81     typedef std::list<ServiceRecord *> sr_list;
82     typedef sr_list::iterator sr_iter;
83     
84     sr_list depends_on; // services this one depends on
85     sr_list dependents; // services depending on this one
86     // unsigned wait_count;  /* if we are waiting for dependents/dependencies to
87     //                         start/stop, this is how many we're waiting for */
88     
89     ServiceSet *service_set; // the set this service belongs to
90     
91     // Implementation details
92     
93     pid_t pid;  /* PID of the process. If state is STARTING or STOPPING,
94                    this is PID of the service script; otherwise it is the
95                    PID of the process itself (process service).
96                    */
97
98     ev_child child_listener;
99     
100     // Move service to STOPPING state. This can only be called once
101     // all dependents have stopped.
102     void stopping();
103     
104     // Service has actually stopped (includes having all dependents
105     // reaching STOPPED state).
106     void stopped();
107     
108     // Service has successfully started
109     void started();
110     
111     // Service failed to start
112     void failed_to_start();
113     
114     // A dependency of this service failed to start.
115     void failed_dependency();
116     
117     // For process services, start the process, return true on success
118     bool start_ps_process();
119     bool start_ps_process(const std::vector<std::string> &args);
120    
121     // Callback from libev when a child process dies
122     static void process_child_callback(struct ev_loop *loop, struct ev_child *w,
123             int revents);
124     
125     void dependentStopped(); // called when a dependent stopped
126     
127     void forceStop(); // force-stop this service and all dependents
128     
129     public:
130
131     ServiceRecord(ServiceSet *set, string name)
132         : service_state(SVC_STOPPED), desired_state(SVC_STOPPED)
133     {
134         service_set = set;
135         service_name = name;
136         service_type = SVC_DUMMY;
137     }
138     
139     ServiceRecord(ServiceSet *set, string name, int service_type, string command,
140             std::list<ServiceRecord *> * pdepends_on)
141         : service_state(SVC_STOPPED), desired_state(SVC_STOPPED)
142     {
143         service_set = set;
144         service_name = name;
145         this->service_type = service_type;
146         program_name = command;
147         auto_restart = false;
148         // TODO splice the contents from the depends_on parameter
149         // rather than duplicating the list.
150         this->depends_on = *pdepends_on;
151         
152         // For each dependency, add us as a dependent.
153         for (sr_iter i = depends_on.begin(); i != depends_on.end(); ++i) {
154             (*i)->dependents.push_back(this);
155         }
156     }
157     
158     // Set logfile, should be done before service is started
159     void setLogfile(string logfile)
160     {
161         this->logfile = logfile;
162     }
163     
164     // Set whether this service should automatically restart when it dies
165     void setAutoRestart(bool auto_restart)
166     {
167         this->auto_restart = auto_restart;
168     }
169     
170     const char *getServiceName() const { return service_name.c_str(); }
171     int getState() const { return service_state; }
172     
173     void start();  // start the service
174     void stop();   // stop the service
175     
176     bool isDummy()
177     {
178         return service_type == SVC_DUMMY;
179     }
180 };
181
182
183 class ServiceSet
184 {
185     int active_services;
186     std::list<ServiceRecord *> records;
187     const char *service_dir;  // directory containing service descriptions
188     bool restart_enabled; // whether automatic restart is enabled (allowed)
189     
190     // Private methods
191     
192     // Locate an existing service record.
193     ServiceRecord *findService(std::string name);
194     
195     // Load a service description, and dependencies, if there is no existing
196     // record for the given name.
197     ServiceRecord *loadServiceRecord(const char *name);
198
199     // Public
200     
201     public:
202     ServiceSet(const char *service_dir)
203     {
204         this->service_dir = service_dir;
205         active_services = 0;
206         restart_enabled = true;
207     }
208     
209     // Start the service with the given name. The named service will begin
210     // transition to the 'started' state.
211     //
212     // Throws an exception if the
213     // service description cannot be loaded.
214     void startService(const char *name);
215     
216     // Stop the service with the given name. The named service will begin
217     // transition to the 'stopped' state.
218     void stopService(const std::string &name);
219     
220     // Notification from service that it is active (state != SVC_STOPPED)
221     // Only to be called on the transition from inactive to active.
222     void service_active(ServiceRecord *);
223     
224     // Notification from service that it is inactive (SVC_STOPPED)
225     // Only to be called on the transition from active to inactive.
226     void service_inactive(ServiceRecord *);
227     
228     // Find out how many services are active (starting, running or stopping,
229     // but not stopped).
230     int count_active_services()
231     {
232         return active_services;
233     }
234     
235     void stop_all_services()
236     {
237         restart_enabled = false;
238         for (std::list<ServiceRecord *>::iterator i = records.begin(); i != records.end(); ++i) {
239             (*i)->stop();
240         }
241     }
242     
243     void set_auto_restart(bool restart)
244     {
245         restart_enabled = restart;
246     }
247     
248     bool get_auto_restart()
249     {
250         return restart_enabled;
251     }
252 };