Browse Source

dinitctl: add is-active and is-failed commands

The is-active command makes dinitctl return 0 when the given
service is currently active, and 1 in any other scenario. The
is-failed command makes it return 0 when the given service has
failed to start and is currently stopped, and 1 in any other
scenario. The service not existing and so on counts as non-success
for both commands.

The purpose of these is to use them in scripts, as parsing status
output is clunky. In general it is enough to pass --quiet and just
check the status code, but both commands also print the current
service status (and nothing else) if in default verbose mode.
Daniel Kolesa 1 year ago
parent
commit
6c0ca720ec
2 changed files with 80 additions and 7 deletions
  1. 19 0
      doc/manpages/dinitctl.8.m4
  2. 61 7
      src/dinitctl.cc

+ 19 - 0
doc/manpages/dinitctl.8.m4

@@ -18,6 +18,12 @@ dinitctl \- control services supervised by Dinit
 [\fIoptions\fR] \fBstatus\fR \fIservice-name\fR
 .HP
 .B dinitctl
+[\fIoptions\fR] \fBis-active\fR \fIservice-name\fR
+.HP
+.B dinitctl
+[\fIoptions\fR] \fBis-failed\fR \fIservice-name\fR
+.HP
+.B dinitctl
 [\fIoptions\fR] \fBrestart\fR [\fB\-\-no\-wait\fR] [\fB\-\-ignore\-unstarted\fR] \fIservice-name\fR
 .HP
 .B dinitctl
@@ -165,6 +171,19 @@ ID (pid) if applicable.
 If the service is stopped for any reason other than a normal stop, the reason for the service
 stopping will be displayed (along with any further relevant information, if available).
 .TP
+\fBis-active\fR
+Check if the specified service is currently active.
+The service counts as active if it is known it is currently started. Any other state, including
+protocol and parse errors, will exit without returning success. Unless quiet, the current service
+status (STOPPED, STARTING, STARTED, STOPPING) is printed out to standard output.
+.TP
+\fBis-failed\fR
+Check if the specified service is currently failed.
+The service counts as failed if it is known it is currently stopped either because of startup
+failure, timeout or dependency failure. Any other state, including protocol and parse errors,
+will exit without returning success. Unless quiet, the current srevice status is printed out
+to standard output like with \fBis-active\fR.
+.TP
 \fBrestart\fR
 Restart the specified service. The service will be stopped and then restarted, without affecting explicit
 activation status or dependency links from dependents.

+ 61 - 7
src/dinitctl.cc

@@ -44,7 +44,7 @@ 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, bool verbose);
 static int reload_service(int socknum, cpbuffer_t &, const char *service_name, bool verbose);
 static int list_services(int socknum, cpbuffer_t &);
-static int service_status(int socknum, cpbuffer_t &rbuffer, const char *service_name);
+static int service_status(int socknum, cpbuffer_t &rbuffer, const char *service_name, command_t command, bool verbose);
 static int shutdown_dinit(int soclknum, cpbuffer_t &, bool verbose);
 static int add_remove_dependency(int socknum, cpbuffer_t &rbuffer, bool add, const char *service_from,
         const char *service_to, dependency_type dep_type, bool verbose);
@@ -84,7 +84,9 @@ enum class command_t {
     SETENV,
     SET_TRIGGER,
     UNSET_TRIGGER,
-	CAT_LOG,
+    CAT_LOG,
+    IS_ACTIVE,
+    IS_FAILED,
 };
 
 class dinit_protocol_error
@@ -206,6 +208,12 @@ int dinitctl_main(int argc, char **argv)
             else if (strcmp(argv[i], "status") == 0) {
                 command = command_t::SERVICE_STATUS;
             }
+            else if (strcmp(argv[i], "is-active") == 0) {
+                command = command_t::IS_ACTIVE;
+            }
+            else if (strcmp(argv[i], "is-failed") == 0) {
+                command = command_t::IS_FAILED;
+            }
             else if (strcmp(argv[i], "shutdown") == 0) {
                 command = command_t::SHUTDOWN;
             }
@@ -326,6 +334,8 @@ int dinitctl_main(int argc, char **argv)
           "\n"
           "Usage:\n"
           "    dinitctl [options] status <service-name>\n"
+          "    dinitctl [options] is-active <service-name>\n"
+          "    dinitctl [options] is-failed <service-name>\n"
           "    dinitctl [options] start [options] <service-name>\n"
           "    dinitctl [options] stop [options] <service-name>\n"
           "    dinitctl [options] restart [options] <service-name>\n"
@@ -413,8 +423,8 @@ int dinitctl_main(int argc, char **argv)
         else if (command == command_t::LIST_SERVICES) {
             return list_services(socknum, rbuffer);
         }
-        else if (command == command_t::SERVICE_STATUS) {
-            return service_status(socknum, rbuffer, service_name);
+        else if (command == command_t::SERVICE_STATUS || command == command_t::IS_ACTIVE || command == command_t::IS_FAILED) {
+            return service_status(socknum, rbuffer, service_name, command, verbose);
         }
         else if (command == command_t::SHUTDOWN) {
             return shutdown_dinit(socknum, rbuffer, verbose);
@@ -1164,10 +1174,12 @@ static int list_services(int socknum, cpbuffer_t &rbuffer)
     return 0;
 }
 
-static int service_status(int socknum, cpbuffer_t &rbuffer, const char *service_name)
+static int service_status(int socknum, cpbuffer_t &rbuffer, const char *service_name, command_t command, bool verbose)
 {
     using namespace std;
 
+    bool is_status = command == command_t::SERVICE_STATUS;
+
     if (issue_load_service(socknum, service_name, true) == 1) {
         return 1;
     }
@@ -1177,11 +1189,13 @@ static int service_status(int socknum, cpbuffer_t &rbuffer, const char *service_
     handle_t handle;
 
     if (rbuffer[0] == DINIT_RP_NOSERVICE) {
-        cerr << "dinitctl: service not loaded." << endl;
+        if (is_status) {
+            cerr << "dinitctl: service not loaded." << endl;
+        }
         return 1;
     }
 
-    if (check_load_reply(socknum, rbuffer, &handle, nullptr) != 0) {
+    if (check_load_reply(socknum, rbuffer, &handle, nullptr, is_status) != 0) {
         return 1;
     }
 
@@ -1223,6 +1237,46 @@ static int service_status(int socknum, cpbuffer_t &rbuffer, const char *service_
             rbuffer.extract((char *)&exit_status, 6, sizeof(exit_status));
         }
 
+        switch (command) {
+        case command_t::IS_ACTIVE:
+        case command_t::IS_FAILED:
+            if (verbose) {
+                switch (current) {
+                case service_state_t::STOPPED:
+                    cout << "STOPPED" << endl;
+                    break;
+                case service_state_t::STARTING:
+                    cout << "STARTING" << endl;
+                    break;
+                case service_state_t::STARTED:
+                    cout << "STARTED" << endl;
+                    break;
+                case service_state_t::STOPPING:
+                    cout << "STOPPING" << endl;
+                }
+            }
+            /* return 0 (success) for started */
+            if (command == command_t::IS_ACTIVE) {
+                return current != service_state_t::STARTED;
+            }
+            /* return 0 (success) for specific stopped reasons */
+            if (current == service_state_t::STOPPED) {
+                switch (stop_reason) {
+                case stopped_reason_t::DEPFAILED:
+                case stopped_reason_t::FAILED:
+                case stopped_reason_t::EXECFAILED:
+                case stopped_reason_t::TIMEDOUT:
+                    return 0;
+                default:
+                    break;
+                }
+            }
+            return 1;
+        default:
+            /* status */
+            break;
+        }
+
         cout << "Service: " << service_name << "\n"
                 "    State: ";