Stub out system functions for testing (WIP).
[oweals/dinit.git] / src / baseproc-service.cc
1 #include <cstring>
2
3 #include "dinit.h"
4 #include "dinit-log.h"
5 #include "dinit-socket.h"
6 #include "proc-service.h"
7
8 #include "baseproc-sys.h"
9
10 /*
11  * Base process implementation (base_process_service).
12  *
13  * See proc-service.h for interface documentation.
14  */
15
16 void base_process_service::do_smooth_recovery() noexcept
17 {
18     if (! restart_ps_process()) {
19         emergency_stop();
20         services->process_queues();
21     }
22 }
23
24 bool base_process_service::bring_up() noexcept
25 {
26     if (restarting) {
27         if (pid == -1) {
28             return restart_ps_process();
29         }
30         return true;
31     }
32     else {
33         event_loop.get_time(restart_interval_time, clock_type::MONOTONIC);
34         restart_interval_count = 0;
35         if (start_ps_process(exec_arg_parts, onstart_flags.starts_on_console)) {
36             if (start_timeout != time_val(0,0)) {
37                 restart_timer.arm_timer_rel(event_loop, start_timeout);
38                 stop_timer_armed = true;
39             }
40             else if (stop_timer_armed) {
41                 restart_timer.stop_timer(event_loop);
42                 stop_timer_armed = false;
43             }
44             return true;
45         }
46         return false;
47     }
48 }
49
50 bool base_process_service::start_ps_process(const std::vector<const char *> &cmd, bool on_console) noexcept
51 {
52     // In general, you can't tell whether fork/exec is successful. We use a pipe to communicate
53     // success/failure from the child to the parent. The pipe is set CLOEXEC so a successful
54     // exec closes the pipe, and the parent sees EOF. If the exec is unsuccessful, the errno
55     // is written to the pipe, and the parent can read it.
56
57     event_loop.get_time(last_start_time, clock_type::MONOTONIC);
58
59     int pipefd[2];
60     if (bp_sys::pipe2(pipefd, O_CLOEXEC)) {
61         log(loglevel_t::ERROR, get_name(), ": can't create status check pipe: ", strerror(errno));
62         return false;
63     }
64
65     const char * logfile = this->logfile.c_str();
66     if (*logfile == 0) {
67         logfile = "/dev/null";
68     }
69
70     bool child_status_registered = false;
71     control_conn_t *control_conn = nullptr;
72
73     int control_socket[2] = {-1, -1};
74     if (onstart_flags.pass_cs_fd) {
75         if (dinit_socketpair(AF_UNIX, SOCK_STREAM, /* protocol */ 0, control_socket, SOCK_NONBLOCK)) {
76             log(loglevel_t::ERROR, get_name(), ": can't create control socket: ", strerror(errno));
77             goto out_p;
78         }
79
80         // Make the server side socket close-on-exec:
81         int fdflags = bp_sys::fcntl(control_socket[0], F_GETFD);
82         bp_sys::fcntl(control_socket[0], F_SETFD, fdflags | FD_CLOEXEC);
83
84         try {
85             control_conn = new control_conn_t(event_loop, services, control_socket[0]);
86         }
87         catch (std::exception &exc) {
88             log(loglevel_t::ERROR, get_name(), ": can't launch process; out of memory");
89             goto out_cs;
90         }
91     }
92
93     // Set up complete, now fork and exec:
94
95     pid_t forkpid;
96
97     try {
98         child_status_listener.add_watch(event_loop, pipefd[0], dasynq::IN_EVENTS);
99         child_status_registered = true;
100
101         // We specify a high priority (i.e. low priority value) so that process termination is
102         // handled early. This means we have always recorded that the process is terminated by the
103         // time that we handle events that might otherwise cause us to signal the process, so we
104         // avoid sending a signal to an invalid (and possibly recycled) process ID.
105         forkpid = child_listener.fork(event_loop, reserved_child_watch, dasynq::DEFAULT_PRIORITY - 10);
106         reserved_child_watch = true;
107     }
108     catch (std::exception &e) {
109         log(loglevel_t::ERROR, get_name(), ": Could not fork: ", e.what());
110         goto out_cs_h;
111     }
112
113     if (forkpid == 0) {
114         run_child_proc(cmd.data(), logfile, on_console, pipefd[1], control_socket[1]);
115     }
116     else {
117         // Parent process
118         bp_sys::close(pipefd[1]); // close the 'other end' fd
119         if (control_socket[1] != -1) {
120             bp_sys::close(control_socket[1]);
121         }
122         pid = forkpid;
123
124         waiting_for_execstat = true;
125         return true;
126     }
127
128     // Failure exit:
129
130     out_cs_h:
131     if (child_status_registered) {
132         child_status_listener.deregister(event_loop);
133     }
134
135     if (onstart_flags.pass_cs_fd) {
136         delete control_conn;
137
138         out_cs:
139         bp_sys::close(control_socket[0]);
140         bp_sys::close(control_socket[1]);
141     }
142
143     out_p:
144     bp_sys::close(pipefd[0]);
145     bp_sys::close(pipefd[1]);
146
147     return false;
148 }
149
150 void base_process_service::bring_down() noexcept
151 {
152     waiting_for_deps = false;
153     if (pid != -1) {
154         // The process is still kicking on - must actually kill it. We signal the process
155         // group (-pid) rather than just the process as there's less risk then of creating
156         // an orphaned process group:
157         if (! onstart_flags.no_sigterm) {
158             kill_pg(SIGTERM);
159         }
160         if (term_signal != -1) {
161             kill_pg(term_signal);
162         }
163
164         // In most cases, the rest is done in handle_exit_status.
165         // If we are a BGPROCESS and the process is not our immediate child, however, that
166         // won't work - check for this now:
167         if (get_type() == service_type_t::BGPROCESS && ! tracking_child) {
168             stopped();
169         }
170         else if (stop_timeout != time_val(0,0)) {
171             restart_timer.arm_timer_rel(event_loop, stop_timeout);
172             stop_timer_armed = true;
173         }
174     }
175     else {
176         // The process is already dead.
177         stopped();
178     }
179 }
180
181 base_process_service::base_process_service(service_set *sset, string name,
182         service_type_t service_type_p, string &&command,
183         std::list<std::pair<unsigned,unsigned>> &command_offsets,
184         const std::list<prelim_dep> &deplist_p)
185      : service_record(sset, name, service_type_p, deplist_p), child_listener(this),
186        child_status_listener(this), restart_timer(this)
187 {
188     program_name = std::move(command);
189     exec_arg_parts = separate_args(program_name, command_offsets);
190
191     restart_interval_count = 0;
192     restart_interval_time = {0, 0};
193     restart_timer.service = this;
194     restart_timer.add_timer(event_loop);
195
196     // By default, allow a maximum of 3 restarts within 10.0 seconds:
197     restart_interval.seconds() = 10;
198     restart_interval.nseconds() = 0;
199     max_restart_interval_count = 3;
200
201     waiting_restart_timer = false;
202     reserved_child_watch = false;
203     tracking_child = false;
204     stop_timer_armed = false;
205     start_is_interruptible = false;
206 }
207
208 void base_process_service::do_restart() noexcept
209 {
210     waiting_restart_timer = false;
211     restart_interval_count++;
212     auto service_state = get_state();
213
214     // We may be STARTING (regular restart) or STARTED ("smooth recovery"). This affects whether
215     // the process should be granted access to the console:
216     bool on_console = service_state == service_state_t::STARTING
217             ? onstart_flags.starts_on_console : onstart_flags.runs_on_console;
218
219     if (service_state == service_state_t::STARTING) {
220         // for a smooth recovery, we want to check dependencies are available before actually
221         // starting:
222         if (! check_deps_started()) {
223             waiting_for_deps = true;
224             return;
225         }
226     }
227
228     if (! start_ps_process(exec_arg_parts, on_console)) {
229         restarting = false;
230         if (service_state == service_state_t::STARTING) {
231             failed_to_start();
232         }
233         else {
234             // desired_state = service_state_t::STOPPED;
235             forced_stop();
236         }
237         services->process_queues();
238     }
239 }
240
241 bool base_process_service::restart_ps_process() noexcept
242 {
243     using time_val = dasynq::time_val;
244
245     time_val current_time;
246     event_loop.get_time(current_time, clock_type::MONOTONIC);
247
248     if (max_restart_interval_count != 0) {
249         // Check whether we're still in the most recent restart check interval:
250         time_val int_diff = current_time - restart_interval_time;
251         if (int_diff < restart_interval) {
252             if (restart_interval_count >= max_restart_interval_count) {
253                 log(loglevel_t::ERROR, "Service ", get_name(), " restarting too quickly; stopping.");
254                 return false;
255             }
256         }
257         else {
258             restart_interval_time = current_time;
259             restart_interval_count = 0;
260         }
261     }
262
263     // Check if enough time has lapsed since the prevous restart. If not, start a timer:
264     time_val tdiff = current_time - last_start_time;
265     if (restart_delay <= tdiff) {
266         // > restart delay (normally 200ms)
267         do_restart();
268     }
269     else {
270         time_val timeout = restart_delay - tdiff;
271         restart_timer.arm_timer_rel(event_loop, timeout);
272         waiting_restart_timer = true;
273     }
274     return true;
275 }
276
277 bool base_process_service::interrupt_start() noexcept
278 {
279     if (waiting_restart_timer) {
280         restart_timer.stop_timer(event_loop);
281         waiting_restart_timer = false;
282         return service_record::interrupt_start();
283     }
284     else {
285         log(loglevel_t::WARN, "Interrupting start of service ", get_name(), " with pid ", pid, " (with SIGINT).");
286         kill_pg(SIGINT);
287         if (stop_timeout != time_val(0,0)) {
288             restart_timer.arm_timer_rel(event_loop, stop_timeout);
289             stop_timer_armed = true;
290         }
291         else if (stop_timer_armed) {
292             restart_timer.stop_timer(event_loop);
293             stop_timer_armed = false;
294         }
295         set_state(service_state_t::STOPPING);
296         notify_listeners(service_event_t::STARTCANCELLED);
297         return false;
298     }
299 }
300
301 void base_process_service::kill_with_fire() noexcept
302 {
303     if (pid != -1) {
304         log(loglevel_t::WARN, "Service ", get_name(), " with pid ", pid, " exceeded allowed stop time; killing.");
305         kill_pg(SIGKILL);
306     }
307 }
308
309 void base_process_service::kill_pg(int signo) noexcept
310 {
311     pid_t pgid = getpgid(pid);
312     if (pgid == -1) {
313         // only should happen if pid is invalid, which should never happen...
314         log(loglevel_t::ERROR, get_name(), ": can't signal process: ", strerror(errno));
315         return;
316     }
317     bp_sys::kill(-pgid, signo);
318 }
319
320 void base_process_service::timer_expired() noexcept
321 {
322     stop_timer_armed = false;
323
324     // Timer expires if:
325     // We are stopping, including after having startup cancelled (stop timeout, state is STOPPING); We are
326     // starting (start timeout, state is STARTING); We are waiting for restart timer before restarting,
327     // including smooth recovery (restart timeout, state is STARTING or STARTED).
328     if (get_state() == service_state_t::STOPPING) {
329         kill_with_fire();
330     }
331     else if (pid != -1) {
332         // Starting, start timed out.
333         stop_dependents();
334         interrupt_start();
335     }
336     else {
337         // STARTING / STARTED, and we have a pid: must be restarting (smooth recovery if STARTED)
338         do_restart();
339     }
340 }