13 #include "dinit-log.h"
14 #include "dinit-socket.h"
15 #include "dinit-util.h"
18 * service.cc - Service management.
19 * See service.h for details.
22 // Find the requested service by name
23 static service_record * find_service(const std::list<service_record *> & records,
24 const char *name) noexcept
27 list<service_record *>::const_iterator i = records.begin();
28 for ( ; i != records.end(); i++ ) {
29 if (strcmp((*i)->get_name().c_str(), name) == 0) {
36 service_record * service_set::find_service(const std::string &name) noexcept
38 return ::find_service(records, name.c_str());
41 void service_set::stop_service(const std::string & name) noexcept
43 service_record *record = find_service(name);
44 if (record != nullptr) {
50 // Called when a service has actually stopped; dependents have stopped already, unless this stop
51 // is due to an unexpected process termination.
52 void service_record::stopped() noexcept
54 if (onstart_flags.runs_on_console) {
55 tcsetpgrp(0, getpgrp());
56 discard_console_log_buffer();
62 // If we are a soft dependency of another target, break the acquisition from that target now:
63 for (auto & dependent : dependents) {
64 if (dependent->dep_type != dependency_type::REGULAR) {
65 if (dependent->holding_acq) {
66 dependent->holding_acq = false;
72 bool will_restart = (desired_state == service_state_t::STARTED)
73 && services->get_auto_restart();
75 for (auto dependency : depends_on) {
76 // we signal dependencies in case they are waiting for us to stop:
77 dependency.get_to()->dependent_stopped();
80 service_state = service_state_t::STOPPED;
83 // Desired state is "started".
91 start_explicit = false;
94 else if (required_by == 0) {
95 services->service_inactive(this);
99 log_service_stopped(service_name);
100 notify_listeners(service_event_t::STOPPED);
103 bool service_record::do_auto_restart() noexcept
106 return services->get_auto_restart();
111 void service_record::require() noexcept
113 if (required_by++ == 0) {
114 prop_require = !prop_release;
115 prop_release = false;
116 services->add_prop_queue(this);
120 void service_record::release(bool issue_stop) noexcept
122 if (--required_by == 0) {
123 desired_state = service_state_t::STOPPED;
125 // Can stop, and can release dependencies now. We don't need to issue a release if
126 // the require was pending though:
127 prop_release = !prop_require;
128 prop_require = false;
129 services->add_prop_queue(this);
131 if (service_state == service_state_t::STOPPED) {
132 services->service_inactive(this);
134 else if (issue_stop) {
140 void service_record::release_dependencies() noexcept
142 for (auto & dependency : depends_on) {
143 service_record * dep_to = dependency.get_to();
144 if (dependency.holding_acq) {
146 dependency.holding_acq = false;
151 void service_record::start(bool activate) noexcept
153 if (activate && ! start_explicit) {
155 start_explicit = true;
158 if (desired_state == service_state_t::STARTED && service_state != service_state_t::STOPPED) return;
160 bool was_active = service_state != service_state_t::STOPPED || desired_state != service_state_t::STOPPED;
161 desired_state = service_state_t::STARTED;
163 if (service_state != service_state_t::STOPPED) {
164 // We're already starting/started, or we are stopping and need to wait for
165 // that the complete.
166 if (service_state != service_state_t::STOPPING || ! can_interrupt_stop()) {
169 // We're STOPPING, and that can be interrupted. Our dependencies might be STOPPING,
170 // but if so they are waiting (for us), so they too can be instantly returned to
172 notify_listeners(service_event_t::STOPCANCELLED);
174 else if (! was_active) {
175 services->service_active(this);
178 service_state = service_state_t::STARTING;
179 waiting_for_deps = true;
181 if (start_check_dependencies()) {
182 services->add_transition_queue(this);
186 void service_record::do_propagation() noexcept
189 // Need to require all our dependencies
190 for (auto & dep : depends_on) {
191 dep.get_to()->require();
192 dep.holding_acq = true;
194 prop_require = false;
198 release_dependencies();
199 prop_release = false;
203 prop_failure = false;
204 failed_to_start(true);
218 void service_record::execute_transition() noexcept
220 // state is STARTED with restarting set true if we are running a smooth recovery.
221 if (service_state == service_state_t::STARTING || (service_state == service_state_t::STARTED
223 if (check_deps_started()) {
227 else if (service_state == service_state_t::STOPPING) {
228 if (stop_check_dependents()) {
229 waiting_for_deps = false;
235 void service_record::do_start() noexcept
237 if (pinned_stopped) return;
239 if (service_state != service_state_t::STARTING) {
243 service_state = service_state_t::STARTING;
245 waiting_for_deps = true;
247 // Ask dependencies to start, mark them as being waited on.
248 if (check_deps_started()) {
249 // Once all dependencies are started, we start properly:
254 void service_record::dependency_started() noexcept
256 if ((service_state == service_state_t::STARTING || service_state == service_state_t::STARTED)
257 && waiting_for_deps) {
258 services->add_transition_queue(this);
262 bool service_record::start_check_dependencies() noexcept
264 bool all_deps_started = true;
266 for (auto & dep : depends_on) {
267 service_record * to = dep.get_to();
268 if (to->service_state != service_state_t::STARTED) {
269 if (to->service_state != service_state_t::STARTING) {
270 to->prop_start = true;
271 services->add_prop_queue(to);
273 dep.waiting_on = true;
274 all_deps_started = false;
278 return all_deps_started;
281 bool service_record::check_deps_started() noexcept
283 for (auto & dep : depends_on) {
284 if (dep.waiting_on) {
292 void service_record::all_deps_started() noexcept
294 if (onstart_flags.starts_on_console && ! have_console) {
299 waiting_for_deps = false;
301 if (! can_proceed_to_start()) {
302 waiting_for_deps = true;
306 bool start_success = bring_up();
307 if (! start_success) {
312 void service_record::acquired_console() noexcept
314 waiting_for_console = false;
317 if (service_state != service_state_t::STARTING) {
318 // We got the console but no longer want it.
321 else if (check_deps_started()) {
325 // We got the console but can't use it yet.
331 void service_record::started() noexcept
333 // If we start on console but don't keep it, release it now:
334 if (have_console && ! onstart_flags.runs_on_console) {
335 tcsetpgrp(0, getpgrp());
339 log_service_started(get_name());
340 service_state = service_state_t::STARTED;
341 notify_listeners(service_event_t::STARTED);
343 if (onstart_flags.rw_ready) {
344 open_control_socket();
346 if (onstart_flags.log_ready) {
347 setup_external_log();
350 if (force_stop || desired_state == service_state_t::STOPPED) {
356 // Notify any dependents whose desired state is STARTED:
357 for (auto dept : dependents) {
358 dept->get_from()->dependency_started();
359 dept->waiting_on = false;
363 void service_record::failed_to_start(bool depfailed) noexcept
366 tcsetpgrp(0, getpgrp());
369 if (waiting_for_console) {
370 services->unqueue_console(this);
371 waiting_for_console = false;
374 log_service_failed(get_name());
375 service_state = service_state_t::STOPPED;
376 if (start_explicit) {
377 start_explicit = false;
380 notify_listeners(service_event_t::FAILEDSTART);
382 // Cancel start of dependents:
383 for (auto & dept : dependents) {
384 switch (dept->dep_type) {
385 case dependency_type::REGULAR:
386 case dependency_type::MILESTONE:
387 if (dept->get_from()->service_state == service_state_t::STARTING) {
388 dept->get_from()->prop_failure = true;
389 services->add_prop_queue(dept->get_from());
392 case dependency_type::WAITS_FOR:
393 case dependency_type::SOFT:
394 if (dept->waiting_on) {
395 dept->waiting_on = false;
396 dept->get_from()->dependency_started();
398 if (dept->holding_acq) {
399 dept->holding_acq = false;
406 bool service_record::bring_up() noexcept
408 // default implementation: there is no process, so we are started.
413 // Mark this and all dependent services as force-stopped.
414 void service_record::forced_stop() noexcept
416 if (service_state != service_state_t::STOPPED) {
418 services->add_transition_queue(this);
422 void service_record::dependent_stopped() noexcept
424 if (service_state == service_state_t::STOPPING && waiting_for_deps) {
425 services->add_transition_queue(this);
429 void service_record::stop(bool bring_down) noexcept
431 if (start_explicit) {
432 start_explicit = false;
441 void service_record::do_stop() noexcept
443 if (pinned_started) return;
445 if (start_explicit && ! do_auto_restart()) {
446 start_explicit = false;
450 bool all_deps_stopped = stop_dependents();
452 if (service_state != service_state_t::STARTED) {
453 if (service_state == service_state_t::STARTING) {
454 // If waiting for a dependency, or waiting for the console, we can interrupt start. Otherwise,
455 // we need to delegate to can_interrupt_start() (which can be overridden).
456 if (! waiting_for_deps && ! waiting_for_console) {
457 if (! can_interrupt_start()) {
458 // Well this is awkward: we're going to have to continue starting. We can stop once we've
459 // reached the started state.
463 if (! interrupt_start()) {
464 // Now wait for service startup to actually end; we don't need to handle it here.
468 else if (waiting_for_console) {
469 services->unqueue_console(this);
470 waiting_for_console = false;
473 // We must have had desired_state == STARTED.
474 notify_listeners(service_event_t::STARTCANCELLED);
476 // Reaching this point, we are starting interruptibly - so we
477 // stop now (by falling through to below).
480 // If we're starting we need to wait for that to complete.
481 // If we're already stopping/stopped there's nothing to do.
486 service_state = service_state_t::STOPPING;
487 waiting_for_deps = true;
488 if (all_deps_stopped) {
489 services->add_transition_queue(this);
493 bool service_record::stop_check_dependents() noexcept
495 bool all_deps_stopped = true;
496 for (auto dept : dependents) {
497 if (dept->dep_type == dependency_type::REGULAR && ! dept->get_from()->is_stopped()) {
498 all_deps_stopped = false;
503 return all_deps_stopped;
506 bool service_record::stop_dependents() noexcept
508 bool all_deps_stopped = true;
509 for (auto dept : dependents) {
510 if (dept->dep_type == dependency_type::REGULAR) {
511 if (! dept->get_from()->is_stopped()) {
512 // Note we check *first* since if the dependent service is not stopped,
513 // 1. We will issue a stop to it shortly and
514 // 2. It will notify us when stopped, at which point the stop_check_dependents()
515 // check is run anyway.
516 all_deps_stopped = false;
520 // If this service is to be forcefully stopped, dependents must also be.
521 dept->get_from()->forced_stop();
524 dept->get_from()->prop_stop = true;
525 services->add_prop_queue(dept->get_from());
529 return all_deps_stopped;
532 // All dependents have stopped; we can stop now, too. Only called when STOPPING.
533 void service_record::bring_down() noexcept
535 waiting_for_deps = false;
539 void service_record::unpin() noexcept
541 if (pinned_started) {
542 pinned_started = false;
543 if (desired_state == service_state_t::STOPPED || force_stop) {
545 services->process_queues();
548 if (pinned_stopped) {
549 pinned_stopped = false;
550 if (desired_state == service_state_t::STARTED) {
552 services->process_queues();
557 void service_record::queue_for_console() noexcept
559 waiting_for_console = true;
560 services->append_console_queue(this);
563 void service_record::release_console() noexcept
565 have_console = false;
566 services->pull_console_queue();
569 bool service_record::interrupt_start() noexcept
571 if (onstart_flags.starts_on_console) {
572 services->unqueue_console(this);
577 void service_set::service_active(service_record *sr) noexcept
582 void service_set::service_inactive(service_record *sr) noexcept