1 /* 2 * Copyright (c) 1980 Regents of the University of California. 3 * All rights reserved. The Berkeley software License Agreement 4 * specifies the terms and conditions for redistribution. 5 */ 6 7 #ifndef lint 8 char copyright[] = 9 "@(#) Copyright (c) 1980 Regents of the University of California.\n\ 10 All rights reserved.\n"; 11 #endif /* not lint */ 12 13 #ifndef lint 14 static char sccsid[] = "@(#)pstat.c 5.30 (Berkeley) 05/02/91"; 15 #endif /* not lint */ 16 17 /* 18 * Print system stuff 19 */ 20 #include <sys/param.h> 21 #include <sys/user.h> 22 #include <sys/proc.h> 23 #include <sys/text.h> 24 #include <sys/time.h> 25 #include <sys/vnode.h> 26 #include <sys/map.h> 27 #define KERNEL 28 #define NFS 29 #include <sys/file.h> 30 #include <sys/mount.h> 31 #include <ufs/quota.h> 32 #include <ufs/inode.h> 33 #include <sys/stat.h> 34 #include <nfs/nfsv2.h> 35 #include <nfs/nfs.h> 36 #include <nfs/nfsnode.h> 37 #include <sys/ioctl.h> 38 #include <sys/tty.h> 39 #undef KERNEL 40 #include <sys/conf.h> 41 #include <sys/vm.h> 42 #include <machine/pte.h> 43 #include <sys/kinfo.h> 44 45 #include <nlist.h> 46 #include <kvm.h> 47 #include <stdio.h> 48 #include "pathnames.h" 49 50 #define mask(x) (x&0377) 51 #define clear(x) ((int)x &~ KERNBASE) 52 53 char *fnlist = NULL; 54 char *fcore = NULL; 55 56 struct nlist nl[] = { 57 #define STEXT 0 58 { "_text" }, 59 #define SPROC 1 60 { "_proc" }, 61 #define SFIL 2 62 { "_file" }, 63 #define SWAPMAP 3 64 { "_swapmap" }, 65 #define SNPROC 4 66 { "_nproc" }, 67 #define SNTEXT 5 68 { "_ntext" }, 69 #define SNFILE 6 70 { "_nfile" }, 71 #define SNSWAPMAP 7 72 { "_nswapmap" }, 73 #define SDMMIN 8 74 { "_dmmin" }, 75 #define SDMMAX 9 76 { "_dmmax" }, 77 #define SNSWDEV 10 78 { "_nswdev" }, 79 #define SSWDEVT 11 80 { "_swdevt" }, 81 #define NLMANDATORY SSWDEVT /* names up to here are mandatory */ 82 #define SCONS 12 83 { "_cons" }, 84 #define SPTY 13 85 { "_pt_tty" }, 86 #define SNPTY 14 87 { "_npty" }, 88 #ifdef vax 89 #define SDZ (SNPTY+1) 90 { "_dz_tty" }, 91 #define SNDZ (SNPTY+2) 92 { "_dz_cnt" }, 93 #define SDMF (SNPTY+3) 94 { "_dmf_tty" }, 95 #define SNDMF (SNPTY+4) 96 { "_ndmf" }, 97 #define SDH (SNPTY+5) 98 { "_dh11" }, 99 #define SNDH (SNPTY+6) 100 { "_ndh11" }, 101 #define SDHU (SNPTY+7) 102 { "_dhu_tty" }, 103 #define SNDHU (SNPTY+8) 104 { "_ndhu" }, 105 #define SDMZ (SNPTY+9) 106 { "_dmz_tty" }, 107 #define SNDMZ (SNPTY+10) 108 { "_ndmz" }, 109 #define SQD (SNPTY+11) 110 { "_qd_tty" }, 111 #define SNQD (SNPTY+12) 112 { "_nNQD" }, 113 #endif 114 115 #ifdef tahoe 116 #define SVX (SNPTY+1) 117 { "_vx_tty" }, 118 #define SNVX (SNPTY+2) 119 { "_nvx" }, 120 #define SMP (SNPTY+3) 121 { "_mp_tty" }, 122 #define SNMP (SNPTY+4) 123 { "_nmp" }, 124 #endif 125 126 #ifdef hp300 127 #define SDCA (SNPTY+1) 128 { "_dca_tty" }, 129 #define SNDCA (SNPTY+2) 130 { "_ndca" }, 131 #define SDCM (SNPTY+3) 132 { "_dcm_tty" }, 133 #define SNDCM (SNPTY+4) 134 { "_ndcm" }, 135 #define SDCL (SNPTY+5) 136 { "_dcl_tty" }, 137 #define SNDCL (SNPTY+6) 138 { "_ndcl" }, 139 #define SITE (SNPTY+7) 140 { "_ite_tty" }, 141 #define SNITE (SNPTY+8) 142 { "_nite" }, 143 #endif 144 { "" } 145 }; 146 147 int vnof; 148 int txtf; 149 int prcf; 150 int ttyf; 151 int usrf; 152 int upid; 153 int filf; 154 int swpf; 155 int totflg; 156 char partab[1]; 157 struct cdevsw cdevsw[1]; 158 struct bdevsw bdevsw[1]; 159 int allflg; 160 int nflg; 161 u_long getword(); 162 off_t mkphys(); 163 164 char *Program; 165 166 #define V(x) (void *)(x) 167 168 main(argc, argv) 169 int argc; 170 char **argv; 171 { 172 extern char *optarg; 173 extern int optind; 174 int ch, ret; 175 176 Program = argv[0]; 177 while ((ch = getopt(argc, argv, "TafvikptU:sxnu")) != EOF) 178 switch (ch) { 179 case 'T': 180 totflg++; 181 break; 182 case 'a': 183 allflg++; 184 /*FALLTHROUGH*/ 185 case 'p': 186 prcf++; 187 break; 188 case 'f': 189 filf++; 190 break; 191 case 'v': 192 case 'i': 193 vnof++; 194 break; 195 case 't': 196 ttyf++; 197 break; 198 case 'U': 199 usrf++; 200 sscanf(optarg, "%d", &upid); 201 break; 202 case 's': 203 swpf++; 204 break; 205 case 'x': 206 txtf++; 207 break; 208 case 'n': 209 nflg++; 210 break; 211 case 'u': 212 fprintf(stderr, "pstat: use [ -U pid ] for -u\n"); 213 exit(1); 214 case '?': 215 default: 216 fprintf(stderr, "usage: pstat -[Tafiptsx] [-U [pid]] [system] [core]\n"); 217 exit(1); 218 } 219 argc -= optind; 220 argv += optind; 221 222 if (argc > 1) 223 fcore = argv[1]; 224 if (argc > 0) 225 fnlist = argv[0]; 226 if (kvm_openfiles(fnlist, fcore, NULL) == -1) { 227 error("kvm_openfiles: %s", kvm_geterr()); 228 exit(1); 229 } 230 if ((ret = kvm_nlist(nl)) != 0) { 231 int i, quit = 0; 232 233 if (ret == -1) { 234 error("kvm_nlist: %s", kvm_geterr()); 235 exit(1); 236 } 237 for (i = 0; i <= NLMANDATORY; i++) { 238 if (!nl[i].n_value) { 239 quit = 1; 240 error("undefined symbol: %s\n", 241 nl[i].n_name); 242 } 243 } 244 if (quit) 245 exit(1); 246 } 247 if (!(filf | totflg | vnof | prcf | txtf | ttyf | usrf | swpf)) { 248 printf("pstat: one or more of -[aivxptfsU] is required\n"); 249 exit(1); 250 } 251 if (filf||totflg) 252 dofile(); 253 if (vnof||totflg) 254 dovnode(); 255 if (prcf||totflg) 256 doproc(); 257 if (txtf||totflg) 258 dotext(); 259 if (ttyf) 260 dotty(); 261 if (usrf) 262 dousr(); 263 if (swpf||totflg) 264 doswap(); 265 } 266 267 struct e_vnode { 268 struct vnode *avnode; 269 struct vnode vnode; 270 }; 271 272 dovnode() 273 { 274 register struct e_vnode *e_vnodebase, *endvnode, *evp; 275 register struct vnode *vp; 276 register struct mount *maddr = NULL, *mp; 277 int numvnodes; 278 struct e_vnode *loadvnodes(); 279 struct mount *getmnt(); 280 281 e_vnodebase = loadvnodes(&numvnodes); 282 if (totflg) { 283 printf("%7d vnodes\n", numvnodes); 284 return; 285 } 286 endvnode = e_vnodebase + numvnodes; 287 printf("%d active vnodes\n", numvnodes); 288 289 290 #define ST mp->mnt_stat 291 for (evp = e_vnodebase; evp < endvnode; evp++) { 292 vp = &evp->vnode; 293 if (vp->v_mount != maddr) { 294 /* 295 * New filesystem 296 */ 297 if ((mp = getmnt(vp->v_mount)) == NULL) 298 continue; 299 maddr = vp->v_mount; 300 mount_print(mp); 301 vnode_header(); 302 switch(ST.f_type) { 303 case MOUNT_UFS: 304 case MOUNT_MFS: 305 ufs_header(); 306 break; 307 case MOUNT_NFS: 308 nfs_header(); 309 break; 310 case MOUNT_NONE: 311 case MOUNT_PC: 312 default: 313 break; 314 } 315 printf("\n"); 316 } 317 vnode_print(evp->avnode, vp); 318 switch(ST.f_type) { 319 case MOUNT_UFS: 320 case MOUNT_MFS: 321 ufs_print(vp); 322 break; 323 case MOUNT_NFS: 324 nfs_print(vp); 325 break; 326 case MOUNT_NONE: 327 case MOUNT_PC: 328 default: 329 break; 330 } 331 printf("\n"); 332 } 333 free(e_vnodebase); 334 } 335 336 vnode_header() 337 { 338 printf("ADDR TYP VFLAG USE HOLD"); 339 } 340 341 vnode_print(avnode, vp) 342 struct vnode *avnode; 343 struct vnode *vp; 344 { 345 char *type, flags[16]; 346 char *fp = flags; 347 register flag; 348 349 /* 350 * set type 351 */ 352 switch(vp->v_type) { 353 case VNON: 354 type = "non"; break; 355 case VREG: 356 type = "reg"; break; 357 case VDIR: 358 type = "dir"; break; 359 case VBLK: 360 type = "blk"; break; 361 case VCHR: 362 type = "chr"; break; 363 case VLNK: 364 type = "lnk"; break; 365 case VSOCK: 366 type = "soc"; break; 367 case VFIFO: 368 type = "fif"; break; 369 case VBAD: 370 type = "bad"; break; 371 default: 372 type = "unk"; break; 373 } 374 /* 375 * gather flags 376 */ 377 flag = vp->v_flag; 378 if (flag & VROOT) 379 *fp++ = 'R'; 380 if (flag & VTEXT) 381 *fp++ = 'T'; 382 if (flag & VSYSTEM) 383 *fp++ = 'S'; 384 if (flag & VXLOCK) 385 *fp++ = 'L'; 386 if (flag & VXWANT) 387 *fp++ = 'W'; 388 if (flag & VBWAIT) 389 *fp++ = 'B'; 390 if (flag & VALIASED) 391 *fp++ = 'A'; 392 if (flag == 0) 393 *fp++ = '-'; 394 *fp = '\0'; 395 /* 396 * print it 397 */ 398 printf("%8x %s %5s %4d %4d", 399 avnode, type, flags, vp->v_usecount, vp->v_holdcnt); 400 } 401 402 ufs_header() 403 { 404 printf(" FILEID IFLAG RDEV|SZ"); 405 } 406 407 ufs_print(vp) 408 struct vnode *vp; 409 { 410 struct inode *ip = VTOI(vp); 411 char flagbuf[16], *flags = flagbuf; 412 register flag; 413 char *name; 414 mode_t type; 415 extern char *devname(); 416 417 flag = ip->i_flag; 418 if (flag & ILOCKED) 419 *flags++ = 'L'; 420 if (flag & IWANT) 421 *flags++ = 'W'; 422 if (flag & IRENAME) 423 *flags++ = 'R'; 424 if (flag & IUPD) 425 *flags++ = 'U'; 426 if (flag & IACC) 427 *flags++ = 'A'; 428 if (flag & ICHG) 429 *flags++ = 'C'; 430 if (flag & IMOD) 431 *flags++ = 'M'; 432 if (flag & ISHLOCK) 433 *flags++ = 'S'; 434 if (flag & IEXLOCK) 435 *flags++ = 'E'; 436 if (flag & ILWAIT) 437 *flags++ = 'Z'; 438 if (flag == 0) 439 *flags++ = '-'; 440 *flags = '\0'; 441 442 printf(" %6d %5s", ip->i_number, flagbuf); 443 type = ip->i_mode & S_IFMT; 444 if (type == S_IFCHR || type == S_IFBLK) 445 if (nflg || ((name = devname(ip->i_rdev, type)) == NULL)) 446 printf(" %2d,%-2d", 447 major(ip->i_rdev), minor(ip->i_rdev)); 448 else 449 printf(" %7s", name); 450 else 451 printf(" %7d", ip->i_size); 452 } 453 454 nfs_header() 455 { 456 printf(" FILEID NFLAG RDEV|SZ"); 457 } 458 459 nfs_print(vp) 460 struct vnode *vp; 461 { 462 struct nfsnode *np = VTONFS(vp); 463 char flagbuf[16], *flags = flagbuf; 464 register flag; 465 char *name; 466 mode_t type; 467 extern char *devname(); 468 469 flag = np->n_flag; 470 if (flag & NLOCKED) 471 *flags++ = 'L'; 472 if (flag & NWANT) 473 *flags++ = 'W'; 474 if (flag & NMODIFIED) 475 *flags++ = 'M'; 476 if (flag & NWRITEERR) 477 *flags++ = 'E'; 478 if (flag == 0) 479 *flags++ = '-'; 480 *flags = '\0'; 481 482 #define VT np->n_vattr 483 printf(" %6d %5s", VT.va_fileid, flagbuf); 484 type = VT.va_mode & S_IFMT; 485 if (type == S_IFCHR || type == S_IFBLK) 486 if (nflg || ((name = devname(VT.va_rdev, type)) == NULL)) 487 printf(" %2d,%-2d", 488 major(VT.va_rdev), minor(VT.va_rdev)); 489 else 490 printf(" %7s", name); 491 else 492 printf(" %7d", np->n_size); 493 } 494 495 /* 496 * Given a pointer to a mount structure in kernel space, 497 * read it in and return a usable pointer to it. 498 */ 499 struct mount * 500 getmnt(maddr) 501 struct mount *maddr; 502 { 503 static struct mtab { 504 struct mtab *next; 505 struct mount *maddr; 506 struct mount mount; 507 } *mhead = NULL; 508 register struct mtab *mt; 509 510 for (mt = mhead; mt != NULL; mt = mt->next) 511 if (maddr == mt->maddr) 512 return (&mt->mount); 513 if ((mt = (struct mtab *)malloc(sizeof (struct mtab))) == NULL) { 514 error("out of memory"); 515 exit(1); 516 } 517 if (kvm_read(V(maddr), &mt->mount, sizeof(struct mount)) != 518 sizeof(struct mount)) { 519 error("can't read mount table at %x", maddr); 520 return (NULL); 521 } 522 mt->maddr = maddr; 523 mt->next = mhead; 524 mhead = mt; 525 return (&mt->mount); 526 } 527 528 mount_print(mp) 529 struct mount *mp; 530 { 531 char *type = "unknown"; 532 register flags; 533 534 #define ST mp->mnt_stat 535 printf("*** MOUNT "); 536 switch (ST.f_type) { 537 case MOUNT_NONE: 538 type = "none"; 539 break; 540 case MOUNT_UFS: 541 type = "ufs"; 542 break; 543 case MOUNT_NFS: 544 type = "nfs"; 545 break; 546 case MOUNT_MFS: 547 type = "mfs"; 548 break; 549 case MOUNT_PC: 550 type = "pc"; 551 break; 552 } 553 printf("%s %s on %s", type, ST.f_mntfromname, ST.f_mntonname); 554 if (flags = mp->mnt_flag) { 555 char *comma = "("; 556 557 putchar(' '); 558 /* user visable flags */ 559 if (flags & MNT_RDONLY) { 560 printf("%srdonly", comma); 561 flags &= ~MNT_RDONLY; 562 comma = ","; 563 } 564 if (flags & MNT_SYNCHRONOUS) { 565 printf("%ssynchronous", comma); 566 flags &= ~MNT_SYNCHRONOUS; 567 comma = ","; 568 } 569 if (flags & MNT_NOEXEC) { 570 printf("%snoexec", comma); 571 flags &= ~MNT_NOEXEC; 572 comma = ","; 573 } 574 if (flags & MNT_NOSUID) { 575 printf("%snosuid", comma); 576 flags &= ~MNT_NOSUID; 577 comma = ","; 578 } 579 if (flags & MNT_NODEV) { 580 printf("%snodev", comma); 581 flags &= ~MNT_NODEV; 582 comma = ","; 583 } 584 if (flags & MNT_EXPORTED) { 585 printf("%sexport", comma); 586 flags &= ~MNT_EXPORTED; 587 comma = ","; 588 } 589 if (flags & MNT_EXRDONLY) { 590 printf("%sexrdonly", comma); 591 flags &= ~MNT_EXRDONLY; 592 comma = ","; 593 } 594 if (flags & MNT_LOCAL) { 595 printf("%slocal", comma); 596 flags &= ~MNT_LOCAL; 597 comma = ","; 598 } 599 if (flags & MNT_QUOTA) { 600 printf("%squota", comma); 601 flags &= ~MNT_QUOTA; 602 comma = ","; 603 } 604 /* filesystem control flags */ 605 if (flags & MNT_UPDATE) { 606 printf("%supdate", comma); 607 flags &= ~MNT_UPDATE; 608 comma = ","; 609 } 610 if (flags & MNT_MLOCK) { 611 printf("%slock", comma); 612 flags &= ~MNT_MLOCK; 613 comma = ","; 614 } 615 if (flags & MNT_MWAIT) { 616 printf("%swait", comma); 617 flags &= ~MNT_MWAIT; 618 comma = ","; 619 } 620 if (flags & MNT_MPBUSY) { 621 printf("%sbusy", comma); 622 flags &= ~MNT_MPBUSY; 623 comma = ","; 624 } 625 if (flags & MNT_MPWANT) { 626 printf("%swant", comma); 627 flags &= ~MNT_MPWANT; 628 comma = ","; 629 } 630 if (flags & MNT_UNMOUNT) { 631 printf("%sunmount", comma); 632 flags &= ~MNT_UNMOUNT; 633 comma = ","; 634 } 635 if (flags) 636 printf("%sunknown_flags:%x", flags); 637 printf(")"); 638 } 639 printf("\n"); 640 #undef ST 641 } 642 643 struct e_vnode * 644 loadvnodes(avnodes) 645 int *avnodes; 646 { 647 int ret, copysize; 648 struct e_vnode *vnodebase; 649 650 if (fcore != NULL) { 651 error("vnodes on dead kernel, not impl yet\n"); 652 exit(1); 653 } 654 if ((ret = getkerninfo(KINFO_VNODE, NULL, NULL, 0)) == -1) { 655 syserror("can't get estimate for kerninfo"); 656 exit(1); 657 } 658 copysize = ret; 659 if ((vnodebase = (struct e_vnode *)malloc(copysize)) 660 == NULL) { 661 error("out of memory"); 662 exit(1); 663 } 664 if ((ret = getkerninfo(KINFO_VNODE, vnodebase, ©size, 0)) 665 == -1) { 666 syserror("can't get vnode list"); 667 exit(1); 668 } 669 if (copysize % sizeof (struct e_vnode)) { 670 error("vnode size mismatch"); 671 exit(1); 672 } 673 *avnodes = copysize / sizeof (struct e_vnode); 674 675 return (vnodebase); 676 } 677 678 u_long 679 getword(loc) 680 int loc; 681 { 682 u_long word; 683 684 kvm_read(V(loc), &word, sizeof (word)); 685 return (word); 686 } 687 688 putf(v, n) 689 { 690 if (v) 691 printf("%c", n); 692 else 693 printf(" "); 694 } 695 696 dotext() 697 { 698 register struct text *xp; 699 int ntext; 700 struct text *xtext, *atext; 701 int ntx, ntxca; 702 703 ntx = ntxca = 0; 704 ntext = getword(nl[SNTEXT].n_value); 705 xtext = (struct text *)calloc(ntext, sizeof (struct text)); 706 atext = (struct text *)getword(nl[STEXT].n_value); 707 if (ntext < 0 || ntext > 10000) { 708 fprintf(stderr, "number of texts is preposterous (%d)\n", 709 ntext); 710 return; 711 } 712 if (xtext == NULL) { 713 fprintf(stderr, "can't allocate memory for text table\n"); 714 return; 715 } 716 kvm_read(atext, xtext, ntext * sizeof (struct text)); 717 for (xp = xtext; xp < &xtext[ntext]; xp++) { 718 if (xp->x_vptr != NULL) 719 ntxca++; 720 if (xp->x_count != 0) 721 ntx++; 722 } 723 if (totflg) { 724 printf("%3d/%3d texts active, %3d used\n", ntx, ntext, ntxca); 725 return; 726 } 727 printf("%d/%d active texts, %d used\n", ntx, ntext, ntxca); 728 printf("\ 729 LOC FLAGS DADDR CADDR RSS SIZE VPTR CNT CCNT FORW BACK\n"); 730 for (xp = xtext; xp < &xtext[ntext]; xp++) { 731 if (xp->x_vptr == NULL) 732 continue; 733 printf("%8.1x", atext + (xp - xtext)); 734 printf(" "); 735 putf(xp->x_flag&XPAGV, 'P'); 736 putf(xp->x_flag&XTRC, 'T'); 737 putf(xp->x_flag&XWRIT, 'W'); 738 putf(xp->x_flag&XLOAD, 'L'); 739 putf(xp->x_flag&XLOCK, 'K'); 740 putf(xp->x_flag&XWANT, 'w'); 741 printf("%5x", xp->x_daddr[0]); 742 printf("%10x", xp->x_caddr); 743 printf("%5d", xp->x_rssize); 744 printf("%5d", xp->x_size); 745 printf("%10.1x", xp->x_vptr); 746 printf("%5d", xp->x_count&0377); 747 printf("%5d", xp->x_ccount); 748 printf("%10x", xp->x_forw); 749 printf("%9x", xp->x_back); 750 printf("\n"); 751 } 752 free(xtext); 753 } 754 755 doproc() 756 { 757 struct proc *xproc, *aproc; 758 int nproc; 759 register struct proc *pp; 760 register loc, np; 761 /* 762 struct pte apte; 763 */ 764 765 nproc = getword(nl[SNPROC].n_value); 766 xproc = (struct proc *)calloc(nproc, sizeof (struct proc)); 767 aproc = (struct proc *)getword(nl[SPROC].n_value); 768 if (nproc < 0 || nproc > 100000) { 769 fprintf(stderr, "number of procs is preposterous (%d)\n", 770 nproc); 771 return; 772 } 773 if (xproc == NULL) { 774 fprintf(stderr, "can't allocate memory for proc table\n"); 775 return; 776 } 777 kvm_read(aproc, xproc, nproc * sizeof (struct proc)); 778 np = 0; 779 for (pp=xproc; pp < &xproc[nproc]; pp++) 780 if (pp->p_stat) 781 np++; 782 if (totflg) { 783 printf("%3d/%3d processes\n", np, nproc); 784 return; 785 } 786 printf("%d/%d processes\n", np, nproc); 787 printf(" LOC S F POIP PRI SIG UID SLP TIM CPU NI PID PPID ADDR RSS SRSS SIZE WCHAN LINK TEXTP\n"); 788 for (pp=xproc; pp<&xproc[nproc]; pp++) { 789 if (pp->p_stat==0 && allflg==0) 790 continue; 791 printf("%8x", aproc + (pp - xproc)); 792 printf(" %2d", pp->p_stat); 793 printf(" %8x", pp->p_flag); 794 printf(" %4d", pp->p_poip); 795 printf(" %3d", pp->p_pri); 796 printf(" %8x", pp->p_sig); 797 printf(" %4d", pp->p_uid); 798 printf(" %3d", pp->p_slptime); 799 printf(" %3d", pp->p_time); 800 printf(" %4d", pp->p_cpu&0377); 801 printf(" %3d", pp->p_nice); 802 printf(" %6d", pp->p_pid); 803 printf(" %6d", pp->p_ppid); 804 /* 805 if (pp->p_flag & SLOAD) { 806 kvm_read(pp->p_addr, &apte, sizeof(apte)); 807 printf(" %8x", apte.pg_pfnum); 808 } else 809 printf(" %8x", pp->p_swaddr); 810 */ 811 printf(" %4x", pp->p_rssize); 812 printf(" %4x", pp->p_swrss); 813 printf(" %5x", pp->p_dsize+pp->p_ssize); 814 printf(" %7x", clear(pp->p_wchan)); 815 printf(" %7x", clear(pp->p_link)); 816 printf(" %7x", clear(pp->p_textp)); 817 printf("\n"); 818 } 819 free(xproc); 820 } 821 822 char mesg[] = " LINE RAW CAN OUT HWT LWT ADDR COL STATE SESS PGID DISC\n"; 823 int ttyspace = 128; 824 struct tty *tty; 825 826 dotty() 827 { 828 829 if ((tty = (struct tty *)malloc(ttyspace * sizeof(*tty))) == 0) { 830 printf("pstat: out of memory\n"); 831 return; 832 } 833 #ifndef hp300 834 printf("1 cons\n"); 835 kvm_read(V(nl[SCONS].n_value), tty, sizeof(*tty)); 836 printf(mesg); 837 ttyprt(&tty[0], 0); 838 #endif 839 #ifdef vax 840 if (nl[SNQD].n_type != 0) 841 doqdss(); 842 if (nl[SNDZ].n_type != 0) 843 dottytype("dz", SDZ, SNDZ); 844 if (nl[SNDH].n_type != 0) 845 dottytype("dh", SDH, SNDH); 846 if (nl[SNDMF].n_type != 0) 847 dottytype("dmf", SDMF, SNDMF); 848 if (nl[SNDHU].n_type != 0) 849 dottytype("dhu", SDHU, SNDHU); 850 if (nl[SNDMZ].n_type != 0) 851 dottytype("dmz", SDMZ, SNDMZ); 852 #endif 853 #ifdef tahoe 854 if (nl[SNVX].n_type != 0) 855 dottytype("vx", SVX, SNVX); 856 if (nl[SNMP].n_type != 0) 857 dottytype("mp", SMP, SNMP); 858 #endif 859 #ifdef hp300 860 if (nl[SNITE].n_type != 0) 861 dottytype("ite", SITE, SNITE); 862 if (nl[SNDCA].n_type != 0) 863 dottytype("dca", SDCA, SNDCA); 864 if (nl[SNDCM].n_type != 0) 865 dottytype("dcm", SDCM, SNDCM); 866 if (nl[SNDCL].n_type != 0) 867 dottytype("dcl", SDCL, SNDCL); 868 #endif 869 if (nl[SNPTY].n_type != 0) 870 dottytype("pty", SPTY, SNPTY); 871 } 872 873 /* 874 * Special case the qdss: there are 4 ttys per qdss, 875 * but only the first of each is used as a tty. 876 */ 877 #ifdef vax 878 doqdss() 879 { 880 int nqd; 881 register struct tty *tp; 882 883 kvm_read(V(nl[SNQD].n_value), &nqd, sizeof(nqd)); 884 printf("%d qd\n", nqd); 885 kvm_read((V(nl[SQD].n_value), tty, nqd * sizeof(struct tty) * 4); 886 printf(mesg); 887 for (tp = tty; tp < &tty[nqd * 4]; tp += 4) 888 ttyprt(tp, tp - tty); 889 } 890 #endif 891 892 dottytype(name, type, number) 893 char *name; 894 { 895 int ntty; 896 register struct tty *tp; 897 extern char *realloc(); 898 899 if (tty == (struct tty *)0) 900 return; 901 kvm_read(V(nl[number].n_value), &ntty, sizeof(ntty)); 902 printf("%d %s lines\n", ntty, name); 903 if (ntty > ttyspace) { 904 ttyspace = ntty; 905 if ((tty = (struct tty *)realloc(tty, ttyspace * sizeof(*tty))) == 0) { 906 printf("pstat: out of memory\n"); 907 return; 908 } 909 } 910 kvm_read(V(nl[type].n_value), tty, ntty * sizeof(struct tty)); 911 printf(mesg); 912 for (tp = tty; tp < &tty[ntty]; tp++) 913 ttyprt(tp, tp - tty); 914 } 915 916 struct { 917 int flag; 918 char val; 919 } ttystates[] = { 920 TS_WOPEN, 'W', 921 TS_ISOPEN, 'O', 922 TS_CARR_ON, 'C', 923 TS_TIMEOUT, 'T', 924 TS_FLUSH, 'F', 925 TS_BUSY, 'B', 926 TS_ASLEEP, 'A', 927 TS_XCLUDE, 'X', 928 TS_TTSTOP, 'S', 929 TS_HUPCLS, 'H', 930 TS_TBLOCK, 'K', 931 TS_RCOLL, 'R', 932 TS_WCOLL, 'I', /* running short on letters ! */ 933 TS_ASYNC, 'Y', 934 TS_BKSL, 'D', 935 TS_ERASE, 'E', 936 TS_LNCH, 'L', 937 TS_TYPEN, 'P', 938 TS_CNTTB, 'N', 939 0, 0 940 }; 941 942 ttyprt(atp, line) 943 struct tty *atp; 944 { 945 register struct tty *tp; 946 char state[20]; 947 register i, j; 948 char *name; 949 extern char *devname(); 950 pid_t pgid; 951 952 tp = atp; 953 if (nflg || tp->t_dev == 0 || /* XXX */ 954 (name = devname(tp->t_dev, S_IFCHR)) == NULL) 955 printf("%7d ", line); 956 else 957 printf("%7s ", name); 958 printf("%2d %3d ", tp->t_rawq.c_cc, tp->t_canq.c_cc); 959 printf("%3d %4d %3d %8x %3d ", tp->t_outq.c_cc, 960 tp->t_hiwat, tp->t_lowat, tp->t_addr, tp->t_col); 961 for (i = j = 0; ttystates[i].flag; i++) 962 if (tp->t_state&ttystates[i].flag) 963 state[j++] = ttystates[i].val; 964 if (j == 0) 965 state[j++] = '-'; 966 state[j] = '\0'; 967 printf("%-4s %6x", state, (u_long)tp->t_session & ~KERNBASE); 968 if (tp->t_pgrp == NULL || kvm_read(&tp->t_pgrp->pg_id, &pgid, 969 sizeof (pid_t)) != sizeof (pid_t)) 970 pgid = 0; 971 printf("%6d ", pgid); 972 switch (tp->t_line) { 973 974 case TTYDISC: 975 printf("term\n"); 976 break; 977 978 case TABLDISC: 979 printf("tab\n"); 980 break; 981 982 case SLIPDISC: 983 printf("slip\n"); 984 break; 985 986 default: 987 printf("%d\n", tp->t_line); 988 } 989 } 990 991 /* 992 * The user structure is going away. What's left here won't 993 * be around for long. 994 */ 995 dousr() 996 { 997 register struct user *up; 998 register i, j, *ip; 999 register struct nameidata *nd; 1000 struct proc *p; 1001 int ret; 1002 1003 if ((ret = kvm_getprocs(KINFO_PROC_PID, upid)) != 1) { 1004 if (ret == -1) 1005 error("kvm_getproc: %s", kvm_geterr()); 1006 else 1007 error("can't locate process %d", upid); 1008 return (1); 1009 } 1010 if ((p = kvm_nextproc()) == NULL) { 1011 error("kvm_nextproc: %s", kvm_geterr()); 1012 return (1); 1013 } 1014 if ((up = kvm_getu(p)) == NULL) { 1015 error("kvm_getu: %s", kvm_geterr()); 1016 return (1); 1017 } 1018 nd = &up->u_nd; 1019 printf("pcb"); 1020 ip = (int *)&up->u_pcb; 1021 i = 0; 1022 while (ip < (int *)((char *)&up->u_pcb + sizeof (struct pcb))) { 1023 if (i%4 == 0) 1024 putchar('\t'); 1025 printf("%#10x ", *ip++); 1026 if (i%4 == 3) 1027 putchar('\n'); 1028 i++; 1029 } 1030 if (i%4) 1031 putchar('\n'); 1032 printf("procp\t%#x\n", up->u_procp); 1033 printf("ar0\t%#x\n", up->u_ar0); 1034 printf("sizes\ttext %d data %d stack %d\n", 1035 up->u_tsize, up->u_dsize, up->u_ssize); 1036 printf("ssave"); 1037 for (i=0; i<sizeof(label_t)/sizeof(int); i++) { 1038 if (i%5==0) 1039 printf("\t"); 1040 printf("%#11x", up->u_ssave.val[i]); 1041 if (i%5==4) 1042 printf("\n"); 1043 } 1044 if (i%5) 1045 printf("\n"); 1046 printf("odsize\t%#x\n", up->u_odsize); 1047 printf("ossize\t%#x\n", up->u_ossize); 1048 printf("outime\t%d\n", up->u_outime); 1049 printf("mmap\t%#x\n", up->u_mmap); 1050 printf("sigs"); 1051 for (i=0; i<NSIG; i++) { 1052 if (i % 8 == 0) 1053 printf("\t"); 1054 printf("%#x ", up->u_signal[i]); 1055 if (i % 8 == 7) 1056 printf("\n"); 1057 } 1058 if (i % 8) 1059 printf("\n"); 1060 printf("sigmask"); 1061 for (i=0; i<NSIG; i++) { 1062 if (i % 8 == 0) 1063 printf("\t"); 1064 printf("%#x ", up->u_sigmask[i]); 1065 if (i % 8 == 7) 1066 printf("\n"); 1067 } 1068 if (i % 8) 1069 printf("\n"); 1070 printf("sigonstack\t%#x\n", up->u_sigonstack); 1071 printf("sigintr\t%#x\n", up->u_sigintr); 1072 printf("oldmask\t%#x\n", up->u_oldmask); 1073 printf("sigstack\t%#x %#x\n", 1074 up->u_sigstack.ss_sp, up->u_sigstack.ss_onstack); 1075 printf("sig\t%#x\n", up->u_sig); 1076 printf("code\t%#x\n", up->u_code); 1077 printf("start\t%ld secs %ld usecs\n", 1078 up->u_start.tv_sec, up->u_start.tv_usec); 1079 printf("acflag\t%#x\n", up->u_acflag); 1080 printf("prof\t%#x %#x %#x %#x\n", up->u_prof.pr_base, up->u_prof.pr_size, 1081 up->u_prof.pr_off, up->u_prof.pr_scale); 1082 printf("ru\t"); 1083 ip = (int *)&up->u_ru; 1084 for (i = 0; i < sizeof(up->u_ru)/sizeof(int); i++) 1085 printf("%ld ", ip[i]); 1086 printf("\n"); 1087 ip = (int *)&up->u_cru; 1088 printf("cru\t"); 1089 for (i = 0; i < sizeof(up->u_cru)/sizeof(int); i++) 1090 printf("%ld ", ip[i]); 1091 printf("\n"); 1092 } 1093 1094 oatoi(s) 1095 char *s; 1096 { 1097 register v; 1098 1099 v = 0; 1100 while (*s) 1101 v = (v<<3) + *s++ - '0'; 1102 return(v); 1103 } 1104 1105 dofile() 1106 { 1107 int nfile; 1108 struct file *xfile, *afile; 1109 register struct file *fp; 1110 register nf; 1111 int loc; 1112 static char *dtypes[] = { "???", "inode", "socket" }; 1113 1114 nf = 0; 1115 nfile = getword(nl[SNFILE].n_value); 1116 xfile = (struct file *)calloc(nfile, sizeof (struct file)); 1117 afile = (struct file *)getword(nl[SFIL].n_value); 1118 if (nfile < 0 || nfile > 100000) { 1119 fprintf(stderr, "number of files is preposterous (%d)\n", 1120 nfile); 1121 return; 1122 } 1123 if (xfile == NULL) { 1124 fprintf(stderr, "can't allocate memory for file table\n"); 1125 return; 1126 } 1127 kvm_read(afile, xfile, nfile * sizeof (struct file)); 1128 for (fp=xfile; fp < &xfile[nfile]; fp++) 1129 if (fp->f_count) 1130 nf++; 1131 if (totflg) { 1132 printf("%3d/%3d files\n", nf, nfile); 1133 return; 1134 } 1135 printf("%d/%d open files\n", nf, nfile); 1136 printf(" LOC TYPE FLG CNT MSG DATA OFFSET\n"); 1137 for (fp=xfile,loc=(int)afile; fp < &xfile[nfile]; fp++,loc+=sizeof(xfile[0])) { 1138 if (fp->f_count==0) 1139 continue; 1140 printf("%x ", loc); 1141 if (fp->f_type <= DTYPE_SOCKET) 1142 printf("%-8.8s", dtypes[fp->f_type]); 1143 else 1144 printf("%8d", fp->f_type); 1145 putf(fp->f_flag&FREAD, 'R'); 1146 putf(fp->f_flag&FWRITE, 'W'); 1147 putf(fp->f_flag&FAPPEND, 'A'); 1148 putf(fp->f_flag&FSHLOCK, 'S'); 1149 putf(fp->f_flag&FEXLOCK, 'X'); 1150 putf(fp->f_flag&FASYNC, 'I'); 1151 printf(" %3d", mask(fp->f_count)); 1152 printf(" %3d", mask(fp->f_msgcount)); 1153 printf(" %8.1x", fp->f_data); 1154 if (fp->f_offset < 0) 1155 printf(" %x\n", fp->f_offset); 1156 else 1157 printf(" %ld\n", fp->f_offset); 1158 } 1159 free(xfile); 1160 } 1161 1162 int dmmin, dmmax, nswdev; 1163 1164 doswap() 1165 { 1166 struct proc *proc; 1167 int nproc; 1168 struct text *xtext; 1169 int ntext; 1170 struct map *swapmap; 1171 int nswapmap; 1172 struct swdevt *swdevt, *sw; 1173 register struct proc *pp; 1174 int nswap, used, tused, free, waste; 1175 int db, sb; 1176 register struct mapent *me; 1177 register struct text *xp; 1178 int i, j; 1179 long rmalloc(); 1180 1181 nproc = getword(nl[SNPROC].n_value); 1182 ntext = getword(nl[SNTEXT].n_value); 1183 if (nproc < 0 || nproc > 10000 || ntext < 0 || ntext > 10000) { 1184 fprintf(stderr, "number of procs/texts is preposterous (%d, %d)\n", 1185 nproc, ntext); 1186 return; 1187 } 1188 proc = (struct proc *)calloc(nproc, sizeof (struct proc)); 1189 if (proc == NULL) { 1190 fprintf(stderr, "can't allocate memory for proc table\n"); 1191 exit(1); 1192 } 1193 xtext = (struct text *)calloc(ntext, sizeof (struct text)); 1194 if (xtext == NULL) { 1195 fprintf(stderr, "can't allocate memory for text table\n"); 1196 exit(1); 1197 } 1198 nswapmap = getword(nl[SNSWAPMAP].n_value); 1199 swapmap = (struct map *)calloc(nswapmap, sizeof (struct map)); 1200 if (swapmap == NULL) { 1201 fprintf(stderr, "can't allocate memory for swapmap\n"); 1202 exit(1); 1203 } 1204 nswdev = getword(nl[SNSWDEV].n_value); 1205 swdevt = (struct swdevt *)calloc(nswdev, sizeof (struct swdevt)); 1206 if (swdevt == NULL) { 1207 fprintf(stderr, "can't allocate memory for swdevt table\n"); 1208 exit(1); 1209 } 1210 kvm_read(V(nl[SSWDEVT].n_value), swdevt, 1211 nswdev * sizeof (struct swdevt)); 1212 kvm_read(V(getword(nl[SPROC].n_value)), proc, 1213 nproc * sizeof (struct proc)); 1214 kvm_read(V(getword(nl[STEXT].n_value)), xtext, 1215 ntext * sizeof (struct text)); 1216 kvm_read(V(getword(nl[SWAPMAP].n_value)), swapmap, 1217 nswapmap * sizeof (struct map)); 1218 1219 swapmap->m_name = "swap"; 1220 swapmap->m_limit = (struct mapent *)&swapmap[nswapmap]; 1221 dmmin = getword(nl[SDMMIN].n_value); 1222 dmmax = getword(nl[SDMMAX].n_value); 1223 nswap = 0; 1224 for (sw = swdevt; sw < &swdevt[nswdev]; sw++) 1225 if (sw->sw_freed) 1226 nswap += sw->sw_nblks; 1227 free = 0; 1228 for (me = (struct mapent *)(swapmap+1); 1229 me < (struct mapent *)&swapmap[nswapmap]; me++) 1230 free += me->m_size; 1231 tused = 0; 1232 for (xp = xtext; xp < &xtext[ntext]; xp++) 1233 if (xp->x_vptr!=NULL) { 1234 tused += ctod(clrnd(xp->x_size)); 1235 if (xp->x_flag & XPAGV) 1236 tused += ctod(clrnd(ctopt(xp->x_size))); 1237 } 1238 used = tused; 1239 waste = 0; 1240 for (pp = proc; pp < &proc[nproc]; pp++) { 1241 if (pp->p_stat == 0 || pp->p_stat == SZOMB) 1242 continue; 1243 if (pp->p_flag & SSYS) 1244 continue; 1245 db = ctod(pp->p_dsize), sb = up(db); 1246 used += sb; 1247 waste += sb - db; 1248 db = ctod(pp->p_ssize), sb = up(db); 1249 used += sb; 1250 waste += sb - db; 1251 if ((pp->p_flag&SLOAD) == 0) 1252 used += ctod(vusize(pp)); 1253 } 1254 if (totflg) { 1255 #define btok(x) ((x) / (1024 / DEV_BSIZE)) 1256 printf("%3d/%3d 00k swap\n", 1257 btok(used/100), btok((used+free)/100)); 1258 return; 1259 } 1260 printf("%dk used (%dk text), %dk free, %dk wasted, %dk missing\n", 1261 btok(used), btok(tused), btok(free), btok(waste), 1262 /* a dmmax/2 block goes to argmap */ 1263 btok(nswap - dmmax/2 - (used + free))); 1264 printf("avail: "); 1265 for (i = dmmax; i >= dmmin; i /= 2) { 1266 j = 0; 1267 while (rmalloc(swapmap, i) != 0) 1268 j++; 1269 if (j) printf("%d*%dk ", j, btok(i)); 1270 } 1271 free = 0; 1272 for (me = (struct mapent *)(swapmap+1); 1273 me < (struct mapent *)&swapmap[nswapmap]; me++) 1274 free += me->m_size; 1275 printf("%d*1k\n", btok(free)); 1276 } 1277 1278 up(size) 1279 register int size; 1280 { 1281 register int i, block; 1282 1283 i = 0; 1284 block = dmmin; 1285 while (i < size) { 1286 i += block; 1287 if (block < dmmax) 1288 block *= 2; 1289 } 1290 return (i); 1291 } 1292 1293 /* 1294 * Compute number of pages to be allocated to the u. area 1295 * and data and stack area page tables, which are stored on the 1296 * disk immediately after the u. area. 1297 */ 1298 vusize(p) 1299 register struct proc *p; 1300 { 1301 register int tsz = p->p_tsize / NPTEPG; 1302 1303 /* 1304 * We do not need page table space on the disk for page 1305 * table pages wholly containing text. 1306 */ 1307 return (clrnd(UPAGES + 1308 clrnd(ctopt(p->p_tsize+p->p_dsize+p->p_ssize+UPAGES)) - tsz)); 1309 } 1310 1311 /* 1312 * Allocate 'size' units from the given 1313 * map. Return the base of the allocated space. 1314 * In a map, the addresses are increasing and the 1315 * list is terminated by a 0 size. 1316 * 1317 * Algorithm is first-fit. 1318 * 1319 * This routine knows about the interleaving of the swapmap 1320 * and handles that. 1321 */ 1322 long 1323 rmalloc(mp, size) 1324 register struct map *mp; 1325 long size; 1326 { 1327 register struct mapent *ep = (struct mapent *)(mp+1); 1328 register int addr; 1329 register struct mapent *bp; 1330 swblk_t first, rest; 1331 1332 if (size <= 0 || size > dmmax) 1333 return (0); 1334 /* 1335 * Search for a piece of the resource map which has enough 1336 * free space to accomodate the request. 1337 */ 1338 for (bp = ep; bp->m_size; bp++) { 1339 if (bp->m_size >= size) { 1340 /* 1341 * If allocating from swapmap, 1342 * then have to respect interleaving 1343 * boundaries. 1344 */ 1345 if (nswdev > 1 && 1346 (first = dmmax - bp->m_addr%dmmax) < bp->m_size) { 1347 if (bp->m_size - first < size) 1348 continue; 1349 addr = bp->m_addr + first; 1350 rest = bp->m_size - first - size; 1351 bp->m_size = first; 1352 if (rest) 1353 rmfree(mp, rest, addr+size); 1354 return (addr); 1355 } 1356 /* 1357 * Allocate from the map. 1358 * If there is no space left of the piece 1359 * we allocated from, move the rest of 1360 * the pieces to the left. 1361 */ 1362 addr = bp->m_addr; 1363 bp->m_addr += size; 1364 if ((bp->m_size -= size) == 0) { 1365 do { 1366 bp++; 1367 (bp-1)->m_addr = bp->m_addr; 1368 } while ((bp-1)->m_size = bp->m_size); 1369 } 1370 if (addr % CLSIZE) 1371 return (0); 1372 return (addr); 1373 } 1374 } 1375 return (0); 1376 } 1377 1378 /* 1379 * Free the previously allocated space at addr 1380 * of size units into the specified map. 1381 * Sort addr into map and combine on 1382 * one or both ends if possible. 1383 */ 1384 rmfree(mp, size, addr) 1385 struct map *mp; 1386 long size, addr; 1387 { 1388 struct mapent *firstbp; 1389 register struct mapent *bp; 1390 register int t; 1391 1392 /* 1393 * Both address and size must be 1394 * positive, or the protocol has broken down. 1395 */ 1396 if (addr <= 0 || size <= 0) 1397 goto badrmfree; 1398 /* 1399 * Locate the piece of the map which starts after the 1400 * returned space (or the end of the map). 1401 */ 1402 firstbp = bp = (struct mapent *)(mp + 1); 1403 for (; bp->m_addr <= addr && bp->m_size != 0; bp++) 1404 continue; 1405 /* 1406 * If the piece on the left abuts us, 1407 * then we should combine with it. 1408 */ 1409 if (bp > firstbp && (bp-1)->m_addr+(bp-1)->m_size >= addr) { 1410 /* 1411 * Check no overlap (internal error). 1412 */ 1413 if ((bp-1)->m_addr+(bp-1)->m_size > addr) 1414 goto badrmfree; 1415 /* 1416 * Add into piece on the left by increasing its size. 1417 */ 1418 (bp-1)->m_size += size; 1419 /* 1420 * If the combined piece abuts the piece on 1421 * the right now, compress it in also, 1422 * by shifting the remaining pieces of the map over. 1423 */ 1424 if (bp->m_addr && addr+size >= bp->m_addr) { 1425 if (addr+size > bp->m_addr) 1426 goto badrmfree; 1427 (bp-1)->m_size += bp->m_size; 1428 while (bp->m_size) { 1429 bp++; 1430 (bp-1)->m_addr = bp->m_addr; 1431 (bp-1)->m_size = bp->m_size; 1432 } 1433 } 1434 goto done; 1435 } 1436 /* 1437 * Don't abut on the left, check for abutting on 1438 * the right. 1439 */ 1440 if (addr+size >= bp->m_addr && bp->m_size) { 1441 if (addr+size > bp->m_addr) 1442 goto badrmfree; 1443 bp->m_addr -= size; 1444 bp->m_size += size; 1445 goto done; 1446 } 1447 /* 1448 * Don't abut at all. Make a new entry 1449 * and check for map overflow. 1450 */ 1451 do { 1452 t = bp->m_addr; 1453 bp->m_addr = addr; 1454 addr = t; 1455 t = bp->m_size; 1456 bp->m_size = size; 1457 bp++; 1458 } while (size = t); 1459 /* 1460 * Segment at bp is to be the delimiter; 1461 * If there is not room for it 1462 * then the table is too full 1463 * and we must discard something. 1464 */ 1465 if (bp+1 > mp->m_limit) { 1466 /* 1467 * Back bp up to last available segment. 1468 * which contains a segment already and must 1469 * be made into the delimiter. 1470 * Discard second to last entry, 1471 * since it is presumably smaller than the last 1472 * and move the last entry back one. 1473 */ 1474 bp--; 1475 printf("%s: rmap ovflo, lost [%d,%d)\n", mp->m_name, 1476 (bp-1)->m_addr, (bp-1)->m_addr+(bp-1)->m_size); 1477 bp[-1] = bp[0]; 1478 bp[0].m_size = bp[0].m_addr = 0; 1479 } 1480 done: 1481 return; 1482 badrmfree: 1483 printf("bad rmfree\n"); 1484 } 1485 1486 #include <varargs.h> 1487 1488 error(va_alist) 1489 va_dcl 1490 { 1491 char *fmt; 1492 va_list ap; 1493 extern errno; 1494 1495 fprintf(stderr, "%s: ", Program); 1496 va_start(ap); 1497 fmt = va_arg(ap, char *); 1498 (void) vfprintf(stderr, fmt, ap); 1499 va_end(ap); 1500 fprintf(stderr, "\n"); 1501 } 1502 1503 syserror(va_alist) 1504 va_dcl 1505 { 1506 char *fmt; 1507 va_list ap; 1508 extern errno; 1509 1510 fprintf(stderr, "%s: ", Program); 1511 va_start(ap); 1512 fmt = va_arg(ap, char *); 1513 (void) vfprintf(stderr, fmt, ap); 1514 va_end(ap); 1515 fprintf(stderr, ": %s\n", strerror(errno)); 1516 } 1517