Implement "skippable" option for scripted services.
authorDavin McCall <davmac@davmac.org>
Thu, 14 Jun 2018 09:37:39 +0000 (10:37 +0100)
committerDavin McCall <davmac@davmac.org>
Thu, 14 Jun 2018 17:02:53 +0000 (18:02 +0100)
This causes a service start that is skipped via interruption (eg ^C) to
be considered as started for dependency purposes.

doc/linux/services/late-filesystems.sh
doc/linux/services/rootfscheck
doc/manpages/dinit-service.5
src/includes/service.h
src/load-service.cc
src/proc-service.cc
src/service.cc

index 3962e4edb9f87cf61db3089bc1d5b2f512186564..454d12f1060c4e73e38cbfcc62e39e2befef42c1 100755 (executable)
@@ -5,8 +5,12 @@ if [ "$1" = start ]; then
     PATH=/usr/bin:/usr/sbin:/bin:/sbin
 
     fsck -a /dev/sdb2
-    mount /dev/sdb2 /mnt/sdb2
-    mount --bind /mnt/sdb2/src /usr/src
-    mount --bind /mnt/sdb2 /mnt/tmp  # hopefully can remove this at some point
+    fsckresult=$?
+    if [ $fsckresult -eq 0 ]; then
+       mount /dev/sdb2 /mnt/sdb2
+       exit $?
+    else
+        exit $fsckresult
+    fi
 
 fi
index fd262280a7f870df98bd5591a27efbda376254f6..48a8d6cc0696b27fad4d7ac55a0c6a07ab27d42e 100644 (file)
@@ -3,7 +3,7 @@
 type = scripted
 command = /etc/dinit.d/rootfscheck.sh start
 restart = false
-options = starts-on-console pass-cs-fd start-interruptible
+options = starts-on-console pass-cs-fd start-interruptible skippable
 start-timeout = 0  # unlimited
 
 depends-on = early-filesystems
index 5b2c02a140f40ffb86867dc1649cad900e20d205..4c735b8508bcbf997122e1305e5d3930f205c25d 100644 (file)
@@ -217,13 +217,13 @@ These options are specified via the \fBoptions\fR parameter.
 .\"
 .TP
 \fBno\-sigterm\fR
-specifies that the TERM signal should not be send to the process to terminate
+Specifies that the TERM signal should not be send to the process to terminate
 it. (Another signal can be specified using the \fBtermsignal\fR setting; if no
 other signal is specified, no signal will be sent, which usually means that
 the service will not terminate).
 .TP
 \fBruns\-on\-console\fR
-specifies that this service uses the console; its input and output should be
+Specifies that this service uses the console; its input and output should be
 directed to the console (or precisely, to the device to which Dinit's standard
 output stream is connected). A service running on the console prevents other
 services from running on the console (they will queue for the console).
@@ -233,7 +233,7 @@ services that run on the console. Handling of an interrupt is determined by
 the service process, but typically will cause it to terminate.
 .TP
 \fBstarts\-on\-console\fR
-specifies that this service uses the console during service startup. This is
+Specifies that this service uses the console during service startup. This is
 implied by \fBruns-on-console\fR, but can be specified separately for services
 that need the console while they start but not afterwards.
 
@@ -243,16 +243,16 @@ interrupting startup via the \fIinterrupt\fR key (normally control-C). This is
 useful to allow filesystem checks to be interrupted/skipped.
 .TP
 \fBstarts-rwfs\fR
-this service mounts the root filesystem read/write (or at least mounts the
+This service mounts the root filesystem read/write (or at least mounts the
 normal writable filesystems for the system). This prompts Dinit to create its
 control socket, if it has not already managed to do so.
 .TP
 \fBstarts-log\fR
-this service starts the system log daemon. Dinit will begin logging via the
+This service starts the system log daemon. Dinit will begin logging via the
 \fI/dev/log\fR socket.
 .TP
 \fBpass-cs-fd\fR
-pass an open Dinit control socket to the process when launching it (the
+Pass an open Dinit control socket to the process when launching it (the
 \fIDINIT_CS_FD\fR environment variable will be set to the file descriptor of
 the socket). This allows the service to issue commands to Dinit even if the
 regular control socket is not available yet.
@@ -263,9 +263,15 @@ should not use this option unless the service is designed to receive a Dinit
 control socket.
 .TP
 \fBstart-interruptible\fR
-this service can have its startup interrupted (cancelled) if it becomes inactive
+This service can have its startup interrupted (cancelled) if it becomes inactive
 while still starting, by sending it the SIGINT signal. This is meaningful only
 for \fBbgprocess\fR and \fBscripted\fR services.
+.TP
+\fBskippable\fR
+For scripted services, indicates that if the service startup process terminates
+via an interrupt signal (SIGINT), then the service should be considered started.
+Note that if the interrupt was issued by Dinit to cancel startup, the service
+will instead be considered stopped.
 .RE
 .LP
 The next section contains example service descriptions including some of the
@@ -298,7 +304,8 @@ Here is an examples for a filesystem check "service", run by a script
 system, but the control socket may not have been created, so it uses the
 \fBpass-cs-fd\fR option to allow the \fBreboot\fR command to issue control
 commands to Dinit. It runs on the console, so that output is visible and
-the process can be interrupted using control-C.
+the process can be interrupted using control-C, in which case the check is
+skipped but dependent services continue to start.
 
 .RS
 .nf
@@ -308,7 +315,7 @@ the process can be interrupted using control-C.
 type = scripted
 command = /etc/dinit.d/rootfscheck.sh
 restart = false
-options = starts-on-console pass-cs-fd
+options = starts-on-console pass-cs-fd start-interruptible skippable
 depends-on = early-filesystems  # /proc and /dev
 depends-on = device-node-daemon
 .ft
index fabe302254e3a06505e4c80ff80ede7718810a20..54dca7e3e58240e715dc23c413192711820e7836 100644 (file)
  */
 
 struct onstart_flags_t {
+    // on-start flags:
     bool rw_ready : 1;  // file system should be writable once this service starts
     bool log_ready : 1; // syslog should be available once this service starts
     
-    // Not actually "onstart" commands:
+    // Other service options flags:
     bool no_sigterm : 1;  // do not send SIGTERM
     bool runs_on_console : 1;  // run "in the foreground"
     bool starts_on_console : 1; // starts in the foreground
     bool pass_cs_fd : 1;  // pass this service a control socket connection via fd
+    bool skippable : 1;   // if interrupted the service is skipped (scripted services)
     
     onstart_flags_t() noexcept : rw_ready(false), log_ready(false),
-            no_sigterm(false), runs_on_console(false), starts_on_console(false), pass_cs_fd(false)
+            no_sigterm(false), runs_on_console(false), starts_on_console(false),
+            pass_cs_fd(false), skippable(false)
     {
     }
 };
@@ -286,6 +289,7 @@ class service_record
 
     bool restarting   : 1;      // re-starting after unexpected termination
     bool start_failed : 1;      // failed to start (reset when begins starting)
+    bool start_skipped : 1;     // start was skipped by interrupt
     
     int required_by = 0;        // number of dependents wanting this service to be started
 
@@ -458,7 +462,7 @@ class service_record
             waiting_for_console(false), have_console(false), waiting_for_execstat(false),
             start_explicit(false), prop_require(false), prop_release(false), prop_failure(false),
             prop_start(false), prop_stop(false), restarting(false), start_failed(false),
-            force_stop(false)
+            start_skipped(false), force_stop(false)
     {
         services = set;
         service_name = name;
@@ -565,11 +569,22 @@ class service_record
     // commence starting/stopping.
     void unpin() noexcept;
     
+    // Is this a dummy service (used only when loading a new service)?
     bool is_dummy() noexcept
     {
         return record_type == service_type_t::DUMMY;
     }
     
+    bool did_start_fail() noexcept
+    {
+        return start_failed;
+    }
+
+    bool was_start_skipped() noexcept
+    {
+        return start_skipped;
+    }
+
     // Add a listener. A listener must only be added once. May throw std::bad_alloc.
     void add_listener(service_listener * listener)
     {
index 15d882a22455c174b8de2f9fe0229157e941f919..108879f825de08dc4bdca13ef5d215aa1c0fbcef 100644 (file)
@@ -601,6 +601,9 @@ service_record * dirload_service_set::load_service(const char * name)
                         else if (option_txt == "start-interruptible") {
                             start_is_interruptible = true;
                         }
+                        else if (option_txt == "skippable") {
+                            onstart_flags.skippable = true;
+                        }
                         else {
                             throw service_description_exc(name, "Unknown option: " + option_txt);
                         }
index 7651ec71366b98c94a7b050f529e433387203b21..f2e0b0df9cc3b2ee92452f317a200afe95dcf203 100644 (file)
@@ -348,6 +348,12 @@ void scripted_service::handle_exit_status(bp_sys::exit_status exit_status) noexc
         if (exit_status.did_exit_clean()) {
             started();
         }
+        else if (was_signalled && exit_status.get_term_sig() == SIGINT && onstart_flags.skippable) {
+            // A skippable service can be skipped by interrupting (eg by ^C if the service
+            // starts on the console).
+            start_skipped = true;
+            started();
+        }
         else {
             // failed to start
             if (did_exit) {
index 40060a7df47e5b7e188f159c285606f9866b892e..8aeb176025d0362550b784577d608cedabf87bd3 100644 (file)
@@ -192,6 +192,7 @@ void service_record::start(bool activate) noexcept
     }
 
     start_failed = false;
+    start_skipped = false;
     service_state = service_state_t::STARTING;
     waiting_for_deps = true;