This patch implements the -G, -U, and -p options for ps according to POSIX.

Why -g and -u are not implemented: In POSIX they are XSI. On BSD platforms, -g
are not generally supported,
and -u have different meanings than in POSIX.

Why -t is not implemented: Because there are subtle differences between
different implementations, and these
implementations conflict with the XSI scheme described by POSIX.

References:

https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html
https://man.freebsd.org/cgi/man.cgi?ps(1)
https://man.netbsd.org/ps.1
https://man.openbsd.org/ps.1
https://man7.org/linux/man-pages/man1/ps.1.html

---
From 3161a935a19285503316814154ce39a80579cbc9 Mon Sep 17 00:00:00 2001
From: chirsz-ever <[email protected]>
Date: Sun, 31 Aug 2025 00:26:54 +0800
Subject: [PATCH] ps: add options -G, -U and -p

---
 include/libbb.h |   6 ++-
 libbb/procps.c  |   4 +-
 procps/ps.c     | 133 +++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 138 insertions(+), 5 deletions(-)

diff --git a/include/libbb.h b/include/libbb.h
index 4b3319824..c00ef41c9 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -2105,9 +2105,11 @@ typedef struct procps_status_t {
     unsigned sid;
     unsigned uid;
     unsigned gid;
-#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
+#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS || ENABLE_FEATURE_PS_FILTERS
     unsigned ruid;
     unsigned rgid;
+#endif
+#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
     int niceness;
 #endif
     unsigned tty_major,tty_minor;
@@ -2159,7 +2161,7 @@ enum {
     PSSCAN_START_TIME = 1 << 18,
     PSSCAN_CPU      = (1 << 19) * ENABLE_FEATURE_TOP_SMP_PROCESS,
     PSSCAN_NICE     = (1 << 20) * ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS,
-    PSSCAN_RUIDGID  = (1 << 21) * ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS,
+    PSSCAN_RUIDGID  = (1 << 21) * (ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
|| ENABLE_FEATURE_PS_FILTERS),
     PSSCAN_TASKS    = (1 << 22) * ENABLE_FEATURE_SHOW_THREADS,
 };
 //procps_status_t* alloc_procps_scan(void) FAST_FUNC;
diff --git a/libbb/procps.c b/libbb/procps.c
index de640d29e..5ed21adc7 100644
--- a/libbb/procps.c
+++ b/libbb/procps.c
@@ -474,7 +474,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t*
sp, int flags)
         if (flags & PSSCAN_SMAPS)
             procps_read_smaps(pid, sp);
 #endif /* TOPMEM */
-#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
+#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS || ENABLE_FEATURE_PS_FILTERS
         if (flags & PSSCAN_RUIDGID) {
             FILE *file;

@@ -496,7 +496,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t*
sp, int flags)
                 fclose(file);
             }
         }
-#endif /* PS_ADDITIONAL_COLUMNS */
+#endif /* PS_ADDITIONAL_COLUMNS || PS_FILTERS */
         if (flags & PSSCAN_EXE) {
             strcpy(filename_tail, "exe");
             free(sp->exe);
diff --git a/procps/ps.c b/procps/ps.c
index 5b521aebd..48c8ca34d 100644
--- a/procps/ps.c
+++ b/procps/ps.c
@@ -48,6 +48,10 @@
 //config:    bool "Enable -o rgroup, -o ruser, -o nice specifiers"
 //config:    default y
 //config:    depends on (PS || MINIPS) && DESKTOP
+//config:config FEATURE_PS_FILTERS
+//config:    bool "Enable filter options (-G -U -p)"
+//config:    default y
+//config:    depends on (PS || MINIPS) && DESKTOP

 //                 APPLET_NOEXEC:name    main location suid_type     help
 //applet:IF_PS(    APPLET_NOEXEC(ps,     ps,  BB_DIR_BIN, BB_SUID_DROP, ps))
@@ -62,10 +66,23 @@
 //usage:       "[-o COL1,COL2=HEADER]" IF_FEATURE_SHOW_THREADS(" [-T]")
 //usage:#define ps_full_usage "\n\n"
 //usage:       "Show list of processes\n"
+//usage:    IF_FEATURE_PS_FILTERS(
+//usage:     "\n    -G group[,group...]    Select rows by real group
ids or names"
+//usage:    )
 //usage:     "\n    -o COL1,COL2=HEADER    Select columns for display"
+//usage:    IF_FEATURE_PS_FILTERS(
+//usage:     "\n    -p pid[,pid...]        Select rows by process
ids"
+//usage:    )
 //usage:    IF_FEATURE_SHOW_THREADS(
 //usage:     "\n    -T            Show threads"
 //usage:    )
+//usage:    IF_FEATURE_PS_FILTERS(
+//usage:     "\n    -U user[,user...]    Select rows by real user
ids or names"
+//usage:    )
+//usage:    IF_FEATURE_PS_FILTERS(
+//usage:     "\n\n-G, -U and -p accept comma or space as separator."
+//usage:     "\n-G and -U accept either names or numeric ids."
+//usage:    )
 //usage:
 //usage:#else /* !ENABLE_DESKTOP */
 //usage:
@@ -541,6 +558,104 @@ static void format_process(const procps_status_t *ps)
     printf("%.*s\n", terminal_width, buffer);
 }

+#if ENABLE_FEATURE_PS_FILTERS
+/* store id to llist_t::data */
+static llist_t *opt_G, *opt_G_items;
+static llist_t *opt_U, *opt_U_items;
+static llist_t *opt_p, *opt_p_items;
+
+static void parse_comma_space_list(char *opt, llist_t **items, void
*(*parser)(const char *))
+{
+    char *p = opt;
+    while (*p) {
+        char *start = p;
+        while (*p && *p != ',' && !isspace(*p)) {
+            ++p;
+        }
+        if (*p) {
+            *p++ = '\0';
+            while (isspace(*p)) {
+                ++p;
+            }
+        }
+        if (*start)
+            llist_add_to(items, parser(start));
+    }
+}
+
+static void* parse_rgid(const char *str)
+{
+    char *endptr;
+    unsigned long rgid = strtoul(str, &endptr, 0);
+    if (*endptr != '\0') { /* try as login name */
+        /* die if unknown group */
+        rgid = xgetgrnam(str)->gr_gid;
+    }
+    if (rgid >= (gid_t)-1L)
+        bb_error_msg_and_die("group ID out of range: %lu",
rgid);
+    return (void*)(uintptr_t)rgid;
+}
+
+static void* parse_ruid(const char *str)
+{
+    char *endptr;
+    unsigned long ruid = strtoul(str, &endptr, 0);
+    if (*endptr != '\0') { /* try as login name */
+        /* die if unknown user */
+        ruid = xgetpwnam(str)->pw_uid;
+    }
+    if (ruid >= (uid_t)-1L)
+        bb_error_msg_and_die("user ID out of range: %lu", ruid);
+    return (void*)(uintptr_t)ruid;
+}
+
+static void* parse_pid(const char *str)
+{
+    char *endptr;
+    unsigned long val = strtoul(str, &endptr, 0);
+    if (*endptr != '\0')
+        bb_error_msg_and_die("bad pid: '%s'", str);
+    if (val >= (unsigned)-1L)
+        bb_error_msg_and_die("pid out of range: %lu", val);
+    return (void*)val;
+}
+
+static void parse_filter_options(void)
+{
+    while (opt_G)
+        parse_comma_space_list(llist_pop(&opt_G), &opt_G_items,
parse_rgid);
+    while (opt_U)
+        parse_comma_space_list(llist_pop(&opt_U), &opt_U_items,
parse_ruid);
+    while (opt_p)
+        parse_comma_space_list(llist_pop(&opt_p), &opt_p_items,
parse_pid);
+}
+
+static int is_in_list_num(const llist_t* items, unsigned num)
+{
+    while (items) {
+        if (items->data == (void*)(uintptr_t)num)
+            return TRUE;
+        items = items->link;
+    }
+    return FALSE;
+}
+
+static int is_filtered_process(const procps_status_t *p)
+{
+    /* if no filters, show all processes */
+    if (!opt_G_items && !opt_U_items && !opt_p_items)
+        return TRUE;
+    /* if any filter exists, the process should match at least one filter
*/
+    if (opt_G_items && is_in_list_num(opt_G_items, p->rgid))
+        return TRUE;
+    if (opt_U_items && is_in_list_num(opt_U_items, p->ruid))
+        return TRUE;
+    if (opt_p_items && is_in_list_num(opt_p_items, p->pid))
+        return TRUE;
+    return FALSE;
+}
+#endif
+
 #if ENABLE_SELINUX
 # define SELINUX_O_PREFIX "label,"
 # define DEFAULT_O_STR    (SELINUX_O_PREFIX "pid,user"
IF_FEATURE_PS_TIME(",time") ",args")
@@ -567,6 +682,10 @@ int ps_main(int argc UNUSED_PARAM, char **argv)
         OPT_f = (1 << 6),
         OPT_l = (1 << 7),
         OPT_T = (1 << 8) * ENABLE_FEATURE_SHOW_THREADS,
+        OPT_G = (1 << 8) * (1 << ENABLE_FEATURE_SHOW_THREADS) *
ENABLE_FEATURE_PS_FILTERS,
+        OPT_U = (1 << 9) * (1 << ENABLE_FEATURE_SHOW_THREADS) *
ENABLE_FEATURE_PS_FILTERS,
+        OPT_p = (1 << 10) * (1 << ENABLE_FEATURE_SHOW_THREADS) *
ENABLE_FEATURE_PS_FILTERS,
+        OPT_t = (1 << 11) * (1 << ENABLE_FEATURE_SHOW_THREADS) *
ENABLE_FEATURE_PS_FILTERS,
     };

     INIT_G();
@@ -595,7 +714,8 @@ int ps_main(int argc UNUSED_PARAM, char **argv)
 #if ENABLE_SELINUX || ENABLE_FEATURE_SHOW_THREADS
     opt =
 #endif
-        getopt32(argv, "Zo:*aAdefl"IF_FEATURE_SHOW_THREADS("T"),
&opt_o);
+        getopt32(argv,
"Zo:*aAdefl"IF_FEATURE_SHOW_THREADS("T")IF_FEATURE_PS_FILTERS("G:*U:*p:*"),
+            &opt_o, &opt_G, &opt_U, &opt_p);

     if (opt_o) {
         do {
@@ -620,6 +740,13 @@ int ps_main(int argc UNUSED_PARAM, char **argv)
     if (opt & OPT_T)
         need_flags |= PSSCAN_TASKS;
 #endif
+#if ENABLE_FEATURE_PS_FILTERS
+    if (opt & (OPT_G | OPT_U))
+        need_flags |= PSSCAN_RUIDGID;
+    if (opt & OPT_p)
+        need_flags |= PSSCAN_PID;
+    parse_filter_options();
+#endif

     /* Was INT_MAX, but some libc's go belly up with printf("%.*s")
      * and such large widths */
@@ -634,6 +761,10 @@ int ps_main(int argc UNUSED_PARAM, char **argv)

     p = NULL;
     while ((p = procps_scan(p, need_flags)) != NULL) {
+#if ENABLE_FEATURE_PS_FILTERS
+        if (!is_filtered_process(p))
+            continue;
+#endif
         format_process(p);
     }

--
2.50.1


_______________________________________________
busybox mailing list
[email protected]
https://lists.busybox.net/mailman/listinfo/busybox

Reply via email to