16 #include "dinit-log.h"
17 #include "dinit-socket.h"
18 #include "dinit-util.h"
21 * service.cc - Service management.
22 * See service.h for details.
25 // Find the requested service by name
26 static service_record * find_service(const std::list<service_record *> & records,
27 const char *name) noexcept
30 list<service_record *>::const_iterator i = records.begin();
31 for ( ; i != records.end(); i++ ) {
32 if (strcmp((*i)->get_name().c_str(), name) == 0) {
39 service_record * service_set::find_service(const std::string &name) noexcept
41 return ::find_service(records, name.c_str());
44 void service_set::stop_service(const std::string & name) noexcept
46 service_record *record = find_service(name);
47 if (record != nullptr) {
53 // Called when a service has actually stopped; dependents have stopped already, unless this stop
54 // is due to an unexpected process termination.
55 void service_record::stopped() noexcept
57 if (onstart_flags.runs_on_console) {
58 tcsetpgrp(0, getpgrp());
59 discard_console_log_buffer();
65 // If we are a soft dependency of another target, break the acquisition from that target now:
66 for (auto & dependent : dependents) {
67 if (dependent->dep_type != dependency_type::REGULAR) {
68 if (dependent->holding_acq) {
69 dependent->holding_acq = false;
75 bool will_restart = (desired_state == service_state_t::STARTED)
76 && services->get_auto_restart();
78 for (auto dependency : depends_on) {
79 // we signal dependencies in case they are waiting for us to stop:
80 dependency.get_to()->dependent_stopped();
83 service_state = service_state_t::STOPPED;
86 // Desired state is "started".
94 start_explicit = false;
97 else if (required_by == 0) {
98 services->service_inactive(this);
102 log_service_stopped(service_name);
103 notify_listeners(service_event_t::STOPPED);
106 bool service_record::do_auto_restart() noexcept
109 return services->get_auto_restart();
114 void service_record::require() noexcept
116 if (required_by++ == 0) {
117 prop_require = !prop_release;
118 prop_release = false;
119 services->add_prop_queue(this);
123 void service_record::release(bool issue_stop) noexcept
125 if (--required_by == 0) {
126 desired_state = service_state_t::STOPPED;
128 // Can stop, and can release dependencies now. We don't need to issue a release if
129 // the require was pending though:
130 prop_release = !prop_require;
131 prop_require = false;
132 services->add_prop_queue(this);
134 if (service_state == service_state_t::STOPPED) {
135 services->service_inactive(this);
137 else if (issue_stop) {
143 void service_record::release_dependencies() noexcept
145 for (auto & dependency : depends_on) {
146 service_record * dep_to = dependency.get_to();
147 if (dependency.holding_acq) {
149 dependency.holding_acq = false;
154 void service_record::start(bool activate) noexcept
156 if (activate && ! start_explicit) {
158 start_explicit = true;
161 if (desired_state == service_state_t::STARTED && service_state != service_state_t::STOPPED) return;
163 bool was_active = service_state != service_state_t::STOPPED || desired_state != service_state_t::STOPPED;
164 desired_state = service_state_t::STARTED;
166 if (service_state != service_state_t::STOPPED) {
167 // We're already starting/started, or we are stopping and need to wait for
168 // that the complete.
169 if (service_state != service_state_t::STOPPING || ! can_interrupt_stop()) {
172 // We're STOPPING, and that can be interrupted. Our dependencies might be STOPPING,
173 // but if so they are waiting (for us), so they too can be instantly returned to
175 notify_listeners(service_event_t::STOPCANCELLED);
177 else if (! was_active) {
178 services->service_active(this);
181 service_state = service_state_t::STARTING;
182 waiting_for_deps = true;
184 if (start_check_dependencies()) {
185 services->add_transition_queue(this);
189 void service_record::do_propagation() noexcept
192 // Need to require all our dependencies
193 for (auto & dep : depends_on) {
194 dep.get_to()->require();
195 dep.holding_acq = true;
197 prop_require = false;
201 release_dependencies();
202 prop_release = false;
206 prop_failure = false;
207 failed_to_start(true);
221 void service_record::execute_transition() noexcept
223 // state is STARTED with restarting set true if we are running a smooth recovery.
224 if (service_state == service_state_t::STARTING || (service_state == service_state_t::STARTED
226 if (check_deps_started()) {
230 else if (service_state == service_state_t::STOPPING) {
231 if (stop_check_dependents()) {
232 waiting_for_deps = false;
238 void service_record::do_start() noexcept
240 if (pinned_stopped) return;
242 if (service_state != service_state_t::STARTING) {
246 service_state = service_state_t::STARTING;
248 waiting_for_deps = true;
250 // Ask dependencies to start, mark them as being waited on.
251 if (check_deps_started()) {
252 // Once all dependencies are started, we start properly:
257 void service_record::dependency_started() noexcept
259 if ((service_state == service_state_t::STARTING || service_state == service_state_t::STARTED)
260 && waiting_for_deps) {
261 services->add_transition_queue(this);
265 bool service_record::start_check_dependencies() noexcept
267 bool all_deps_started = true;
269 for (auto & dep : depends_on) {
270 service_record * to = dep.get_to();
271 if (to->service_state != service_state_t::STARTED) {
272 if (to->service_state != service_state_t::STARTING) {
273 to->prop_start = true;
274 services->add_prop_queue(to);
276 dep.waiting_on = true;
277 all_deps_started = false;
281 return all_deps_started;
284 bool service_record::check_deps_started() noexcept
286 for (auto & dep : depends_on) {
287 if (dep.waiting_on) {
295 void service_record::all_deps_started() noexcept
297 if (onstart_flags.starts_on_console && ! have_console) {
302 waiting_for_deps = false;
304 if (! can_proceed_to_start()) {
305 waiting_for_deps = true;
309 bool start_success = bring_up();
310 if (! start_success) {
315 void service_record::acquired_console() noexcept
317 waiting_for_console = false;
320 if (service_state != service_state_t::STARTING) {
321 // We got the console but no longer want it.
324 else if (check_deps_started()) {
328 // We got the console but can't use it yet.
334 void service_record::started() noexcept
336 // If we start on console but don't keep it, release it now:
337 if (have_console && ! onstart_flags.runs_on_console) {
338 tcsetpgrp(0, getpgrp());
342 log_service_started(get_name());
343 service_state = service_state_t::STARTED;
344 notify_listeners(service_event_t::STARTED);
346 if (onstart_flags.rw_ready) {
347 open_control_socket();
349 if (onstart_flags.log_ready) {
350 setup_external_log();
353 if (force_stop || desired_state == service_state_t::STOPPED) {
359 // Notify any dependents whose desired state is STARTED:
360 for (auto dept : dependents) {
361 dept->get_from()->dependency_started();
362 dept->waiting_on = false;
366 void service_record::failed_to_start(bool depfailed) noexcept
369 tcsetpgrp(0, getpgrp());
372 if (waiting_for_console) {
373 services->unqueue_console(this);
374 waiting_for_console = false;
377 log_service_failed(get_name());
378 service_state = service_state_t::STOPPED;
379 if (start_explicit) {
380 start_explicit = false;
383 notify_listeners(service_event_t::FAILEDSTART);
385 // Cancel start of dependents:
386 for (auto & dept : dependents) {
387 switch (dept->dep_type) {
388 case dependency_type::REGULAR:
389 case dependency_type::MILESTONE:
390 if (dept->get_from()->service_state == service_state_t::STARTING) {
391 dept->get_from()->prop_failure = true;
392 services->add_prop_queue(dept->get_from());
395 case dependency_type::WAITS_FOR:
396 case dependency_type::SOFT:
397 if (dept->waiting_on) {
398 dept->waiting_on = false;
399 dept->get_from()->dependency_started();
401 if (dept->holding_acq) {
402 dept->holding_acq = false;
409 bool service_record::bring_up() noexcept
411 // default implementation: there is no process, so we are started.
416 // Mark this and all dependent services as force-stopped.
417 void service_record::forced_stop() noexcept
419 if (service_state != service_state_t::STOPPED) {
421 services->add_transition_queue(this);
425 void service_record::dependent_stopped() noexcept
427 if (service_state == service_state_t::STOPPING && waiting_for_deps) {
428 services->add_transition_queue(this);
432 void service_record::stop(bool bring_down) noexcept
434 if (start_explicit) {
435 start_explicit = false;
444 void service_record::do_stop() noexcept
446 if (pinned_started) return;
448 if (start_explicit && ! do_auto_restart()) {
449 start_explicit = false;
453 bool all_deps_stopped = stop_dependents();
455 if (service_state != service_state_t::STARTED) {
456 if (service_state == service_state_t::STARTING) {
457 // If waiting for a dependency, or waiting for the console, we can interrupt start. Otherwise,
458 // we need to delegate to can_interrupt_start() (which can be overridden).
459 if (! waiting_for_deps && ! waiting_for_console) {
460 if (! can_interrupt_start()) {
461 // Well this is awkward: we're going to have to continue starting. We can stop once we've
462 // reached the started state.
466 if (! interrupt_start()) {
467 // Now wait for service startup to actually end; we don't need to handle it here.
471 else if (waiting_for_console) {
472 services->unqueue_console(this);
473 waiting_for_console = false;
476 // We must have had desired_state == STARTED.
477 notify_listeners(service_event_t::STARTCANCELLED);
479 // Reaching this point, we are starting interruptibly - so we
480 // stop now (by falling through to below).
483 // If we're starting we need to wait for that to complete.
484 // If we're already stopping/stopped there's nothing to do.
489 service_state = service_state_t::STOPPING;
490 waiting_for_deps = true;
491 if (all_deps_stopped) {
492 services->add_transition_queue(this);
496 bool service_record::stop_check_dependents() noexcept
498 bool all_deps_stopped = true;
499 for (auto dept : dependents) {
500 if (dept->dep_type == dependency_type::REGULAR && ! dept->get_from()->is_stopped()) {
501 all_deps_stopped = false;
506 return all_deps_stopped;
509 bool service_record::stop_dependents() noexcept
511 bool all_deps_stopped = true;
512 for (auto dept : dependents) {
513 if (dept->dep_type == dependency_type::REGULAR) {
514 if (! dept->get_from()->is_stopped()) {
515 // Note we check *first* since if the dependent service is not stopped,
516 // 1. We will issue a stop to it shortly and
517 // 2. It will notify us when stopped, at which point the stop_check_dependents()
518 // check is run anyway.
519 all_deps_stopped = false;
523 // If this service is to be forcefully stopped, dependents must also be.
524 dept->get_from()->forced_stop();
527 dept->get_from()->prop_stop = true;
528 services->add_prop_queue(dept->get_from());
532 return all_deps_stopped;
535 // All dependents have stopped; we can stop now, too. Only called when STOPPING.
536 void service_record::bring_down() noexcept
538 waiting_for_deps = false;
542 void service_record::unpin() noexcept
544 if (pinned_started) {
545 pinned_started = false;
546 if (desired_state == service_state_t::STOPPED || force_stop) {
548 services->process_queues();
551 if (pinned_stopped) {
552 pinned_stopped = false;
553 if (desired_state == service_state_t::STARTED) {
555 services->process_queues();
560 void service_record::queue_for_console() noexcept
562 waiting_for_console = true;
563 services->append_console_queue(this);
566 void service_record::release_console() noexcept
568 have_console = false;
569 services->pull_console_queue();
572 bool service_record::interrupt_start() noexcept
574 services->unqueue_console(this);
578 void service_set::service_active(service_record *sr) noexcept
583 void service_set::service_inactive(service_record *sr) noexcept