1 /*- 2 * Copyright (c) 1988 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifndef lint 35 char copyright[] = 36 "@(#) Copyright (c) 1988 The Regents of the University of California.\n\ 37 All rights reserved.\n"; 38 #endif /* not lint */ 39 40 #ifndef lint 41 /* from: static char sccsid[] = "@(#)fstat.c 5.32 (Berkeley) 6/17/91"; */ 42 static char rcsid[] = "$Id: fstat.c,v 1.4 1993/05/20 11:54:51 cgd Exp $"; 43 #endif /* not lint */ 44 45 /* 46 * fstat 47 */ 48 #include <sys/param.h> 49 #include <sys/time.h> 50 #include <sys/proc.h> 51 #include <sys/user.h> 52 #ifdef SPPWAIT 53 #define NEWVM 54 #endif 55 #ifndef NEWVM 56 #include <machine/pte.h> 57 #include <sys/vmmac.h> 58 #endif 59 #include <sys/stat.h> 60 #include <sys/vnode.h> 61 #include <sys/socket.h> 62 #include <sys/socketvar.h> 63 #include <sys/domain.h> 64 #include <sys/protosw.h> 65 #include <sys/unpcb.h> 66 #include <sys/kinfo.h> 67 #include <sys/filedesc.h> 68 #define KERNEL 69 #define NFSSERVER 70 #define NFS_CLIENT 71 #include <sys/file.h> 72 #include <sys/mount.h> 73 #include <ufs/quota.h> 74 #include <ufs/inode.h> 75 #include <nfs/nfsv2.h> 76 #include <nfs/nfs.h> 77 #include <nfs/nfsnode.h> 78 #undef KERNEL 79 80 #include <net/route.h> 81 #include <netinet/in.h> 82 #include <netinet/in_systm.h> 83 #include <netinet/ip.h> 84 #include <netinet/in_pcb.h> 85 86 #include <errno.h> 87 #include <nlist.h> 88 #include <kvm.h> 89 #include <pwd.h> 90 #include <stdio.h> 91 #include <paths.h> 92 #include <ctype.h> 93 #include <stdlib.h> 94 #include <string.h> 95 96 #define TEXT -1 97 #define CDIR -2 98 #define RDIR -3 99 #define TRACE -4 100 101 typedef struct devs { 102 struct devs *next; 103 long fsid; 104 ino_t ino; 105 char *name; 106 } DEVS; 107 DEVS *devs; 108 109 struct filestat { 110 long fsid; 111 long fileid; 112 mode_t mode; 113 u_long size; 114 dev_t rdev; 115 }; 116 117 #ifdef notdef 118 struct nlist nl[] = { 119 { "" }, 120 }; 121 #endif 122 123 int fsflg, /* show files on same filesystem as file(s) argument */ 124 pflg, /* show files open by a particular pid */ 125 uflg; /* show files open by a particular (effective) user */ 126 int checkfile; /* true if restricting to particular files or filesystems */ 127 int nflg; /* (numerical) display f.s. and rdev as dev_t */ 128 int vflg; /* display errors in locating kernel data objects etc... */ 129 130 #define dprintf if (vflg) fprintf 131 132 struct file **ofiles; /* buffer of pointers to file structures */ 133 int maxfiles; 134 #define ALLOC_OFILES(d) \ 135 if ((d) > maxfiles) { \ 136 free(ofiles); \ 137 ofiles = malloc((d) * sizeof(struct file *)); \ 138 if (ofiles == NULL) { \ 139 fprintf(stderr, "fstat: %s\n", strerror(errno)); \ 140 exit(1); \ 141 } \ 142 maxfiles = (d); \ 143 } 144 145 /* 146 * a kvm_read that returns true if everything is read 147 */ 148 #define KVM_READ(kaddr, paddr, len) (kvm_read((kaddr), (paddr), (len)) == (len)) 149 150 void dofiles(), getinetproto(), socktrans(), nfs_filestat(), ufs_filestat(); 151 void usage(), vtrans(); 152 153 main(argc, argv) 154 int argc; 155 char **argv; 156 { 157 extern char *optarg; 158 extern int optind; 159 register struct passwd *passwd; 160 struct proc *p; 161 int arg, ch, what; 162 char *namelist = NULL, *memfile = NULL; 163 164 arg = 0; 165 what = KINFO_PROC_ALL; 166 while ((ch = getopt(argc, argv, "fnp:u:vNM")) != EOF) 167 switch((char)ch) { 168 case 'f': 169 fsflg = 1; 170 break; 171 case 'n': 172 nflg = 1; 173 break; 174 case 'p': 175 if (pflg++) 176 usage(); 177 if (!isdigit(*optarg)) { 178 fprintf(stderr, 179 "fstat: -p requires a process id\n"); 180 usage(); 181 } 182 what = KINFO_PROC_PID; 183 arg = atoi(optarg); 184 break; 185 case 'u': 186 if (uflg++) 187 usage(); 188 if (!(passwd = getpwnam(optarg))) { 189 fprintf(stderr, "%s: unknown uid\n", 190 optarg); 191 exit(1); 192 } 193 what = KINFO_PROC_UID; 194 arg = passwd->pw_uid; 195 break; 196 case 'v': 197 vflg = 1; 198 break; 199 case 'N': 200 namelist = optarg; 201 break; 202 case 'M': 203 memfile = optarg; 204 break; 205 case '?': 206 default: 207 usage(); 208 } 209 210 if (*(argv += optind)) { 211 for (; *argv; ++argv) { 212 if (getfname(*argv)) 213 checkfile = 1; 214 } 215 if (!checkfile) /* file(s) specified, but none accessable */ 216 exit(1); 217 } 218 219 ALLOC_OFILES(256); /* reserve space for file pointers */ 220 221 if (fsflg && !checkfile) { 222 /* -f with no files means use wd */ 223 if (getfname(".") == 0) 224 exit(1); 225 checkfile = 1; 226 } 227 228 if (kvm_openfiles(namelist, memfile, NULL) == -1) { 229 fprintf(stderr, "fstat: %s\n", kvm_geterr()); 230 exit(1); 231 } 232 #ifdef notdef 233 if (kvm_nlist(nl) != 0) { 234 fprintf(stderr, "fstat: no namelist: %s\n", kvm_geterr()); 235 exit(1); 236 } 237 #endif 238 if (kvm_getprocs(what, arg) == -1) { 239 fprintf(stderr, "fstat: %s\n", kvm_geterr()); 240 exit(1); 241 } 242 if (nflg) 243 printf("%s", 244 "USER CMD PID FD DEV INUM MODE SZ|DV"); 245 else 246 printf("%s", 247 "USER CMD PID FD MOUNT INUM MODE SZ|DV"); 248 if (checkfile && fsflg == 0) 249 printf(" NAME\n"); 250 else 251 putchar('\n'); 252 253 while ((p = kvm_nextproc()) != NULL) { 254 if (p->p_stat == SZOMB) 255 continue; 256 dofiles(p); 257 } 258 exit(0); 259 } 260 261 char *Uname, *Comm; 262 int Pid; 263 264 #define PREFIX(i) printf("%-8.8s %-10s %5d", Uname, Comm, Pid); \ 265 switch(i) { \ 266 case TEXT: \ 267 printf(" text"); \ 268 break; \ 269 case CDIR: \ 270 printf(" wd"); \ 271 break; \ 272 case RDIR: \ 273 printf(" root"); \ 274 break; \ 275 case TRACE: \ 276 printf(" tr"); \ 277 break; \ 278 default: \ 279 printf(" %4d", i); \ 280 break; \ 281 } 282 283 /* 284 * print open files attributed to this process 285 */ 286 void 287 dofiles(p) 288 struct proc *p; 289 { 290 int i, last; 291 struct file file; 292 #ifdef NEWVM 293 struct filedesc0 filed0; 294 #define filed filed0.fd_fd 295 struct eproc *ep; 296 #else 297 struct filedesc filed; 298 #endif 299 300 extern char *user_from_uid(); 301 #ifndef NEWVM 302 struct vnode *xvptr; 303 #endif 304 305 #ifdef NEWVM 306 ep = kvm_geteproc(p); 307 Uname = user_from_uid(ep->e_ucred.cr_uid, 0); 308 #else 309 Uname = user_from_uid(p->p_uid, 0); 310 #endif 311 Pid = p->p_pid; 312 Comm = p->p_comm; 313 314 if (p->p_fd == NULL) 315 return; 316 #ifdef NEWVM 317 if (!KVM_READ(p->p_fd, &filed0, sizeof (filed0))) { 318 dprintf(stderr, "can't read filedesc at %x for pid %d\n", 319 p->p_fd, Pid); 320 return; 321 } 322 #else 323 if (!KVM_READ(p->p_fd, &filed, sizeof (filed))) { 324 dprintf(stderr, "can't read filedesc at %x for pid %d\n", 325 p->p_fd, Pid); 326 return; 327 } 328 #endif 329 /* 330 * root directory vnode, if one 331 */ 332 if (filed.fd_rdir) 333 vtrans(filed.fd_rdir, RDIR); 334 #ifndef NEWVM 335 /* 336 * text vnode 337 */ 338 if (p->p_textp && 339 KVM_READ(&(p->p_textp->x_vptr), &xvptr, sizeof (struct vnode *)) && 340 xvptr != NULL) 341 vtrans(xvptr, TEXT); 342 #endif 343 /* 344 * current working directory vnode 345 */ 346 vtrans(filed.fd_cdir, CDIR); 347 /* 348 * ktrace vnode, if one 349 */ 350 if (p->p_tracep) 351 vtrans(p->p_tracep, TRACE); 352 /* 353 * open files 354 */ 355 #define FPSIZE (sizeof (struct file *)) 356 ALLOC_OFILES(filed.fd_lastfile); 357 #ifdef NEWVM 358 if (filed.fd_nfiles > NDFILE) { 359 if (!KVM_READ(filed.fd_ofiles, ofiles, 360 filed.fd_lastfile * FPSIZE)) { 361 dprintf(stderr, 362 "can't read file structures at %x for pid %d\n", 363 filed.fd_ofiles, Pid); 364 return; 365 } 366 } else 367 bcopy(filed0.fd_dfiles, ofiles, filed.fd_lastfile * FPSIZE); 368 #else 369 bcopy(filed.fd_ofile, ofiles, MIN(filed.fd_lastfile, NDFILE) * FPSIZE); 370 last = filed.fd_lastfile; 371 if ((last > NDFILE) && !KVM_READ(filed.fd_moreofiles, &ofiles[NDFILE], 372 (last - NDFILE) * FPSIZE)) { 373 dprintf(stderr, "can't read rest of files at %x for pid %d\n", 374 filed.fd_moreofiles, Pid); 375 return; 376 } 377 #endif 378 for (i = 0; i <= filed.fd_lastfile; i++) { 379 if (ofiles[i] == NULL) 380 continue; 381 if (!KVM_READ(ofiles[i], &file, sizeof (struct file))) { 382 dprintf(stderr, "can't read file %d at %x for pid %d\n", 383 i, ofiles[i], Pid); 384 continue; 385 } 386 if (file.f_type == DTYPE_VNODE) 387 vtrans((struct vnode *)file.f_data, i); 388 else if (file.f_type == DTYPE_SOCKET && checkfile == 0) 389 socktrans((struct socket *)file.f_data, i); 390 else { 391 dprintf(stderr, 392 "unknown file type %d for file %d of pid %d\n", 393 file.f_type, i, Pid); 394 } 395 } 396 } 397 398 void 399 vtrans(vp, i) 400 struct vnode *vp; 401 int i; 402 { 403 extern char *devname(); 404 struct vnode vn; 405 struct filestat fst; 406 char mode[15]; 407 char *badtype, *filename, *getmnton(); 408 409 filename = badtype = NULL; 410 if (!KVM_READ(vp, &vn, sizeof (struct vnode))) { 411 dprintf(stderr, "can't read vnode at %x for pid %d\n", 412 vp, Pid); 413 return; 414 } 415 if (vn.v_type == VNON || vn.v_tag == VT_NON) 416 badtype = "none"; 417 else if (vn.v_type == VBAD) 418 badtype = "bad"; 419 else 420 switch (vn.v_tag) { 421 case VT_UFS: 422 ufs_filestat(&vn, &fst); 423 break; 424 case VT_MFS: 425 ufs_filestat(&vn, &fst); 426 break; 427 case VT_NFS: 428 nfs_filestat(&vn, &fst); 429 break; 430 default: { 431 static char unknown[10]; 432 sprintf(badtype = unknown, "?(%x)", vn.v_tag); 433 break;; 434 } 435 } 436 if (checkfile) { 437 int fsmatch = 0; 438 register DEVS *d; 439 440 if (badtype) 441 return; 442 for (d = devs; d != NULL; d = d->next) 443 if (d->fsid == fst.fsid) { 444 fsmatch = 1; 445 if (d->ino == fst.fileid) { 446 filename = d->name; 447 break; 448 } 449 } 450 if (fsmatch == 0 || (filename == NULL && fsflg == 0)) 451 return; 452 } 453 PREFIX(i); 454 if (badtype) { 455 (void)printf(" - - %10s -\n", badtype); 456 return; 457 } 458 if (nflg) 459 (void)printf(" %2d,%-2d", major(fst.fsid), minor(fst.fsid)); 460 else 461 (void)printf(" %-8s", getmnton(vn.v_mount)); 462 if (nflg) 463 (void)sprintf(mode, "%o", fst.mode); 464 else 465 strmode(fst.mode, mode); 466 (void)printf(" %6d %10s", fst.fileid, mode); 467 switch (vn.v_type) { 468 case VBLK: 469 case VCHR: { 470 char *name; 471 472 if (nflg || ((name = devname(fst.rdev, vn.v_type == VCHR ? 473 S_IFCHR : S_IFBLK)) == NULL)) 474 printf(" %2d,%-2d", major(fst.rdev), minor(fst.rdev)); 475 else 476 printf(" %6s", name); 477 break; 478 } 479 default: 480 printf(" %6d", fst.size); 481 } 482 if (filename && !fsflg) 483 printf(" %s", filename); 484 485 putchar('\n'); 486 } 487 488 void 489 ufs_filestat(vp, fsp) 490 struct vnode *vp; 491 struct filestat *fsp; 492 { 493 struct inode *ip = VTOI(vp); 494 495 fsp->fsid = ip->i_dev & 0xffff; 496 fsp->fileid = (long)ip->i_number; 497 fsp->mode = (mode_t)ip->i_mode; 498 fsp->size = (u_long)ip->i_size; 499 fsp->rdev = ip->i_rdev; 500 } 501 502 void 503 nfs_filestat(vp, fsp) 504 struct vnode *vp; 505 struct filestat *fsp; 506 { 507 register struct nfsnode *np = VTONFS(vp); 508 register mode_t mode; 509 510 fsp->fsid = np->n_vattr.va_fsid; 511 fsp->fileid = np->n_vattr.va_fileid; 512 fsp->size = np->n_size; 513 fsp->rdev = np->n_vattr.va_rdev; 514 mode = (mode_t)np->n_vattr.va_mode; 515 switch (vp->v_type) { 516 case VREG: 517 mode |= S_IFREG; 518 break; 519 case VDIR: 520 mode |= S_IFDIR; 521 break; 522 case VBLK: 523 mode |= S_IFBLK; 524 break; 525 case VCHR: 526 mode |= S_IFCHR; 527 break; 528 case VLNK: 529 mode |= S_IFLNK; 530 break; 531 case VSOCK: 532 mode |= S_IFSOCK; 533 break; 534 case VFIFO: 535 mode |= S_IFIFO; 536 break; 537 }; 538 fsp->mode = mode; 539 } 540 541 542 char * 543 getmnton(m) 544 struct mount *m; 545 { 546 static struct mount mount; 547 static struct mtab { 548 struct mtab *next; 549 struct mount *m; 550 char mntonname[MNAMELEN]; 551 } *mhead = NULL; 552 register struct mtab *mt; 553 554 for (mt = mhead; mt != NULL; mt = mt->next) 555 if (m == mt->m) 556 return (mt->mntonname); 557 if (!KVM_READ(m, &mount, sizeof(struct mount))) { 558 fprintf(stderr, "can't read mount table at %x\n", m); 559 return (NULL); 560 } 561 if ((mt = malloc(sizeof (struct mtab))) == NULL) { 562 fprintf(stderr, "fstat: %s\n", strerror(errno)); 563 exit(1); 564 } 565 mt->m = m; 566 bcopy(&mount.mnt_stat.f_mntonname[0], &mt->mntonname[0], MNAMELEN); 567 mt->next = mhead; 568 mhead = mt; 569 return (mt->mntonname); 570 } 571 572 void 573 socktrans(sock, i) 574 struct socket *sock; 575 int i; 576 { 577 static char *stypename[] = { 578 "unused", /* 0 */ 579 "stream", /* 1 */ 580 "dgram", /* 2 */ 581 "raw", /* 3 */ 582 "rdm", /* 4 */ 583 "seqpak" /* 5 */ 584 }; 585 #define STYPEMAX 5 586 struct socket so; 587 struct protosw proto; 588 struct domain dom; 589 struct inpcb inpcb; 590 struct unpcb unpcb; 591 int len; 592 char dname[32], *strcpy(); 593 594 PREFIX(i); 595 596 /* fill in socket */ 597 if (!KVM_READ(sock, &so, sizeof(struct socket))) { 598 dprintf(stderr, "can't read sock at %x\n", sock); 599 goto bad; 600 } 601 602 /* fill in protosw entry */ 603 if (!KVM_READ(so.so_proto, &proto, sizeof(struct protosw))) { 604 dprintf(stderr, "can't read protosw at %x", so.so_proto); 605 goto bad; 606 } 607 608 /* fill in domain */ 609 if (!KVM_READ(proto.pr_domain, &dom, sizeof(struct domain))) { 610 dprintf(stderr, "can't read domain at %x\n", proto.pr_domain); 611 goto bad; 612 } 613 614 if ((len = 615 kvm_read(dom.dom_name, dname, sizeof(dname) - 1)) < 0) { 616 dprintf(stderr, "can't read domain name at %x\n", 617 dom.dom_name); 618 dname[0] = '\0'; 619 } 620 else 621 dname[len] = '\0'; 622 623 if ((u_short)so.so_type > STYPEMAX) 624 printf("* %s ?%d", dname, so.so_type); 625 else 626 printf("* %s %s", dname, stypename[so.so_type]); 627 628 /* 629 * protocol specific formatting 630 * 631 * Try to find interesting things to print. For tcp, the interesting 632 * thing is the address of the tcpcb, for udp and others, just the 633 * inpcb (socket pcb). For unix domain, its the address of the socket 634 * pcb and the address of the connected pcb (if connected). Otherwise 635 * just print the protocol number and address of the socket itself. 636 * The idea is not to duplicate netstat, but to make available enough 637 * information for further analysis. 638 */ 639 switch(dom.dom_family) { 640 case AF_INET: 641 getinetproto(proto.pr_protocol); 642 if (proto.pr_protocol == IPPROTO_TCP ) { 643 if (so.so_pcb) { 644 if (kvm_read(so.so_pcb, &inpcb, 645 sizeof(struct inpcb)) 646 != sizeof(struct inpcb)) { 647 dprintf(stderr, 648 "can't read inpcb at %x\n", 649 so.so_pcb); 650 goto bad; 651 } 652 printf(" %x", (int)inpcb.inp_ppcb); 653 } 654 } 655 else if (so.so_pcb) 656 printf(" %x", (int)so.so_pcb); 657 break; 658 case AF_UNIX: 659 /* print address of pcb and connected pcb */ 660 if (so.so_pcb) { 661 printf(" %x", (int)so.so_pcb); 662 if (kvm_read(so.so_pcb, &unpcb, 663 sizeof(struct unpcb)) != sizeof(struct unpcb)){ 664 dprintf(stderr, "can't read unpcb at %x\n", 665 so.so_pcb); 666 goto bad; 667 } 668 if (unpcb.unp_conn) { 669 char shoconn[4], *cp; 670 671 cp = shoconn; 672 if (!(so.so_state & SS_CANTRCVMORE)) 673 *cp++ = '<'; 674 *cp++ = '-'; 675 if (!(so.so_state & SS_CANTSENDMORE)) 676 *cp++ = '>'; 677 *cp = '\0'; 678 printf(" %s %x", shoconn, 679 (int)unpcb.unp_conn); 680 } 681 } 682 break; 683 default: 684 /* print protocol number and socket address */ 685 printf(" %d %x", proto.pr_protocol, (int)sock); 686 } 687 printf("\n"); 688 return; 689 bad: 690 printf("* error\n"); 691 } 692 693 /* 694 * getinetproto -- 695 * print name of protocol number 696 */ 697 void 698 getinetproto(number) 699 int number; 700 { 701 char *cp; 702 703 switch(number) { 704 case IPPROTO_IP: 705 cp = "ip"; break; 706 case IPPROTO_ICMP: 707 cp ="icmp"; break; 708 case IPPROTO_GGP: 709 cp ="ggp"; break; 710 case IPPROTO_TCP: 711 cp ="tcp"; break; 712 case IPPROTO_EGP: 713 cp ="egp"; break; 714 case IPPROTO_PUP: 715 cp ="pup"; break; 716 case IPPROTO_UDP: 717 cp ="udp"; break; 718 case IPPROTO_IDP: 719 cp ="idp"; break; 720 case IPPROTO_RAW: 721 cp ="raw"; break; 722 default: 723 printf(" %d", number); 724 return; 725 } 726 printf(" %s", cp); 727 } 728 729 getfname(filename) 730 char *filename; 731 { 732 struct stat statbuf; 733 DEVS *cur; 734 735 if (stat(filename, &statbuf)) { 736 fprintf(stderr, "fstat: %s: %s\n", strerror(errno), 737 filename); 738 return(0); 739 } 740 if ((cur = malloc(sizeof(DEVS))) == NULL) { 741 fprintf(stderr, "fstat: %s\n", strerror(errno)); 742 exit(1); 743 } 744 cur->next = devs; 745 devs = cur; 746 747 cur->ino = statbuf.st_ino; 748 cur->fsid = statbuf.st_dev & 0xffff; 749 cur->name = filename; 750 return(1); 751 } 752 753 void 754 usage() 755 { 756 (void)fprintf(stderr, 757 "usage: fstat [-fnv] [-p pid] [-u user] [-N system] [-M core] [file ...]\n"); 758 exit(1); 759 } 760