=/^G show total bytes now; document may not always work
[unix-history] / usr / src / usr.bin / fstat / fstat.c
CommitLineData
055eee28
KB
1/*
2 * Copyright (c) 1987 Regents of the University of California.
5ebc207c
KB
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
b8c620d6
KB
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.
055eee28
KB
16 */
17
18#ifndef lint
19char copyright[] =
20"@(#) Copyright (c) 1987 Regents of the University of California.\n\
21 All rights reserved.\n";
5ebc207c 22#endif /* not lint */
055eee28
KB
23
24#ifndef lint
b8c620d6 25static char sccsid[] = "@(#)fstat.c 5.13 (Berkeley) %G%";
5ebc207c 26#endif /* not lint */
055eee28
KB
27
28/*
29 * fstat
30 */
f255214c 31#include <machine/pte.h>
1a585c5c 32
055eee28 33#include <sys/param.h>
055eee28
KB
34#include <sys/dir.h>
35#include <sys/user.h>
36#include <sys/proc.h>
055eee28
KB
37#include <sys/text.h>
38#include <sys/stat.h>
055eee28
KB
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>
055eee28 44#include <sys/unpcb.h>
f255214c
KB
45#include <sys/vmmac.h>
46#define KERNEL
055eee28 47#include <sys/file.h>
f255214c 48#undef KERNEL
1a585c5c
KB
49#include <net/route.h>
50#include <netinet/in.h>
51#include <netinet/in_pcb.h>
52#include <stdio.h>
7168b138 53#include <ctype.h>
1a585c5c
KB
54#include <nlist.h>
55#include <pwd.h>
055eee28 56
1a585c5c
KB
57#ifdef ULTRIX
58 /* UFS -> GFS */
055eee28
KB
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
f255214c
KB
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"
055eee28 71
1a585c5c
KB
72#define TEXT -2
73#define WD -1
f255214c 74
f255214c 75typedef struct devs {
4575617f
KB
76 struct devs *next;
77 dev_t dev;
78 int inum;
79 char *name;
f255214c 80} DEVS;
4575617f 81DEVS *devs;
5e369078 82
f255214c 83static struct nlist nl[] = {
055eee28
KB
84 { "_proc" },
85#define X_PROC 0
86 { "_Usrptmap" },
87#define X_USRPTMA 1
055eee28 88 { "_nproc" },
f255214c
KB
89#define X_NPROC 2
90 { "_usrpt" },
91#define X_USRPT 3
055eee28
KB
92 { "" },
93};
94
4575617f
KB
95struct proc *mproc;
96struct pte *Usrptma, *usrpt;
f255214c 97
055eee28 98union {
4575617f
KB
99 struct user user;
100 char upages[UPAGES][NBPG];
055eee28 101} user;
055eee28 102
4575617f
KB
103extern int errno;
104static int fflg, vflg;
105static int kmem, mem, nproc, swap;
106static char *uname;
1a585c5c 107
4575617f 108off_t lseek();
055eee28
KB
109
110main(argc, argv)
4575617f
KB
111 int argc;
112 char **argv;
055eee28 113{
f255214c
KB
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;
1a585c5c
KB
124 while ((ch = getopt(argc, argv, "p:u:v")) != EOF)
125 switch((char)ch) {
126 case 'p':
7168b138
MT
127 if (pflg++)
128 usage();
129 if (!isdigit(*optarg)) {
130 fputs("fstat: -p option requires a process id.\n", stderr);
055eee28 131 usage();
055eee28 132 }
f255214c 133 pid = atoi(optarg);
1a585c5c
KB
134 break;
135 case 'u':
136 if (uflg++)
055eee28 137 usage();
f255214c
KB
138 if (!(passwd = getpwnam(optarg))) {
139 fprintf(stderr, "%s: unknown uid\n", optarg);
055eee28
KB
140 exit(1);
141 }
f255214c
KB
142 uid = passwd->pw_uid;
143 uname = passwd->pw_name;
1a585c5c 144 break;
5ebc207c 145 case 'v': /* undocumented: print read error messages */
1a585c5c
KB
146 vflg++;
147 break;
148 case '?':
149 default:
150 usage();
055eee28 151 }
055eee28 152
5ebc207c 153 if (*(argv += optind)) {
193d3a86 154 for (; *argv; ++argv) {
5ebc207c
KB
155 if (getfname(*argv))
156 fflg = 1;
157 }
158 if (!fflg) /* file(s) specified, but none accessable */
159 exit(1);
055eee28 160 }
d81abd9a 161
055eee28 162 openfiles();
055eee28 163
f255214c
KB
164 if (nlist(N_UNIX, nl) == -1 || !nl[0].n_type) {
165 fprintf(stderr, "%s: No namelist\n", N_UNIX);
055eee28
KB
166 exit(1);
167 }
1a585c5c 168 Usrptma = (struct pte *)nl[X_USRPTMA].n_value;
f255214c 169 usrpt = (struct pte *) nl[X_USRPT].n_value;
f255214c
KB
170 nproc = (int)lgetw((off_t)nl[X_NPROC].n_value);
171
5ebc207c 172 (void)lseek(kmem, lgetw((off_t)nl[X_PROC].n_value), L_SET);
f255214c
KB
173 size = nproc * sizeof(struct proc);
174 if ((mproc = (struct proc *)malloc((u_int)size)) == NULL) {
175 fprintf(stderr, "fstat: out of space.\n");
055eee28
KB
176 exit(1);
177 }
f255214c 178 if (read(kmem, (char *)mproc, size) != size)
5ebc207c
KB
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" : "");
f255214c
KB
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);
055eee28
KB
201}
202
1a585c5c 203static
055eee28
KB
204getu()
205{
206 struct pte *pteaddr, apte;
207 struct pte arguutl[UPAGES+CLSIZE];
208 register int i;
f255214c 209 int ncl;
055eee28 210
055eee28
KB
211 if ((mproc->p_flag & SLOAD) == 0) {
212 if (swap < 0)
f255214c 213 return(0);
1a585c5c 214 (void)lseek(swap, (off_t)dtob(mproc->p_swaddr), L_SET);
f255214c
KB
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);
1a585c5c 218 return(0);
055eee28 219 }
1a585c5c 220 return(1);
055eee28
KB
221 }
222 pteaddr = &Usrptma[btokmx(mproc->p_p0br) + mproc->p_szpt - 1];
1a585c5c 223 (void)lseek(kmem, (off_t)pteaddr, L_SET);
055eee28 224 if (read(kmem, (char *)&apte, sizeof(apte)) != sizeof(apte)) {
f255214c 225 printf("fstat: can't read indir pte to get u for pid %d from %s\n", mproc->p_pid, N_SWAP);
1a585c5c 226 return(0);
055eee28 227 }
1a585c5c
KB
228 (void)lseek(mem, (off_t)ctob(apte.pg_pfnum+1) - (UPAGES+CLSIZE)
229 * sizeof(struct pte), L_SET);
055eee28 230 if (read(mem, (char *)arguutl, sizeof(arguutl)) != sizeof(arguutl)) {
f255214c 231 printf("fstat: can't read page table for u of pid %d from %s\n", mproc->p_pid, N_KMEM);
1a585c5c 232 return(0);
055eee28 233 }
f255214c 234 ncl = (sizeof(struct user) + NBPG*CLSIZE - 1) / (NBPG*CLSIZE);
055eee28
KB
235 while (--ncl >= 0) {
236 i = ncl * CLSIZE;
1a585c5c 237 (void)lseek(mem, (off_t)ctob(arguutl[CLSIZE+i].pg_pfnum), L_SET);
055eee28 238 if (read(mem, user.upages[i], CLSIZE*NBPG) != CLSIZE*NBPG) {
f255214c 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);
055eee28
KB
240 return(0);
241 }
242 }
1a585c5c 243 return(1);
055eee28
KB
244}
245
1a585c5c 246static
055eee28
KB
247dotext()
248{
4575617f 249 struct text text;
055eee28 250
1a585c5c 251 (void)lseek(kmem, (off_t)mproc->p_textp, L_SET);
055eee28 252 if (read(kmem, (char *) &text, sizeof(text)) != sizeof(text)) {
5ebc207c 253 rerr1("text table", N_KMEM);
055eee28
KB
254 return;
255 }
f255214c
KB
256 if (text.x_flag)
257 itrans(DTYPE_INODE, text.x_iptr, TEXT);
055eee28
KB
258}
259
1a585c5c 260static
055eee28 261itrans(ftype, g, fno)
1a585c5c 262 int ftype, fno;
4575617f 263 struct inode *g; /* if ftype is inode */
055eee28 264{
f255214c
KB
265 struct inode inode;
266 dev_t idev;
267 char *comm, *itype();
d81abd9a 268 char *name = (char *)NULL; /* set by devmatch() on a match */
1a585c5c
KB
269
270 if (g || fflg) {
271 (void)lseek(kmem, (off_t)g, L_SET);
272 if (read(kmem, (char *)&inode, sizeof(inode)) != sizeof(inode)) {
5ebc207c 273 rerr2(errno, (int)g, "inode");
1a585c5c
KB
274 return;
275 }
276 idev = inode.i_dev;
d81abd9a 277 if (fflg && !devmatch(idev, inode.i_number, &name))
1a585c5c 278 return;
055eee28 279 }
055eee28
KB
280 if (mproc->p_pid == 0)
281 comm = "swapper";
282 else if (mproc->p_pid == 2)
283 comm = "pagedaemon";
284 else
1a585c5c 285 comm = user.user.u_comm;
055eee28 286 printf("%-8.8s %-10.10s %5d ", uname, comm, mproc->p_pid);
f255214c
KB
287
288 switch(fno) {
289 case WD:
290 printf(" wd"); break;
291 case TEXT:
292 printf("text"); break;
293 default:
055eee28 294 printf("%4d", fno);
f255214c 295 }
055eee28
KB
296
297 if (g == 0) {
298 printf("* (deallocated)\n");
299 return;
300 }
301
1a585c5c
KB
302 switch(ftype) {
303 case DTYPE_INODE:
d81abd9a 304 printf("\t%2d, %2d\t%5lu\t%6ld\t%3s %s\n", major(inode.i_dev),
1a585c5c 305 minor(inode.i_dev), inode.i_number,
f255214c 306 inode.i_mode == IFSOCK ? 0 : inode.i_size,
d81abd9a 307 itype(inode.i_mode), name ? name : "");
1a585c5c
KB
308 break;
309 case DTYPE_SOCKET:
055eee28 310 socktrans((struct socket *)g);
1a585c5c 311 break;
055eee28 312#ifdef DTYPE_PORT
1a585c5c 313 case DTYPE_PORT:
055eee28 314 printf("* (fifo / named pipe)\n");
1a585c5c
KB
315 break;
316#endif
317 default:
055eee28
KB
318 printf("* (unknown file type)\n");
319 }
320}
1a585c5c 321
f255214c
KB
322static char *
323itype(mode)
324 u_short mode;
5e369078 325{
f255214c
KB
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*/
5e369078 343}
055eee28 344
1a585c5c 345static
055eee28 346socktrans(sock)
1a585c5c 347 struct socket *sock;
055eee28 348{
f255214c 349 static char *stypename[] = {
1a585c5c
KB
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;
f255214c
KB
363 int len;
364 char dname[32], *strcpy();
055eee28
KB
365
366 /* fill in socket */
1a585c5c
KB
367 (void)lseek(kmem, (off_t)sock, L_SET);
368 if (read(kmem, (char *)&so, sizeof(struct socket))
369 != sizeof(struct socket)) {
5ebc207c 370 rerr2(errno, (int)sock, "socket");
055eee28
KB
371 return;
372 }
373
374 /* fill in protosw entry */
1a585c5c
KB
375 (void)lseek(kmem, (off_t)so.so_proto, L_SET);
376 if (read(kmem, (char *)&proto, sizeof(struct protosw))
377 != sizeof(struct protosw)) {
5ebc207c 378 rerr2(errno, (int)so.so_proto, "protosw");
055eee28
KB
379 return;
380 }
381
382 /* fill in domain */
1a585c5c
KB
383 (void)lseek(kmem, (off_t)proto.pr_domain, L_SET);
384 if (read(kmem, (char *)&dom, sizeof(struct domain))
385 != sizeof(struct domain)) {
5ebc207c 386 rerr2(errno, (int)proto.pr_domain, "domain");
055eee28
KB
387 return;
388 }
389
f255214c
KB
390 /*
391 * grab domain name
392 * kludge "internet" --> "inet" for brevity
393 */
1a585c5c
KB
394 if (dom.dom_family == AF_INET)
395 (void)strcpy(dname, "inet");
f255214c
KB
396 else {
397 (void)lseek(kmem, (off_t)dom.dom_name, L_SET);
398 if ((len = read(kmem, dname, sizeof(dname) - 1)) < 0) {
5ebc207c 399 rerr2(errno, (int)dom.dom_name, "char");
f255214c
KB
400 dname[0] = '\0';
401 }
402 else
403 dname[len] = '\0';
1a585c5c 404 }
055eee28 405
f255214c
KB
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);
055eee28
KB
411
412 /*
5ebc207c 413 * protocol specific formatting
055eee28 414 *
f255214c
KB
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.
055eee28 422 */
f255214c
KB
423 switch(dom.dom_family) {
424 case AF_INET:
425 getinetproto(proto.pr_protocol);
055eee28
KB
426 if (proto.pr_protocol == IPPROTO_TCP ) {
427 if (so.so_pcb) {
1a585c5c
KB
428 (void)lseek(kmem, (off_t)so.so_pcb, L_SET);
429 if (read(kmem, (char *)&inpcb, sizeof(struct inpcb))
055eee28 430 != sizeof(struct inpcb)){
5ebc207c 431 rerr2(errno, (int)so.so_pcb, "inpcb");
055eee28
KB
432 return;
433 }
1a585c5c 434 printf(" %x", (int)inpcb.inp_ppcb);
055eee28 435 }
055eee28 436 }
1a585c5c
KB
437 else if (so.so_pcb)
438 printf(" %x", (int)so.so_pcb);
f255214c
KB
439 break;
440 case AF_UNIX:
055eee28
KB
441 /* print address of pcb and connected pcb */
442 if (so.so_pcb) {
1a585c5c
KB
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))
055eee28 446 != sizeof(struct unpcb)){
5ebc207c 447 rerr2(errno, (int)so.so_pcb, "unpcb");
055eee28
KB
448 return;
449 }
5e369078 450 if (unpcb.unp_conn) {
f255214c 451 char shoconn[4], *cp;
1a585c5c 452
f255214c 453 cp = shoconn;
5e369078 454 if (!(so.so_state & SS_CANTRCVMORE))
f255214c
KB
455 *cp++ = '<';
456 *cp++ = '-';
5e369078 457 if (!(so.so_state & SS_CANTSENDMORE))
f255214c
KB
458 *cp++ = '>';
459 *cp = '\0';
1a585c5c 460 printf(" %s %x", shoconn, (int)unpcb.unp_conn);
5e369078 461 }
055eee28 462 }
f255214c
KB
463 break;
464 default:
465 /* print protocol number and socket address */
1a585c5c 466 printf(" %d %x", proto.pr_protocol, (int)sock);
f255214c 467 }
055eee28
KB
468 printf(")\n");
469}
470
f255214c
KB
471/*
472 * getinetproto --
473 * print name of protocol number
474 */
475static
055eee28 476getinetproto(number)
1a585c5c 477 int number;
055eee28 478{
1a585c5c 479 char *cp;
9bd38ba8 480
055eee28 481 switch(number) {
f255214c
KB
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;
9bd38ba8 500 default:
f255214c
KB
501 printf(" %d", number);
502 return;
055eee28 503 }
f255214c 504 printf(" %s", cp);
055eee28
KB
505}
506
f255214c
KB
507static
508readf()
055eee28 509{
1a585c5c 510 struct file lfile;
f255214c 511 int i;
055eee28 512
1a585c5c 513 itrans(DTYPE_INODE, user.user.u_cdir, WD);
055eee28 514 for (i = 0; i < NOFILE; i++) {
1a585c5c 515 if (user.user.u_ofile[i] == 0)
055eee28 516 continue;
1a585c5c
KB
517 (void)lseek(kmem, (off_t)user.user.u_ofile[i], L_SET);
518 if (read(kmem, (char *)&lfile, sizeof(lfile))
519 != sizeof(lfile)) {
5ebc207c 520 rerr1("file", N_KMEM);
055eee28
KB
521 continue;
522 }
1a585c5c 523 itrans(lfile.f_type, (struct inode *)lfile.f_data, i);
055eee28
KB
524 }
525}
526
1a585c5c 527static
d81abd9a 528devmatch(idev, inum, name)
f255214c
KB
529 dev_t idev;
530 ino_t inum;
d81abd9a 531 char **name;
055eee28 532{
f255214c 533 register DEVS *d;
1a585c5c 534
f255214c 535 for (d = devs; d; d = d->next)
d81abd9a
MT
536 if (d->dev == idev && (d->inum == 0 || d->inum == inum)) {
537 *name = d->name;
f255214c 538 return(1);
d81abd9a 539 }
f255214c 540 return(0);
055eee28
KB
541}
542
1a585c5c 543static
055eee28 544getfname(filename)
1a585c5c 545 char *filename;
055eee28 546{
f255214c
KB
547 struct stat statbuf;
548 DEVS *cur;
549 char *malloc();
055eee28 550
1a585c5c
KB
551 if (stat(filename, &statbuf)) {
552 perror(filename);
6270ab99 553 return(0);
1a585c5c 554 }
f255214c
KB
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;
055eee28 561
f255214c 562 /* if file is block special, look for open files on it */
055eee28 563 if ((statbuf.st_mode & S_IFMT) != S_IFBLK) {
f255214c
KB
564 cur->inum = statbuf.st_ino;
565 cur->dev = statbuf.st_dev;
1a585c5c
KB
566 }
567 else {
f255214c
KB
568 cur->inum = 0;
569 cur->dev = statbuf.st_rdev;
055eee28 570 }
d81abd9a 571 cur->name = filename;
6270ab99 572 return(1);
f255214c
KB
573}
574
575static
576openfiles()
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
592static
5ebc207c 593rerr1(what, fromwhat)
f255214c
KB
594 char *what, *fromwhat;
595{
5ebc207c
KB
596 if (vflg)
597 printf("fstat: error reading %s from %s", what, fromwhat);
598}
599
600static
601rerr2(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);
f255214c
KB
607}
608
609static long
610lgetw(loc)
611 off_t loc;
612{
613 long word;
614
615 (void)lseek(kmem, (off_t)loc, L_SET);
5ebc207c
KB
616 if (read(kmem, (char *)&word, sizeof(word)) != sizeof(word))
617 rerr2(errno, (int)loc, "word");
f255214c
KB
618 return(word);
619}
620
621static
622usage()
623{
624 fputs("usage: fstat [-v] [-u user] [-p pid] [filename ...]\n", stderr);
625 exit(1);
055eee28 626}