From 46f30daea839b20c90c88c1e58f5a2b332f0c2ad Mon Sep 17 00:00:00 2001 From: Davin McCall Date: Sat, 20 Jul 2019 09:50:41 +0100 Subject: [PATCH] Implement careful/"gentle" service stop (abort if dependent would stop). --- src/control.cc | 46 ++++++++++++++++++++++++++++++++++++- src/includes/control-cmds.h | 3 +++ src/includes/control.h | 7 +++++- src/includes/service.h | 5 ++++ 4 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/control.cc b/src/control.cc index 8b355a8..a418a42 100644 --- a/src/control.cc +++ b/src/control.cc @@ -175,6 +175,35 @@ bool control_conn_t::process_find_load(int pktType) return true; } +bool control_conn_t::check_dependents(service_record *service, bool &had_dependents) +{ + std::vector reply_pkt; + + for (service_dep *dep : service->get_dependents()) { + if (dep->dep_type == dependency_type::REGULAR) { + // find or allocate a service handle + handle_t dept_handle = allocate_service_handle(dep->get_from()); + if (reply_pkt.empty()) { + // packet type, size + reply_pkt.reserve(1 + sizeof(size_t) + sizeof(handle_t)); + reply_pkt.resize(1 + sizeof(size_t)); + reply_pkt[0] = DINIT_RP_DEPENDENTS; + } + auto old_size = reply_pkt.size(); + reply_pkt.resize(old_size + sizeof(handle_t)); + memcpy(reply_pkt.data() + old_size, &dept_handle, sizeof(dept_handle)); + } + } + + had_dependents = !reply_pkt.empty(); + if (had_dependents) { + // We didn't build a reply packet, so there are no affected dependents + if (! queue_packet(std::move(reply_pkt))) return false; + } + + return true; +} + bool control_conn_t::process_start_stop(int pktType) { using std::string; @@ -190,7 +219,7 @@ bool control_conn_t::process_start_stop(int pktType) // 1 byte: pin in requested state (0 = no pin, 1 = pin) // 4 bytes: service handle - bool do_pin = (rbuf[1] == 1); + bool do_pin = ((rbuf[1] & 1) == 1); handle_t handle; rbuf.extract((char *) &handle, 2, sizeof(handle)); @@ -219,13 +248,27 @@ bool control_conn_t::process_start_stop(int pktType) if (service->get_state() == service_state_t::STARTED) ack_buf[0] = DINIT_RP_ALREADYSS; break; case DINIT_CP_STOPSERVICE: + { // force service to stop + bool gentle = ((rbuf[1] & 2) == 2); + if (gentle) { + // Check dependents; return appropriate response if any will be affected + bool has_dependents; + if (check_dependents(service, has_dependents)) { + return false; + } + if (has_dependents) { + // Reply packet has already been sent + goto clear_out; + } + } if (do_pin) service->pin_stop(); service->stop(true); service->forced_stop(); services->process_queues(); if (service->get_state() == service_state_t::STOPPED) ack_buf[0] = DINIT_RP_ALREADYSS; break; + } case DINIT_CP_WAKESERVICE: // re-start a stopped service (do not mark as required) if (services->is_shutting_down()) { @@ -249,6 +292,7 @@ bool control_conn_t::process_start_stop(int pktType) if (! queue_packet(ack_buf, 1)) return false; } + clear_out: // Clear the packet from the buffer rbuf.consume(pkt_size); chklen = 0; diff --git a/src/includes/control-cmds.h b/src/includes/control-cmds.h index ed5d844..3c734cb 100644 --- a/src/includes/control-cmds.h +++ b/src/includes/control-cmds.h @@ -78,6 +78,9 @@ constexpr static int DINIT_RP_LISTDONE = 63; // Service loader information: constexpr static int DINIT_RP_LOADER_MECH = 64; +// Dependent services prevent stopping/restarting. Includes size_t count, handle_t * N handles. +constexpr static int DINIT_RP_DEPENDENTS = 65; + // Information: // Service event occurred (4-byte service handle, 1 byte event code) diff --git a/src/includes/control.h b/src/includes/control.h index 62f54b0..05721ed 100644 --- a/src/includes/control.h +++ b/src/includes/control.h @@ -119,7 +119,7 @@ class control_conn_t : private service_listener // true (with bad_conn_close == false) if the packet was successfully // queued; // true (with bad_conn_close == true) if the packet was not successfully - // queued (but a suitable error packate has been queued). + // queued (but a suitable error packet has been queued). // The in/out watch enabled state will also be set appropriately. bool queue_packet(vector &&v) noexcept; bool queue_packet(const char *pkt, unsigned size) noexcept; @@ -163,6 +163,11 @@ class control_conn_t : private service_listener bool send_data() noexcept; + // Check if any dependents will be affected by stopping a service, generate a response packet if so. + // had_dependents will be set true if the service should not be stopped, false otherwise. + // Returns false if the connection must be closed, true otherwise. + bool check_dependents(service_record *service, bool &had_dependents); + // Allocate a new handle for a service; may throw std::bad_alloc handle_t allocate_service_handle(service_record *record); diff --git a/src/includes/service.h b/src/includes/service.h index 1acec08..487d976 100644 --- a/src/includes/service.h +++ b/src/includes/service.h @@ -606,6 +606,11 @@ class service_record return depends_on; } + dpt_list & get_dependents() + { + return dependents; + } + // Add a dependency. Caller must ensure that the services are in an appropriate state and that // a circular dependency chain is not created. Propagation queues should be processed after // calling this. May throw std::bad_alloc. -- 2.25.1