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