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