/*
 * dnode.c - DEC OSF/1 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.25 96/02/20 06:48:33 abe Exp $";
#endif


#include "lsof.h"


_PROTOTYPE(static int readvnode,(caddr_t va, struct vnode *v));
_PROTOTYPE(static void get_proc_sz,(struct procnode *pn));


/*
 * get_proc_sz() - get size of /proc file system file
 */

static void
get_proc_sz(pn)
	struct procnode *pn;		/* pointer to procnode */
{
	struct vm_map m;
	struct proc *p;
	KA_T pa;
	int px;
	struct task t;
/*
 * Search for procnode's process by PID.
 */
	for (p = Ps, px = 0; px < Psn; p++, px++) {
		if (p->p_pid == pn->prc_pid)
			break;
	}
	if (px >= Psn)
		return;
/*
 * Get the task structure address, then read the task structure.  Set
 * the procnode's file size from the memory map information in the task
 * structure.
 */

# if	_OSF1V<30000
	if ((pa = (KA_T)p->task) == NULL)
		return;
# else	/* _OSF1V>=30000 */
	if ((pa = Pa[px]) == NULL)
		return;
	pa = (KA_T)((char *)pa - sizeof(t));
# endif	/* _OSF1V<30000 */

	if (kread(pa, (char *)&t, sizeof(t)))
		return;
	if (t.map == NULL || kread((KA_T)t.map, (char *)&m, sizeof(m)))
		return;
	Lf->sz = (unsigned long)m.vm_size;
	Lf->sz_def = 1;
}


/*
 * process_node() - process vnode
 */

void
process_node(va)
	caddr_t va;			/* vnode kernel space address */
{
	struct advfsnode *a = NULL;
	struct cdnode *c = NULL;
	dev_t dev;
	struct inode *i = NULL;
	struct mfsnode *m = NULL;
	struct procnode *p = NULL;
	struct procfsid *pfi;
	struct rnode *r = NULL;
	struct spec_node *s = NULL;
	struct spec_node sn;
	struct s5inode *s5 = NULL;
	char *ty;
	enum vtype type;
	static struct vnode *v = NULL;
	struct l_vfs *vfs;

#if	_OSF1V>=30000
	char dev_ch[32];
	struct fifonode *f = NULL;
	struct fifonode fn;
#endif	/* _OSF1V>=30000 */

/*
 * Read the vnode.
 */
	if ( ! va) {
		enter_nm("no vnode address");
		return;
	}
	if (v == NULL) {

	/*
	 * Allocate space for the DEC Alpha OSF/1 vnode.
	 */
		if ((v = (struct vnode *)malloc(sizeof(struct vnode)-1+Vnmxp))
		== NULL) {
			(void) fprintf(stderr,
				"%s: no space for vnode buffer\n", Pn);
			exit(1);
		}
	}
	if (readvnode(va, v)) {
                enter_nm(Namech);
                return;
        }

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

/*
 * Get the mount structure and determine the vnode type.
 */
	if (v->v_mount == NULL)
		vfs = NULL;
	else
		vfs = readvfs(v->v_mount);
	if (vfs) {
		switch (vfs->type) {
		case MOUNT_NFS:
			Ntype = N_NFS;
			break;
		}
		if (Ntype == N_REGLR) {
			switch (v->v_type) {
			case VFIFO:
				Ntype = N_FIFO;
				break;
			}
		}
	}
/*
 * Determine the 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';
	}
/*
 * Define the specific DEC Alpha OSF/1 node pointer.
 */

#if	_OSF1V>=30000
	if (Ntype == N_FIFO) {
		if (v->v_fifonode
		&&  kread((KA_T)v->v_fifonode, (char *)&fn, sizeof(fn)) == 0) {
			f = &fn;
			vfs = NULL;
		}
	}
	if (f == NULL) {
#endif	/* _OSF1V>=30000 */

	    switch (v->v_tag) {
	    case VT_CDFS:
		c = (struct cdnode *)v->v_data;
		break;
	    case VT_MFS:
		m = (struct mfsnode *)v->v_data;
		break;
	    case VT_NFS:
		r = (struct rnode *)v->v_data;
		break;
	    case VT_NON:

#if     _OSF1V<20000
		if (v->v_specinfo
		&&  kread((KA_T)v->v_specinfo, (char *)&sn, sizeof(sn)) == 0)
			s = &sn;
		else
#endif  /* _OSF1V<20000 */

			s = (struct spec_node *)v->v_data;
		break;
	    case VT_PRFS:
		p = (struct procnode *)v->v_data;
		break;
	    case VT_S5FS:
		s5 = (struct s5inode *)v->v_data;
		break;
	    case VT_MSFS:
		a = (struct advfsnode *)v->v_data;
		break;
	    case VT_UFS:
		i = (struct inode *)v->v_data;
		break;
	    }

#if	_OSF1V>=30000
	}
#endif	/* _OSF1V>=30000 */

/*
 * Get device and type for printing.
 */
	type = v->v_type;
	if (a != NULL) {
		if (type == VCHR)
			dev = a->a_rdev;
		else if (vfs)
			dev = vfs->dev;
		else
			dev = 0;
	} else if (c != NULL)
		dev = c->cd_dev;
	else if (i != NULL)
		dev = (type == VCHR) ? i->i_din.di_db[0] : i->i_dev;
	else if (r != NULL)
		dev = r->r_attr.va_fsid;
	else if (s != NULL) {
		dev = s->sn_vattr.va_rdev;
		if ( ! lkupdev(&dev, 0) && Clonedev != -1)
			dev = makedev(major(Clonedev), major(dev));
	} else if (s5 != NULL)
		dev = s5->i_dev;
/*
 * Obtain the inode number.
 */
	if (a != NULL) {
		Lf->inode = (unsigned long)a->a_number;
		Lf->inp_ty = 1;
	} else if (c != NULL) {
		Lf->inode = (unsigned long)c->cd_number;
		Lf->inp_ty = 1;
	}

#if	_OSF1V>=30000
	else if (f != NULL) {
		Lf->inode = (unsigned long)f->fn_fileid;
		Lf->inp_ty = 1;
	}
#endif	/* _OSF1V>=30000 */

	else if (i != NULL) {
		Lf->inode = (unsigned long)i->i_number;
		Lf->inp_ty = 1;
	} else if (p != NULL) {
		Lf->inode = (unsigned long)((type == VDIR) ? PR_ROOTINO
					   : p->prc_pid + PR_INOBIAS);
		Lf->inp_ty = 1;
	} else if (r != NULL) {
		Lf->inode = (unsigned long)r->r_attr.va_fileid;
		Lf->inp_ty = 1;
	} else if (s5 != NULL) {
		Lf->inode = (unsigned long)s5->i_number;
		Lf->inp_ty = 1;
	}
/*
 * Obtain the file size.
 */
	if (Foffset) {
		Lf->off_def = 1;

#if	_OSF1V>=30000
		if (Ntype == N_FIFO && f != NULL)
			Lf->off = (unsigned long)
				  (Lf->access == 'r') ? f->fn_rptr
						      : f->fn_wptr;
#endif	/* _OSF1V>=30000 */

	} else {
		switch (Ntype) {
		case N_FIFO:

#if	_OSF1V>=30000
			if (f != NULL) {
				Lf->sz = (unsigned long)f->fn_size;
				Lf->sz_def = 1;
			} else if (!Fsize)
				Lf->off_def = 1;
#else	/* _OSF1V<30000 */
			if (!Fsize)
				Lf->off_def = 1;
#endif	/* _OSF1V>=30000 */

			break;
		case N_NFS:
			if (r != NULL) {
				Lf->sz = (unsigned long)r->r_attr.va_qsize;
				Lf->sz_def = 1;
			}
			break;
		case N_REGLR:
			if (type == VREG || type == VDIR) {
				if (a != NULL) {
					Lf->sz = (unsigned long)a->a_size;
					Lf->sz_def = 1;
				} else if (c != NULL) {
					Lf->sz = (unsigned long)
						 c->cd_size;
					Lf->sz_def = 1;
				} else if (i != NULL) {
					Lf->sz = (unsigned long)
						 i->i_din.di_qsize;
					Lf->sz_def = 1;
				} else if (m != NULL) {
					Lf->sz = (unsigned long)m->mfs_size;
					Lf->sz_def = 1;
				} else if (p != NULL) {
					if (type != VDIR)
						get_proc_sz(p);
				} else if (s5 != NULL) {
					Lf->sz = (unsigned long)s5->i_size;
					Lf->sz_def = 1;
				}
			} else if (type == VCHR && !Fsize)
				Lf->off_def = 1;
		}
	}
/*
 * 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:

#if	_OSF1V<30000
		Lf->dev = dev;
		Lf->dev_def = 1;
#endif	/* _OSF1V<30000 */

		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;
 *	FIFOs (OSF/1 V3.0 and higher);
 *	memory node files;
 *	/proc files.
 */

	if (type == VBAD)
		(void) strcpy(Namech, "(revoked)");

#if	_OSF1V>=30000
	else if (f != NULL) {
		(void) sprintf(dev_ch, "0x%08x", v->v_fifonode);
		(void) enter_dev_ch(dev_ch);
	}
#endif	/* _OSF1V>=30000 */

	else if (m != NULL) {
		Lf->dev_def = 0;
		(void) sprintf(Namech, "%#x", m->mfs_baseoff);
		(void) enter_dev_ch("    memory");
	} else if (p != NULL) {
		Lf->dev_def = 0;
		if (type != VDIR)
			(void) sprintf(Namech, "/proc/%0*d", PNSIZ,
				p->prc_pid);
		else
			(void) strcpy(Namech, "/proc");
	}
/*
 * 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 (p != NULL) {
		if (Procsrch)
			Lf->sf |= SELNM;
		else {
			for (pfi = Procfsid; pfi; pfi = pfi->next) {
				if (pfi->pid == p->prc_pid) {
					Lf->sf |= SELNM;
					break;
				}
			}
		}
	} else {
		if (Sfile && is_file_named(NULL, type))
			Lf->sf |= SELNM;
	}
/*
 * Enter name characters.
 */
	if (Namech[0])
		enter_nm(Namech);
}


/*
 * readvnode() - read vnode
 */

static int
readvnode(va, v)
	caddr_t va;			/* vnode kernel space address */
	struct vnode *v;		/* vnode buffer pointer */
{

	if (kread((KA_T)va, (char *)v, sizeof(struct vnode) - 1 + Vnmxp)) {
		(void) sprintf(Namech, "can't read vnode at %#x", va);
		return(1);
	}
	return(0);
}
