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