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