Subject: bin/7772: ps(1) should fallback to procfs when kvm_getargv() fails
To: None <gnats-bugs@gnats.netbsd.org>
From: Jaromir Dolecek <dolecek@ics.muni.cz>
List: netbsd-bugs
Date: 06/14/1999 04:50:54
>Number:         7772
>Category:       bin
>Synopsis:       ps(1) should fallback to procfs when kvm_getargv() fails
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    bin-bug-people (Utility Bug People)
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Mon Jun 14 04:50:01 1999
>Last-Modified:
>Originator:     Jaromir Dolecek
>Organization:
	ICS MU, Brno, Czech Republic
>Release:        NetBSD-19990611
>Environment:
System: NetBSD jdolecek.per4mance.cz 1.4C NetBSD 1.4C (SARUMAN) #4: Fri Jun 11 10:09:07 MEST 1999 dolecek@jdolecek.per4mance.cz:/usr/src/sys/arch/i386/compile/SARUMAN i386


>Description:
	When kvm_* routines fail (typically when kernel doesn't match userland),
	much of the information can be obtained via procfs, if it's present.
	Currently, it's used to retrieve list of processes and their status,
	which I found REALLY very cool after I upgraded to 1.4C. However,
	argv is not retrieved, even through it's available through
	/proc/#/cmdline, added few days before 1.4 release cut.
>How-To-Repeat:
	run 1.4C kernel with 1.4 userland
	watch ps falling back to /proc, but printing all processes
		as "(name_of_process)"
>Fix:
	This is short two hour cut on the thing.

	If only top would fallback to procfs too, I'd be quite happy ;-)
	I'm also curious why STAT is printed as ?W< for all processess -
	the information is not exported via procfs (didn't look closely) ?

--- extern.h.orig	Mon Jun 14 11:08:18 1999
+++ extern.h	Mon Jun 14 13:24:53 1999
@@ -69,6 +69,7 @@ void	 pnice __P((KINFO *, VARENT *));
 void	 pri __P((KINFO *, VARENT *));
 void	 printheader __P((void));
 struct kinfo_proc *	 procfs_getprocs __P((int, int, int*));
+int	 procfs_getargv __P((const struct kinfo_proc *, char *, int));
 void	 pvar __P((KINFO *, VARENT *));
 void	 rssize __P((KINFO *, VARENT *));
 void	 runame __P((KINFO *, VARENT *));
--- print.c.orig	Mon Jun 14 11:06:55 1999
+++ print.c	Mon Jun 14 13:34:20 1999
@@ -143,8 +143,8 @@ command(ki, ve)
 	VARENT *ve;
 {
 	VAR *v;
-	int left;
-	char **argv, **p, *name;
+	int left, print_title;
+	char **argv, **p, *name, *argv_str=NULL;
 
 	v = ve->var;
 	if (ve->next != NULL || termwidth != UNLIMITED) {
@@ -170,6 +170,7 @@ command(ki, ve)
 		name = KI_PROC(ki)->p_comm;
 		if (!commandonly) {
 			argv = kvm_getargv(kd, ki->ki_p, termwidth);
+			print_title = 1;
 			if ((p = argv) != NULL) {
 				while (*p) {
 					fmt_puts(*p, &left);
@@ -177,7 +178,22 @@ command(ki, ve)
 					fmt_putc(' ', &left);
 				}
 			}
-			if (titlecmp(name, argv)) {
+			else {
+				/* fallback to procfs-based lookup when kvm
+				 * routine fails; note procfs_getargv()
+				 * returns plain char* and not an array */
+				/* see usage of procfs_getprocs() for additional
+				 * comments about /proc-based code */
+				
+				/* allocate buffer when used for first time */
+				if (!argv_str) argv_str = alloca(termwidth+1);
+				if (procfs_getargv(ki->ki_p, argv_str,
+					termwidth)) {
+					fmt_puts(argv_str, &left);
+					print_title = 0;
+				}
+			}
+			if (print_title && titlecmp(name, argv)) {
 				fmt_putc('(', &left);
 				fmt_puts(name, &left);
 				fmt_putc(')', &left);
--- procfs_ops.c.orig	Tue May 18 17:42:37 1999
+++ procfs_ops.c	Mon Jun 14 13:39:01 1999
@@ -72,6 +72,8 @@
 static int verify_procfs_fd __P((int, const char *));
 static int parsekinfo __P((const char *, struct kinfo_proc *));
 struct kinfo_proc *procfs_getprocs __P((int, int, int *));
+int	 procfs_getargv __P((const struct kinfo_proc *, char *, int));
+
 
 static int
 verify_procfs_fd(fd, path)
@@ -324,4 +326,41 @@ procfs_getprocs(op, arg, cnt)
 	*cnt = knum;
 	close(procdirfd);
 	return kp;
+}
+
+/*
+ * get argv strings for process
+ */
+int
+procfs_getargv(kp, buf, nchr)
+	const struct kinfo_proc *kp;
+	char *buf;
+	int nchr;
+{
+	char filename[50];
+	int fd;
+	size_t len, idx;
+	
+	snprintf(filename, sizeof(filename),
+		"/proc/%d/cmdline", kp->kp_proc.p_pid);
+	if ((fd = open(filename, O_RDONLY)) == -1) {
+		/* ignore errors - it's pretty normal we attempt to
+		 * read info about process which finished already */
+		return 0;
+	}
+
+	/* verify the file is on procfs filesystem */
+	if (verify_procfs_fd(fd, filename)) {
+		/* the warning has been printed alredy, so just quit */
+		return 0;
+	}
+
+	/* read in the argv string */
+	len = read(fd, buf, nchr);
+	buf[len] = '\0';
+	/* now find and substitute any \0 in the string for space */
+	for(idx=0; idx < len; idx++) {
+		if (buf[idx] == '\0') buf[idx] = ' ';
+	}
+	return 1;
 }
>Audit-Trail:
>Unformatted: