2 #include <sys/resource.h>
4 #include "baseproc-sys.h"
6 #include "dinit-utmp.h"
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.
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
15 std::vector<const char *> separate_args(std::string &s,
16 const std::list<std::pair<unsigned,unsigned>> &arg_indices);
18 // Parameters for process execution
19 struct run_proc_params
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
35 const std::vector<service_rlimits> &rlimits;
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)
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
50 extern const char * const exec_stage_descriptions[static_cast<int>(exec_stage::DO_EXEC) + 1];
52 // Error information from process execution transferred via this struct
59 class base_process_service;
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>
66 base_process_service * service;
68 explicit process_restart_timer(base_process_service *service_p)
73 dasynq::rearm timer_expiry(eventloop_t &, int expiry_count);
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>
80 base_process_service * service;
81 dasynq::rearm fd_event(eventloop_t &eloop, int fd, int flags) noexcept;
83 exec_status_pipe_watcher(base_process_service * sr) noexcept : service(sr) { }
85 exec_status_pipe_watcher(const exec_status_pipe_watcher &) = delete;
86 void operator=(const exec_status_pipe_watcher &) = delete;
89 // Watcher for readiness notification pipe
90 class ready_notify_watcher : public eventloop_t::fd_watcher_impl<ready_notify_watcher>
93 base_process_service * service;
94 dasynq::rearm fd_event(eventloop_t &eloop, int fd, int flags) noexcept;
96 ready_notify_watcher(base_process_service * sr) noexcept : service(sr) { }
98 ready_notify_watcher(const ready_notify_watcher &) = delete;
99 void operator=(const ready_notify_watcher &) = delete;
103 class service_child_watcher : public eventloop_t::child_proc_watcher_impl<service_child_watcher>
106 base_process_service * service;
107 dasynq::rearm status_change(eventloop_t &eloop, pid_t child, int status) noexcept;
109 service_child_watcher(base_process_service * sr) noexcept : service(sr) { }
111 service_child_watcher(const service_child_watcher &) = delete;
112 void operator=(const service_child_watcher &) = delete;
115 // Base class for process-based services.
116 class base_process_service : public service_record
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;
125 void do_restart() noexcept;
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;
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;
136 string working_dir; // working directory (or empty)
137 string env_file; // file with environment settings for this service
139 std::vector<service_rlimits> rlimits; // resource limits
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;
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
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
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
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
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
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
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
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;
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;
187 // Restart the process (due to start failure or unexpected termination). Restarts will be
189 bool restart_ps_process() noexcept;
191 // Perform smooth recovery process
192 void do_smooth_recovery() noexcept;
194 // Start the process, return true on success
195 virtual bool bring_up() noexcept override;
197 // Called after forking (before executing remote process).
198 virtual void after_fork(pid_t child_pid) noexcept { }
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;
204 // Called if an exec fails.
205 virtual void exec_failed(run_proc_err errcode) noexcept = 0;
207 // Called if exec succeeds.
208 virtual void exec_succeeded() noexcept { };
210 virtual bool can_interrupt_start() noexcept override
212 return waiting_restart_timer || onstart_flags.start_interruptible
213 || service_record::can_interrupt_start();
216 virtual bool can_proceed_to_start() noexcept override
218 return ! waiting_restart_timer;
221 virtual bool interrupt_start() noexcept override;
223 void becoming_inactive() noexcept override;
226 void kill_with_fire() noexcept;
228 // Signal the process group of the service process
229 void kill_pg(int signo) noexcept;
232 void emergency_stop() noexcept;
234 // Open the activation socket, return false on failure
235 bool open_socket() noexcept;
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
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);
250 ~base_process_service() noexcept
252 if (reserved_child_watch) {
253 child_listener.unreserve(event_loop);
255 restart_timer.deregister(event_loop);
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
262 program_name = std::move(command_p);
263 exec_arg_parts = std::move(command_parts_p);
266 void get_command(std::string &command_p, std::vector<const char *> &command_parts_p)
268 command_p = program_name;
269 command_parts_p = exec_arg_parts;
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)
276 stop_command = command;
277 stop_arg_parts = separate_args(stop_command, stop_command_offsets);
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
286 stop_command = std::move(command);
287 stop_arg_parts = std::move(command_parts);
290 void set_env_file(const std::string &env_file_p)
292 env_file = env_file_p;
295 void set_env_file(std::string &&env_file_p) noexcept
297 env_file = std::move(env_file_p);
300 void set_rlimits(std::vector<service_rlimits> &&rlimits_p)
302 rlimits = std::move(rlimits_p);
305 void set_restart_interval(timespec interval, int max_restarts) noexcept
307 restart_interval = interval;
308 max_restart_interval_count = max_restarts;
311 void set_restart_delay(timespec delay) noexcept
313 restart_delay = delay;
316 void set_stop_timeout(timespec timeout) noexcept
318 stop_timeout = timeout;
321 void set_start_timeout(timespec timeout) noexcept
323 start_timeout = timeout;
326 // Set an additional signal (other than SIGTERM) to be used to terminate the process
327 void set_extra_termination_signal(int signo) noexcept
329 this->term_signal = signo;
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
339 // Set the working directory
340 void set_working_dir(const string &working_dir_p)
342 working_dir = working_dir_p;
345 void set_working_dir(string &&working_dir_p) noexcept
347 working_dir = std::move(working_dir_p);
350 // Set the notification fd number that the service process will use
351 void set_notification_fd(int fd)
353 force_notification_fd = fd;
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)
360 notification_var = std::move(varname);
363 // The restart/stop timer expired.
364 void timer_expired() noexcept;
366 // Accessor for testing:
367 const std::vector<const char *> & get_exec_arg_parts() noexcept
369 return exec_arg_parts;
372 pid_t get_pid() override
377 int get_exit_status() override
379 return exit_status.as_int();
383 // Standard process service.
384 class process_service : public base_process_service
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;
391 ready_notify_watcher readiness_watcher;
395 char inittab_id[sizeof(utmpx().ut_id)];
396 char inittab_line[sizeof(utmpx().ut_line)];
399 void after_fork(pid_t child_pid) noexcept override
401 if (*inittab_id || *inittab_line) {
402 create_utmp_entry(inittab_id, inittab_line, child_pid);
409 ready_notify_watcher *get_ready_watcher() noexcept override
411 return &readiness_watcher;
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)
425 // Set the id of the process in utmp (the "inittab" id)
426 void set_utmp_id(const char *id)
428 strncpy(inittab_id, id, sizeof(inittab_id));
431 // Set the device line of the process in utmp database
432 void set_utmp_line(const char *line)
434 strncpy(inittab_line, line, sizeof(inittab_line));
437 // Get the utmp (inittab) id, may not be nul terminated if maximum length!
438 const char *get_utmp_id()
443 // Get the utmp (inittab) line, may not be nul terminated if maximum length!
444 const char *get_utmp_line()
449 constexpr size_t get_utmp_id_size() { return sizeof(inittab_id); }
450 constexpr size_t get_utmp_line_size() { return sizeof(inittab_line); }
454 ~process_service() noexcept
459 // Bgproc (self-"backgrounding", i.e. double-forking) process service
460 class bgproc_service : public base_process_service
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;
466 enum class pid_result_t {
468 FAILED, // failed to read pid or read invalid pid
469 TERMINATED // read pid successfully, but the process already terminated
474 // Read the pid-file contents
475 pid_result_t read_pid_file(bp_sys::exit_status *exit_status) noexcept;
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,
486 ~bgproc_service() noexcept
490 void set_pid_file(string &&pid_file) noexcept
492 this->pid_file = std::move(pid_file);
495 const std::string &get_pid_file() noexcept
501 // Service which is started and stopped via separate commands
502 class scripted_service : public base_process_service
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;
509 virtual bool interrupt_start() noexcept override
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;
517 bool interrupting_start : 1; // running start script (true) or stop script (false)
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)
528 ~scripted_service() noexcept