]> granicus.if.org Git - procps-ng/commitdiff
pgrep: Match on cgroup v2 paths
authorCraig Small <csmall@dropbear.xyz>
Tue, 26 Oct 2021 09:56:19 +0000 (20:56 +1100)
committerCraig Small <csmall@dropbear.xyz>
Tue, 26 Oct 2021 09:56:19 +0000 (20:56 +1100)
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>
NEWS
pgrep.1
pgrep.c

diff --git a/NEWS b/NEWS
index f4d3186b08cdd31f421267abadd3c842c73e5697..987c5d10c5eaa24c09914e703303aa5d2cca2258 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -9,6 +9,7 @@ procps-ng-NEXT
   * pkill: Check for lt- variants of program name          issue #192
   * pgrep: Add newline after regex error message           merge #91
   * 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 IO Accounting fields                           issue #184
   * ps: Add PSS and USS fields                             issue #112
diff --git a/pgrep.1 b/pgrep.1
index 301c895bb4ecfdc7407fba259687a5c5b3cdaef0..0d836844f2e285fa81786eaee62e63577838b645 100644 (file)
--- a/pgrep.1
+++ b/pgrep.1
@@ -7,7 +7,7 @@
 .\" the Free Software Foundation; either version 2 of the License, or
 .\" (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
 pgrep, pkill, pidwait \- look up, signal, or wait for processes based on name and other attributes
 .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...
 Match only processes which match the process state.
 .TP
+\fB\-\-cgroup \fIname\fP,...
+Match on provided control group (cgroup) v2 name. See
+.BR cgroups (8)
+.TP
 \fB\-\-ns \fIpid\fP
 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
@@ -282,7 +286,8 @@ Defunct processes are reported.
 .BR killall (1),
 .BR skill (1),
 .BR kill (1),
-.BR kill (2)
+.BR kill (2),
+.BR cgroups (8)
 .SH AUTHOR
 .UR kjetilho@ifi.uio.no
 Kjetil Torgrim Homme
diff --git a/pgrep.c b/pgrep.c
index c5cd6f1d064175d22aa8cbbc3a64dcf5d947f418..6457fd40c7a5407c869e758fdddca7d7fb1a5cc2 100644 (file)
--- a/pgrep.c
+++ b/pgrep.c
@@ -72,11 +72,13 @@ enum pids_item Items[] = {
     PIDS_CMD,
     PIDS_CMDLINE,
     PIDS_STATE,
-    PIDS_TIME_ELAPSED
+    PIDS_TIME_ELAPSED,
+    PIDS_CGROUP_V
 };
 enum rel_items {
     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 { \
        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_ruid = NULL;
 static struct el *opt_nslist = NULL;
+static struct el *opt_cgroup = NULL;
 static char *opt_pattern = NULL;
 static char *opt_pidfile = 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(_(" -L, --logpidfile          fail if PID file is not locked\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"
         "                           namespace as <pid>\n"), fp);
     fputs(_(" --nslist <ns,...>         list which namespaces will be considered for\n"
@@ -455,6 +459,35 @@ static int match_ns (const int pid,
     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)
 {
     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_GETSTR(e) PIDS_VAL(EU_ ## e, str, 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 procps_ns nsp;
     struct pids_stack *stack;
@@ -568,7 +602,7 @@ static struct el * select_procs (int *num)
               _("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,
               _("Unable to create pid info structure"));
     which = PIDS_FETCH_TASKS_ONLY;
@@ -607,6 +641,8 @@ static struct el * select_procs (int *num)
             match = match_strlist(PIDS_GETSTR(TTYNAME), opt_term);
         else if (opt_runstates && ! strchr(opt_runstates, PIDS_GETSCH(STA)))
             match = 0;
+        else if (opt_cgroup && ! match_cgroup_list (PIDS_GETSTV(CGROUP), opt_cgroup))
+            match = 0;
 
         task_cmdline = PIDS_GETSTR(CMDLINE);
 
@@ -681,6 +717,7 @@ static struct el * select_procs (int *num)
 #undef PIDS_GETUNT
 #undef PIDS_GETULL
 #undef PIDS_GETSTR
+#undef PIDS_GETSTV
 }
 
 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,
         NS_OPTION,
         NSLIST_OPTION,
+        CGROUP_OPTION,
     };
     static const struct option longopts[] = {
         {"signal", required_argument, NULL, SIGNAL_OPTION},
         {"count", no_argument, NULL, 'c'},
+        {"cgroup", required_argument, NULL, CGROUP_OPTION},
         {"delimiter", required_argument, NULL, 'd'},
         {"list-name", no_argument, NULL, 'l'},
         {"list-full", no_argument, NULL, 'a'},
@@ -920,6 +959,12 @@ static void parse_opts (int argc, char **argv)
             sigval.sival_int = atoi(optarg);
             use_sigqueue = true;
             break;
+        case CGROUP_OPTION:
+            opt_cgroup = split_list (optarg, conv_str);
+            if (opt_cgroup == NULL)
+                usage ('?');
+            ++criteria_count;
+            break;
         case 'h':
         case '?':
             usage (opt);