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