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