/*
 * dproc.c - DEC OSF/1 process access 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: dproc.c,v 1.15 95/12/21 07:32:02 abe Exp $";
#endif

#include "lsof.h"

#if     defined(HASNCACHE)
#include <sys/namei.h>
#endif  /* defined(HASNCACHE) */


_PROTOTYPE(static void enter_vn_text,(KA_T va, int *n));
_PROTOTYPE(static void get_kernel_access,(void));

#if     defined(HASNCACHE)
_PROTOTYPE(static void ncache_load,(void));
#endif  /* defined(HASNCACHE) */

#if	_OSF1V<30000
_PROTOTYPE(static void process_text,(struct task *tp, struct utask *utp));
#else	/* _OSF1V>=30000 */
_PROTOTYPE(static void process_text,(struct task *tp));
#endif	/* _OSF1V<30000 */

_PROTOTYPE(static void read_proc,(void));



/*
 * Local static values
 */

#if     _OSF1V<30000
static KA_T Kp;				/* kernel proc[] address */
#endif  /* _OSF1V<30000 */

static int Np = 0;			/* number of processes */
static MALLOC_S Nv = 0;			/* allocateed Vp[] entries */

#if	_OSF1V>=30000
static KA_T Pidtab;			/* kernel pidtab[] address */
#endif	/* _OSF1V>=30000 */

static KA_T *Vp = NULL;			/* vnode address cache */


/*
 * enter_vn_text() - enter a vnode text reference
 */

static void
enter_vn_text(va, n)
	KA_T va;			/* vnode address */
	int *n;				/* number of vnodes in vp[] */
{
	int i;

/*
 * Ignore the request if the vnode has already been printed.
 */
	for (i = 0; i < *n; i++) {
		if (va == Vp[i])
			return;
	}
/*
 * Print the vnode.
 */
	alloc_lfile(" txt", -1);
	process_node((caddr_t)va);
	if (Lf->sf)
		link_lfile();
	if (i >= Nv) {

	/*
	 * Allocate space for remembering the vnode.
	 */
		if (Vp == NULL) {
			if ((Vp = (KA_T *)malloc((MALLOC_S)
				  (sizeof(struct vnode *) * 10)))
			== NULL) {
				(void) fprintf(stderr,
					"%s: no txt ptr space, PID %d\n",
					Pn, Lp->pid);
				exit(1);
			}
			Nv = 10;
		} else {
			Nv += 10;
			if ((Vp = (KA_T *)realloc((MALLOC_P *)Vp,
				  (MALLOC_S)(Nv * sizeof(struct vnode *))))
			== NULL) {
				(void) fprintf(stderr,
					"%s: no more txt ptr space, PID %d\n",
					Pn, Lp->pid);
				exit(1);
			}
		}
	}
/*
 * Remember the vnode.
 */
	Vp[*n] = va;
	(*n)++;
}


/*
 * gather_proc_info() -- gather process information
 */

void
gather_proc_info()
{
	struct file *fp;
	int i, j;
	static int nufb = 0;
	struct pgrp pg;
	int pgrp, px;
	struct proc *p;
	short pss, sf;
	struct ucred pcred;
	static struct file **ufb = NULL;
	uid_t uid;
	struct utask ut, *utp;

#if	_OSF1V>=30000
	struct pid_entry pe;
#endif	/* _OSF1V>=30000 */

#if     defined(HASNCACHE)
/*
 * Read kernel name cache.
 */
	ncache_load();
#endif  /* defined(HASNCACHE) */

/*
 * Read process table entries.
 */
	read_proc();
/*
 * Examine proc structures and their associated information.
 */
	for (p = Ps, px = 0, utp = &ut; px < Psn; p++, px++) {
		if (p->p_stat == 0 || p->p_stat == SZOMB)
			continue;
		if (Fpgrp) {
		    if (p->p_pgrp == NULL
		    ||  kread((KA_T)p->p_pgrp, (char *)&pg, sizeof(pg)))
			continue;
		    pgrp = pg.pg_id;
		} else
		    pgrp = 0;
		if (p->p_rcred == NULL
		||  kread((KA_T)p->p_rcred, (char *)&pcred, sizeof(pcred)))
		    continue;
		uid = (uid_t)pcred.cr_ruid;
		if (is_proc_excl(p->p_pid, pgrp, (UID_ARG)uid, &pss, &sf))
		    continue;

#if	_OSF1V<30000
		if (p->utask == NULL
		||  kread((KA_T)p->utask,
#else	/* _OSF1V>=30000 */
		if (kread((KA_T)((char *)Pa[px] + sizeof(struct proc)),
#endif	/* _OSF1V<30000 */

		    (char *)&ut, sizeof(ut)))
		{
		    continue;
		}
	/*
	 * Allocate a local process structure.
	 */
		if (is_cmd_excl(utp->u_comm, &pss, &sf))
			continue;
		alloc_lproc(p->p_pid, pgrp, (UID_ARG)uid, utp->u_comm,
			(int)pss, (int)sf);
		Plf = NULL;
	/*
	 * Save current working directory information.
	 */
		if (utp->uu_utnd.utnd_cdir) {
			alloc_lfile(CWD, -1);
			process_node((caddr_t)utp->uu_utnd.utnd_cdir);
			if (Lf->sf)
				link_lfile();
		}
	/*
	 * Save root directory information.
	 */
		if (utp->uu_utnd.utnd_rdir) {
			alloc_lfile(RTD, -1);
			process_node((caddr_t)utp->uu_utnd.utnd_rdir);
			if (Lf->sf)
				link_lfile();
		}
	/*
	 * Print information on the text file.
	 */

#if	_OSF1V<30000
		if (p->task)
			process_text(p->task, p->utask);
#else	/* _OSF1V>=30000 */
		process_text((struct task *) ((char *)Pa[px]
			- sizeof(struct task)));
#endif	/* _OSF1V<30000 */

	/*
	 * Save information on file descriptors.
	 */
		for (i = j = 0; i <= utp->uu_file_state.uf_lastfile; i++) {
		    if (i < NOFILE_IN_U)
			fp = utp->uu_file_state.uf_ofile[i];
		    else {
			if (j == 0) {
			    if (ufb == NULL) {
				nufb = utp->uu_file_state.uf_of_count;
				if ((ufb = (struct file **)malloc((MALLOC_S)
				    (nufb * sizeof(struct file *))))
				== NULL) {
				    (void) fprintf(stderr,
					"%s: PID %d, no file * space\n",
					Pn, Lp->pid);
				    exit(1);
				}
			    } else if (utp->uu_file_state.uf_of_count > nufb) {
				nufb = utp->uu_file_state.uf_of_count;
				if ((ufb = (struct file **)realloc(
				    (MALLOC_P *)ufb,
				    (nufb * sizeof(struct file *))))
				== NULL) {
				    (void) fprintf(stderr,
					"%s: PID %d, no realloc file * space\n",
					Pn, Lp->pid);
				    exit(1);
				}
			    }
			    if (kread((KA_T)utp->uu_file_state.uf_ofile_of,
				(char *)ufb, utp->uu_file_state.uf_of_count
				* sizeof(struct file *)))
			    {
				break;
			    }
			}
			fp = ufb[j++];
		    }
		    if (fp != NULL && (ulong)fp != 0xffffffffffffffff) {
			alloc_lfile(NULL, i);
			process_file(fp);
			if (Lf->sf)
			    link_lfile();
		    }
		}
	/*
	 * Examine results.
	 */
		if (examine_lproc())
			return;
	}
}


/*
 * get_kernel_access() - get access to kernel memory
 */

static void
get_kernel_access()
{

#if	defined(WILLDROPGID)
/*
 * If kernel memory isn't coming from KMEM, drop setgid permission
 * before attempting to open the (Memory) file.
 */
	if (Memory)
		(void) dropgid();
#else	/* !defined(WILLDROPGID) */
/*
 * See if the non-KMEM memory file is readable.
 */
	if (Memory && !is_readable(Memory, 1))
		exit(1);
#endif	/* defined(WILLDROPGID) */

/*
 * Open kernel memory access.
 */
	if ((Kmem = open(Memory ? Memory : KMEM, O_RDONLY, 0)) < 0) {
		(void) fprintf(stderr, "%s: can't open %s: %s\n", Pn,
			Memory ? Memory : KMEM, sys_errlist[errno]);
		exit(1);
	}

#if	defined(WILLDROPGID)
/*
 * Drop setgid permission, if necessary.
 */
	if (!Memory)
		(void) dropgid();
#else	/* !defined(WILLDROPGID) */
/*
 * See if the name list file is readable.
 */
	if (Nmlst && !is_readable(Nmlst, 1))
		exit(1);
#endif	/* defined(WILLDROPGID) */

/*
 * Access kernel symbols.
 */
        if (nlist(Nmlst ? Nmlst : N_UNIX, Nl) == -1) {
		(void) fprintf(stderr,
			"%s: can't read kernel name list from %s\n",
			Pn, Nmlst ? Nmlst : N_UNIX);
		exit(1);
	}

#if	_OSF1V<30000
	if (Nl[X_PROC].n_value == NULL
	||  kread((KA_T)Nl[X_PROC].n_value, (char *)&Kp, sizeof(Kp))
	||  Nl[X_NPROC].n_value == NULL
	||  kread((KA_T)Nl[X_NPROC].n_value, (char *)&Np, sizeof(Np)))
#else	/* _OSF1V>=30000 */
	if (Nl[X_NPID].n_value == NULL
	||  kread((KA_T)Nl[X_NPID].n_value, (char *)&Np, sizeof(Np))
	||  Nl[X_PIDTAB].n_value == NULL
	||  kread((KA_T)Nl[X_PIDTAB].n_value, (char *)&Pidtab, sizeof(Pidtab)))
#endif	/* _OSF1V<30000 */

	{
		(void) fprintf(stderr, "%s: can't read proc table info\n", Pn);
		exit(1);
	}
	if (Nl[X_VNMXP].n_value == NULL
	||  kread((KA_T)Nl[X_VNMXP].n_value, (char *)&Vnmxp, sizeof(Vnmxp))) {
		(void) fprintf(stderr,
			"%s: can't determine vnode length\n", Pn);
		exit(1);
	}
	if (Nl[X_CLONE].n_value == NULL
	||  kread((KA_T)Nl[X_CLONE].n_value, (char *)&Clonedev,
	    sizeof(Clonedev)))
	{
		if (!Fwarn)
			(void) fprintf(stderr,
				"%s: can't read clone device number\n", Pn);
	}
}


/*
 * initialize() - perform all initialization
 */

void
initialize()
{
	get_kernel_access();
	(void) iuidcache(Np);
}


/*
 * kread() - read from kernel memory
 */

int
kread(addr, buf, len)
	KA_T addr;			/* kernel memory address */
	char *buf;			/* buffer to receive data */
	READLEN_T len;			/* length to read */
{
	int br;

	if (lseek(Kmem, addr, L_SET) == (off_t)-1L)
		return(-1);
	br = read(Kmem, buf, len);
	return((br == len) ? 0 : 1);
}


/*
 * process_text() - print text information
 */
static void

#if	_OSF1V<30000
process_text(tp, utp)
#else	/* _OSF1V>=30000 */
process_text(tp)
#endif	/* _OSF1V<30000 */

	struct task *tp;		/* kernel task structure */

#if	_OSF1V<30000
	struct utask *utp;		/* user task structure address for
					 * the task */
#endif	/* _OSF1V<30000 */

{
	int i;
	KA_T ka, kb;
	int n = 0;
	struct task t;
	struct vm_anon_object vmao;
	struct vm_map_entry vmme;
	struct vm_map vmm;
	struct vm_object vmo;
	struct vm_seg vms;

#if	_OSF1V<40000
	struct vm_vp_object vpo;
#else	/* _OSF1V>=40000 */
	struct vm_ubc_object vpo;
#endif	/* _OSF1V<40000 */

/*
 * Read task structure from kernel.
 */

	if (kread((KA_T)tp, (char *)&t, sizeof(t))

#if	_OSF1V<30000
	||  t.u_address != utp
#endif	/* _OSF1V<30000 */

	)
		return;
/*
 * Print information about the text vnode referenced in the procfs
 * structure inside the task structure.
 */
	if (t.procfs.pr_exvp)
		enter_vn_text((KA_T)t.procfs.pr_exvp, &n);
/*
 * Read the vm_map structure.  Search its vm_map_entry structure list.
 */
	if (t.map == NULL
	||  kread((KA_T)t.map, (char *)&vmm, sizeof(vmm)))
		return;
	if ( ! vmm.vm_is_mainmap)
		return;
	for (i = 0, ka = (KA_T)vmm.vm_links.next;
	     i < vmm.vm_nentries && ka != (KA_T)t.map;
	     i++, ka = (KA_T)vmme.vme_links.next)
	{
	/*
	 * Read the next vm_map_entry structure and its object.
	 */
		if (kread(ka, (char *)&vmme, sizeof(vmme)))
			return;
		if ((kb = (KA_T)vmme.vme_uobject.vm_object) == NULL
		||  kread(kb, (char *)&vmo, sizeof(vmo)))
			continue;
	/*
	 * Process by object type.
	 */
		switch (vmo.ob_type) {
		case OT_ANON:
	    /*
	     * If an anonymous object is backed by an OT_UBC or OT_VP object,
	     * read its vm_ubc_object or vm_vp_object to find a vnode pointer.
	     */
		    if (kread(kb, (char *)&vmao, sizeof(vmao)))
			break;
		    if (vmao.ao_bobject == NULL
		    ||  kread((KA_T)vmao.ao_bobject, (char *)&vmo, sizeof(vmo)))
			break;

#if	_OSF1V<40000
		    if (vmo.ob_type != OT_VP
		    ||  kread((KA_T)vmao.ao_bobject, (char *)&vpo, sizeof(vpo)))
			break;
		    enter_vn_text((KA_T)vpo.vo_vp, &n);
#else	/* _OSF1V>=40000 */
		    if (vmo.ob_type != OT_UBC
		    ||  kread((KA_T)vmao.ao_bobject, (char *)&vpo, sizeof(vpo)))
			break;
		    enter_vn_text((KA_T)vpo.vu_vfp.vp, &n);
#endif	/* _OSF1V<40000 */
		    break;
	    /*
	     * If this is a segment object, read the segment map, and search
	     * for backing objects whose object type is OT_UBC or OT_VP.
	     */
		case OT_SEG:
		    for (kb = (KA_T)vmme.vmet.tvme_seg;
			 kb != NULL;
			 kb = (KA_T)vms.seg_vnext)
		    {
			if (kread(kb, (char *)&vms, sizeof(vms)))
			    break;
			if (vms.seg_vop == NULL
			||  kread((KA_T)vms.seg_vop, (char *)&vmo, sizeof(vmo)))
			    continue;

#if	_OSF1V<40000
			if (vmo.ob_type != OT_VP)
#else	/* _OSF1V>=40000 */
			if (vmo.ob_type != OT_UBC)
#endif	/* _OSF1V<40000 */

			    continue;
			if (kread((KA_T)vms.seg_vop, (char *)&vpo, sizeof(vpo)))
			    break;

#if	_OSF1V<40000
			enter_vn_text((KA_T)vpo.vo_vp, &n);
#else	/* _OSF1V>=40000 */
		        enter_vn_text((KA_T)vpo.vu_vfp.vp, &n);
#endif	/* _OSF1V<40000 */

		    }
		}
	}
}


/*
 * read_proc() - read process table entries
 */

static void
read_proc()
{
	MALLOC_S len;
	struct proc *p;
	KA_T pa;
	int px, try;

#if     _OSF1V>=30000
	struct pid_entry pe;
#endif  /* _OSF1V>=30000 */

	if (!Ps) {

	/*
	 * Allocate local proc table space.
	 */
	    if (Np < 1) {
		(void) fprintf(stderr, "%s: proc table has no entries\n", Pn);
		exit(1);
	    }
	    len = (MALLOC_S)(Np * sizeof(struct proc));
	    if ((Ps = (struct proc *)malloc(len)) == NULL) {
		(void) fprintf(stderr, "%s: no proc table space (%d bytes)\n",
		    Pn, len);
		exit(1);
	    }

#if	_OSF1V>=30000
	/*
	 * Allocate kernel proc address table space.
	 */
	    len = (MALLOC_S)(Np * sizeof(KA_T));
	    if ((Pa = (KA_T *)malloc(len)) == NULL) {
		(void) fprintf(stderr,
		    "%s: no proc address table space (%d bytes)\n", Pn, len);
		exit(1);
	    }
#endif	/* _OSF1V>=30000 */

	}
/*
 * Try to read the proc structure table PROCTRYLM times.
 * The operation must yield PROCMIN structures.
 */
	for (try = 0; try < PROCTRYLM; try++) {
	    for (p = Ps, Psn = px = 0; px < Np; px++) {

#if     _OSF1V<30000
		pa = Kp + (KA_T)(px * sizeof(struct proc));
		if (kread(pa, (char *)p, sizeof(struct proc)))
		    continue;
#else   /* _OSF1V>=30000 */
		pa = Pidtab + (KA_T)(px * sizeof(struct pid_entry));
		if (kread(pa, (char *)&pe, sizeof(struct pid_entry)))
		    continue;
		if ((pa = (KA_T)pe.pe_proc) == NULL
		||  kread(pa, (char *)p, sizeof(struct proc)))
		    continue;
		if (pe.pe_pid != p->p_pid)
		    continue;
		Pa[Psn] = pa;
#endif  /* _OSF1V<30000 */

		Psn++;
		p++;
	    }
	/*
	 * Check the results of the scan.
	 */
	    if (Psn >= PROCMIN)
		break;
	}
/*
 * Quit if not enough proc structures could be accumulated.
 */
	if (try >= PROCTRYLM) {
	    (void) fprintf(stderr, "%s: can't read proc table\n", Pn);
	    exit(1);
	}
	if (Psn < Np && !RptTm) {

	/*
	 * Reduce the local proc structure tables to their minimum if
	 * not in repeat mode.
	 */
	    len = (MALLOC_S)(Psn * sizeof(struct proc));
	    if ((Ps = (struct proc *)realloc((MALLOC_P *)Ps, len))
	    == NULL) {
		(void) fprintf(stderr,
		    "%s: can't reduce proc table to %d bytes\n",
		    Pn, len);
		exit(1);
	    }

#if	_OSF1V>=30000
	    len = (MALLOC_S)(Psn * sizeof(KA_T));
	    if ((Pa = (KA_T *)realloc((MALLOC_P *)Pa, len))
	    == NULL) {
		(void) fprintf(stderr,
		    "%s: can't reduce proc address table to %d bytes\n",
		    Pn, len);
		exit(1);
	    }
#endif	/* _OSF1V>=30000 */

	}
}


/*
 * The ncache_addr(), ncache_load(), and ncache_lookup() functions are
 * obtained from ../common/rnam.frag.
 */

#if	defined(HASNCACHE)
#define	NCACHE		namecache	/* kernel's structure name */
#define	NCACHE_NM	nc_name		/* name in NCACHE */
#define	NCACHE_NMLEN	nc_nlen		/* name length in NCACHE */
#define	NCACHE_NXT	nc_nxt		/* link in NCACHE */
#define	NCACHE_NODEADDR	nc_vp		/* node address in NCACHE */
#define	NCACHE_NODEID	nc_vpid		/* node ID in NCACHE */
#define	NCACHE_PARADDR	nc_dvp		/* parent node address in NCACHE */
#define	NCACHE_PARID	nc_dvpid	/* parent node ID in NCACHE */
#endif	/* defined(HASNCACHE) */


