fstat.c revision 1.26 1 /* $NetBSD: fstat.c,v 1.26 1998/07/06 07:50:19 mrg 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.26 1998/07/06 07:50:19 mrg 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
94 #define TEXT -1
95 #define CDIR -2
96 #define RDIR -3
97 #define TRACE -4
98
99 typedef struct devs {
100 struct devs *next;
101 long fsid;
102 ino_t ino;
103 char *name;
104 } DEVS;
105 DEVS *devs;
106
107 struct filestat {
108 long fsid;
109 long fileid;
110 mode_t mode;
111 u_long size;
112 dev_t rdev;
113 };
114
115 #ifdef notdef
116 struct nlist nl[] = {
117 { "" },
118 };
119 #endif
120
121 int 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 */
124 int checkfile; /* true if restricting to particular files or filesystems */
125 int nflg; /* (numerical) display f.s. and rdev as dev_t */
126 int vflg; /* display errors in locating kernel data objects etc... */
127
128 #define dprintf if (vflg) fprintf
129
130 struct file **ofiles; /* buffer of pointers to file structures */
131 int 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) \
147 (kvm_read(kd, (u_long)(kaddr), (char *)(paddr), (len)) == (len))
148
149 kvm_t *kd;
150
151 void dofiles __P((struct kinfo_proc *));
152 int ext2fs_filestat __P((struct vnode *, struct filestat *));
153 int getfname __P((char *));
154 void getinetproto __P((int));
155 char *getmnton __P((struct mount *));
156 int main __P((int, char **));
157 int nfs_filestat __P((struct vnode *, struct filestat *));
158 void socktrans __P((struct socket *, int));
159 int ufs_filestat __P((struct vnode *, struct filestat *));
160 void usage __P((void));
161 void vtrans __P((struct vnode *, int, int));
162
163 int
164 main(argc, argv)
165 int argc;
166 char **argv;
167 {
168 struct passwd *passwd;
169 struct kinfo_proc *p, *plast;
170 int arg, ch, what;
171 char *memf, *nlistf;
172 char buf[_POSIX2_LINE_MAX];
173 int cnt;
174 gid_t egid = getegid();
175
176 (void)setegid(getgid());
177 arg = 0;
178 what = KERN_PROC_ALL;
179 nlistf = memf = NULL;
180 while ((ch = getopt(argc, argv, "fnp:u:vN:M:")) != -1)
181 switch((char)ch) {
182 case 'f':
183 fsflg = 1;
184 break;
185 case 'M':
186 memf = optarg;
187 break;
188 case 'N':
189 nlistf = optarg;
190 break;
191 case 'n':
192 nflg = 1;
193 break;
194 case 'p':
195 if (pflg++)
196 usage();
197 if (!isdigit(*optarg)) {
198 fprintf(stderr,
199 "fstat: -p requires a process id\n");
200 usage();
201 }
202 what = KERN_PROC_PID;
203 arg = atoi(optarg);
204 break;
205 case 'u':
206 if (uflg++)
207 usage();
208 if (!(passwd = getpwnam(optarg))) {
209 fprintf(stderr, "%s: unknown uid\n",
210 optarg);
211 exit(1);
212 }
213 what = KERN_PROC_UID;
214 arg = passwd->pw_uid;
215 break;
216 case 'v':
217 vflg = 1;
218 break;
219 case '?':
220 default:
221 usage();
222 }
223
224 if (*(argv += optind)) {
225 for (; *argv; ++argv) {
226 if (getfname(*argv))
227 checkfile = 1;
228 }
229 if (!checkfile) /* file(s) specified, but none accessable */
230 exit(1);
231 }
232
233 ALLOC_OFILES(256); /* reserve space for file pointers */
234
235 if (fsflg && !checkfile) {
236 /* -f with no files means use wd */
237 if (getfname(".") == 0)
238 exit(1);
239 checkfile = 1;
240 }
241
242 /*
243 * Discard setgid privileges. If not the running kernel, we toss
244 * them away totally so that bad guys can't print interesting stuff
245 * from kernel memory, otherwise switch back to kmem for the
246 * duration of the kvm_openfiles() call.
247 */
248 if (nlistf != NULL || memf != NULL)
249 (void)setgid(getgid());
250 else
251 (void)setegid(egid);
252
253 if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, buf)) == NULL)
254 errx(1, "%s", buf);
255
256 /* get rid of it now anyway */
257 if (nlistf == NULL && memf == NULL)
258 (void)setgid(getgid());
259
260 #ifdef notdef
261 if (kvm_nlist(kd, nl) != 0) {
262 fprintf(stderr, "fstat: no namelist: %s\n", kvm_geterr(kd));
263 exit(1);
264 }
265 #endif
266 if ((p = kvm_getprocs(kd, what, arg, &cnt)) == NULL) {
267 fprintf(stderr, "fstat: %s\n", kvm_geterr(kd));
268 exit(1);
269 }
270 if (nflg)
271 printf("%s",
272 "USER CMD PID FD DEV INUM MODE SZ|DV R/W");
273 else
274 printf("%s",
275 "USER CMD PID FD MOUNT INUM MODE SZ|DV R/W");
276 if (checkfile && fsflg == 0)
277 printf(" NAME\n");
278 else
279 putchar('\n');
280
281 for (plast = &p[cnt]; p < plast; ++p) {
282 if (p->kp_proc.p_stat == SZOMB)
283 continue;
284 dofiles(p);
285 }
286 exit(0);
287 }
288
289 char *Uname, *Comm;
290 int Pid;
291
292 #define PREFIX(i) printf("%-8.8s %-10s %5d", Uname, Comm, Pid); \
293 switch(i) { \
294 case TEXT: \
295 printf(" text"); \
296 break; \
297 case CDIR: \
298 printf(" wd"); \
299 break; \
300 case RDIR: \
301 printf(" root"); \
302 break; \
303 case TRACE: \
304 printf(" tr"); \
305 break; \
306 default: \
307 printf(" %4d", i); \
308 break; \
309 }
310
311 /*
312 * print open files attributed to this process
313 */
314 void
315 dofiles(kp)
316 struct kinfo_proc *kp;
317 {
318 int i;
319 struct file file;
320 struct filedesc0 filed0;
321 #define filed filed0.fd_fd
322 struct proc *p = &kp->kp_proc;
323 struct eproc *ep = &kp->kp_eproc;
324
325 Uname = user_from_uid(ep->e_ucred.cr_uid, 0);
326 Pid = p->p_pid;
327 Comm = p->p_comm;
328
329 if (p->p_fd == NULL)
330 return;
331 if (!KVM_READ(p->p_fd, &filed0, sizeof (filed0))) {
332 dprintf(stderr, "can't read filedesc at %lx for pid %d\n",
333 (long)p->p_fd, Pid);
334 return;
335 }
336 if (filed.fd_nfiles < 0 || filed.fd_lastfile >= filed.fd_nfiles ||
337 filed.fd_freefile > filed.fd_lastfile + 1) {
338 dprintf(stderr, "filedesc corrupted at %lx for pid %d\n",
339 (long)p->p_fd, Pid);
340 return;
341 }
342 /*
343 * root directory vnode, if one
344 */
345 if (filed.fd_rdir)
346 vtrans(filed.fd_rdir, RDIR, FREAD);
347 /*
348 * current working directory vnode
349 */
350 vtrans(filed.fd_cdir, CDIR, FREAD);
351 /*
352 * ktrace vnode, if one
353 */
354 if (p->p_tracep)
355 vtrans(p->p_tracep, TRACE, FREAD|FWRITE);
356 /*
357 * open files
358 */
359 #define FPSIZE (sizeof (struct file *))
360 ALLOC_OFILES(filed.fd_lastfile+1);
361 if (filed.fd_nfiles > NDFILE) {
362 if (!KVM_READ(filed.fd_ofiles, ofiles,
363 (filed.fd_lastfile+1) * FPSIZE)) {
364 dprintf(stderr,
365 "can't read file structures at %lx for pid %d\n",
366 (long)filed.fd_ofiles, Pid);
367 return;
368 }
369 } else
370 memmove(ofiles, filed0.fd_dfiles,
371 (filed.fd_lastfile+1) * FPSIZE);
372 for (i = 0; i <= filed.fd_lastfile; i++) {
373 if (ofiles[i] == NULL)
374 continue;
375 if (!KVM_READ(ofiles[i], &file, sizeof (struct file))) {
376 dprintf(stderr,
377 "can't read file %d at %lx for pid %d\n",
378 i, (long)ofiles[i], Pid);
379 continue;
380 }
381 if (file.f_type == DTYPE_VNODE)
382 vtrans((struct vnode *)file.f_data, i, file.f_flag);
383 else if (file.f_type == DTYPE_SOCKET) {
384 if (checkfile == 0)
385 socktrans((struct socket *)file.f_data, i);
386 }
387 else {
388 dprintf(stderr,
389 "unknown file type %d for file %d of pid %d\n",
390 file.f_type, i, Pid);
391 }
392 }
393 }
394
395 void
396 vtrans(vp, i, flag)
397 struct vnode *vp;
398 int i;
399 int flag;
400 {
401 struct vnode vn;
402 struct filestat fst;
403 char mode[15];
404 char *badtype = NULL, *filename;
405
406 filename = badtype = NULL;
407 if (!KVM_READ(vp, &vn, sizeof (struct vnode))) {
408 dprintf(stderr, "can't read vnode at %lx for pid %d\n",
409 (long)vp, Pid);
410 return;
411 }
412 if (vn.v_type == VNON || vn.v_tag == VT_NON)
413 badtype = "none";
414 else if (vn.v_type == VBAD)
415 badtype = "bad";
416 else
417 switch (vn.v_tag) {
418 case VT_UFS:
419 if (!ufs_filestat(&vn, &fst))
420 badtype = "error";
421 break;
422 case VT_MFS:
423 if (!ufs_filestat(&vn, &fst))
424 badtype = "error";
425 break;
426 case VT_NFS:
427 if (!nfs_filestat(&vn, &fst))
428 badtype = "error";
429 break;
430 case VT_EXT2FS:
431 if (!ext2fs_filestat(&vn, &fst))
432 badtype = "error";
433 break;
434 default: {
435 static char unknown[10];
436 (void)snprintf(badtype = unknown, sizeof unknown,
437 "?(%x)", vn.v_tag);
438 break;;
439 }
440 }
441 if (checkfile) {
442 int fsmatch = 0;
443 DEVS *d;
444
445 if (badtype)
446 return;
447 for (d = devs; d != NULL; d = d->next)
448 if (d->fsid == fst.fsid) {
449 fsmatch = 1;
450 if (d->ino == fst.fileid) {
451 filename = d->name;
452 break;
453 }
454 }
455 if (fsmatch == 0 || (filename == NULL && fsflg == 0))
456 return;
457 }
458 PREFIX(i);
459 if (badtype) {
460 (void)printf(" - - %10s -\n", badtype);
461 return;
462 }
463 if (nflg)
464 (void)printf(" %2d,%-2d", major(fst.fsid), minor(fst.fsid));
465 else
466 (void)printf(" %-8s", getmnton(vn.v_mount));
467 if (nflg)
468 (void)snprintf(mode, sizeof mode, "%o", fst.mode);
469 else
470 strmode(fst.mode, mode);
471 (void)printf(" %6ld %10s", (long)fst.fileid, mode);
472 switch (vn.v_type) {
473 case VBLK:
474 case VCHR: {
475 char *name;
476
477 if (nflg || ((name = devname(fst.rdev, vn.v_type == VCHR ?
478 S_IFCHR : S_IFBLK)) == NULL))
479 printf(" %2d,%-2d", major(fst.rdev), minor(fst.rdev));
480 else
481 printf(" %6s", name);
482 break;
483 }
484 default:
485 printf(" %6ld", (long)fst.size);
486 }
487 putchar(' ');
488 if (flag & FREAD)
489 putchar('r');
490 if (flag & FWRITE)
491 putchar('w');
492 if (filename && !fsflg)
493 printf(" %s", filename);
494 putchar('\n');
495 }
496
497 int
498 ufs_filestat(vp, fsp)
499 struct vnode *vp;
500 struct filestat *fsp;
501 {
502 struct inode inode;
503
504 if (!KVM_READ(VTOI(vp), &inode, sizeof (inode))) {
505 dprintf(stderr, "can't read inode at %lx for pid %d\n",
506 (long)VTOI(vp), Pid);
507 return 0;
508 }
509 fsp->fsid = inode.i_dev & 0xffff;
510 fsp->fileid = (long)inode.i_number;
511 fsp->mode = (mode_t)inode.i_ffs_mode;
512 fsp->size = (u_long)inode.i_ffs_size;
513 fsp->rdev = inode.i_ffs_rdev;
514
515 return 1;
516 }
517
518 int
519 ext2fs_filestat(vp, fsp)
520 struct vnode *vp;
521 struct filestat *fsp;
522 {
523 struct inode inode;
524
525 if (!KVM_READ(VTOI(vp), &inode, sizeof (inode))) {
526 dprintf(stderr, "can't read inode at %lx for pid %d\n",
527 (long)VTOI(vp), Pid);
528 return 0;
529 }
530 fsp->fsid = inode.i_dev & 0xffff;
531 fsp->fileid = (long)inode.i_number;
532 fsp->mode = (mode_t)inode.i_e2fs_mode;
533 fsp->size = (u_long)inode.i_e2fs_size;
534 fsp->rdev = 0; /* XXX */
535 return 1;
536 }
537
538 int
539 nfs_filestat(vp, fsp)
540 struct vnode *vp;
541 struct filestat *fsp;
542 {
543 struct nfsnode nfsnode;
544 struct vattr va;
545 mode_t mode;
546
547 if (!KVM_READ(VTONFS(vp), &nfsnode, sizeof (nfsnode))) {
548 dprintf(stderr, "can't read nfsnode at 0x%lx for pid %d\n",
549 (u_long)VTONFS(vp), Pid);
550 return 0;
551 }
552 if (!KVM_READ(nfsnode.n_vattr, &va, sizeof(va))) {
553 dprintf(stderr,
554 "can't read vnode attributes at 0x%lx for pid %d\n",
555 (u_long)nfsnode.n_vattr, Pid);
556 return 0;
557 }
558 fsp->fsid = va.va_fsid;
559 fsp->fileid = va.va_fileid;
560 fsp->size = nfsnode.n_size;
561 fsp->rdev = va.va_rdev;
562 mode = (mode_t)va.va_mode;
563 switch (vp->v_type) {
564 case VREG:
565 mode |= S_IFREG;
566 break;
567 case VDIR:
568 mode |= S_IFDIR;
569 break;
570 case VBLK:
571 mode |= S_IFBLK;
572 break;
573 case VCHR:
574 mode |= S_IFCHR;
575 break;
576 case VLNK:
577 mode |= S_IFLNK;
578 break;
579 case VSOCK:
580 mode |= S_IFSOCK;
581 break;
582 case VFIFO:
583 mode |= S_IFIFO;
584 break;
585 default:
586 break;
587 };
588 fsp->mode = mode;
589
590 return 1;
591 }
592
593
594 char *
595 getmnton(m)
596 struct mount *m;
597 {
598 static struct mount mount;
599 static struct mtab {
600 struct mtab *next;
601 struct mount *m;
602 char mntonname[MNAMELEN];
603 } *mhead = NULL;
604 struct mtab *mt;
605
606 for (mt = mhead; mt != NULL; mt = mt->next)
607 if (m == mt->m)
608 return (mt->mntonname);
609 if (!KVM_READ(m, &mount, sizeof(struct mount))) {
610 fprintf(stderr, "can't read mount table at %lx\n", (long)m);
611 return (NULL);
612 }
613 if ((mt = malloc(sizeof (struct mtab))) == NULL) {
614 fprintf(stderr, "fstat: %s\n", strerror(errno));
615 exit(1);
616 }
617 mt->m = m;
618 memmove(&mt->mntonname[0], &mount.mnt_stat.f_mntonname[0], MNAMELEN);
619 mt->next = mhead;
620 mhead = mt;
621 return (mt->mntonname);
622 }
623
624 void
625 socktrans(sock, i)
626 struct socket *sock;
627 int i;
628 {
629 static char *stypename[] = {
630 "unused", /* 0 */
631 "stream", /* 1 */
632 "dgram", /* 2 */
633 "raw", /* 3 */
634 "rdm", /* 4 */
635 "seqpak" /* 5 */
636 };
637 #define STYPEMAX 5
638 struct socket so;
639 struct protosw proto;
640 struct domain dom;
641 struct inpcb inpcb;
642 struct unpcb unpcb;
643 int len;
644 char dname[32];
645
646 PREFIX(i);
647
648 /* fill in socket */
649 if (!KVM_READ(sock, &so, sizeof(struct socket))) {
650 dprintf(stderr, "can't read sock at %lx\n", (long)sock);
651 goto bad;
652 }
653
654 /* fill in protosw entry */
655 if (!KVM_READ(so.so_proto, &proto, sizeof(struct protosw))) {
656 dprintf(stderr, "can't read protosw at %lx", (long)so.so_proto);
657 goto bad;
658 }
659
660 /* fill in domain */
661 if (!KVM_READ(proto.pr_domain, &dom, sizeof(struct domain))) {
662 dprintf(stderr, "can't read domain at %lx\n",
663 (long)proto.pr_domain);
664 goto bad;
665 }
666
667 if ((len = kvm_read(kd, (u_long)dom.dom_name, dname,
668 sizeof(dname) - 1)) != sizeof(dname) -1) {
669 dprintf(stderr, "can't read domain name at %lx\n",
670 (long)dom.dom_name);
671 dname[0] = '\0';
672 }
673 else
674 dname[len] = '\0';
675
676 if ((u_short)so.so_type > STYPEMAX)
677 printf("* %s ?%d", dname, so.so_type);
678 else
679 printf("* %s %s", dname, stypename[so.so_type]);
680
681 /*
682 * protocol specific formatting
683 *
684 * Try to find interesting things to print. For tcp, the interesting
685 * thing is the address of the tcpcb, for udp and others, just the
686 * inpcb (socket pcb). For unix domain, its the address of the socket
687 * pcb and the address of the connected pcb (if connected). Otherwise
688 * just print the protocol number and address of the socket itself.
689 * The idea is not to duplicate netstat, but to make available enough
690 * information for further analysis.
691 */
692 switch(dom.dom_family) {
693 case AF_INET:
694 getinetproto(proto.pr_protocol);
695 if (proto.pr_protocol == IPPROTO_TCP ) {
696 if (so.so_pcb) {
697 if (kvm_read(kd, (u_long)so.so_pcb,
698 (char *)&inpcb, sizeof(struct inpcb))
699 != sizeof(struct inpcb)) {
700 dprintf(stderr,
701 "can't read inpcb at %lx\n",
702 (long)so.so_pcb);
703 goto bad;
704 }
705 printf(" %lx", (long)inpcb.inp_ppcb);
706 }
707 }
708 else if (so.so_pcb)
709 printf(" %lx", (long)so.so_pcb);
710 break;
711 case AF_UNIX:
712 /* print address of pcb and connected pcb */
713 if (so.so_pcb) {
714 printf(" %lx", (long)so.so_pcb);
715 if (kvm_read(kd, (u_long)so.so_pcb, (char *)&unpcb,
716 sizeof(struct unpcb)) != sizeof(struct unpcb)){
717 dprintf(stderr, "can't read unpcb at %lx\n",
718 (long)so.so_pcb);
719 goto bad;
720 }
721 if (unpcb.unp_conn) {
722 char shoconn[4], *cp;
723
724 cp = shoconn;
725 if (!(so.so_state & SS_CANTRCVMORE))
726 *cp++ = '<';
727 *cp++ = '-';
728 if (!(so.so_state & SS_CANTSENDMORE))
729 *cp++ = '>';
730 *cp = '\0';
731 printf(" %s %lx", shoconn,
732 (long)unpcb.unp_conn);
733 }
734 }
735 break;
736 default:
737 /* print protocol number and socket address */
738 printf(" %d %lx", proto.pr_protocol, (long)sock);
739 }
740 printf("\n");
741 return;
742 bad:
743 printf("* error\n");
744 }
745
746 /*
747 * getinetproto --
748 * print name of protocol number
749 */
750 void
751 getinetproto(number)
752 int number;
753 {
754 char *cp;
755
756 switch (number) {
757 case IPPROTO_IP:
758 cp = "ip"; break;
759 case IPPROTO_ICMP:
760 cp ="icmp"; break;
761 case IPPROTO_GGP:
762 cp ="ggp"; break;
763 case IPPROTO_TCP:
764 cp ="tcp"; break;
765 case IPPROTO_EGP:
766 cp ="egp"; break;
767 case IPPROTO_PUP:
768 cp ="pup"; break;
769 case IPPROTO_UDP:
770 cp ="udp"; break;
771 case IPPROTO_IDP:
772 cp ="idp"; break;
773 case IPPROTO_RAW:
774 cp ="raw"; break;
775 default:
776 printf(" %d", number);
777 return;
778 }
779 printf(" %s", cp);
780 }
781
782 int
783 getfname(filename)
784 char *filename;
785 {
786 struct stat statbuf;
787 DEVS *cur;
788
789 if (stat(filename, &statbuf)) {
790 fprintf(stderr, "fstat: %s: %s\n", filename, strerror(errno));
791 return(0);
792 }
793 if ((cur = malloc(sizeof(DEVS))) == NULL) {
794 fprintf(stderr, "fstat: %s\n", strerror(errno));
795 exit(1);
796 }
797 cur->next = devs;
798 devs = cur;
799
800 cur->ino = statbuf.st_ino;
801 cur->fsid = statbuf.st_dev & 0xffff;
802 cur->name = filename;
803 return(1);
804 }
805
806 void
807 usage()
808 {
809
810 (void)fprintf(stderr,
811 "usage: fstat [-fnv] [-p pid] [-u user] [-N system] [-M core] [file ...]\n");
812 exit(1);
813 }
814