From 890836c96e68836d953cb1c913a89c7eb81af0c3 Mon Sep 17 00:00:00 2001 From: Davin McCall Date: Wed, 13 Jan 2016 20:02:14 +0000 Subject: [PATCH] Add support for unpin command in dinitctl (includes corresponding control protocol changes) --- src/control-cmds.h | 2 + src/control.cc | 44 ++++++++++++++++++++- src/control.h | 3 ++ src/dinitctl.cc | 96 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 142 insertions(+), 3 deletions(-) diff --git a/src/control-cmds.h b/src/control-cmds.h index 31c0b1c..98be5e5 100644 --- a/src/control-cmds.h +++ b/src/control-cmds.h @@ -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 diff --git a/src/control.cc b/src/control.cc index 2efe731..5ac656b 100644 --- a/src/control.cc +++ b/src/control.cc @@ -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; diff --git a/src/control.h b/src/control.h index c55fa88..f027f01 100644 --- a/src/control.h +++ b/src/control.h @@ -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; diff --git a/src/dinitctl.cc b/src/dinitctl.cc index 4bead22..742b2de 100644 --- a/src/dinitctl.cc +++ b/src/dinitctl.cc @@ -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(rbuffer[1]); + //target_state = static_cast(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; +} -- 2.25.1