Minor header/signature adjustments.
[oweals/dinit.git] / src / includes / proc-service.h
1 #include <sys/types.h>
2
3 #include "baseproc-sys.h"
4 #include "service.h"
5
6 // This header defines base_proc_service (base process service) and several derivatives, as well as some
7 // utility functions and classes. See service.h for full details of services.
8
9 // Given a string and a list of pairs of (start,end) indices for each argument in that string,
10 // store a null terminator for the argument. Return a `char *` vector containing the beginning
11 // of each argument and a trailing nullptr. (The returned array is invalidated if the string is later modified).
12 std::vector<const char *> separate_args(std::string &s,
13         const std::list<std::pair<unsigned,unsigned>> &arg_indices);
14
15 class base_process_service;
16
17 // A timer for process restarting. Used to ensure a minimum delay between process restarts (and
18 // also for timing service stop before the SIGKILL hammer is used).
19 class process_restart_timer : public eventloop_t::timer_impl<process_restart_timer>
20 {
21     public:
22     base_process_service * service;
23
24     explicit process_restart_timer(base_process_service *service_p)
25         : service(service_p)
26     {
27     }
28
29     dasynq::rearm timer_expiry(eventloop_t &, int expiry_count);
30 };
31
32 // Base class for process-based services.
33 class base_process_service : public service_record
34 {
35     friend class service_child_watcher;
36     friend class exec_status_pipe_watcher;
37     friend class base_process_service_test;
38
39     private:
40     // Re-launch process
41     void do_restart() noexcept;
42
43     protected:
44     string program_name;          // storage for program/script and arguments
45     std::vector<const char *> exec_arg_parts; // pointer to each argument/part of the program_name, and nullptr
46
47     string stop_command;          // storage for stop program/script and arguments
48     std::vector<const char *> stop_arg_parts; // pointer to each argument/part of the stop_command, and nullptr
49
50     string working_dir;       // working directory (or empty)
51
52     service_child_watcher child_listener;
53     exec_status_pipe_watcher child_status_listener;
54     process_restart_timer restart_timer;
55     time_val last_start_time;
56
57     // Restart interval time and restart count are used to track the number of automatic restarts
58     // over an interval. Too many restarts over an interval will inhibit further restarts.
59     time_val restart_interval_time;  // current restart interval
60     int restart_interval_count;      // count of restarts within current interval
61
62     time_val restart_interval;       // maximum restart interval
63     int max_restart_interval_count;  // number of restarts allowed over maximum interval
64     time_val restart_delay;          // delay between restarts
65
66     // Time allowed for service stop, after which SIGKILL is sent. 0 to disable.
67     time_val stop_timeout = {10, 0}; // default of 10 seconds
68
69     // Time allowed for service start, after which SIGINT is sent (and then SIGKILL after
70     // <stop_timeout>). 0 to disable.
71     time_val start_timeout = {60, 0}; // default of 1 minute
72
73     uid_t run_as_uid = -1;
74     gid_t run_as_gid = -1;
75
76     pid_t pid = -1;  // PID of the process. If state is STARTING or STOPPING,
77                      //   this is PID of the service script; otherwise it is the
78                      //   PID of the process itself (process service).
79     bp_sys::exit_status exit_status; // Exit status, if the process has exited (pid == -1).
80     int socket_fd = -1;  // For socket-activation services, this is the file
81                          // descriptor for the socket.
82
83     bool waiting_restart_timer : 1;
84     bool stop_timer_armed : 1;
85     bool reserved_child_watch : 1;
86     bool tracking_child : 1;  // whether we expect to see child process status
87
88     // Launch the process with the given arguments, return true on success
89     bool start_ps_process(const std::vector<const char *> &args, bool on_console) noexcept;
90
91     // Restart the process (due to start failure or unexpected termination). Restarts will be
92     // rate-limited.
93     bool restart_ps_process() noexcept;
94
95     // Perform smooth recovery process
96     void do_smooth_recovery() noexcept;
97
98     // Start the process, return true on success
99     virtual bool bring_up() noexcept override;
100
101     // Called when the process exits. The exit_status is the status value yielded by
102     // the "wait" system call.
103     virtual void handle_exit_status(bp_sys::exit_status exit_status) noexcept = 0;
104
105     // Called if an exec fails.
106     virtual void exec_failed(int errcode) noexcept = 0;
107
108     // Called if exec succeeds.
109     virtual void exec_succeeded() noexcept { };
110
111     virtual bool can_interrupt_start() noexcept override
112     {
113         return waiting_restart_timer || onstart_flags.start_interruptible
114                 || service_record::can_interrupt_start();
115     }
116
117     virtual bool can_proceed_to_start() noexcept override
118     {
119         return ! waiting_restart_timer;
120     }
121
122     virtual bool interrupt_start() noexcept override;
123
124     void becoming_inactive() noexcept override;
125
126     // Kill with SIGKILL
127     void kill_with_fire() noexcept;
128
129     // Signal the process group of the service process
130     void kill_pg(int signo) noexcept;
131
132     // stop immediately
133     void emergency_stop() noexcept;
134
135     // Open the activation socket, return false on failure
136     bool open_socket() noexcept;
137
138     public:
139     // Constructor for a base_process_service. Note that the various parameters not specified here must in
140     // general be set separately (using the appropriate set_xxx function for each).
141     base_process_service(service_set *sset, string name, service_type_t record_type_p, string &&command,
142             const std::list<std::pair<unsigned,unsigned>> &command_offsets,
143             const std::list<prelim_dep> &deplist_p);
144
145     ~base_process_service() noexcept
146     {
147         if (reserved_child_watch) {
148             child_listener.unreserve(event_loop);
149         }
150         restart_timer.deregister(event_loop);
151     }
152
153     // Set the stop command and arguments (may throw std::bad_alloc)
154     void set_stop_command(const std::string &command,
155             std::list<std::pair<unsigned,unsigned>> &stop_command_offsets)
156     {
157         stop_command = command;
158         stop_arg_parts = separate_args(stop_command, stop_command_offsets);
159     }
160
161     void set_restart_interval(timespec interval, int max_restarts) noexcept
162     {
163         restart_interval = interval;
164         max_restart_interval_count = max_restarts;
165     }
166
167     void set_restart_delay(timespec delay) noexcept
168     {
169         restart_delay = delay;
170     }
171
172     void set_stop_timeout(timespec timeout) noexcept
173     {
174         stop_timeout = timeout;
175     }
176
177     void set_start_timeout(timespec timeout) noexcept
178     {
179         start_timeout = timeout;
180     }
181
182     // Set an additional signal (other than SIGTERM) to be used to terminate the process
183     void set_extra_termination_signal(int signo) noexcept
184     {
185         this->term_signal = signo;
186     }
187
188     // Set the uid/gid that the service process will be run as
189     void set_run_as_uid_gid(uid_t uid, gid_t gid) noexcept
190     {
191         run_as_uid = uid;
192         run_as_gid = gid;
193     }
194
195     // Set the working directory
196     void set_workding_dir(const string &working_dir_p)
197     {
198         working_dir = working_dir_p;
199     }
200
201     // The restart/stop timer expired.
202     void timer_expired() noexcept;
203
204     // Accessor for testing:
205     const std::vector<const char *> & get_exec_arg_parts() noexcept
206     {
207         return exec_arg_parts;
208     }
209
210     pid_t get_pid() override
211     {
212         return pid;
213     }
214
215     int get_exit_status() override
216     {
217         return exit_status.as_int();
218     }
219 };
220
221 // Standard process service.
222 class process_service : public base_process_service
223 {
224     virtual void handle_exit_status(bp_sys::exit_status exit_status) noexcept override;
225     virtual void exec_failed(int errcode) noexcept override;
226     virtual void exec_succeeded() noexcept override;
227     virtual void bring_down() noexcept override;
228
229     public:
230     process_service(service_set *sset, const string &name, string &&command,
231             std::list<std::pair<unsigned,unsigned>> &command_offsets,
232             const std::list<prelim_dep> &depends_p)
233          : base_process_service(sset, name, service_type_t::PROCESS, std::move(command), command_offsets,
234              depends_p)
235     {
236     }
237
238     ~process_service() noexcept
239     {
240     }
241 };
242
243 // Bgproc (self-"backgrounding", i.e. double-forking) process service
244 class bgproc_service : public base_process_service
245 {
246     virtual void handle_exit_status(bp_sys::exit_status exit_status) noexcept override;
247     virtual void exec_failed(int errcode) noexcept override;
248     virtual void bring_down() noexcept override;
249
250     enum class pid_result_t {
251         OK,
252         FAILED,      // failed to read pid or read invalid pid
253         TERMINATED   // read pid successfully, but the process already terminated
254     };
255
256     // Read the pid-file, return false on failure
257     pid_result_t read_pid_file(bp_sys::exit_status *exit_status) noexcept;
258
259     public:
260     bgproc_service(service_set *sset, const string &name, string &&command,
261             std::list<std::pair<unsigned,unsigned>> &command_offsets,
262             const std::list<prelim_dep> &depends_p)
263          : base_process_service(sset, name, service_type_t::BGPROCESS, std::move(command), command_offsets,
264              depends_p)
265     {
266     }
267
268     ~bgproc_service() noexcept
269     {
270     }
271 };
272
273 // Service which is started and stopped via separate commands
274 class scripted_service : public base_process_service
275 {
276     virtual void handle_exit_status(bp_sys::exit_status exit_status) noexcept override;
277     virtual void exec_succeeded() noexcept override;
278     virtual void exec_failed(int errcode) noexcept override;
279     virtual void bring_down() noexcept override;
280
281     virtual bool interrupt_start() noexcept override
282     {
283         // if base::interrupt_start() returns false, then start hasn't been fully interrupted, but an
284         // interrupt has been issued:
285         interrupting_start = ! base_process_service::interrupt_start();
286         return ! interrupting_start;
287     }
288
289     bool interrupting_start : 1;  // running start script (true) or stop script (false)
290
291     public:
292     scripted_service(service_set *sset, const string &name, string &&command,
293             std::list<std::pair<unsigned,unsigned>> &command_offsets,
294             const std::list<prelim_dep> &depends_p)
295          : base_process_service(sset, name, service_type_t::SCRIPTED, std::move(command), command_offsets,
296              depends_p), interrupting_start(false)
297     {
298     }
299
300     ~scripted_service() noexcept
301     {
302     }
303 };