From 601fd527c64d2f9802cc41affef059476de17afb Mon Sep 17 00:00:00 2001 From: Davin McCall Date: Mon, 2 Jul 2018 19:24:09 +0100 Subject: [PATCH] dinitctl: add-dep subcommand, adds dependency between services. --- doc/manpages/dinitctl.8 | 12 ++++ src/dinitctl.cc | 114 +++++++++++++++++++++++++++++-- src/includes/service-constants.h | 8 +++ src/includes/service.h | 8 --- 4 files changed, 127 insertions(+), 15 deletions(-) diff --git a/doc/manpages/dinitctl.8 b/doc/manpages/dinitctl.8 index a3e8475..464da77 100644 --- a/doc/manpages/dinitctl.8 +++ b/doc/manpages/dinitctl.8 @@ -24,6 +24,12 @@ unload [\fIservice-name\fR] .br .B dinitctl [\-s] list +.br +.B dinitctl +[\-s] shutdown +.br +.B dinitctl +[\-s] add-dep [\fIdependency-type\fR] [\fIfrom-service\fR] [\fIto-service\fR] .\" .SH DESCRIPTION .\" @@ -104,6 +110,12 @@ or is waiting to acquire, the console; the process ID; the exit status or signal \fBshutdown\fR Stop all services (without restart) and terminate Dinit. If issued to the system instance of Dinit, this will also shut down the system. +.TP +\fBadd-dep\fR +Add a dependency between two services. The \fIdependency-type\fR must be one of \fBregular\fR, +\fBmilestone\fR or \fBwaits-for\fR. Note that adding a regular dependency requires that the service +states are consistent with the dependency (i.e. if the "from" service is started, the "to" service +must also be started). Circular dependency chains may not be created. .\" .SH SERVICE OPERATION .\" diff --git a/src/dinitctl.cc b/src/dinitctl.cc index 7016ea1..4e7f3f0 100644 --- a/src/dinitctl.cc +++ b/src/dinitctl.cc @@ -36,6 +36,8 @@ static int unpin_service(int socknum, cpbuffer_t &, const char *service_name, bo static int unload_service(int socknum, cpbuffer_t &, const char *service_name); static int list_services(int socknum, cpbuffer_t &); static int shutdown_dinit(int soclknum, cpbuffer_t &); +static int add_dependency(int socknum, cpbuffer_t &rbuffer, char *service_from, char *service_to, + dependency_type dep_type); static const char * describeState(bool stopped) @@ -57,7 +59,8 @@ enum class command_t { UNPIN_SERVICE, UNLOAD_SERVICE, LIST_SERVICES, - SHUTDOWN + SHUTDOWN, + ADD_DEPENDENCY }; @@ -68,6 +71,9 @@ int main(int argc, char **argv) bool show_help = argc < 2; char *service_name = nullptr; + char *to_service_name = nullptr; + dependency_type dep_type; + bool dep_type_set = false; std::string control_socket_str; const char * control_socket_path = nullptr; @@ -126,19 +132,52 @@ int main(int argc, char **argv) else if (strcmp(argv[i], "shutdown") == 0) { command = command_t::SHUTDOWN; } + else if (strcmp(argv[i], "add-dep") == 0) { + command = command_t::ADD_DEPENDENCY; + } else { show_help = true; break; } } else { - // service name - if (service_name != nullptr) { - show_help = true; - break; + // service name / other non-option + if (command == command_t::ADD_DEPENDENCY) { + if (! dep_type_set) { + if (strcmp(argv[i], "regular") == 0) { + dep_type = dependency_type::REGULAR; + } + else if (strcmp(argv[i], "milestone") == 0) { + dep_type = dependency_type::MILESTONE; + } + else if (strcmp(argv[i], "waits-for") == 0) { + dep_type = dependency_type::WAITS_FOR; + } + else { + show_help = true; + break; + } + dep_type_set = true; + } + else if (service_name == nullptr) { + service_name = argv[i]; + } + else if (to_service_name == nullptr) { + to_service_name = argv[i]; + } + else { + show_help = true; + break; + } + } + else { + if (service_name != nullptr) { + show_help = true; + break; + } + service_name = argv[i]; + // TODO support multiple services } - service_name = argv[i]; - // TODO support multiple services } } @@ -152,6 +191,11 @@ int main(int argc, char **argv) show_help = true; } + if (command == command_t::ADD_DEPENDENCY && (! dep_type_set || service_name == nullptr + || to_service_name == nullptr)) { + show_help = true; + } + if (show_help) { cout << "dinitctl: control Dinit services" << endl; @@ -164,6 +208,7 @@ int main(int argc, char **argv) cout << " dinitctl unload : unload the service" << endl; cout << " dinitctl list : list loaded services" << endl; cout << " dinitctl shutdown : stop all services and terminate dinit" << endl; + cout << " dinitctl add-dep : add a dependency between services" << endl; cout << "\nNote: An activated service continues running when its dependents stop." << endl; @@ -243,6 +288,9 @@ int main(int argc, char **argv) else if (command == command_t::SHUTDOWN) { return shutdown_dinit(socknum, rbuffer); } + else if (command == command_t::ADD_DEPENDENCY) { + return add_dependency(socknum, rbuffer, service_name, to_service_name, dep_type); + } else { return start_stop_service(socknum, rbuffer, service_name, command, do_pin, wait_for_service, verbose); @@ -643,6 +691,58 @@ static int list_services(int socknum, cpbuffer_t &rbuffer) return 0; } +static int add_dependency(int socknum, cpbuffer_t &rbuffer, char *service_from, char *service_to, + dependency_type dep_type) +{ + using namespace std; + + // First find the "from" service: + if (issue_load_service(socknum, service_from, true) == 1) { + return 1; + } + + wait_for_reply(rbuffer, socknum); + + handle_t from_handle; + + if (check_load_reply(socknum, rbuffer, &from_handle, nullptr) != 0) { + return 1; + } + + // Then find or load the "to" service: + if (issue_load_service(socknum, service_to, true) == 1) { + return 1; + } + + wait_for_reply(rbuffer, socknum); + + handle_t to_handle; + + if (check_load_reply(socknum, rbuffer, &to_handle, nullptr) != 0) { + return 1; + } + + constexpr int pktsize = 2 + sizeof(handle_t) * 2; + char cmdbuf[pktsize] = { (char)DINIT_CP_ADD_DEP, (char)dep_type}; + memcpy(cmdbuf + 2, &from_handle, sizeof(from_handle)); + memcpy(cmdbuf + 2 + sizeof(from_handle), &to_handle, sizeof(to_handle)); + write_all_x(socknum, cmdbuf, pktsize); + + wait_for_reply(rbuffer, socknum); + + // check reply + if (rbuffer[0] == DINIT_RP_NAK) { + cerr << "dinitctl: Could not add dependency: circular dependency or wrong state" << endl; + return 1; + } + if (rbuffer[0] != DINIT_RP_ACK) { + cerr << "dinitctl: Control socket protocol error" << endl; + return 1; + } + + return 0; +} + static int shutdown_dinit(int socknum, cpbuffer_t &rbuffer) { // TODO support no-wait option. diff --git a/src/includes/service-constants.h b/src/includes/service-constants.h index 884a487..1716cf2 100644 --- a/src/includes/service-constants.h +++ b/src/includes/service-constants.h @@ -53,4 +53,12 @@ enum class stopped_reason_t TERMINATED // process terminated }; +enum class dependency_type +{ + REGULAR, + SOFT, // dependency starts in parallel, failure/stop does not affect dependent + WAITS_FOR, // as for SOFT, but dependent waits until dependency starts/fails before starting + MILESTONE // dependency must start successfully, but once started the dependency becomes soft +}; + #endif diff --git a/src/includes/service.h b/src/includes/service.h index 0d3acc7..4c3c982 100644 --- a/src/includes/service.h +++ b/src/includes/service.h @@ -176,14 +176,6 @@ class service_record; class service_set; class base_process_service; -enum class dependency_type -{ - REGULAR, - SOFT, // dependency starts in parallel, failure/stop does not affect dependent - WAITS_FOR, // as for SOFT, but dependent waits until dependency starts/fails before starting - MILESTONE // dependency must start successfully, but once started the dependency becomes soft -}; - /* Service dependency record */ class service_dep { -- 2.25.1