diff --git a/lib/libkvm/kvm_proc.c b/lib/libkvm/kvm_proc.c index 876af50..d8fee73 100644 --- a/lib/libkvm/kvm_proc.c +++ b/lib/libkvm/kvm_proc.c @@ -623,276 +623,16 @@ _kvm_realloc(kvm_t *kd, void *p, size_t n) return (np); } -#ifndef MAX -#define MAX(a, b) ((a) > (b) ? (a) : (b)) -#endif - -/* - * Read in an argument vector from the user address space of process kp. - * addr if the user-space base address of narg null-terminated contiguous - * strings. This is used to read in both the command arguments and - * environment strings. Read at most maxcnt characters of strings. - */ -static char ** -kvm_argv(kvm_t *kd, const struct kinfo_proc *kp, u_long addr, int narg, - int maxcnt) -{ - char *np, *cp, *ep, *ap; - u_long oaddr = -1; - int len, cc; - char **argv; - - /* - * Check that there aren't an unreasonable number of arguments, - * and that the address is in user space. Special test for - * VM_MIN_ADDRESS as it evaluates to zero, but is not a simple zero - * constant for some archs. We cannot use the pre-processor here and - * for some archs the compiler would trigger a signedness warning. - */ - if (narg > 512 || addr + 1 < VM_MIN_ADDRESS + 1 || addr >= VM_MAXUSER_ADDRESS) - return (0); - - /* - * kd->argv : work space for fetching the strings from the target - * process's space, and is converted for returning to caller - */ - if (kd->argv == 0) { - /* - * Try to avoid reallocs. - */ - kd->argc = MAX(narg + 1, 32); - kd->argv = (char **)_kvm_malloc(kd, kd->argc * - sizeof(*kd->argv)); - if (kd->argv == 0) - return (0); - } else if (narg + 1 > kd->argc) { - kd->argc = MAX(2 * kd->argc, narg + 1); - kd->argv = (char **)_kvm_realloc(kd, kd->argv, kd->argc * - sizeof(*kd->argv)); - if (kd->argv == 0) - return (0); - } - /* - * kd->argspc : returned to user, this is where the kd->argv - * arrays are left pointing to the collected strings. - */ - if (kd->argspc == 0) { - kd->argspc = (char *)_kvm_malloc(kd, PAGE_SIZE); - if (kd->argspc == 0) - return (0); - kd->arglen = PAGE_SIZE; - } - /* - * kd->argbuf : used to pull in pages from the target process. - * the strings are copied out of here. - */ - if (kd->argbuf == 0) { - kd->argbuf = (char *)_kvm_malloc(kd, PAGE_SIZE); - if (kd->argbuf == 0) - return (0); - } - - /* Pull in the target process'es argv vector */ - cc = sizeof(char *) * narg; - if (kvm_uread(kd, kp, addr, (char *)kd->argv, cc) != cc) - return (0); - /* - * ap : saved start address of string we're working on in kd->argspc - * np : pointer to next place to write in kd->argspc - * len: length of data in kd->argspc - * argv: pointer to the argv vector that we are hunting around the - * target process space for, and converting to addresses in - * our address space (kd->argspc). - */ - ap = np = kd->argspc; - argv = kd->argv; - len = 0; - /* - * Loop over pages, filling in the argument vector. - * Note that the argv strings could be pointing *anywhere* in - * the user address space and are no longer contiguous. - * Note that *argv is modified when we are going to fetch a string - * that crosses a page boundary. We copy the next part of the string - * into to "np" and eventually convert the pointer. - */ - while (argv < kd->argv + narg && *argv != 0) { - - /* get the address that the current argv string is on */ - addr = (u_long)*argv & ~(PAGE_SIZE - 1); - - /* is it the same page as the last one? */ - if (addr != oaddr) { - if (kvm_uread(kd, kp, addr, kd->argbuf, PAGE_SIZE) != - PAGE_SIZE) - return (0); - oaddr = addr; - } - - /* offset within the page... kd->argbuf */ - addr = (u_long)*argv & (PAGE_SIZE - 1); - - /* cp = start of string, cc = count of chars in this chunk */ - cp = kd->argbuf + addr; - cc = PAGE_SIZE - addr; - - /* dont get more than asked for by user process */ - if (maxcnt > 0 && cc > maxcnt - len) - cc = maxcnt - len; - - /* pointer to end of string if we found it in this page */ - ep = memchr(cp, '\0', cc); - if (ep != 0) - cc = ep - cp + 1; - /* - * at this point, cc is the count of the chars that we are - * going to retrieve this time. we may or may not have found - * the end of it. (ep points to the null if the end is known) - */ - - /* will we exceed the malloc/realloced buffer? */ - if (len + cc > kd->arglen) { - int off; - char **pp; - char *op = kd->argspc; - - kd->arglen *= 2; - kd->argspc = (char *)_kvm_realloc(kd, kd->argspc, - kd->arglen); - if (kd->argspc == 0) - return (0); - /* - * Adjust argv pointers in case realloc moved - * the string space. - */ - off = kd->argspc - op; - for (pp = kd->argv; pp < argv; pp++) - *pp += off; - ap += off; - np += off; - } - /* np = where to put the next part of the string in kd->argspc*/ - /* np is kinda redundant.. could use "kd->argspc + len" */ - memcpy(np, cp, cc); - np += cc; /* inc counters */ - len += cc; - - /* - * if end of string found, set the *argv pointer to the - * saved beginning of string, and advance. argv points to - * somewhere in kd->argv.. This is initially relative - * to the target process, but when we close it off, we set - * it to point in our address space. - */ - if (ep != 0) { - *argv++ = ap; - ap = np; - } else { - /* update the address relative to the target process */ - *argv += cc; - } - - if (maxcnt > 0 && len >= maxcnt) { - /* - * We're stopping prematurely. Terminate the - * current string. - */ - if (ep == 0) { - *np = '\0'; - *argv++ = ap; - } - break; - } - } - /* Make sure argv is terminated. */ - *argv = 0; - return (kd->argv); -} - -static void -ps_str_a(struct ps_strings *p, u_long *addr, int *n) -{ - *addr = (u_long)p->ps_argvstr; - *n = p->ps_nargvstr; -} - -static void -ps_str_e (struct ps_strings *p, u_long *addr, int *n) -{ - *addr = (u_long)p->ps_envstr; - *n = p->ps_nenvstr; -} - /* - * Determine if the proc indicated by p is still active. - * This test is not 100% foolproof in theory, but chances of - * being wrong are very low. + * Get the command args or environment. */ -static int -proc_verify(const struct kinfo_proc *curkp) -{ - struct kinfo_proc newkp; - int mib[4]; - size_t len; - - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PID; - mib[3] = curkp->ki_pid; - len = sizeof(newkp); - if (sysctl(mib, 4, &newkp, &len, NULL, 0) == -1) - return (0); - return (curkp->ki_pid == newkp.ki_pid && - (newkp.ki_stat != SZOMB || curkp->ki_stat == SZOMB)); -} - static char ** -kvm_doargv(kvm_t *kd, const struct kinfo_proc *kp, int nchr, - void (*info)(struct ps_strings *, u_long *, int *)) -{ - char **ap; - u_long addr; - int cnt; - static struct ps_strings arginfo; - static u_long ps_strings; - size_t len; - - if (ps_strings == 0) { - len = sizeof(ps_strings); - if (sysctlbyname("kern.ps_strings", &ps_strings, &len, NULL, - 0) == -1) - ps_strings = PS_STRINGS; - } - - /* - * Pointers are stored at the top of the user stack. - */ - if (kp->ki_stat == SZOMB || - kvm_uread(kd, kp, ps_strings, (char *)&arginfo, - sizeof(arginfo)) != sizeof(arginfo)) - return (0); - - (*info)(&arginfo, &addr, &cnt); - if (cnt == 0) - return (0); - ap = kvm_argv(kd, kp, addr, cnt, nchr); - /* - * For live kernels, make sure this process didn't go away. - */ - if (ap != 0 && ISALIVE(kd) && !proc_verify(kp)) - ap = 0; - return (ap); -} - -/* - * Get the command args. This code is now machine independent. - */ -char ** -kvm_getargv(kvm_t *kd, const struct kinfo_proc *kp, int nchr) +kvm_argv(kvm_t *kd, const struct kinfo_proc *kp, int env, int nchr) { int oid[4]; int i; size_t bufsz; - static unsigned long buflen; + static int buflen; static char *buf, *p; static char **bufp; static int argc; @@ -903,24 +643,28 @@ kvm_getargv(kvm_t *kd, const struct kinfo_proc *kp, int nchr) return (0); } - if (!buflen) { - bufsz = sizeof(buflen); - i = sysctlbyname("kern.ps_arg_cache_limit", - &buflen, &bufsz, NULL, 0); - if (i == -1) { - buflen = 0; - } else { - buf = malloc(buflen); - if (buf == NULL) - buflen = 0; - argc = 32; - bufp = malloc(sizeof(char *) * argc); + if (nchr == 0 || nchr > ARG_MAX) + nchr = ARG_MAX; + if (buflen == 0) { + buf = malloc(nchr); + if (buf == NULL) { + _kvm_err(kd, kd->program, "cannot allocate memory"); + return (0); + } + buflen = nchr; + argc = 32; + bufp = malloc(sizeof(char *) * argc); + } else if (nchr > buflen) { + p = realloc(buf, nchr); + if (p != NULL) { + buf = p; + buflen = nchr; } } if (buf != NULL) { oid[0] = CTL_KERN; oid[1] = KERN_PROC; - oid[2] = KERN_PROC_ARGS; + oid[2] = env ? KERN_PROC_ENV : KERN_PROC_ARGS; oid[3] = kp->ki_pid; bufsz = buflen; i = sysctl(oid, 4, buf, &bufsz, 0, 0); @@ -940,65 +684,17 @@ kvm_getargv(kvm_t *kd, const struct kinfo_proc *kp, int nchr) return (bufp); } } - if (kp->ki_flag & P_SYSTEM) - return (NULL); - return (kvm_doargv(kd, kp, nchr, ps_str_a)); + return (NULL); } char ** -kvm_getenvv(kvm_t *kd, const struct kinfo_proc *kp, int nchr) +kvm_getargv(kvm_t *kd, const struct kinfo_proc *kp, int nchr) { - return (kvm_doargv(kd, kp, nchr, ps_str_e)); + return (kvm_argv(kd, kp, 0, nchr)); } -/* - * Read from user space. The user context is given by p. - */ -ssize_t -kvm_uread(kvm_t *kd, const struct kinfo_proc *kp, u_long uva, char *buf, - size_t len) +char ** +kvm_getenvv(kvm_t *kd, const struct kinfo_proc *kp, int nchr) { - char *cp; - char procfile[MAXPATHLEN]; - ssize_t amount; - int fd; - - if (!ISALIVE(kd)) { - _kvm_err(kd, kd->program, - "cannot read user space from dead kernel"); - return (0); - } - - sprintf(procfile, "/proc/%d/mem", kp->ki_pid); - fd = open(procfile, O_RDONLY, 0); - if (fd < 0) { - _kvm_err(kd, kd->program, "cannot open %s", procfile); - return (0); - } - - cp = buf; - while (len > 0) { - errno = 0; - if (lseek(fd, (off_t)uva, 0) == -1 && errno != 0) { - _kvm_err(kd, kd->program, "invalid address (%lx) in %s", - uva, procfile); - break; - } - amount = read(fd, cp, len); - if (amount < 0) { - _kvm_syserr(kd, kd->program, "error reading %s", - procfile); - break; - } - if (amount == 0) { - _kvm_err(kd, kd->program, "EOF reading %s", procfile); - break; - } - cp += amount; - uva += amount; - len -= amount; - } - - close(fd); - return ((ssize_t)(cp - buf)); + return (kvm_argv(kd, kp, 1, nchr)); } diff --git a/usr.bin/procstat/Makefile b/usr.bin/procstat/Makefile index e8e35ed..07b8fb1 100644 --- a/usr.bin/procstat/Makefile +++ b/usr.bin/procstat/Makefile @@ -4,6 +4,7 @@ PROG= procstat MAN= procstat.1 SRCS= procstat.c \ procstat_args.c \ + procstat_auxv.c \ procstat_basic.c \ procstat_bin.c \ procstat_cred.c \ diff --git a/usr.bin/procstat/procstat.1 b/usr.bin/procstat/procstat.1 index 35fab1f..84e31fb 100644 --- a/usr.bin/procstat/procstat.1 +++ b/usr.bin/procstat/procstat.1 @@ -56,6 +56,8 @@ for printing: Display binary information for the process. .It Fl c Display command line arguments for the process. +.It Fl e +Display environment variables for the process. .It Fl f Display file descriptor information for the process. .It Fl i @@ -73,6 +75,8 @@ Display security credential information for the process. Display thread information for the process. .It Fl v Display virtual memory mappings for the process. +.It Fl x +Display ELF auxiliary vector for the process. .El .Pp All options generate output in the format of a table, the first field of diff --git a/usr.bin/procstat/procstat.c b/usr.bin/procstat/procstat.c index 97ff879..680325b 100644 --- a/usr.bin/procstat/procstat.c +++ b/usr.bin/procstat/procstat.c @@ -39,7 +39,8 @@ #include "procstat.h" -static int aflag, bflag, cflag, fflag, iflag, jflag, kflag, sflag, tflag, vflag; +static int aflag, bflag, cflag, eflag, fflag, iflag, jflag, kflag, sflag, tflag; +static int vflag, xflag; int hflag, nflag, Cflag; static void @@ -47,8 +48,9 @@ usage(void) { fprintf(stderr, "usage: procstat [-h] [-C] [-M core] [-N system] " - "[-w interval] [-b | -c | -f | -i | -j | -k | -s | -t | -v]\n"); - fprintf(stderr, " [-a | pid ...]\n"); + "[-w interval] \n"); + fprintf(stderr, " [-b | -c | -e | -f | -i | -j | -k | " + "-s | -t | -v | -x] [-a | pid ...]\n"); exit(EX_USAGE); } @@ -60,6 +62,8 @@ procstat(struct procstat *prstat, struct kinfo_proc *kipp) procstat_bin(kipp); else if (cflag) procstat_args(kipp); + else if (eflag) + procstat_env(kipp); else if (fflag) procstat_files(prstat, kipp); else if (iflag) @@ -74,6 +78,8 @@ procstat(struct procstat *prstat, struct kinfo_proc *kipp) procstat_threads(kipp); else if (vflag) procstat_vm(kipp); + else if (xflag) + procstat_auxv(kipp); else procstat_basic(kipp); } @@ -117,7 +123,7 @@ main(int argc, char *argv[]) interval = 0; memf = nlistf = NULL; - while ((ch = getopt(argc, argv, "CN:M:abcfijkhstvw:")) != -1) { + while ((ch = getopt(argc, argv, "CN:M:abcefijkhstvw:x")) != -1) { switch (ch) { case 'C': Cflag++; @@ -141,6 +147,10 @@ main(int argc, char *argv[]) cflag++; break; + case 'e': + eflag++; + break; + case 'f': fflag++; break; @@ -186,6 +196,10 @@ main(int argc, char *argv[]) interval = l; break; + case 'x': + xflag++; + break; + case '?': default: usage(); @@ -196,7 +210,8 @@ main(int argc, char *argv[]) argv += optind; /* We require that either 0 or 1 mode flags be set. */ - tmp = bflag + cflag + fflag + (kflag ? 1 : 0) + sflag + tflag + vflag; + tmp = bflag + cflag + eflag + fflag + (kflag ? 1 : 0) + sflag + tflag + + vflag + xflag; if (!(tmp == 0 || tmp == 1)) usage(); diff --git a/usr.bin/procstat/procstat.h b/usr.bin/procstat/procstat.h index 71e3ca7..2f722d0 100644 --- a/usr.bin/procstat/procstat.h +++ b/usr.bin/procstat/procstat.h @@ -35,9 +35,11 @@ struct kinfo_proc; void kinfo_proc_sort(struct kinfo_proc *kipp, int count); void procstat_args(struct kinfo_proc *kipp); +void procstat_auxv(struct kinfo_proc *kipp); void procstat_basic(struct kinfo_proc *kipp); void procstat_bin(struct kinfo_proc *kipp); void procstat_cred(struct kinfo_proc *kipp); +void procstat_env(struct kinfo_proc *kipp); void procstat_files(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_kstack(struct kinfo_proc *kipp, int kflag); void procstat_sigs(struct procstat *prstat, struct kinfo_proc *kipp); diff --git a/usr.bin/procstat/procstat_args.c b/usr.bin/procstat/procstat_args.c index e8e6b94..b13aa72 100644 --- a/usr.bin/procstat/procstat_args.c +++ b/usr.bin/procstat/procstat_args.c @@ -42,24 +42,26 @@ static char args[ARG_MAX]; -void -procstat_args(struct kinfo_proc *kipp) +static void +do_args(struct kinfo_proc *kipp, int env) { int error, name[4]; size_t len; char *cp; if (!hflag) - printf("%5s %-16s %-53s\n", "PID", "COMM", "ARGS"); + printf("%5s %-16s %-53s\n", "PID", "COMM", + env ? "ENVIRONMENT" : "ARGS"); name[0] = CTL_KERN; name[1] = KERN_PROC; - name[2] = KERN_PROC_ARGS; + name[2] = env ? KERN_PROC_ENV : KERN_PROC_ARGS; name[3] = kipp->ki_pid; len = sizeof(args); error = sysctl(name, 4, args, &len, NULL, 0); - if (error < 0 && errno != ESRCH) { - warn("sysctl: kern.proc.args: %d", kipp->ki_pid); + if (error < 0 && errno != ESRCH && errno != EPERM) { + warn("sysctl: kern.proc.%s: %d: %d", env ? "env" : "args", + kipp->ki_pid, errno); return; } if (error < 0) @@ -75,3 +77,15 @@ procstat_args(struct kinfo_proc *kipp) printf("%s%s", cp != args ? " " : "", cp); printf("\n"); } + +void +procstat_args(struct kinfo_proc *kipp) +{ + do_args(kipp, 0); +} + +void +procstat_env(struct kinfo_proc *kipp) +{ + do_args(kipp, 1); +}