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