Browse Source

Add kill-all-on-stop service option and documentation

"Before stopping this service, send a TERM signal and then (after a
short pause) a KILL signal to all other processes in the system,
forcibly terminating them. This option is intended to allow system
shutdown scripts to run without any possible interference from
"leftover" or orphaned processes (for example, unmounting file systems
usually requires that the file systems are no longer in use).

This option must be used with care since the signal broadcast does not
discriminate and potentially kills other services (or their shutdown
scripts); a strict dependency ordering is suggested, i.e. every other
service should either be a (possibly transitive) dependency or dependent
of the service with this option set.

This option can be used for scripted and internal services only."
Davin McCall 1 year ago
parent
commit
2c50757365
3 changed files with 34 additions and 8 deletions
  1. 14 3
      doc/manpages/dinit-service.5.m4
  2. 14 5
      src/includes/load-service.h
  3. 6 0
      src/service.cc

+ 14 - 3
doc/manpages/dinit-service.5.m4

@@ -512,10 +512,21 @@ sending it a signal for any reason.
 Alters behaviour of the \fBchain-to\fR property, forcing the chained service to
 always start on termination of this service (instead of only when this service
 terminates with an exit status indicating success).
+.TP
+\fBkill\-all\-on\-stop\fR
+Before stopping this service, send a TERM signal and then (after a short pause) a
+KILL signal to all other processes in the system, forcibly terminating them.
+This option is intended to allow system shutdown scripts to run without any possible
+interference from "leftover" or orphaned processes (for example, unmounting file systems usually
+requires that the file systems are no longer in use).
+.sp
+This option must be used with care since the signal broadcast does not discriminate and
+potentially kills other services (or their shutdown scripts); a strict dependency ordering
+is suggested, i.e. every other service should either be a (possibly transitive) dependency or
+dependent of the service with this option set.
+.sp
+This option can be used for scripted and internal services only.
 .RE
-.LP
-The next section contains example service descriptions including some of the
-parameters and options described above.
 .\"
 .SS RESOURCE LIMITS
 .\"

+ 14 - 5
src/includes/load-service.h

@@ -27,19 +27,20 @@ struct service_flags_t
     bool log_ready : 1; // syslog should be available once this service starts
 
     // Other service options flags:
-    bool runs_on_console : 1;  // run "in the foreground"
+    bool runs_on_console : 1;   // run "in the foreground"
     bool starts_on_console : 1; // starts in the foreground
     bool shares_console : 1;    // run on console, but not exclusively
-    bool pass_cs_fd : 1;  // pass this service a control socket connection via fd
+    bool pass_cs_fd : 1;        // pass this service a control socket connection via fd
     bool start_interruptible : 1; // the startup of this service process is ok to interrupt with SIGINT
-    bool skippable : 1;   // if interrupted the service is skipped (scripted services)
+    bool skippable : 1;         // if interrupted the service is skipped (scripted services)
     bool signal_process_only : 1;  // signal the session process, not the whole group
-    bool always_chain : 1; // always start chain-to service on exit
+    bool always_chain : 1;      // always start chain-to service on exit
+    bool kill_all_on_stop : 1;  // kill all other processes before stopping this service
 
     service_flags_t() noexcept : rw_ready(false), log_ready(false),
             runs_on_console(false), starts_on_console(false), shares_console(false),
             pass_cs_fd(false), start_interruptible(false), skippable(false), signal_process_only(false),
-            always_chain(false)
+            always_chain(false), kill_all_on_stop(false)
     {
     }
 };
@@ -860,6 +861,11 @@ class service_settings_wrapper
             }
         }
 
+        if (onstart_flags.kill_all_on_stop && service_type != service_type_t::INTERNAL
+                && service_type != service_type_t::SCRIPTED) {
+            report_error("kill-all-on-stop can only be set on scripted or internal services.");
+        }
+
         // Resolve paths via variable substitution
         {
             auto do_resolve = [&](const char *setting_name, string &setting_value) {
@@ -1057,6 +1063,9 @@ void process_service_line(settings_wrapper &settings, const char *name, string &
             else if (option_txt == "always-chain") {
                 settings.onstart_flags.always_chain = true;
             }
+            else if (option_txt == "kill-all-on-stop") {
+                settings.onstart_flags.kill_all_on_stop = true;
+            }
             else {
                 throw service_description_exc(name, "Unknown option: " + option_txt, line_num);
             }

+ 6 - 0
src/service.cc

@@ -286,6 +286,12 @@ void service_record::execute_transition() noexcept
     else if (service_state == service_state_t::STOPPING) {
         if (stop_check_dependents()) {
             waiting_for_deps = false;
+            if (onstart_flags.kill_all_on_stop) {
+                log(loglevel_t::NOTICE, true, "Sending TERM/KILL to all processes...\n");
+                kill(-1, SIGTERM);
+                sleep(1);
+                kill(-1, SIGKILL);
+            }
             bring_down();
         }
     }