X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fservice.cc;h=18987c669ba69cc5e811bc55930f91d3cff1a7e0;hb=d8b9f2e18aa0b6cf81032353efc5e14ba1e44c9b;hp=7c87cd0a25063ec5ea9b05c27779aabf047b29af;hpb=70f9cd56023ccf850f92024b7bb5852dc191d3f2;p=oweals%2Fdinit.git diff --git a/src/service.cc b/src/service.cc index 7c87cd0..18987c6 100644 --- a/src/service.cc +++ b/src/service.cc @@ -26,7 +26,7 @@ static service_record * find_service(const std::list & records { using std::list; list::const_iterator i = records.begin(); - for ( ; i != records.end(); i++ ) { + for ( ; i != records.end(); ++i ) { if (strcmp((*i)->get_name().c_str(), name) == 0) { return *i; } @@ -39,41 +39,44 @@ service_record * service_set::find_service(const std::string &name) noexcept return ::find_service(records, name.c_str()); } -void service_set::stop_service(const std::string & name) noexcept -{ - service_record *record = find_service(name); - if (record != nullptr) { - record->stop(); - process_queues(); - } -} - // Called when a service has actually stopped; dependents have stopped already, unless this stop // is due to an unexpected process termination. void service_record::stopped() noexcept { if (have_console) { bp_sys::tcsetpgrp(0, bp_sys::getpgrp()); - discard_console_log_buffer(); release_console(); } force_stop = false; - // If we are a soft dependency of another target, break the acquisition from that target now: - for (auto & dependent : dependents) { - if (dependent->dep_type != dependency_type::REGULAR) { - if (dependent->holding_acq) { - dependent->holding_acq = false; - release(); + restarting |= auto_restart; + bool will_restart = restarting && required_by > 0; + if (restarting && ! will_restart) { + notify_listeners(service_event_t::STARTCANCELLED); + } + restarting = false; + + // If we won't restart, break soft dependencies now + if (! will_restart) { + for (auto dept : dependents) { + if (! dept->is_hard()) { + // waits-for or soft dependency: + if (dept->waiting_on) { + dept->waiting_on = false; + dept->get_from()->dependency_started(); + } + if (dept->holding_acq) { + dept->holding_acq = false; + // release without issuing stop, since we're called only when this + // service is already stopped/stopping: + release(false); + } } } } - bool will_restart = (desired_state == service_state_t::STARTED) - && services->get_auto_restart(); - - for (auto dependency : depends_on) { + for (auto & dependency : depends_on) { // we signal dependencies in case they are waiting for us to stop: dependency.get_to()->dependent_stopped(); } @@ -104,16 +107,27 @@ void service_record::stopped() noexcept } } - log_service_stopped(service_name); - notify_listeners(service_event_t::STOPPED); -} + // Start failure will have been logged already, only log if we are stopped for other reasons: + if (! start_failed) { + log_service_stopped(service_name); -bool service_record::do_auto_restart() noexcept -{ - if (auto_restart) { - return services->get_auto_restart(); + // If this service chains to another, start the other service now: + if (! will_restart && ! start_on_completion.empty()) { + try { + auto chain_to = services->load_service(start_on_completion.c_str()); + chain_to->start(); + } + catch (service_load_exc &sle) { + log(loglevel_t::ERROR, "Couldn't chain to service ", start_on_completion, ": ", + "couldn't load ", sle.service_name, ": ", sle.exc_description); + } + catch (std::bad_alloc &bae) { + log(loglevel_t::ERROR, "Couldn't chain to service ", start_on_completion, + ": Out of memory"); + } + } } - return false; + notify_listeners(service_event_t::STOPPED); } void service_record::require() noexcept @@ -132,17 +146,21 @@ void service_record::release(bool issue_stop) noexcept { if (--required_by == 0) { desired_state = service_state_t::STOPPED; + prop_require = false; // Can stop, and can release dependencies now. We don't need to issue a release if // the require was pending though: - prop_release = !prop_require; - prop_require = false; - services->add_prop_queue(this); + if (service_state != service_state_t::STOPPED && service_state != service_state_t::STOPPING) { + prop_release = !prop_require; + prop_require = false; + services->add_prop_queue(this); + } if (service_state == service_state_t::STOPPED) { services->service_inactive(this); } else if (issue_stop) { + stop_reason = stopped_reason_t::NORMAL; do_stop(); } } @@ -167,8 +185,6 @@ void service_record::start(bool activate) noexcept require(); start_explicit = true; } - - if (desired_state == service_state_t::STARTED && service_state != service_state_t::STOPPED) return; bool was_active = service_state != service_state_t::STOPPED || desired_state != service_state_t::STOPPED; desired_state = service_state_t::STARTED; @@ -176,9 +192,15 @@ void service_record::start(bool activate) noexcept if (service_state != service_state_t::STOPPED) { // We're already starting/started, or we are stopping and need to wait for // that the complete. - if (service_state != service_state_t::STOPPING || ! can_interrupt_stop()) { + if (service_state != service_state_t::STOPPING) { + return; + } + + if (! can_interrupt_stop()) { + restarting = true; return; } + // We're STOPPING, and that can be interrupted. Our dependencies might be STOPPING, // but if so they are waiting (for us), so they too can be instantly returned to // STARTING state. @@ -188,6 +210,8 @@ void service_record::start(bool activate) noexcept services->service_active(this); } + start_failed = false; + start_skipped = false; service_state = service_state_t::STARTING; waiting_for_deps = true; @@ -214,6 +238,7 @@ void service_record::do_propagation() noexcept if (prop_failure) { prop_failure = false; + stop_reason = stopped_reason_t::DEPFAILED; failed_to_start(true); } @@ -240,6 +265,14 @@ void service_record::execute_transition() noexcept else if (service_state == service_state_t::STOPPING) { if (stop_check_dependents()) { waiting_for_deps = false; + + // A service that does actually stop for any reason should have its explicit activation released, unless + // it will restart: + if (start_explicit && !auto_restart && !restarting) { + start_explicit = false; + release(false); + } + bring_down(); } } @@ -319,6 +352,7 @@ void service_record::all_deps_started() noexcept } bool start_success = bring_up(); + restarting = false; if (! start_success) { failed_to_start(); } @@ -355,7 +389,7 @@ void service_record::started() noexcept notify_listeners(service_event_t::STARTED); if (onstart_flags.rw_ready) { - open_control_socket(); + rootfs_is_rw(); } if (onstart_flags.log_ready) { setup_external_log(); @@ -412,6 +446,7 @@ void service_record::failed_to_start(bool depfailed, bool immediate_stop) noexce } } + start_failed = true; log_service_failed(get_name()); notify_listeners(service_event_t::FAILEDSTART); @@ -434,7 +469,7 @@ void service_record::forced_stop() noexcept force_stop = true; if (! pinned_started) { prop_stop = true; - services->add_transition_queue(this); + services->add_prop_queue(this); } } } @@ -450,23 +485,43 @@ void service_record::stop(bool bring_down) noexcept { if (start_explicit) { start_explicit = false; - release(); + required_by--; + } + + // If our required_by count is 0, we should treat this as a full manual stop regardless + if (required_by == 0) { + bring_down = true; } - if (bring_down) { + desired_state = service_state_t::STOPPED; + + if (bring_down && service_state != service_state_t::STOPPED + && service_state != service_state_t::STOPPING) { + stop_reason = stopped_reason_t::NORMAL; do_stop(); } } -void service_record::do_stop() noexcept +bool service_record::restart() noexcept { - // A service that does actually stop for any reason should have its explicit activation released, unless - // it will restart: - if (start_explicit && ! do_auto_restart()) { - start_explicit = false; - release(false); + // Re-start without affecting dependency links/activation. + + if (service_state == service_state_t::STARTED) { + restarting = true; + stop_reason = stopped_reason_t::NORMAL; + do_stop(); + return true; } + // Wrong state + return false; +} + +void service_record::do_stop() noexcept +{ + // Called when we should definitely stop. We may need to restart afterwards, but we + // won't know that for sure until the execution transition. + bool all_deps_stopped = stop_dependents(); if (service_state != service_state_t::STARTED) { @@ -475,8 +530,8 @@ void service_record::do_stop() noexcept // we need to delegate to can_interrupt_start() (which can be overridden). if (! waiting_for_deps && ! waiting_for_console) { if (! can_interrupt_start()) { - // Well this is awkward: we're going to have to continue starting. We can stop once we've - // reached the started state. + // Well this is awkward: we're going to have to continue starting. We can stop once + // we've reached the started state. return; } @@ -517,12 +572,12 @@ bool service_record::stop_check_dependents() noexcept { bool all_deps_stopped = true; for (auto dept : dependents) { - if (dept->dep_type == dependency_type::REGULAR && ! dept->get_from()->is_stopped()) { + if (dept->is_hard() && dept->holding_acq) { all_deps_stopped = false; break; } } - + return all_deps_stopped; } @@ -530,9 +585,7 @@ bool service_record::stop_dependents() noexcept { bool all_deps_stopped = true; for (auto dept : dependents) { - if (dept->dep_type == dependency_type::REGULAR || - (dept->dep_type == dependency_type::MILESTONE && - dept->get_from()->service_state != service_state_t::STARTED)) { + if (dept->is_hard() && dept->holding_acq) { if (! dept->get_from()->is_stopped()) { // Note we check *first* since if the dependent service is not stopped, // 1. We will issue a stop to it shortly and @@ -549,17 +602,8 @@ bool service_record::stop_dependents() noexcept dept->get_from()->prop_stop = true; services->add_prop_queue(dept->get_from()); } - else { - // waits-for or soft dependency: - if (dept->waiting_on) { - dept->waiting_on = false; - dept->get_from()->dependency_started(); - } - if (dept->holding_acq) { - dept->holding_acq = false; - release(); - } - } + // Note that soft dependencies are held (for now). If we restart, we don't want those dependencies + // to be broken. } return all_deps_stopped; @@ -576,6 +620,19 @@ void service_record::unpin() noexcept { if (pinned_started) { pinned_started = false; + + for (auto &dep : depends_on) { + if (dep.is_hard()) { + if (dep.get_to()->get_state() != service_state_t::STARTED) { + desired_state = service_state_t::STOPPED; + } + } + else if (dep.holding_acq) { + dep.holding_acq = false; + dep.get_to()->release(); + } + } + if (desired_state == service_state_t::STOPPED || force_stop) { do_stop(); services->process_queues();