1 /* 2 * Copyright (c) 1987 Regents of the University of California. 3 * All rights reserved. The Berkeley software License Agreement 4 * specifies the terms and conditions for redistribution. 5 */ 6 7 #ifndef lint 8 char copyright[] = 9 "@(#) Copyright (c) 1987 Regents of the University of California.\n\ 10 All rights reserved.\n"; 11 #endif /* !lint */ 12 13 #ifndef lint 14 static char sccsid[] = "@(#)fstat.c 5.9 (Berkeley) 11/24/87"; 15 #endif /* !lint */ 16 17 /* 18 * fstat 19 */ 20 #include <machine/pte.h> 21 22 #include <sys/param.h> 23 #include <sys/dir.h> 24 #include <sys/user.h> 25 #include <sys/proc.h> 26 #include <sys/text.h> 27 #include <sys/stat.h> 28 #include <sys/inode.h> 29 #include <sys/socket.h> 30 #include <sys/socketvar.h> 31 #include <sys/domain.h> 32 #include <sys/protosw.h> 33 #include <sys/unpcb.h> 34 #include <sys/vmmac.h> 35 #define KERNEL 36 #include <sys/file.h> 37 #undef KERNEL 38 #include <net/route.h> 39 #include <netinet/in.h> 40 #include <netinet/in_pcb.h> 41 #include <stdio.h> 42 #include <ctype.h> 43 #include <nlist.h> 44 #include <pwd.h> 45 46 #ifdef ULTRIX 47 /* UFS -> GFS */ 48 # define inode gnode 49 # define x_iptr x_gptr 50 # define i_dev g_dev 51 # define i_number g_number 52 # define i_mode g_mode 53 # define i_size g_size 54 #endif 55 56 #define N_KMEM "/dev/kmem" 57 #define N_MEM "/dev/mem" 58 #define N_SWAP "/dev/drum" 59 #define N_UNIX "/vmunix" 60 61 #define TEXT -2 62 #define WD -1 63 64 #define vprintf if (vflg) 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 { "" }, 84 }; 85 86 struct proc *mproc; 87 struct pte *Usrptma, *usrpt; 88 89 union { 90 struct user user; 91 char upages[UPAGES][NBPG]; 92 } user; 93 94 extern int errno; 95 static off_t procp; 96 static int fflg, hadfflg, vflg; 97 static int kmem, mem, nproc, swap; 98 static char *uname; 99 100 off_t lseek(); 101 102 main(argc, argv) 103 int argc; 104 char **argv; 105 { 106 extern char *optarg; 107 extern int optind; 108 register struct passwd *passwd; 109 register int pflg, pid, uflg, uid; 110 int ch, size; 111 struct passwd *getpwnam(), *getpwuid(); 112 long lgetw(); 113 char *malloc(); 114 115 pflg = uflg = 0; 116 while ((ch = getopt(argc, argv, "p:u:v")) != EOF) 117 switch((char)ch) { 118 case 'p': 119 if (pflg++) 120 usage(); 121 if (!isdigit(*optarg)) { 122 fputs("fstat: -p option requires a process id.\n", stderr); 123 usage(); 124 } 125 pid = atoi(optarg); 126 break; 127 case 'u': 128 if (uflg++) 129 usage(); 130 if (!(passwd = getpwnam(optarg))) { 131 fprintf(stderr, "%s: unknown uid\n", optarg); 132 exit(1); 133 } 134 uid = passwd->pw_uid; 135 uname = passwd->pw_name; 136 break; 137 case 'v': 138 vflg++; 139 break; 140 case '?': 141 default: 142 usage(); 143 } 144 145 for (argv += optind; *argv; ++argv) { 146 hadfflg = 1; 147 if (getfname(*argv)) 148 fflg = 1; 149 } 150 if (hadfflg && !fflg) /* file(s) specified, but none accessable */ 151 exit(1); 152 153 printf("USER\t CMD\t PID FD\tDEVICE\tINODE\t SIZE TYPE"); 154 if (fflg) 155 printf(" NAME\n"); 156 else 157 printf("\n"); 158 159 openfiles(); 160 161 if (nlist(N_UNIX, nl) == -1 || !nl[0].n_type) { 162 fprintf(stderr, "%s: No namelist\n", N_UNIX); 163 exit(1); 164 } 165 Usrptma = (struct pte *)nl[X_USRPTMA].n_value; 166 usrpt = (struct pte *) nl[X_USRPT].n_value; 167 procp = lgetw((off_t)nl[X_PROC].n_value); 168 nproc = (int)lgetw((off_t)nl[X_NPROC].n_value); 169 170 size = nproc * sizeof(struct proc); 171 if ((mproc = (struct proc *)malloc((u_int)size)) == NULL) { 172 fprintf(stderr, "fstat: out of space.\n"); 173 exit(1); 174 } 175 176 (void)lseek(kmem, (off_t)procp, L_SET); 177 if (read(kmem, (char *)mproc, size) != size) 178 cantread("proc table", N_KMEM); 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 fprintf(stderr, "fstat: can't read u for pid %d from %s\n", mproc->p_pid, N_SWAP); 214 return(0); 215 } 216 return(1); 217 } 218 pteaddr = &Usrptma[btokmx(mproc->p_p0br) + mproc->p_szpt - 1]; 219 (void)lseek(kmem, (off_t)pteaddr, L_SET); 220 if (read(kmem, (char *)&apte, sizeof(apte)) != sizeof(apte)) { 221 printf("fstat: can't read indir pte to get u for pid %d from %s\n", mproc->p_pid, N_SWAP); 222 return(0); 223 } 224 (void)lseek(mem, (off_t)ctob(apte.pg_pfnum+1) - (UPAGES+CLSIZE) 225 * sizeof(struct pte), L_SET); 226 if (read(mem, (char *)arguutl, sizeof(arguutl)) != sizeof(arguutl)) { 227 printf("fstat: can't read page table for u of pid %d from %s\n", mproc->p_pid, N_KMEM); 228 return(0); 229 } 230 ncl = (sizeof(struct user) + NBPG*CLSIZE - 1) / (NBPG*CLSIZE); 231 while (--ncl >= 0) { 232 i = ncl * CLSIZE; 233 (void)lseek(mem, (off_t)ctob(arguutl[CLSIZE+i].pg_pfnum), L_SET); 234 if (read(mem, user.upages[i], CLSIZE*NBPG) != CLSIZE*NBPG) { 235 printf("fstat: can't read page %u of u of pid %d from %s\n", arguutl[CLSIZE+i].pg_pfnum, mproc->p_pid, N_MEM); 236 return(0); 237 } 238 } 239 return(1); 240 } 241 242 static 243 dotext() 244 { 245 struct text text; 246 247 (void)lseek(kmem, (off_t)mproc->p_textp, L_SET); 248 if (read(kmem, (char *) &text, sizeof(text)) != sizeof(text)) { 249 cantread("text table", N_KMEM); 250 return; 251 } 252 if (text.x_flag) 253 itrans(DTYPE_INODE, text.x_iptr, TEXT); 254 } 255 256 static 257 itrans(ftype, g, fno) 258 int ftype, fno; 259 struct inode *g; /* if ftype is inode */ 260 { 261 struct inode inode; 262 dev_t idev; 263 char *comm, *itype(); 264 char *name = (char *)NULL; /* set by devmatch() on a match */ 265 266 if (g || fflg) { 267 (void)lseek(kmem, (off_t)g, L_SET); 268 if (read(kmem, (char *)&inode, sizeof(inode)) != sizeof(inode)) { 269 vprintf("error %d reading inode at %x from kmem\n", errno, (int)g); 270 return; 271 } 272 idev = inode.i_dev; 273 if (fflg && !devmatch(idev, inode.i_number, &name)) 274 return; 275 } 276 if (mproc->p_pid == 0) 277 comm = "swapper"; 278 else if (mproc->p_pid == 2) 279 comm = "pagedaemon"; 280 else 281 comm = user.user.u_comm; 282 printf("%-8.8s %-10.10s %5d ", uname, comm, mproc->p_pid); 283 284 switch(fno) { 285 case WD: 286 printf(" wd"); break; 287 case TEXT: 288 printf("text"); break; 289 default: 290 printf("%4d", fno); 291 } 292 293 if (g == 0) { 294 printf("* (deallocated)\n"); 295 return; 296 } 297 298 switch(ftype) { 299 case DTYPE_INODE: 300 printf("\t%2d, %2d\t%5lu\t%6ld\t%3s %s\n", major(inode.i_dev), 301 minor(inode.i_dev), inode.i_number, 302 inode.i_mode == IFSOCK ? 0 : inode.i_size, 303 itype(inode.i_mode), name ? name : ""); 304 break; 305 case DTYPE_SOCKET: 306 socktrans((struct socket *)g); 307 break; 308 #ifdef DTYPE_PORT 309 case DTYPE_PORT: 310 printf("* (fifo / named pipe)\n"); 311 break; 312 #endif 313 default: 314 printf("* (unknown file type)\n"); 315 } 316 } 317 318 static char * 319 itype(mode) 320 u_short mode; 321 { 322 switch(mode & IFMT) { 323 case IFCHR: 324 return("chr"); 325 case IFDIR: 326 return("dir"); 327 case IFBLK: 328 return("blk"); 329 case IFREG: 330 return("reg"); 331 case IFLNK: 332 return("lnk"); 333 case IFSOCK: 334 return("soc"); 335 default: 336 return("unk"); 337 } 338 /*NOTREACHED*/ 339 } 340 341 static 342 socktrans(sock) 343 struct socket *sock; 344 { 345 static char *stypename[] = { 346 "unused", /* 0 */ 347 "stream", /* 1 */ 348 "dgram", /* 2 */ 349 "raw", /* 3 */ 350 "rdm", /* 4 */ 351 "seqpak" /* 5 */ 352 }; 353 #define STYPEMAX 5 354 struct socket so; 355 struct protosw proto; 356 struct domain dom; 357 struct inpcb inpcb; 358 struct unpcb unpcb; 359 int len; 360 char dname[32], *strcpy(); 361 362 /* fill in socket */ 363 (void)lseek(kmem, (off_t)sock, L_SET); 364 if (read(kmem, (char *)&so, sizeof(struct socket)) 365 != sizeof(struct socket)) { 366 vprintf("error %d reading socket at %x from kmem\n", errno, (int)sock); 367 return; 368 } 369 370 /* fill in protosw entry */ 371 (void)lseek(kmem, (off_t)so.so_proto, L_SET); 372 if (read(kmem, (char *)&proto, sizeof(struct protosw)) 373 != sizeof(struct protosw)) { 374 vprintf("error %d reading protosw at %x from kmem\n", errno, (int)so.so_proto); 375 return; 376 } 377 378 /* fill in domain */ 379 (void)lseek(kmem, (off_t)proto.pr_domain, L_SET); 380 if (read(kmem, (char *)&dom, sizeof(struct domain)) 381 != sizeof(struct domain)) { 382 vprintf("error %d reading domain at %x from kmem\n", errno, (int)proto.pr_domain); 383 return; 384 } 385 386 /* 387 * grab domain name 388 * kludge "internet" --> "inet" for brevity 389 */ 390 if (dom.dom_family == AF_INET) 391 (void)strcpy(dname, "inet"); 392 else { 393 (void)lseek(kmem, (off_t)dom.dom_name, L_SET); 394 if ((len = read(kmem, dname, sizeof(dname) - 1)) < 0) { 395 vprintf("error %d reading char at %x from kmem\n", errno, (int)dom.dom_name); 396 dname[0] = '\0'; 397 } 398 else 399 dname[len] = '\0'; 400 } 401 402 if ((u_short)so.so_type > STYPEMAX) 403 printf("* (%s unk%d %x", dname, so.so_type, so.so_state); 404 else 405 printf("* (%s %s %x", dname, stypename[so.so_type], 406 so.so_state); 407 408 /* 409 * protocol specific formating 410 * 411 * Try to find interesting things to print. For tcp, the interesting 412 * thing is the address of the tcpcb, for udp and others, just the 413 * inpcb (socket pcb). For unix domain, its the address of the socket 414 * pcb and the address of the connected pcb (if connected). Otherwise 415 * just print the protocol number and address of the socket itself. 416 * The idea is not to duplicate netstat, but to make available enough 417 * information for further analysis. 418 */ 419 switch(dom.dom_family) { 420 case AF_INET: 421 getinetproto(proto.pr_protocol); 422 if (proto.pr_protocol == IPPROTO_TCP ) { 423 if (so.so_pcb) { 424 (void)lseek(kmem, (off_t)so.so_pcb, L_SET); 425 if (read(kmem, (char *)&inpcb, sizeof(struct inpcb)) 426 != sizeof(struct inpcb)){ 427 vprintf("error %d reading inpcb at %x from kmem\n", errno, (int)so.so_pcb); 428 return; 429 } 430 printf(" %x", (int)inpcb.inp_ppcb); 431 } 432 } 433 else if (so.so_pcb) 434 printf(" %x", (int)so.so_pcb); 435 break; 436 case AF_UNIX: 437 /* print address of pcb and connected pcb */ 438 if (so.so_pcb) { 439 printf(" %x", (int)so.so_pcb); 440 (void)lseek(kmem, (off_t)so.so_pcb, L_SET); 441 if (read(kmem, (char *)&unpcb, sizeof(struct unpcb)) 442 != sizeof(struct unpcb)){ 443 vprintf("error %d reading unpcb at %x from kmem\n", errno, (int)so.so_pcb); 444 return; 445 } 446 if (unpcb.unp_conn) { 447 char shoconn[4], *cp; 448 449 cp = shoconn; 450 if (!(so.so_state & SS_CANTRCVMORE)) 451 *cp++ = '<'; 452 *cp++ = '-'; 453 if (!(so.so_state & SS_CANTSENDMORE)) 454 *cp++ = '>'; 455 *cp = '\0'; 456 printf(" %s %x", shoconn, (int)unpcb.unp_conn); 457 } 458 } 459 break; 460 default: 461 /* print protocol number and socket address */ 462 printf(" %d %x", proto.pr_protocol, (int)sock); 463 } 464 printf(")\n"); 465 } 466 467 /* 468 * getinetproto -- 469 * print name of protocol number 470 */ 471 static 472 getinetproto(number) 473 int number; 474 { 475 char *cp; 476 477 switch(number) { 478 case IPPROTO_IP: 479 cp = "ip"; break; 480 case IPPROTO_ICMP: 481 cp ="icmp"; break; 482 case IPPROTO_GGP: 483 cp ="ggp"; break; 484 case IPPROTO_TCP: 485 cp ="tcp"; break; 486 case IPPROTO_EGP: 487 cp ="egp"; break; 488 case IPPROTO_PUP: 489 cp ="pup"; break; 490 case IPPROTO_UDP: 491 cp ="udp"; break; 492 case IPPROTO_IDP: 493 cp ="idp"; break; 494 case IPPROTO_RAW: 495 cp ="raw"; break; 496 default: 497 printf(" %d", number); 498 return; 499 } 500 printf(" %s", cp); 501 } 502 503 static 504 readf() 505 { 506 struct file lfile; 507 int i; 508 509 itrans(DTYPE_INODE, user.user.u_cdir, WD); 510 for (i = 0; i < NOFILE; i++) { 511 if (user.user.u_ofile[i] == 0) 512 continue; 513 (void)lseek(kmem, (off_t)user.user.u_ofile[i], L_SET); 514 if (read(kmem, (char *)&lfile, sizeof(lfile)) 515 != sizeof(lfile)) { 516 cantread("file", N_KMEM); 517 continue; 518 } 519 itrans(lfile.f_type, (struct inode *)lfile.f_data, i); 520 } 521 } 522 523 static 524 devmatch(idev, inum, name) 525 dev_t idev; 526 ino_t inum; 527 char **name; 528 { 529 register DEVS *d; 530 531 for (d = devs; d; d = d->next) 532 if (d->dev == idev && (d->inum == 0 || d->inum == inum)) { 533 *name = d->name; 534 return(1); 535 } 536 return(0); 537 } 538 539 static 540 getfname(filename) 541 char *filename; 542 { 543 struct stat statbuf; 544 DEVS *cur; 545 char *malloc(); 546 547 if (stat(filename, &statbuf)) { 548 perror(filename); 549 return(0); 550 } 551 if ((cur = (DEVS *)malloc(sizeof(DEVS))) == NULL) { 552 fprintf(stderr, "fstat: out of space.\n"); 553 exit(1); 554 } 555 cur->next = devs; 556 devs = cur; 557 558 /* if file is block special, look for open files on it */ 559 if ((statbuf.st_mode & S_IFMT) != S_IFBLK) { 560 cur->inum = statbuf.st_ino; 561 cur->dev = statbuf.st_dev; 562 } 563 else { 564 cur->inum = 0; 565 cur->dev = statbuf.st_rdev; 566 } 567 cur->name = filename; 568 return(1); 569 } 570 571 static 572 openfiles() 573 { 574 if ((kmem = open(N_KMEM, O_RDONLY, 0)) < 0) { 575 perror(N_KMEM); 576 exit(1); 577 } 578 if ((mem = open(N_MEM, O_RDONLY, 0)) < 0) { 579 perror(N_MEM); 580 exit(1); 581 } 582 if ((swap = open(N_SWAP, O_RDONLY, 0)) < 0) { 583 perror(N_SWAP); 584 exit(1); 585 } 586 } 587 588 static 589 cantread(what, fromwhat) 590 char *what, *fromwhat; 591 { 592 vprintf("fstat: error reading %s from %s", what, fromwhat); 593 } 594 595 static long 596 lgetw(loc) 597 off_t loc; 598 { 599 long word; 600 601 (void)lseek(kmem, (off_t)loc, L_SET); 602 if (read(kmem, (char *) &word, sizeof(word)) != sizeof(word)) 603 vprintf("error reading kmem at %lx\n", loc); 604 return(word); 605 } 606 607 static 608 usage() 609 { 610 fputs("usage: fstat [-v] [-u user] [-p pid] [filename ...]\n", stderr); 611 exit(1); 612 } 613