xref: /original-bsd/usr.bin/fstat/fstat.c (revision cd18b70b)
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