diff options
Diffstat (limited to 'usr.sbin/pstat/pstat.c')
-rw-r--r-- | usr.sbin/pstat/pstat.c | 597 |
1 files changed, 597 insertions, 0 deletions
diff --git a/usr.sbin/pstat/pstat.c b/usr.sbin/pstat/pstat.c new file mode 100644 index 0000000..2017841 --- /dev/null +++ b/usr.sbin/pstat/pstat.c @@ -0,0 +1,597 @@ +/*- + * Copyright (c) 1980, 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 2002 Networks Associates Technologies, Inc. + * All rights reserved. + * + * Portions of this software were developed for the FreeBSD Project by + * ThinkSec AS and NAI Labs, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 + * ("CBOSS"), as part of the DARPA CHATS research program. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1980, 1991, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)pstat.c 8.16 (Berkeley) 5/9/95"; +#endif /* not lint */ +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/stdint.h> +#include <sys/ioctl.h> +#include <sys/tty.h> +#include <sys/blist.h> + +#include <sys/sysctl.h> +#include <vm/vm_param.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <kvm.h> +#include <libutil.h> +#include <limits.h> +#include <nlist.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +enum { + NL_CONSTTY, + NL_MAXFILES, + NL_NFILES, + NL_TTY_LIST, + NL_MARKER +}; + +static struct { + int order; + const char *name; +} namelist[] = { + { NL_CONSTTY, "_constty" }, + { NL_MAXFILES, "_maxfiles" }, + { NL_NFILES, "_openfiles" }, + { NL_TTY_LIST, "_tty_list" }, + { NL_MARKER, "" }, +}; +#define NNAMES (sizeof(namelist) / sizeof(*namelist)) +static struct nlist nl[NNAMES]; + +static int humanflag; +static int usenumflag; +static int totalflag; +static int swapflag; +static char *nlistf; +static char *memf; +static kvm_t *kd; + +static const char *usagestr; + +static void filemode(void); +static int getfiles(struct xfile **, size_t *); +static void swapmode(void); +static void ttymode(void); +static void ttyprt(struct xtty *); +static void usage(void); + +int +main(int argc, char *argv[]) +{ + int ch, quit, ret; + int fileflag, ttyflag; + unsigned int i; + char buf[_POSIX2_LINE_MAX]; + const char *opts; + + fileflag = swapflag = ttyflag = 0; + + /* We will behave like good old swapinfo if thus invoked */ + opts = strrchr(argv[0], '/'); + if (opts) + opts++; + else + opts = argv[0]; + if (!strcmp(opts, "swapinfo")) { + swapflag = 1; + opts = "ghkmM:N:"; + usagestr = "swapinfo [-ghkm] [-M core [-N system]]"; + } else { + opts = "TM:N:fghkmnst"; + usagestr = "pstat [-Tfghkmnst] [-M core [-N system]]"; + } + + while ((ch = getopt(argc, argv, opts)) != -1) + switch (ch) { + case 'f': + fileflag = 1; + break; + case 'g': + setenv("BLOCKSIZE", "1G", 1); + break; + case 'h': + humanflag = 1; + break; + case 'k': + setenv("BLOCKSIZE", "1K", 1); + break; + case 'm': + setenv("BLOCKSIZE", "1M", 1); + break; + case 'M': + memf = optarg; + break; + case 'N': + nlistf = optarg; + break; + case 'n': + usenumflag = 1; + break; + case 's': + ++swapflag; + break; + case 'T': + totalflag = 1; + break; + case 't': + ttyflag = 1; + break; + default: + usage(); + } + argc -= optind; + argv += optind; + + /* + * Initialize symbol names list. + */ + for (i = 0; i < NNAMES; i++) + nl[namelist[i].order].n_name = strdup(namelist[i].name); + + if (memf != NULL) { + kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, buf); + if (kd == NULL) + errx(1, "kvm_openfiles: %s", buf); + if ((ret = kvm_nlist(kd, nl)) != 0) { + if (ret == -1) + errx(1, "kvm_nlist: %s", kvm_geterr(kd)); + quit = 0; + for (i = 0; nl[i].n_name[0] != '\0'; ++i) + if (nl[i].n_value == 0) { + quit = 1; + warnx("undefined symbol: %s", + nl[i].n_name); + } + if (quit) + exit(1); + } + } + if (!(fileflag | ttyflag | swapflag | totalflag)) + usage(); + if (fileflag || totalflag) + filemode(); + if (ttyflag) + ttymode(); + if (swapflag || totalflag) + swapmode(); + exit (0); +} + +static void +usage(void) +{ + fprintf(stderr, "usage: %s\n", usagestr); + exit (1); +} + +static const char fhdr32[] = + " LOC TYPE FLG CNT MSG DATA OFFSET\n"; +/* c0000000 ------ RWAI 123 123 c0000000 1000000000000000 */ + +static const char fhdr64[] = + " LOC TYPE FLG CNT MSG DATA OFFSET\n"; +/* c000000000000000 ------ RWAI 123 123 c000000000000000 1000000000000000 */ + +static const char hdr[] = +" LINE INQ CAN LIN LOW OUTQ USE LOW COL SESS PGID STATE\n"; + +static void +ttymode_kvm(void) +{ + TAILQ_HEAD(, tty) tl; + struct tty *tp, tty; + struct xtty xt; + + (void)printf("%s", hdr); + bzero(&xt, sizeof xt); + xt.xt_size = sizeof xt; + if (kvm_read(kd, nl[NL_TTY_LIST].n_value, &tl, sizeof tl) != sizeof tl) + errx(1, "kvm_read(): %s", kvm_geterr(kd)); + tp = TAILQ_FIRST(&tl); + while (tp != NULL) { + if (kvm_read(kd, (u_long)tp, &tty, sizeof tty) != sizeof tty) + errx(1, "kvm_read(): %s", kvm_geterr(kd)); + xt.xt_insize = tty.t_inq.ti_nblocks * TTYINQ_DATASIZE; + xt.xt_incc = tty.t_inq.ti_linestart - tty.t_inq.ti_begin; + xt.xt_inlc = tty.t_inq.ti_end - tty.t_inq.ti_linestart; + xt.xt_inlow = tty.t_inlow; + xt.xt_outsize = tty.t_outq.to_nblocks * TTYOUTQ_DATASIZE; + xt.xt_outcc = tty.t_outq.to_end - tty.t_outq.to_begin; + xt.xt_outlow = tty.t_outlow; + xt.xt_column = tty.t_column; + /* xt.xt_pgid = ... */ + /* xt.xt_sid = ... */ + xt.xt_flags = tty.t_flags; + xt.xt_dev = NODEV; + ttyprt(&xt); + tp = TAILQ_NEXT(&tty, t_list); + } +} + +static void +ttymode_sysctl(void) +{ + struct xtty *xttys; + size_t len; + unsigned int i, n; + + (void)printf("%s", hdr); + if ((xttys = malloc(len = sizeof(*xttys))) == NULL) + err(1, "malloc()"); + while (sysctlbyname("kern.ttys", xttys, &len, 0, 0) == -1) { + if (errno != ENOMEM) + err(1, "sysctlbyname()"); + len *= 2; + if ((xttys = realloc(xttys, len)) == NULL) + err(1, "realloc()"); + } + n = len / sizeof(*xttys); + for (i = 0; i < n; i++) + ttyprt(&xttys[i]); +} + +static void +ttymode(void) +{ + + if (kd != NULL) + ttymode_kvm(); + else + ttymode_sysctl(); +} + +static struct { + int flag; + char val; +} ttystates[] = { +#if 0 + { TF_NOPREFIX, 'N' }, +#endif + { TF_INITLOCK, 'I' }, + { TF_CALLOUT, 'C' }, + + /* Keep these together -> 'Oi' and 'Oo'. */ + { TF_OPENED, 'O' }, + { TF_OPENED_IN, 'i' }, + { TF_OPENED_OUT, 'o' }, + { TF_OPENED_CONS, 'c' }, + + { TF_GONE, 'G' }, + { TF_OPENCLOSE, 'B' }, + { TF_ASYNC, 'Y' }, + { TF_LITERAL, 'L' }, + + /* Keep these together -> 'Hi' and 'Ho'. */ + { TF_HIWAT, 'H' }, + { TF_HIWAT_IN, 'i' }, + { TF_HIWAT_OUT, 'o' }, + + { TF_STOPPED, 'S' }, + { TF_EXCLUDE, 'X' }, + { TF_BYPASS, 'l' }, + { TF_ZOMBIE, 'Z' }, + { TF_HOOK, 's' }, + + /* Keep these together -> 'bi' and 'bo'. */ + { TF_BUSY, 'b' }, + { TF_BUSY_IN, 'i' }, + { TF_BUSY_OUT, 'o' }, + + { 0, '\0'}, +}; + +static void +ttyprt(struct xtty *xt) +{ + int i, j; + char *name; + + if (xt->xt_size != sizeof *xt) + errx(1, "struct xtty size mismatch"); + if (usenumflag || xt->xt_dev == 0 || + (name = devname(xt->xt_dev, S_IFCHR)) == NULL) + printf("%#10jx ", (uintmax_t)xt->xt_dev); + else + printf("%10s ", name); + printf("%5zu %4zu %4zu %4zu %5zu %4zu %4zu %5u %5d %5d ", + xt->xt_insize, xt->xt_incc, xt->xt_inlc, + (xt->xt_insize - xt->xt_inlow), xt->xt_outsize, + xt->xt_outcc, (xt->xt_outsize - xt->xt_outlow), + MIN(xt->xt_column, 99999), xt->xt_sid, xt->xt_pgid); + for (i = j = 0; ttystates[i].flag; i++) + if (xt->xt_flags & ttystates[i].flag) { + putchar(ttystates[i].val); + j++; + } + if (j == 0) + putchar('-'); + putchar('\n'); +} + +static void +filemode(void) +{ + struct xfile *fp, *buf; + char flagbuf[16], *fbp; + int maxf, openf; + size_t len; + static char const * const dtypes[] = { "???", "inode", "socket", + "pipe", "fifo", "kqueue", "crypto" }; + int i; + int wid; + + if (kd != NULL) { + if (kvm_read(kd, nl[NL_MAXFILES].n_value, + &maxf, sizeof maxf) != sizeof maxf || + kvm_read(kd, nl[NL_NFILES].n_value, + &openf, sizeof openf) != sizeof openf) + errx(1, "kvm_read(): %s", kvm_geterr(kd)); + } else { + len = sizeof(int); + if (sysctlbyname("kern.maxfiles", &maxf, &len, 0, 0) == -1 || + sysctlbyname("kern.openfiles", &openf, &len, 0, 0) == -1) + err(1, "sysctlbyname()"); + } + + if (totalflag) { + (void)printf("%3d/%3d files\n", openf, maxf); + return; + } + if (getfiles(&buf, &len) == -1) + return; + openf = len / sizeof *fp; + + (void)printf("%d/%d open files\n", openf, maxf); + printf(sizeof(uintptr_t) == 4 ? fhdr32 : fhdr64); + wid = (int)sizeof(uintptr_t) * 2; + for (fp = (struct xfile *)buf, i = 0; i < openf; ++fp, ++i) { + if ((size_t)fp->xf_type >= sizeof(dtypes) / sizeof(dtypes[0])) + continue; + (void)printf("%*jx", wid, (uintmax_t)(uintptr_t)fp->xf_file); + (void)printf(" %-6.6s", dtypes[fp->xf_type]); + fbp = flagbuf; + if (fp->xf_flag & FREAD) + *fbp++ = 'R'; + if (fp->xf_flag & FWRITE) + *fbp++ = 'W'; + if (fp->xf_flag & FAPPEND) + *fbp++ = 'A'; + if (fp->xf_flag & FASYNC) + *fbp++ = 'I'; + *fbp = '\0'; + (void)printf(" %4s %3d", flagbuf, fp->xf_count); + (void)printf(" %3d", fp->xf_msgcount); + (void)printf(" %*jx", wid, (uintmax_t)(uintptr_t)fp->xf_data); + (void)printf(" %*jx\n", (int)sizeof(fp->xf_offset) * 2, + (uintmax_t)fp->xf_offset); + } + free(buf); +} + +static int +getfiles(struct xfile **abuf, size_t *alen) +{ + struct xfile *buf; + size_t len; + int mib[2]; + + /* + * XXX + * Add emulation of KINFO_FILE here. + */ + if (kd != NULL) + errx(1, "files on dead kernel, not implemented"); + + mib[0] = CTL_KERN; + mib[1] = KERN_FILE; + if (sysctl(mib, 2, NULL, &len, NULL, 0) == -1) { + warn("sysctl: KERN_FILE"); + return (-1); + } + if ((buf = malloc(len)) == NULL) + errx(1, "malloc"); + if (sysctl(mib, 2, buf, &len, NULL, 0) == -1) { + warn("sysctl: KERN_FILE"); + return (-1); + } + *abuf = buf; + *alen = len; + return (0); +} + +/* + * swapmode is based on a program called swapinfo written + * by Kevin Lahey <kml@rokkaku.atl.ga.us>. + */ + +#define CONVERT(v) ((int64_t)(v) * pagesize / blocksize) +#define CONVERT_BLOCKS(v) ((int64_t)(v) * pagesize) +static struct kvm_swap swtot; +static int nswdev; + +static void +print_swap_header(void) +{ + int hlen; + long blocksize; + const char *header; + + header = getbsize(&hlen, &blocksize); + if (totalflag == 0) + (void)printf("%-15s %*s %8s %8s %8s\n", + "Device", hlen, header, + "Used", "Avail", "Capacity"); +} + +static void +print_swap_line(const char *swdevname, intmax_t nblks, intmax_t bused, + intmax_t bavail, float bpercent) +{ + char usedbuf[5]; + char availbuf[5]; + int hlen, pagesize; + long blocksize; + + pagesize = getpagesize(); + getbsize(&hlen, &blocksize); + + printf("%-15s %*jd ", swdevname, hlen, CONVERT(nblks)); + if (humanflag) { + humanize_number(usedbuf, sizeof(usedbuf), + CONVERT_BLOCKS(bused), "", + HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); + humanize_number(availbuf, sizeof(availbuf), + CONVERT_BLOCKS(bavail), "", + HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); + printf("%8s %8s %5.0f%%\n", usedbuf, availbuf, bpercent); + } else { + printf("%8jd %8jd %5.0f%%\n", (intmax_t)CONVERT(bused), + (intmax_t)CONVERT(bavail), bpercent); + } +} + +static void +print_swap(struct kvm_swap *ksw) +{ + + swtot.ksw_total += ksw->ksw_total; + swtot.ksw_used += ksw->ksw_used; + ++nswdev; + if (totalflag == 0) + print_swap_line(ksw->ksw_devname, ksw->ksw_total, + ksw->ksw_used, ksw->ksw_total - ksw->ksw_used, + (ksw->ksw_used * 100.0) / ksw->ksw_total); +} + +static void +print_swap_total(void) +{ + int hlen, pagesize; + long blocksize; + + pagesize = getpagesize(); + getbsize(&hlen, &blocksize); + if (totalflag) { + blocksize = 1024 * 1024; + (void)printf("%jdM/%jdM swap space\n", + CONVERT(swtot.ksw_used), CONVERT(swtot.ksw_total)); + } else if (nswdev > 1) { + print_swap_line("Total", swtot.ksw_total, swtot.ksw_used, + swtot.ksw_total - swtot.ksw_used, + (swtot.ksw_used * 100.0) / swtot.ksw_total); + } +} + +static void +swapmode_kvm(void) +{ + struct kvm_swap kswap[16]; + int i, n; + + n = kvm_getswapinfo(kd, kswap, sizeof kswap / sizeof kswap[0], + SWIF_DEV_PREFIX); + + print_swap_header(); + for (i = 0; i < n; ++i) + print_swap(&kswap[i]); + print_swap_total(); +} + +static void +swapmode_sysctl(void) +{ + struct kvm_swap ksw; + struct xswdev xsw; + size_t mibsize, size; + int mib[16], n; + + print_swap_header(); + mibsize = sizeof mib / sizeof mib[0]; + if (sysctlnametomib("vm.swap_info", mib, &mibsize) == -1) + err(1, "sysctlnametomib()"); + for (n = 0; ; ++n) { + mib[mibsize] = n; + size = sizeof xsw; + if (sysctl(mib, mibsize + 1, &xsw, &size, NULL, 0) == -1) + break; + if (xsw.xsw_version != XSWDEV_VERSION) + errx(1, "xswdev version mismatch"); + if (xsw.xsw_dev == NODEV) + snprintf(ksw.ksw_devname, sizeof ksw.ksw_devname, + "<NFSfile>"); + else + snprintf(ksw.ksw_devname, sizeof ksw.ksw_devname, + "/dev/%s", devname(xsw.xsw_dev, S_IFCHR)); + ksw.ksw_used = xsw.xsw_used; + ksw.ksw_total = xsw.xsw_nblks; + ksw.ksw_flags = xsw.xsw_flags; + print_swap(&ksw); + } + if (errno != ENOENT) + err(1, "sysctl()"); + print_swap_total(); +} + +static void +swapmode(void) +{ + if (kd != NULL) + swapmode_kvm(); + else + swapmode_sysctl(); +} |