xref: /original-bsd/usr.bin/fstat/fstat.c (revision d11ff5ba)
1 /*-
2  * Copyright (c) 1988 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 char copyright[] =
10 "@(#) Copyright (c) 1988 The Regents of the University of California.\n\
11  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)fstat.c	5.26 (Berkeley) 12/18/90";
16 #endif /* not lint */
17 
18 /*
19  *  fstat
20  */
21 #include <machine/pte.h>
22 
23 #include <sys/param.h>
24 #include <sys/user.h>
25 #include <sys/proc.h>
26 #include <sys/text.h>
27 #include <sys/stat.h>
28 #include <sys/time.h>
29 #include <sys/vnode.h>
30 #include <sys/socket.h>
31 #include <sys/socketvar.h>
32 #include <sys/domain.h>
33 #include <sys/protosw.h>
34 #include <sys/unpcb.h>
35 #include <sys/vmmac.h>
36 #define	KERNEL
37 #define NFS
38 #include <sys/file.h>
39 #include <sys/mount.h>
40 #include <ufs/quota.h>
41 #include <ufs/inode.h>
42 #include <nfs/nfsv2.h>
43 #include <nfs/nfs.h>
44 #include <nfs/nfsnode.h>
45 #undef KERNEL
46 
47 #include <net/route.h>
48 #include <netinet/in.h>
49 #include <netinet/in_systm.h>
50 #include <netinet/ip.h>
51 #include <netinet/in_pcb.h>
52 
53 #include <kvm.h>
54 #include <paths.h>
55 #include <ctype.h>
56 #include <nlist.h>
57 #include <pwd.h>
58 #include <string.h>
59 #include <stdio.h>
60 
61 #define	TEXT	-1
62 #define	CDIR	-2
63 #define	RDIR	-3
64 #define	TRACE	-4
65 
66 typedef struct devs {
67 	struct	devs *next;
68 	long	fsid;
69 	ino_t	ino;
70 	char	*name;
71 } DEVS;
72 DEVS *devs;
73 
74 struct  filestat {
75 	long	fsid;
76 	long	fileid;
77 	mode_t	mode;
78 	u_long	size;
79 	dev_t	rdev;
80 };
81 
82 #ifdef notdef
83 struct nlist nl[] = {
84 	{ "" },
85 };
86 #endif
87 
88 int 	fsflg,	/* show files on same filesystem as file(s) argument */
89 	pflg,	/* show files open by a particular pid */
90 	uflg;	/* show files open by a particular (effective) user */
91 int 	checkfile; /* true if restricting to particular files or filesystems */
92 int	nflg;	/* (numerical) display f.s. and rdev as dev_t */
93 int	vflg;	/* display errors in locating kernel data objects etc... */
94 
95 #define dprintf	if (vflg) fprintf
96 
97 extern int errno;
98 off_t lseek();
99 
100 main(argc, argv)
101 	int argc;
102 	char **argv;
103 {
104 	register struct passwd *passwd;
105 	int what = KINFO_PROC_ALL, arg = 0;
106 	struct passwd *getpwnam(), *getpwuid();
107 	struct proc *p;
108 	extern char *optarg;
109 	extern int optind;
110 	int ch;
111 	char *malloc();
112 
113 
114 	while ((ch = getopt(argc, argv, "p:u:fnv")) != EOF)
115 		switch((char)ch) {
116 		case 'p':
117 			if (pflg++)
118 				usage();
119 			if (!isdigit(*optarg)) {
120 				fputs("fstat: -p option requires a process id.\n", stderr);
121 				usage();
122 			}
123 			what = KINFO_PROC_PID;
124 			arg = atoi(optarg);
125 			break;
126 		case 'u':
127 			if (uflg++)
128 				usage();
129 			if (!(passwd = getpwnam(optarg))) {
130 				fprintf(stderr, "%s: unknown uid\n",
131 				    optarg);
132 				exit(1);
133 			}
134 			what = KINFO_PROC_UID;
135 			arg = passwd->pw_uid;
136 			break;
137 		case 'f':
138 			fsflg++;
139 			break;
140 		case 'n':
141 			nflg++;
142 			break;
143 		case 'v':
144 			vflg++;
145 			break;
146 		case '?':
147 		default:
148 			usage();
149 		}
150 
151 	if (*(argv += optind)) {
152 		for (; *argv; ++argv) {
153 			if (getfname(*argv))
154 				checkfile = 1;
155 		}
156 		if (!checkfile)	/* file(s) specified, but none accessable */
157 			exit(1);
158 	}
159 	if (fsflg && !checkfile) {
160 		/* -f with no files means use wd */
161 		if (getfname(".") == 0)
162 			exit(1);
163 		checkfile = 1;
164 	}
165 
166 	/* modify the following to make work on dead kernels */
167 	if (kvm_openfiles(NULL, NULL, NULL) == -1) {
168 		fprintf(stderr, "fstat: %s\n", kvm_geterr());
169 		exit(1);
170 	}
171 #ifdef notdef
172 	if (kvm_nlist(nl) != 0) {
173 		fprintf(stderr, "fstat: no namelist: %s\n", kvm_geterr());
174 		exit(1);
175 	}
176 #endif
177 	if (kvm_getprocs(what, arg) == -1) {
178 		fprintf(stderr, "fstat: %s\n", kvm_geterr());
179 		exit(1);
180 	}
181 	if (nflg)
182 fputs("USER     CMD        PID   FD  DEV    INUM       MODE SZ|DV", stdout);
183 	else
184 fputs("USER     CMD        PID   FD MOUNT      INUM MODE         SZ|DV", stdout);
185 	if (checkfile && fsflg == 0)
186 		fputs(" NAME\n", stdout);
187 	else
188 		putchar('\n');
189 
190 	while ((p = kvm_nextproc()) != NULL) {
191 		if (p->p_stat == SZOMB)
192 			continue;
193 		dofiles(p);
194 	}
195 	exit(0);
196 }
197 
198 char	*Uname, *Comm;
199 int	Pid;
200 
201 #define PREFIX(i) printf("%-8.8s %-8.8s %5d", Uname, Comm, Pid); \
202 	switch(i) { \
203 	case TEXT: \
204 		fputs(" text", stdout); \
205 		break; \
206 	case CDIR: \
207 		fputs("   wd", stdout); \
208 		break; \
209 	case RDIR: \
210 		fputs(" root", stdout); \
211 		break; \
212 	case TRACE: \
213 		fputs("   tr", stdout); \
214 		break; \
215 	default: \
216 		printf(" %4d", i); \
217 		break; \
218 	}
219 
220 /*
221  * print open files attributed to this process
222  */
223 dofiles(p)
224 	struct proc *p;
225 {
226 	int i;
227 	struct file file;
228 	struct user *up = kvm_getu(p);
229 	struct vnode *xvptr;
230 	extern char *user_from_uid();
231 
232 	Uname = user_from_uid(p->p_uid, 0);
233 	Pid = p->p_pid;
234 	Comm = p->p_comm;
235 
236 	if (up == NULL) {
237 		dprintf(stderr, "can't read u for pid %d\n", Pid);
238 		return;
239 	}
240 	/*
241 	 * root directory vnode, if one
242 	 */
243 	if (up->u_rdir)
244 		vtrans(up->u_rdir, RDIR);
245 	/*
246 	 * text vnode
247 	 */
248 	if (p->p_textp &&
249 	    kvm_read(&(p->p_textp->x_vptr), &xvptr,
250 	    sizeof (struct vnode *)) == sizeof (struct vnode *) &&
251 	    xvptr != NULL)
252 		vtrans(xvptr, TEXT);
253 	/*
254 	 * current working directory vnode
255 	 */
256 	vtrans(up->u_cdir, CDIR);
257 	/*
258 	 * ktrace vnode, if one
259 	 */
260 	if (p->p_tracep)
261 		vtrans(p->p_tracep, TRACE);
262 	/*
263 	 * open files
264 	 */
265 	for (i = 0; i <= up->u_lastfile; i++) {
266 		if (up->u_ofile[i] == 0)
267 			continue;
268 		if (kvm_read(up->u_ofile[i], &file, sizeof (struct file)) !=
269 		    sizeof (struct file)) {
270 			dprintf(stderr, "can't read file %d for pid %d\n",
271 				i, Pid);
272 			continue;
273 		}
274 		if (file.f_type == DTYPE_VNODE)
275 			vtrans((struct vnode *)file.f_data, i);
276 		else if (file.f_type == DTYPE_SOCKET && checkfile == 0)
277 			socktrans((struct socket *)file.f_data, i);
278 		else {
279 			dprintf(stderr,
280 				"unknown file type %d for file %d of pid %d\n",
281 				file.f_type, i, Pid);
282 		}
283 	}
284 }
285 
286 vtrans(vp, i)
287 	struct vnode *vp;
288 {
289 	struct vnode vn;
290 	struct filestat fst;
291 	char *filename = NULL;
292 	char *badtype = NULL;
293 	char *getmnton();
294 	extern char *devname();
295 	char mode[15];
296 
297 	if (kvm_read((off_t)vp, &vn, sizeof (struct vnode)) !=
298 	    sizeof (struct vnode)) {
299 		dprintf(stderr, "can't read vnode at %x for pid %d\n",
300 			vp, Pid);
301 		return;
302 	}
303 	if (vn.v_type == VNON || vn.v_tag == VT_NON)
304 		badtype = "none";
305 	else if (vn.v_type == VBAD)
306 		badtype = "bad";
307 	else
308 		switch (vn.v_tag) {
309 		case VT_UFS:
310 			ufs_filestat(&vn, &fst);
311 			break;
312 		case VT_MFS:
313 			ufs_filestat(&vn, &fst);
314 			break;
315 		case VT_NFS:
316 			nfs_filestat(&vn, &fst);
317 			break;
318 		default: {
319 			static char unknown[10];
320 			sprintf(badtype = unknown, "?(%x)", vn.v_tag);
321 			break;;
322 		}
323 	}
324 	if (checkfile) {
325 		int fsmatch = 0;
326 		register DEVS *d;
327 
328 		if (badtype)
329 			return;
330 		for (d = devs; d != NULL; d = d->next)
331 			if (d->fsid == fst.fsid) {
332 				fsmatch = 1;
333 				if (d->ino == fst.fileid) {
334 					filename = d->name;
335 					break;
336 				}
337 			}
338 		if (fsmatch == 0 || (filename == NULL && fsflg == 0))
339 			return;
340 	}
341 	PREFIX(i);
342 	if (badtype) {
343 		(void)printf(" -         -  %10s    -\n", badtype);
344 		return;
345 	}
346 	if (nflg)
347 		(void)printf(" %2d,%-2d", major(fst.fsid), minor(fst.fsid));
348 	else
349 		(void)printf(" %-8s", getmnton(vn.v_mount));
350 	if (nflg)
351 		(void)sprintf(mode, "%o", fst.mode);
352 	else
353 		strmode(fst.mode, mode);
354 	(void)printf(" %6d %10s", fst.fileid, mode);
355 	switch (vn.v_type) {
356 	case VBLK:
357 	case VCHR: {
358 		char *name;
359 
360 		if (nflg || ((name = devname(fst.rdev, vn.v_type == VCHR ?
361 		    S_IFCHR : S_IFBLK)) == NULL))
362 			printf("  %2d,%-2d", major(fst.rdev), minor(fst.rdev));
363 		else
364 			printf(" %6s", name);
365 		break;
366 	}
367 	default:
368 		printf(" %6d", fst.size);
369 	}
370 	if (filename && !fsflg)
371 		printf(" %s", filename);
372 
373 	putchar('\n');
374 }
375 
376 ufs_filestat(vp, fsp)
377 	struct vnode *vp;
378 	struct filestat *fsp;
379 {
380 	struct inode *ip = VTOI(vp);
381 
382 	fsp->fsid = ip->i_dev & 0xffff;
383 	fsp->fileid = (long)ip->i_number;
384 	fsp->mode = (mode_t)ip->i_mode;
385 	fsp->size = (u_long)ip->i_size;
386 	fsp->rdev = ip->i_rdev;
387 }
388 
389 nfs_filestat(vp, fsp)
390 	struct vnode *vp;
391 	struct filestat *fsp;
392 {
393 	register struct nfsnode *np = VTONFS(vp);
394 	register mode_t mode;
395 
396 	fsp->fsid = np->n_vattr.va_fsid;
397 	fsp->fileid = np->n_vattr.va_fileid;
398 	fsp->size = np->n_size;
399 	fsp->rdev = np->n_vattr.va_rdev;
400 	mode = (mode_t)np->n_vattr.va_mode;
401 	switch (vp->v_type) {
402 	case VREG:
403 		mode |= S_IFREG;
404 		break;
405 	case VDIR:
406 		mode |= S_IFDIR;
407 		break;
408 	case VBLK:
409 		mode |= S_IFBLK;
410 		break;
411 	case VCHR:
412 		mode |= S_IFCHR;
413 		break;
414 	case VLNK:
415 		mode |= S_IFLNK;
416 		break;
417 	case VSOCK:
418 		mode |= S_IFSOCK;
419 		break;
420 	case VFIFO:
421 		mode |= S_IFIFO;
422 		break;
423 	};
424 	fsp->mode = mode;
425 }
426 
427 
428 char *
429 getmnton(m)
430 	struct mount *m;
431 {
432 	static struct mount mount;
433 	static struct mtab {
434 		struct mtab *next;
435 		struct mount *m;
436 		char mntonname[MNAMELEN];
437 	} *mhead = NULL;
438 	register struct mtab *mt;
439 
440 	for (mt = mhead; mt != NULL; mt = mt->next)
441 		if (m == mt->m)
442 			return (mt->mntonname);
443 	if (kvm_read((off_t)m, &mount, sizeof(struct mount)) !=
444 	    sizeof(struct mount)) {
445 		fprintf(stderr, "can't read mount table at %x\n", m);
446 		return (NULL);
447 	}
448 	if ((mt = (struct mtab *)malloc(sizeof (struct mtab))) == NULL) {
449 		fprintf(stderr, "out of memory\n");
450 		exit(1);
451 	}
452 	mt->m = m;
453 	bcopy(&mount.mnt_stat.f_mntonname[0], &mt->mntonname[0], MNAMELEN);
454 	mt->next = mhead;
455 	mhead = mt;
456 	return (mt->mntonname);
457 }
458 
459 socktrans(sock, i)
460 	struct socket *sock;
461 {
462 	static char *stypename[] = {
463 		"unused",	/* 0 */
464 		"stream", 	/* 1 */
465 		"dgram",	/* 2 */
466 		"raw",		/* 3 */
467 		"rdm",		/* 4 */
468 		"seqpak"	/* 5 */
469 	};
470 #define	STYPEMAX 5
471 	struct socket	so;
472 	struct protosw	proto;
473 	struct domain	dom;
474 	struct inpcb	inpcb;
475 	struct unpcb	unpcb;
476 	int len;
477 	char dname[32], *strcpy();
478 
479 	PREFIX(i);
480 
481 	/* fill in socket */
482 	if (kvm_read((off_t)sock, (char *)&so, sizeof(struct socket))
483 	    != sizeof(struct socket)) {
484 		dprintf(stderr, "can't read sock at %x\n", sock);
485 		goto bad;
486 	}
487 
488 	/* fill in protosw entry */
489 	if (kvm_read((off_t)so.so_proto, (char *)&proto, sizeof(struct protosw))
490 	    != sizeof(struct protosw)) {
491 		dprintf(stderr, "can't read protosw at %x", so.so_proto);
492 		goto bad;
493 	}
494 
495 	/* fill in domain */
496 	if (kvm_read((off_t)proto.pr_domain, (char *)&dom, sizeof(struct domain))
497 	    != sizeof(struct domain)) {
498 		dprintf(stderr, "can't read domain at %x\n", proto.pr_domain);
499 		goto bad;
500 	}
501 
502 	/*
503 	 * grab domain name
504 	 * kludge "internet" --> "inet" for brevity
505 	 */
506 	if (dom.dom_family == AF_INET)
507 		strcpy(dname, "inet");
508 	else {
509 		if ((len = kvm_read((off_t)dom.dom_name, dname, sizeof(dname) - 1)) < 0) {
510 			dprintf(stderr, "can't read domain name at %x\n",
511 				dom.dom_name);
512 			dname[0] = '\0';
513 		}
514 		else
515 			dname[len] = '\0';
516 	}
517 
518 	if ((u_short)so.so_type > STYPEMAX)
519 		printf("* %s ?%d", dname, so.so_type);
520 	else
521 		printf("* %s %s", dname, stypename[so.so_type]);
522 
523 	/*
524 	 * protocol specific formatting
525 	 *
526 	 * Try to find interesting things to print.  For tcp, the interesting
527 	 * thing is the address of the tcpcb, for udp and others, just the
528 	 * inpcb (socket pcb).  For unix domain, its the address of the socket
529 	 * pcb and the address of the connected pcb (if connected).  Otherwise
530 	 * just print the protocol number and address of the socket itself.
531 	 * The idea is not to duplicate netstat, but to make available enough
532 	 * information for further analysis.
533 	 */
534 	switch(dom.dom_family) {
535 	case AF_INET:
536 		getinetproto(proto.pr_protocol);
537 		if (proto.pr_protocol == IPPROTO_TCP ) {
538 			if (so.so_pcb) {
539 				if (kvm_read((off_t)so.so_pcb, (char *)&inpcb, sizeof(struct inpcb))
540 				    != sizeof(struct inpcb)){
541 					dprintf(stderr,
542 					     "can't read inpcb at %x\n", so.so_pcb);
543 					goto bad;
544 				}
545 				printf(" %x", (int)inpcb.inp_ppcb);
546 			}
547 		}
548 		else if (so.so_pcb)
549 			printf(" %x", (int)so.so_pcb);
550 		break;
551 	case AF_UNIX:
552 		/* print address of pcb and connected pcb */
553 		if (so.so_pcb) {
554 			printf(" %x", (int)so.so_pcb);
555 			if (kvm_read((off_t)so.so_pcb, (char *)&unpcb, sizeof(struct unpcb))
556 			    != sizeof(struct unpcb)){
557 				dprintf(stderr, "can't read unpcb at %x\n",
558 					so.so_pcb);
559 				goto bad;
560 			}
561 			if (unpcb.unp_conn) {
562 				char shoconn[4], *cp;
563 
564 				cp = shoconn;
565 				if (!(so.so_state & SS_CANTRCVMORE))
566 					*cp++ = '<';
567 				*cp++ = '-';
568 				if (!(so.so_state & SS_CANTSENDMORE))
569 					*cp++ = '>';
570 				*cp = '\0';
571 				printf(" %s %x", shoconn,
572 				    (int)unpcb.unp_conn);
573 			}
574 		}
575 		break;
576 	default:
577 		/* print protocol number and socket address */
578 		printf(" %d %x", proto.pr_protocol, (int)sock);
579 	}
580 	printf("\n");
581 	return;
582 bad:
583 	printf("* error\n");
584 }
585 
586 /*
587  * getinetproto --
588  *	print name of protocol number
589  */
590 getinetproto(number)
591 	int number;
592 {
593 	char *cp;
594 
595 	switch(number) {
596 	case IPPROTO_IP:
597 		cp = "ip"; break;
598 	case IPPROTO_ICMP:
599 		cp ="icmp"; break;
600 	case IPPROTO_GGP:
601 		cp ="ggp"; break;
602 	case IPPROTO_TCP:
603 		cp ="tcp"; break;
604 	case IPPROTO_EGP:
605 		cp ="egp"; break;
606 	case IPPROTO_PUP:
607 		cp ="pup"; break;
608 	case IPPROTO_UDP:
609 		cp ="udp"; break;
610 	case IPPROTO_IDP:
611 		cp ="idp"; break;
612 	case IPPROTO_RAW:
613 		cp ="raw"; break;
614 	default:
615 		printf(" %d", number);
616 		return;
617 	}
618 	printf(" %s", cp);
619 }
620 
621 getfname(filename)
622 	char *filename;
623 {
624 	struct stat statbuf;
625 	DEVS *cur;
626 	char *malloc();
627 
628 	if (stat(filename, &statbuf)) {
629 		fprintf(stderr, "fstat: %s: %s\n", strerror(errno),
630 		    filename);
631 		return(0);
632 	}
633 	if ((cur = (DEVS *)malloc(sizeof(DEVS))) == NULL) {
634 		fprintf(stderr, "fstat: out of space.\n");
635 		exit(1);
636 	}
637 	cur->next = devs;
638 	devs = cur;
639 
640 	cur->ino = statbuf.st_ino;
641 	cur->fsid = statbuf.st_dev & 0xffff;
642 	cur->name = filename;
643 	return(1);
644 }
645 
646 usage()
647 {
648 	(void)fprintf(stderr,
649 	    "usage: fstat [-fnv] [-p pid] [-u user] [filename ...]\n");
650 	exit(1);
651 }
652