/* * dnode.c - NEXTSTEP and OPENSTEP node functions for lsof */ /* * Copyright 1994 Purdue Research Foundation, West Lafayette, Indiana * 47907. All rights reserved. * * Written by Victor A. Abell * * This software is not subject to any license of the American Telephone * and Telegraph Company or the Regents of the University of California. * * Permission is granted to anyone to use this software for any purpose on * any computer system, and to alter it and redistribute it freely, subject * to the following restrictions: * * 1. Neither the authors nor Purdue University are responsible for any * consequences of the use of this software. * * 2. The origin of this software must not be misrepresented, either by * explicit claim or by omission. Credit to the authors and Purdue * University must appear in documentation and sources. * * 3. Altered versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 4. This notice may not be removed or altered. */ #ifndef lint static char copyright[] = "@(#) Copyright 1994 Purdue Research Foundation.\nAll rights reserved.\n"; static char *rcsid = "$Id: dnode.c,v 1.17 2006/03/28 22:08:17 abe Exp $"; #endif #include "lsof.h" #if STEPV>=31 /* * Local definitions */ struct l_lockf { /* local lock info */ short type; /* lock type */ off_t start, end; /* lock start and end */ pid_t pid; /* owning process ID */ struct l_lockf *next; }; struct l_svn { /* shadow vnode */ KA_T vp; /* associated vnode */ struct l_lockf *lp; /* local lock chain */ struct l_svn *next; }; struct posix_proc { pid_t p_pid; }; #define POSIX_KERN 1 #include #define SVNHASH(n) (((int)((long)(n) * 31415l) >> 5) & (LF_SVNODE_HSZ - 1)) /* * Local static variables */ static struct l_svn **Svnc = (struct l_svn **)NULL; /* local shadow vnode cache */ static int SvncSt = 0; /* Svnc[] load status */ /* * Local function prototypes */ _PROTOTYPE(static char isvlocked,(KA_T vp)); _PROTOTYPE(static int load_svnc,(void)); /* * clr_svnc() - clear shadow vnode cache */ void clr_svnc() { struct l_lockf *lf, *lfn; int i; struct l_svn *sv, *svn; if (!Svnc || !SvncSt) return; for (i = 0; i < LF_SVNODE_HSZ; i++) { if (!(sv = Svnc[i])) continue; do { if ((lf = sv->lp)) { do { lfn = lf->next; (void) free((FREE_P *)lf); } while ((lf = lfn)); } svn = sv->next; (void) free((FREE_P *)sv); } while ((sv = svn)); Svnc[i] = (struct l_svn *)NULL; } SvncSt = 0; } /* * isvlocked() - is vnode locked? */ static char isvlocked(vp) KA_T vp; /* vnode's kernel address */ { int i; struct l_lockf *lp; struct l_svn *sv; if (!Svnc || !SvncSt) { if (!load_svnc()) return(' '); } /* * Hash the vnode address and see if there's a shadow (lock) vnode structure * assigned to it. */ i = SVNHASH(vp); for (sv = Svnc[i]; sv; sv = sv->next) { if ((KA_T)sv->vp == vp) break; } if (!sv) return(' '); /* * Search the lock owners represented by the shadow vnode's lock chain * for this process. */ for (lp = sv->lp; lp; lp = lp->next) { if (lp->pid == (pid_t)Lp->pid) { if (lp->start == 0 && lp->end == 0x7fffffff) i = 1; else i = 0; if (lp->type == F_RDLCK) return(i ? 'R' : 'r'); else if (lp->type == F_WRLCK) return(i ? 'W' : 'w'); return(' '); } } return(' '); } /* * load_svnc() - load the shadow vnode cache */ int load_svnc() { int i, j; static KA_T kp = (KA_T)NULL; struct lockf lf, *lp; struct l_lockf *lsf; struct l_svn *lsv; struct posix_proc p; struct lf_svnode *sn, *sp[LF_SVNODE_HSZ], sv; if (Svnc && SvncSt) return(1); /* * Get the shadow vnode hash table address from the kernel. */ if (!kp) { if (get_Nl_value("lfsvh", Drive_Nl, &kp) < 0 || !kp) return(0); } /* * Define local hash buckets, if necessary. */ if (!Svnc) { if (!(Svnc = (struct l_svn **)calloc(sizeof(struct l_svn *), LF_SVNODE_HSZ))) { (void) fprintf(stderr, "%s: no space for %d local shadow vnode hash buckets\n", Pn, LF_SVNODE_HSZ); Exit(1); } } /* * Search the hash buckets of the shadow vnode table. */ if (kread(kp, (char *)&sp, sizeof(sp))) return(0); for (i = 0; i < LF_SVNODE_HSZ; i++) { if (!(sn = sp[i])) continue; do { /* * Duplicate the chain of shadow vnodes in the bucket. */ if (kread((KA_T)sn, (char *)&sv, sizeof(sv)) || !sv.lf_vnodep || !sv.lf_lockfp) break; /* * Allocate and initialize a local shadow vnode structure. */ if (!(lsv = (struct l_svn *)malloc(sizeof(struct l_svn)))) { (void) fprintf(stderr, "%s: no space for local shadow vnode -- PID: %ld\n", Pn, Lp->pid); Exit(1); } lsv->vp = (KA_T)sv.lf_vnodep; lsv->lp = (struct l_lockf *)NULL; lsv->next = (struct l_svn *)NULL; lp = sv.lf_lockfp; do { /* * Duplicate the lock chain for this shadow vnode. */ if (kread((KA_T)lp, (char *)&lf, sizeof(lf))) break; if (!lf.lf_posix_procp || kread((KA_T)lf.lf_posix_procp, (char *)&p, sizeof(p)) || !p.p_pid) continue; if (!(lsf=(struct l_lockf *)malloc(sizeof(struct l_lockf)))) { (void) fprintf(stderr, "%s: no space for local lock struct -- PID: %ld\n", Pn, Lp->pid); Exit(1); } lsf->type = lf.lf_type; lsf->start = lf.lf_start; lsf->end = lf.lf_end; lsf->pid = (pid_t)p.p_pid; lsf->next = lsv->lp; lsv->lp = lsf; } while ((lp = lf.lf_next)); /* * Link the shadow vnode to its local hash bucket. */ j = SVNHASH(lsv->vp); lsv->next = Svnc[j]; Svnc[j] = lsv; } while ((sn = sv.lf_next)); } SvncSt = 1; return(1); } #endif /* STEPV>=31 */ /* * process_node() - process vnode */ void process_node(va) KA_T va; /* vnode kernel space address */ { dev_t dev, rdev; int devs = 0; static int ft = 1; static KA_T fvops = (KA_T)0; struct inode i; int ins = 0; static KA_T nvops = (KA_T)0; struct rnode r; int rdevs = 0; struct vnode rv; struct snode s; static KA_T svops = (KA_T)0; char tbuf[32], *ty; static KA_T uvops = (KA_T)0; enum vtype type; static struct vnode *v = (struct vnode *)NULL; #if defined(HAS_AFS) static int afs = 0; /* AFS test status: -1 = no AFS * 0 = not tested * 1 = AFS present */ struct afsnode an; static KA_T avops = (KA_T)0; #endif /* defined(HAS_AFS) */ /* * Read the vnode. */ if (!va) { enter_nm("no vnode address"); return; } /* * Read the vnode. */ if (!v) { /* * Allocate space for the vnode or AFS vcache structure. */ #if defined(HAS_AFS) v = alloc_vcache(); #else /* !defined(HAS_AFS) */ v = (struct vnode *)malloc(sizeof(struct vnode)); #endif /* defined(HAS_AFS) */ if (!v) { (void) fprintf(stderr, "%s: can't allocate %s space\n", Pn, #if defined(HAS_AFS) "vcache" #else /* !defined(HAS_AFS) */ "vnode" #endif /* defined(HAS_AFS) */ ); Exit(1); } } if (readvnode(va, v)) { enter_nm(Namech); return; } # if defined(HASNCACHE) Lf->na = va; # endif /* defined(HASNCACHE) */ # if defined(HASFSTRUCT) Lf->fna = va; Lf->fsv |= FSV_NI; # endif /* defined(HASFSTRUCT) */ /* * Get vnode operations addresses, as required. */ if (ft) { #if defined(HAS_AFS) (void) get_Nl_value("avops", Drive_Nl, &avops); #endif /* defined(HAS_AFS) */ (void) get_Nl_value("fvops", Drive_Nl, &fvops); (void) get_Nl_value("nvops", Drive_Nl, &nvops); (void) get_Nl_value("svops", Drive_Nl, &svops); (void) get_Nl_value("uvops", Drive_Nl, &uvops); ft = 0; } /* * Determine the vnode type. */ if ((uvops && (KA_T)v->v_op == uvops) || (svops && (KA_T)v->v_op == svops)) Ntype = N_REGLR; else if (nvops && (KA_T)v->v_op == nvops) Ntype = N_NFS; else if (fvops && (KA_T)v->v_op == fvops) Ntype = N_FIFO; #if defined(HAS_AFS) /* * Caution: this AFS test should be the last one. */ else if (avops) { if ((KA_T)v->v_op == avops) Ntype = N_AFS; else { unknown_v_op: (void) snpf(Namech, Namechl, "unknown file system type; v_op: %s", print_kptr((KA_T)v->v_op, (char *)NULL, 0)); enter_nm(Namech); return; } } else if (v->v_data || !v->v_vfsp) goto unknown_v_op; else { switch (afs) { case -1: goto unknown_v_op; case 0: if (!hasAFS(v)) { afs = -1; goto unknown_v_op; } afs = 1; Ntype = N_AFS; AFSVfsp = (KA_T)v->v_vfsp; break; case 1: if ((KA_T)v->v_vfsp == AFSVfsp) Ntype = N_AFS; else goto unknown_v_op; } } #else /* !defined(HAS_AFS) */ else { (void) snpf(Namech, Namechl, "unknown file system type; v_op: %s", print_kptr((KA_T)v->v_op, (char *)NULL, 0)); enter_nm(Namech); return; } #endif /* defined(HAS_AFS) */ /* * Determine the lock type. */ if (v->v_shlockc || v->v_exlockc) { if (FILEPTR && (FILEPTR->f_flag & FSHLOCK)) Lf->lock = 'R'; else if (FILEPTR && (FILEPTR->f_flag & FEXLOCK)) Lf->lock = 'W'; else #if STEPV>=31 Lf->lock = isvlocked(va); #else /* STEPV<31 */ Lf->lock = ' '; #endif /* STEPV>=31 */ } /* * Read the inode, rnode, snode, or vcache struct. */ switch (Ntype) { #if defined(HAS_AFS) case N_AFS: if (readafsnode(va, v, &an)) return; break; #endif /* defined(HAS_AFS) */ case N_NFS: if (!v->v_data || readrnode((KA_T)v->v_data, &r)) { (void) snpf(Namech, Namechl, "vnode at %s: can't read rnode (%s)", print_kptr(va, tbuf, sizeof(tbuf)), print_kptr((KA_T)v->v_data, (char *)NULL, 0)); enter_nm(Namech); return; } break; case N_REGLR: default: /* * VBLK, VCHR and VFIFO vnodes point to an snode. The snode's s_realvp * usually points to a real vnode, which points to an inode. */ if (v->v_type == VBLK || v->v_type == VCHR || v->v_type == VFIFO) { if (!v->v_data || readsnode((KA_T)v->v_data, &s)) { (void) snpf(Namech, Namechl, "vnode at %s: can't read snode(%s)", print_kptr(va, tbuf, sizeof(tbuf)), print_kptr((KA_T)v->v_data, (char *)NULL, 0)); enter_nm(Namech); return; } if (s.s_realvp) { if (readvnode((KA_T)s.s_realvp, &rv)) { (void) snpf(Namech, Namechl, "snode at %s: can't read real vnode (%s)", print_kptr((KA_T)v->v_data, tbuf, sizeof(tbuf)), print_kptr((KA_T)s.s_realvp, (char *)NULL, 0)); enter_nm(Namech); return; } if (!rv.v_data || readinode((KA_T)rv.v_data, &i)) { (void) snpf(Namech, Namechl, "snode at %s: can't read inode (%s)", print_kptr((KA_T)v->v_data, tbuf, sizeof(tbuf)), print_kptr((KA_T)rv.v_data, (char *)NULL, 0)); enter_nm(Namech); return; } ins = 1; } break; } else { if (!v->v_data || readinode((KA_T)v->v_data, &i)) { (void) snpf(Namech, Namechl, "vnode at %s: can't read inode (%s)", print_kptr(va, tbuf, sizeof(tbuf)), print_kptr((KA_T)v->v_data, (char *)NULL, 0)); enter_nm(Namech); return; } ins = 1; } } /* * Get device and type for printing. */ switch (Ntype) { #if defined(HAS_AFS) case N_AFS: dev = an.dev; devs = 1; break; #endif /* defined(HAS_AFS) */ case N_NFS: dev = r.r_attr.va_fsid; devs = 1; if (dev & 0x8000) dev |= 0xff00; break; case N_FIFO: case N_REGLR: if (ins) { dev = i.i_dev; devs = 1; } if ((v->v_type == VBLK) || (v->v_type == VCHR)) { rdev = v->v_rdev; rdevs = 1; } } type = v->v_type; /* * Obtain the inode number. */ switch(Ntype) { #if defined(HAS_AFS) case N_AFS: if (an.ino_st) { Lf->inode = (INODETYPE)an.inode; Lf->inp_ty = 1; } break; #endif /* defined(HAS_AFS) */ case N_NFS: Lf->inode = (INODETYPE)r.r_attr.va_nodeid; Lf->inp_ty = 1; break; case N_FIFO: case N_REGLR: if (ins) { Lf->inode = (INODETYPE)i.i_number; Lf->inp_ty = 1; } } /* * Obtain the file size. */ if (Foffset) Lf->off_def = 1; else { switch (Ntype) { #if defined(HAS_AFS) case N_AFS: Lf->sz = (SZOFFTYPE)an.size; Lf->sz_def = 1; break; #endif /* defined(HAS_AFS) */ case N_NFS: Lf->sz = (SZOFFTYPE)r.r_attr.va_size; Lf->sz_def = 1; break; case N_FIFO: Lf->off_def = 1; break; case N_REGLR: if (type == VREG || type == VDIR) { if (ins) { Lf->sz = (SZOFFTYPE)i.i_size; Lf->sz_def = 1; } } else if ((type == VCHR || type == VBLK) && !Fsize) Lf->off_def = 1; break; } } /* * Record the link count. */ if (Fnlink) { #if defined(HAS_AFS) case N_AFS: Lf->nlink = an.nlink; Lf->nlink_def = an.nlink_st; break; #endif /* defined(HAS_AFS) */ switch (Ntype) { case N_NFS: Lf->nlink = (long)r.r_attr.va_nlink; Lf->nlink_def = 1; break; case N_REGLR: if (ins) { Lf->nlink = (long)i.i_nlink; Lf->nlink_def = 1; } break; } if (Lf->nlink_def && Nlink && (Lf->nlink < Nlink)) Lf->sf |= SELNLINK; } /* * Record an NFS file selection. */ if (Ntype == N_NFS && Fnfs) Lf->sf |= SELNFS; /* * Defer file system info lookup until printname(). */ Lf->lmi_srch = 1; /* * Save the device numbers and their states. * * Format the vnode type. */ Lf->dev = dev; Lf->dev_def = devs; Lf->rdev = rdev; Lf->rdev_def = rdevs; switch (type) { case VNON: ty ="VNON"; break; case VREG: case VDIR: ty = (type == VREG) ? "VREG" : "VDIR"; break; case VBLK: ty = "VBLK"; Ntype = N_BLK; break; case VCHR: ty = "VCHR"; Ntype = N_CHR; break; case VLNK: ty = "VLNK"; break; #if defined(VSOCK) case VSOCK: ty = "SOCK"; break; #endif case VBAD: ty = "VBAD"; break; case VFIFO: ty = "FIFO"; break; default: (void) snpf(Lf->type, sizeof(Lf->type), "%04o", (type & 0xfff)); ty = NULL; } if (ty) (void) snpf(Lf->type, sizeof(Lf->type), ty); Lf->ntype = Ntype; /* * If this is a VBLK file and it's missing an inode number, try to * supply one. */ if ((Lf->inp_ty == 0) && (Lf->ntype == N_BLK)) find_bl_ino(); /* * If this is a VCHR file and it's missing an inode number, try to * supply one. */ if ((Lf->inp_ty == 0) && (Lf->ntype == N_CHR)) find_ch_ino(); /* * Test for specified file. */ if (Sfile && is_file_named((char *)NULL, ((type == VCHR) || (type == VBLK)) ? 1 : 0)) Lf->sf |= SELNM; /* * Enter name characters. */ if (Namech[0]) enter_nm(Namech); }