Implement control protocol for reloading services
authorDavin McCall <davmac@davmac.org>
Sun, 1 Dec 2019 02:44:45 +0000 (12:44 +1000)
committerDavin McCall <davmac@davmac.org>
Sun, 1 Dec 2019 04:59:35 +0000 (14:59 +1000)
src/control.cc
src/includes/control-cmds.h
src/includes/control.h
src/includes/service.h

index 2cb8b410c10472742109765daa8b7d376a549b09..0ef1a997f7b417219d1b48c353b996c0f839f528 100644 (file)
@@ -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
index 7b80adacca8339bbe8dba704a40b339e6534135e..a46b99d6154962ad793938a2d029ae1497585dd3 100644 (file)
@@ -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
index 440a2408b10c9963f03870b4438925a82e922b4b..f4608c7f74fff43543d40a0f3b3d76c6ead13985 100644 (file)
@@ -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();
 
index 3bdad9e6d844d63a90fe93cacf89174855a74cba..5ebb8b92c1d57537ee7ea44b05c43336921ca2f7 100644 (file)
@@ -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());
     }