1 /*- 2 * Copyright (c) 1980, 1991, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * Copyright (c) 2002 Networks Associates Technologies, Inc. 5 * All rights reserved. 6 * 7 * Portions of this software were developed for the FreeBSD Project by 8 * ThinkSec AS and NAI Labs, the Security Research Division of Network 9 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 10 * ("CBOSS"), as part of the DARPA CHATS research program. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #if 0 38 #ifndef lint 39 static const char copyright[] = 40 "@(#) Copyright (c) 1980, 1991, 1993, 1994\n\ 41 The Regents of the University of California. All rights reserved.\n"; 42 #endif /* not lint */ 43 44 #ifndef lint 45 static char sccsid[] = "@(#)pstat.c 8.16 (Berkeley) 5/9/95"; 46 #endif /* not lint */ 47 #endif 48 #include <sys/cdefs.h> 49 __FBSDID("$FreeBSD$"); 50 51 #include <sys/param.h> 52 #include <sys/time.h> 53 #include <sys/file.h> 54 #include <sys/stat.h> 55 #include <sys/stdint.h> 56 #include <sys/ioctl.h> 57 #include <sys/ioctl_compat.h> /* XXX NTTYDISC is too well hidden */ 58 #include <sys/tty.h> 59 #include <sys/blist.h> 60 61 #include <sys/sysctl.h> 62 #include <vm/vm_param.h> 63 64 #include <err.h> 65 #include <errno.h> 66 #include <fcntl.h> 67 #include <kvm.h> 68 #include <limits.h> 69 #include <nlist.h> 70 #include <stdio.h> 71 #include <stdlib.h> 72 #include <string.h> 73 #include <unistd.h> 74 75 enum { 76 NL_CONSTTY, 77 NL_MAXFILES, 78 NL_NFILES, 79 NL_TTY_LIST 80 }; 81 82 static struct nlist nl[] = { 83 { "_constty", 0 }, 84 { "_maxfiles", 0 }, 85 { "_nfiles", 0 }, 86 { "_tty_list", 0 }, 87 { "" } 88 }; 89 90 static int usenumflag; 91 static int totalflag; 92 static int swapflag; 93 static char *nlistf; 94 static char *memf; 95 static kvm_t *kd; 96 97 static char *usagestr; 98 99 static void filemode(void); 100 static int getfiles(char **, size_t *); 101 static void swapmode(void); 102 static void ttymode(void); 103 static void ttyprt(struct xtty *); 104 static void usage(void); 105 106 int 107 main(int argc, char *argv[]) 108 { 109 int ch, i, quit, ret; 110 int fileflag, ttyflag; 111 char buf[_POSIX2_LINE_MAX],*opts; 112 113 fileflag = swapflag = ttyflag = 0; 114 115 /* We will behave like good old swapinfo if thus invoked */ 116 opts = strrchr(argv[0], '/'); 117 if (opts) 118 opts++; 119 else 120 opts = argv[0]; 121 if (!strcmp(opts, "swapinfo")) { 122 swapflag = 1; 123 opts = "kM:N:"; 124 usagestr = "swapinfo [-k] [-M core [-N system]]"; 125 } else { 126 opts = "TM:N:fknst"; 127 usagestr = "pstat [-Tfknst] [-M core [-N system]]"; 128 } 129 130 while ((ch = getopt(argc, argv, opts)) != -1) 131 switch (ch) { 132 case 'f': 133 fileflag = 1; 134 break; 135 case 'k': 136 putenv("BLOCKSIZE=1K"); 137 break; 138 case 'M': 139 memf = optarg; 140 break; 141 case 'N': 142 nlistf = optarg; 143 break; 144 case 'n': 145 usenumflag = 1; 146 break; 147 case 's': 148 ++swapflag; 149 break; 150 case 'T': 151 totalflag = 1; 152 break; 153 case 't': 154 ttyflag = 1; 155 break; 156 default: 157 usage(); 158 } 159 argc -= optind; 160 argv += optind; 161 162 if (memf != NULL) { 163 kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, buf); 164 if (kd == NULL) 165 errx(1, "kvm_openfiles: %s", buf); 166 if ((ret = kvm_nlist(kd, nl)) != 0) { 167 if (ret == -1) 168 errx(1, "kvm_nlist: %s", kvm_geterr(kd)); 169 quit = 0; 170 for (i = 0; nl[i].n_name[0] != '\0'; ++i) 171 if (nl[i].n_value == 0) { 172 quit = 1; 173 warnx("undefined symbol: %s", 174 nl[i].n_name); 175 } 176 if (quit) 177 exit(1); 178 } 179 } 180 if (!(fileflag | ttyflag | swapflag | totalflag)) 181 usage(); 182 if (fileflag || totalflag) 183 filemode(); 184 if (ttyflag) 185 ttymode(); 186 if (swapflag || totalflag) 187 swapmode(); 188 exit (0); 189 } 190 191 static void 192 usage(void) 193 { 194 fprintf(stderr, "usage: %s\n", usagestr); 195 exit (1); 196 } 197 198 static const char fhdr32[] = 199 " LOC TYPE FLG CNT MSG DATA OFFSET\n"; 200 /* c0000000 ------ RWAI 123 123 c0000000 1000000000000000 */ 201 202 static const char fhdr64[] = 203 " LOC TYPE FLG CNT MSG DATA OFFSET\n"; 204 /* c000000000000000 ------ RWAI 123 123 c000000000000000 1000000000000000 */ 205 206 static const char hdr[] = 207 " LINE RAW CAN OUT IHIWT ILOWT OHWT LWT COL STATE SESS PGID DISC\n"; 208 209 static void 210 ttymode_kvm(void) 211 { 212 TAILQ_HEAD(, tty) tl; 213 struct tty *tp, tty; 214 struct xtty xt; 215 216 (void)printf("%s", hdr); 217 bzero(&xt, sizeof xt); 218 xt.xt_size = sizeof xt; 219 if (kvm_read(kd, nl[NL_TTY_LIST].n_value, &tl, sizeof tl) != sizeof tl) 220 errx(1, "kvm_read(): %s", kvm_geterr(kd)); 221 tp = TAILQ_FIRST(&tl); 222 while (tp != NULL) { 223 if (kvm_read(kd, (u_long)tp, &tty, sizeof tty) != sizeof tty) 224 errx(1, "kvm_read(): %s", kvm_geterr(kd)); 225 xt.xt_rawcc = tty.t_rawq.c_cc; 226 xt.xt_cancc = tty.t_canq.c_cc; 227 xt.xt_outcc = tty.t_outq.c_cc; 228 #define XT_COPY(field) xt.xt_##field = tty.t_##field 229 XT_COPY(line); 230 XT_COPY(state); 231 XT_COPY(column); 232 XT_COPY(ihiwat); 233 XT_COPY(ilowat); 234 XT_COPY(ohiwat); 235 XT_COPY(olowat); 236 #undef XT_COPY 237 ttyprt(&xt); 238 tp = TAILQ_NEXT(tp, t_list); 239 } 240 } 241 242 static void 243 ttymode_sysctl(void) 244 { 245 struct xtty *xt, *end; 246 void *xttys; 247 size_t len; 248 249 (void)printf("%s", hdr); 250 if ((xttys = malloc(len = sizeof *xt)) == NULL) 251 err(1, "malloc()"); 252 while (sysctlbyname("kern.ttys", xttys, &len, 0, 0) == -1) { 253 if (errno != ENOMEM) 254 err(1, "sysctlbyname()"); 255 len *= 2; 256 if ((xttys = realloc(xttys, len)) == NULL) 257 err(1, "realloc()"); 258 } 259 if (len > 0) { 260 end = (struct xtty *)((char *)xttys + len); 261 for (xt = xttys; xt < end; xt++) 262 ttyprt(xt); 263 } 264 } 265 266 static void 267 ttymode(void) 268 { 269 270 if (kd != NULL) 271 ttymode_kvm(); 272 else 273 ttymode_sysctl(); 274 } 275 276 static struct { 277 int flag; 278 char val; 279 } ttystates[] = { 280 #ifdef TS_WOPEN 281 { TS_WOPEN, 'W'}, 282 #endif 283 { TS_ISOPEN, 'O'}, 284 { TS_CARR_ON, 'C'}, 285 #ifdef TS_CONNECTED 286 { TS_CONNECTED, 'c'}, 287 #endif 288 { TS_TIMEOUT, 'T'}, 289 { TS_FLUSH, 'F'}, 290 { TS_BUSY, 'B'}, 291 #ifdef TS_ASLEEP 292 { TS_ASLEEP, 'A'}, 293 #endif 294 #ifdef TS_SO_OLOWAT 295 { TS_SO_OLOWAT, 'A'}, 296 #endif 297 #ifdef TS_SO_OCOMPLETE 298 { TS_SO_OCOMPLETE, 'a'}, 299 #endif 300 { TS_XCLUDE, 'X'}, 301 { TS_TTSTOP, 'S'}, 302 #ifdef TS_CAR_OFLOW 303 { TS_CAR_OFLOW, 'm'}, 304 #endif 305 #ifdef TS_CTS_OFLOW 306 { TS_CTS_OFLOW, 'o'}, 307 #endif 308 #ifdef TS_DSR_OFLOW 309 { TS_DSR_OFLOW, 'd'}, 310 #endif 311 { TS_TBLOCK, 'K'}, 312 { TS_ASYNC, 'Y'}, 313 { TS_BKSL, 'D'}, 314 { TS_ERASE, 'E'}, 315 { TS_LNCH, 'L'}, 316 { TS_TYPEN, 'P'}, 317 { TS_CNTTB, 'N'}, 318 #ifdef TS_CAN_BYPASS_L_RINT 319 { TS_CAN_BYPASS_L_RINT, 'l'}, 320 #endif 321 #ifdef TS_SNOOP 322 { TS_SNOOP, 's'}, 323 #endif 324 #ifdef TS_ZOMBIE 325 { TS_ZOMBIE, 'Z'}, 326 #endif 327 { 0, '\0'}, 328 }; 329 330 static void 331 ttyprt(struct xtty *xt) 332 { 333 int i, j; 334 pid_t pgid; 335 char *name, state[20]; 336 337 if (xt->xt_size != sizeof *xt) 338 errx(1, "struct xtty size mismatch"); 339 if (usenumflag || xt->xt_dev == 0 || 340 (name = devname(xt->xt_dev, S_IFCHR)) == NULL) 341 printf(" %2d,%-2d", major(xt->xt_dev), minor(xt->xt_dev)); 342 else 343 (void)printf("%7s ", name); 344 (void)printf("%2ld %3ld ", xt->xt_rawcc, xt->xt_cancc); 345 (void)printf("%3ld %5d %5d %4d %3d %7d ", xt->xt_outcc, 346 xt->xt_ihiwat, xt->xt_ilowat, xt->xt_ohiwat, xt->xt_olowat, 347 xt->xt_column); 348 for (i = j = 0; ttystates[i].flag; i++) 349 if (xt->xt_state & ttystates[i].flag) 350 state[j++] = ttystates[i].val; 351 if (j == 0) 352 state[j++] = '-'; 353 state[j] = '\0'; 354 (void)printf("%-6s %8d", state, xt->xt_sid); 355 pgid = 0; 356 (void)printf("%6d ", xt->xt_pgid); 357 switch (xt->xt_line) { 358 case TTYDISC: 359 (void)printf("term\n"); 360 break; 361 case NTTYDISC: 362 (void)printf("ntty\n"); 363 break; 364 case SLIPDISC: 365 (void)printf("slip\n"); 366 break; 367 case PPPDISC: 368 (void)printf("ppp\n"); 369 break; 370 default: 371 (void)printf("%d\n", xt->xt_line); 372 break; 373 } 374 } 375 376 static void 377 filemode(void) 378 { 379 struct xfile *fp; 380 char *buf, flagbuf[16], *fbp; 381 int maxf, openf; 382 size_t len; 383 static char *dtypes[] = { "???", "inode", "socket", "pipe", 384 "fifo", "kqueue", "crypto" }; 385 int i; 386 int wid; 387 388 if (kd != NULL) { 389 if (kvm_read(kd, nl[NL_MAXFILES].n_value, 390 &maxf, sizeof maxf) != sizeof maxf || 391 kvm_read(kd, nl[NL_NFILES].n_value, 392 &openf, sizeof openf) != sizeof openf) 393 errx(1, "kvm_read(): %s", kvm_geterr(kd)); 394 } else { 395 len = sizeof(int); 396 if (sysctlbyname("kern.maxfiles", &maxf, &len, 0, 0) == -1 || 397 sysctlbyname("kern.openfiles", &openf, &len, 0, 0) == -1) 398 err(1, "sysctlbyname()"); 399 } 400 401 if (totalflag) { 402 (void)printf("%3d/%3d files\n", openf, maxf); 403 return; 404 } 405 if (getfiles(&buf, &len) == -1) 406 return; 407 openf = len / sizeof *fp; 408 409 (void)printf("%d/%d open files\n", openf, maxf); 410 printf(sizeof(uintptr_t) == 4 ? fhdr32 : fhdr64); 411 wid = (int)sizeof(uintptr_t) * 2; 412 for (fp = (struct xfile *)buf, i = 0; i < openf; ++fp, ++i) { 413 if ((size_t)fp->xf_type >= sizeof(dtypes) / sizeof(dtypes[0])) 414 continue; 415 (void)printf("%*jx", wid, (uintmax_t)(uintptr_t)fp->xf_file); 416 (void)printf(" %-6.6s", dtypes[fp->xf_type]); 417 fbp = flagbuf; 418 if (fp->xf_flag & FREAD) 419 *fbp++ = 'R'; 420 if (fp->xf_flag & FWRITE) 421 *fbp++ = 'W'; 422 if (fp->xf_flag & FAPPEND) 423 *fbp++ = 'A'; 424 if (fp->xf_flag & FASYNC) 425 *fbp++ = 'I'; 426 *fbp = '\0'; 427 (void)printf(" %4s %3d", flagbuf, fp->xf_count); 428 (void)printf(" %3d", fp->xf_msgcount); 429 (void)printf(" %*jx", wid, (uintmax_t)(uintptr_t)fp->xf_data); 430 (void)printf(" %*jx\n", (int)sizeof(fp->xf_offset) * 2, 431 (uintmax_t)fp->xf_offset); 432 } 433 free(buf); 434 } 435 436 static int 437 getfiles(char **abuf, size_t *alen) 438 { 439 size_t len; 440 int mib[2]; 441 char *buf; 442 443 /* 444 * XXX 445 * Add emulation of KINFO_FILE here. 446 */ 447 if (kd != NULL) 448 errx(1, "files on dead kernel, not implemented"); 449 450 mib[0] = CTL_KERN; 451 mib[1] = KERN_FILE; 452 if (sysctl(mib, 2, NULL, &len, NULL, 0) == -1) { 453 warn("sysctl: KERN_FILE"); 454 return (-1); 455 } 456 if ((buf = malloc(len)) == NULL) 457 errx(1, "malloc"); 458 if (sysctl(mib, 2, buf, &len, NULL, 0) == -1) { 459 warn("sysctl: KERN_FILE"); 460 return (-1); 461 } 462 *abuf = buf; 463 *alen = len; 464 return (0); 465 } 466 467 /* 468 * swapmode is based on a program called swapinfo written 469 * by Kevin Lahey <kml@rokkaku.atl.ga.us>. 470 */ 471 472 #define CONVERT(v) ((int)((intmax_t)(v) * pagesize / blocksize)) 473 static struct kvm_swap swtot; 474 static int nswdev; 475 476 static void 477 print_swap_header(void) 478 { 479 int hlen; 480 long blocksize; 481 const char *header; 482 483 header = getbsize(&hlen, &blocksize); 484 if (totalflag == 0) 485 (void)printf("%-15s %*s %8s %8s %8s\n", 486 "Device", hlen, header, 487 "Used", "Avail", "Capacity"); 488 } 489 490 static void 491 print_swap(struct kvm_swap *ksw) 492 { 493 int hlen, pagesize; 494 long blocksize; 495 496 pagesize = getpagesize(); 497 getbsize(&hlen, &blocksize); 498 swtot.ksw_total += ksw->ksw_total; 499 swtot.ksw_used += ksw->ksw_used; 500 ++nswdev; 501 if (totalflag == 0) { 502 (void)printf("%-15s %*d ", 503 ksw->ksw_devname, hlen, 504 CONVERT(ksw->ksw_total)); 505 (void)printf("%8d %8d %5.0f%%\n", 506 CONVERT(ksw->ksw_used), 507 CONVERT(ksw->ksw_total - ksw->ksw_used), 508 (ksw->ksw_used * 100.0) / ksw->ksw_total); 509 } 510 } 511 512 static void 513 print_swap_total(void) 514 { 515 int hlen, pagesize; 516 long blocksize; 517 518 pagesize = getpagesize(); 519 getbsize(&hlen, &blocksize); 520 if (totalflag) { 521 blocksize = 1024 * 1024; 522 (void)printf("%dM/%dM swap space\n", 523 CONVERT(swtot.ksw_used), CONVERT(swtot.ksw_total)); 524 } else if (nswdev > 1) { 525 (void)printf("%-15s %*d %8d %8d %5.0f%%\n", 526 "Total", hlen, CONVERT(swtot.ksw_total), 527 CONVERT(swtot.ksw_used), 528 CONVERT(swtot.ksw_total - swtot.ksw_used), 529 (swtot.ksw_used * 100.0) / swtot.ksw_total); 530 } 531 } 532 533 static void 534 swapmode_kvm(void) 535 { 536 struct kvm_swap kswap[16]; 537 int i, n; 538 539 n = kvm_getswapinfo(kd, kswap, sizeof kswap / sizeof kswap[0], 540 SWIF_DEV_PREFIX); 541 542 print_swap_header(); 543 for (i = 0; i < n; ++i) 544 print_swap(&kswap[i]); 545 print_swap_total(); 546 } 547 548 static void 549 swapmode_sysctl(void) 550 { 551 struct kvm_swap ksw; 552 struct xswdev xsw; 553 size_t mibsize, size; 554 int mib[16], n; 555 556 print_swap_header(); 557 mibsize = sizeof mib / sizeof mib[0]; 558 if (sysctlnametomib("vm.swap_info", mib, &mibsize) == -1) 559 err(1, "sysctlnametomib()"); 560 for (n = 0; ; ++n) { 561 mib[mibsize] = n; 562 size = sizeof xsw; 563 if (sysctl(mib, mibsize + 1, &xsw, &size, NULL, 0) == -1) 564 break; 565 if (xsw.xsw_version != XSWDEV_VERSION) 566 errx(1, "xswdev version mismatch"); 567 if (xsw.xsw_dev == NODEV) 568 snprintf(ksw.ksw_devname, sizeof ksw.ksw_devname, 569 "<NFSfile>"); 570 else 571 snprintf(ksw.ksw_devname, sizeof ksw.ksw_devname, 572 "/dev/%s", devname(xsw.xsw_dev, S_IFCHR)); 573 ksw.ksw_used = xsw.xsw_used; 574 ksw.ksw_total = xsw.xsw_nblks; 575 ksw.ksw_flags = xsw.xsw_flags; 576 print_swap(&ksw); 577 } 578 if (errno != ENOENT) 579 err(1, "sysctl()"); 580 print_swap_total(); 581 } 582 583 static void 584 swapmode(void) 585 { 586 if (kd != NULL) 587 swapmode_kvm(); 588 else 589 swapmode_sysctl(); 590 } 591