Add support for unpin command in dinitctl (includes corresponding
authorDavin McCall <davmac@davmac.org>
Wed, 13 Jan 2016 20:02:14 +0000 (20:02 +0000)
committerDavin McCall <davmac@davmac.org>
Wed, 13 Jan 2016 20:02:14 +0000 (20:02 +0000)
control protocol changes)

src/control-cmds.h
src/control.cc
src/control.h
src/dinitctl.cc

index 31c0b1c7ac7bdf462e89366979d3a90be7f8bd4c..98be5e5d517c051f38518af5392816edf8928332 100644 (file)
@@ -17,6 +17,8 @@ constexpr static int DINIT_CP_STOPSERVICE  = 4;
 constexpr static int DINIT_CP_WAKESERVICE = 5;
 constexpr static int DINIT_CP_RELEASESERVICE = 6;
 
+constexpr static int DINIT_CP_UNPINSERVICE = 7;
+
 // Shutdown:
 constexpr static int DINIT_CP_SHUTDOWN = 10;
  // followed by 1-byte shutdown type
index 2efe73128e56c2d65a3ad4100f55950b68978718..5ac656b0cb5171f118844624d7a1efe1ca2342f1 100644 (file)
@@ -27,7 +27,11 @@ void ControlConn::processPacket()
         processStartStop(pktType);
         return;
     }
-    else if (pktType == DINIT_CP_SHUTDOWN) {
+    if (pktType == DINIT_CP_UNPINSERVICE) {
+        processUnpinService();
+        return;
+    }
+    if (pktType == DINIT_CP_SHUTDOWN) {
         // Shutdown/reboot
         if (rbuf.get_length() < 2) {
             chklen = 2;
@@ -190,6 +194,44 @@ void ControlConn::processStartStop(int pktType)
     return;
 }
 
+void ControlConn::processUnpinService()
+{
+    using std::string;
+    
+    constexpr int pkt_size = 1 + sizeof(handle_t);
+    
+    if (rbuf.get_length() < pkt_size) {
+        chklen = pkt_size;
+        return;
+    }
+    
+    // 1 byte: packet type
+    // 4 bytes: service handle
+    
+    handle_t handle;
+    rbuf.extract((char *) &handle, 1, sizeof(handle));
+    
+    ServiceRecord *service = findServiceForKey(handle);
+    if (service == nullptr) {
+        // Service handle is bad
+        char badreqRep[] = { DINIT_RP_BADREQ };
+        if (! queuePacket(badreqRep, 1)) return;
+        bad_conn_close = true;
+        ev_io_set(&iob, iob.fd, EV_WRITE);
+        return;
+    }
+    else {
+        service->unpin();
+        char ack_buf[] = { (char) DINIT_RP_ACK };
+        if (! queuePacket(ack_buf, 1)) return;
+    }
+    
+    // Clear the packet from the buffer
+    rbuf.consume(pkt_size);
+    chklen = 0;
+    return;
+}
+
 ControlConn::handle_t ControlConn::allocateServiceHandle(ServiceRecord *record)
 {
     bool is_unique = true;
index c55fa884b18b1d56624a3de9536cf78c2a640d23..f027f0184b74eed7d968b3fe4809c906ae15a768 100644 (file)
@@ -95,6 +95,9 @@ class ControlConn : private ServiceListener
     // Process a FINDSERVICE/LOADSERVICE packet. May throw std::bad_alloc.
     void processFindLoad(int pktType);
 
+    // Process an UNPINSERVICE packet. May throw std::bad_alloc.
+    void processUnpinService();
+
     // Notify that data is ready to be read from the socket. Returns true in cases where the
     // connection was deleted with potentially pending outgoing packets.
     bool dataReady() noexcept;
index 4bead220a1f9c46977e17f4f43bd34585bf07c42..742b2de0acce87536656061a1bd119cae1088d9f 100644 (file)
@@ -87,6 +87,9 @@ static int write_all(int fd, const void *buf, size_t count)
     return w;
 }
 
+static int unpinService(int socknum, const char *service_name);
+
+
 // Entry point.
 int main(int argc, char **argv)
 {
@@ -108,6 +111,7 @@ int main(int argc, char **argv)
     
     constexpr int START_SERVICE = 1;
     constexpr int STOP_SERVICE = 2;
+    constexpr int UNPIN_SERVICE = 3;
         
     for (int i = 1; i < argc; i++) {
         if (argv[i][0] == '-') {
@@ -139,6 +143,9 @@ int main(int argc, char **argv)
             else if (strcmp(argv[i], "stop") == 0) {
                 command = STOP_SERVICE;
             }
+            else if (strcmp(argv[i], "unpin") == 0) {
+                command = UNPIN_SERVICE;
+            }
             else {
                 show_help = true;
                 break;
@@ -178,8 +185,6 @@ int main(int argc, char **argv)
         return 1;
     }
     
-    do_stop = (command == STOP_SERVICE);
-    
     control_socket_path = "/dev/dinitctl";
     
     if (! sys_dinit) {
@@ -227,6 +232,12 @@ int main(int argc, char **argv)
     
     // TODO should start by querying protocol version
     
+    if (command == UNPIN_SERVICE) {
+        return unpinService(socknum, service_name);
+    }
+
+    do_stop = (command == STOP_SERVICE);
+    
     // Build buffer;
     uint16_t sname_len = strlen(service_name);
     int bufsize = 3 + sname_len;
@@ -393,3 +404,84 @@ int main(int argc, char **argv)
     
     return 0;
 }
+
+// TODO refactor shared code with above
+static int unpinService(int socknum, const char *service_name)
+{
+    using namespace std;
+    
+    // Build buffer;
+    uint16_t sname_len = strlen(service_name);
+    int bufsize = 3 + sname_len;
+    char * buf = new char[bufsize];
+    
+    buf[0] = DINIT_CP_LOADSERVICE;
+    memcpy(buf + 1, &sname_len, 2);
+    memcpy(buf + 3, service_name, sname_len);
+    
+    int r = write_all(socknum, buf, bufsize);
+    delete [] buf;
+    if (r == -1) {
+        perror("write");
+        return 1;
+    }
+    
+    // Now we expect a reply:
+    
+    try {
+        CPBuffer rbuffer;
+        wait_for_reply(rbuffer, socknum);
+        
+        //ServiceState state;
+        //ServiceState target_state;
+        handle_t handle;
+        
+        if (rbuffer[0] == DINIT_RP_SERVICERECORD) {
+            fillBufferTo(&rbuffer, socknum, 2 + sizeof(handle));
+            rbuffer.extract((char *) &handle, 2, sizeof(handle));
+            //state = static_cast<ServiceState>(rbuffer[1]);
+            //target_state = static_cast<ServiceState>(rbuffer[2 + sizeof(handle)]);
+            rbuffer.consume(3 + sizeof(handle));
+        }
+        else if (rbuffer[0] == DINIT_RP_NOSERVICE) {
+            cerr << "Failed to find/load service." << endl;
+            return 1;
+        }
+        else {
+            cerr << "Protocol error." << endl;
+            return 1;
+        }
+        
+        // Issue UNPIN command.
+        {
+            buf = new char[1 + sizeof(handle)];
+            buf[0] = DINIT_CP_UNPINSERVICE;
+            memcpy(buf + 1, &handle, sizeof(handle));
+            r = write_all(socknum, buf, 2 + sizeof(handle));
+            delete buf;
+            
+            if (r == -1) {
+                perror("write");
+                return 1;
+            }
+            
+            wait_for_reply(rbuffer, socknum);
+            if (rbuffer[0] != DINIT_RP_ACK) {
+                cerr << "Protocol error." << endl;
+                return 1;
+            }
+            rbuffer.consume(1);
+        }
+    }
+    catch (ReadCPException &exc) {
+        cerr << "control socket read failure or protocol error" << endl;
+        return 1;
+    }
+    catch (std::bad_alloc &exc) {
+        cerr << "out of memory" << endl;
+        return 1;
+    }
+    
+    cout << "Service unpinned." << endl;
+    return 0;
+}