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