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.46 (Berkeley) 06/02/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 struct vnode vn; 345 struct filestat fst; 346 char rw[3], 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 rw[0] = '\0'; 426 if (flag & FREAD) 427 strcat(rw, "r"); 428 if (flag & FWRITE) 429 strcat(rw, "w"); 430 printf(" %2s", rw); 431 if (filename && !fsflg) 432 printf(" %s", filename); 433 putchar('\n'); 434 } 435 436 int 437 ufs_filestat(vp, fsp) 438 struct vnode *vp; 439 struct filestat *fsp; 440 { 441 struct inode inode; 442 443 if (!KVM_READ(VTOI(vp), &inode, sizeof (inode))) { 444 dprintf(stderr, "can't read inode at %x for pid %d\n", 445 VTOI(vp), Pid); 446 return 0; 447 } 448 fsp->fsid = inode.i_dev & 0xffff; 449 fsp->fileid = (long)inode.i_number; 450 fsp->mode = (mode_t)inode.i_mode; 451 fsp->size = (u_long)inode.i_size; 452 fsp->rdev = inode.i_rdev; 453 454 return 1; 455 } 456 457 int 458 nfs_filestat(vp, fsp) 459 struct vnode *vp; 460 struct filestat *fsp; 461 { 462 struct nfsnode nfsnode; 463 register mode_t mode; 464 465 if (!KVM_READ(VTONFS(vp), &nfsnode, sizeof (nfsnode))) { 466 dprintf(stderr, "can't read nfsnode at %x for pid %d\n", 467 VTONFS(vp), Pid); 468 return 0; 469 } 470 fsp->fsid = nfsnode.n_vattr.va_fsid; 471 fsp->fileid = nfsnode.n_vattr.va_fileid; 472 fsp->size = nfsnode.n_size; 473 fsp->rdev = nfsnode.n_vattr.va_rdev; 474 mode = (mode_t)nfsnode.n_vattr.va_mode; 475 switch (vp->v_type) { 476 case VREG: 477 mode |= S_IFREG; 478 break; 479 case VDIR: 480 mode |= S_IFDIR; 481 break; 482 case VBLK: 483 mode |= S_IFBLK; 484 break; 485 case VCHR: 486 mode |= S_IFCHR; 487 break; 488 case VLNK: 489 mode |= S_IFLNK; 490 break; 491 case VSOCK: 492 mode |= S_IFSOCK; 493 break; 494 case VFIFO: 495 mode |= S_IFIFO; 496 break; 497 }; 498 fsp->mode = mode; 499 500 return 1; 501 } 502 503 504 char * 505 getmnton(m) 506 struct mount *m; 507 { 508 static struct mount mount; 509 static struct mtab { 510 struct mtab *next; 511 struct mount *m; 512 char mntonname[MNAMELEN]; 513 } *mhead = NULL; 514 register struct mtab *mt; 515 516 for (mt = mhead; mt != NULL; mt = mt->next) 517 if (m == mt->m) 518 return (mt->mntonname); 519 if (!KVM_READ(m, &mount, sizeof(struct mount))) { 520 fprintf(stderr, "can't read mount table at %x\n", m); 521 return (NULL); 522 } 523 if ((mt = malloc(sizeof (struct mtab))) == NULL) { 524 fprintf(stderr, "fstat: %s\n", strerror(errno)); 525 exit(1); 526 } 527 mt->m = m; 528 bcopy(&mount.mnt_stat.f_mntonname[0], &mt->mntonname[0], MNAMELEN); 529 mt->next = mhead; 530 mhead = mt; 531 return (mt->mntonname); 532 } 533 534 void 535 socktrans(sock, i) 536 struct socket *sock; 537 int i; 538 { 539 static char *stypename[] = { 540 "unused", /* 0 */ 541 "stream", /* 1 */ 542 "dgram", /* 2 */ 543 "raw", /* 3 */ 544 "rdm", /* 4 */ 545 "seqpak" /* 5 */ 546 }; 547 #define STYPEMAX 5 548 struct socket so; 549 struct protosw proto; 550 struct domain dom; 551 struct inpcb inpcb; 552 struct unpcb unpcb; 553 int len; 554 char dname[32], *strcpy(); 555 556 PREFIX(i); 557 558 /* fill in socket */ 559 if (!KVM_READ(sock, &so, sizeof(struct socket))) { 560 dprintf(stderr, "can't read sock at %x\n", sock); 561 goto bad; 562 } 563 564 /* fill in protosw entry */ 565 if (!KVM_READ(so.so_proto, &proto, sizeof(struct protosw))) { 566 dprintf(stderr, "can't read protosw at %x", so.so_proto); 567 goto bad; 568 } 569 570 /* fill in domain */ 571 if (!KVM_READ(proto.pr_domain, &dom, sizeof(struct domain))) { 572 dprintf(stderr, "can't read domain at %x\n", proto.pr_domain); 573 goto bad; 574 } 575 576 if ((len = kvm_read(kd, (u_long)dom.dom_name, dname, 577 sizeof(dname) - 1)) < 0) { 578 dprintf(stderr, "can't read domain name at %x\n", 579 dom.dom_name); 580 dname[0] = '\0'; 581 } 582 else 583 dname[len] = '\0'; 584 585 if ((u_short)so.so_type > STYPEMAX) 586 printf("* %s ?%d", dname, so.so_type); 587 else 588 printf("* %s %s", dname, stypename[so.so_type]); 589 590 /* 591 * protocol specific formatting 592 * 593 * Try to find interesting things to print. For tcp, the interesting 594 * thing is the address of the tcpcb, for udp and others, just the 595 * inpcb (socket pcb). For unix domain, its the address of the socket 596 * pcb and the address of the connected pcb (if connected). Otherwise 597 * just print the protocol number and address of the socket itself. 598 * The idea is not to duplicate netstat, but to make available enough 599 * information for further analysis. 600 */ 601 switch(dom.dom_family) { 602 case AF_INET: 603 getinetproto(proto.pr_protocol); 604 if (proto.pr_protocol == IPPROTO_TCP ) { 605 if (so.so_pcb) { 606 if (kvm_read(kd, (u_long)so.so_pcb, 607 (char *)&inpcb, sizeof(struct inpcb)) 608 != sizeof(struct inpcb)) { 609 dprintf(stderr, 610 "can't read inpcb at %x\n", 611 so.so_pcb); 612 goto bad; 613 } 614 printf(" %x", (int)inpcb.inp_ppcb); 615 } 616 } 617 else if (so.so_pcb) 618 printf(" %x", (int)so.so_pcb); 619 break; 620 case AF_UNIX: 621 /* print address of pcb and connected pcb */ 622 if (so.so_pcb) { 623 printf(" %x", (int)so.so_pcb); 624 if (kvm_read(kd, (u_long)so.so_pcb, (char *)&unpcb, 625 sizeof(struct unpcb)) != sizeof(struct unpcb)){ 626 dprintf(stderr, "can't read unpcb at %x\n", 627 so.so_pcb); 628 goto bad; 629 } 630 if (unpcb.unp_conn) { 631 char shoconn[4], *cp; 632 633 cp = shoconn; 634 if (!(so.so_state & SS_CANTRCVMORE)) 635 *cp++ = '<'; 636 *cp++ = '-'; 637 if (!(so.so_state & SS_CANTSENDMORE)) 638 *cp++ = '>'; 639 *cp = '\0'; 640 printf(" %s %x", shoconn, 641 (int)unpcb.unp_conn); 642 } 643 } 644 break; 645 default: 646 /* print protocol number and socket address */ 647 printf(" %d %x", proto.pr_protocol, (int)sock); 648 } 649 printf("\n"); 650 return; 651 bad: 652 printf("* error\n"); 653 } 654 655 /* 656 * getinetproto -- 657 * print name of protocol number 658 */ 659 void 660 getinetproto(number) 661 int number; 662 { 663 char *cp; 664 665 switch(number) { 666 case IPPROTO_IP: 667 cp = "ip"; break; 668 case IPPROTO_ICMP: 669 cp ="icmp"; break; 670 case IPPROTO_GGP: 671 cp ="ggp"; break; 672 case IPPROTO_TCP: 673 cp ="tcp"; break; 674 case IPPROTO_EGP: 675 cp ="egp"; break; 676 case IPPROTO_PUP: 677 cp ="pup"; break; 678 case IPPROTO_UDP: 679 cp ="udp"; break; 680 case IPPROTO_IDP: 681 cp ="idp"; break; 682 case IPPROTO_RAW: 683 cp ="raw"; break; 684 default: 685 printf(" %d", number); 686 return; 687 } 688 printf(" %s", cp); 689 } 690 691 getfname(filename) 692 char *filename; 693 { 694 struct stat statbuf; 695 DEVS *cur; 696 697 if (stat(filename, &statbuf)) { 698 fprintf(stderr, "fstat: %s: %s\n", filename, strerror(errno)); 699 return(0); 700 } 701 if ((cur = malloc(sizeof(DEVS))) == NULL) { 702 fprintf(stderr, "fstat: %s\n", strerror(errno)); 703 exit(1); 704 } 705 cur->next = devs; 706 devs = cur; 707 708 cur->ino = statbuf.st_ino; 709 cur->fsid = statbuf.st_dev & 0xffff; 710 cur->name = filename; 711 return(1); 712 } 713 714 void 715 usage() 716 { 717 (void)fprintf(stderr, 718 "usage: fstat [-fnv] [-p pid] [-u user] [-N system] [-M core] [file ...]\n"); 719 exit(1); 720 } 721