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