need to change iphlen earlier; icmp_error needs original ip_len; cleanups
[unix-history] / usr / src / usr.bin / fstat / fstat.c
CommitLineData
7f9552fe
MT
1/*-
2 * Copyright (c) 1988 The Regents of the University of California.
5ebc207c
KB
3 * All rights reserved.
4 *
7f9552fe 5 * %sccs.include.redist.c%
055eee28
KB
6 */
7
8#ifndef lint
9char copyright[] =
7f9552fe 10"@(#) Copyright (c) 1988 The Regents of the University of California.\n\
055eee28 11 All rights reserved.\n";
5ebc207c 12#endif /* not lint */
055eee28
KB
13
14#ifndef lint
7f9552fe 15static char sccsid[] = "@(#)fstat.c 5.25 (Berkeley) %G%";
5ebc207c 16#endif /* not lint */
055eee28
KB
17
18/*
19 * fstat
20 */
f255214c 21#include <machine/pte.h>
1a585c5c 22
055eee28 23#include <sys/param.h>
055eee28
KB
24#include <sys/user.h>
25#include <sys/proc.h>
055eee28
KB
26#include <sys/text.h>
27#include <sys/stat.h>
3dae3806
KM
28#include <sys/time.h>
29#include <sys/vnode.h>
055eee28
KB
30#include <sys/socket.h>
31#include <sys/socketvar.h>
32#include <sys/domain.h>
33#include <sys/protosw.h>
055eee28 34#include <sys/unpcb.h>
f255214c
KB
35#include <sys/vmmac.h>
36#define KERNEL
e00a6228 37#define NFS
055eee28 38#include <sys/file.h>
e00a6228 39#include <sys/mount.h>
7f9552fe 40#include <ufs/quota.h>
3dae3806 41#include <ufs/inode.h>
e00a6228
MT
42#include <nfs/nfsv2.h>
43#include <nfs/nfs.h>
44#include <nfs/nfsnode.h>
45#undef KERNEL
46
1a585c5c
KB
47#include <net/route.h>
48#include <netinet/in.h>
e00a6228
MT
49#include <netinet/in_systm.h>
50#include <netinet/ip.h>
1a585c5c 51#include <netinet/in_pcb.h>
e00a6228
MT
52
53#include <kvm.h>
54#include <paths.h>
7168b138 55#include <ctype.h>
1a585c5c
KB
56#include <nlist.h>
57#include <pwd.h>
7f9552fe 58#include <string.h>
e00a6228 59#include <stdio.h>
f255214c 60
e00a6228
MT
61#define TEXT -1
62#define CDIR -2
63#define RDIR -3
64#define TRACE -4
cf2c7bf9 65
f255214c 66typedef struct devs {
e00a6228 67 struct devs *next;
7f9552fe 68 long fsid;
e00a6228
MT
69 ino_t ino;
70 char *name;
f255214c 71} DEVS;
4575617f 72DEVS *devs;
5e369078 73
e00a6228
MT
74struct filestat {
75 long fsid;
76 long fileid;
7f9552fe 77 mode_t mode;
e00a6228
MT
78 u_long size;
79 dev_t rdev;
80};
81
7f9552fe 82#ifdef notdef
e00a6228 83struct nlist nl[] = {
055eee28
KB
84 { "" },
85};
7f9552fe 86#endif
055eee28 87
e00a6228
MT
88int 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 */
91int checkfile; /* true if restricting to particular files or filesystems */
92int nflg; /* (numerical) display f.s. and rdev as dev_t */
93int vflg; /* display errors in locating kernel data objects etc... */
f255214c 94
e00a6228 95#define dprintf if (vflg) fprintf
055eee28 96
4575617f 97extern int errno;
4575617f 98off_t lseek();
055eee28
KB
99
100main(argc, argv)
4575617f
KB
101 int argc;
102 char **argv;
055eee28 103{
e00a6228
MT
104 register struct passwd *passwd;
105 int what = KINFO_PROC_ALL, arg = 0;
106 struct passwd *getpwnam(), *getpwuid();
107 struct proc *p;
f255214c
KB
108 extern char *optarg;
109 extern int optind;
7f9552fe 110 int ch;
f255214c
KB
111 char *malloc();
112
e00a6228
MT
113
114 while ((ch = getopt(argc, argv, "p:u:fnv")) != EOF)
1a585c5c
KB
115 switch((char)ch) {
116 case 'p':
7168b138
MT
117 if (pflg++)
118 usage();
119 if (!isdigit(*optarg)) {
120 fputs("fstat: -p option requires a process id.\n", stderr);
055eee28 121 usage();
055eee28 122 }
e00a6228
MT
123 what = KINFO_PROC_PID;
124 arg = atoi(optarg);
1a585c5c
KB
125 break;
126 case 'u':
127 if (uflg++)
055eee28 128 usage();
f255214c 129 if (!(passwd = getpwnam(optarg))) {
7f9552fe 130 fprintf(stderr, "%s: unknown uid\n",
cf2c7bf9 131 optarg);
055eee28
KB
132 exit(1);
133 }
e00a6228
MT
134 what = KINFO_PROC_UID;
135 arg = passwd->pw_uid;
1a585c5c 136 break;
e00a6228
MT
137 case 'f':
138 fsflg++;
139 break;
140 case 'n':
141 nflg++;
142 break;
143 case 'v':
1a585c5c
KB
144 vflg++;
145 break;
146 case '?':
147 default:
148 usage();
055eee28 149 }
055eee28 150
5ebc207c 151 if (*(argv += optind)) {
193d3a86 152 for (; *argv; ++argv) {
5ebc207c 153 if (getfname(*argv))
e00a6228 154 checkfile = 1;
5ebc207c 155 }
e00a6228 156 if (!checkfile) /* file(s) specified, but none accessable */
5ebc207c 157 exit(1);
055eee28 158 }
e00a6228
MT
159 if (fsflg && !checkfile) {
160 /* -f with no files means use wd */
161 if (getfname(".") == 0)
162 exit(1);
163 checkfile = 1;
164 }
d81abd9a 165
d0410028 166 /* modify the following to make work on dead kernels */
e00a6228
MT
167 if (kvm_openfiles(NULL, NULL, NULL) == -1) {
168 fprintf(stderr, "fstat: %s\n", kvm_geterr());
055eee28
KB
169 exit(1);
170 }
7f9552fe 171#ifdef notdef
e00a6228 172 if (kvm_nlist(nl) != 0) {
7f9552fe 173 fprintf(stderr, "fstat: no namelist: %s\n", kvm_geterr());
e00a6228
MT
174 exit(1);
175 }
7f9552fe 176#endif
e00a6228
MT
177 if (kvm_getprocs(what, arg) == -1) {
178 fprintf(stderr, "fstat: %s\n", kvm_geterr());
055eee28
KB
179 exit(1);
180 }
e00a6228 181 if (nflg)
7f9552fe 182fputs("USER CMD PID FD DEV INUM MODE SZ|DV", stdout);
e00a6228 183 else
7f9552fe 184fputs("USER CMD PID FD MOUNT INUM MODE SZ|DV", stdout);
e00a6228
MT
185 if (checkfile && fsflg == 0)
186 fputs(" NAME\n", stdout);
187 else
188 putchar('\n');
5ebc207c 189
e00a6228
MT
190 while ((p = kvm_nextproc()) != NULL) {
191 if (p->p_stat == SZOMB)
f255214c 192 continue;
e00a6228 193 dofiles(p);
f255214c
KB
194 }
195 exit(0);
055eee28
KB
196}
197
e00a6228
MT
198char *Uname, *Comm;
199int 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; \
055eee28 218 }
e00a6228
MT
219
220/*
221 * print open files attributed to this process
222 */
223dofiles(p)
224 struct proc *p;
225{
226 int i;
e00a6228
MT
227 struct file file;
228 struct user *up = kvm_getu(p);
229 struct vnode *xvptr;
7f9552fe 230 extern char *user_from_uid();
e00a6228
MT
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;
055eee28 239 }
e00a6228
MT
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 &&
d0410028 249 kvm_read(&(p->p_textp->x_vptr), &xvptr,
e00a6228
MT
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);
055eee28
KB
282 }
283 }
055eee28
KB
284}
285
e00a6228
MT
286vtrans(vp, i)
287 struct vnode *vp;
055eee28 288{
e00a6228
MT
289 struct vnode vn;
290 struct filestat fst;
291 char *filename = NULL;
7f9552fe
MT
292 char *badtype = NULL;
293 char *getmnton();
7179a0b6 294 extern char *devname();
7f9552fe 295 char mode[15];
e00a6228
MT
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);
055eee28
KB
301 return;
302 }
7f9552fe
MT
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 }
e00a6228
MT
323 }
324 if (checkfile) {
325 int fsmatch = 0;
326 register DEVS *d;
055eee28 327
7f9552fe 328 if (badtype)
1a585c5c 329 return;
e00a6228 330 for (d = devs; d != NULL; d = d->next)
7f9552fe 331 if (d->fsid == fst.fsid) {
e00a6228
MT
332 fsmatch = 1;
333 if (d->ino == fst.fileid) {
334 filename = d->name;
335 break;
336 }
3dae3806 337 }
e00a6228
MT
338 if (fsmatch == 0 || (filename == NULL && fsflg == 0))
339 return;
340 }
341 PREFIX(i);
7f9552fe
MT
342 if (badtype) {
343 (void)printf(" - - %10s -\n", badtype);
e00a6228 344 return;
055eee28 345 }
e00a6228 346 if (nflg)
7f9552fe 347 (void)printf(" %2d,%-2d", major(fst.fsid), minor(fst.fsid));
055eee28 348 else
7f9552fe
MT
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);
e00a6228
MT
355 switch (vn.v_type) {
356 case VBLK:
7179a0b6
MT
357 case VCHR: {
358 char *name;
359
360 if (nflg || ((name = devname(fst.rdev, vn.v_type == VCHR ?
361 S_IFCHR : S_IFBLK)) == NULL))
e00a6228
MT
362 printf(" %2d,%-2d", major(fst.rdev), minor(fst.rdev));
363 else
7179a0b6 364 printf(" %6s", name);
e00a6228 365 break;
7179a0b6 366 }
f255214c 367 default:
e00a6228 368 printf(" %6d", fst.size);
055eee28 369 }
e00a6228
MT
370 if (filename && !fsflg)
371 printf(" %s", filename);
372
373 putchar('\n');
374}
055eee28 375
e00a6228
MT
376ufs_filestat(vp, fsp)
377 struct vnode *vp;
378 struct filestat *fsp;
379{
380 struct inode *ip = VTOI(vp);
3dae3806 381
7f9552fe 382 fsp->fsid = ip->i_dev & 0xffff;
e00a6228 383 fsp->fileid = (long)ip->i_number;
7f9552fe 384 fsp->mode = (mode_t)ip->i_mode;
e00a6228
MT
385 fsp->size = (u_long)ip->i_size;
386 fsp->rdev = ip->i_rdev;
387}
3dae3806 388
e00a6228
MT
389nfs_filestat(vp, fsp)
390 struct vnode *vp;
391 struct filestat *fsp;
392{
7f9552fe
MT
393 register struct nfsnode *np = VTONFS(vp);
394 register mode_t mode;
3dae3806 395
e00a6228
MT
396 fsp->fsid = np->n_vattr.va_fsid;
397 fsp->fileid = np->n_vattr.va_fileid;
7179a0b6 398 fsp->size = np->n_size;
e00a6228 399 fsp->rdev = np->n_vattr.va_rdev;
7f9552fe
MT
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;
e00a6228 425}
3dae3806 426
3dae3806 427
e00a6228
MT
428char *
429getmnton(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);
055eee28 447 }
e00a6228
MT
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;
7f9552fe 453 bcopy(&mount.mnt_stat.f_mntonname[0], &mt->mntonname[0], MNAMELEN);
e00a6228
MT
454 mt->next = mhead;
455 mhead = mt;
456 return (mt->mntonname);
055eee28 457}
1a585c5c 458
e00a6228 459socktrans(sock, i)
1a585c5c 460 struct socket *sock;
055eee28 461{
f255214c 462 static char *stypename[] = {
1a585c5c
KB
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;
f255214c
KB
476 int len;
477 char dname[32], *strcpy();
055eee28 478
e00a6228
MT
479 PREFIX(i);
480
055eee28 481 /* fill in socket */
e00a6228 482 if (kvm_read((off_t)sock, (char *)&so, sizeof(struct socket))
1a585c5c 483 != sizeof(struct socket)) {
e00a6228 484 dprintf(stderr, "can't read sock at %x\n", sock);
7f9552fe 485 goto bad;
055eee28
KB
486 }
487
488 /* fill in protosw entry */
e00a6228 489 if (kvm_read((off_t)so.so_proto, (char *)&proto, sizeof(struct protosw))
1a585c5c 490 != sizeof(struct protosw)) {
e00a6228 491 dprintf(stderr, "can't read protosw at %x", so.so_proto);
7f9552fe 492 goto bad;
055eee28
KB
493 }
494
495 /* fill in domain */
e00a6228 496 if (kvm_read((off_t)proto.pr_domain, (char *)&dom, sizeof(struct domain))
1a585c5c 497 != sizeof(struct domain)) {
e00a6228 498 dprintf(stderr, "can't read domain at %x\n", proto.pr_domain);
7f9552fe 499 goto bad;
055eee28
KB
500 }
501
f255214c
KB
502 /*
503 * grab domain name
504 * kludge "internet" --> "inet" for brevity
505 */
1a585c5c 506 if (dom.dom_family == AF_INET)
7f9552fe 507 strcpy(dname, "inet");
f255214c 508 else {
e00a6228
MT
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);
f255214c
KB
512 dname[0] = '\0';
513 }
514 else
515 dname[len] = '\0';
1a585c5c 516 }
055eee28 517
f255214c 518 if ((u_short)so.so_type > STYPEMAX)
7f9552fe 519 printf("* %s ?%d", dname, so.so_type);
f255214c 520 else
7f9552fe 521 printf("* %s %s", dname, stypename[so.so_type]);
055eee28
KB
522
523 /*
5ebc207c 524 * protocol specific formatting
055eee28 525 *
f255214c
KB
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.
055eee28 533 */
f255214c
KB
534 switch(dom.dom_family) {
535 case AF_INET:
536 getinetproto(proto.pr_protocol);
055eee28
KB
537 if (proto.pr_protocol == IPPROTO_TCP ) {
538 if (so.so_pcb) {
e00a6228 539 if (kvm_read((off_t)so.so_pcb, (char *)&inpcb, sizeof(struct inpcb))
055eee28 540 != sizeof(struct inpcb)){
e00a6228
MT
541 dprintf(stderr,
542 "can't read inpcb at %x\n", so.so_pcb);
7f9552fe 543 goto bad;
055eee28 544 }
7f9552fe 545 printf(" %x", (int)inpcb.inp_ppcb);
055eee28 546 }
055eee28 547 }
1a585c5c 548 else if (so.so_pcb)
7f9552fe 549 printf(" %x", (int)so.so_pcb);
f255214c
KB
550 break;
551 case AF_UNIX:
055eee28
KB
552 /* print address of pcb and connected pcb */
553 if (so.so_pcb) {
7f9552fe 554 printf(" %x", (int)so.so_pcb);
e00a6228 555 if (kvm_read((off_t)so.so_pcb, (char *)&unpcb, sizeof(struct unpcb))
055eee28 556 != sizeof(struct unpcb)){
e00a6228
MT
557 dprintf(stderr, "can't read unpcb at %x\n",
558 so.so_pcb);
7f9552fe 559 goto bad;
055eee28 560 }
5e369078 561 if (unpcb.unp_conn) {
f255214c 562 char shoconn[4], *cp;
1a585c5c 563
f255214c 564 cp = shoconn;
5e369078 565 if (!(so.so_state & SS_CANTRCVMORE))
f255214c
KB
566 *cp++ = '<';
567 *cp++ = '-';
5e369078 568 if (!(so.so_state & SS_CANTSENDMORE))
f255214c
KB
569 *cp++ = '>';
570 *cp = '\0';
7f9552fe 571 printf(" %s %x", shoconn,
cf2c7bf9 572 (int)unpcb.unp_conn);
5e369078 573 }
055eee28 574 }
f255214c
KB
575 break;
576 default:
577 /* print protocol number and socket address */
7f9552fe 578 printf(" %d %x", proto.pr_protocol, (int)sock);
f255214c 579 }
7f9552fe
MT
580 printf("\n");
581 return;
582bad:
583 printf("* error\n");
055eee28
KB
584}
585
f255214c
KB
586/*
587 * getinetproto --
588 * print name of protocol number
589 */
055eee28 590getinetproto(number)
1a585c5c 591 int number;
055eee28 592{
1a585c5c 593 char *cp;
9bd38ba8 594
055eee28 595 switch(number) {
f255214c
KB
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;
9bd38ba8 614 default:
7f9552fe 615 printf(" %d", number);
f255214c 616 return;
055eee28 617 }
7f9552fe 618 printf(" %s", cp);
055eee28
KB
619}
620
055eee28 621getfname(filename)
1a585c5c 622 char *filename;
055eee28 623{
f255214c
KB
624 struct stat statbuf;
625 DEVS *cur;
626 char *malloc();
055eee28 627
1a585c5c 628 if (stat(filename, &statbuf)) {
7f9552fe 629 fprintf(stderr, "fstat: %s: %s\n", strerror(errno),
cf2c7bf9 630 filename);
6270ab99 631 return(0);
1a585c5c 632 }
f255214c 633 if ((cur = (DEVS *)malloc(sizeof(DEVS))) == NULL) {
7f9552fe 634 fprintf(stderr, "fstat: out of space.\n");
f255214c
KB
635 exit(1);
636 }
637 cur->next = devs;
638 devs = cur;
055eee28 639
e00a6228 640 cur->ino = statbuf.st_ino;
7f9552fe 641 cur->fsid = statbuf.st_dev & 0xffff;
d81abd9a 642 cur->name = filename;
6270ab99 643 return(1);
f255214c
KB
644}
645
f255214c
KB
646usage()
647{
cf2c7bf9
KB
648 (void)fprintf(stderr,
649 "usage: fstat [-u user] [-p pid] [filename ...]\n");
f255214c 650 exit(1);
055eee28 651}