blob: 67479429ac9a0ded77f502107927bf318248d1ee [file] [log] [blame]
/*
* 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 <ufs/lockf.h>
#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);
}