Subject: ps /proc-based lookup enhancement
To: None <tech-userlevel@netbsd.org>
From: Jaromir Dolecek <dolecek@ics.muni.cz>
List: tech-userlevel
Date: 10/07/1999 16:36:05
Hi,
I finally got to clean up some old patches I had. Basically, they
alter the /proc-based lookup so that more information is exstracted
from the procfs, namely process start time, whether it's session
leader and program's arguments (from /proc/<pid>/cmdline). It also
falls back to procfs when kvm_openfiles() (called on the very
beginning) fails.
If nobody would complain, I'll commit the changes shortly.
Jaromir
XXXXX
Index: extern.h
===================================================================
RCS file: /cvsroot/basesrc/bin/ps/extern.h,v
retrieving revision 1.13
diff -u -p -r1.13 extern.h
--- extern.h 1999/03/26 22:36:02 1.13
+++ extern.h 1999/10/07 14:22:09
@@ -68,7 +68,8 @@ void pmem __P((KINFO *, VARENT *));
void pnice __P((KINFO *, VARENT *));
void pri __P((KINFO *, VARENT *));
void printheader __P((void));
-struct kinfo_proc * procfs_getprocs __P((int, int, int*));
+KINFO *procfs_getprocs __P((int, int, int*));
+char **procfs_getargv __P((const struct kinfo_proc *, int));
void pvar __P((KINFO *, VARENT *));
void rssize __P((KINFO *, VARENT *));
void runame __P((KINFO *, VARENT *));
Index: print.c
===================================================================
RCS file: /cvsroot/basesrc/bin/ps/print.c,v
retrieving revision 1.43
diff -u -p -r1.43 print.c
--- print.c 1999/07/23 08:56:14 1.43
+++ print.c 1999/10/07 14:22:10
@@ -68,7 +68,7 @@ __RCSID("$NetBSD: print.c,v 1.43 1999/07
#include "ps.h"
extern kvm_t *kd;
-extern int needenv, needcomm, commandonly;
+extern int needenv, needcomm, commandonly, dontuseprocfs;
static char *cmdpart __P((char *));
static void printval __P((char *, VAR *));
@@ -144,6 +144,7 @@ command(ki, ve)
{
VAR *v;
int left;
+ static int use_procfs=0;
char **argv, **p, *name;
v = ve->var;
@@ -156,7 +157,7 @@ command(ki, ve)
left = v->width;
} else
left = -1;
- if (needenv) {
+ if (needenv && kd) {
argv = kvm_getenvv(kd, ki->ki_p, termwidth);
if ((p = argv) != NULL) {
while (*p) {
@@ -169,7 +170,13 @@ command(ki, ve)
if (needcomm) {
name = KI_PROC(ki)->p_comm;
if (!commandonly) {
- argv = kvm_getargv(kd, ki->ki_p, termwidth);
+ argv = NULL;
+ if (kd && !use_procfs)
+ argv = kvm_getargv(kd, ki->ki_p, termwidth);
+ if (argv == NULL && !dontuseprocfs) {
+ argv = procfs_getargv(ki->ki_p, termwidth);
+ use_procfs = 1;
+ }
if ((p = argv) != NULL) {
while (*p) {
fmt_puts(*p, &left);
@@ -182,6 +189,10 @@ command(ki, ve)
fmt_puts(name, &left);
fmt_putc(')', &left);
}
+ if (use_procfs) {
+ free(argv[0]);
+ free(argv);
+ }
} else {
fmt_puts(name, &left);
}
@@ -548,7 +559,7 @@ getpcpu(k)
static int failure;
if (!nlistread)
- failure = donlist();
+ failure = (kd) ? donlist() : 1;
if (failure)
return (0.0);
@@ -587,7 +598,7 @@ getpmem(k)
int szptudot;
if (!nlistread)
- failure = donlist();
+ failure = (kd) ? donlist() : 1;
if (failure)
return (0.0);
Index: procfs_ops.c
===================================================================
RCS file: /cvsroot/basesrc/bin/ps/procfs_ops.c,v
retrieving revision 1.5
diff -u -p -r1.5 procfs_ops.c
--- procfs_ops.c 1999/05/09 19:23:38 1.5
+++ procfs_ops.c 1999/10/07 14:22:10
@@ -42,6 +42,7 @@
#include <sys/sysctl.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/resource.h> /* for rusage */
#include <fcntl.h>
#include <dirent.h>
@@ -52,6 +53,8 @@
#include <err.h>
#include <kvm.h>
+#include "ps.h"
+
/* Assume that no process status file will ever be larger than this. */
#define STATUS_SIZE 8192
@@ -70,8 +73,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 *));
+static int parsekinfo __P((const char *, KINFO *));
+KINFO *procfs_getprocs __P((int, int, int *));
static int
verify_procfs_fd(fd, path)
@@ -97,15 +100,16 @@ verify_procfs_fd(fd, path)
}
static int
-parsekinfo(path, kp)
+parsekinfo(path, ki)
const char *path;
- struct kinfo_proc *kp;
+ KINFO *ki;
{
char fullpath[MAXPATHLEN];
- int dirfd, fd, nbytes, devmajor, devminor;
+ int fd, nbytes, devmajor, devminor;
struct timeval usertime, systime, starttime;
char buff[STATUS_SIZE];
char flagstr[256];
+ struct kinfo_proc *kp = ki->ki_p;
/*
* Verify that /proc/<pid> is a procfs file (and that no one has
@@ -131,7 +135,6 @@ parsekinfo(path, kp)
* Don't print warning, as the process may have died since our
* scan of the directory entries.
*/
- close(fd);
return -1; /* Process may no longer exist. */
}
@@ -174,6 +177,14 @@ parsekinfo(path, kp)
kp->kp_proc.p_rtime.tv_sec = usertime.tv_sec + systime.tv_sec;
kp->kp_proc.p_rtime.tv_usec = usertime.tv_usec + systime.tv_usec;
+ /* if starttime.[u]sec is != -1, it's in-memory process */
+ if (starttime.tv_sec != -1 && starttime.tv_usec != -1) {
+ kp->kp_proc.p_flag |= P_INMEM;
+ ki->ki_u.u_valid = 1;
+ ki->ki_u.u_start.tv_sec = starttime.tv_sec;
+ ki->ki_u.u_start.tv_usec = starttime.tv_usec;
+ }
+
/*
* CPU time isn't shown unless the ki_u.u_valid flag is set.
* Unfortunately, we don't have access to that here.
@@ -183,10 +194,14 @@ parsekinfo(path, kp)
if (strstr(flagstr, "ctty"))
kp->kp_proc.p_flag |= P_CONTROLT;
+ /* Set the flag for whether or not this process is session leader */
+ if (strstr(flagstr, "sldr"))
+ kp->kp_eproc.e_flag |= EPROC_SLEADER;
+
return 0;
}
-struct kinfo_proc *
+KINFO *
procfs_getprocs(op, arg, cnt)
int op, arg;
int *cnt;
@@ -195,6 +210,7 @@ procfs_getprocs(op, arg, cnt)
int procdirfd, nbytes, knum = 0, maxknum = 0;
char *direntbuff;
struct kinfo_proc *kp;
+ KINFO *ki;
int mib[4];
size_t len;
struct statfs procfsstat;
@@ -250,8 +266,8 @@ procfs_getprocs(op, arg, cnt)
err(1, "sysctl to fetch maxproc");
maxknum *= 2; /* Double it, to be really paranoid. */
- kp = (struct kinfo_proc *) malloc(sizeof(struct kinfo_proc) * maxknum);
- memset(kp, 0, sizeof(struct kinfo_proc) * maxknum);
+ kp = (struct kinfo_proc *) calloc(sizeof(struct kinfo_proc)*maxknum, 1);
+ ki = (KINFO *) calloc(sizeof(KINFO)*maxknum, 1);
/* Read in a batch of entries at a time. */
while ((knum < maxknum) &&
@@ -268,7 +284,8 @@ procfs_getprocs(op, arg, cnt)
continue;
if (strcmp(dp->d_name, "curproc") == 0)
continue;
- if (parsekinfo(dp->d_name, &kp[knum]) != 0)
+ ki[knum].ki_p = &kp[knum];
+ if (parsekinfo(dp->d_name, &ki[knum]) != 0)
continue;
/*
* Now check some of the flags. If the newest entry
@@ -323,5 +340,80 @@ procfs_getprocs(op, arg, cnt)
*cnt = knum;
close(procdirfd);
- return kp;
+ /* free unused memory */
+ if (knum < maxknum) {
+ kp = realloc(kp, sizeof(*kp) * knum);
+ ki = realloc(ki, sizeof(*ki) * knum);
+ for(; knum >= 0; knum--)
+ ki[knum].ki_p = &kp[knum];
+ }
+ return ki;
+}
+
+/*
+ * return process arguments, possibly ones used when exec()ing
+ * the process; return the array as two element array, first is
+ * argv[0], second element is all the other args separated by spaces
+ */
+char **
+procfs_getargv(kp, nchr)
+ const struct kinfo_proc *kp;
+ int nchr;
+{
+ char fullpath[MAXPATHLEN], *buf, *name, *args, **argv;
+ int fd, num;
+ ssize_t len;
+ size_t idx;
+
+ /* Open /proc/<pid>/cmdline, and parse it into the argv array */
+ snprintf(fullpath, MAXPATHLEN, "/proc/%d/cmdline", kp->kp_proc.p_pid);
+ fd = open(fullpath, O_RDONLY, 0);
+ if (fd == -1 || verify_procfs_fd(fd, fullpath)) {
+ /*
+ * Don't print warning, as the process may have died since our
+ * scan of the directory entries.
+ */
+ return NULL; /* Process may no longer exist. */
+ }
+
+ buf = (char *)malloc(nchr+1);
+ len = read(fd, buf, nchr);
+ close(fd);
+ if (len == -1) {
+ warnx("procfs_getargv");
+ return NULL;
+ }
+
+ num = 1;
+ args = NULL;
+ name = buf;
+ /* substitute any \0's with space */
+ for(idx=0; idx < len; idx++) {
+ if (buf[idx] == '\0') {
+ if (!args)
+ args = &buf[idx+1];
+ else
+ buf[idx] = ' ';
+ num++;
+ }
+ }
+ buf[len] = '\0'; /* end the string */
+
+ /* if the name is the same as the p_comm, just enclosed
+ * in parentheses, remove the parentheses */
+ if (num == 1 && name[0] == '(' && name[len-1] == ')'
+ && strncmp(name+1, kp->kp_proc.p_comm, len-2) == 0)
+ {
+ len -= 2;
+ strncpy(name, name+1, len);
+ name[len] = '\0';
+ }
+
+ argv = (char **) malloc(3*sizeof(char *));
+ argv[0] = name;
+ argv[1] = args;
+ argv[2] = NULL;
+
+ return argv;
}
+
Index: ps.c
===================================================================
RCS file: /cvsroot/basesrc/bin/ps/ps.c,v
retrieving revision 1.28
diff -u -p -r1.28 ps.c
--- ps.c 1999/03/27 21:38:08 1.28
+++ ps.c 1999/10/07 14:22:10
@@ -305,8 +305,14 @@ main(argc, argv)
(void)setegid(egid);
kd = kvm_openfiles(nlistf, memf, swapf, O_RDONLY, errbuf);
- if (kd == 0)
- errx(1, "%s", errbuf);
+ if (kd == 0) {
+ if (dontuseprocfs)
+ errx(1, "%s", errbuf);
+ else {
+ warnx("kvm_openfiles: %s", errbuf);
+ fprintf(stderr, "ps: falling back to /proc-based lookup\n");
+ }
+ }
if (nlistf == NULL && memf == NULL && swapf == NULL)
(void)setgid(getgid());
@@ -322,8 +328,16 @@ main(argc, argv)
/*
* select procs
*/
- if ((kp = kvm_getprocs(kd, what, flag, &nentries)) == 0)
+ if (kd && (kp = kvm_getprocs(kd, what, flag, &nentries)) != 0)
{
+ if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL)
+ err(1, "%s", "");
+ for (i = nentries; --i >= 0; ++kp) {
+ kinfo[i].ki_p = kp;
+ if (needuser)
+ saveuser(&kinfo[i]);
+ }
+ } else {
/* If/when the /proc-based code is ripped out
* again, make sure all references to the -K
* option are also pulled (getopt(), usage(),
@@ -339,14 +353,15 @@ main(argc, argv)
* mounted) to grab as much information as we can.
* The guts of emulating kvm_getprocs() is in
* the file procfs_ops.c. */
- warnx("%s.", kvm_geterr(kd));
+ if (kd)
+ warnx("%s.", kvm_geterr(kd));
if (dontuseprocfs) {
exit(1);
}
/* procfs_getprocs supports all but the
* KERN_PROC_RUID flag. */
- kp=procfs_getprocs(what, flag, &nentries);
- if (kp == 0) {
+ kinfo=procfs_getprocs(what, flag, &nentries);
+ if (kinfo == 0) {
errx(1, "fallback /proc-based lookup also failed. %s",
"Giving up...");
}
@@ -354,13 +369,7 @@ main(argc, argv)
"Warning: /proc does not provide ",
"valid data for all fields.\n");
}
- if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL)
- err(1, "%s", "");
- for (i = nentries; --i >= 0; ++kp) {
- kinfo[i].ki_p = kp;
- if (needuser)
- saveuser(&kinfo[i]);
- }
+
/*
* print header
*/
@@ -425,7 +434,7 @@ saveuser(ki)
struct usave *usp;
usp = &ki->ki_u;
- if (kvm_read(kd, (u_long)&KI_PROC(ki)->p_addr->u_stats,
+ if (kd && kvm_read(kd, (u_long)&KI_PROC(ki)->p_addr->u_stats,
(char *)&pstats, sizeof(pstats)) == sizeof(pstats)) {
/*
* The u-area might be swapped out, and we can't get
XXXXX
--
Jaromir Dolecek <jdolecek@NetBSD.org> http://www.ics.muni.cz/~dolecek/
"The only way how to get rid temptation is to yield to it." -- Oscar Wilde