dinitctl: add-dep subcommand, adds dependency between services.
authorDavin McCall <davmac@davmac.org>
Mon, 2 Jul 2018 18:24:09 +0000 (19:24 +0100)
committerDavin McCall <davmac@davmac.org>
Mon, 2 Jul 2018 20:30:54 +0000 (21:30 +0100)
doc/manpages/dinitctl.8
src/dinitctl.cc
src/includes/service-constants.h
src/includes/service.h

index a3e8475d1d9ad8dcc92d096a186285c9c05d36de..464da77f9fb041b315d78c8167f4d7c33467fa3b 100644 (file)
@@ -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
 .\"
index 7016ea1e56dfb1b3f47f5b75301d741b646449e0..4e7f3f0dc41f1b70f1464ef14f9098c1c8cc9133 100644 (file)
@@ -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 <service-name>                    : 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 <type> <from-service> <to-service> : 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.
index 884a487fad5799556344d53316947be65311b4f3..1716cf2bfe779184bb6f3895029317eb47450898 100644 (file)
@@ -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
index 0d3acc7e475955b427be93ec1afd15b592c2068b..4c3c9824dde328f302b43edff3cfcb65dcb06b55 100644 (file)
@@ -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
 {