/*
 * dnode.c - HP-UX 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.14 96/03/08 14:03:27 abe Exp $";
#endif


#include "lsof.h"
#include <sys/inode.h>

#if	_HPUXV>=1000
_PROTOTYPE(static void enter_nma,(char *b));
#endif	/* _HPUXV>=1000 */

_PROTOTYPE(static int readinode,(struct inode *ia, struct inode *i));


#if	_HPUXV>=1000
/*
 * enter_nma() - enter NAME column addition
 */

static void
enter_nma(b)
	char *b;			/* addition buffer */
{
	char *cp;
	MALLOC_S len;

	if (Lf->nma)
		return;
	if ((len = (MALLOC_S)(strlen(b) + 1)) < 2)
		return;
	if ((cp = (char *)malloc(len)) == (char *)NULL)
		return;
	(void) strcpy(cp, b);
	Lf->nma = cp;
}
#endif	/* _HPUXV>=1000 */


/*
 * process_node() - process vnode
 */

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

#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) */

#if	_HPUXV>=1000
	struct cdnode c;
	struct fifonode f;
	char fb[128];
	struct vnode rv;
	struct snode s;
	struct vattr vat;
	int vats = 0;
#endif	/* _HPUXV>=1000 */

/*
 * 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);
		}
	}
	if (readvnode((caddr_t)va, v)) {
                enter_nm(Namech);
                return;
        }

#if	defined(HASNCACHE)
	Lf->na = (unsigned long)va;
#endif	/* defined(HASNCACHE) */

/*
 * Determine the vnode type.
 */
	if (Nl[X_NFS_OPS].n_value
	&& (unsigned long)v->v_op == Nl[X_NFS_OPS].n_value)
		Ntype = N_NFS;

#if	defined(HASVXFS)
	else if (Nl[X_VXFS_OPS].n_value
	&& (unsigned long)v->v_op == Nl[X_VXFS_OPS].n_value)
		Ntype = N_VXFS;
#endif	/* defined(HASVXFS) */

#if	_HPUXV>=1000
	else if (Nl[X_CDFS_OPS].n_value
	&& (unsigned long)v->v_op == Nl[X_CDFS_OPS].n_value)
		Ntype = N_CDFS;
	else if (Nl[X_FIFO_OPS].n_value
	&& (unsigned long)v->v_op == Nl[X_FIFO_OPS].n_value)
		Ntype = N_FIFO;
	else if (Nl[X_PIPE_OPS].n_value
	&& (unsigned long)v->v_op == Nl[X_PIPE_OPS].n_value)
		Ntype = N_PIPE;
	else if (Nl[X_SPEC_OPS].n_value
	&& (unsigned long)v->v_op == Nl[X_SPEC_OPS].n_value)
		Ntype = N_SPEC;
#else	/* _HPUXV<1000 */
	else if (v->v_type == VFIFO)
		Ntype = N_FIFO;
#endif	/* _HPUXV<1000 */

#if	defined(HAS_AFS)
	/*
	 * Caution: this AFS test should be the last one.
	 */
	
	else if (Nl[X_AFS_OPS].n_value) {
		if ((unsigned long)v->v_op == Nl[X_AFS_OPS].n_value)
			Ntype = N_AFS;
		else
			Ntype = N_REGLR;
	} else if (v->v_data || !v->v_vfsp)
		Ntype = N_REGLR;
	else {
		switch (afs) {
		case -1:
			Ntype = N_REGLR;
			break;
		case 0:
			if (!hasAFS(v)) {
				afs = -1;
				Ntype = N_REGLR;
			}
			afs = 1;
			Ntype = N_AFS;
			break;
		case 1:
			Ntype = (v->v_vfsp == AFSVfsp) ? N_AFS : N_REGLR;
		}
	}
#else	/* !defined(HAS_AFS) */
	else
		Ntype = N_REGLR;
#endif	/* defined(HAS_AFS) */

/*
 * Determine lock type.
 */
	if (v->v_shlockc || v->v_exlockc) {
		if (v->v_shlockc && v->v_exlockc)
			Lf->lock = 'u';
		else if (v->v_shlockc)
			Lf->lock = 'R';
		else
			Lf->lock = 'W';
	}
/*
 * Establish the local virtual file system structure.
 */
	if ( ! v->v_vfsp)
		vfs = NULL;
	else if ((vfs = readvfs(la, v)) == NULL) {
                (void) sprintf(Namech, "can't read vfs for %#x at %#x", va,
			v->v_vfsp);
                enter_nm(Namech);
		return;
	}
/*
 * Read the cdnode, fifonode, inode, rnode, snode, or vache struct.
 */
	switch (Ntype) {

#if	defined(HAS_AFS)
	case N_AFS:
		if (readafsnode(va, v, &an))
			return;
		break;
#endif	/* defined(HAS_AFS) */

#if	defined(HASVXFS)
	case N_VXFS:
		if (!v->v_data || read_vxnode(v, vfs, &dev)) {
			(void) sprintf(Namech,
				"vnode at %#x: can't read vx_inode (%#x)",
				va, v->v_data);
			enter_nm(Namech);
			return;
		}
#endif	/* defined(HASVXFS) */

#if	_HPUXV>=1000
	case N_CDFS:
		if (!v->v_data
		||  kread((KA_T)v->v_data, (char *)&c, sizeof(c))) {
			(void) sprintf(Namech,
				"vnode at %#x: can't read cdnode (%#x)",
				va, v->v_data);
			enter_nm(Namech);
			return;
		}
		break;
	case N_FIFO:
	case N_PIPE:
		if (!v->v_data
		||  kread((KA_T)v->v_data, (char *)&f, sizeof(f))) {
			(void) sprintf(Namech,
				"vnode at %#x: can't read fifonode (%#x)",
				va, v->v_data);
			enter_nm(Namech);
			return;
		}
		if (f.fn_vap
		&&  kread((KA_T)f.fn_vap, (char *)&vat, sizeof(vat)) == 0)
			vats = 1;
		break;
#endif	/* _HPUXV>=1000 */

	case N_NFS:
		if (!v->v_data || readrnode((caddr_t)v->v_data, &r)) {
			(void) sprintf(Namech,
				"vnode at %#x: can't read rnode (%#x)",
				va, v->v_data);
			enter_nm(Namech);
			return;
		}
		break;

#if	_HPUXV>=1000
	case N_SPEC:
		if (v->v_type == VCHR) {
			if (!v->v_data || readsnode(v->v_data, &s)) {
				(void) sprintf(Namech,
					"vnode at %#x: can't read snode(%#x)",
					va, v->v_data);
				enter_nm(Namech);
				return;
			}
			if ( ! s.s_realvp
			||  readvnode((caddr_t)s.s_realvp, &rv)) {
				(void) sprintf(Namech,
				    "snode at %#x: can't read real vnode (%#x)",
				    v->v_data, s.s_realvp);
				enter_nm(Namech);
				return;
			}
			if (!rv.v_data
			||  readinode((struct inode *)rv.v_data, &i)) {
				(void) sprintf(Namech,
					"snode at %#x: can't read inode (%#x)",
					v->v_data, rv.v_data);
				enter_nm(Namech);
				return;
			}
			break;
		}
		if (!v->v_data || readinode((struct inode *)v->v_data, &i)) {
			(void) sprintf(Namech,
				"vnode at %#x: can't read inode (%#x)",
				va, v->v_data);
			enter_nm(Namech);
			return;
		}
		break;
#endif	/* _HPUXV>=1000 */

	case N_REGLR:
	default:
		if (!v->v_data || readinode((struct inode *)v->v_data, &i)) {
			(void) sprintf(Namech,
				"vnode at %#x: can't read inode (%#x)",
				va, v->v_data);
			enter_nm(Namech);
			return;
		}
	}
/*
 * Get device and type for printing.
 */
	switch (Ntype) {

#if	defined(HAS_AFS)
	case N_AFS:
		dev = an.dev;
		break;
#endif	/* defined(HAS_AFS) */

	case N_NFS:
		dev = vfs ? vfs->dev : 0;
		break;

#if	_HPUXV>=1000
	case N_CDFS:
		dev = c.cd_dev;
		break;
	case N_FIFO:
	case N_PIPE:
		if (vfs && vfs->fsname)
			dev = vfs->dev;
		else {
			(void) sprintf(dev_ch, "0x%08x", va);
			enter_dev_ch(dev_ch);
		}
		break;
#endif	/* _HPUX>=1000 */

#if	defined(HASVXFS)
	case N_VXFS:
		/* obtained via read_vxnode */
		break;
#endif	/* defined(HASVXFS) */

	case N_SPEC:
	default:

#if	_HPUXV>=800
		if (v->v_type == VCHR)
			dev = v->v_rdev;
		else if (vfs && vfs->fsname)
			dev = vfs->dev;
		else
			dev = i.i_dev;
#else	/* _HPUXV<800 */
		dev = (v->v_type == VCHR) ? v->v_rdev : i.i_dev;
#endif	/* _HPUXV>=800 */

	}

	type = v->v_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_nfsattr.na_nodeid;
		Lf->inp_ty = 1;
		break;

#if	_HPUXV>=1000
	case N_CDFS:
		Lf->inode = (unsigned long)c.cd_num;
		break;
	case N_FIFO:
	case N_PIPE:
		if (vats) {
			Lf->inode = (unsigned long)vat.va_nodeid;
			Lf->inp_ty = 1;
		} else {
			Lf->inode = (unsigned long)v->v_nodeid;
			Lf->inp_ty = 1;
		}
		break;

#else	/* _HPUXV<1000 */
	case N_FIFO:
#endif	/* _HPUXV>=1000 */

#if	defined(HASVXFS)
	case N_VXFS:
		/* set in read_vxnode() */
		break;
#endif	/* defined(HASVXFS) */

	case N_REGLR:
	case N_SPEC:
		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	_HPUXV>=1000
		case N_CDFS:
			Lf->sz = (unsigned long)c.cd_cdc.cdc_size;
			Lf->sz_def = 1;
			break;
		case N_FIFO:
			if (Fsize || (Lf->access != 'r' && Lf->access != 'w')) {
				(void) sprintf(fb, "rd=%#x; wr=%#x",
					f.fn_rptr, f.fn_wptr);
				(void) enter_nma(fb);
				Lf->sz = (unsigned long)f.fn_size;
				Lf->sz_def = 1;
				break;
			}
			Lf->off = (unsigned long)
				  (Lf->access == 'r') ? f.fn_rptr : f.fn_wptr;
			(void) sprintf(fb, "%s=%#x",
				(Lf->access == 'r') ? "wr" : "rd",
				(Lf->access == 'r') ? f.fn_wptr : f.fn_rptr);
			(void) enter_nma(fb);
			Lf->off_def = 1;
			break;
		case N_PIPE:
			if (vats) {
				Lf->sz = (unsigned long)vat.va_size;
				Lf->sz_def = 1;
			}
			break;
#endif	/* _HPUXV>=1000 */

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

#if	defined(HASVXFS)
		case N_VXFS:
			/* set in read_vxnode() */
			break;
#endif	/* defined(HASVXFS) */

		case N_SPEC:
		case N_REGLR:
			if (type == VREG || type == VDIR) {
				Lf->sz = (unsigned long)i.i_size;
				Lf->sz_def = 1;
			} else if (type == VCHR && !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;

#if	defined(HASFSINO)
		Lf->fs_ino = vfs->fs_ino;
#endif	/* defined(HASFSINO) */

	}
/*
 * Format the vnode type, and possibly the device name.
 */
	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:
		switch (Ntype) {

#if	_HPUXV>=1000
		case N_FIFO:
			if (vfs && vfs->fsname) {
				Lf->dev = dev;
				Lf->dev_def = 1;
			}
			ty = "FIFO";
			break;
		case N_PIPE:
			if (vfs && vfs->fsname) {
				Lf->dev = dev;
				Lf->dev_def = 1;
			}
			ty = "PIPE";
			break;
#endif	/* _HPUXV>=1000 */

		default:
			Lf->dev = dev;
			Lf->dev_def = 1;
			ty = "FIFO";
		}
		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))
		Lf->sf |= SELNM;
/*
 * Enter name characters.
 */
	if (Namech[0])
		enter_nm(Namech);
}



/*
 * readinode() - read inode
 */

static int
readinode(ia, i)
	struct inode *ia;		/* inode kernel address */
	struct inode *i;		/* inode buffer */
{
	if (kread((KA_T)ia, (char *)i, sizeof(struct inode))) {
		(void) sprintf(Namech, "can't read inode at %#x", ia);
		return(1);
	}
	return(0);
}
