From 99a77344cd1ecd495162ad4520aab6cb8fbabfea Mon Sep 17 00:00:00 2001 From: Davin McCall Date: Sun, 1 Dec 2019 12:44:45 +1000 Subject: [PATCH] Implement control protocol for reloading services --- src/control.cc | 72 +++++++++++++++++++++++++++++++++++++ src/includes/control-cmds.h | 3 ++ src/includes/control.h | 3 ++ src/includes/service.h | 4 +-- 4 files changed, 80 insertions(+), 2 deletions(-) diff --git a/src/control.cc b/src/control.cc index 2cb8b41..0ef1a99 100644 --- a/src/control.cc +++ b/src/control.cc @@ -57,6 +57,9 @@ bool control_conn_t::process_packet() if (pktType == DINIT_CP_UNLOADSERVICE) { return process_unload_service(); } + if (pktType == DINIT_CP_RELOADSERVICE) { + return process_reload_service(); + } if (pktType == DINIT_CP_SHUTDOWN) { // Shutdown/reboot if (rbuf.get_length() < 2) { @@ -429,6 +432,75 @@ bool control_conn_t::process_unload_service() return true; } +bool control_conn_t::process_reload_service() +{ + using std::string; + + constexpr int pkt_size = 1 + sizeof(handle_t); + + if (rbuf.get_length() < pkt_size) { + chklen = pkt_size; + return true; + } + + // 1 byte: packet type + // 4 bytes: service handle + + handle_t handle; + rbuf.extract((char *) &handle, 1, sizeof(handle)); + + service_record *service = find_service_for_key(handle); + if (service == nullptr) { + // Service handle is bad + char badreq_rep[] = { DINIT_RP_BADREQ }; + if (! queue_packet(badreq_rep, 1)) return false; + bad_conn_close = true; + iob.set_watches(OUT_EVENTS); + return true; + } + + if (! service->has_lone_ref(false)) { + // Cannot unload: has other references + char nak_rep[] = { DINIT_RP_NAK }; + if (! queue_packet(nak_rep, 1)) return false; + } + else { + try { + // reload + auto *new_service = services->reload_service(service); + if (new_service != service) { + service->prepare_for_unload(); + services->remove_service(service); + delete service; + } + else { + service->remove_listener(this); + } + + // drop handle + key_service_map.erase(handle); + service_key_map.erase(service); + + services->process_queues(); + + // send ack + char ack_buf[] = { (char) DINIT_RP_ACK }; + if (! queue_packet(ack_buf, 1)) return false; + } + catch (service_load_exc &slexc) { + log(loglevel_t::ERROR, "Could not reload service ", slexc.service_name, ": ", + slexc.exc_description); + char nak_rep[] = { DINIT_RP_NAK }; + if (! queue_packet(nak_rep, 1)) return false; + } + } + + // Clear the packet from the buffer + rbuf.consume(pkt_size); + chklen = 0; + return true; +} + bool control_conn_t::list_services() { rbuf.consume(1); // clear request packet diff --git a/src/includes/control-cmds.h b/src/includes/control-cmds.h index 7b80ada..a46b99d 100644 --- a/src/includes/control-cmds.h +++ b/src/includes/control-cmds.h @@ -42,6 +42,9 @@ constexpr static int DINIT_CP_ENABLESERVICE = 14; // Find the name of a service (from a handle) constexpr static int DINIT_CP_QUERYSERVICENAME = 15; +// Reload a service: +constexpr static int DINIT_CP_RELOADSERVICE = 16; + // Replies: // Reply: ACK/NAK to request diff --git a/src/includes/control.h b/src/includes/control.h index 440a240..f4608c7 100644 --- a/src/includes/control.h +++ b/src/includes/control.h @@ -146,6 +146,9 @@ class control_conn_t : private service_listener // Process an UNLOADSERVICE packet. bool process_unload_service(); + // Process a RELOADSERVICE packet. May throw std::bad_alloc. + bool process_reload_service(); + // Process a QUERYSERVICENAME packet. bool process_query_name(); diff --git a/src/includes/service.h b/src/includes/service.h index 3bdad9e..5ebb8b9 100644 --- a/src/includes/service.h +++ b/src/includes/service.h @@ -557,9 +557,9 @@ class service_record // Assuming there is one reference (from a control link), return true if this is the only reference, // or false if there are others (including dependents). - bool has_lone_ref() noexcept + bool has_lone_ref(bool check_deps = true) noexcept { - if (! dependents.empty()) return false; + if (check_deps && ! dependents.empty()) return false; auto i = listeners.begin(); return (++i == listeners.end()); } -- 2.25.1