-* Complete control socket handling and protocol
- - support for listing all services and their state
* Implement a control utility to start/stop services after dinit has started
- basic version exists, needs a little more work to support all
start/stop command variants
For version 1.0:
----------------
* Documentation including sample service definitions
+ - Man pages
* Better error handling, logging of errors (largely done, still some patches
of code where it may be missing - check TODO's in code).
* Write wtmp entry on startup (see simpleinit)
* Allow running services as a different UID
* Must be able to specify kill timeout (after which kill -9!)
-* Must rate-limit restart of services
+* Must rate-limit restart of services. Trying to start at a greater rate
+ should trigger failed-to-start notification.
* "triggered" service type: external process notifies Dinit when the service
has started.
chklen = 0;
return true;
}
+ if (pktType == DINIT_CP_LISTSERVICES) {
+ return listServices();
+ }
else {
// Unrecognized: give error response
char outbuf[] = { DINIT_RP_BADREQ };
return true;
}
+bool ControlConn::listServices()
+{
+ rbuf.consume(1); // clear request packet
+ chklen = 0;
+
+ try {
+ auto slist = service_set->listServices();
+ for (auto sptr : slist) {
+ std::vector<char> pkt_buf;
+
+ const std::string &name = sptr->getServiceName();
+ int nameLen = std::min((size_t)256, name.length());
+ pkt_buf.resize(8 + nameLen);
+
+ pkt_buf[0] = DINIT_RP_SVCINFO;
+ pkt_buf[1] = nameLen;
+ pkt_buf[2] = static_cast<char>(sptr->getState());
+ pkt_buf[3] = static_cast<char>(sptr->getTargetState());
+
+ pkt_buf[4] = 0; // reserved
+ pkt_buf[5] = 0;
+ pkt_buf[6] = 0;
+ pkt_buf[7] = 0;
+
+ for (int i = 0; i < nameLen; i++) {
+ pkt_buf[8+i] = name[i];
+ }
+
+ if (! queuePacket(std::move(pkt_buf))) return false;
+ }
+
+ char ack_buf[] = { (char) DINIT_RP_LISTDONE };
+ if (! queuePacket(ack_buf, 1)) return false;
+
+ return true;
+ }
+ catch (std::bad_alloc &exc)
+ {
+ doOomClose();
+ return true;
+ }
+}
+
ControlConn::handle_t ControlConn::allocateServiceHandle(ServiceRecord *record)
{
bool is_unique = true;
static int checkLoadReply(int socknum, CPBuffer<1024> &rbuffer, handle_t *handle_p, ServiceState *state_p);
static int startStopService(int socknum, const char *service_name, int command, bool do_pin, bool wait_for_service, bool verbose);
static int unpinService(int socknum, const char *service_name, bool verbose);
+static int listServices(int socknum);
// Fill a circular buffer from a file descriptor, reading at least _rlength_ bytes.
static const int START_SERVICE = 1;
static const int STOP_SERVICE = 2;
static const int UNPIN_SERVICE = 3;
+static const int LIST_SERVICES = 4;
// Entry point.
else if (strcmp(argv[i], "unpin") == 0) {
command = UNPIN_SERVICE;
}
+ else if (strcmp(argv[i], "list") == 0) {
+ command = LIST_SERVICES;
+ }
else {
show_help = true;
break;
}
}
- if (service_name == nullptr || command == 0) {
+ if ((service_name == nullptr && command != LIST_SERVICES) || command == 0) {
show_help = true;
}
cout << " dinitctl [options] unpin <service-name> : un-pin the service (after a previous pin)" << endl;
// TODO:
// cout << " dinitctl [options] wake <service-name> : start but don't activate service" << endl;
+ cout << " dinitctl list : list loaded services" << endl;
- cout << "\nNote: An activated service keeps its dependencies running when possible." << endl;
+ cout << "\nNote: An activated service continues running when its dependents stop." << endl;
cout << "\nGeneral options:" << endl;
cout << " -s, --system : control system daemon instead of user daemon" << endl;
if (command == UNPIN_SERVICE) {
return unpinService(socknum, service_name, verbose);
}
+ else if (command == LIST_SERVICES) {
+ return listServices(socknum);
+ }
return startStopService(socknum, service_name, command, do_pin, wait_for_service, verbose);
}
}
return 0;
}
+
+static int listServices(int socknum)
+{
+ using namespace std;
+
+ try {
+ char cmdbuf[] = { (char)DINIT_CP_LISTSERVICES };
+ int r = write_all(socknum, cmdbuf, 1);
+
+ if (r == -1) {
+ perror("dinitctl: write");
+ return 1;
+ }
+
+ CPBuffer<1024> rbuffer;
+ wait_for_reply(rbuffer, socknum);
+ while (rbuffer[0] == DINIT_RP_SVCINFO) {
+ fillBufferTo(&rbuffer, socknum, 8);
+ int nameLen = rbuffer[1];
+ ServiceState current = static_cast<ServiceState>(rbuffer[2]);
+ ServiceState target = static_cast<ServiceState>(rbuffer[3]);
+
+ fillBufferTo(&rbuffer, socknum, nameLen + 8);
+
+ char *name_ptr = rbuffer.get_ptr(8);
+ int clength = std::min(rbuffer.get_contiguous_length(name_ptr), nameLen);
+
+ string name = string(name_ptr, clength);
+ name.append(rbuffer.get_buf_base(), nameLen - clength);
+
+ cout << "[";
+
+ cout << (target == ServiceState::STARTED ? "{" : " ");
+ cout << (current == ServiceState::STARTED ? "+" : " ");
+ cout << (target == ServiceState::STARTED ? "}" : " ");
+
+ if (current == ServiceState::STARTING) {
+ cout << "<<";
+ }
+ else if (current == ServiceState::STOPPING) {
+ cout << ">>";
+ }
+ else {
+ cout << " ";
+ }
+
+ cout << (target == ServiceState::STOPPED ? "{" : " ");
+ cout << (current == ServiceState::STOPPED ? "-" : " ");
+ cout << (target == ServiceState::STOPPED ? "}" : " ");
+
+ cout << "] " << name << endl;
+
+ rbuffer.consume(8 + nameLen);
+ wait_for_reply(rbuffer, socknum);
+ }
+
+ if (rbuffer[0] != DINIT_RP_LISTDONE) {
+ cerr << "dinitctl: Control socket protocol error" << endl;
+ return 1;
+ }
+ }
+ catch (ReadCPException &exc) {
+ cerr << "dinitctl: Control socket read failure or protocol error" << endl;
+ return 1;
+ }
+ catch (std::bad_alloc &exc) {
+ cerr << "dinitctl: Out of memory" << endl;
+ return 1;
+ }
+
+ return 0;
+}