/*
 * dproc.c - HP-UX 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 96/03/05 12:52:28 abe Exp $";
#endif

#include "lsof.h"

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


/*
 * Local static values
 */

static KA_T Kp;				/* kernel's process table address */
static int Np;				/* number of kernel processes */

#if	_HPUXV>=800
static MALLOC_S Nv = 0;			/* number of entries in vnode cache */
static struct vnode **Vp = NULL;	/* vnode cache */
#endif	/* _HPUXV>=800 */


_PROTOTYPE(static void get_kernel_access,(void));

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

#if	_HPUXV>=800
_PROTOTYPE(static void process_text,(struct vas *vasp));
#endif	/* _HPUXV>=800 */


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

void
gather_proc_info()
{
	int err, i, j;

#if	_HPUXV>=800
	char *c, *s;
	struct ofile_t *ofp;
	struct ofile_t oft;
	struct pst_status ps;
	struct user us;
#else	/* _HPUXV<800 */
	int k;
	long sw;
	char us[U_SIZE];	/* must read HP-UX SWAP in DEV_BSIZE chunks */

# if	defined(hp9000s300)
	struct pte pte1, pte2;
	KA_T pte_off, pte_addr;
# endif	/* defined(hp9000s300) */
#endif	/* _HPUXV>=800 */

	struct proc *p;
	KA_T pa;
	struct proc pbuf;
	short pss, sf;
	int px;
	struct user *u;

#if     defined(HASNCACHE)
        ncache_load();
#endif  /* defined(HASNCACHE) */

/*
 * Examine proc structures and their associated information.
 */

#if	_HPUXV>=800
	for (p = &pbuf, px = 0, u = &us; px < Np; px++)
#else	/* _HPUXV<800 */
	for (p = &pbuf, px = 0, u = (struct user *)us; px < np; px++)
#endif	/* _HPUXV>=800 */

	{
		pa = Kp + (KA_T)(px * sizeof(struct proc));
		if (kread(pa, (char *)&pbuf, sizeof(pbuf)))
			continue;
		if (p->p_stat == 0 || p->p_stat == SZOMB)
			continue;
	/*
	 * See if process is excluded.
	 */
		if (is_proc_excl(p->p_pid, (int)p->p_pgrp, (UID_ARG)p->p_uid,
		    &pss, &sf))
			continue;
	/*
	 * Read the user area.
	 */

#if	_HPUXV>=800
/*
 * Use the pstat() syscall to read process status.
 */
	(void) zeromem(u, U_SIZE);
	if (pstat(PSTAT_PROC, &ps, sizeof(ps), 0, p->p_pid) != 1) {
	    if (!Fwarn)
		(void) fprintf(stderr, "%s: can't pstat process %d: %s\n",
			Pn, p->p_pid, strerror(errno));
	    continue;
	}
/*
 * Scan the pst_cmd command buffer and skip to the last component of the
 * first path name.  Also skip any leading `-', signifying a login shell.
 * Copy the result to u_comm[].
 */
	c = ps.pst_cmd;
	ps.pst_cmd[PST_CLEN - 1] = '\0';	/* paranoia */
	if (*c == '/' || *c == '.') {
		for (s = c; *s && *s != ' '; s++) {
			if (*s == '/')
				c = s + 1;
		}
	}
	if (*c == '-')
		c++;
	for (i = 0; i < MAXCOMLEN; i++) {
		if (*c == '\0' || *c == ' ' || *c == '/')
			break;
		u->u_comm[i] = *c++;
	}
	u->u_comm[i] = '\0';
#else	/* _HPUXV<800 */

/*
 * Read the user area from the swap file or memory.
 */
	if ((p->p_flag & SLOAD) == 0) {

	/*
	 * If the process is not loaded, read the user area from the swap file.
	 */
		if (Swap < 0)
			continue;
		sw = (long)p->p_swaddr;

# if	defined(hp9000s800)
		sw += (long)ctod(btoc(STACKSIZE * NBPG));
# endif	/* defined(hp9000s800) */

		if (lseek(Swap, (off_t)dtob(sw), L_SET) == (off_t)-1
		||  read(Swap, u, U_SIZE) != U_SIZE)
			continue;
	} else {

	/*
	 * Read the user area via the page table.
	 */

# if	defined(hp9000s300)
		pte_off = (KA_T) &Usrptmap[btokmx(p->p_p0br) + p->p_szpt - 1];
		if (kread(pte_off, (char *)&pte1, sizeof(pte1)))
			continue;
		pte_addr = (KA_T)(ctob(pte1.pg_pfnum + 1)
			 - ((UPAGES + FLOAT) * sizeof(pte2)));
		if (mread(pte_addr, (char *)&pte2, sizeof(pte2)))
			continue;
		if (mread((KA_T)ctob(pte2.pg_pfnum), (char *)u,
		    sizeof(struct user)))
			continue;
# endif	/* defined(hp9000s300) */.

# if	defined(hp9000s800)
		if (kread((KA_T)uvadd((struct proc *)pa), (char *)u,
		    sizeof(struct user)))
			continue;
	}
# endif	/* defined(hp9000s800) */
#endif	/* _HPUXV>=800 */

	/*
	 * Allocate a local process structure.
	 */
		if (is_cmd_excl(u->u_comm, &pss, &sf))
			continue;
		alloc_lproc(p->p_pid, (int)p->p_pgrp, (UID_ARG)p->p_uid,
			u->u_comm, (int)pss, (int)sf);
		Plf = NULL;
	/*
	 * Save current working directory information.
	 */
		if (CURDIR) {
			alloc_lfile(CWD, -1);
			process_node((caddr_t)CURDIR);
			if (Lf->sf)
				link_lfile();
		}
	/*
	 * Save root directory information.
	 */
		if (ROOTDIR) {
			alloc_lfile(RTD, -1);
			process_node((caddr_t)ROOTDIR);
			if (Lf->sf)
				link_lfile();
		}

#if	_HPUXV>=800
	/*
	 * Print information on the text file.
	 */
		if (p->p_vas)
			process_text(p->p_vas);
#endif	/* _HPUXV>=800 */

	/*
	 * Loop through user's files.
	 */

#if	_HPUXV>=800
		for (i = 0, j = SFDCHUNK; i < p->p_maxof; i++)
#else	/* _HPUXV<800 */
		for (i = j = k = 0;; i++)
#endif	/* _HPUXV>=800 */

		{

#if	_HPUXV>=800
		    if (j >= SFDCHUNK) {
			if (p->p_ofilep == NULL
			||  kread((KA_T)p->p_ofilep, (char *)&ofp, sizeof(ofp))
			||  ofp == NULL
			||  kread((KA_T)ofp, (char *)&oft, sizeof(oft)))
			    break;
			j = 0;
			p->p_ofilep++;
		    }
		    j++;
		    if (oft.ofile[j - 1])
#else	/* _HPUXV<800 */
		    if (j >= SFDCHUNK) {

		    /*
		     * Get next file pointer "chunk".
		     */
			while (++k < NFDCHUNKS && u->u_ofilep[k] == NULL)
			    ;
			if (k >= NFDCHUNKS)
			    break;
			if (kread((KA_T)u->u_ofilep[k], (char *)&u->u_ofile,
			    sizeof(struct ofile_t)))
			{
			    break;
			}
			j = 0;
		    }
		    j++;
		    if (u->u_ofile.ofile[j - 1])
#endif	/* _HPUXV>=800 */

		/*
		 * Process the file pointer.
		 */

		    {
			alloc_lfile(NULL, i);
			process_file(FILEPTR);
			if (Lf->sf)
			    link_lfile();
		    }
		}
	/*
	 * Examine results.
	 */
		if (examine_lproc())
		return;
	}
}


/*
 * get_kernel_access() - access the required information in the kernel
 */

static void
get_kernel_access()
{

#if	defined(HAS_AFS)
	struct nlist *nl = (struct nlist *)NULL;
#define	NLSIZE	((X_LAST + 1) * sizeof(struct nlist))
#endif	/* defined(HAS_AFS) */

#if	_HPUXV<800
/*
 * Open access to /dev/mem and SWAP.
 */
	if ((Mem = open("/dev/mem", O_RDONLY, 0)) < 0) {
		(void) fprintf(stderr, "%s: can't open /dev/mem: %s\n",
			Pn, strerror(errno));
		err = 1;
	}
	if (Memory == NULL || strcmp(Memory, KMEM) == 0) {
		if ((Swap = open(SWAP, O_RDONLY, 0)) < 0) {
			(void) fprintf(stderr, "%s: %s: %s\n",
				Pn, SWAP, strerror(errno));
			err = 1;
		}
	}
#endif	/* _HPUXV<800 */

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

#if	defined(HAS_AFS)
	if (!Nmlst) {

	/*
	 * If AFS is defined and we're getting kernel symbol values from
	 * from N_UNIX, make a copy of Nl[] for possible use with the AFS
	 * module name list file.
	 */
		if ((nl = (struct nlist *)malloc(NLSIZE))
		== (struct nlist *)NULL)
		{
			(void) fprintf(stderr,
				"%s: no space (%d) for Nl[] copy\n",
				Pn, NLSIZE);
			exit(1);
		}
		(void) memcpy((void *)nl, (void *)Nl, (size_t)NLSIZE);
	}
#endif	/* defined(HAS_AFS) */

/*
 * Access kernel symbols.
 */
	if (nlist(Nmlst ? Nmlst : N_UNIX, Nl) < 0) {
                (void) fprintf(stderr, "%s: can't read namelist from %s\n",
			Pn, Nmlst ? Nmlst : N_UNIX);
                exit(1);
	}
	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))
	||  Kp == NULL || Np == 0) {
		(void) fprintf(stderr, "%s: can't read proc table info\n",
			Pn);
		exit(1);
	}
	Vnfops = (struct fileops *) Nl[X_VN_FOPS].n_value;

#if	_HPUXV<800 && defined(hp9000s300)
	if ((Usrptmap = (struct pte *)Nl[X_USRPTMAP].n_value) == NULL) {
		(void) fprintf(stderr, "%s: can't get kernel's Usrptmap\n", Pn);
		exit(1);
	}
	if ((usrpt = (struct pte *)Nl[X_USRPT].n_value) == NULL) {
		(void) fprintf(stderr, "%s: can't get kernel's usrpt\n", Pn);
		exit(1);
	}
#endif	/* _HPUXV<800 && defined(hp9000s300) */

#if	_HPUXV<800 && defined(hp9000s800)
	proc = (struct proc *)Kp;
	if ((ubase = (struct user *)Nl[X_UBASE].n_value) == NULL) {
		(void) fprintf(stderr, "%s: can't get kernel's ubase\n", Pn);
		exit(1);
	}
	if (Nl[X_NPIDS].n_value == NULL
	||  kread((KA_T)Nl[X_NPIDS].n_value, (char *)&npids, sizeof(npids))) {
		(void) fprintf(stderr, "%s: can't get kernel's npids\n", Pn);
		exit(1);
	}
#endif	/* _HPUXV<800 && defined(hp9000s800) */

#if	defined(HAS_AFS)
	if (nl) {

	/*
	 * If AFS is defined and we're getting kernel symbol values from
	 * N_UNIX, and if any X_AFS_* symbols isn't there, see if it is in the
	 * the AFS module name list file.  Make sure that other symbols that
	 * appear in both name list files have the same values.
	 */
		if (!Nl[X_AFS_FID].n_value || !Nl[X_AFS_OPS].n_value
		||  !Nl[X_AFS_VOL].n_value)
			(void) ckAFSsym(nl);
		(void) free((FREE_P *)nl);
	}
#endif	/* defined(HAS_AFS) */

}


/*
 * 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);
}


#if	_HPUXV<800
/*
 * mread() -- read from /dev/mem
 */

static int
mread(addr, buf, len)
	KA_T addr;			/* /dev/mem address */
	char *buf;			/* buffer to receive data */
	READLEN_T len;			/* length to read */
{
	int br;

	if (lseek(Mem, addr, L_SET) == (off_t)-1L)
		return(1);
	br = read(Mem, buf, len);
	return((br == len) ? 0 : 1);
}
#endif	/* _HPUXV<800 */


#if	_HPUXV>=800
/*
 * process_text() - process text access information
 */

static void
process_text(vasp)
	struct vas *vasp;		/* virtual address space pointer */
{
	char fd[FDLEN];
	int i, j, lm;
	struct pregion p, *prp;
	struct region r;
	struct vas v;
	struct vnode *va;
/*
 * Read virtual address space pointer.
 */
	if (kread((KA_T)vasp, (char *)&v, sizeof(v)))
		return;
/*
 * Follow the virtual address space pregion structure chain.
 */
	for (i = lm = 0, prp = v.va_next;
	     prp != (struct pregion *)vasp;
	     prp = p.p_next, lm++)
	{

	/*
	 * Avoid infinite loop.
	 */
		if (lm > 1000) {
		    if (!Fwarn)
			(void) fprintf(stderr,
			    "%s: too many virtual address regions for PID %d\n",
			    Pn, Lp->pid);
		    return;
		}
	/*
	 * Read the pregion and region.
	 */
		if (kread((KA_T)prp, (char *)&p, sizeof(p)))
			return;
		if (kread((KA_T)p.p_reg, (char *)&r, sizeof(r)))
			return;
	/*
	 * Skip file entries with no file pointers.
	 */
		if ((va = r.r_fstore) == NULL)
			continue;
	/*
	 * Skip entries whose vnodes have already been displayed.
	 *
	 *  Record new, unique vnode pointers.
	 */
		for (j = 0; j < i; j++) {
			if (Vp[j] == va)
				break;
		}
		if (j < i)
			continue;
		if (Vp == NULL) {
			if ((Vp = (struct vnode **)malloc((MALLOC_S)
			    (sizeof(struct vnode *) * 10)))
			== NULL) {
				(void) fprintf(stderr,
				    "%s: no space for text vnode pointers\n",
				    Pn);
				exit(1);
			}
			Nv = 10;
		} else if (i >= Nv) {
			Nv += 10;
			if ((Vp = (struct vnode **)realloc((MALLOC_P *)Vp,
			    (MALLOC_S)(Nv * sizeof(struct vnode *))))
			== NULL) {
			    (void) fprintf(stderr,
				"%s: no more space for text vnode pointers\n",
				Pn);
			    exit(1);
			}
		}
		Vp[i++] = va;
	/*
	 * Allocate local file structure.
	 */
		switch (p.p_type) {
		case PT_DATA:
		case PT_TEXT:
			alloc_lfile(" txt", -1);
			break;
		case PT_MMAP:
			alloc_lfile(" mem", -1);
			break;
		default:
			(void) sprintf(fd, "R%02d", p.p_type);
			alloc_lfile(fd, -1);
		}
	/*
	 * Save vnode information.
	 */
		process_node((caddr_t)va);
		if (Lf->sf)
			link_lfile();
	}
}
#endif	/* _HPUXV>=800 */


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

#if	defined(HASNCACHE)
# if	_HPUXV<1000
#define	ADDR_NCACHE	1
# endif	/* _HPUXV<1000 */
#endif	/* defined*HASNCACHE) */


