notification when rollback is complete).
-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
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
--- /dev/null
+// 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;
--- /dev/null
+#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--;
+}
+#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
// (2 bytes) service name length
// (M buyes) service name (without nul terminator)
+class ServiceSet;
+
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();
};
ControlConn *conn = (ControlConn *) w->data;
conn->dataReady();
}
+
+#endif
void ServiceSet::service_inactive(ServiceRecord *sr)
{
active_services--;
+ if (active_services == 0 && rollback_handler != nullptr) {
+ rollback_handler->rollbackComplete();
+ }
}
#include <vector>
#include <csignal>
#include "ev.h"
+#include "control.h"
/*
* Possible service states
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
{
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;
+ }
+ }
};