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