1 /* 2 * Copyright (c) 1987 Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are permitted 6 * provided that the above copyright notice and this paragraph are 7 * duplicated in all such forms and that any documentation, 8 * advertising materials, and other materials related to such 9 * distribution and use acknowledge that the software was developed 10 * by the University of California, Berkeley. The name of the 11 * University may not be used to endorse or promote products derived 12 * from this software without specific prior written permission. 13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 16 */ 17 18 #ifndef lint 19 char copyright[] = 20 "@(#) Copyright (c) 1987 Regents of the University of California.\n\ 21 All rights reserved.\n"; 22 #endif /* not lint */ 23 24 #ifndef lint 25 static char sccsid[] = "@(#)fstat.c 5.19 (Berkeley) 08/15/89"; 26 #endif /* not lint */ 27 28 /* 29 * fstat 30 */ 31 #include <machine/pte.h> 32 33 #include <sys/param.h> 34 #include <sys/dir.h> 35 #include <sys/user.h> 36 #include <sys/proc.h> 37 #include <sys/text.h> 38 #include <sys/stat.h> 39 #include <sys/time.h> 40 #include <sys/vnode.h> 41 #include <sys/socket.h> 42 #include <sys/socketvar.h> 43 #include <sys/domain.h> 44 #include <sys/protosw.h> 45 #include <sys/unpcb.h> 46 #include <sys/vmmac.h> 47 #define KERNEL 48 #include <sys/file.h> 49 #undef KERNEL 50 #include <ufs/inode.h> 51 #include <net/route.h> 52 #include <netinet/in.h> 53 #include <netinet/in_pcb.h> 54 #include <stdio.h> 55 #include <ctype.h> 56 #include <nlist.h> 57 #include <pwd.h> 58 #include <strings.h> 59 #include <paths.h> 60 61 #define TEXT -2 62 #define WD -1 63 64 #define VP if (vflg) (void)printf 65 66 typedef struct devs { 67 struct devs *next; 68 dev_t dev; 69 int inum; 70 char *name; 71 } DEVS; 72 DEVS *devs; 73 74 static struct nlist nl[] = { 75 { "_proc" }, 76 #define X_PROC 0 77 { "_Usrptmap" }, 78 #define X_USRPTMA 1 79 { "_nproc" }, 80 #define X_NPROC 2 81 { "_usrpt" }, 82 #define X_USRPT 3 83 { "_ufs_vnodeops" }, 84 #define X_UFSOPS 4 85 { "_blk_vnodeops" }, 86 #define X_BLKOPS 5 87 { "" }, 88 }; 89 90 struct proc *mproc; 91 struct pte *Usrptma, *usrpt; 92 93 union { 94 struct user user; 95 char upages[UPAGES][NBPG]; 96 } user; 97 98 extern int errno; 99 static int fflg, vflg; 100 static int kmem, mem, nproc, swap; 101 static char *uname; 102 103 off_t lseek(); 104 105 main(argc, argv) 106 int argc; 107 char **argv; 108 { 109 extern char *optarg; 110 extern int optind; 111 register struct passwd *passwd; 112 register int pflg, pid, uflg, uid; 113 int ch, size; 114 struct passwd *getpwnam(), *getpwuid(); 115 long lgetw(); 116 char *malloc(); 117 118 pflg = uflg = 0; 119 while ((ch = getopt(argc, argv, "p:u:v")) != EOF) 120 switch((char)ch) { 121 case 'p': 122 if (pflg++) 123 usage(); 124 if (!isdigit(*optarg)) { 125 fputs("fstat: -p option requires a process id.\n", stderr); 126 usage(); 127 } 128 pid = atoi(optarg); 129 break; 130 case 'u': 131 if (uflg++) 132 usage(); 133 if (!(passwd = getpwnam(optarg))) { 134 (void)fprintf(stderr, "%s: unknown uid\n", 135 optarg); 136 exit(1); 137 } 138 uid = passwd->pw_uid; 139 uname = passwd->pw_name; 140 break; 141 case 'v': /* undocumented: print read error messages */ 142 vflg++; 143 break; 144 case '?': 145 default: 146 usage(); 147 } 148 149 if (*(argv += optind)) { 150 for (; *argv; ++argv) { 151 if (getfname(*argv)) 152 fflg = 1; 153 } 154 if (!fflg) /* file(s) specified, but none accessable */ 155 exit(1); 156 } 157 158 openfiles(); 159 160 if (nlist(_PATH_UNIX, nl) == -1 || !nl[0].n_type) { 161 (void)fprintf(stderr, "%s: no namelist\n", _PATH_UNIX); 162 exit(1); 163 } 164 Usrptma = (struct pte *)nl[X_USRPTMA].n_value; 165 usrpt = (struct pte *) nl[X_USRPT].n_value; 166 nproc = (int)lgetw((off_t)nl[X_NPROC].n_value); 167 168 (void)lseek(kmem, lgetw((off_t)nl[X_PROC].n_value), L_SET); 169 size = nproc * sizeof(struct proc); 170 if ((mproc = (struct proc *)malloc((u_int)size)) == NULL) { 171 (void)fprintf(stderr, "fstat: out of space.\n"); 172 exit(1); 173 } 174 if (read(kmem, (char *)mproc, size) != size) 175 rerr1("proc table", _PATH_KMEM); 176 177 (void)printf("USER\t CMD\t PID FD\tDEVICE\tINODE\t SIZE TYPE%s\n", 178 fflg ? " NAME" : ""); 179 for (; nproc--; ++mproc) { 180 if (mproc->p_stat == 0) 181 continue; 182 if (pflg && mproc->p_pid != pid) 183 continue; 184 if (uflg) { 185 if (mproc->p_uid != uid) 186 continue; 187 } 188 else 189 uname = (passwd = getpwuid(mproc->p_uid)) ? 190 passwd->pw_name : "unknown"; 191 if (mproc->p_stat != SZOMB && getu() == 0) 192 continue; 193 dotext(); 194 readf(); 195 } 196 exit(0); 197 } 198 199 static 200 getu() 201 { 202 struct pte *pteaddr, apte; 203 struct pte arguutl[UPAGES+CLSIZE]; 204 register int i; 205 int ncl; 206 207 if ((mproc->p_flag & SLOAD) == 0) { 208 if (swap < 0) 209 return(0); 210 (void)lseek(swap, (off_t)dtob(mproc->p_swaddr), L_SET); 211 if (read(swap, (char *)&user.user, sizeof(struct user)) 212 != sizeof(struct user)) { 213 VP("fstat: can't read u for pid %d from %s\n", 214 mproc->p_pid, _PATH_DRUM); 215 return(0); 216 } 217 return(1); 218 } 219 pteaddr = &Usrptma[btokmx(mproc->p_p0br) + mproc->p_szpt - 1]; 220 (void)lseek(kmem, (off_t)pteaddr, L_SET); 221 if (read(kmem, (char *)&apte, sizeof(apte)) != sizeof(apte)) { 222 VP("fstat: can't read indir pte to get u for pid %d from %s\n", 223 mproc->p_pid, _PATH_DRUM); 224 return(0); 225 } 226 (void)lseek(mem, (off_t)ctob(apte.pg_pfnum+1) - (UPAGES+CLSIZE) 227 * sizeof(struct pte), L_SET); 228 if (read(mem, (char *)arguutl, sizeof(arguutl)) != sizeof(arguutl)) { 229 VP("fstat: can't read page table for u of pid %d from %s\n", 230 mproc->p_pid, _PATH_KMEM); 231 return(0); 232 } 233 ncl = (sizeof(struct user) + NBPG*CLSIZE - 1) / (NBPG*CLSIZE); 234 while (--ncl >= 0) { 235 i = ncl * CLSIZE; 236 (void)lseek(mem, (off_t)ctob(arguutl[CLSIZE+i].pg_pfnum), L_SET); 237 if (read(mem, user.upages[i], CLSIZE*NBPG) != CLSIZE*NBPG) { 238 VP("fstat: can't read page %u of u of pid %d from %s\n", 239 arguutl[CLSIZE+i].pg_pfnum, mproc->p_pid, 240 _PATH_MEM); 241 return(0); 242 } 243 } 244 return(1); 245 } 246 247 static 248 dotext() 249 { 250 struct text text; 251 252 (void)lseek(kmem, (off_t)mproc->p_textp, L_SET); 253 if (read(kmem, (char *) &text, sizeof(text)) != sizeof(text)) { 254 rerr1("text table", _PATH_KMEM); 255 return; 256 } 257 if (text.x_flag) 258 vtrans(DTYPE_VNODE, text.x_vptr, TEXT); 259 } 260 261 static 262 vtrans(ftype, g, fno) 263 int ftype, fno; 264 struct vnode *g; /* if ftype is vnode */ 265 { 266 struct vnode vnode; 267 struct inode inode; 268 dev_t idev; 269 char *comm, *vtype(); 270 char *name = (char *)NULL; /* set by devmatch() on a match */ 271 272 if (g || fflg) { 273 (void)lseek(kmem, (off_t)g, L_SET); 274 if (read(kmem, (char *)&vnode, sizeof(vnode)) != sizeof(vnode)) { 275 rerr2((int)g, "vnode"); 276 return; 277 } 278 if (vnode.v_op != (struct vnodeops *)nl[X_UFSOPS].n_value && 279 (vnode.v_op != (struct vnodeops *)nl[X_BLKOPS].n_value || 280 vnode.v_data == (qaddr_t)0)) { 281 if (fflg) 282 return; 283 ftype = -1; 284 } else { 285 (void)lseek(kmem, (off_t)vnode.v_data, L_SET); 286 if (read(kmem, (char *)&inode, sizeof(inode)) 287 != sizeof(inode)) { 288 rerr2((int)vnode.v_data, "inode"); 289 return; 290 } 291 idev = inode.i_dev; 292 if (fflg && !devmatch(idev, inode.i_number, &name)) 293 return; 294 } 295 } 296 if (mproc->p_pid == 0) 297 comm = "swapper"; 298 else if (mproc->p_pid == 2) 299 comm = "pagedaemon"; 300 else 301 comm = user.user.u_comm; 302 (void)printf("%-8.8s %-10.10s %5d ", uname, comm, mproc->p_pid); 303 304 switch(fno) { 305 case WD: 306 (void)printf(" wd"); break; 307 case TEXT: 308 (void)printf("text"); break; 309 default: 310 (void)printf("%4d", fno); 311 } 312 313 if (g == 0) { 314 (void)printf("* (deallocated)\n"); 315 return; 316 } 317 318 switch(ftype) { 319 320 case DTYPE_VNODE: 321 (void)printf("\t%2d, %2d\t%5lu\t", major(inode.i_dev), 322 minor(inode.i_dev), inode.i_number); 323 switch(inode.i_mode & IFMT) { 324 case IFSOCK: 325 (void)printf(" 0\t"); 326 break; 327 case IFBLK: 328 case IFCHR: 329 (void)printf("%2d, %2d\t", major(inode.i_rdev), 330 minor(inode.i_rdev)); 331 break; 332 default: 333 (void)printf("%6ld\t", inode.i_size); 334 } 335 (void)printf("%3s %s\n", vtype(vnode.v_type), name ? name : ""); 336 break; 337 338 case -1: /* DTYPE_VNODE for a remote filesystem */ 339 (void)printf(" from remote filesystem\t"); 340 (void)printf("%3s %s\n", vtype(vnode.v_type), name ? name : ""); 341 break; 342 343 case DTYPE_SOCKET: 344 socktrans((struct socket *)g); 345 break; 346 347 #ifdef DTYPE_PORT 348 case DTYPE_PORT: 349 (void)printf("* (fifo / named pipe)\n"); 350 break; 351 #endif 352 353 default: 354 (void)printf("* (unknown file type)\n"); 355 } 356 } 357 358 static char * 359 vtype(type) 360 enum vtype type; 361 { 362 switch(type) { 363 case VCHR: 364 return("chr"); 365 case VDIR: 366 return("dir"); 367 case VBLK: 368 return("blk"); 369 case VREG: 370 return("reg"); 371 case VLNK: 372 return("lnk"); 373 case VSOCK: 374 return("soc"); 375 case VBAD: 376 return("bad"); 377 default: 378 return("unk"); 379 } 380 /*NOTREACHED*/ 381 } 382 383 static 384 socktrans(sock) 385 struct socket *sock; 386 { 387 static char *stypename[] = { 388 "unused", /* 0 */ 389 "stream", /* 1 */ 390 "dgram", /* 2 */ 391 "raw", /* 3 */ 392 "rdm", /* 4 */ 393 "seqpak" /* 5 */ 394 }; 395 #define STYPEMAX 5 396 struct socket so; 397 struct protosw proto; 398 struct domain dom; 399 struct inpcb inpcb; 400 struct unpcb unpcb; 401 int len; 402 char dname[32], *strcpy(); 403 404 /* fill in socket */ 405 (void)lseek(kmem, (off_t)sock, L_SET); 406 if (read(kmem, (char *)&so, sizeof(struct socket)) 407 != sizeof(struct socket)) { 408 rerr2((int)sock, "socket"); 409 return; 410 } 411 412 /* fill in protosw entry */ 413 (void)lseek(kmem, (off_t)so.so_proto, L_SET); 414 if (read(kmem, (char *)&proto, sizeof(struct protosw)) 415 != sizeof(struct protosw)) { 416 rerr2((int)so.so_proto, "protosw"); 417 return; 418 } 419 420 /* fill in domain */ 421 (void)lseek(kmem, (off_t)proto.pr_domain, L_SET); 422 if (read(kmem, (char *)&dom, sizeof(struct domain)) 423 != sizeof(struct domain)) { 424 rerr2((int)proto.pr_domain, "domain"); 425 return; 426 } 427 428 /* 429 * grab domain name 430 * kludge "internet" --> "inet" for brevity 431 */ 432 if (dom.dom_family == AF_INET) 433 (void)strcpy(dname, "inet"); 434 else { 435 (void)lseek(kmem, (off_t)dom.dom_name, L_SET); 436 if ((len = read(kmem, dname, sizeof(dname) - 1)) < 0) { 437 rerr2((int)dom.dom_name, "char"); 438 dname[0] = '\0'; 439 } 440 else 441 dname[len] = '\0'; 442 } 443 444 if ((u_short)so.so_type > STYPEMAX) 445 (void)printf("* (%s unk%d %x", dname, so.so_type, so.so_state); 446 else 447 (void)printf("* (%s %s %x", dname, stypename[so.so_type], 448 so.so_state); 449 450 /* 451 * protocol specific formatting 452 * 453 * Try to find interesting things to print. For tcp, the interesting 454 * thing is the address of the tcpcb, for udp and others, just the 455 * inpcb (socket pcb). For unix domain, its the address of the socket 456 * pcb and the address of the connected pcb (if connected). Otherwise 457 * just print the protocol number and address of the socket itself. 458 * The idea is not to duplicate netstat, but to make available enough 459 * information for further analysis. 460 */ 461 switch(dom.dom_family) { 462 case AF_INET: 463 getinetproto(proto.pr_protocol); 464 if (proto.pr_protocol == IPPROTO_TCP ) { 465 if (so.so_pcb) { 466 (void)lseek(kmem, (off_t)so.so_pcb, L_SET); 467 if (read(kmem, (char *)&inpcb, sizeof(struct inpcb)) 468 != sizeof(struct inpcb)){ 469 rerr2((int)so.so_pcb, "inpcb"); 470 return; 471 } 472 (void)printf(" %x", (int)inpcb.inp_ppcb); 473 } 474 } 475 else if (so.so_pcb) 476 (void)printf(" %x", (int)so.so_pcb); 477 break; 478 case AF_UNIX: 479 /* print address of pcb and connected pcb */ 480 if (so.so_pcb) { 481 (void)printf(" %x", (int)so.so_pcb); 482 (void)lseek(kmem, (off_t)so.so_pcb, L_SET); 483 if (read(kmem, (char *)&unpcb, sizeof(struct unpcb)) 484 != sizeof(struct unpcb)){ 485 rerr2((int)so.so_pcb, "unpcb"); 486 return; 487 } 488 if (unpcb.unp_conn) { 489 char shoconn[4], *cp; 490 491 cp = shoconn; 492 if (!(so.so_state & SS_CANTRCVMORE)) 493 *cp++ = '<'; 494 *cp++ = '-'; 495 if (!(so.so_state & SS_CANTSENDMORE)) 496 *cp++ = '>'; 497 *cp = '\0'; 498 (void)printf(" %s %x", shoconn, 499 (int)unpcb.unp_conn); 500 } 501 } 502 break; 503 default: 504 /* print protocol number and socket address */ 505 (void)printf(" %d %x", proto.pr_protocol, (int)sock); 506 } 507 (void)printf(")\n"); 508 } 509 510 /* 511 * getinetproto -- 512 * print name of protocol number 513 */ 514 static 515 getinetproto(number) 516 int number; 517 { 518 char *cp; 519 520 switch(number) { 521 case IPPROTO_IP: 522 cp = "ip"; break; 523 case IPPROTO_ICMP: 524 cp ="icmp"; break; 525 case IPPROTO_GGP: 526 cp ="ggp"; break; 527 case IPPROTO_TCP: 528 cp ="tcp"; break; 529 case IPPROTO_EGP: 530 cp ="egp"; break; 531 case IPPROTO_PUP: 532 cp ="pup"; break; 533 case IPPROTO_UDP: 534 cp ="udp"; break; 535 case IPPROTO_IDP: 536 cp ="idp"; break; 537 case IPPROTO_RAW: 538 cp ="raw"; break; 539 default: 540 (void)printf(" %d", number); 541 return; 542 } 543 (void)printf(" %s", cp); 544 } 545 546 static 547 readf() 548 { 549 struct file lfile; 550 int i; 551 552 vtrans(DTYPE_VNODE, user.user.u_cdir, WD); 553 for (i = 0; i < NOFILE; i++) { 554 if (user.user.u_ofile[i] == 0) 555 continue; 556 (void)lseek(kmem, (off_t)user.user.u_ofile[i], L_SET); 557 if (read(kmem, (char *)&lfile, sizeof(lfile)) 558 != sizeof(lfile)) { 559 rerr1("file", _PATH_KMEM); 560 continue; 561 } 562 vtrans(lfile.f_type, (struct vnode *)lfile.f_data, i); 563 } 564 } 565 566 static 567 devmatch(idev, inum, name) 568 dev_t idev; 569 ino_t inum; 570 char **name; 571 { 572 register DEVS *d; 573 574 for (d = devs; d; d = d->next) 575 if (d->dev == idev && (d->inum == 0 || d->inum == inum)) { 576 *name = d->name; 577 return(1); 578 } 579 return(0); 580 } 581 582 static 583 getfname(filename) 584 char *filename; 585 { 586 struct stat statbuf; 587 DEVS *cur; 588 char *malloc(); 589 590 if (stat(filename, &statbuf)) { 591 (void)fprintf(stderr, "fstat: %s: %s\n", strerror(errno), 592 filename); 593 return(0); 594 } 595 if ((cur = (DEVS *)malloc(sizeof(DEVS))) == NULL) { 596 (void)fprintf(stderr, "fstat: out of space.\n"); 597 exit(1); 598 } 599 cur->next = devs; 600 devs = cur; 601 602 /* if file is block special, look for open files on it */ 603 if ((statbuf.st_mode & S_IFMT) != S_IFBLK) { 604 cur->inum = statbuf.st_ino; 605 cur->dev = statbuf.st_dev; 606 } 607 else { 608 cur->inum = 0; 609 cur->dev = statbuf.st_rdev; 610 } 611 cur->name = filename; 612 return(1); 613 } 614 615 static 616 openfiles() 617 { 618 if ((kmem = open(_PATH_KMEM, O_RDONLY, 0)) < 0) { 619 (void)fprintf(stderr, "fstat: %s: %s\n", 620 strerror(errno), _PATH_KMEM); 621 exit(1); 622 } 623 if ((mem = open(_PATH_MEM, O_RDONLY, 0)) < 0) { 624 (void)fprintf(stderr, "fstat: %s: %s\n", 625 strerror(errno), _PATH_MEM); 626 exit(1); 627 } 628 if ((swap = open(_PATH_DRUM, O_RDONLY, 0)) < 0) { 629 (void)fprintf(stderr, "fstat: %s: %s\n", 630 strerror(errno), _PATH_DRUM); 631 exit(1); 632 } 633 } 634 635 static 636 rerr1(what, fromwhat) 637 char *what, *fromwhat; 638 { 639 VP("error reading %s from %s", what, fromwhat); 640 } 641 642 static 643 rerr2(address, what) 644 int address; 645 char *what; 646 { 647 VP("error %d reading %s at %x from %s\n", errno, what, address, 648 _PATH_KMEM); 649 } 650 651 static long 652 lgetw(loc) 653 off_t loc; 654 { 655 long word; 656 657 (void)lseek(kmem, (off_t)loc, L_SET); 658 if (read(kmem, (char *)&word, sizeof(word)) != sizeof(word)) 659 rerr2((int)loc, "word"); 660 return(word); 661 } 662 663 static 664 usage() 665 { 666 (void)fprintf(stderr, 667 "usage: fstat [-u user] [-p pid] [filename ...]\n"); 668 exit(1); 669 } 670