/*
 * dnode.c - BSDI 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.1 96/01/12 10:49:43 abe Exp $";
#endif


#include "lsof.h"


_PROTOTYPE(static void process_lock,(struct lockf *lp));

#if	defined(HASPROCFS)
_PROTOTYPE(static void getmemsz,(pid_t pid));


/*
 * getmemsz() - get memory size of a /proc/<n>/mem entry
 */

static void
getmemsz(pid)
	pid_t pid;
{
	int n;
	struct kinfo_proc *p;
	struct vmspace vm;

	for (n = 0, p = P; n < Np; n++, p++) {
		if (p->P_PID == pid) {
			if (p->P_VMSPACE == NULL
			||  kread((KA_T)p->P_VMSPACE, (char *)&vm, sizeof(vm)))
				return;
			Lf->sz = (unsigned long)ctob(vm.vm_tsize + vm.vm_dsize
						     + vm.vm_ssize);
			Lf->sz_def = 1;
			return;
		}
	}
}
#endif	/* defined(HASPROCFS) */


/*
 * process_lock() - process node lock information
 */

static void
process_lock(lp)
	struct lockf *lp;		/* kernel lockf structure */
{
	struct lockf lf;
	int lt;

	if (!lp || kread((KA_T)lp, (char *)&lf, sizeof(lf)))
		return;
	if (lf.lf_start == (off_t)0 && lf.lf_end == (off_t)-1)
		lt = 1;
	else
		lt = 0;
	if (lf.lf_type == F_RDLCK)
		Lf->lock = lt ? 'R' : 'r';
	else if (lf.lf_type == F_WRLCK)
		Lf->lock = lt ? 'W' : 'w';
	else if (lf.lf_type == (F_RDLCK | F_WRLCK))
		Lf->lock = 'u';
}


/*
 * process_node() - process vnode
 */

void
process_node(va)
	caddr_t va;			/* vnode kernel space address */
{
	struct iso_node cd;
	u_int cn;
	dev_t dev;
	struct denode dn;
	char dev_ch[32];
	int lt;
	struct inode i;
	struct mfsnode m;
	struct nfsnode n;
	char *ty;
	enum vtype type;
	struct vnode *v, vb;
	struct l_vfs *vfs;

#if	defined(HASPROCFS)
	enum nodetype {NONODE, INODE, MFSNODE, NFSNODE, CDFSNODE, PCFSNODE,
		PFSNODE} nty = NONODE;
	struct pfsnode p;
	struct procfsid *pfi;
#else	/* !defined(HASPROCFS) */
	enum nodetype {NONODE, INODE, MFSNODE, NFSNODE, CDFSNODE, PCFSNODE}
		nty = NONODE;
#endif	/* defined(HASPROCFS) */

/*
 * Read the vnode.
 */
	if ( ! va) {
		enter_nm("no vnode address");
		return;
	}
	v = &vb;
	if (readvnode((caddr_t)va, v)) {
                enter_nm(Namech);
                return;
        }

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

/*
 * Get the vnode type.
 */
	if (v->v_mount == NULL)
		vfs = NULL;
	else {
		vfs = readvfs(v->v_mount);
		if (vfs) {
			if (vfs->type == MOUNT_NFS)
				Ntype = N_NFS;
			else if (vfs->type == MOUNT_CD9660)
				Ntype = N_CDFS;
			else if (vfs->type == MOUNT_MSDOS)
				Ntype = N_PCFS;

#if	defined(HASPROCFS)
			else if (vfs->type == MOUNT_PROCFS)
				Ntype = N_PROC;
#endif	/* defined(HASPROCFS) */

		}
	}
	if (Ntype == N_REGLR) {
		switch (v->v_type) {
		case VFIFO:
			Ntype = N_FIFO;
			break;
		}
	}
/*
 * Read the successor node.
 */
	switch (v->v_tag) {
	case VT_ISOFS:
		if (kread((KA_T)v->v_data, (char *)&cd, sizeof(cd)) != 0) {
			(void) sprintf(Namech, "can't read cd9660_node at: %x",
				v->v_data);
			enter_nm(Namech);
			return;
		}
		nty = CDFSNODE;
		(void) process_lock(cd.i_lockf);
		break;
	case VT_MFS:
		if (kread((KA_T)v->v_data, (char *)&m, sizeof(m)) != 0) {
			(void) sprintf(Namech, "can't read mfsnode at: %x",
				v->v_data);
			enter_nm(Namech);
			return;
		}
		nty = MFSNODE;
		break;
	case VT_MSDOSFS:
		if (kread((KA_T)v->v_data, (char *)&dn, sizeof(dn)) != 0) {
			(void) sprintf(Namech, "can't read denode at: %x",
				v->v_data);
			enter_nm(Namech);
			return;
		}
		nty = PCFSNODE;
		(void) process_lock(dn.de_lockf);
		break;
	case VT_NFS:
		if (kread((KA_T)v->v_data, (char *)&n, sizeof(n)) != 0) {
			(void) sprintf(Namech, "can't read nfsnode at: %x",
				v->v_data);
			enter_nm(Namech);
			return;
		}
		nty = NFSNODE;
		break;

#if	defined(HASPROCFS)
	case VT_PROCFS:
		if (kread((KA_T)v->v_data, (char *)&p, sizeof(p)) != 0) {
			(void) sprintf(Namech, "can't read pfsnode at: %x",
				v->v_data);
			enter_nm(Namech);
			return;
		}
		nty = PFSNODE;
		break;
#endif	/* defined(HASPROCFS) */

	case VT_UFS:
		if (kread((KA_T)v->v_data, (char *)&i, sizeof(i)) != 0) {
			(void) sprintf(Namech, "can't read inode at: %x",
				v->v_data);
			enter_nm(Namech);
			return;
		}
		nty = INODE;
		(void) process_lock(i.i_lockf);
		break;
	}
/*
 * Get device and type for printing.
 */
	type = v->v_type;
	switch (nty) {
	case INODE:
		dev = (type == VCHR) ? i.i_din.di_rdev : i.i_dev;
		break;
	case NFSNODE:
		dev = n.n_vattr.va_fsid;
		break;
	case CDFSNODE:
		dev = cd.i_dev;
		break;
	case PCFSNODE:
		dev = dn.de_dev;
	}
/*
 * Obtain the inode number.
 */
	switch (nty) {
	case INODE:
		Lf->inode = (unsigned long)i.i_number;
		Lf->inp_ty = 1;
		break;
	case NFSNODE:
		Lf->inode = (unsigned long)n.n_vattr.va_fileid;
		Lf->inp_ty = 1;
		break;
	case CDFSNODE:
		Lf->inode = (unsigned long)cd.i_number;
		Lf->inp_ty = 1;
		break;
	case PCFSNODE:
		if (dn.de_Attributes & ATTR_DIRECTORY) {
			if ((cn = dn.de_StartCluster) == MSDOSFSROOT)
				cn = 1;
		} else {
			if ((cn = dn.de_dirclust) == MSDOSFSROOT)
				cn = 1;
			cn = (cn << 16) | (dn.de_diroffset & 0xffff);
		}
		Lf->inode = (unsigned long)cn;
		Lf->inp_ty = 1;
		break;

#if	defined(HASPROCFS)
	case PFSNODE:
		Lf->inode = (unsigned long)p.pfs_fileno;
		Lf->inp_ty = 1;
		break;
#endif	/* defined(HASPROCFS) */

	}

/*
 * Obtain the file size.
 */
	if (Foffset)
		Lf->off_def = 1;
	else {
		switch (Ntype) {
		case N_CDFS:
			if (nty == CDFSNODE) {
				Lf->sz = (unsigned long)cd.i_size;
				Lf->sz_def = 1;
			}
			break;
		case N_FIFO:
			if (!Fsize)
				Lf->off_def = 1;
			break;
		case N_PCFS:
			if (nty == PCFSNODE) {
				Lf->sz = (unsigned long)dn.de_FileSize;
				Lf->sz_def = 1;
			}
			break;
		case N_NFS:
			if (nty == NFSNODE) {
				Lf->sz = (unsigned long)n.n_vattr.va_size;
				Lf->sz_def = 1;
			}
			break;

#if	defined(HASPROCFS)
		case N_PROC:
			if (nty == PFSNODE) {
				switch (p.pfs_type) {
				case Proot:
				case Pproc:
					Lf->sz = (unsigned long)DEV_BSIZE;
					Lf->sz_def = 1;
					break;
				case Pmem:
					(void) getmemsz(p.pfs_pid);
					break;
				}
			}
			break;
#endif	/* defined(HASPROCFS) */

		case N_REGLR:
			if (type == VREG || type == VDIR) {
				if (nty == INODE) {
					Lf->sz = (unsigned long)i.i_din.di_size;
					Lf->sz_def = 1;
				} else if (nty == MFSNODE) {
					Lf->sz = (unsigned long)m.mfs_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;
	}
/*
 * 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	/* defined(VSOCK) */

	case VBAD:
		ty = "VBAD";
		break;
	case VFIFO:
		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);
/*
 * Handle some special cases:
 *
 * 	ioctl(fd, TIOCNOTTY) files;
 *	memory node files;
 *	/proc files.
 */

	if (type == VBAD)
		(void) strcpy(Namech, "(revoked)");
	else if (nty == MFSNODE) {
		Lf->dev_def = 0;
		(void) sprintf(Namech, "%#x", m.mfs_baseoff);
		(void) strcpy(dev_ch, "    memory");
		enter_dev_ch(dev_ch);
	}

#if	defined(HASPROCFS)
	else if (nty == PFSNODE) {
		Lf->dev_def = 0;
		ty = NULL;
		(void) sprintf(Namech, "/%s", HASPROCFS);
		switch (p.pfs_type) {
		case Proot:
			ty = "PDIR";
			break;
		case Pproc:
			(void) sprintf(endnm(), "/%d", p.pfs_pid);
			ty = "PDIR";
			break;
		case Pfile:
			(void) sprintf(endnm(), "/%d/file", p.pfs_pid);
			ty = "PFIL";
			break;
		case Pmem:
			(void) sprintf(endnm(), "/%d/mem", p.pfs_pid);
			ty = "PMEM";
			break;
		case Pregs:
			(void) sprintf(endnm(), "/%d/regs", p.pfs_pid);
			ty = "PREG";
			break;
		case Pfpregs:
			(void) sprintf(endnm(), "/%d/fpregs", p.pfs_pid);
			ty = "PFPR";
			break;
		case Pctl:
			(void) sprintf(endnm(), "/%d/ctl", p.pfs_pid);
			ty = "PCTL";
			break;
		case Pstatus:
			(void) sprintf(endnm(), "/%d/status", p.pfs_pid);
			ty = "PSTA";
			break;
		case Pnote:
			(void) sprintf(endnm(), "/%d/note", p.pfs_pid);
			ty = "PNTF";
			break;
		case Pnotepg:
			(void) sprintf(endnm(), "/%d/notepg", p.pfs_pid);
			ty = "PGRP";
			break;
		}
		if (ty)
			(void) strcpy(Lf->type, ty);
		enter_nm(Namech);
	}
#endif	/* defined(HASPROCFS) */

/*
 * 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	defined(HASPROCFS)
	if (Ntype == N_PROC) {
		if (Procsrch)
			Lf->sf |= SELNM;
		else if (nty == PFSNODE) {
			for (pfi = Procfsid; pfi; pfi = pfi->next) {

# if	defined(HASPINODEN)
				if (pfi->inode == p.pfs_fileno)
# else	/* !defined(HASPINODEN) */
				if (pfi->pid == p.pfs_pid)
# endif	/* defined(HASPINODEN) */

				{
					Lf->sf |= SELNM;
					break;
				}
			}
		}
	} else
#endif	/* defined(HASPROCFS) */

	{
		if (Sfile && is_file_named(NULL, type))
			Lf->sf |= SELNM;
	}
/*
 * Enter name characters.
 */
	if (Namech[0])
		enter_nm(Namech);
}
