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

Reply via email to