Kaynağa Gözat

Implement careful/"gentle" service stop (abort if dependent would stop).

Davin McCall 4 yıl önce
ebeveyn
işleme
46f30daea8

+ 45 - 1
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<char> 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;

+ 3 - 0
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)

+ 6 - 1
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<char> &&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);
     

+ 5 - 0
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.