Browse Source

Clarify chain-to details. Fix implementation.

Don't chain-to if the initiating service will restart, if it didn't exit
cleanly, if it was explicitly stopped, or if a shutdown is in progress.
Davin McCall 3 years ago
parent
commit
4e3272fe39
3 changed files with 28 additions and 10 deletions
  1. 15 7
      doc/manpages/dinit-service.5.m4
  2. 6 1
      src/includes/service-constants.h
  3. 7 2
      src/service.cc

+ 15 - 7
doc/manpages/dinit-service.5.m4

@@ -24,9 +24,12 @@ searches \fI/etc/dinit.d\fR, \fI/usr/local/lib/dinit.d\fR and
 All services have a \fItype\fR and a set of \fIdependencies\fR. Service
 types are discussed in the following subsection. If a service depends on
 another service, then starting the first service causes the second to start
-also (and the second service must complete its startup before the first
-is considered started). Similarly, if one service depends on another which
-becomes stopped, the first service must also stop.
+also; depending on the type of dependency, if the second service fails to
+start, the dependent may have its startup aborted. For a service which will
+represent a running process, the process is not generally launched until the
+dependencies are all satisfied. If a service has a hard dependency on another
+which becomes stopped, the dependent service must also stop. A service
+process will not be signaled to terminate until the service's dependents have stopped.
 .\"
 .SS SERVICE TYPES
 .\"
@@ -180,10 +183,10 @@ The directory path, if not absolute, is relative to the directory containing
 the service description file.
 .TP
 \fBchain-to\fR = \fIservice-name\fR
-When this service completes successfully (i.e. starts and then stops), the
-named service should be started. Note that the named service is not loaded
-until that time; naming an invalid service will not cause this service to
-fail to load.
+When this service terminates (i.e. starts successfully, and then stops of its
+own accord), the named service should be started. Note that the named service
+is not loaded until that time; naming an invalid service will not cause this
+service to fail to load.
 
 This can be used for a service that supplies an interactive "recovery mode"
 for another service; once the user exits the recovery shell, the primary
@@ -192,6 +195,11 @@ multi-stage system startup where later service description files reside on
 a separate filesystem that is mounted during the first stage; such service
 descriptions will not be found at initial start, and so cannot be started
 directly, but can be chained via this directive.
+
+The chain is not executed if the initial service was explicitly stopped, stopped
+due to a dependency stopping (for any reason), if it will restart (including due
+to a dependent restarting), or if its process terminates abnormally or with an
+exit status indicating an error.
 .TP
 \fBsocket\-listen\fR = \fIsocket-path\fR
 Pre-open a socket for the service and pass it to the service using the

+ 6 - 1
src/includes/service-constants.h

@@ -50,10 +50,15 @@ enum class stopped_reason_t
 	EXECFAILED, // failed to start (couldn't launch process)
     TIMEDOUT,  // timed out when starting
 
-    // Failure after starting:
+    // Failure(?) after starting:
     TERMINATED // process terminated
 };
 
+inline bool did_finish(stopped_reason_t reason)
+{
+    return reason == stopped_reason_t::TERMINATED;
+}
+
 enum class dependency_type
 {
     REGULAR,

+ 7 - 2
src/service.cc

@@ -113,8 +113,13 @@ void service_record::stopped() noexcept
     if (! start_failed) {
         log_service_stopped(service_name);
 
-        // If this service chains to another, start the other service now:
-        if (! will_restart && ! start_on_completion.empty() && ! services->is_shutting_down()) {
+        // If this service chains to another, start the chained service now, if:
+        // - this service self-terminated (rather than being stopped),
+        // - ... successfully (i.e. exit code 0)
+        // - this service won't restart, and
+        // - a shutdown isn't in progress
+        if (did_finish(stop_reason) && get_exit_status() == 0 && ! will_restart
+                && ! start_on_completion.empty() && ! services->is_shutting_down()) {
             try {
                 auto chain_to = services->load_service(start_on_completion.c_str());
                 chain_to->start();