Move separate_args function from header to source file.
[oweals/dinit.git] / src / proc-service.h
1 #include "service.h"
2
3 // Given a string and a list of pairs of (start,end) indices for each argument in that string,
4 // store a null terminator for the argument. Return a `char *` vector containing the beginning
5 // of each argument and a trailing nullptr. (The returned array is invalidated if the string is later modified).
6 std::vector<const char *> separate_args(std::string &s, std::list<std::pair<unsigned,unsigned>> &arg_indices);
7
8 class base_process_service;
9
10 // A timer for process restarting. Used to ensure a minimum delay between process restarts (and
11 // also for timing service stop before the SIGKILL hammer is used).
12 class process_restart_timer : public eventloop_t::timer_impl<process_restart_timer>
13 {
14     public:
15     base_process_service * service;
16
17     process_restart_timer(base_process_service *service_p)
18         : service(service_p)
19     {
20     }
21
22     dasynq::rearm timer_expiry(eventloop_t &, int expiry_count);
23 };
24
25 class base_process_service : public service_record
26 {
27     friend class service_child_watcher;
28     friend class exec_status_pipe_watcher;
29     friend class process_restart_timer;
30
31     private:
32     // Re-launch process
33     void do_restart() noexcept;
34
35     protected:
36     string program_name;          // storage for program/script and arguments
37     std::vector<const char *> exec_arg_parts; // pointer to each argument/part of the program_name, and nullptr
38
39     string stop_command;          // storage for stop program/script and arguments
40     std::vector<const char *> stop_arg_parts; // pointer to each argument/part of the stop_command, and nullptr
41
42     service_child_watcher child_listener;
43     exec_status_pipe_watcher child_status_listener;
44     process_restart_timer restart_timer;
45     time_val last_start_time;
46
47     // Restart interval time and restart count are used to track the number of automatic restarts
48     // over an interval. Too many restarts over an interval will inhibit further restarts.
49     time_val restart_interval_time;  // current restart interval
50     int restart_interval_count;      // count of restarts within current interval
51
52     time_val restart_interval;       // maximum restart interval
53     int max_restart_interval_count;  // number of restarts allowed over maximum interval
54     time_val restart_delay;          // delay between restarts
55
56     // Time allowed for service stop, after which SIGKILL is sent. 0 to disable.
57     time_val stop_timeout = {10, 0}; // default of 10 seconds
58
59     // Time allowed for service start, after which SIGINT is sent (and then SIGKILL after
60     // <stop_timeout>). 0 to disable.
61     time_val start_timeout = {60, 0}; // default of 1 minute
62
63     bool waiting_restart_timer : 1;
64     bool stop_timer_armed : 1;
65     bool reserved_child_watch : 1;
66     bool tracking_child : 1;  // whether we expect to see child process status
67     bool start_is_interruptible : 1;  // whether we can interrupt start
68
69     // Launch the process with the given arguments, return true on success
70     bool start_ps_process(const std::vector<const char *> &args, bool on_console) noexcept;
71
72     // Restart the process (due to start failure or unexpected termination). Restarts will be
73     // rate-limited.
74     bool restart_ps_process() noexcept;
75
76     // Perform smooth recovery process
77     void do_smooth_recovery() noexcept;
78
79     // Start the process, return true on success
80     virtual bool bring_up() noexcept override;
81
82     virtual void bring_down() noexcept override;
83
84     // Called when the process exits. The exit_status is the status value yielded by
85     // the "wait" system call.
86     virtual void handle_exit_status(int exit_status) noexcept = 0;
87
88     // Called if an exec fails.
89     virtual void exec_failed(int errcode) noexcept = 0;
90
91     virtual bool can_interrupt_start() noexcept override
92     {
93         return waiting_restart_timer || start_is_interruptible || service_record::can_interrupt_start();
94     }
95
96     virtual bool can_proceed_to_start() noexcept override
97     {
98         return ! waiting_restart_timer;
99     }
100
101     virtual bool interrupt_start() noexcept override;
102
103     // Kill with SIGKILL
104     void kill_with_fire() noexcept;
105
106     // Signal the process group of the service process
107     void kill_pg(int signo) noexcept;
108
109     public:
110     base_process_service(service_set *sset, string name, service_type_t record_type_p, string &&command,
111             std::list<std::pair<unsigned,unsigned>> &command_offsets,
112             const std::list<prelim_dep> &deplist_p);
113
114     ~base_process_service() noexcept
115     {
116     }
117
118     // Set the stop command and arguments (may throw std::bad_alloc)
119     void set_stop_command(std::string command, std::list<std::pair<unsigned,unsigned>> &stop_command_offsets)
120     {
121         stop_command = command;
122         stop_arg_parts = separate_args(stop_command, stop_command_offsets);
123     }
124
125     void set_restart_interval(timespec interval, int max_restarts) noexcept
126     {
127         restart_interval = interval;
128         max_restart_interval_count = max_restarts;
129     }
130
131     void set_restart_delay(timespec delay) noexcept
132     {
133         restart_delay = delay;
134     }
135
136     void set_stop_timeout(timespec timeout) noexcept
137     {
138         stop_timeout = timeout;
139     }
140
141     void set_start_timeout(timespec timeout) noexcept
142     {
143         start_timeout = timeout;
144     }
145
146     void set_start_interruptible(bool value) noexcept
147     {
148         start_is_interruptible = value;
149     }
150 };
151
152 class process_service : public base_process_service
153 {
154     virtual void handle_exit_status(int exit_status) noexcept override;
155     virtual void exec_failed(int errcode) noexcept override;
156     virtual void bring_down() noexcept override;
157
158     public:
159     process_service(service_set *sset, string name, string &&command,
160             std::list<std::pair<unsigned,unsigned>> &command_offsets,
161             std::list<prelim_dep> depends_p)
162          : base_process_service(sset, name, service_type_t::PROCESS, std::move(command), command_offsets,
163              depends_p)
164     {
165     }
166
167     ~process_service() noexcept
168     {
169     }
170 };
171
172 class bgproc_service : public base_process_service
173 {
174     virtual void handle_exit_status(int exit_status) noexcept override;
175     virtual void exec_failed(int errcode) noexcept override;
176
177     enum class pid_result_t {
178         OK,
179         FAILED,      // failed to read pid or read invalid pid
180         TERMINATED   // read pid successfully, but the process already terminated
181     };
182
183     // Read the pid-file, return false on failure
184     pid_result_t read_pid_file(int *exit_status) noexcept;
185
186     public:
187     bgproc_service(service_set *sset, string name, string &&command,
188             std::list<std::pair<unsigned,unsigned>> &command_offsets,
189             std::list<prelim_dep> depends_p)
190          : base_process_service(sset, name, service_type_t::BGPROCESS, std::move(command), command_offsets,
191              depends_p)
192     {
193     }
194
195     ~bgproc_service() noexcept
196     {
197     }
198 };
199
200 class scripted_service : public base_process_service
201 {
202     virtual void handle_exit_status(int exit_status) noexcept override;
203     virtual void exec_failed(int errcode) noexcept override;
204     virtual void bring_down() noexcept override;
205
206     virtual bool interrupt_start() noexcept override
207     {
208         // if base::interrupt_start() returns false, then start hasn't been fully interrupted, but an
209         // interrupt has been issued:
210         interrupting_start = ! base_process_service::interrupt_start();
211         return ! interrupting_start;
212     }
213
214     bool interrupting_start : 1;  // running start script (true) or stop script (false)
215
216     public:
217     scripted_service(service_set *sset, string name, string &&command,
218             std::list<std::pair<unsigned,unsigned>> &command_offsets,
219             std::list<prelim_dep> depends_p)
220          : base_process_service(sset, name, service_type_t::SCRIPTED, std::move(command), command_offsets,
221              depends_p), interrupting_start(false)
222     {
223     }
224
225     ~scripted_service() noexcept
226     {
227     }
228 };