Log more information on exec failure.
[oweals/dinit.git] / src / includes / proc-service.h
1 #include <sys/types.h>
2 #include <sys/resource.h>
3
4 #include "baseproc-sys.h"
5 #include "service.h"
6 #include "dinit-utmp.h"
7
8 // This header defines base_proc_service (base process service) and several derivatives, as well as some
9 // utility functions and classes. See service.h for full details of services.
10
11 // Given a string and a list of pairs of (start,end) indices for each argument in that string,
12 // store a null terminator for the argument. Return a `char *` vector containing the beginning
13 // of each argument and a trailing nullptr. (The returned array is invalidated if the string is later
14 // modified).
15 std::vector<const char *> separate_args(std::string &s,
16         const std::list<std::pair<unsigned,unsigned>> &arg_indices);
17
18 struct service_rlimits
19 {
20     int resource_id; // RLIMIT_xxx identifying resource
21     bool soft_set : 1;
22     bool hard_set : 1;
23     struct rlimit limits;
24
25     service_rlimits(int id) : resource_id(id), soft_set(0), hard_set(0), limits({0,0}) { }
26 };
27
28 // Parameters for process execution
29 struct run_proc_params
30 {
31     const char * const *args; // program arguments including executable (args[0])
32     const char *working_dir;  // working directory
33     const char *logfile;      // log file or nullptr (stdout/stderr); must be valid if !on_console
34     const char *env_file;     // file with environment settings (or nullptr)
35     bool on_console;          // whether to run on console
36     bool in_foreground;       // if on console: whether to run in foreground
37     int wpipefd;              // pipe to which error status will be sent (if error occurs)
38     int csfd;                 // control socket fd (or -1); may be moved
39     int socket_fd;            // pre-opened socket fd (or -1); may be moved
40     int notify_fd;            // pipe for readiness notification message (or -1); may be moved
41     int force_notify_fd;      // if not -1, notification fd must be moved to this fd
42     const char *notify_var;   // environment variable name where notification fd will be stored, or nullptr
43     uid_t uid;
44     gid_t gid;
45     const std::vector<service_rlimits> &rlimits;
46
47     run_proc_params(const char * const *args, const char *working_dir, const char *logfile, int wpipefd,
48             uid_t uid, gid_t gid, const std::vector<service_rlimits> &rlimits)
49             : args(args), working_dir(working_dir), logfile(logfile), env_file(nullptr), on_console(false),
50               in_foreground(false), wpipefd(wpipefd), csfd(-1), socket_fd(-1), notify_fd(-1),
51               force_notify_fd(-1), notify_var(nullptr), uid(uid), gid(gid), rlimits(rlimits)
52     { }
53 };
54
55 enum class exec_stage {
56     ARRANGE_FDS, READ_ENV_FILE, SET_NOTIFYFD_VAR, SETUP_ACTIVATION_SOCKET, SETUP_CONTROL_SOCKET,
57     CHDIR, SETUP_STDINOUTERR, SET_RLIMITS, SET_UIDGID, /* must be last: */ DO_EXEC
58 };
59
60 extern const char * const exec_stage_descriptions[static_cast<int>(exec_stage::DO_EXEC) + 1];
61
62 // Error information from process execution transferred via this struct
63 struct run_proc_err
64 {
65     exec_stage stage;
66     int st_errno;
67 };
68
69 class base_process_service;
70
71 // A timer for process restarting. Used to ensure a minimum delay between process restarts (and
72 // also for timing service stop before the SIGKILL hammer is used).
73 class process_restart_timer : public eventloop_t::timer_impl<process_restart_timer>
74 {
75     public:
76     base_process_service * service;
77
78     explicit process_restart_timer(base_process_service *service_p)
79         : service(service_p)
80     {
81     }
82
83     dasynq::rearm timer_expiry(eventloop_t &, int expiry_count);
84 };
85
86 // Watcher for the pipe used to receive exec() failure status errno
87 class exec_status_pipe_watcher : public eventloop_t::fd_watcher_impl<exec_status_pipe_watcher>
88 {
89     public:
90     base_process_service * service;
91     dasynq::rearm fd_event(eventloop_t &eloop, int fd, int flags) noexcept;
92
93     exec_status_pipe_watcher(base_process_service * sr) noexcept : service(sr) { }
94
95     exec_status_pipe_watcher(const exec_status_pipe_watcher &) = delete;
96     void operator=(const exec_status_pipe_watcher &) = delete;
97 };
98
99 // Watcher for readiness notification pipe
100 class ready_notify_watcher : public eventloop_t::fd_watcher_impl<ready_notify_watcher>
101 {
102     public:
103     base_process_service * service;
104     dasynq::rearm fd_event(eventloop_t &eloop, int fd, int flags) noexcept;
105
106     ready_notify_watcher(base_process_service * sr) noexcept : service(sr) { }
107
108     ready_notify_watcher(const ready_notify_watcher &) = delete;
109     void operator=(const ready_notify_watcher &) = delete;
110 };
111
112
113 class service_child_watcher : public eventloop_t::child_proc_watcher_impl<service_child_watcher>
114 {
115     public:
116     base_process_service * service;
117     dasynq::rearm status_change(eventloop_t &eloop, pid_t child, int status) noexcept;
118
119     service_child_watcher(base_process_service * sr) noexcept : service(sr) { }
120
121     service_child_watcher(const service_child_watcher &) = delete;
122     void operator=(const service_child_watcher &) = delete;
123 };
124
125 // Base class for process-based services.
126 class base_process_service : public service_record
127 {
128     friend class service_child_watcher;
129     friend class exec_status_pipe_watcher;
130     friend class base_process_service_test;
131     friend class ready_notify_watcher;
132
133     private:
134     // Re-launch process
135     void do_restart() noexcept;
136
137     protected:
138     string program_name;          // storage for program/script and arguments
139     // pointer to each argument/part of the program_name, and nullptr:
140     std::vector<const char *> exec_arg_parts;
141
142     string stop_command;          // storage for stop program/script and arguments
143     // pointer to each argument/part of the stop_command, and nullptr:
144     std::vector<const char *> stop_arg_parts;
145
146     string working_dir;       // working directory (or empty)
147     string env_file;          // file with environment settings for this service
148
149     std::vector<service_rlimits> rlimits; // resource limits
150
151     service_child_watcher child_listener;
152     exec_status_pipe_watcher child_status_listener;
153     process_restart_timer restart_timer;
154     time_val last_start_time;
155
156     // Restart interval time and restart count are used to track the number of automatic restarts
157     // over an interval. Too many restarts over an interval will inhibit further restarts.
158     time_val restart_interval_time;  // current restart interval
159     int restart_interval_count;      // count of restarts within current interval
160
161     time_val restart_interval;       // maximum restart interval
162     int max_restart_interval_count;  // number of restarts allowed over maximum interval
163     time_val restart_delay;          // delay between restarts
164
165     // Time allowed for service stop, after which SIGKILL is sent. 0 to disable.
166     time_val stop_timeout = {10, 0}; // default of 10 seconds
167
168     // Time allowed for service start, after which SIGINT is sent (and then SIGKILL after
169     // <stop_timeout>). 0 to disable.
170     time_val start_timeout = {60, 0}; // default of 1 minute
171
172     uid_t run_as_uid = -1;
173     gid_t run_as_gid = -1;
174     int force_notification_fd = -1;  // if set, notification fd for service process is set to this fd
175     string notification_var; // if set, name of an environment variable for notification fd
176
177     pid_t pid = -1;  // PID of the process. If state is STARTING or STOPPING,
178                      //   this is PID of the service script; otherwise it is the
179                      //   PID of the process itself (process service).
180     bp_sys::exit_status exit_status; // Exit status, if the process has exited (pid == -1).
181     int socket_fd = -1;  // For socket-activation services, this is the file
182                          // descriptor for the socket.
183     int notification_fd = -1;  // If readiness notification is via fd
184
185     bool waiting_restart_timer : 1;
186     bool stop_timer_armed : 1;
187     bool reserved_child_watch : 1;
188     bool tracking_child : 1;  // whether we expect to see child process status
189
190     // Run a child process (call after forking). Note that some parameters specify file descriptors,
191     // but in general file descriptors may be moved before the exec call.
192     void run_child_proc(run_proc_params params) noexcept;
193
194     // Launch the process with the given arguments, return true on success
195     bool start_ps_process(const std::vector<const char *> &args, bool on_console) noexcept;
196
197     // Restart the process (due to start failure or unexpected termination). Restarts will be
198     // rate-limited.
199     bool restart_ps_process() noexcept;
200
201     // Perform smooth recovery process
202     void do_smooth_recovery() noexcept;
203
204     // Start the process, return true on success
205     virtual bool bring_up() noexcept override;
206
207     // Called after forking (before executing remote process).
208     virtual void after_fork(pid_t child_pid) noexcept { }
209
210     // Called when the process exits. The exit_status is the status value yielded by
211     // the "wait" system call.
212     virtual void handle_exit_status(bp_sys::exit_status exit_status) noexcept = 0;
213
214     // Called if an exec fails.
215     virtual void exec_failed(run_proc_err errcode) noexcept = 0;
216
217     // Called if exec succeeds.
218     virtual void exec_succeeded() noexcept { };
219
220     virtual bool can_interrupt_start() noexcept override
221     {
222         return waiting_restart_timer || onstart_flags.start_interruptible
223                 || service_record::can_interrupt_start();
224     }
225
226     virtual bool can_proceed_to_start() noexcept override
227     {
228         return ! waiting_restart_timer;
229     }
230
231     virtual bool interrupt_start() noexcept override;
232
233     void becoming_inactive() noexcept override;
234
235     // Kill with SIGKILL
236     void kill_with_fire() noexcept;
237
238     // Signal the process group of the service process
239     void kill_pg(int signo) noexcept;
240
241     // stop immediately
242     void emergency_stop() noexcept;
243
244     // Open the activation socket, return false on failure
245     bool open_socket() noexcept;
246
247     // Get the readiness notification watcher for this service, if it has one; may return nullptr.
248     virtual ready_notify_watcher *get_ready_watcher() noexcept
249     {
250         return nullptr;
251     }
252
253     public:
254     // Constructor for a base_process_service. Note that the various parameters not specified here must in
255     // general be set separately (using the appropriate set_xxx function for each).
256     base_process_service(service_set *sset, string name, service_type_t record_type_p, string &&command,
257             const std::list<std::pair<unsigned,unsigned>> &command_offsets,
258             const std::list<prelim_dep> &deplist_p);
259
260     ~base_process_service() noexcept
261     {
262         if (reserved_child_watch) {
263             child_listener.unreserve(event_loop);
264         }
265         restart_timer.deregister(event_loop);
266     }
267
268     // Set the stop command and arguments (may throw std::bad_alloc)
269     void set_stop_command(const std::string &command,
270             std::list<std::pair<unsigned,unsigned>> &stop_command_offsets)
271     {
272         stop_command = command;
273         stop_arg_parts = separate_args(stop_command, stop_command_offsets);
274     }
275
276     void set_env_file(const std::string &env_file_p)
277     {
278         env_file = env_file_p;
279     }
280
281     void set_rlimits(std::vector<service_rlimits> &&rlimits_p)
282     {
283         rlimits = std::move(rlimits_p);
284     }
285
286     void set_restart_interval(timespec interval, int max_restarts) noexcept
287     {
288         restart_interval = interval;
289         max_restart_interval_count = max_restarts;
290     }
291
292     void set_restart_delay(timespec delay) noexcept
293     {
294         restart_delay = delay;
295     }
296
297     void set_stop_timeout(timespec timeout) noexcept
298     {
299         stop_timeout = timeout;
300     }
301
302     void set_start_timeout(timespec timeout) noexcept
303     {
304         start_timeout = timeout;
305     }
306
307     // Set an additional signal (other than SIGTERM) to be used to terminate the process
308     void set_extra_termination_signal(int signo) noexcept
309     {
310         this->term_signal = signo;
311     }
312
313     // Set the uid/gid that the service process will be run as
314     void set_run_as_uid_gid(uid_t uid, gid_t gid) noexcept
315     {
316         run_as_uid = uid;
317         run_as_gid = gid;
318     }
319
320     // Set the working directory
321     void set_working_dir(const string &working_dir_p)
322     {
323         working_dir = working_dir_p;
324     }
325
326     // Set the notification fd number that the service process will use
327     void set_notification_fd(int fd)
328     {
329         force_notification_fd = fd;
330     }
331
332     // Set the name of the environment variable that will be set to the notification fd number
333     // when the service process is run
334     void set_notification_var(string &&varname)
335     {
336         notification_var = std::move(varname);
337     }
338
339     // The restart/stop timer expired.
340     void timer_expired() noexcept;
341
342     // Accessor for testing:
343     const std::vector<const char *> & get_exec_arg_parts() noexcept
344     {
345         return exec_arg_parts;
346     }
347
348     pid_t get_pid() override
349     {
350         return pid;
351     }
352
353     int get_exit_status() override
354     {
355         return exit_status.as_int();
356     }
357 };
358
359 // Standard process service.
360 class process_service : public base_process_service
361 {
362     virtual void handle_exit_status(bp_sys::exit_status exit_status) noexcept override;
363     virtual void exec_failed(run_proc_err errcode) noexcept override;
364     virtual void exec_succeeded() noexcept override;
365     virtual void bring_down() noexcept override;
366
367     ready_notify_watcher readiness_watcher;
368
369 #if USE_UTMPX
370
371     char inittab_id[sizeof(utmpx().ut_id)];
372     char inittab_line[sizeof(utmpx().ut_line)];
373
374     protected:
375     void after_fork(pid_t child_pid) noexcept override
376     {
377         if (*inittab_id || *inittab_line) {
378             create_utmp_entry(inittab_id, inittab_line, child_pid);
379         }
380     }
381
382 #endif
383
384     protected:
385     ready_notify_watcher *get_ready_watcher() noexcept override
386     {
387         return &readiness_watcher;
388     }
389
390     public:
391     process_service(service_set *sset, const string &name, string &&command,
392             std::list<std::pair<unsigned,unsigned>> &command_offsets,
393             const std::list<prelim_dep> &depends_p)
394          : base_process_service(sset, name, service_type_t::PROCESS, std::move(command), command_offsets,
395              depends_p), readiness_watcher(this)
396     {
397     }
398
399 #if USE_UTMPX
400
401     // Set the id of the process in utmp (the "inittab" id)
402     void set_utmp_id(const char *id)
403     {
404         strncpy(inittab_id, id, sizeof(inittab_id));
405     }
406
407     // Set the device line of the process in utmp database
408     void set_utmp_line(const char *line)
409     {
410         strncpy(inittab_line, line, sizeof(inittab_line));
411     }
412
413 #endif
414
415     ~process_service() noexcept
416     {
417     }
418 };
419
420 // Bgproc (self-"backgrounding", i.e. double-forking) process service
421 class bgproc_service : public base_process_service
422 {
423     virtual void handle_exit_status(bp_sys::exit_status exit_status) noexcept override;
424     virtual void exec_failed(run_proc_err errcode) noexcept override;
425     virtual void bring_down() noexcept override;
426
427     enum class pid_result_t {
428         OK,
429         FAILED,      // failed to read pid or read invalid pid
430         TERMINATED   // read pid successfully, but the process already terminated
431     };
432
433     // Read the pid-file, return false on failure
434     pid_result_t read_pid_file(bp_sys::exit_status *exit_status) noexcept;
435
436     public:
437     bgproc_service(service_set *sset, const string &name, string &&command,
438             std::list<std::pair<unsigned,unsigned>> &command_offsets,
439             const std::list<prelim_dep> &depends_p)
440          : base_process_service(sset, name, service_type_t::BGPROCESS, std::move(command), command_offsets,
441              depends_p)
442     {
443     }
444
445     ~bgproc_service() noexcept
446     {
447     }
448 };
449
450 // Service which is started and stopped via separate commands
451 class scripted_service : public base_process_service
452 {
453     virtual void handle_exit_status(bp_sys::exit_status exit_status) noexcept override;
454     virtual void exec_succeeded() noexcept override;
455     virtual void exec_failed(run_proc_err errcode) noexcept override;
456     virtual void bring_down() noexcept override;
457
458     virtual bool interrupt_start() noexcept override
459     {
460         // if base::interrupt_start() returns false, then start hasn't been fully interrupted, but an
461         // interrupt has been issued:
462         interrupting_start = ! base_process_service::interrupt_start();
463         return ! interrupting_start;
464     }
465
466     bool interrupting_start : 1;  // running start script (true) or stop script (false)
467
468     public:
469     scripted_service(service_set *sset, const string &name, string &&command,
470             std::list<std::pair<unsigned,unsigned>> &command_offsets,
471             const std::list<prelim_dep> &depends_p)
472          : base_process_service(sset, name, service_type_t::SCRIPTED, std::move(command), command_offsets,
473              depends_p), interrupting_start(false)
474     {
475     }
476
477     ~scripted_service() noexcept
478     {
479     }
480 };