Răsfoiți Sursa

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 ani în urmă
părinte
comite
4e3272fe39
3 a modificat fișierele cu 28 adăugiri și 10 ștergeri
  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
 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
 types are discussed in the following subsection. If a service depends on
 another service, then starting the first service causes the second to start
 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
 .SS SERVICE TYPES
 .\"
 .\"
@@ -180,10 +183,10 @@ The directory path, if not absolute, is relative to the directory containing
 the service description file.
 the service description file.
 .TP
 .TP
 \fBchain-to\fR = \fIservice-name\fR
 \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"
 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
 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
 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
 descriptions will not be found at initial start, and so cannot be started
 directly, but can be chained via this directive.
 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
 .TP
 \fBsocket\-listen\fR = \fIsocket-path\fR
 \fBsocket\-listen\fR = \fIsocket-path\fR
 Pre-open a socket for the service and pass it to the service using the
 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)
 	EXECFAILED, // failed to start (couldn't launch process)
     TIMEDOUT,  // timed out when starting
     TIMEDOUT,  // timed out when starting
 
 
-    // Failure after starting:
+    // Failure(?) after starting:
     TERMINATED // process terminated
     TERMINATED // process terminated
 };
 };
 
 
+inline bool did_finish(stopped_reason_t reason)
+{
+    return reason == stopped_reason_t::TERMINATED;
+}
+
 enum class dependency_type
 enum class dependency_type
 {
 {
     REGULAR,
     REGULAR,

+ 7 - 2
src/service.cc

@@ -113,8 +113,13 @@ void service_record::stopped() noexcept
     if (! start_failed) {
     if (! start_failed) {
         log_service_stopped(service_name);
         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 {
             try {
                 auto chain_to = services->load_service(start_on_completion.c_str());
                 auto chain_to = services->load_service(start_on_completion.c_str());
                 chain_to->start();
                 chain_to->start();