systemctl: add --kill-subgroup= switch for killing subcgroup

This commit is contained in:
Lennart Poettering
2025-06-30 14:54:12 +02:00
parent 0f23564ad4
commit 6b02854f50
4 changed files with 66 additions and 7 deletions

View File

@@ -2494,11 +2494,12 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
one is then considered the main process of the unit (if it can be determined). This is different
for service units of other types, where the process forked off by the manager for
<varname>ExecStart=</varname> is always the main process itself. A service unit consists of zero or
one main process, zero or one control process plus any number of additional processes. Not all unit
types manage processes of these types however. For example, for mount units, control processes are
defined (which are the invocations of <filename>&MOUNT_PATH;</filename> and
<filename>&UMOUNT_PATH;</filename>), but no main process is defined. If omitted, defaults to
<option>all</option>.</para>
one main process, zero or one control process plus any number of additional processes part of the
unit's control group. Not all unit types manage processes of these types however. For example, for
mount units, control processes are defined (which are the invocations of
<filename>&MOUNT_PATH;</filename> and <filename>&UMOUNT_PATH;</filename>), but no main process is
defined. If omitted, defaults to <option>all</option>, except if <option>--kill-subgroup=</option>
is used in which case defaults to <option>cgroup</option>.</para>
<xi:include href="version-info.xml" xpointer="v252"/>
</listitem>
@@ -2525,6 +2526,28 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<xi:include href="version-info.xml" xpointer="v254"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--kill-subgroup=<replaceable>PATH</replaceable></option></term>
<listitem><para>Takes a control group sub-path to send signals to, for use with the
<command>kill</command> command. By default the chosen signal is delivered to all processes of the
unit's cgroups (as well as the main/control processes (if outside) subject to
<option>--kill-whom=</option>). But with this option a subgroup can be selelected instead. This
functionality is only available if <literal>cgroup</literal> or <literal>cgroup-fail</literal> are
used with <option>--kill-whom=</option>, and in fact the former is the default if
<option>--kill-subgroup=</option> is used.</para>
<para>The specified path may, but doesn't have to be prefixed with a slash, and its absence or
presence has no effect, the path is either way taken relative to the unit's main control group
path.</para>
<para>This functionality is only available on units where control group delegation is enabled (see
<varname>Delegate=</varname> in
<member><citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>).</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="signal" />
<varlistentry>

View File

@@ -20,6 +20,9 @@ int verb_kill(int argc, char *argv[], void *userdata) {
sd_bus *bus;
int r, q;
if (arg_kill_subgroup && arg_kill_value_set)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--kill-subgroup= and --kill-value= may not be combined.");
r = acquire_bus(BUS_MANAGER, &bus);
if (r < 0)
return r;
@@ -32,7 +35,7 @@ int verb_kill(int argc, char *argv[], void *userdata) {
polkit_agent_open_maybe();
kill_whom = arg_kill_whom ?: "all";
kill_whom = arg_kill_whom ?: arg_kill_subgroup ? "cgroup" : "all";
/* --fail was specified */
if (streq(arg_job_mode(), "fail"))
@@ -53,6 +56,14 @@ int verb_kill(int argc, char *argv[], void *userdata) {
&error,
NULL,
"ssii", *name, kill_whom, arg_signal, arg_kill_value);
else if (arg_kill_subgroup)
q = bus_call_method(
bus,
bus_systemd_mgr,
"KillUnitSubgroup",
&error,
NULL,
"sssi", *name, kill_whom, arg_kill_subgroup, arg_signal);
else
q = bus_call_method(
bus,

View File

@@ -16,6 +16,7 @@
#include "pager.h"
#include "parse-argument.h"
#include "parse-util.h"
#include "path-util.h"
#include "pretty-print.h"
#include "static-destruct.h"
#include "string-table.h"
@@ -88,6 +89,7 @@ bool arg_mkdir = false;
bool arg_marked = false;
const char *arg_drop_in = NULL;
ImagePolicy *arg_image_policy = NULL;
char *arg_kill_subgroup = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_types, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_states, strv_freep);
@@ -103,6 +105,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_boot_loader_entry, unsetp);
STATIC_DESTRUCTOR_REGISTER(arg_clean_what, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_drop_in, unsetp);
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
STATIC_DESTRUCTOR_REGISTER(arg_kill_subgroup, freep);
static int systemctl_help(void) {
_cleanup_free_ char *link = NULL;
@@ -253,9 +256,11 @@ static int systemctl_help(void) {
" Whether to check inhibitors before shutting down,\n"
" sleeping, or hibernating\n"
" -i Shortcut for --check-inhibitors=no\n"
" -s --signal=SIGNAL Which signal to send\n"
" --kill-whom=WHOM Whom to send signal to\n"
" --kill-value=INT Signal value to enqueue\n"
" -s --signal=SIGNAL Which signal to send\n"
" --kill-subgroup=PATH\n"
" Send signal to sub-control-group only\n"
" --what=RESOURCES Which types of resources to remove\n"
" --now Start or stop unit after enabling or disabling it\n"
" --dry-run Only print what would be done\n"
@@ -438,6 +443,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
ARG_DROP_IN,
ARG_WHEN,
ARG_STDIN,
ARG_KILL_SUBGROUP,
};
static const struct option options[] = {
@@ -507,6 +513,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
{ "drop-in", required_argument, NULL, ARG_DROP_IN },
{ "when", required_argument, NULL, ARG_WHEN },
{ "stdin", no_argument, NULL, ARG_STDIN },
{ "kill-subgroup", required_argument, NULL, ARG_KILL_SUBGROUP },
{}
};
@@ -1021,6 +1028,23 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
arg_stdin = true;
break;
case ARG_KILL_SUBGROUP: {
if (empty_or_root(optarg)) {
arg_kill_subgroup = mfree(arg_kill_subgroup);
break;
}
_cleanup_free_ char *p = NULL;
if (path_simplify_alloc(optarg, &p) < 0)
return log_oom();
if (!path_is_safe(p))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Control group sub-path '%s' is not valid.", p);
free_and_replace(arg_kill_subgroup, p);
break;
}
case '.':
/* Output an error mimicking getopt, and print a hint afterwards */
log_error("%s: invalid option -- '.'", program_invocation_name);

View File

@@ -100,6 +100,7 @@ extern bool arg_mkdir;
extern bool arg_marked;
extern const char *arg_drop_in;
extern ImagePolicy *arg_image_policy;
extern char *arg_kill_subgroup;
static inline const char* arg_job_mode(void) {
return _arg_job_mode ?: "replace";