9acc09c24ce16ce0f6a24e28e43dd286efe46b30
[oweals/dinit.git] / src / service.cc
1 #include <cstring>
2 #include <cerrno>
3 #include <iterator>
4 #include <memory>
5 #include <cstddef>
6
7 #include <sys/ioctl.h>
8 #include <fcntl.h>
9 #include <termios.h>
10
11 #include "dinit.h"
12 #include "service.h"
13 #include "dinit-log.h"
14 #include "dinit-socket.h"
15 #include "dinit-util.h"
16 #include "baseproc-sys.h"
17
18 /*
19  * service.cc - Service management.
20  * See service.h for details.
21  */
22
23 // Find the requested service by name
24 static service_record * find_service(const std::list<service_record *> & records,
25                                     const char *name) noexcept
26 {
27     using std::list;
28     list<service_record *>::const_iterator i = records.begin();
29     for ( ; i != records.end(); i++ ) {
30         if (strcmp((*i)->get_name().c_str(), name) == 0) {
31             return *i;
32         }
33     }
34     return nullptr;
35 }
36
37 service_record * service_set::find_service(const std::string &name) noexcept
38 {
39     return ::find_service(records, name.c_str());
40 }
41
42 void service_set::stop_service(const std::string & name) noexcept
43 {
44     service_record *record = find_service(name);
45     if (record != nullptr) {
46         record->stop();
47         process_queues();
48     }
49 }
50
51 // Called when a service has actually stopped; dependents have stopped already, unless this stop
52 // is due to an unexpected process termination.
53 void service_record::stopped() noexcept
54 {
55     if (have_console) {
56         bp_sys::tcsetpgrp(0, bp_sys::getpgrp());
57         discard_console_log_buffer();
58         release_console();
59     }
60
61     force_stop = false;
62
63     // If we are a soft dependency of another target, break the acquisition from that target now:
64     for (auto & dependent : dependents) {
65         if (dependent->dep_type != dependency_type::REGULAR) {
66             if (dependent->holding_acq) {
67                 dependent->holding_acq = false;
68                 release();
69             }
70         }
71     }
72
73     bool will_restart = (desired_state == service_state_t::STARTED)
74             && services->get_auto_restart();
75
76     for (auto dependency : depends_on) {
77         // we signal dependencies in case they are waiting for us to stop:
78         dependency.get_to()->dependent_stopped();
79     }
80
81     service_state = service_state_t::STOPPED;
82
83     if (will_restart) {
84         // Desired state is "started".
85         restarting = true;
86         start(false);
87     }
88     else {
89         becoming_inactive();
90         
91         if (start_explicit) {
92             start_explicit = false;
93             release();
94         }
95         else if (required_by == 0) {
96             services->service_inactive(this);
97         }
98     }
99
100     log_service_stopped(service_name);
101     notify_listeners(service_event_t::STOPPED);
102 }
103
104 bool service_record::do_auto_restart() noexcept
105 {
106     if (auto_restart) {
107         return services->get_auto_restart();
108     }
109     return false;
110 }
111
112 void service_record::require() noexcept
113 {
114     if (required_by++ == 0) {
115         prop_require = !prop_release;
116         prop_release = false;
117         services->add_prop_queue(this);
118         if (service_state != service_state_t::STARTING && service_state != service_state_t::STARTED) {
119             prop_start = true;
120         }
121     }
122 }
123
124 void service_record::release(bool issue_stop) noexcept
125 {
126     if (--required_by == 0) {
127         desired_state = service_state_t::STOPPED;
128
129         // Can stop, and can release dependencies now. We don't need to issue a release if
130         // the require was pending though:
131         prop_release = !prop_require;
132         prop_require = false;
133         services->add_prop_queue(this);
134
135         if (service_state == service_state_t::STOPPED) {
136             services->service_inactive(this);
137         }
138         else if (issue_stop) {
139             do_stop();
140         }
141     }
142 }
143
144 void service_record::release_dependencies() noexcept
145 {
146     for (auto & dependency : depends_on) {
147         service_record * dep_to = dependency.get_to();
148         if (dependency.holding_acq) {
149             dep_to->release();
150             dependency.holding_acq = false;
151         }
152     }
153 }
154
155 void service_record::start(bool activate) noexcept
156 {
157     if (activate && ! start_explicit) {
158         require();
159         start_explicit = true;
160     }
161     
162     if (desired_state == service_state_t::STARTED && service_state != service_state_t::STOPPED) return;
163
164     bool was_active = service_state != service_state_t::STOPPED || desired_state != service_state_t::STOPPED;
165     desired_state = service_state_t::STARTED;
166     
167     if (service_state != service_state_t::STOPPED) {
168         // We're already starting/started, or we are stopping and need to wait for
169         // that the complete.
170         if (service_state != service_state_t::STOPPING || ! can_interrupt_stop()) {
171             return;
172         }
173         // We're STOPPING, and that can be interrupted. Our dependencies might be STOPPING,
174         // but if so they are waiting (for us), so they too can be instantly returned to
175         // STARTING state.
176         notify_listeners(service_event_t::STOPCANCELLED);
177     }
178     else if (! was_active) {
179         services->service_active(this);
180     }
181
182     service_state = service_state_t::STARTING;
183     waiting_for_deps = true;
184
185     if (start_check_dependencies()) {
186         services->add_transition_queue(this);
187     }
188 }
189
190 void service_record::do_propagation() noexcept
191 {
192     if (prop_require) {
193         // Need to require all our dependencies
194         for (auto & dep : depends_on) {
195             dep.get_to()->require();
196             dep.holding_acq = true;
197         }
198         prop_require = false;
199     }
200     
201     if (prop_release) {
202         release_dependencies();
203         prop_release = false;
204     }
205     
206     if (prop_failure) {
207         prop_failure = false;
208         failed_to_start(true);
209     }
210     
211     if (prop_start) {
212         prop_start = false;
213         start(false);
214     }
215
216     if (prop_stop) {
217         prop_stop = false;
218         do_stop();
219     }
220 }
221
222 void service_record::execute_transition() noexcept
223 {
224     // state is STARTED with restarting set true if we are running a smooth recovery.
225     if (service_state == service_state_t::STARTING || (service_state == service_state_t::STARTED
226             && restarting)) {
227         if (check_deps_started()) {
228             all_deps_started();
229         }
230     }
231     else if (service_state == service_state_t::STOPPING) {
232         if (stop_check_dependents()) {
233             waiting_for_deps = false;
234             bring_down();
235         }
236     }
237 }
238
239 void service_record::do_start() noexcept
240 {
241     if (pinned_stopped) return;
242     
243     if (service_state != service_state_t::STARTING) {
244         return;
245     }
246     
247     service_state = service_state_t::STARTING;
248
249     waiting_for_deps = true;
250
251     // Ask dependencies to start, mark them as being waited on.
252     if (check_deps_started()) {
253         // Once all dependencies are started, we start properly:
254         all_deps_started();
255     }
256 }
257
258 void service_record::dependency_started() noexcept
259 {
260     // Note that we check for STARTED state here in case the service is in smooth recovery while pinned.
261     // In that case it will wait for dependencies to start before restarting the process.
262     if ((service_state == service_state_t::STARTING || service_state == service_state_t::STARTED)
263             && waiting_for_deps) {
264         services->add_transition_queue(this);
265     }
266 }
267
268 bool service_record::start_check_dependencies() noexcept
269 {
270     bool all_deps_started = true;
271
272     for (auto & dep : depends_on) {
273         service_record * to = dep.get_to();
274         if (to->service_state != service_state_t::STARTED) {
275             if (to->service_state != service_state_t::STARTING) {
276                 to->prop_start = true;
277                 services->add_prop_queue(to);
278             }
279             dep.waiting_on = true;
280             all_deps_started = false;
281         }
282     }
283     
284     return all_deps_started;
285 }
286
287 bool service_record::check_deps_started() noexcept
288 {
289     for (auto & dep : depends_on) {
290         if (dep.waiting_on) {
291             return false;
292         }
293     }
294
295     return true;
296 }
297
298 void service_record::all_deps_started() noexcept
299 {
300     if (onstart_flags.starts_on_console && ! have_console) {
301         queue_for_console();
302         return;
303     }
304     
305     waiting_for_deps = false;
306
307     if (! can_proceed_to_start()) {
308         waiting_for_deps = true;
309         return;
310     }
311
312     bool start_success = bring_up();
313     if (! start_success) {
314         failed_to_start();
315     }
316 }
317
318 void service_record::acquired_console() noexcept
319 {
320     waiting_for_console = false;
321     have_console = true;
322
323     if (service_state != service_state_t::STARTING) {
324         // We got the console but no longer want it.
325         release_console();
326     }
327     else if (check_deps_started()) {
328         all_deps_started();
329     }
330     else {
331         // We got the console but can't use it yet.
332         release_console();
333     }
334 }
335
336 void service_record::started() noexcept
337 {
338     // If we start on console but don't keep it, release it now:
339     if (have_console && ! onstart_flags.runs_on_console) {
340         bp_sys::tcsetpgrp(0, bp_sys::getpgrp());
341         release_console();
342     }
343
344     log_service_started(get_name());
345     service_state = service_state_t::STARTED;
346     notify_listeners(service_event_t::STARTED);
347
348     if (onstart_flags.rw_ready) {
349         open_control_socket();
350     }
351     if (onstart_flags.log_ready) {
352         setup_external_log();
353     }
354
355     if (force_stop || desired_state == service_state_t::STOPPED) {
356         // We must now stop.
357         do_stop();
358         return;
359     }
360
361     // Notify any dependents whose desired state is STARTED:
362     for (auto dept : dependents) {
363         dept->get_from()->dependency_started();
364         dept->waiting_on = false;
365     }
366 }
367
368 void service_record::failed_to_start(bool depfailed, bool immediate_stop) noexcept
369 {
370     if (immediate_stop) {
371         service_state = service_state_t::STOPPED;
372         if (have_console) {
373             bp_sys::tcsetpgrp(0, bp_sys::getpgrp());
374             release_console();
375         }
376     }
377
378     if (waiting_for_console) {
379         services->unqueue_console(this);
380         waiting_for_console = false;
381     }
382     
383     if (start_explicit) {
384         start_explicit = false;
385         release(false);
386     }
387     
388     // Cancel start of dependents:
389     for (auto & dept : dependents) {
390         switch (dept->dep_type) {
391         case dependency_type::REGULAR:
392         case dependency_type::MILESTONE:
393             if (dept->get_from()->service_state == service_state_t::STARTING) {
394                 dept->get_from()->prop_failure = true;
395                 services->add_prop_queue(dept->get_from());
396             }
397             break;
398         case dependency_type::WAITS_FOR:
399         case dependency_type::SOFT:
400             if (dept->waiting_on) {
401                 dept->waiting_on = false;
402                 dept->get_from()->dependency_started();
403             }
404             if (dept->holding_acq) {
405                 dept->holding_acq = false;
406                 release();
407             }
408         }
409     }
410
411     log_service_failed(get_name());
412     notify_listeners(service_event_t::FAILEDSTART);
413 }
414
415 bool service_record::bring_up() noexcept
416 {
417     // default implementation: there is no process, so we are started.
418     started();
419     return true;
420 }
421
422 // Mark this and all dependent services as force-stopped.
423 void service_record::forced_stop() noexcept
424 {
425     if (service_state != service_state_t::STOPPED) {
426         force_stop = true;
427         if (! pinned_started) {
428             prop_stop = true;
429             services->add_transition_queue(this);
430         }
431     }
432 }
433
434 void service_record::dependent_stopped() noexcept
435 {
436     if (service_state == service_state_t::STOPPING && waiting_for_deps) {
437         services->add_transition_queue(this);
438     }
439 }
440
441 void service_record::stop(bool bring_down) noexcept
442 {
443     if (start_explicit) {
444         start_explicit = false;
445         release();
446     }
447
448     if (bring_down) {
449         do_stop();
450     }
451 }
452
453 void service_record::do_stop() noexcept
454 {
455     // A service that does actually stop for any reason should have its explicit activation released, unless
456     // it will restart:
457     if (start_explicit && ! do_auto_restart()) {
458         start_explicit = false;
459         release(false);
460     }
461
462     bool all_deps_stopped = stop_dependents();
463
464     if (service_state != service_state_t::STARTED) {
465         if (service_state == service_state_t::STARTING) {
466             // If waiting for a dependency, or waiting for the console, we can interrupt start. Otherwise,
467             // we need to delegate to can_interrupt_start() (which can be overridden).
468             if (! waiting_for_deps && ! waiting_for_console) {
469                 if (! can_interrupt_start()) {
470                     // Well this is awkward: we're going to have to continue starting. We can stop once we've
471                     // reached the started state.
472                     return;
473                 }
474
475                 if (! interrupt_start()) {
476                     // Now wait for service startup to actually end; we don't need to handle it here.
477                     notify_listeners(service_event_t::STARTCANCELLED);
478                     return;
479                 }
480             }
481             else if (waiting_for_console) {
482                 services->unqueue_console(this);
483                 waiting_for_console = false;
484             }
485
486             // We must have had desired_state == STARTED.
487             notify_listeners(service_event_t::STARTCANCELLED);
488
489             // Reaching this point, we are starting interruptibly - so we
490             // stop now (by falling through to below).
491         }
492         else {
493             // If we're starting we need to wait for that to complete.
494             // If we're already stopping/stopped there's nothing to do.
495             return;
496         }
497     }
498
499     if (pinned_started) return;
500
501     service_state = service_state_t::STOPPING;
502     waiting_for_deps = true;
503     if (all_deps_stopped) {
504         services->add_transition_queue(this);
505     }
506 }
507
508 bool service_record::stop_check_dependents() noexcept
509 {
510     bool all_deps_stopped = true;
511     for (auto dept : dependents) {
512         if (dept->dep_type == dependency_type::REGULAR && ! dept->get_from()->is_stopped()) {
513             all_deps_stopped = false;
514             break;
515         }
516     }
517     
518     return all_deps_stopped;
519 }
520
521 bool service_record::stop_dependents() noexcept
522 {
523     bool all_deps_stopped = true;
524     for (auto dept : dependents) {
525         if (dept->dep_type == dependency_type::REGULAR ||
526                 (dept->dep_type == dependency_type::MILESTONE &&
527                 dept->get_from()->service_state != service_state_t::STARTED)) {
528             if (! dept->get_from()->is_stopped()) {
529                 // Note we check *first* since if the dependent service is not stopped,
530                 // 1. We will issue a stop to it shortly and
531                 // 2. It will notify us when stopped, at which point the stop_check_dependents()
532                 //    check is run anyway.
533                 all_deps_stopped = false;
534             }
535
536             if (force_stop) {
537                 // If this service is to be forcefully stopped, dependents must also be.
538                 dept->get_from()->forced_stop();
539             }
540
541             dept->get_from()->prop_stop = true;
542             services->add_prop_queue(dept->get_from());
543         }
544         else {
545             // waits-for or soft dependency:
546             if (dept->waiting_on) {
547                 dept->waiting_on = false;
548                 dept->get_from()->dependency_started();
549             }
550             if (dept->holding_acq) {
551                 dept->holding_acq = false;
552                 release();
553             }
554         }
555     }
556
557     return all_deps_stopped;
558 }
559
560 // All dependents have stopped; we can stop now, too. Only called when STOPPING.
561 void service_record::bring_down() noexcept
562 {
563     waiting_for_deps = false;
564     stopped();
565 }
566
567 void service_record::unpin() noexcept
568 {
569     if (pinned_started) {
570         pinned_started = false;
571         if (desired_state == service_state_t::STOPPED || force_stop) {
572             do_stop();
573             services->process_queues();
574         }
575     }
576     if (pinned_stopped) {
577         pinned_stopped = false;
578         if (desired_state == service_state_t::STARTED) {
579             do_start();
580             services->process_queues();
581         }
582     }
583 }
584
585 void service_record::queue_for_console() noexcept
586 {
587     waiting_for_console = true;
588     services->append_console_queue(this);
589 }
590
591 void service_record::release_console() noexcept
592 {
593     have_console = false;
594     services->pull_console_queue();
595 }
596
597 bool service_record::interrupt_start() noexcept
598 {
599     return true;
600 }
601
602 void service_set::service_active(service_record *sr) noexcept
603 {
604     active_services++;
605 }
606
607 void service_set::service_inactive(service_record *sr) noexcept
608 {
609     active_services--;
610 }