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