Add a control command to initiate service rollback (and receive
authorDavin McCall <davmac@davmac.org>
Thu, 19 Nov 2015 18:28:34 +0000 (18:28 +0000)
committerDavin McCall <davmac@davmac.org>
Thu, 19 Nov 2015 18:28:34 +0000 (18:28 +0000)
notification when rollback is complete).

Makefile
control-cmds.h [new file with mode: 0644]
control.cc [new file with mode: 0644]
control.h
service.cc
service.h

index 827535c46346d38900fb1076a1f22a563cc08ee2..10d789bd327d90aefeb9087a09126690ad9a9099 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,8 @@
 -include mconfig
 
-objects = dinit.o load_service.o service.o dinit-log.o dinit-start.o
+objects = dinit.o load_service.o service.o control.o dinit-log.o dinit-start.o
 
-dinit_objects = dinit.o load_service.o service.o dinit-log.o
+dinit_objects = dinit.o load_service.o service.o control.o dinit-log.o
 
 all: dinit dinit-start
 
@@ -12,7 +12,7 @@ dinit: $(dinit_objects)
 dinit-start: dinit-start.o
        $(CXX) -o dinit-start dinit-start.o $(EXTRA_LIBS)
 
-$(objects): %.o: %.cc service.h dinit-log.h
+$(objects): %.o: %.cc service.h dinit-log.h control.h control-cmds.h
        $(CXX) -D_GLIBCXX_USE_CXX11_ABI=0 -std=gnu++11 -c -Os -Wall $< -o $@
 
 #install: all
diff --git a/control-cmds.h b/control-cmds.h
new file mode 100644 (file)
index 0000000..dd208ca
--- /dev/null
@@ -0,0 +1,11 @@
+// Dinit control command packet types
+
+// Start or stop a named service:
+constexpr static int DINIT_CP_STARTSERVICE = 0;
+constexpr static int DINIT_CP_STOPSERVICE  = 1;
+
+// Roll-back all services:
+constexpr static int DINIT_CP_ROLLBACKALL = 2;
+
+// Reply: request completed
+constexpr static int DINIT_RP_COMPLETED = 50;
diff --git a/control.cc b/control.cc
new file mode 100644 (file)
index 0000000..d32a602
--- /dev/null
@@ -0,0 +1,121 @@
+#include "control.h"
+#include "service.h"
+
+void ControlConn::processPacket()
+{
+    using std::string;
+
+    int pktType = iobuf[0];
+    if (pktType == DINIT_CP_STARTSERVICE || pktType == DINIT_CP_STOPSERVICE) {
+        if (bufidx < 4) {
+            chklen = 4;
+            return;
+        }
+        
+        uint16_t svcSize;
+        memcpy(&svcSize, iobuf + 1, 2);
+        if (svcSize <= 0) {
+            // TODO error response
+            bufidx = 1024; // dataReady will delete - TODO clean up
+        }
+        
+        chklen = svcSize + 3;
+        if (chklen > 1024) {
+            // We can't have a service name this long
+            // TODO error response
+            bufidx = 1024; // TODO cleanup.
+        }
+        
+        if (bufidx < chklen) {
+            // packet not complete yet; read more
+            return;
+        }
+        
+        string serviceName(iobuf + 3, (size_t) svcSize);
+        if (pktType == DINIT_CP_STARTSERVICE) {
+            // TODO do not allow services to be started during system shutdown
+            service_set->startService(serviceName.c_str());
+            // TODO catch exceptions, error response
+        }
+        else {
+            // TODO verify the named service exists?
+            service_set->stopService(serviceName.c_str());
+        }
+        
+        // Clear the packet from the buffer
+        memmove(iobuf, iobuf + chklen, 1024 - chklen);
+        bufidx -= chklen;
+        chklen = 0;
+        return;
+    }
+    else if (pktType == DINIT_CP_ROLLBACKALL) {
+        // Roll-back all services
+        if (service_set->setRollbackHandler(this)) {
+            service_set->stop_all_services();
+            log_to_console = true;
+            // TODO send ACK
+        }
+        else {
+            // TODO send NAK
+        }
+    }
+    else {
+        // TODO error response
+    }
+}
+
+void ControlConn::rollbackComplete()
+{
+    char ackBuf[1] = { DINIT_RP_COMPLETED };
+    if (write(iob.fd, ackBuf, 1) == -1) {
+        // TODO queue or at least re-try if error or 0 bytes written.
+        log(LogLevel::ERROR, "Couldn't write response to control socket");
+    }
+}
+
+void ControlConn::dataReady()
+{
+    int fd = iob.fd;
+    int buffree = 1024 - bufidx;
+    
+    int r = read(fd, iobuf + bufidx, buffree);
+    
+    // Note file descriptor is non-blocking
+    if (r == -1) {
+        if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
+            return;
+        }
+        // TODO log error
+        delete this;
+        return;
+    }
+    
+    if (r == 0) {
+        delete this;
+        return;
+    }
+    
+    bufidx += r;
+    buffree -= r;
+    
+    // complete packet?
+    if (bufidx >= chklen) {
+        processPacket();
+    }
+    
+    if (bufidx == 1024) {
+        // Too big packet
+        // TODO log error?
+        // TODO error response?
+        delete this;
+    }
+}
+
+ControlConn::~ControlConn()
+{
+    close(iob.fd);
+    ev_io_stop(loop, &iob);
+    delete [] iobuf;
+    service_set->clearRollbackHandler(this);
+    active_control_conns--;
+}
index 51479f1a2f71d190a731d57b2d4596227082a981..2454318c7a8c9fcfd9ce91c80de5c527722a58c4 100644 (file)
--- a/control.h
+++ b/control.h
@@ -1,4 +1,10 @@
+#ifndef DINIT_CONTROL_H
+#define DINIT_CONTROL_H
+
+#include <unistd.h>
 #include <ev++.h>
+#include "dinit-log.h"
+#include "control-cmds.h"
 
 // Control connection for dinit
 
 // forward-declaration of callback:
 static void control_conn_cb(struct ev_loop * loop, ev_io * w, int revents);
 
+class ControlConn;
+
+// Pointer to the control connection that is listening for rollback completion
+extern ControlConn * rollback_handler_conn;
 
-// Packet types:
-constexpr static int DINIT_CP_STARTSERVICE = 0;
-constexpr static int DINIT_CP_STOPSERVICE  = 1;
+extern int active_control_conns;
 
 // "packet" format:
 // (1 byte) packet type
@@ -18,6 +26,8 @@ constexpr static int DINIT_CP_STOPSERVICE  = 1;
 //      (2 bytes) service name length
 //      (M buyes) service name (without nul terminator)
 
+class ServiceSet;
+
 
 class ControlConn
 {
@@ -38,101 +48,15 @@ class ControlConn
         ev_io_init(&iob, control_conn_cb, fd, EV_READ);
         iob.data = this;
         ev_io_start(loop, &iob);
-    }
-    
-    void processPacket()
-    {
-        using std::string;
-    
-        int pktType = iobuf[0];
-        if (pktType == DINIT_CP_STARTSERVICE || pktType == DINIT_CP_STOPSERVICE) {
-            if (bufidx < 4) {
-                chklen = 4;
-                return;
-            }
-            
-            uint16_t svcSize;
-            memcpy(&svcSize, iobuf + 1, 2);
-            if (svcSize <= 0) {
-                // TODO error response
-                bufidx = 1024; // dataReady will delete - TODO clean up
-            }
-            
-            chklen = svcSize + 3;
-            if (chklen > 1024) {
-                // We can't have a service name this long
-                // TODO error response
-                bufidx = 1024; // TODO cleanup.
-            }
-            
-            if (bufidx < chklen) {
-                // packet not complete yet; read more
-                return;
-            }
-            
-            string serviceName(iobuf + 3, (size_t) svcSize);
-            if (pktType == DINIT_CP_STARTSERVICE) {
-                service_set->startService(serviceName.c_str());
-                // TODO catch exceptions, error response
-            }
-            else {
-                // TODO verify the named service exists?
-                service_set->stopService(serviceName.c_str());
-            }
-            
-            // Clear the packet from the buffer
-            memmove(iobuf, iobuf + chklen, 1024 - chklen);
-            bufidx -= chklen;
-            chklen = 0;
-            return;
-        }
-    
-    }
-    
-    void dataReady()
-    {
-        int fd = iob.fd;
-        int buffree = 1024 - bufidx;
-        
-        int r = read(fd, iobuf + bufidx, buffree);
-        
-        // Note file descriptor is non-blocking
-        if (r == -1) {
-            if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
-                return;
-            }
-            // TODO log error
-            delete this;
-            return;
-        }
-        
-        if (r == 0) {
-            delete this;
-            return;
-        }
-        
-        bufidx += r;
-        buffree -= r;
         
-        // complete packet?
-        if (bufidx >= chklen) {
-            processPacket();
-        }
-        
-        if (bufidx == 1024) {
-            // Too big packet
-            // TODO log error?
-            // TODO error response?
-            delete this;
-        }
+        active_control_conns++;
     }
     
-    ~ControlConn()
-    {
-        close(iob.fd);
-        ev_io_stop(loop, &iob);
-        delete [] iobuf;
-    }
+    void processPacket();
+    void rollbackComplete();
+    void dataReady();
+    
+    ~ControlConn();
 };
 
 
@@ -141,3 +65,5 @@ static void control_conn_cb(struct ev_loop * loop, ev_io * w, int revents)
     ControlConn *conn = (ControlConn *) w->data;
     conn->dataReady();
 }
+
+#endif
index bc989254e90b1efca30cafe9506fc19e8df643e8..c245a308f7e721e7e0519401ed5a0d9355c682ee 100644 (file)
@@ -544,4 +544,7 @@ void ServiceSet::service_active(ServiceRecord *sr)
 void ServiceSet::service_inactive(ServiceRecord *sr)
 {
     active_services--;
+    if (active_services == 0 && rollback_handler != nullptr) {
+        rollback_handler->rollbackComplete();
+    }
 }
index b391cc8360ac16bf0b8f9b5ef42fcd7426be4751..31d277fb54c749f8c964ce5c91beeeb339120fac 100644 (file)
--- a/service.h
+++ b/service.h
@@ -3,6 +3,7 @@
 #include <vector>
 #include <csignal>
 #include "ev.h"
+#include "control.h"
 
 /*
  * Possible service states
@@ -323,6 +324,7 @@ class ServiceSet
     std::list<ServiceRecord *> records;
     const char *service_dir;  // directory containing service descriptions
     bool restart_enabled; // whether automatic restart is enabled (allowed)
+    ControlConn *rollback_handler; // recieves notification when all services stopped
     
     // Private methods
     
@@ -386,4 +388,25 @@ class ServiceSet
     {
         return restart_enabled;
     }
+    
+    // Set the rollback handler, which will be notified when all services have stopped.
+    // There can be only one rollback handler; attempts to set it when already set will
+    // fail. Returns true if successful.
+    bool setRollbackHandler(ControlConn *conn)
+    {
+        if (rollback_handler == nullptr) {
+            rollback_handler = conn;
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+    
+    void clearRollbackHandler(ControlConn *conn)
+    {
+        if (rollback_handler == conn) {
+            rollback_handler = nullptr;
+        }
+    }
 };