The previous email was automatically wrapped by Thunderbird, so in this one the
patch is sent as an attachment.
---
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