123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889 |
- #include <cstring>
- #include <cerrno>
- #include <iterator>
- #include <memory>
- #include <cstddef>
- #include <sys/ioctl.h>
- #include <fcntl.h>
- #include <termios.h>
- #include "dinit.h"
- #include "service.h"
- #include "dinit-log.h"
- #include "dinit-socket.h"
- #include "dinit-util.h"
- #include "baseproc-sys.h"
- /*
- * service.cc - Service management.
- * See service.h for details.
- */
- // Find the requested service by name.
- static service_record * find_service(const std::list<service_record *> & records,
- const char *name) noexcept
- {
- using std::list;
- list<service_record *>::const_iterator i = records.begin();
- for ( ; i != records.end(); ++i ) {
- if (strcmp((*i)->get_name().c_str(), name) == 0) {
- return *i;
- }
- }
- return nullptr;
- }
- service_record * service_set::find_service(const std::string &name, bool find_placeholders) noexcept
- {
- service_record *r = ::find_service(records, name.c_str());
- if (r != nullptr && !find_placeholders && r->get_type() == service_type_t::PLACEHOLDER) {
- return nullptr;
- }
- return r;
- }
- void service_record::prepare_for_unload() noexcept
- {
- // Remove all dependencies:
- for (auto &dep : depends_on) {
- service_record *dependency = dep.get_to();
- auto &dep_dpts = dependency->dependents;
- dep_dpts.erase(std::find(dep_dpts.begin(), dep_dpts.end(), &dep));
- if (dep.dep_type == dependency_type::AFTER) {
- if (dependency->get_type() == service_type_t::PLACEHOLDER) {
- if (dependency->is_unrefd()) {
- services->remove_service(dependency);
- delete dependency;
- }
- }
- }
- }
- depends_on.clear();
- // Also remove all dependents. This should not be necessary except for "before" links.
- for (auto i = dependents.begin(); i != dependents.end();) {
- service_record *before_svc = (*i)->get_from();
- before_svc->rm_dep(**i++);
- if (before_svc->get_type() == service_type_t::PLACEHOLDER && before_svc->is_unrefd()) {
- services->remove_service(before_svc);
- delete before_svc;
- }
- }
- }
- // 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());
- release_console();
- }
- force_stop = false;
- // If we are to re-start, restarting should have been set true and desired_state should be STARTED.
- // (A restart could be cancelled via a separately issued stop, including via a shutdown).
- bool will_restart = desired_state == service_state_t::STARTED && !pinned_stopped;
- // 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);
- }
- }
- }
- }
- for (auto & dependency : depends_on) {
- // we signal dependencies in case they are waiting for us to stop:
- dependency.get_to()->dependent_stopped();
- }
- service_state = service_state_t::STOPPED;
- if (will_restart) {
- // Desired state is "started".
- initiate_start();
- }
- else {
- becoming_inactive();
- if (start_explicit) {
- // If we were explicitly started, our required_by count must be at least 1. Use
- // release() to correctly release, mark inactive and release dependencies.
- start_explicit = false;
- release(false);
- }
- else if (required_by == 0) {
- // This can only be the case if we didn't have start_explicit, since required_by would
- // otherwise by non-zero. Since our release(s) above were with state != STOPPED, we now
- // must mark inactive (i.e. it won't have been done as part of the release).
- services->service_inactive(this);
- }
- }
- // Start failure will have been logged already, only log if we are stopped for other reasons:
- if (! start_failed) {
- log_service_stopped(service_name);
- // If this service chains to another, start the chained service now, if:
- // - this service self-terminated (rather than being stopped),
- // - ... successfully (i.e. exit code 0)
- // - this service won't restart, and
- // - a shutdown isn't in progress
- if ((onstart_flags.always_chain || (did_finish(stop_reason) && get_exit_status() == 0 && ! will_restart))
- && ! start_on_completion.empty() && ! services->is_shutting_down()) {
- 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");
- }
- }
- }
- notify_listeners(service_event_t::STOPPED);
- }
- void service_record::require() noexcept
- {
- if (required_by++ == 0) {
- if (service_state != service_state_t::STARTING && service_state != service_state_t::STARTED) {
- prop_start = true;
- services->add_prop_queue(this);
- // Note: pin is checked in start().
- // Require will be propagated to dependencies if/when the service actually starts.
- }
- }
- }
- void service_record::release(bool issue_stop) noexcept
- {
- if (--required_by == 0) {
- if (service_state == service_state_t::STOPPING) {
- // If we are stopping but would have restarted, we now need to notify that the restart
- // has been cancelled. Other start-cancelled cases are handled by do_stop() (called
- // below).
- if (desired_state == service_state_t::STARTED && !is_start_pinned()) {
- notify_listeners(service_event_t::STARTCANCELLED);
- }
- }
- desired_state = service_state_t::STOPPED;
- if (is_start_pinned()) return;
- // Can stop, and can release dependencies now. We don't need to issue a release if
- // a require was pending though:
- prop_release = !prop_require;
- prop_require = false;
- if (prop_release) {
- services->add_prop_queue(this);
- }
- if (service_state != service_state_t::STOPPED && service_state != service_state_t::STOPPING
- && issue_stop) {
- stop_reason = stopped_reason_t::NORMAL;
- do_stop();
- }
- }
- }
- void service_record::release_dependencies() noexcept
- {
- for (auto & dependency : depends_on) {
- service_record * dep_to = dependency.get_to();
- if (dependency.holding_acq) {
- // We must clear holding_acq before calling release, otherwise the dependency
- // may decide to stop, check this link and release itself a second time.
- dependency.holding_acq = false;
- dep_to->release();
- }
- }
- }
- void service_record::start() noexcept
- {
- if (pinned_stopped) {
- // bail out early for this case, we don't want to set start_explicit
- return;
- }
- if (!start_explicit) {
- ++required_by;
- start_explicit = true;
- }
- do_start();
- }
- void service_record::initiate_start() noexcept
- {
- start_failed = false;
- start_skipped = false;
- service_state = service_state_t::STARTING;
- waiting_for_deps = true;
- if (start_check_dependencies()) {
- // Note: we can't set waiting_for_deps false here since ordering dependencies might still kick in
- services->add_transition_queue(this);
- }
- }
- void service_record::do_propagation() noexcept
- {
- if (prop_require) {
- // Need to require all our dependencies
- for (auto & dep : depends_on) {
- if (!dep.is_only_ordering()) {
- dep.get_to()->require();
- dep.holding_acq = true;
- }
- }
- prop_require = false;
- }
- if (prop_release) {
- release_dependencies();
- prop_release = false;
- }
- if (prop_failure) {
- prop_failure = false;
- stop_reason = stopped_reason_t::DEPFAILED;
- service_state = service_state_t::STOPPED;
- failed_to_start(true);
- }
- if (prop_start) {
- prop_start = false;
- do_start();
- }
- if (prop_stop) {
- prop_stop = false;
- do_stop(in_user_restart);
- }
- if (prop_pin_dpt) {
- bool dept_pin = false;
- for (auto *dept : dependents) {
- if (dept->is_hard() && dept->get_from()->is_start_pinned()) {
- dept_pin = true;
- break;
- }
- }
- if (dept_pin != dept_pinned_started) {
- dept_pinned_started = dept_pin;
- for (auto &dep : depends_on) {
- if (dep.is_hard() && dep.get_to()->dept_pinned_started != dept_pin) {
- dep.get_to()->prop_pin_dpt = true;
- services->add_prop_queue(dep.get_to());
- }
- }
- if (!dept_pinned_started && !pinned_started) {
- // No longer pinned at all
- if ((desired_state == service_state_t::STOPPED || force_stop)
- && service_state == service_state_t::STARTED) {
- do_stop();
- }
- }
- }
- }
- }
- void service_record::execute_transition() noexcept
- {
- if (service_state == service_state_t::STARTING) {
- if (check_deps_started()) {
- waiting_for_deps = false;
- all_deps_started();
- }
- }
- else if (service_state == service_state_t::STOPPING) {
- if (stop_check_dependents()) {
- waiting_for_deps = false;
- if (onstart_flags.kill_all_on_stop) {
- log(loglevel_t::NOTICE, true, "Sending TERM/KILL to all processes...\n");
- kill(-1, SIGTERM);
- sleep(1);
- kill(-1, SIGKILL);
- }
- bring_down();
- }
- }
- }
- void service_record::do_start() noexcept
- {
- bool was_active = service_state != service_state_t::STOPPED;
- desired_state = service_state_t::STARTED;
- if (pinned_stopped) {
- if (!was_active) {
- failed_to_start(false, false);
- }
- return;
- }
- // re-attach any soft dependents, now that we are starting again
- if (!was_active) {
- for (auto dept : dependents) {
- if (!dept->is_hard()) {
- service_state_t dept_state = dept->get_from()->service_state;
- if (!dept->holding_acq
- && (dept_state == service_state_t::STARTED || dept_state == service_state_t::STARTING)) {
- dept->holding_acq = true;
- ++required_by;
- }
- }
- }
- }
- if (was_active) {
- // We're already starting/started, or we are stopping and need to wait for
- // that the complete.
- if (service_state != service_state_t::STOPPING) {
- return;
- }
- if (! can_interrupt_stop()) {
- 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.
- notify_listeners(service_event_t::STOPCANCELLED);
- }
- else { // !was_active
- services->service_active(this);
- prop_require = !prop_release;
- prop_release = false;
- if (prop_require) {
- services->add_prop_queue(this);
- }
- }
- initiate_start();
- }
- void service_record::dependency_started() noexcept
- {
- // Note that we check for STARTED state here in case the service is in smooth recovery while pinned.
- // In that case it will wait for dependencies to start before restarting the process.
- if ((service_state == service_state_t::STARTING || service_state == service_state_t::STARTED)
- && waiting_for_deps) {
- services->add_transition_queue(this);
- }
- }
- bool service_record::start_check_dependencies() noexcept
- {
- bool all_deps_started = true;
- for (auto & dep : depends_on) {
- service_record * to = dep.get_to();
- if (dep.is_only_ordering()
- && to->service_state != service_state_t::STARTING) continue;
- if (to->service_state != service_state_t::STARTED) {
- dep.waiting_on = true;
- all_deps_started = false;
- }
- }
- for (auto * dept : dependents) {
- if (!dept->waiting_on && dept->is_only_ordering()) {
- service_record *from = dept->get_from();
- if (from->get_state() == service_state_t::STARTING) {
- dept->waiting_on = true;
- }
- }
- }
- return all_deps_started;
- }
- bool service_record::check_deps_started() noexcept
- {
- for (auto & dep : depends_on) {
- if (dep.waiting_on) {
- return false;
- }
- }
- return true;
- }
- void service_record::all_deps_started() noexcept
- {
- if (onstart_flags.starts_on_console && !have_console) {
- queue_for_console();
- return;
- }
- waiting_for_deps = false;
- if (!bring_up()) {
- service_state = service_state_t::STOPPING;
- failed_to_start();
- }
- }
- void service_record::acquired_console() noexcept
- {
- waiting_for_console = false;
- have_console = true;
- if (service_state != service_state_t::STARTING) {
- // We got the console but no longer want it.
- release_console();
- }
- else if (check_deps_started()) {
- all_deps_started();
- }
- else {
- // We got the console but can't use it yet.
- release_console();
- }
- }
- void service_record::started() noexcept
- {
- // If we start on console but don't keep it, release it now:
- if (have_console && !onstart_flags.runs_on_console) {
- bp_sys::tcsetpgrp(0, bp_sys::getpgrp());
- release_console();
- }
- log_service_started(get_name());
- service_state = service_state_t::STARTED;
- notify_listeners(service_event_t::STARTED);
- if (onstart_flags.rw_ready) {
- rootfs_is_rw();
- }
- if (onstart_flags.log_ready) {
- setup_external_log();
- }
- if (force_stop || desired_state == service_state_t::STOPPED) {
- // We must now stop.
- do_stop();
- return;
- }
- // Notify any dependents whose desired state is STARTED:
- for (auto dept : dependents) {
- if (dept->waiting_on) {
- dept->get_from()->dependency_started();
- dept->waiting_on = false;
- }
- }
- }
- void service_record::failed_to_start(bool depfailed, bool immediate_stop) noexcept
- {
- desired_state = service_state_t::STOPPED;
- if (waiting_for_console) {
- services->unqueue_console(this);
- waiting_for_console = false;
- }
- if (start_explicit) {
- start_explicit = false;
- release(false);
- }
- // Cancel start of dependents:
- for (auto & dept : dependents) {
- switch (dept->dep_type) {
- case dependency_type::REGULAR:
- case dependency_type::MILESTONE:
- // If REGULAR and STARTED, we can't have failed to start i.e. we must be started, so
- // we don't worry about that case. If MILESTONE and started the dependency is already
- // satisfied so again we don't need to do anything.
- if (dept->get_from()->service_state == service_state_t::STARTING) {
- dept->get_from()->prop_failure = true;
- services->add_prop_queue(dept->get_from());
- }
- break;
- case dependency_type::WAITS_FOR:
- case dependency_type::SOFT:
- case dependency_type::BEFORE:
- case dependency_type::AFTER:
- if (dept->waiting_on) {
- dept->waiting_on = false;
- dept->get_from()->dependency_started();
- }
- }
- // Always release now, so that our desired state will be STOPPED before we call
- // stopped() below (if we do so). Otherwise it may decide to restart us.
- if (dept->holding_acq) {
- dept->holding_acq = false;
- release(false);
- }
- }
- start_failed = true;
- log_service_failed(get_name());
- notify_listeners(service_event_t::FAILEDSTART);
- pinned_started = false;
- if (immediate_stop) {
- stopped();
- }
- }
- void service_record::unrecoverable_stop() noexcept
- {
- desired_state = service_state_t::STOPPED;
- forced_stop();
- }
- bool service_record::bring_up() noexcept
- {
- // default implementation: there is no process, so we are started.
- started();
- return true;
- }
- // Mark this and all dependent services to be force-stopped.
- void service_record::forced_stop() noexcept
- {
- if (service_state != service_state_t::STOPPED) {
- force_stop = true;
- if (!is_start_pinned()) {
- prop_stop = true;
- services->add_prop_queue(this);
- }
- }
- }
- void service_record::dependent_stopped() noexcept
- {
- if (service_state == service_state_t::STOPPING && waiting_for_deps) {
- services->add_transition_queue(this);
- }
- }
- void service_record::stop(bool bring_down) noexcept
- {
- // Stop; remove activation, and don't self-restart.
- if (start_explicit) {
- start_explicit = false;
- required_by--;
- }
- if (bring_down || required_by == 0) {
- // Set desired state to STOPPED, this will inhibit automatic restart (and will be
- // propagated to dependents)
- desired_state = service_state_t::STOPPED;
- }
- if (is_start_pinned()) {
- return;
- }
- // If our required_by count is 0, we should treat this as a full manual stop regardless
- if (required_by == 0) {
- bring_down = true;
- prop_release = !prop_require;
- if (prop_release) {
- services->add_prop_queue(this);
- }
- }
- if (bring_down && service_state != service_state_t::STOPPED) {
- // Note even if we are already STOPPING we should call do_stop for the case where we need
- // to interrupt any currently ongoing restart of dependents.
- stop_reason = stopped_reason_t::NORMAL;
- do_stop();
- }
- }
- bool service_record::restart() noexcept
- {
- // Re-start without affecting dependency links/activation. Hard dependents will be similarly
- // restarted.
- if (service_state == service_state_t::STARTED) {
- stop_reason = stopped_reason_t::NORMAL;
- force_stop = true;
- do_stop(true);
- return true;
- }
- // Wrong state
- return false;
- }
- void service_record::do_stop(bool with_restart) 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.
- // Note: to inhibit automatic restart, including restart due to dependent still requiring this
- // service, caller must first set desired_state to STOPPED
- if (is_start_pinned()) return;
- in_auto_restart = false;
- in_user_restart = false;
- // Will we restart? desired state of STOPPED inhibits auto-restart
- bool for_restart = with_restart || (auto_restart && desired_state == service_state_t::STARTED);
- bool restart_deps = with_restart;
- if (!with_restart && for_restart) {
- // auto restart - check for restarting too often
- for_restart = check_restart();
- in_auto_restart = for_restart;
- }
- // If we won't restart, release explicit activation:
- if (!for_restart) {
- if (start_explicit) {
- start_explicit = false;
- release(false);
- }
- }
- bool all_deps_stopped = stop_dependents(for_restart, restart_deps);
- if (service_state != service_state_t::STARTED) {
- if (service_state == service_state_t::STARTING) {
- // If waiting for a dependency, or waiting for the console, we can interrupt start. Otherwise,
- // 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.
- return;
- }
- if (!interrupt_start()) {
- // Now wait for service startup to actually end; we don't need to handle it here.
- notify_listeners(service_event_t::STARTCANCELLED);
- return;
- }
- }
- else if (waiting_for_console) {
- services->unqueue_console(this);
- waiting_for_console = false;
- }
- // We must have had desired_state == STARTED.
- notify_listeners(service_event_t::STARTCANCELLED);
- // Reaching this point, we are starting interruptibly - so we
- // stop now (by falling through to below).
- }
- else {
- // If we're starting we need to wait for that to complete.
- // If we're already stopping/stopped there's nothing to do.
- return;
- }
- }
- service_state = service_state_t::STOPPING;
- waiting_for_deps = !all_deps_stopped;
- if (all_deps_stopped) {
- services->add_transition_queue(this);
- }
- }
- bool service_record::stop_check_dependents() noexcept
- {
- bool all_deps_stopped = true;
- for (auto dept : dependents) {
- // Note if the dependent is waiting on us, it must be restarting (since the
- // waiting_on flag gets cleared when we stop, and would only be set if the
- // service tries to restart). We can treat that as "stopped" for purposes of
- // checking whether we can transition to stopped state.
- if (dept->is_hard() && dept->holding_acq && !dept->waiting_on) {
- all_deps_stopped = false;
- break;
- }
- }
- return all_deps_stopped;
- }
- bool service_record::stop_dependents(bool for_restart, bool restart_deps) noexcept
- {
- // We are in either STARTED or STARTING states.
- bool all_deps_stopped = true;
- for (auto dept : dependents) {
- if (dept->is_hard()) {
- service_record *dep_from = dept->get_from();
- if (!dep_from->is_fundamentally_stopped()) {
- // Note we check *first* since if the dependent service is not stopped,
- // 1. We will issue a stop to it shortly and
- // 2. It will notify us when stopped, at which point the stop_check_dependents()
- // check is run anyway.
- all_deps_stopped = false;
- }
- if (force_stop) {
- // If this service is to be forcefully stopped, dependents must also be.
- dep_from->forced_stop();
- }
- if (dep_from->get_state() != service_state_t::STOPPED) {
- if (desired_state == service_state_t::STOPPED) {
- if (dep_from->desired_state != service_state_t::STOPPED) {
- // if we don't want to restart, don't restart dependent
- dep_from->desired_state = service_state_t::STOPPED;
- if (dep_from->start_explicit) {
- dep_from->start_explicit = false;
- dep_from->release(true);
- }
- dep_from->prop_stop = true;
- services->add_prop_queue(dep_from);
- }
- }
- else if (restart_deps && dep_from->get_state() != service_state_t::STOPPING) {
- // restart dependent and propagate restart to all their hard dependents.
- dep_from->stop_reason = stopped_reason_t::DEPRESTART;
- dep_from->in_user_restart = true;
- dep_from->prop_stop = true;
- services->add_prop_queue(dep_from);
- }
- }
- }
- // Note that soft dependencies are retained if restarting, but otherwise
- // they are broken.
- else if (!for_restart) {
- if (dept->waiting_on) {
- // Note, milestone which is still waiting is considered a hard dependency and
- // is handled above. This is therefore a true soft dependency, and we can just
- // break the dependency link.
- dept->waiting_on = false;
- dept->get_from()->dependency_started();
- }
- if (dept->holding_acq) {
- dept->holding_acq = false;
- release(false);
- }
- }
- }
- return all_deps_stopped;
- }
- // All dependents have stopped; we can stop now, too. Only called when STOPPING.
- void service_record::bring_down() noexcept
- {
- stopped();
- }
- void service_record::pin_start() noexcept
- {
- if (!pinned_started) {
- if (!dept_pinned_started) {
- for (auto &dep : depends_on) {
- if (dep.is_hard() && !dep.get_to()->dept_pinned_started) {
- dep.get_to()->prop_pin_dpt = true;
- services->add_prop_queue(dep.get_to());
- }
- }
- }
- pinned_started = true;
- }
- }
- void service_record::unpin() noexcept
- {
- if (pinned_started) {
- pinned_started = false;
- if (dept_pinned_started) return;
- // unpin dependencies
- for (auto &dep : depends_on) {
- if (dep.is_hard() && dep.get_to()->dept_pinned_started) {
- dep.get_to()->prop_pin_dpt = true;
- services->add_prop_queue(dep.get_to());
- }
- }
- // We only need special handling here if service was in STARTED state
- if (service_state == service_state_t::STARTED) {
- // If any dependents are stopping, then force_stop should already be set.
- // If we reached required_by 0, we need to propagate release now (since it wasn't
- // propagated as it normally would be when we hit 0, due to the pin)
- if (required_by == 0) {
- prop_release = true;
- services->add_prop_queue(this);
- }
- if (desired_state == service_state_t::STOPPED || force_stop) {
- do_stop();
- services->process_queues();
- }
- }
- }
- if (pinned_stopped) {
- pinned_stopped = false;
- // We don't need to check state. If we're pinned stopped we can't be required and so desired
- // state should always be stopped.
- }
- }
- void service_record::queue_for_console() noexcept
- {
- waiting_for_console = true;
- services->append_console_queue(this);
- }
- void service_record::release_console() noexcept
- {
- have_console = false;
- services->pull_console_queue();
- }
- bool service_record::interrupt_start() noexcept
- {
- return true;
- }
- void service_set::service_active(service_record *sr) noexcept
- {
- active_services++;
- }
- void service_set::service_inactive(service_record *sr) noexcept
- {
- active_services--;
- }
- bool triggered_service::bring_up() noexcept
- {
- if (is_triggered) {
- started();
- }
- return true;
- }
|