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