386BSD 0.1 development
[unix-history] / usr / src / usr.bin / fstat / fstat.c
CommitLineData
2d4e31cc
WJ
1/*-
2 * Copyright (c) 1988 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35char copyright[] =
36"@(#) Copyright (c) 1988 The Regents of the University of California.\n\
37 All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41static char sccsid[] = "@(#)fstat.c 5.32 (Berkeley) 6/17/91";
42#endif /* not lint */
43
44/*
45 * fstat
46 */
47#include <sys/param.h>
48#include <sys/time.h>
49#include <sys/proc.h>
50#include <sys/user.h>
51#ifdef SPPWAIT
52#define NEWVM
53#endif
54#ifndef NEWVM
55#include <machine/pte.h>
56#include <sys/vmmac.h>
57#endif
58#include <sys/stat.h>
59#include <sys/vnode.h>
60#include <sys/socket.h>
61#include <sys/socketvar.h>
62#include <sys/domain.h>
63#include <sys/protosw.h>
64#include <sys/unpcb.h>
65#include <sys/kinfo.h>
66#include <sys/filedesc.h>
67#define KERNEL
68#define NFS
69#include <sys/file.h>
70#include <sys/mount.h>
71#include <ufs/quota.h>
72#include <ufs/inode.h>
73#include <nfs/nfsv2.h>
74#include <nfs/nfs.h>
75#include <nfs/nfsnode.h>
76#undef KERNEL
77
78#include <net/route.h>
79#include <netinet/in.h>
80#include <netinet/in_systm.h>
81#include <netinet/ip.h>
82#include <netinet/in_pcb.h>
83
84#include <errno.h>
85#include <nlist.h>
86#include <kvm.h>
87#include <pwd.h>
88#include <stdio.h>
89#include <paths.h>
90#include <ctype.h>
91#include <stdlib.h>
92#include <string.h>
93
94#define TEXT -1
95#define CDIR -2
96#define RDIR -3
97#define TRACE -4
98
99typedef struct devs {
100 struct devs *next;
101 long fsid;
102 ino_t ino;
103 char *name;
104} DEVS;
105DEVS *devs;
106
107struct filestat {
108 long fsid;
109 long fileid;
110 mode_t mode;
111 u_long size;
112 dev_t rdev;
113};
114
115#ifdef notdef
116struct nlist nl[] = {
117 { "" },
118};
119#endif
120
121int fsflg, /* show files on same filesystem as file(s) argument */
122 pflg, /* show files open by a particular pid */
123 uflg; /* show files open by a particular (effective) user */
124int checkfile; /* true if restricting to particular files or filesystems */
125int nflg; /* (numerical) display f.s. and rdev as dev_t */
126int vflg; /* display errors in locating kernel data objects etc... */
127
128#define dprintf if (vflg) fprintf
129
130struct file **ofiles; /* buffer of pointers to file structures */
131int maxfiles;
132#define ALLOC_OFILES(d) \
133 if ((d) > maxfiles) { \
134 free(ofiles); \
135 ofiles = malloc((d) * sizeof(struct file *)); \
136 if (ofiles == NULL) { \
137 fprintf(stderr, "fstat: %s\n", strerror(errno)); \
138 exit(1); \
139 } \
140 maxfiles = (d); \
141 }
142
143/*
144 * a kvm_read that returns true if everything is read
145 */
146#define KVM_READ(kaddr, paddr, len) (kvm_read((kaddr), (paddr), (len)) == (len))
147
148void dofiles(), getinetproto(), socktrans(), nfs_filestat(), ufs_filestat();
149void usage(), vtrans();
150
151main(argc, argv)
152 int argc;
153 char **argv;
154{
155 extern char *optarg;
156 extern int optind;
157 register struct passwd *passwd;
158 struct proc *p;
159 int arg, ch, what;
160 char *namelist = NULL, *memfile = NULL;
161
162 arg = 0;
163 what = KINFO_PROC_ALL;
164 while ((ch = getopt(argc, argv, "fnp:u:vNM")) != EOF)
165 switch((char)ch) {
166 case 'f':
167 fsflg = 1;
168 break;
169 case 'n':
170 nflg = 1;
171 break;
172 case 'p':
173 if (pflg++)
174 usage();
175 if (!isdigit(*optarg)) {
176 fprintf(stderr,
177 "fstat: -p requires a process id\n");
178 usage();
179 }
180 what = KINFO_PROC_PID;
181 arg = atoi(optarg);
182 break;
183 case 'u':
184 if (uflg++)
185 usage();
186 if (!(passwd = getpwnam(optarg))) {
187 fprintf(stderr, "%s: unknown uid\n",
188 optarg);
189 exit(1);
190 }
191 what = KINFO_PROC_UID;
192 arg = passwd->pw_uid;
193 break;
194 case 'v':
195 vflg = 1;
196 break;
197 case 'N':
198 namelist = optarg;
199 break;
200 case 'M':
201 memfile = optarg;
202 break;
203 case '?':
204 default:
205 usage();
206 }
207
208 if (*(argv += optind)) {
209 for (; *argv; ++argv) {
210 if (getfname(*argv))
211 checkfile = 1;
212 }
213 if (!checkfile) /* file(s) specified, but none accessable */
214 exit(1);
215 }
216
217 ALLOC_OFILES(256); /* reserve space for file pointers */
218
219 if (fsflg && !checkfile) {
220 /* -f with no files means use wd */
221 if (getfname(".") == 0)
222 exit(1);
223 checkfile = 1;
224 }
225
226 if (kvm_openfiles(namelist, memfile, NULL) == -1) {
227 fprintf(stderr, "fstat: %s\n", kvm_geterr());
228 exit(1);
229 }
230#ifdef notdef
231 if (kvm_nlist(nl) != 0) {
232 fprintf(stderr, "fstat: no namelist: %s\n", kvm_geterr());
233 exit(1);
234 }
235#endif
236 if (kvm_getprocs(what, arg) == -1) {
237 fprintf(stderr, "fstat: %s\n", kvm_geterr());
238 exit(1);
239 }
240 if (nflg)
241 printf("%s",
242"USER CMD PID FD DEV INUM MODE SZ|DV");
243 else
244 printf("%s",
245"USER CMD PID FD MOUNT INUM MODE SZ|DV");
246 if (checkfile && fsflg == 0)
247 printf(" NAME\n");
248 else
249 putchar('\n');
250
251 while ((p = kvm_nextproc()) != NULL) {
252 if (p->p_stat == SZOMB)
253 continue;
254 dofiles(p);
255 }
256 exit(0);
257}
258
259char *Uname, *Comm;
260int Pid;
261
262#define PREFIX(i) printf("%-8.8s %-10s %5d", Uname, Comm, Pid); \
263 switch(i) { \
264 case TEXT: \
265 printf(" text"); \
266 break; \
267 case CDIR: \
268 printf(" wd"); \
269 break; \
270 case RDIR: \
271 printf(" root"); \
272 break; \
273 case TRACE: \
274 printf(" tr"); \
275 break; \
276 default: \
277 printf(" %4d", i); \
278 break; \
279 }
280
281/*
282 * print open files attributed to this process
283 */
284void
285dofiles(p)
286 struct proc *p;
287{
288 int i, last;
289 struct file file;
290#ifdef NEWVM
291 struct filedesc0 filed0;
292#define filed filed0.fd_fd
293 struct eproc *ep;
294#else
295 struct filedesc filed;
296#endif
297
298 extern char *user_from_uid();
299#ifndef NEWVM
300 struct vnode *xvptr;
301#endif
302
303#ifdef NEWVM
304 ep = kvm_geteproc(p);
305 Uname = user_from_uid(ep->e_ucred.cr_uid, 0);
306#else
307 Uname = user_from_uid(p->p_uid, 0);
308#endif
309 Pid = p->p_pid;
310 Comm = p->p_comm;
311
312 if (p->p_fd == NULL)
313 return;
314#ifdef NEWVM
315 if (!KVM_READ(p->p_fd, &filed0, sizeof (filed0))) {
316 dprintf(stderr, "can't read filedesc at %x for pid %d\n",
317 p->p_fd, Pid);
318 return;
319 }
320#else
321 if (!KVM_READ(p->p_fd, &filed, sizeof (filed))) {
322 dprintf(stderr, "can't read filedesc at %x for pid %d\n",
323 p->p_fd, Pid);
324 return;
325 }
326#endif
327 /*
328 * root directory vnode, if one
329 */
330 if (filed.fd_rdir)
331 vtrans(filed.fd_rdir, RDIR);
332#ifndef NEWVM
333 /*
334 * text vnode
335 */
336 if (p->p_textp &&
337 KVM_READ(&(p->p_textp->x_vptr), &xvptr, sizeof (struct vnode *)) &&
338 xvptr != NULL)
339 vtrans(xvptr, TEXT);
340#endif
341 /*
342 * current working directory vnode
343 */
344 vtrans(filed.fd_cdir, CDIR);
345 /*
346 * ktrace vnode, if one
347 */
348 if (p->p_tracep)
349 vtrans(p->p_tracep, TRACE);
350 /*
351 * open files
352 */
353#define FPSIZE (sizeof (struct file *))
354 ALLOC_OFILES(filed.fd_lastfile);
355#ifdef NEWVM
356 if (filed.fd_nfiles > NDFILE) {
357 if (!KVM_READ(filed.fd_ofiles, ofiles,
358 filed.fd_lastfile * FPSIZE)) {
359 dprintf(stderr,
360 "can't read file structures at %x for pid %d\n",
361 filed.fd_ofiles, Pid);
362 return;
363 }
364 } else
365 bcopy(filed0.fd_dfiles, ofiles, filed.fd_lastfile * FPSIZE);
366#else
367 bcopy(filed.fd_ofile, ofiles, MIN(filed.fd_lastfile, NDFILE) * FPSIZE);
368 last = filed.fd_lastfile;
369 if ((last > NDFILE) && !KVM_READ(filed.fd_moreofiles, &ofiles[NDFILE],
370 (last - NDFILE) * FPSIZE)) {
371 dprintf(stderr, "can't read rest of files at %x for pid %d\n",
372 filed.fd_moreofiles, Pid);
373 return;
374 }
375#endif
376 for (i = 0; i <= filed.fd_lastfile; i++) {
377 if (ofiles[i] == NULL)
378 continue;
379 if (!KVM_READ(ofiles[i], &file, sizeof (struct file))) {
380 dprintf(stderr, "can't read file %d at %x for pid %d\n",
381 i, ofiles[i], Pid);
382 continue;
383 }
384 if (file.f_type == DTYPE_VNODE)
385 vtrans((struct vnode *)file.f_data, i);
386 else if (file.f_type == DTYPE_SOCKET && checkfile == 0)
387 socktrans((struct socket *)file.f_data, i);
388 else {
389 dprintf(stderr,
390 "unknown file type %d for file %d of pid %d\n",
391 file.f_type, i, Pid);
392 }
393 }
394}
395
396void
397vtrans(vp, i)
398 struct vnode *vp;
399 int i;
400{
401 extern char *devname();
402 struct vnode vn;
403 struct filestat fst;
404 char mode[15];
405 char *badtype, *filename, *getmnton();
406
407 filename = badtype = NULL;
408 if (!KVM_READ(vp, &vn, sizeof (struct vnode))) {
409 dprintf(stderr, "can't read vnode at %x for pid %d\n",
410 vp, Pid);
411 return;
412 }
413 if (vn.v_type == VNON || vn.v_tag == VT_NON)
414 badtype = "none";
415 else if (vn.v_type == VBAD)
416 badtype = "bad";
417 else
418 switch (vn.v_tag) {
419 case VT_UFS:
420 ufs_filestat(&vn, &fst);
421 break;
422 case VT_MFS:
423 ufs_filestat(&vn, &fst);
424 break;
425 case VT_NFS:
426 nfs_filestat(&vn, &fst);
427 break;
428 default: {
429 static char unknown[10];
430 sprintf(badtype = unknown, "?(%x)", vn.v_tag);
431 break;;
432 }
433 }
434 if (checkfile) {
435 int fsmatch = 0;
436 register DEVS *d;
437
438 if (badtype)
439 return;
440 for (d = devs; d != NULL; d = d->next)
441 if (d->fsid == fst.fsid) {
442 fsmatch = 1;
443 if (d->ino == fst.fileid) {
444 filename = d->name;
445 break;
446 }
447 }
448 if (fsmatch == 0 || (filename == NULL && fsflg == 0))
449 return;
450 }
451 PREFIX(i);
452 if (badtype) {
453 (void)printf(" - - %10s -\n", badtype);
454 return;
455 }
456 if (nflg)
457 (void)printf(" %2d,%-2d", major(fst.fsid), minor(fst.fsid));
458 else
459 (void)printf(" %-8s", getmnton(vn.v_mount));
460 if (nflg)
461 (void)sprintf(mode, "%o", fst.mode);
462 else
463 strmode(fst.mode, mode);
464 (void)printf(" %6d %10s", fst.fileid, mode);
465 switch (vn.v_type) {
466 case VBLK:
467 case VCHR: {
468 char *name;
469
470 if (nflg || ((name = devname(fst.rdev, vn.v_type == VCHR ?
471 S_IFCHR : S_IFBLK)) == NULL))
472 printf(" %2d,%-2d", major(fst.rdev), minor(fst.rdev));
473 else
474 printf(" %6s", name);
475 break;
476 }
477 default:
478 printf(" %6d", fst.size);
479 }
480 if (filename && !fsflg)
481 printf(" %s", filename);
482
483 putchar('\n');
484}
485
486void
487ufs_filestat(vp, fsp)
488 struct vnode *vp;
489 struct filestat *fsp;
490{
491 struct inode *ip = VTOI(vp);
492
493 fsp->fsid = ip->i_dev & 0xffff;
494 fsp->fileid = (long)ip->i_number;
495 fsp->mode = (mode_t)ip->i_mode;
496 fsp->size = (u_long)ip->i_size;
497 fsp->rdev = ip->i_rdev;
498}
499
500void
501nfs_filestat(vp, fsp)
502 struct vnode *vp;
503 struct filestat *fsp;
504{
505 register struct nfsnode *np = VTONFS(vp);
506 register mode_t mode;
507
508 fsp->fsid = np->n_vattr.va_fsid;
509 fsp->fileid = np->n_vattr.va_fileid;
510 fsp->size = np->n_size;
511 fsp->rdev = np->n_vattr.va_rdev;
512 mode = (mode_t)np->n_vattr.va_mode;
513 switch (vp->v_type) {
514 case VREG:
515 mode |= S_IFREG;
516 break;
517 case VDIR:
518 mode |= S_IFDIR;
519 break;
520 case VBLK:
521 mode |= S_IFBLK;
522 break;
523 case VCHR:
524 mode |= S_IFCHR;
525 break;
526 case VLNK:
527 mode |= S_IFLNK;
528 break;
529 case VSOCK:
530 mode |= S_IFSOCK;
531 break;
532 case VFIFO:
533 mode |= S_IFIFO;
534 break;
535 };
536 fsp->mode = mode;
537}
538
539
540char *
541getmnton(m)
542 struct mount *m;
543{
544 static struct mount mount;
545 static struct mtab {
546 struct mtab *next;
547 struct mount *m;
548 char mntonname[MNAMELEN];
549 } *mhead = NULL;
550 register struct mtab *mt;
551
552 for (mt = mhead; mt != NULL; mt = mt->next)
553 if (m == mt->m)
554 return (mt->mntonname);
555 if (!KVM_READ(m, &mount, sizeof(struct mount))) {
556 fprintf(stderr, "can't read mount table at %x\n", m);
557 return (NULL);
558 }
559 if ((mt = malloc(sizeof (struct mtab))) == NULL) {
560 fprintf(stderr, "fstat: %s\n", strerror(errno));
561 exit(1);
562 }
563 mt->m = m;
564 bcopy(&mount.mnt_stat.f_mntonname[0], &mt->mntonname[0], MNAMELEN);
565 mt->next = mhead;
566 mhead = mt;
567 return (mt->mntonname);
568}
569
570void
571socktrans(sock, i)
572 struct socket *sock;
573 int i;
574{
575 static char *stypename[] = {
576 "unused", /* 0 */
577 "stream", /* 1 */
578 "dgram", /* 2 */
579 "raw", /* 3 */
580 "rdm", /* 4 */
581 "seqpak" /* 5 */
582 };
583#define STYPEMAX 5
584 struct socket so;
585 struct protosw proto;
586 struct domain dom;
587 struct inpcb inpcb;
588 struct unpcb unpcb;
589 int len;
590 char dname[32], *strcpy();
591
592 PREFIX(i);
593
594 /* fill in socket */
595 if (!KVM_READ(sock, &so, sizeof(struct socket))) {
596 dprintf(stderr, "can't read sock at %x\n", sock);
597 goto bad;
598 }
599
600 /* fill in protosw entry */
601 if (!KVM_READ(so.so_proto, &proto, sizeof(struct protosw))) {
602 dprintf(stderr, "can't read protosw at %x", so.so_proto);
603 goto bad;
604 }
605
606 /* fill in domain */
607 if (!KVM_READ(proto.pr_domain, &dom, sizeof(struct domain))) {
608 dprintf(stderr, "can't read domain at %x\n", proto.pr_domain);
609 goto bad;
610 }
611
612 if ((len =
613 kvm_read(dom.dom_name, dname, sizeof(dname) - 1)) < 0) {
614 dprintf(stderr, "can't read domain name at %x\n",
615 dom.dom_name);
616 dname[0] = '\0';
617 }
618 else
619 dname[len] = '\0';
620
621 if ((u_short)so.so_type > STYPEMAX)
622 printf("* %s ?%d", dname, so.so_type);
623 else
624 printf("* %s %s", dname, stypename[so.so_type]);
625
626 /*
627 * protocol specific formatting
628 *
629 * Try to find interesting things to print. For tcp, the interesting
630 * thing is the address of the tcpcb, for udp and others, just the
631 * inpcb (socket pcb). For unix domain, its the address of the socket
632 * pcb and the address of the connected pcb (if connected). Otherwise
633 * just print the protocol number and address of the socket itself.
634 * The idea is not to duplicate netstat, but to make available enough
635 * information for further analysis.
636 */
637 switch(dom.dom_family) {
638 case AF_INET:
639 getinetproto(proto.pr_protocol);
640 if (proto.pr_protocol == IPPROTO_TCP ) {
641 if (so.so_pcb) {
642 if (kvm_read(so.so_pcb, &inpcb,
643 sizeof(struct inpcb))
644 != sizeof(struct inpcb)) {
645 dprintf(stderr,
646 "can't read inpcb at %x\n",
647 so.so_pcb);
648 goto bad;
649 }
650 printf(" %x", (int)inpcb.inp_ppcb);
651 }
652 }
653 else if (so.so_pcb)
654 printf(" %x", (int)so.so_pcb);
655 break;
656 case AF_UNIX:
657 /* print address of pcb and connected pcb */
658 if (so.so_pcb) {
659 printf(" %x", (int)so.so_pcb);
660 if (kvm_read(so.so_pcb, &unpcb,
661 sizeof(struct unpcb)) != sizeof(struct unpcb)){
662 dprintf(stderr, "can't read unpcb at %x\n",
663 so.so_pcb);
664 goto bad;
665 }
666 if (unpcb.unp_conn) {
667 char shoconn[4], *cp;
668
669 cp = shoconn;
670 if (!(so.so_state & SS_CANTRCVMORE))
671 *cp++ = '<';
672 *cp++ = '-';
673 if (!(so.so_state & SS_CANTSENDMORE))
674 *cp++ = '>';
675 *cp = '\0';
676 printf(" %s %x", shoconn,
677 (int)unpcb.unp_conn);
678 }
679 }
680 break;
681 default:
682 /* print protocol number and socket address */
683 printf(" %d %x", proto.pr_protocol, (int)sock);
684 }
685 printf("\n");
686 return;
687bad:
688 printf("* error\n");
689}
690
691/*
692 * getinetproto --
693 * print name of protocol number
694 */
695void
696getinetproto(number)
697 int number;
698{
699 char *cp;
700
701 switch(number) {
702 case IPPROTO_IP:
703 cp = "ip"; break;
704 case IPPROTO_ICMP:
705 cp ="icmp"; break;
706 case IPPROTO_GGP:
707 cp ="ggp"; break;
708 case IPPROTO_TCP:
709 cp ="tcp"; break;
710 case IPPROTO_EGP:
711 cp ="egp"; break;
712 case IPPROTO_PUP:
713 cp ="pup"; break;
714 case IPPROTO_UDP:
715 cp ="udp"; break;
716 case IPPROTO_IDP:
717 cp ="idp"; break;
718 case IPPROTO_RAW:
719 cp ="raw"; break;
720 default:
721 printf(" %d", number);
722 return;
723 }
724 printf(" %s", cp);
725}
726
727getfname(filename)
728 char *filename;
729{
730 struct stat statbuf;
731 DEVS *cur;
732
733 if (stat(filename, &statbuf)) {
734 fprintf(stderr, "fstat: %s: %s\n", strerror(errno),
735 filename);
736 return(0);
737 }
738 if ((cur = malloc(sizeof(DEVS))) == NULL) {
739 fprintf(stderr, "fstat: %s\n", strerror(errno));
740 exit(1);
741 }
742 cur->next = devs;
743 devs = cur;
744
745 cur->ino = statbuf.st_ino;
746 cur->fsid = statbuf.st_dev & 0xffff;
747 cur->name = filename;
748 return(1);
749}
750
751void
752usage()
753{
754 (void)fprintf(stderr,
755 "usage: fstat [-fnv] [-p pid] [-u user] [-N system] [-M core] [file ...]\n");
756 exit(1);
757}