]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
systemd-analyze: added the verb unit-gdb to spawn and attach gdb
authorZIHCO <chizobajames21@gmail.com>
Wed, 9 Jul 2025 11:56:32 +0000 (12:56 +0100)
committerLuca Boccassi <luca.boccassi@gmail.com>
Thu, 17 Jul 2025 14:09:58 +0000 (15:09 +0100)
man/coredumpctl.xml
man/systemd-analyze.xml
shell-completion/bash/systemd-analyze
src/analyze/analyze-unit-gdb.c [new file with mode: 0644]
src/analyze/analyze-unit-gdb.h [new file with mode: 0644]
src/analyze/analyze.c
src/analyze/analyze.h
src/analyze/meson.build
src/basic/signal-util.c
src/basic/signal-util.h
src/coredump/coredumpctl.c

index 9ad7608b2770188623b5980b53678acf612cacd4..9315679f07d6fa8dce4489931d16185d9c11a4a3 100644 (file)
         <listitem><para>Use the given debugger for the <command>debug</command>
         command. If not given and <varname>$SYSTEMD_DEBUGGER</varname> is unset, then
         <citerefentry project='man-pages'><refentrytitle>gdb</refentrytitle><manvolnum>1</manvolnum></citerefentry>
-        will be used. </para>
+        will be used.</para>
 
         <xi:include href="version-info.xml" xpointer="v239"/></listitem>
       </varlistentry>
index d59967efb44a9f836483fd14b22ce9fc4a672638..dc4ab642a819bf1ea72dffee6430059b38b07660 100644 (file)
       <arg choice="opt" rep="repeat">OPTIONS</arg>
       <arg choice="plain">unit-files</arg>
     </cmdsynopsis>
+    <cmdsynopsis>
+      <command>systemd-analyze</command>
+      <arg choice="opt" rep="repeat">OPTIONS</arg>
+      <arg choice="plain">unit-gdb</arg>
+      <arg choice="plain"><replaceable>SERVICE</replaceable></arg>
+    </cmdsynopsis>
     <cmdsynopsis>
       <command>systemd-analyze</command>
       <arg choice="opt" rep="repeat">OPTIONS</arg>
@@ -1183,6 +1189,23 @@ boot  efi  exitrd  init  lib32   libx32  media       opt  root  sbin  sys  usr  vmlin
 
       <xi:include href="version-info.xml" xpointer="v258"/>
     </refsect2>
+
+    <refsect2>
+      <title><command>systemd-analyze unit-gdb <replaceable>SERVICE</replaceable></command></title>
+
+      <para>Spawn and attach a debugger to the given service. By default,
+      <citerefentry project='man-pages'><refentrytitle>gdb</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+      will be used. This may be changed using the <option>--debugger=</option> option or the
+      <varname>$SYSTEMD_DEBUGGER</varname> environment variable. Use the <option>--debugger-arguments=</option> option
+      to pass extra command line arguments to the debugger and quote as appropriate
+      when <replaceable>ARGS</replaceable> contain whitespace (See Example).</para>
+
+      <example>
+        <programlisting>$ systemd-analyze --debugger-arguments="-batch -ex 'info all-registers'" unit-gdb systemd-oomd.service</programlisting>
+      </example>
+
+      <xi:include href="version-info.xml" xpointer="v258"/>
+    </refsect2>
   </refsect1>
 
   <refsect1>
@@ -1328,8 +1351,8 @@ boot  efi  exitrd  init  lib32    libx32  media       opt  root  sbin  sys  usr  vmlin
         <term><option>--root=<replaceable>PATH</replaceable></option></term>
 
         <listitem><para>With <command>cat-config</command>, <command>verify</command>,
-        <command>condition</command> and <command>security</command> when used with
-        <option>--offline=</option>, operate on files underneath the specified root path
+        <command>condition</command>, <command>unit-gdb</command>, and <command>security</command> when
+        used with <option>--offline=</option>, operate on files underneath the specified root path
         <replaceable>PATH</replaceable>.</para>
 
         <xi:include href="version-info.xml" xpointer="v239"/></listitem>
@@ -1784,6 +1807,26 @@ boot  efi  exitrd  init  lib32   libx32  media       opt  root  sbin  sys  usr  vmlin
         <xi:include href="version-info.xml" xpointer="v258"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--debugger=<replaceable>DEBUGGER</replaceable></option></term>
+
+        <listitem><para>Use the given debugger for the <command>unit-gdb</command> command. If not given and
+        <varname>$SYSTEMD_DEBUGGER</varname> is unset, then
+        <citerefentry project='man-pages'><refentrytitle>gdb</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+        will be used.</para>
+
+        <xi:include href="version-info.xml" xpointer="v258"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-A</option> <replaceable>ARGS</replaceable></term>
+        <term><option>--debugger-arguments=<replaceable>ARGS</replaceable></option></term>
+
+        <listitem><para>Pass the given <replaceable>ARGS</replaceable> as extra command line arguments to the debugger.</para>
+
+        <xi:include href="version-info.xml" xpointer="v258"/></listitem>
+      </varlistentry>
+
       <xi:include href="standard-options.xml" xpointer="help" />
       <xi:include href="standard-options.xml" xpointer="version" />
       <xi:include href="standard-options.xml" xpointer="no-pager" />
index 8329b1b495be22c720a21289cb7c7f43bc5cc72d..d3d3fe4c63e45eecc07e3574bac4c47f931dd0fb 100644 (file)
@@ -63,7 +63,7 @@ _systemd_analyze() {
     local -A OPTS=(
         [STANDALONE]='-h --help --version --system --user --global --order --require --no-pager
                              --man=no --generators=yes -q --quiet'
-        [ARG]='-H --host -M --machine --fuzz --from-pattern --to-pattern --root'
+        [ARG]='-H --host -M --machine --fuzz --from-pattern --to-pattern --root --debugger'
     )
 
     local -A VERBS=(
@@ -83,6 +83,7 @@ _systemd_analyze() {
         [CAPABILITY]='capability'
         [TRANSIENT_SETTINGS]='transient-settings'
         [UNIT_SHELL]='unit-shell'
+        [UNIT_GDB]='unit-gdb'
     )
 
     local CONFIGS='locale.conf systemd/bootchart.conf systemd/coredump.conf systemd/journald.conf
@@ -241,6 +242,13 @@ _systemd_analyze() {
         else
             comps=$( __get_services $mode )
         fi
+
+    elif __contains_word "$verb" ${VERBS[UNIT_GDB]}; then
+        if [[ $cur = -* ]]; then
+            comps='--help --version --debugger --debugger-arguments --root'
+        else
+            comps=$( __get_services $mode )
+        fi
     fi
 
     COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
diff --git a/src/analyze/analyze-unit-gdb.c b/src/analyze/analyze-unit-gdb.c
new file mode 100644 (file)
index 0000000..92b8a5c
--- /dev/null
@@ -0,0 +1,123 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "sd-bus.h"
+
+#include "alloc-util.h"
+#include "analyze.h"
+#include "analyze-unit-gdb.h"
+#include "bus-error.h"
+#include "bus-util.h"
+#include "log.h"
+#include "pidref.h"
+#include "process-util.h"
+#include "runtime-scope.h"
+#include "signal-util.h"
+#include "strv.h"
+#include "unit-def.h"
+#include "unit-name.h"
+
+int verb_unit_gdb(int argc, char *argv[], void *userdata) {
+        static const struct sigaction sa = {
+                .sa_sigaction = sigterm_process_group_handler,
+                .sa_flags = SA_SIGINFO,
+        };
+
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_free_ char *unit = NULL;
+        int r;
+
+        if (arg_transport != BUS_TRANSPORT_LOCAL)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot spawn a debugger for a remote service");
+
+        r = unit_name_mangle_with_suffix(argv[1], "as unit", UNIT_NAME_MANGLE_WARN, ".service", &unit);
+        if (r < 0)
+                return log_error_errno(r, "Failed to mangle name '%s': %m", argv[1]);
+
+        r = acquire_bus(&bus, /* use_full_bus= */ NULL);
+        if (r < 0)
+                return bus_log_connect_error(r, arg_transport, arg_runtime_scope);
+
+        _cleanup_free_ char *object = unit_dbus_path_from_name(unit);
+        if (!object)
+                return log_oom();
+
+        r = sd_bus_get_property(
+                        bus,
+                        "org.freedesktop.systemd1",
+                        object,
+                        "org.freedesktop.systemd1.Service",
+                        "MainPID",
+                        &error,
+                        &reply,
+                        "u");
+        if (r < 0)
+                return log_error_errno(r, "Failed to get the main PID of %s: %s", unit, bus_error_message(&error, r));
+
+        pid_t pid;
+        r = sd_bus_message_read(reply, "u", &pid);
+        if (r < 0)
+                return log_error_errno(r, "Failed to read the main PID of %s from reply: %m", unit);
+
+        if (!arg_debugger)
+                arg_debugger = secure_getenv("SYSTEMD_DEBUGGER") ?: "gdb";
+
+        _cleanup_strv_free_ char **debugger_call = NULL;
+        r = strv_extend(&debugger_call, arg_debugger);
+        if (r < 0)
+                return log_oom();
+
+        if (!STR_IN_SET(arg_debugger, "gdb", "lldb"))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The debugger must be either 'gdb' or 'lldb'");
+
+        if (streq(arg_debugger, "gdb")) {
+                r = strv_extendf(&debugger_call, "--pid=" PID_FMT, pid);
+                if (r < 0)
+                        return log_oom();
+        }
+
+        if (streq(arg_debugger, "lldb")) {
+                r = strv_extendf(&debugger_call, "--attach-pid=" PID_FMT, pid);
+                if (r < 0)
+                        return log_oom();
+        }
+
+        if (arg_root) {
+                if (streq(arg_debugger, "gdb")) {
+                        _cleanup_free_ char *sysroot_cmd = strjoin("set sysroot ", arg_root);
+                        r = strv_extend_many(&debugger_call, "-iex", sysroot_cmd);
+                        if (r < 0)
+                                return log_oom();
+                } else if (streq(arg_debugger, "lldb")) {
+                        _cleanup_free_ char *sysroot_cmd = strjoin("platform select --sysroot ", arg_root, " host");
+                        r = strv_extend_many(&debugger_call, "-O", sysroot_cmd);
+                        if (r < 0)
+                                return log_oom();
+                }
+        }
+
+        /* Don't interfere with debugger and its handling of SIGINT. */
+        (void) ignore_signals(SIGINT);
+        (void) sigaction(SIGTERM, &sa, NULL);
+
+        _cleanup_free_ char *fork_name = strjoin("(", debugger_call[0], ")");
+        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+        r = pidref_safe_fork(fork_name, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL, &pidref);
+        if (r < 0)
+                return log_error_errno(r, "Fork failed: %m");
+
+        if (r == 0) {
+                (void) execvp(debugger_call[0], debugger_call);
+                log_error_errno(errno, "Failed to invoke '%s': %m", debugger_call[0]);
+                _exit(EXIT_FAILURE);
+        }
+
+        return pidref_wait_for_terminate_and_check(
+                        debugger_call[0],
+                        &pidref,
+                        WAIT_LOG_ABNORMAL|WAIT_LOG_NON_ZERO_EXIT_STATUS);
+}
diff --git a/src/analyze/analyze-unit-gdb.h b/src/analyze/analyze-unit-gdb.h
new file mode 100644 (file)
index 0000000..a3df6b1
--- /dev/null
@@ -0,0 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+int verb_unit_gdb(int argc, char *argv[], void *userdata);
index cc50eb410acf8f1b697575cd2dcd2d0261b2b1aa..762317a6536ed09f029ee06e81af89b1be28226e 100644 (file)
@@ -43,6 +43,7 @@
 #include "analyze-timespan.h"
 #include "analyze-timestamp.h"
 #include "analyze-unit-files.h"
+#include "analyze-unit-gdb.h"
 #include "analyze-unit-paths.h"
 #include "analyze-unit-shell.h"
 #include "analyze-verify.h"
@@ -53,6 +54,7 @@
 #include "bus-util.h"
 #include "calendarspec.h"
 #include "dissect-image.h"
+#include "extract-word.h"
 #include "image-policy.h"
 #include "log.h"
 #include "loop-util.h"
@@ -79,6 +81,8 @@ usec_t arg_fuzz = 0;
 PagerFlags arg_pager_flags = 0;
 CatFlags arg_cat_flags = 0;
 BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
+const char *arg_debugger = NULL;
+static char **arg_debugger_args = NULL;
 const char *arg_host = NULL;
 RuntimeScope arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
 RecursiveErrors arg_recursive_errors = _RECURSIVE_ERRORS_INVALID;
@@ -242,6 +246,7 @@ static int help(int argc, char *argv[], void *userdata) {
                "  security [UNIT...]         Analyze security of unit\n"
                "  fdstore SERVICE...         Show file descriptor store contents of service\n"
                "  malloc [D-BUS SERVICE...]  Dump malloc stats of a D-Bus service\n"
+               "  unit-gdb SERVICE           Attach a debugger to the given running service\n"
                "  unit-shell SERVICE [Command]\n"
                "                             Run command on the namespace of the service\n"
                "\n%3$sExecutable Analysis:%4$s\n"
@@ -296,6 +301,9 @@ static int help(int argc, char *argv[], void *userdata) {
                "     --image-policy=POLICY   Specify disk image dissection policy\n"
                "  -m --mask                  Parse parameter as numeric capability mask\n"
                "     --drm-device=PATH       Use this DRM device sysfs path to get EDID\n"
+               "     --debugger=DEBUGGER     Use the given debugger\n"
+               "  -A --debugger-arguments=ARGS\n"
+               "                             Pass the given arguments to the debugger\n"
 
                "\nSee the %2$s for details.\n",
                program_invocation_short_name,
@@ -344,45 +352,48 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_SCALE_FACTOR_SVG,
                 ARG_DETAILED_SVG,
                 ARG_DRM_DEVICE_PATH,
+                ARG_DEBUGGER,
         };
 
         static const struct option options[] = {
-                { "help",             no_argument,       NULL, 'h'                  },
-                { "version",          no_argument,       NULL, ARG_VERSION          },
-                { "quiet",            no_argument,       NULL, 'q'                  },
-                { "order",            no_argument,       NULL, ARG_ORDER            },
-                { "require",          no_argument,       NULL, ARG_REQUIRE          },
-                { "root",             required_argument, NULL, ARG_ROOT             },
-                { "image",            required_argument, NULL, ARG_IMAGE            },
-                { "image-policy",     required_argument, NULL, ARG_IMAGE_POLICY     },
-                { "recursive-errors", required_argument, NULL, ARG_RECURSIVE_ERRORS },
-                { "offline",          required_argument, NULL, ARG_OFFLINE          },
-                { "threshold",        required_argument, NULL, ARG_THRESHOLD        },
-                { "security-policy",  required_argument, NULL, ARG_SECURITY_POLICY  },
-                { "system",           no_argument,       NULL, ARG_SYSTEM           },
-                { "user",             no_argument,       NULL, ARG_USER             },
-                { "global",           no_argument,       NULL, ARG_GLOBAL           },
-                { "from-pattern",     required_argument, NULL, ARG_DOT_FROM_PATTERN },
-                { "to-pattern",       required_argument, NULL, ARG_DOT_TO_PATTERN   },
-                { "fuzz",             required_argument, NULL, ARG_FUZZ             },
-                { "no-pager",         no_argument,       NULL, ARG_NO_PAGER         },
-                { "man",              optional_argument, NULL, ARG_MAN              },
-                { "generators",       optional_argument, NULL, ARG_GENERATORS       },
-                { "instance",         required_argument, NULL, ARG_INSTANCE         },
-                { "host",             required_argument, NULL, 'H'                  },
-                { "machine",          required_argument, NULL, 'M'                  },
-                { "iterations",       required_argument, NULL, ARG_ITERATIONS       },
-                { "base-time",        required_argument, NULL, ARG_BASE_TIME        },
-                { "unit",             required_argument, NULL, 'U'                  },
-                { "json",             required_argument, NULL, ARG_JSON             },
-                { "profile",          required_argument, NULL, ARG_PROFILE          },
-                { "table",            optional_argument, NULL, ARG_TABLE            },
-                { "no-legend",        optional_argument, NULL, ARG_NO_LEGEND        },
-                { "tldr",             no_argument,       NULL, ARG_TLDR             },
-                { "mask",             no_argument,       NULL, 'm'                  },
-                { "scale-svg",        required_argument, NULL, ARG_SCALE_FACTOR_SVG },
-                { "detailed",         no_argument,       NULL, ARG_DETAILED_SVG     },
-                { "drm-device",       required_argument, NULL, ARG_DRM_DEVICE_PATH  },
+                { "help",               no_argument,       NULL, 'h'                  },
+                { "version",            no_argument,       NULL, ARG_VERSION          },
+                { "quiet",              no_argument,       NULL, 'q'                  },
+                { "order",              no_argument,       NULL, ARG_ORDER            },
+                { "require",            no_argument,       NULL, ARG_REQUIRE          },
+                { "root",               required_argument, NULL, ARG_ROOT             },
+                { "image",              required_argument, NULL, ARG_IMAGE            },
+                { "image-policy",       required_argument, NULL, ARG_IMAGE_POLICY     },
+                { "recursive-errors"  , required_argument, NULL, ARG_RECURSIVE_ERRORS },
+                { "offline",            required_argument, NULL, ARG_OFFLINE          },
+                { "threshold",          required_argument, NULL, ARG_THRESHOLD        },
+                { "security-policy",    required_argument, NULL, ARG_SECURITY_POLICY  },
+                { "system",             no_argument,       NULL, ARG_SYSTEM           },
+                { "user",               no_argument,       NULL, ARG_USER             },
+                { "global",             no_argument,       NULL, ARG_GLOBAL           },
+                { "from-pattern",       required_argument, NULL, ARG_DOT_FROM_PATTERN },
+                { "to-pattern",         required_argument, NULL, ARG_DOT_TO_PATTERN   },
+                { "fuzz",               required_argument, NULL, ARG_FUZZ             },
+                { "no-pager",           no_argument,       NULL, ARG_NO_PAGER         },
+                { "man",                optional_argument, NULL, ARG_MAN              },
+                { "generators",         optional_argument, NULL, ARG_GENERATORS       },
+                { "instance",           required_argument, NULL, ARG_INSTANCE         },
+                { "host",               required_argument, NULL, 'H'                  },
+                { "machine",            required_argument, NULL, 'M'                  },
+                { "iterations",         required_argument, NULL, ARG_ITERATIONS       },
+                { "base-time",          required_argument, NULL, ARG_BASE_TIME        },
+                { "unit",               required_argument, NULL, 'U'                  },
+                { "json",               required_argument, NULL, ARG_JSON             },
+                { "profile",            required_argument, NULL, ARG_PROFILE          },
+                { "table",              optional_argument, NULL, ARG_TABLE            },
+                { "no-legend",          optional_argument, NULL, ARG_NO_LEGEND        },
+                { "tldr",               no_argument,       NULL, ARG_TLDR             },
+                { "mask",               no_argument,       NULL, 'm'                  },
+                { "scale-svg",          required_argument, NULL, ARG_SCALE_FACTOR_SVG },
+                { "detailed",           no_argument,       NULL, ARG_DETAILED_SVG     },
+                { "drm-device",         required_argument, NULL, ARG_DRM_DEVICE_PATH  },
+                { "debugger",           required_argument, NULL, ARG_DEBUGGER         },
+                { "debugger-arguments", required_argument, NULL, 'A'                  },
                 {}
         };
 
@@ -397,7 +408,7 @@ static int parse_argv(int argc, char *argv[]) {
         optind = 0;
 
         for (;;) {
-                static const char option_string[] = "-hqH:M:U:m";
+                static const char option_string[] = "-hqH:M:U:mA:";
 
                 c = getopt_long(argc, argv, option_string + reorder, options, NULL);
                 if (c < 0)
@@ -653,6 +664,19 @@ static int parse_argv(int argc, char *argv[]) {
                                 return r;
                         break;
 
+                case ARG_DEBUGGER:
+                        arg_debugger = strdup(optarg);
+                        break;
+
+                case 'A': {
+                        _cleanup_strv_free_ char **l = NULL;
+                        r = strv_split_full(&l, optarg, WHITESPACE, EXTRACT_UNQUOTE);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse debugger arguments '%s': %m", optarg);
+                        strv_free_and_replace(arg_debugger_args, l);
+                        break;
+                }
+
                 case '?':
                         return -EINVAL;
 
@@ -700,10 +724,10 @@ done:
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "Option --security-policy= is only supported for security.");
 
-        if ((arg_root || arg_image) && (!STRPTR_IN_SET(argv[optind], "cat-config", "verify", "condition", "inspect-elf")) &&
+        if ((arg_root || arg_image) && (!STRPTR_IN_SET(argv[optind], "cat-config", "verify", "condition", "inspect-elf", "unit-gdb")) &&
            (!(streq_ptr(argv[optind], "security") && arg_offline)))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "Options --root= and --image= are only supported for cat-config, verify, condition and security when used with --offline= right now.");
+                                       "Options --root= and --image= are only supported for cat-config, verify, condition, unit-gdb, and security when used with --offline= right now.");
 
         /* Having both an image and a root is not supported by the code */
         if (arg_root && arg_image)
@@ -756,6 +780,7 @@ static int run(int argc, char *argv[]) {
                 { "dump",               VERB_ANY, VERB_ANY, 0,  verb_dump               },
                 { "cat-config",         2,        VERB_ANY, 0,  verb_cat_config         },
                 { "unit-files",         VERB_ANY, VERB_ANY, 0,  verb_unit_files         },
+                { "unit-gdb",           2,        VERB_ANY, 0,  verb_unit_gdb           },
                 { "unit-paths",         1,        1,        0,  verb_unit_paths         },
                 { "unit-shell",         2,        VERB_ANY, 0,  verb_unit_shell         },
                 { "exit-status",        VERB_ANY, VERB_ANY, 0,  verb_exit_status        },
index ac31dfce8320c049ad94ca16351c9cc70f22c3c8..1d489b723e98066d8374c8797559fe5d3dba8811 100644 (file)
@@ -24,6 +24,7 @@ extern PagerFlags arg_pager_flags;
 extern CatFlags arg_cat_flags;
 extern BusTransport arg_transport;
 extern const char *arg_host;
+extern const char *arg_debugger;
 extern RuntimeScope arg_runtime_scope;
 extern RecursiveErrors arg_recursive_errors;
 extern bool arg_man;
index 283378a2f3c4e7219aee6b4826e112f92eb3ab27..b1a5b691ce5c4dede74ceacb91ce51e2f11c1978 100644 (file)
@@ -32,6 +32,7 @@ systemd_analyze_sources = files(
         'analyze-timespan.c',
         'analyze-timestamp.c',
         'analyze-unit-files.c',
+        'analyze-unit-gdb.c',
         'analyze-unit-paths.c',
         'analyze-unit-shell.c',
         'analyze-verify.c',
index 459328a3c722a0a8ddec2d4579d04a01046b8c28..bd7d88f28fab753e6d8a9b66d4ccf7953dceb044 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "errno-util.h"
 #include "parse-util.h"
+#include "process-util.h"
 #include "signal-util.h"
 #include "stdio-util.h"
 #include "string-table.h"
@@ -319,3 +320,15 @@ int parse_signo(const char *s, int *ret) {
 
         return 0;
 }
+
+void sigterm_process_group_handler(int signal, siginfo_t *info, void *ucontext) {
+        assert(signal == SIGTERM);
+        assert(info);
+
+        /* If the sender is not us, propagate the signal to all processes in
+         * the same process group */
+        if (si_code_from_process(info->si_code) &&
+            pid_is_valid(info->si_pid) &&
+            info->si_pid != getpid_cached())
+                (void) kill(0, signal);
+}
index c45b9934d021cf1cef2d7dd2875708769c719a40..7d8a284c50361606ffab4537b03cb4341701431a 100644 (file)
@@ -93,3 +93,5 @@ static inline bool si_code_from_process(int si_code) {
 
         return si_code < 0 || IN_SET(si_code, SI_USER, SI_QUEUE);
 }
+
+void sigterm_process_group_handler(int signal, siginfo_t *info, void *ucontext);
index a8d81955c0acf7f9eb3da17b2151ebfa6f53d58a..d0755d9e1b46ff3dad377a0ff286a7dbcdb22a73 100644 (file)
@@ -1164,21 +1164,9 @@ static int dump_core(int argc, char **argv, void *userdata) {
         return 0;
 }
 
-static void sigterm_handler(int signal, siginfo_t *info, void *ucontext) {
-        assert(signal == SIGTERM);
-        assert(info);
-
-        /* If the sender is not us, propagate the signal to all processes in
-         * the same process group */
-        if (si_code_from_process(info->si_code) &&
-            pid_is_valid(info->si_pid) &&
-            info->si_pid != getpid_cached())
-                (void) kill(0, signal);
-}
-
 static int run_debug(int argc, char **argv, void *userdata) {
         static const struct sigaction sa = {
-                .sa_sigaction = sigterm_handler,
+                .sa_sigaction = sigterm_process_group_handler,
                 .sa_flags = SA_SIGINFO,
         };