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