if (pktType == DINIT_CP_UNPINSERVICE) {
return process_unpin_service();
}
+ if (pktType == DINIT_CP_UNLOADSERVICE) {
+ return process_unload_service();
+ }
if (pktType == DINIT_CP_SHUTDOWN) {
// Shutdown/reboot
if (rbuf.get_length() < 2) {
iob.set_watches(OUT_EVENTS);
return true;
}
+
+ service->unpin();
+ services->process_queues();
+ char ack_buf[] = { (char) DINIT_RP_ACK };
+ if (! queue_packet(ack_buf, 1)) return false;
+
+ // Clear the packet from the buffer
+ rbuf.consume(pkt_size);
+ chklen = 0;
+ return true;
+}
+
+bool control_conn_t::process_unload_service()
+{
+ using std::string;
+
+ constexpr int pkt_size = 1 + sizeof(handle_t);
+
+ if (rbuf.get_length() < pkt_size) {
+ chklen = pkt_size;
+ return true;
+ }
+
+ // 1 byte: packet type
+ // 4 bytes: service handle
+
+ handle_t handle;
+ rbuf.extract((char *) &handle, 1, sizeof(handle));
+
+ service_record *service = find_service_for_key(handle);
+ if (service == nullptr) {
+ // Service handle is bad
+ char badreq_rep[] = { DINIT_RP_BADREQ };
+ if (! queue_packet(badreq_rep, 1)) return false;
+ bad_conn_close = true;
+ iob.set_watches(OUT_EVENTS);
+ return true;
+ }
+
+ if (! service->has_lone_ref() || service->get_state() != service_state_t::STOPPED) {
+ // Cannot unload: has other references
+ char nak_rep[] = { DINIT_RP_NAK };
+ if (! queue_packet(nak_rep, 1)) return false;
+ }
else {
- service->unpin();
- services->process_queues();
+ // unload
+ service->prepare_for_unload();
+ services->remove_service(service);
+ delete service;
+
+ // drop handle
+ service_key_map.erase(service);
+ key_service_map.erase(handle);
+
+ // send ack
char ack_buf[] = { (char) DINIT_RP_ACK };
if (! queue_packet(ack_buf, 1)) return false;
}
-
+
// Clear the packet from the buffer
rbuf.consume(pkt_size);
chklen = 0;
enum class command_t;
-static int issue_load_service(int socknum, const char *service_name);
+static int issue_load_service(int socknum, const char *service_name, bool find_only = false);
static int check_load_reply(int socknum, cpbuffer<1024> &rbuffer, handle_t *handle_p, service_state_t *state_p);
static int start_stop_service(int socknum, const char *service_name, command_t command, bool do_pin, bool wait_for_service, bool verbose);
static int unpin_service(int socknum, const char *service_name, bool verbose);
+static int unload_service(int socknum, const char *service_name);
static int list_services(int socknum);
static int shutdown_dinit(int soclknum);
STOP_SERVICE,
RELEASE_SERVICE,
UNPIN_SERVICE,
+ UNLOAD_SERVICE,
LIST_SERVICES,
SHUTDOWN
};
else if (strcmp(argv[i], "unpin") == 0) {
command = command_t::UNPIN_SERVICE;
}
+ else if (strcmp(argv[i], "unload") == 0) {
+ command = command_t::UNLOAD_SERVICE;
+ }
else if (strcmp(argv[i], "list") == 0) {
command = command_t::LIST_SERVICES;
}
cout << " dinitctl [options] wake [options] <service-name> : start but do not mark activated" << endl;
cout << " dinitctl [options] release [options] <service-name> : release activation, stop if no dependents" << endl;
cout << " dinitctl [options] unpin <service-name> : un-pin the service (after a previous pin)" << endl;
+ cout << " dinitctl unload <service-name> : unload the service" << endl;
cout << " dinitctl list : list loaded services" << endl;
cout << " dinitctl shutdown : stop all services and terminate dinit" << endl;
if (command == command_t::UNPIN_SERVICE) {
return unpin_service(socknum, service_name, verbose);
}
+ else if (command == command_t::UNLOAD_SERVICE) {
+ return unload_service(socknum, service_name);
+ }
else if (command == command_t::LIST_SERVICES) {
return list_services(socknum);
}
// Issue a "load service" command (DINIT_CP_LOADSERVICE), without waiting for
// a response. Returns 1 on failure (with error logged), 0 on success.
-static int issue_load_service(int socknum, const char *service_name)
+static int issue_load_service(int socknum, const char *service_name, bool find_only)
{
// Build buffer;
uint16_t sname_len = strlen(service_name);
std::unique_ptr<char[]> ubuf(new char[bufsize]);
auto buf = ubuf.get();
- buf[0] = DINIT_CP_LOADSERVICE;
+ buf[0] = find_only ? DINIT_CP_FINDSERVICE : DINIT_CP_LOADSERVICE;
memcpy(buf + 1, &sname_len, 2);
memcpy(buf + 3, service_name, sname_len);
return 0;
}
+static int unload_service(int socknum, const char *service_name)
+{
+ using namespace std;
+
+ // Build buffer;
+ if (issue_load_service(socknum, service_name, true) == 1) {
+ return 1;
+ }
+
+ // Now we expect a reply:
+
+ try {
+ cpbuffer<1024> rbuffer;
+ wait_for_reply(rbuffer, socknum);
+
+ handle_t handle;
+
+ if (check_load_reply(socknum, rbuffer, &handle, nullptr) != 0) {
+ return 1;
+ }
+
+ // Issue UNLOAD command.
+ {
+ int r;
+
+ {
+ char *buf = new char[1 + sizeof(handle)];
+ unique_ptr<char[]> ubuf(buf);
+ buf[0] = DINIT_CP_UNLOADSERVICE;
+ memcpy(buf + 1, &handle, sizeof(handle));
+ r = write_all(socknum, buf, 2 + sizeof(handle));
+ }
+
+ if (r == -1) {
+ perror("dinitctl: write");
+ return 1;
+ }
+
+ wait_for_reply(rbuffer, socknum);
+ if (rbuffer[0] == DINIT_RP_NAK) {
+ cerr << "dinitctl: Could not unload service; service not stopped, or is a dependency of "
+ "other service." << endl;
+ return 1;
+ }
+ if (rbuffer[0] != DINIT_RP_ACK) {
+ cerr << "dinitctl: Protocol error." << endl;
+ return 1;
+ }
+ rbuffer.consume(1);
+ }
+ }
+ catch (read_cp_exception &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;
+ }
+
+ cout << "Service unloaded." << endl;
+ return 0;
+}
+
static int list_services(int socknum)
{
using namespace std;
{
listeners.erase(listener);
}
+
+ // Assuming there is one reference (from a control link), return true if this is the only reference,
+ // or false if there are others (including dependents).
+ bool has_lone_ref() noexcept
+ {
+ if (! dependents.empty()) return false;
+ auto i = listeners.begin();
+ return (++i == listeners.end());
+ }
+
+ // Prepare this service to be unloaded.
+ void prepare_for_unload() noexcept
+ {
+ // Remove all dependencies:
+ for (auto &dep : depends_on) {
+ auto &dep_dpts = dep.get_to()->dependents;
+ dep_dpts.erase(std::find(dep_dpts.begin(), dep_dpts.end(), &dep));
+ }
+ depends_on.clear();
+ }
};
inline auto extract_prop_queue(service_record *sr) -> decltype(sr->prop_queue_node) &
void remove_service(service_record *svc)
{
- std::remove(records.begin(), records.end(), svc);
+ records.erase(std::find(records.begin(), records.end(), svc));
}
// Get the list of all loaded services.