systemd-analyze: added the verb unit-gdb to spawn and attach gdb

This commit is contained in:
ZIHCO
2025-07-09 12:56:32 +01:00
committed by Luca Boccassi
parent 6235121abf
commit ad6e02e7b4
11 changed files with 264 additions and 56 deletions

View File

@@ -251,7 +251,7 @@
<listitem><para>Use the given debugger for the <command>debug</command> <listitem><para>Use the given debugger for the <command>debug</command>
command. If not given and <varname>$SYSTEMD_DEBUGGER</varname> is unset, then command. If not given and <varname>$SYSTEMD_DEBUGGER</varname> is unset, then
<citerefentry project='man-pages'><refentrytitle>gdb</refentrytitle><manvolnum>1</manvolnum></citerefentry> <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> <xi:include href="version-info.xml" xpointer="v239"/></listitem>
</varlistentry> </varlistentry>

View File

@@ -65,6 +65,12 @@
<arg choice="opt" rep="repeat">OPTIONS</arg> <arg choice="opt" rep="repeat">OPTIONS</arg>
<arg choice="plain">unit-files</arg> <arg choice="plain">unit-files</arg>
</cmdsynopsis> </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> <cmdsynopsis>
<command>systemd-analyze</command> <command>systemd-analyze</command>
<arg choice="opt" rep="repeat">OPTIONS</arg> <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"/> <xi:include href="version-info.xml" xpointer="v258"/>
</refsect2> </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>
<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> <term><option>--root=<replaceable>PATH</replaceable></option></term>
<listitem><para>With <command>cat-config</command>, <command>verify</command>, <listitem><para>With <command>cat-config</command>, <command>verify</command>,
<command>condition</command> and <command>security</command> when used with <command>condition</command>, <command>unit-gdb</command>, and <command>security</command> when
<option>--offline=</option>, operate on files underneath the specified root path used with <option>--offline=</option>, operate on files underneath the specified root path
<replaceable>PATH</replaceable>.</para> <replaceable>PATH</replaceable>.</para>
<xi:include href="version-info.xml" xpointer="v239"/></listitem> <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> <xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry> </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="help" />
<xi:include href="standard-options.xml" xpointer="version" /> <xi:include href="standard-options.xml" xpointer="version" />
<xi:include href="standard-options.xml" xpointer="no-pager" /> <xi:include href="standard-options.xml" xpointer="no-pager" />

View File

@@ -63,7 +63,7 @@ _systemd_analyze() {
local -A OPTS=( local -A OPTS=(
[STANDALONE]='-h --help --version --system --user --global --order --require --no-pager [STANDALONE]='-h --help --version --system --user --global --order --require --no-pager
--man=no --generators=yes -q --quiet' --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=( local -A VERBS=(
@@ -83,6 +83,7 @@ _systemd_analyze() {
[CAPABILITY]='capability' [CAPABILITY]='capability'
[TRANSIENT_SETTINGS]='transient-settings' [TRANSIENT_SETTINGS]='transient-settings'
[UNIT_SHELL]='unit-shell' [UNIT_SHELL]='unit-shell'
[UNIT_GDB]='unit-gdb'
) )
local CONFIGS='locale.conf systemd/bootchart.conf systemd/coredump.conf systemd/journald.conf local CONFIGS='locale.conf systemd/bootchart.conf systemd/coredump.conf systemd/journald.conf
@@ -241,6 +242,13 @@ _systemd_analyze() {
else else
comps=$( __get_services $mode ) comps=$( __get_services $mode )
fi 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 fi
COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) COMPREPLY=( $(compgen -W '$comps' -- "$cur") )

View File

@@ -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);
}

View File

@@ -0,0 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
int verb_unit_gdb(int argc, char *argv[], void *userdata);

View File

@@ -43,6 +43,7 @@
#include "analyze-timespan.h" #include "analyze-timespan.h"
#include "analyze-timestamp.h" #include "analyze-timestamp.h"
#include "analyze-unit-files.h" #include "analyze-unit-files.h"
#include "analyze-unit-gdb.h"
#include "analyze-unit-paths.h" #include "analyze-unit-paths.h"
#include "analyze-unit-shell.h" #include "analyze-unit-shell.h"
#include "analyze-verify.h" #include "analyze-verify.h"
@@ -53,6 +54,7 @@
#include "bus-util.h" #include "bus-util.h"
#include "calendarspec.h" #include "calendarspec.h"
#include "dissect-image.h" #include "dissect-image.h"
#include "extract-word.h"
#include "image-policy.h" #include "image-policy.h"
#include "log.h" #include "log.h"
#include "loop-util.h" #include "loop-util.h"
@@ -79,6 +81,8 @@ usec_t arg_fuzz = 0;
PagerFlags arg_pager_flags = 0; PagerFlags arg_pager_flags = 0;
CatFlags arg_cat_flags = 0; CatFlags arg_cat_flags = 0;
BusTransport arg_transport = BUS_TRANSPORT_LOCAL; BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
const char *arg_debugger = NULL;
static char **arg_debugger_args = NULL;
const char *arg_host = NULL; const char *arg_host = NULL;
RuntimeScope arg_runtime_scope = RUNTIME_SCOPE_SYSTEM; RuntimeScope arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
RecursiveErrors arg_recursive_errors = _RECURSIVE_ERRORS_INVALID; 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" " security [UNIT...] Analyze security of unit\n"
" fdstore SERVICE... Show file descriptor store contents of service\n" " fdstore SERVICE... Show file descriptor store contents of service\n"
" malloc [D-BUS SERVICE...] Dump malloc stats of a D-Bus 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" " unit-shell SERVICE [Command]\n"
" Run command on the namespace of the service\n" " Run command on the namespace of the service\n"
"\n%3$sExecutable Analysis:%4$s\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" " --image-policy=POLICY Specify disk image dissection policy\n"
" -m --mask Parse parameter as numeric capability mask\n" " -m --mask Parse parameter as numeric capability mask\n"
" --drm-device=PATH Use this DRM device sysfs path to get EDID\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", "\nSee the %2$s for details.\n",
program_invocation_short_name, program_invocation_short_name,
@@ -344,45 +352,48 @@ static int parse_argv(int argc, char *argv[]) {
ARG_SCALE_FACTOR_SVG, ARG_SCALE_FACTOR_SVG,
ARG_DETAILED_SVG, ARG_DETAILED_SVG,
ARG_DRM_DEVICE_PATH, ARG_DRM_DEVICE_PATH,
ARG_DEBUGGER,
}; };
static const struct option options[] = { static const struct option options[] = {
{ "help", no_argument, NULL, 'h' }, { "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION }, { "version", no_argument, NULL, ARG_VERSION },
{ "quiet", no_argument, NULL, 'q' }, { "quiet", no_argument, NULL, 'q' },
{ "order", no_argument, NULL, ARG_ORDER }, { "order", no_argument, NULL, ARG_ORDER },
{ "require", no_argument, NULL, ARG_REQUIRE }, { "require", no_argument, NULL, ARG_REQUIRE },
{ "root", required_argument, NULL, ARG_ROOT }, { "root", required_argument, NULL, ARG_ROOT },
{ "image", required_argument, NULL, ARG_IMAGE }, { "image", required_argument, NULL, ARG_IMAGE },
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY }, { "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
{ "recursive-errors", required_argument, NULL, ARG_RECURSIVE_ERRORS }, { "recursive-errors" , required_argument, NULL, ARG_RECURSIVE_ERRORS },
{ "offline", required_argument, NULL, ARG_OFFLINE }, { "offline", required_argument, NULL, ARG_OFFLINE },
{ "threshold", required_argument, NULL, ARG_THRESHOLD }, { "threshold", required_argument, NULL, ARG_THRESHOLD },
{ "security-policy", required_argument, NULL, ARG_SECURITY_POLICY }, { "security-policy", required_argument, NULL, ARG_SECURITY_POLICY },
{ "system", no_argument, NULL, ARG_SYSTEM }, { "system", no_argument, NULL, ARG_SYSTEM },
{ "user", no_argument, NULL, ARG_USER }, { "user", no_argument, NULL, ARG_USER },
{ "global", no_argument, NULL, ARG_GLOBAL }, { "global", no_argument, NULL, ARG_GLOBAL },
{ "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN }, { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
{ "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN }, { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
{ "fuzz", required_argument, NULL, ARG_FUZZ }, { "fuzz", required_argument, NULL, ARG_FUZZ },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER }, { "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "man", optional_argument, NULL, ARG_MAN }, { "man", optional_argument, NULL, ARG_MAN },
{ "generators", optional_argument, NULL, ARG_GENERATORS }, { "generators", optional_argument, NULL, ARG_GENERATORS },
{ "instance", required_argument, NULL, ARG_INSTANCE }, { "instance", required_argument, NULL, ARG_INSTANCE },
{ "host", required_argument, NULL, 'H' }, { "host", required_argument, NULL, 'H' },
{ "machine", required_argument, NULL, 'M' }, { "machine", required_argument, NULL, 'M' },
{ "iterations", required_argument, NULL, ARG_ITERATIONS }, { "iterations", required_argument, NULL, ARG_ITERATIONS },
{ "base-time", required_argument, NULL, ARG_BASE_TIME }, { "base-time", required_argument, NULL, ARG_BASE_TIME },
{ "unit", required_argument, NULL, 'U' }, { "unit", required_argument, NULL, 'U' },
{ "json", required_argument, NULL, ARG_JSON }, { "json", required_argument, NULL, ARG_JSON },
{ "profile", required_argument, NULL, ARG_PROFILE }, { "profile", required_argument, NULL, ARG_PROFILE },
{ "table", optional_argument, NULL, ARG_TABLE }, { "table", optional_argument, NULL, ARG_TABLE },
{ "no-legend", optional_argument, NULL, ARG_NO_LEGEND }, { "no-legend", optional_argument, NULL, ARG_NO_LEGEND },
{ "tldr", no_argument, NULL, ARG_TLDR }, { "tldr", no_argument, NULL, ARG_TLDR },
{ "mask", no_argument, NULL, 'm' }, { "mask", no_argument, NULL, 'm' },
{ "scale-svg", required_argument, NULL, ARG_SCALE_FACTOR_SVG }, { "scale-svg", required_argument, NULL, ARG_SCALE_FACTOR_SVG },
{ "detailed", no_argument, NULL, ARG_DETAILED_SVG }, { "detailed", no_argument, NULL, ARG_DETAILED_SVG },
{ "drm-device", required_argument, NULL, ARG_DRM_DEVICE_PATH }, { "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; optind = 0;
for (;;) { 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); c = getopt_long(argc, argv, option_string + reorder, options, NULL);
if (c < 0) if (c < 0)
@@ -653,6 +664,19 @@ static int parse_argv(int argc, char *argv[]) {
return r; return r;
break; 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 '?': case '?':
return -EINVAL; return -EINVAL;
@@ -700,10 +724,10 @@ done:
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Option --security-policy= is only supported for security."); "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))) (!(streq_ptr(argv[optind], "security") && arg_offline)))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), 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 */ /* Having both an image and a root is not supported by the code */
if (arg_root && arg_image) if (arg_root && arg_image)
@@ -756,6 +780,7 @@ static int run(int argc, char *argv[]) {
{ "dump", VERB_ANY, VERB_ANY, 0, verb_dump }, { "dump", VERB_ANY, VERB_ANY, 0, verb_dump },
{ "cat-config", 2, VERB_ANY, 0, verb_cat_config }, { "cat-config", 2, VERB_ANY, 0, verb_cat_config },
{ "unit-files", VERB_ANY, VERB_ANY, 0, verb_unit_files }, { "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-paths", 1, 1, 0, verb_unit_paths },
{ "unit-shell", 2, VERB_ANY, 0, verb_unit_shell }, { "unit-shell", 2, VERB_ANY, 0, verb_unit_shell },
{ "exit-status", VERB_ANY, VERB_ANY, 0, verb_exit_status }, { "exit-status", VERB_ANY, VERB_ANY, 0, verb_exit_status },

View File

@@ -24,6 +24,7 @@ extern PagerFlags arg_pager_flags;
extern CatFlags arg_cat_flags; extern CatFlags arg_cat_flags;
extern BusTransport arg_transport; extern BusTransport arg_transport;
extern const char *arg_host; extern const char *arg_host;
extern const char *arg_debugger;
extern RuntimeScope arg_runtime_scope; extern RuntimeScope arg_runtime_scope;
extern RecursiveErrors arg_recursive_errors; extern RecursiveErrors arg_recursive_errors;
extern bool arg_man; extern bool arg_man;

View File

@@ -32,6 +32,7 @@ systemd_analyze_sources = files(
'analyze-timespan.c', 'analyze-timespan.c',
'analyze-timestamp.c', 'analyze-timestamp.c',
'analyze-unit-files.c', 'analyze-unit-files.c',
'analyze-unit-gdb.c',
'analyze-unit-paths.c', 'analyze-unit-paths.c',
'analyze-unit-shell.c', 'analyze-unit-shell.c',
'analyze-verify.c', 'analyze-verify.c',

View File

@@ -5,6 +5,7 @@
#include "errno-util.h" #include "errno-util.h"
#include "parse-util.h" #include "parse-util.h"
#include "process-util.h"
#include "signal-util.h" #include "signal-util.h"
#include "stdio-util.h" #include "stdio-util.h"
#include "string-table.h" #include "string-table.h"
@@ -319,3 +320,15 @@ int parse_signo(const char *s, int *ret) {
return 0; 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);
}

View 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); return si_code < 0 || IN_SET(si_code, SI_USER, SI_QUEUE);
} }
void sigterm_process_group_handler(int signal, siginfo_t *info, void *ucontext);

View File

@@ -1164,21 +1164,9 @@ static int dump_core(int argc, char **argv, void *userdata) {
return 0; 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 int run_debug(int argc, char **argv, void *userdata) {
static const struct sigaction sa = { static const struct sigaction sa = {
.sa_sigaction = sigterm_handler, .sa_sigaction = sigterm_process_group_handler,
.sa_flags = SA_SIGINFO, .sa_flags = SA_SIGINFO,
}; };