Browse Source

Add "--ignore-unstarted" option to dinitctl

This allows issuing a restart command for a non-existent or already
stopped service, and having the command "succeed" rather than fail.

(If the service is started, it will be restarted, and success will
depend on whether the service starts successfully).
Davin McCall 3 years ago
parent
commit
bc23b8ee76
4 changed files with 56 additions and 23 deletions
  1. 7 3
      doc/manpages/dinitctl.8.m4
  2. 3 3
      src/control.cc
  3. 44 17
      src/dinitctl.cc
  4. 2 0
      src/includes/control-cmds.h

+ 7 - 3
doc/manpages/dinitctl.8.m4

@@ -9,16 +9,16 @@ dinitctl \- control services supervised by Dinit
 [\fIoptions\fR] \fBstart\fR [\fB\-\-no\-wait\fR] [\fB\-\-pin\fR] \fIservice-name\fR
 .br
 .B dinitctl
-[\fIoptions\fR] \fBstop\fR [\fB\-\-no\-wait\fR] [\fB\-\-pin\fR] \fIservice-name\fR
+[\fIoptions\fR] \fBstop\fR [\fB\-\-no\-wait\fR] [\fB\-\-pin\fR] [\fB\-\-ignore\-unstarted\fR] \fIservice-name\fR
 .br
 .B dinitctl
-[\fIoptions\fR] \fBrestart\fR [\fB\-\-no\-wait\fR] \fIservice-name\fR
+[\fIoptions\fR] \fBrestart\fR [\fB\-\-no\-wait\fR] [\fB\-\-ignore\-unstarted\fR] \fIservice-name\fR
 .br
 .B dinitctl
 [\fIoptions\fR] \fBwake\fR [\fB\-\-no\-wait\fR] \fIservice-name\fR
 .br
 .B dinitctl
-[\fIoptions\fR] \fBrelease\fR \fIservice-name\fR
+[\fIoptions\fR] \fBrelease\fR [\fB\-\-ignore\-unstarted\fR] \fIservice-name\fR
 .br
 .B dinitctl
 [\fIoptions\fR] \fBunpin\fR \fIservice-name\fR
@@ -102,6 +102,10 @@ started will have no effect other than removing explicit activation.
 \fB\-\-force\fR
 Stop the service even if it will require stopping other services which depend on the specified service.
 .TP
+\fB\-\-ignore\-unstarted\fR
+If the service is not started or doesn't exist, ignore the command and return an exit code indicating
+success.
+.TP
 \fIservice-name\fR
 Specifies the name of the service to which the command applies.
 .TP

+ 3 - 3
src/control.cc

@@ -249,7 +249,7 @@ bool control_conn_t::process_start_stop(int pktType)
         case DINIT_CP_STARTSERVICE:
             // start service, mark as required
             if (services->is_shutting_down()) {
-                ack_buf[0] = DINIT_RP_NAK;
+                ack_buf[0] = DINIT_RP_SHUTTINGDOWN;
                 break;
             }
             if ((service->get_state() == service_state_t::STOPPED
@@ -269,7 +269,7 @@ bool control_conn_t::process_start_stop(int pktType)
             bool do_restart = ((rbuf[1] & 4) == 4);
             bool gentle = ((rbuf[1] & 2) == 2) || do_restart;  // restart is always "gentle"
             if (do_restart && services->is_shutting_down()) {
-                ack_buf[0] = DINIT_RP_NAK;
+                ack_buf[0] = DINIT_RP_SHUTTINGDOWN;
                 break;
             }
             if ((service->get_state() == service_state_t::STARTED
@@ -311,7 +311,7 @@ bool control_conn_t::process_start_stop(int pktType)
         {
             // re-attach a service to its (started) dependents, causing it to start.
             if (services->is_shutting_down()) {
-                ack_buf[0] = DINIT_RP_NAK;
+                ack_buf[0] = DINIT_RP_SHUTTINGDOWN;
                 break;
             }
             if ((service->get_state() == service_state_t::STOPPED

+ 44 - 17
src/dinitctl.cc

@@ -36,9 +36,10 @@ static constexpr uint16_t max_cp_version = 1;
 enum class command_t;
 
 static int issue_load_service(int socknum, const char *service_name, bool find_only = false);
-static int check_load_reply(int socknum, cpbuffer_t &, handle_t *handle_p, service_state_t *state_p);
+static int check_load_reply(int socknum, cpbuffer_t &, handle_t *handle_p, service_state_t *state_p,
+        bool write_error=true);
 static int start_stop_service(int socknum, cpbuffer_t &, const char *service_name, command_t command,
-        bool do_pin, bool do_force, bool wait_for_service, bool verbose);
+        bool do_pin, bool do_force, bool wait_for_service, bool ignore_unstarted, bool verbose);
 static int unpin_service(int socknum, cpbuffer_t &, const char *service_name, bool verbose);
 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);
@@ -77,6 +78,10 @@ enum class command_t {
     DISABLE_SERVICE
 };
 
+class dinit_protocol_error
+{
+    // no body
+};
 
 // Entry point.
 int main(int argc, char **argv)
@@ -97,6 +102,7 @@ int main(int argc, char **argv)
     bool wait_for_service = true;
     bool do_pin = false;
     bool do_force = false;
+    bool ignore_unstarted = false;
     
     command_t command = command_t::NONE;
         
@@ -113,6 +119,9 @@ int main(int argc, char **argv)
             else if (strcmp(argv[i], "--no-wait") == 0) {
                 wait_for_service = false;
             }
+            else if (strcmp(argv[i], "--ignore-unstarted") == 0) {
+                ignore_unstarted = true;
+            }
             else if (strcmp(argv[i], "--quiet") == 0) {
                 verbose = false;
             }
@@ -388,7 +397,7 @@ int main(int argc, char **argv)
         }
         else {
             return start_stop_service(socknum, rbuffer, service_name, command, do_pin, do_force,
-                    wait_for_service, verbose);
+                    wait_for_service, ignore_unstarted, verbose);
         }
     }
     catch (cp_old_client_exception &e) {
@@ -407,6 +416,10 @@ int main(int argc, char **argv)
         cerr << "dinitctl: control socket write error: " << std::strerror(e.errcode) << endl;
         return 1;
     }
+    catch (dinit_protocol_error &e) {
+        cerr << "dinitctl: protocol error" << endl;
+        return 1;
+    }
 }
 
 // Extract/read a string of specified length from the buffer/socket. The string is consumed
@@ -445,8 +458,9 @@ static std::string read_string(int socknum, cpbuffer_t &rbuffer, uint32_t length
 //      name     - the name of the service to load
 //      handle   - where to store the handle of the loaded service
 //      state    - where to store the state of the loaded service (may be null).
+//      write_error - whether to write an error message if the service can't be loaded
 static bool load_service(int socknum, cpbuffer_t &rbuffer, const char *name, handle_t *handle,
-        service_state_t *state)
+        service_state_t *state, bool write_error=true)
 {
     // Load 'to' service:
     if (issue_load_service(socknum, name)) {
@@ -455,7 +469,7 @@ static bool load_service(int socknum, cpbuffer_t &rbuffer, const char *name, han
 
     wait_for_reply(rbuffer, socknum);
 
-    if (check_load_reply(socknum, rbuffer, handle, state) != 0) {
+    if (check_load_reply(socknum, rbuffer, handle, state, write_error) != 0) {
         return false;
     }
 
@@ -512,7 +526,8 @@ static std::string get_service_name(int socknum, cpbuffer_t &rbuffer, handle_t h
 
 // Start/stop a service
 static int start_stop_service(int socknum, cpbuffer_t &rbuffer, const char *service_name,
-        command_t command, bool do_pin, bool do_force, bool wait_for_service, bool verbose)
+        command_t command, bool do_pin, bool do_force, bool wait_for_service, bool ignore_unstarted,
+        bool verbose)
 {
     using namespace std;
 
@@ -521,8 +536,13 @@ static int start_stop_service(int socknum, cpbuffer_t &rbuffer, const char *serv
     service_state_t state;
     handle_t handle;
     
-    if (! load_service(socknum, rbuffer, service_name, &handle, &state)) {
-        return 1;
+    if (command != command_t::RESTART_SERVICE && command != command_t::STOP_SERVICE
+            && command != command_t::RELEASE_SERVICE) {
+        ignore_unstarted = false;
+    }
+
+    if (! load_service(socknum, rbuffer, service_name, &handle, &state, !ignore_unstarted)) {
+        return ignore_unstarted ? 0 : 1;
     }
 
     service_state_t wanted_state = do_stop ? service_state_t::STOPPED : service_state_t::STARTED;
@@ -606,15 +626,21 @@ static int start_stop_service(int socknum, cpbuffer_t &rbuffer, const char *serv
             return 1;
         }
         if (reply_pkt_h == DINIT_RP_NAK && command == command_t::RESTART_SERVICE) {
-            cerr << "dinitctl: cannot restart service; service not started (or system is shutting down).\n";
+            if (ignore_unstarted) {
+                if (verbose) {
+                    cout << "Service '" << service_name << "' is not currently started.\n";
+                }
+                return 0;
+            }
+            cerr << "dinitctl: cannot restart service; service not started.\n";
             return 1;
         }
-        if (reply_pkt_h == DINIT_RP_NAK && command == command_t::START_SERVICE) {
-            cerr << "dinitctl: cannot start service (during shut down).\n";
+        if (reply_pkt_h == DINIT_RP_NAK && command == command_t::WAKE_SERVICE) {
+            cerr << "dinitctl: service has no active dependents, cannot wake.\n";
             return 1;
         }
-        if (reply_pkt_h == DINIT_RP_NAK && command == command_t::WAKE_SERVICE) {
-            cerr << "dinitctl: service has no active dependents (or system is shutting down), cannot wake.\n";
+        if (reply_pkt_h == DINIT_RP_SHUTTINGDOWN) {
+            cerr << "dinitctl: cannot start/restart/wake service, shutdown is in progress.\n";
             return 1;
         }
         if (reply_pkt_h != DINIT_RP_ACK && reply_pkt_h != DINIT_RP_ALREADYSS) {
@@ -717,7 +743,7 @@ static int issue_load_service(int socknum, const char *service_name, bool find_o
 
 // Check that a "load service" reply was received, and that the requested service was found.
 //   state_p may be null.
-static int check_load_reply(int socknum, cpbuffer_t &rbuffer, handle_t *handle_p, service_state_t *state_p)
+static int check_load_reply(int socknum, cpbuffer_t &rbuffer, handle_t *handle_p, service_state_t *state_p, bool write_error)
 {
     using namespace std;
     
@@ -730,12 +756,13 @@ static int check_load_reply(int socknum, cpbuffer_t &rbuffer, handle_t *handle_p
         return 0;
     }
     else if (rbuffer[0] == DINIT_RP_NOSERVICE) {
-        cerr << "dinitctl: failed to find/load service." << endl;
+        if (write_error) {
+            cerr << "dinitctl: failed to find/load service." << endl;
+        }
         return 1;
     }
     else {
-        cerr << "dinitctl: protocol error." << endl;
-        return 1;
+        throw dinit_protocol_error();
     }
 }
 

+ 2 - 0
src/includes/control-cmds.h

@@ -95,6 +95,8 @@ constexpr static int DINIT_RP_SERVICENAME = 66;
 constexpr static int DINIT_RP_PINNEDSTOPPED = 67;
 constexpr static int DINIT_RP_PINNEDSTARTED = 68;
 
+// Shutdown is in progress, can't start/restart/wake service:
+constexpr static int DINIT_RP_SHUTTINGDOWN = 69;
 
 // Information: