/*
 * dnode.c - AIX node reading 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.11 96/03/08 16:23:37 abe Exp $";
#endif


#include "lsof.h"


/*
 * isglocked() - is a gnode locked
 */

char
isglocked(ga)
	struct gnode *ga;		/* local gnode address */
{

	struct filock f;
	off_t l;

	if (ga->gn_filocks == NULL)
		return(' ');
	if (kread((KA_T)ga->gn_filocks, (char *)&f, sizeof(f)))
		return(' ');
	if (f.set.l_whence == 0 && f.set.l_start == 0 && f.set.l_len == 0)
		l = 1;
	else
		l = 0;
	switch (f.set.l_type & (F_RDLCK | F_WRLCK)) {

	case F_RDLCK:
		return((l) ? 'R' : 'r');
	case F_WRLCK:
		return((l) ? 'W' : 'w');
	case (F_RDLCK + F_WRLCK):
		return('u');
	}
	return(' ');
}


/*
 * process_node() - process vnode
 */

void
process_node(va)
	caddr_t va;			/* vnode kernel space address */
{
	struct cdrnode c;
	dev_t dev;
	char dev_ch[32];
	struct gnode g;
	struct inode i;
	struct vfs *la = NULL;
	struct rnode r;
	char *ty;
	enum vtype type;
	struct l_vfs *vfs;
	static struct vnode *v = (struct vnode *)NULL;

#if	_AIXV>=3200
	struct devnode dn;
	struct gnode pg;
	struct specnode sn;
	struct fifonode f;
#endif	/* _AIXV>=3200 */

#if	defined(HAS_AFS)
	static int afs = 0;		/* AFS test status: -1 = no AFS
					 *		     0 = not tested
					 *		     1 = AFS present */
	struct afsnode an;
#endif	/* defined(HAS_AFS) */

/*
 * Read the vnode.
 */
	if (!va) {
		enter_nm("no vnode address");
		return;
	}
	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);
		}
	}
/*
 * Read the vnode.
 */
	if (readvnode((caddr_t)va, v)) {
                enter_nm(Namech);
                return;
        }
/*
 * Read the gnode.
 */
	if (!v->v_gnode || readgnode(v->v_gnode, &g)) {
		(void) sprintf(Namech, "vnode at %#x has no gnode\n", va);
		enter_nm(Namech);
		return;
	}

#if	_AIXV>=3200

/*
 * Under AIX 3.2, if the vnode type is ISVDEV, then there is a special
 * node and a fifonode or devnode.  Behind them are the "real" gnode,
 * inode and vnode.
 */
	if (ISVDEV(g.gn_type)) {
		switch (g.gn_type) {
		case VBLK:
			Ntype = N_BLK;
			break;
		case VCHR:
			Ntype = N_CHR;
			break;
		case VFIFO:
			Ntype = N_FIFO;
			break;
		case VMPC:
			Ntype = N_MPC;
			break;
		default:
			(void) sprintf(Namech,
				"vnode at %#x: unknown ISVDEV(%#x)",
				va, g.gn_type);
			enter_nm(Namech);
			return;
		}
	/*
	 * Read the special node.
	 */
		if (!g.gn_data
		|| kread((KA_T)g.gn_data, (char *)&sn, sizeof(sn))
		!= 0) {
			(void) sprintf(Namech,
				"vnode at %#x: can't read specnode (%#x)",
				va, g.gn_data);
			enter_nm(Namech);
			return;
		}
	/*
	 * Read the PFS gnode and its inode and vnode.
	 */
		if (sn.sn_pfsgnode) {
			if (readgnode(sn.sn_pfsgnode, &g)) {
				(void) sprintf(Namech,
				    "vnode at %#x: can't read pfsgnode (%#x)",
				    va, sn.sn_pfsgnode);
				enter_nm(Namech);
				return;
			}
			if (!g.gn_data || readinode(g.gn_data, &i)) {
				(void) sprintf(Namech,
				    "pfsgnode at %#x: can't read inode (%#x)",
				    sn.sn_pfsgnode, g.gn_data);
				enter_nm(Namech);
				return;
			}
			if (!g.gn_vnode || readvnode((caddr_t)g.gn_vnode, v)) {
				(void) sprintf(Namech,
				    "pfsgnode at %#x: can't read vnode (%#x)",
				    sn.sn_pfsgnode, g.gn_vnode);
				enter_nm(Namech);
				return;
			}
		} else {
			(void) zeromem((char *)&i, sizeof(i));
			i.i_number = -1l;
		}
	/*
	 * If it's a FIFO, read its fifonode.
	 */
		if (Ntype == N_FIFO) {
			if (!sn.sn_fifonode
			|| readfifonode(sn.sn_fifonode, &f)) {
				(void) sprintf(Namech,
				    "vnode at %#x: can't read fifonode (%#x)",
				    va, sn.sn_fifonode);
				enter_nm(Namech);
				return;
			}
	/*
	 * Otherwise, read the devnode and its gnode.
	 */
		} else {
			if (!sn.sn_devnode
			|| kread((KA_T)sn.sn_devnode,(char *)&dn,sizeof(dn))) {
				(void) sprintf(Namech,
				    "vnode at %#x: can't read devnode (%#x)",
				    va, sn.sn_devnode);
				enter_nm(Namech);
				return;
			}
			g = dn.dv_gnode;
		}
	}
#endif	/* _AIXV>=3200 */

/*
 * Read the AIX virtual file system structure.
 */
	if (Ntype != N_AFS && g.gn_rdev == NODEVICE) {
		vfs = NULL;
		(void) sprintf(dev_ch, "0x%08x", va);
		enter_dev_ch(dev_ch);
	} else {
		if ((vfs = readvfs(v)) == NULL) {
			(void) sprintf(Namech,
				"can't read vfs for %#x at %#x",
				va, v->v_vfsp);
			enter_nm(Namech);
			return;
		}
	}
/*
 * Set the type for an NFS vnode.
 * Get the lock status.
 */
	if (vfs && vfs->vmt_flags & MNT_REMOTE)
		Ntype = N_NFS;
	Lf->lock = isglocked(&g);
	switch (Ntype) {
/*
 * Read an NFS rnode.
 */
	case N_NFS:
		if (g.gn_data == NULL || readrnode(g.gn_data, &r)) {
			(void) sprintf(Namech,
				"remote gnode at %#x has no rnode", v->v_gnode);
			enter_nm(Namech);
			return;
		}
		break;
/*
 * Read N_REGLR nodes.
 */
	case N_REGLR:
		if (vfs && vfs->vmt_gfstype == MNT_CDROM) {

		/*
		 * Read a CD-ROM cdrnode.
		 */
			if (g.gn_data == NULL || readcdrnode(g.gn_data, &c)) {
				(void) sprintf(Namech,
					"gnode at %#x has no cdrnode",
					v->v_gnode);
				enter_nm(Namech);
				return;
			}
			(void) zeromem((char *)&i, sizeof(i));
			i.i_number = c.cn_inumber;
			i.i_size = (off_t)c.cn_size;
		/*
		 * Otherwise, read the inode.
		 */

		} else if (g.gn_data) {
			if (readinode(g.gn_data, &i)) {
				(void) sprintf(Namech,
					"gnode at %#x can't read inode: %#x",
					v->v_gnode, g.gn_data);
				enter_nm(Namech);
				return;
			}
		}

#if	defined(HAS_AFS)
		else {

		/*
		 * See if this is an AFS node.
		 */
			if (AFSVfsp && v->v_vfsp == AFSVfsp)
				Ntype = N_AFS;
			else if (v->v_vfsp) {
				switch (afs) {
				case -1:
					break;
				case 0:
					if (!hasAFS(v)) {
						afs = 1;
						break;
					}
					afs = 1;
					Ntype = N_AFS;
					break;
				case 1:
					if (v->v_vfsp == AFSVfsp)
						Ntype = N_AFS;
				}
			}
		/*
		 * If this is an AFS node, read the afsnode.
		 */
			if (Ntype == N_AFS) {
				if (readafsnode(va, v, &an))
					return;
			} else {
				(void) sprintf(Namech,
					"gnode at %#x has no inode",
					v->v_gnode);
				enter_nm(Namech);
				return;
			}
		}
#else	/* !defined(HAS_AFS) */
		else {
			(void) sprintf(Namech, "gnode at %#x has no inode",
				v->v_gnode);
			enter_nm(Namech);
			return;
		}
#endif	/* defined(HAS_AFS) */

	}
/*
 * Get device and type for printing.
 */
	if (Ntype == N_NFS)
		dev = vfs ? vfs->dev : 0;

#if	defined(HAS_AFS)
	else if (Ntype == N_AFS)
		dev = an.dev;
#endif	/* defined(HAS_AFS) */

	else
		dev = g.gn_rdev;

#if	_AIXV>=3200
	if (Ntype == N_MPC)
		type = VMPC;
	else
#endif	/* _AIXV>=3200 */

		type = g.gn_type;
/*
 * Obtain the inode number.
 */
	switch (Ntype) {

#if	defined(HAS_AFS)
	case N_AFS:
		if (an.ino_st) {
			Lf->inode = an.inode;
			Lf->inp_ty = 1;
		}
		break;
#endif	/* defined(HAS_AFS) */

	case N_NFS:
		Lf->inode = (unsigned long) r.r_attr.va_serialno;
		Lf->inp_ty = 1;
		break;

# if	_AIXV>=3200
	case N_BLK:
	case N_CHR:
	case N_MPC:
# endif	/* _AIXV>=3200 */

	case N_REGLR:
		Lf->inode = (unsigned long) 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 = an.size;
			Lf->sz_def = 1;
			break;
#endif	/* defined(HAS_AFS) */

#if	_AIXV>=3200
		case N_FIFO:
			Lf->sz = (unsigned long)f.ff_size;
			Lf->sz_def = 1;
			break;
#endif	/* _AIXV>=3200 */

		case N_NFS:
			Lf->sz = (unsigned long)r.r_attr.va_size;
			Lf->sz_def = 1;
			break;

#if	 _AIXV>=3200
		case N_BLK:
			Lf->sz = (unsigned long)i.i_size;
			Lf->sz_def = 1;
			break;
		case N_CHR:
		case N_MPC:
			if (!Fsize)
				Lf->off_def = 1;
			break;
#endif	/* _AIXV>=3200 */

		case N_REGLR:
			if (type == VREG || type == VDIR) {
				Lf->sz = (unsigned long)i.i_size;
				Lf->sz_def = 1;
			} else if ((type == VCHR || type == VMPC) && !Fsize)
				Lf->off_def = 1;
			break;
		}
	}
/*
 * Record an NFS file selection.
 */
	if (Ntype == N_NFS && Fnfs)
		Lf->sf |= SELNFS;
/*
 * Save the file system names.
 */
	if (vfs) {
		Lf->fsdir = vfs->dir;
		Lf->fsdev = vfs->fsname;
	}
/*
 * Format the vnode type.
 */
	switch (type) {

	case VNON:
		ty ="VNON";
		break;
	case VREG:
	case VDIR:
		ty = (type == VREG) ? "VREG" : "VDIR";
		Lf->dev_def = 1;
		Lf->dev = dev;
		break;
	case VBLK:
		ty = "VBLK";
		break;
	case VCHR:
		Lf->dev = dev;
		Lf->dev_def = Lf->is_chr_dev = 1;
		ty = "VCHR";
		break;
	case VLNK:
		ty = "VLNK";
		break;

#if	defined(VSOCK)
	case VSOCK:
		ty = "SOCK";
		break;
#endif

	case VBAD:
		ty = "VBAD";
		break;
	case VFIFO:
		if (!Lf->dev_ch || Lf->dev_ch[0] == '\0') {
			Lf->dev = dev;
			Lf->dev_def = 1;
		}
		ty = "FIFO";
		break;
	case VMPC:
		Lf->dev = g.gn_rdev;
		Lf->ch = g.gn_chan;

#if	_AIXV<3200
		Lf->inp_ty = 0;
#endif	/* _AIXV<3200 */

		Lf->dev_def = Lf->is_chr_dev = 1;
		ty = "VMPC";
		break;
	default:
		if (type > 9999)
			(void) sprintf(Lf->type, "*%03d", type % 1000);
		else
			(void) sprintf(Lf->type, "%4d", type);
		(void) strcpy(Namech, "unknown type");
		ty = NULL;
	}
	if (ty)
		(void) strcpy(Lf->type, ty);
/*
 * If this is a VCHR file and it's missing an inode number, try to
 * supply one.
 */
	if (Lf->inp_ty == 0 && type == VCHR && Lf->dev_def)
		find_ch_ino();
/*
 * Test for specified file.
 */
	if (Sfile && is_file_named(NULL, type, g.gn_chan))
		Lf->sf |= SELNM;
/*
 * Enter name characters.
 */
	if (Namech[0])
		enter_nm(Namech);
}
