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.45 (Berkeley) 05/25/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 R/W"); 222 else 223 printf("%s", 224 "USER CMD PID FD MOUNT INUM MODE SZ|DV R/W"); 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, FREAD); 292 /* 293 * current working directory vnode 294 */ 295 vtrans(filed.fd_cdir, CDIR, FREAD); 296 /* 297 * ktrace vnode, if one 298 */ 299 if (p->p_tracep) 300 vtrans(p->p_tracep, TRACE, FREAD|FWRITE); 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, file.f_flag); 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, flag) 340 struct vnode *vp; 341 int i; 342 int flag; 343 { 344 extern char *devname(); 345 struct vnode vn; 346 struct filestat fst; 347 char rw[3], mode[15]; 348 char *badtype = NULL, *filename, *getmnton(); 349 350 filename = badtype = NULL; 351 if (!KVM_READ(vp, &vn, sizeof (struct vnode))) { 352 dprintf(stderr, "can't read vnode at %x for pid %d\n", 353 vp, Pid); 354 return; 355 } 356 if (vn.v_type == VNON || vn.v_tag == VT_NON) 357 badtype = "none"; 358 else if (vn.v_type == VBAD) 359 badtype = "bad"; 360 else 361 switch (vn.v_tag) { 362 case VT_UFS: 363 if (!ufs_filestat(&vn, &fst)) 364 badtype = "error"; 365 break; 366 case VT_MFS: 367 if (!ufs_filestat(&vn, &fst)) 368 badtype = "error"; 369 break; 370 case VT_NFS: 371 if (!nfs_filestat(&vn, &fst)) 372 badtype = "error"; 373 break; 374 default: { 375 static char unknown[10]; 376 sprintf(badtype = unknown, "?(%x)", vn.v_tag); 377 break;; 378 } 379 } 380 if (checkfile) { 381 int fsmatch = 0; 382 register DEVS *d; 383 384 if (badtype) 385 return; 386 for (d = devs; d != NULL; d = d->next) 387 if (d->fsid == fst.fsid) { 388 fsmatch = 1; 389 if (d->ino == fst.fileid) { 390 filename = d->name; 391 break; 392 } 393 } 394 if (fsmatch == 0 || (filename == NULL && fsflg == 0)) 395 return; 396 } 397 PREFIX(i); 398 if (badtype) { 399 (void)printf(" - - %10s -\n", badtype); 400 return; 401 } 402 if (nflg) 403 (void)printf(" %2d,%-2d", major(fst.fsid), minor(fst.fsid)); 404 else 405 (void)printf(" %-8s", getmnton(vn.v_mount)); 406 if (nflg) 407 (void)sprintf(mode, "%o", fst.mode); 408 else 409 strmode(fst.mode, mode); 410 (void)printf(" %6d %10s", fst.fileid, mode); 411 switch (vn.v_type) { 412 case VBLK: 413 case VCHR: { 414 char *name; 415 416 if (nflg || ((name = devname(fst.rdev, vn.v_type == VCHR ? 417 S_IFCHR : S_IFBLK)) == NULL)) 418 printf(" %2d,%-2d", major(fst.rdev), minor(fst.rdev)); 419 else 420 printf(" %6s", name); 421 break; 422 } 423 default: 424 printf(" %6d", fst.size); 425 } 426 rw[0] = '\0'; 427 if (flag & FREAD) 428 strcat(rw, "r"); 429 if (flag & FWRITE) 430 strcat(rw, "w"); 431 printf(" %2s", rw); 432 if (filename && !fsflg) 433 printf(" %s", filename); 434 putchar('\n'); 435 } 436 437 int 438 ufs_filestat(vp, fsp) 439 struct vnode *vp; 440 struct filestat *fsp; 441 { 442 struct inode inode; 443 444 if (!KVM_READ(VTOI(vp), &inode, sizeof (inode))) { 445 dprintf(stderr, "can't read inode at %x for pid %d\n", 446 VTOI(vp), Pid); 447 return 0; 448 } 449 fsp->fsid = inode.i_dev & 0xffff; 450 fsp->fileid = (long)inode.i_number; 451 fsp->mode = (mode_t)inode.i_mode; 452 fsp->size = (u_long)inode.i_size; 453 fsp->rdev = inode.i_rdev; 454 455 return 1; 456 } 457 458 int 459 nfs_filestat(vp, fsp) 460 struct vnode *vp; 461 struct filestat *fsp; 462 { 463 struct nfsnode nfsnode; 464 register mode_t mode; 465 466 if (!KVM_READ(VTONFS(vp), &nfsnode, sizeof (nfsnode))) { 467 dprintf(stderr, "can't read nfsnode at %x for pid %d\n", 468 VTONFS(vp), Pid); 469 return 0; 470 } 471 fsp->fsid = nfsnode.n_vattr.va_fsid; 472 fsp->fileid = nfsnode.n_vattr.va_fileid; 473 fsp->size = nfsnode.n_size; 474 fsp->rdev = nfsnode.n_vattr.va_rdev; 475 mode = (mode_t)nfsnode.n_vattr.va_mode; 476 switch (vp->v_type) { 477 case VREG: 478 mode |= S_IFREG; 479 break; 480 case VDIR: 481 mode |= S_IFDIR; 482 break; 483 case VBLK: 484 mode |= S_IFBLK; 485 break; 486 case VCHR: 487 mode |= S_IFCHR; 488 break; 489 case VLNK: 490 mode |= S_IFLNK; 491 break; 492 case VSOCK: 493 mode |= S_IFSOCK; 494 break; 495 case VFIFO: 496 mode |= S_IFIFO; 497 break; 498 }; 499 fsp->mode = mode; 500 501 return 1; 502 } 503 504 505 char * 506 getmnton(m) 507 struct mount *m; 508 { 509 static struct mount mount; 510 static struct mtab { 511 struct mtab *next; 512 struct mount *m; 513 char mntonname[MNAMELEN]; 514 } *mhead = NULL; 515 register struct mtab *mt; 516 517 for (mt = mhead; mt != NULL; mt = mt->next) 518 if (m == mt->m) 519 return (mt->mntonname); 520 if (!KVM_READ(m, &mount, sizeof(struct mount))) { 521 fprintf(stderr, "can't read mount table at %x\n", m); 522 return (NULL); 523 } 524 if ((mt = malloc(sizeof (struct mtab))) == NULL) { 525 fprintf(stderr, "fstat: %s\n", strerror(errno)); 526 exit(1); 527 } 528 mt->m = m; 529 bcopy(&mount.mnt_stat.f_mntonname[0], &mt->mntonname[0], MNAMELEN); 530 mt->next = mhead; 531 mhead = mt; 532 return (mt->mntonname); 533 } 534 535 void 536 socktrans(sock, i) 537 struct socket *sock; 538 int i; 539 { 540 static char *stypename[] = { 541 "unused", /* 0 */ 542 "stream", /* 1 */ 543 "dgram", /* 2 */ 544 "raw", /* 3 */ 545 "rdm", /* 4 */ 546 "seqpak" /* 5 */ 547 }; 548 #define STYPEMAX 5 549 struct socket so; 550 struct protosw proto; 551 struct domain dom; 552 struct inpcb inpcb; 553 struct unpcb unpcb; 554 int len; 555 char dname[32], *strcpy(); 556 557 PREFIX(i); 558 559 /* fill in socket */ 560 if (!KVM_READ(sock, &so, sizeof(struct socket))) { 561 dprintf(stderr, "can't read sock at %x\n", sock); 562 goto bad; 563 } 564 565 /* fill in protosw entry */ 566 if (!KVM_READ(so.so_proto, &proto, sizeof(struct protosw))) { 567 dprintf(stderr, "can't read protosw at %x", so.so_proto); 568 goto bad; 569 } 570 571 /* fill in domain */ 572 if (!KVM_READ(proto.pr_domain, &dom, sizeof(struct domain))) { 573 dprintf(stderr, "can't read domain at %x\n", proto.pr_domain); 574 goto bad; 575 } 576 577 if ((len = kvm_read(kd, (u_long)dom.dom_name, dname, 578 sizeof(dname) - 1)) < 0) { 579 dprintf(stderr, "can't read domain name at %x\n", 580 dom.dom_name); 581 dname[0] = '\0'; 582 } 583 else 584 dname[len] = '\0'; 585 586 if ((u_short)so.so_type > STYPEMAX) 587 printf("* %s ?%d", dname, so.so_type); 588 else 589 printf("* %s %s", dname, stypename[so.so_type]); 590 591 /* 592 * protocol specific formatting 593 * 594 * Try to find interesting things to print. For tcp, the interesting 595 * thing is the address of the tcpcb, for udp and others, just the 596 * inpcb (socket pcb). For unix domain, its the address of the socket 597 * pcb and the address of the connected pcb (if connected). Otherwise 598 * just print the protocol number and address of the socket itself. 599 * The idea is not to duplicate netstat, but to make available enough 600 * information for further analysis. 601 */ 602 switch(dom.dom_family) { 603 case AF_INET: 604 getinetproto(proto.pr_protocol); 605 if (proto.pr_protocol == IPPROTO_TCP ) { 606 if (so.so_pcb) { 607 if (kvm_read(kd, (u_long)so.so_pcb, 608 (char *)&inpcb, sizeof(struct inpcb)) 609 != sizeof(struct inpcb)) { 610 dprintf(stderr, 611 "can't read inpcb at %x\n", 612 so.so_pcb); 613 goto bad; 614 } 615 printf(" %x", (int)inpcb.inp_ppcb); 616 } 617 } 618 else if (so.so_pcb) 619 printf(" %x", (int)so.so_pcb); 620 break; 621 case AF_UNIX: 622 /* print address of pcb and connected pcb */ 623 if (so.so_pcb) { 624 printf(" %x", (int)so.so_pcb); 625 if (kvm_read(kd, (u_long)so.so_pcb, (char *)&unpcb, 626 sizeof(struct unpcb)) != sizeof(struct unpcb)){ 627 dprintf(stderr, "can't read unpcb at %x\n", 628 so.so_pcb); 629 goto bad; 630 } 631 if (unpcb.unp_conn) { 632 char shoconn[4], *cp; 633 634 cp = shoconn; 635 if (!(so.so_state & SS_CANTRCVMORE)) 636 *cp++ = '<'; 637 *cp++ = '-'; 638 if (!(so.so_state & SS_CANTSENDMORE)) 639 *cp++ = '>'; 640 *cp = '\0'; 641 printf(" %s %x", shoconn, 642 (int)unpcb.unp_conn); 643 } 644 } 645 break; 646 default: 647 /* print protocol number and socket address */ 648 printf(" %d %x", proto.pr_protocol, (int)sock); 649 } 650 printf("\n"); 651 return; 652 bad: 653 printf("* error\n"); 654 } 655 656 /* 657 * getinetproto -- 658 * print name of protocol number 659 */ 660 void 661 getinetproto(number) 662 int number; 663 { 664 char *cp; 665 666 switch(number) { 667 case IPPROTO_IP: 668 cp = "ip"; break; 669 case IPPROTO_ICMP: 670 cp ="icmp"; break; 671 case IPPROTO_GGP: 672 cp ="ggp"; break; 673 case IPPROTO_TCP: 674 cp ="tcp"; break; 675 case IPPROTO_EGP: 676 cp ="egp"; break; 677 case IPPROTO_PUP: 678 cp ="pup"; break; 679 case IPPROTO_UDP: 680 cp ="udp"; break; 681 case IPPROTO_IDP: 682 cp ="idp"; break; 683 case IPPROTO_RAW: 684 cp ="raw"; break; 685 default: 686 printf(" %d", number); 687 return; 688 } 689 printf(" %s", cp); 690 } 691 692 getfname(filename) 693 char *filename; 694 { 695 struct stat statbuf; 696 DEVS *cur; 697 698 if (stat(filename, &statbuf)) { 699 fprintf(stderr, "fstat: %s: %s\n", filename, strerror(errno)); 700 return(0); 701 } 702 if ((cur = malloc(sizeof(DEVS))) == NULL) { 703 fprintf(stderr, "fstat: %s\n", strerror(errno)); 704 exit(1); 705 } 706 cur->next = devs; 707 devs = cur; 708 709 cur->ino = statbuf.st_ino; 710 cur->fsid = statbuf.st_dev & 0xffff; 711 cur->name = filename; 712 return(1); 713 } 714 715 void 716 usage() 717 { 718 (void)fprintf(stderr, 719 "usage: fstat [-fnv] [-p pid] [-u user] [-N system] [-M core] [file ...]\n"); 720 exit(1); 721 } 722