X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fservice.cc;h=18987c669ba69cc5e811bc55930f91d3cff1a7e0;hb=d8b9f2e18aa0b6cf81032353efc5e14ba1e44c9b;hp=504a9b6de0836710449785effbac4a3f04b97452;hpb=f1713b253dfcdb773d91d0138e297fb65217327d;p=oweals%2Fdinit.git diff --git a/src/service.cc b/src/service.cc index 504a9b6..18987c6 100644 --- a/src/service.cc +++ b/src/service.cc @@ -39,15 +39,6 @@ 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 @@ -59,20 +50,32 @@ void service_record::stopped() noexcept force_stop = false; - // If we are a soft dependency of another target, break the acquisition from that target now, - // so that we don't re-start: - for (auto & dependent : dependents) { - if (dependent->dep_type != dependency_type::REGULAR) { - if (dependent->holding_acq && ! dependent->waiting_on) { - 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->is_shutting_down(); - for (auto & dependency : depends_on) { // we signal dependencies in case they are waiting for us to stop: dependency.get_to()->dependent_stopped(); @@ -127,14 +130,6 @@ void service_record::stopped() noexcept notify_listeners(service_event_t::STOPPED); } -bool service_record::do_auto_restart() noexcept -{ - if (auto_restart) { - return !services->is_shutting_down(); - } - return false; -} - void service_record::require() noexcept { if (required_by++ == 0) { @@ -151,12 +146,15 @@ 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); @@ -187,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; @@ -196,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. @@ -263,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(); } } @@ -342,6 +352,7 @@ void service_record::all_deps_started() noexcept } bool start_success = bring_up(); + restarting = false; if (! start_success) { failed_to_start(); } @@ -474,9 +485,16 @@ 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; } + 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; @@ -484,15 +502,26 @@ void service_record::stop(bool bring_down) noexcept } } -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) { @@ -543,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; } @@ -556,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 @@ -575,19 +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 without issuing stop, since we should be called only when this - // service is already stopped/stopping: - release(false); - } - } + // Note that soft dependencies are held (for now). If we restart, we don't want those dependencies + // to be broken. } return all_deps_stopped; @@ -604,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();