pgrep: Match on cgroup v2 paths

You can match or filter on cgroup paths. Currently the match is only
done for version 2 cgroups because these are way simpler as they have
a unified name and always start with "0::".

cgroup v1 can have:
 named groups "1:name=myspecialname:"
 controllers "9:blkio:"
 multiple controllers! "4:cpu,cpuacct:"

So they are very much more complicated from a options parsing and
cgroup matching point of view.

In addition, both my Debian bookworm and bullseye systems use
v2 cgroups.

$ ./pgrep --cgroup /system.slice/cron.service
760

Signed-off-by: Craig Small <csmall@dropbear.xyz>
This commit is contained in:
Craig Small 2021-10-26 20:56:19 +11:00
parent 14d6ab27d6
commit 4cfb0fb763
3 changed files with 56 additions and 5 deletions

1
NEWS
View File

@ -9,6 +9,7 @@ procps-ng-NEXT
* pkill: Check for lt- variants of program name issue #192 * pkill: Check for lt- variants of program name issue #192
* pgrep: Add newline after regex error message merge #91 * pgrep: Add newline after regex error message merge #91
* pgrep: Fix selection where uid/gid > 2^31 merge !146 * pgrep: Fix selection where uid/gid > 2^31 merge !146
* pgrep: Select on cgroup v2 paths issue #168
* ps: Add OOM and OOMADJ fields issue #198 * ps: Add OOM and OOMADJ fields issue #198
* ps: Add IO Accounting fields issue #184 * ps: Add IO Accounting fields issue #184
* ps: Add PSS and USS fields issue #112 * ps: Add PSS and USS fields issue #112

View File

@ -7,7 +7,7 @@
.\" the Free Software Foundation; either version 2 of the License, or .\" the Free Software Foundation; either version 2 of the License, or
.\" (at your option) any later version. .\" (at your option) any later version.
.\" .\"
.TH PGREP "1" "2020-06-04" "procps-ng" "User Commands" .TH PGREP "1" "2021-10-26" "procps-ng" "User Commands"
.SH NAME .SH NAME
pgrep, pkill, pidwait \- look up, signal, or wait for processes based on name and other attributes pgrep, pkill, pidwait \- look up, signal, or wait for processes based on name and other attributes
.SH SYNOPSIS .SH SYNOPSIS
@ -177,6 +177,10 @@ Fail if pidfile (see \fB\-F\fR) not locked.
\fB\-r\fR, \fB\-\-runstates\fR \fID,R,S,Z,\fP... \fB\-r\fR, \fB\-\-runstates\fR \fID,R,S,Z,\fP...
Match only processes which match the process state. Match only processes which match the process state.
.TP .TP
\fB\-\-cgroup \fIname\fP,...
Match on provided control group (cgroup) v2 name. See
.BR cgroups (8)
.TP
\fB\-\-ns \fIpid\fP \fB\-\-ns \fIpid\fP
Match processes that belong to the same namespaces. Required to run as Match processes that belong to the same namespaces. Required to run as
root to match processes from other users. See \fB\-\-nslist\fR for how to root to match processes from other users. See \fB\-\-nslist\fR for how to
@ -282,7 +286,8 @@ Defunct processes are reported.
.BR killall (1), .BR killall (1),
.BR skill (1), .BR skill (1),
.BR kill (1), .BR kill (1),
.BR kill (2) .BR kill (2),
.BR cgroups (8)
.SH AUTHOR .SH AUTHOR
.UR kjetilho@ifi.uio.no .UR kjetilho@ifi.uio.no
Kjetil Torgrim Homme Kjetil Torgrim Homme

51
pgrep.c
View File

@ -72,11 +72,13 @@ enum pids_item Items[] = {
PIDS_CMD, PIDS_CMD,
PIDS_CMDLINE, PIDS_CMDLINE,
PIDS_STATE, PIDS_STATE,
PIDS_TIME_ELAPSED PIDS_TIME_ELAPSED,
PIDS_CGROUP_V
}; };
enum rel_items { enum rel_items {
EU_PID, EU_PPID, EU_PGRP, EU_EUID, EU_RUID, EU_RGID, EU_SESSION, EU_PID, EU_PPID, EU_PGRP, EU_EUID, EU_RUID, EU_RGID, EU_SESSION,
EU_TGID, EU_STARTTIME, EU_TTYNAME, EU_CMD, EU_CMDLINE, EU_STA, EU_ELAPSED EU_TGID, EU_STARTTIME, EU_TTYNAME, EU_CMD, EU_CMDLINE, EU_STA, EU_ELAPSED,
EU_CGROUP
}; };
#define grow_size(x) do { \ #define grow_size(x) do { \
if ((x) < 0 || (size_t)(x) >= INT_MAX / 5 / sizeof(struct el)) \ if ((x) < 0 || (size_t)(x) >= INT_MAX / 5 / sizeof(struct el)) \
@ -127,6 +129,7 @@ static struct el *opt_term = NULL;
static struct el *opt_euid = NULL; static struct el *opt_euid = NULL;
static struct el *opt_ruid = NULL; static struct el *opt_ruid = NULL;
static struct el *opt_nslist = NULL; static struct el *opt_nslist = NULL;
static struct el *opt_cgroup = NULL;
static char *opt_pattern = NULL; static char *opt_pattern = NULL;
static char *opt_pidfile = NULL; static char *opt_pidfile = NULL;
static char *opt_runstates = NULL; static char *opt_runstates = NULL;
@ -177,6 +180,7 @@ static int __attribute__ ((__noreturn__)) usage(int opt)
fputs(_(" -F, --pidfile <file> read PIDs from file\n"), fp); fputs(_(" -F, --pidfile <file> read PIDs from file\n"), fp);
fputs(_(" -L, --logpidfile fail if PID file is not locked\n"), fp); fputs(_(" -L, --logpidfile fail if PID file is not locked\n"), fp);
fputs(_(" -r, --runstates <state> match runstates [D,S,Z,...]\n"), fp); fputs(_(" -r, --runstates <state> match runstates [D,S,Z,...]\n"), fp);
fputs(_(" --cgroup <grp,...> match by cgroup v2 names\n"), fp);
fputs(_(" --ns <PID> match the processes that belong to the same\n" fputs(_(" --ns <PID> match the processes that belong to the same\n"
" namespace as <pid>\n"), fp); " namespace as <pid>\n"), fp);
fputs(_(" --nslist <ns,...> list which namespaces will be considered for\n" fputs(_(" --nslist <ns,...> list which namespaces will be considered for\n"
@ -455,6 +459,35 @@ static int match_ns (const int pid,
return found; return found;
} }
static int cgroup_cmp(const char *restrict cgroup,
const char *restrict path)
{
if (cgroup == NULL || path == NULL)
return 1;
// Cgroup v2 have 0::
if (strncmp("0::", cgroup, 3) == 0) {
return strcmp(cgroup+3, path);
} //might try for cgroup v1 later
return 1;
}
static int match_cgroup_list(char **values,
const struct el *restrict list)
{
if (list != NULL && values != NULL) {
int i, j;
for (i = list[0].num; i > 0; i--) {
for (j=0; values[j] && values[j][0]; j++) {
if (! cgroup_cmp (values[j], list[i].str)) {
return 1;
}
}
}
}
return 0;
}
static void output_numlist (const struct el *restrict list, int num) static void output_numlist (const struct el *restrict list, int num)
{ {
int i; int i;
@ -535,6 +568,7 @@ static struct el * select_procs (int *num)
#define PIDS_GETULL(e) PIDS_VAL(EU_ ## e, ull_int, stack, info) #define PIDS_GETULL(e) PIDS_VAL(EU_ ## e, ull_int, stack, info)
#define PIDS_GETSTR(e) PIDS_VAL(EU_ ## e, str, stack, info) #define PIDS_GETSTR(e) PIDS_VAL(EU_ ## e, str, stack, info)
#define PIDS_GETSCH(e) PIDS_VAL(EU_ ## e, s_ch, stack, info) #define PIDS_GETSCH(e) PIDS_VAL(EU_ ## e, s_ch, stack, info)
#define PIDS_GETSTV(e) PIDS_VAL(EU_ ## e, strv, stack, info)
struct pids_info *info=NULL; struct pids_info *info=NULL;
struct procps_ns nsp; struct procps_ns nsp;
struct pids_stack *stack; struct pids_stack *stack;
@ -568,7 +602,7 @@ static struct el * select_procs (int *num)
_("Error reading reference namespace information\n")); _("Error reading reference namespace information\n"));
} }
if (procps_pids_new(&info, Items, 14) < 0) if (procps_pids_new(&info, Items, 15) < 0)
xerrx(EXIT_FATAL, xerrx(EXIT_FATAL,
_("Unable to create pid info structure")); _("Unable to create pid info structure"));
which = PIDS_FETCH_TASKS_ONLY; which = PIDS_FETCH_TASKS_ONLY;
@ -607,6 +641,8 @@ static struct el * select_procs (int *num)
match = match_strlist(PIDS_GETSTR(TTYNAME), opt_term); match = match_strlist(PIDS_GETSTR(TTYNAME), opt_term);
else if (opt_runstates && ! strchr(opt_runstates, PIDS_GETSCH(STA))) else if (opt_runstates && ! strchr(opt_runstates, PIDS_GETSCH(STA)))
match = 0; match = 0;
else if (opt_cgroup && ! match_cgroup_list (PIDS_GETSTV(CGROUP), opt_cgroup))
match = 0;
task_cmdline = PIDS_GETSTR(CMDLINE); task_cmdline = PIDS_GETSTR(CMDLINE);
@ -681,6 +717,7 @@ static struct el * select_procs (int *num)
#undef PIDS_GETUNT #undef PIDS_GETUNT
#undef PIDS_GETULL #undef PIDS_GETULL
#undef PIDS_GETSTR #undef PIDS_GETSTR
#undef PIDS_GETSTV
} }
static int signal_option(int *argc, char **argv) static int signal_option(int *argc, char **argv)
@ -718,10 +755,12 @@ static void parse_opts (int argc, char **argv)
SIGNAL_OPTION = CHAR_MAX + 1, SIGNAL_OPTION = CHAR_MAX + 1,
NS_OPTION, NS_OPTION,
NSLIST_OPTION, NSLIST_OPTION,
CGROUP_OPTION,
}; };
static const struct option longopts[] = { static const struct option longopts[] = {
{"signal", required_argument, NULL, SIGNAL_OPTION}, {"signal", required_argument, NULL, SIGNAL_OPTION},
{"count", no_argument, NULL, 'c'}, {"count", no_argument, NULL, 'c'},
{"cgroup", required_argument, NULL, CGROUP_OPTION},
{"delimiter", required_argument, NULL, 'd'}, {"delimiter", required_argument, NULL, 'd'},
{"list-name", no_argument, NULL, 'l'}, {"list-name", no_argument, NULL, 'l'},
{"list-full", no_argument, NULL, 'a'}, {"list-full", no_argument, NULL, 'a'},
@ -920,6 +959,12 @@ static void parse_opts (int argc, char **argv)
sigval.sival_int = atoi(optarg); sigval.sival_int = atoi(optarg);
use_sigqueue = true; use_sigqueue = true;
break; break;
case CGROUP_OPTION:
opt_cgroup = split_list (optarg, conv_str);
if (opt_cgroup == NULL)
usage ('?');
++criteria_count;
break;
case 'h': case 'h':
case '?': case '?':
usage (opt); usage (opt);