/*
 * dproc.c - Ultrix 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.9 95/10/15 12:47:16 abe Exp $";
#endif

#include "lsof.h"


/*
 * Local static values
 */

static int Np = 0;			/* number of processes */
static long Kp;				/* pernel process table */


_PROTOTYPE(static void get_kernel_access,(void));

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


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

void
gather_proc_info()
{
	struct file *fp;
	int i, n, nb;
	struct proc *p, ps;
	struct pte *pte, upt[UPAGES];
	KA_T pa;
	short pss, sf;
	int px;
	struct text t;
	struct user *u, us;
	int uerr;
	caddr_t upa;

#if	ULTRIXV>=40200
	struct dmap dm;
	static int nufb = 0;
	int j, ubn;
	static struct file **ufb = NULL;
#endif	/* ULTRIXV>=40200 */

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

	for (p = &ps, px = 0, u = &us; px < Np; px++) {

	/*
	 * Read the proc structure.
	 */
		pa = (KA_T)(Kp + (long)(px * sizeof(struct proc)));
		if (kread(pa, (char *)&ps, sizeof(ps)))
			continue;
		if (p->p_stat == 0 || p->p_stat == SZOMB)
			continue;
		if (is_proc_excl(p->p_pid, (int)p->p_pgrp, (uid_t)p->p_uid,
		    &pss, &sf))
			continue;
	/*
	 * Read the user area.
	 */
		/*
		 * If the process has been swapped, read its user structure
		 * via its stack segment map's page table address.
		 */

#if     ULTRIXV==20200
		if ((p->p_flag & SLOAD) == 0) 
#endif  /* ULTRIXV==20200 */

#if     ULTRIXV>=40200
		if ((p->p_sched & SLOAD) == 0)
#endif  /* ULTRIXV>=40200 */

		{
			if (Nmlst && strcmp(Nmlst, N_UNIX) == 0)
				continue;

#if     ULTRIXV==20200
			if (lseek(Swap, (long)dtob(p->p_swaddr), 0) == -1)
				continue;
#endif  /* ULTRIXV==20200 */

#if     ULTRIXV>=40200
			if (Nmlst && strcmp(Nmlst, N_UNIX) == 0)
				continue;
			if (kread((KA_T)p->p_smap, (char *)&dm, sizeof(dm)))
				continue;
			if (kread((KA_T)dm.dm_ptdaddr, (char *)&ubn,
			    sizeof(ubn)))
				continue;
			if (lseek(Swap, (long)dtob(ubn), 0) == -1)
				continue;
# endif /* ULTRIXV>=40200 */

			if (read(Swap, (char *)u, U_SIZE) != U_SIZE)
				continue;
		} else {

/*
 * Read the user structure via its kernel map.
 */
			if (kread((KA_T)p->p_addr, (char *)upt, sizeof(upt)))
				continue;
			for (nb = U_SIZE, pte = upt, uerr = 0, upa = (caddr_t)u;
			     nb > 0;
			     nb -= n, pte++, upa += n)
			{
				if (lseek(Mem, (long)pte->pg_pfnum * NBPG, 0)
				== -1) {
					uerr++;
					break;
				}
				n = (nb < NBPG) ? nb : NBPG;
				if (read(Mem, upa, n) != n) {
					uerr++;
					break;
				}
			}
			if (uerr)
				continue;
		}
	/*
	 * 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_t)p->p_uid,
			u->u_comm, (int)pss, (int)sf);
		Plf = NULL;
	/*
	 * Save current working directory information.
	 */
		if (u->u_cdir) {
			alloc_lfile(CWD, -1);
			process_node((caddr_t)u->u_cdir);
			if (Lf->sf)
				link_lfile();
		}
	/*
	 * Save root directory information.
	 */
		if (u->u_rdir) {
			alloc_lfile(RTD, -1);
			process_node((caddr_t)u->u_rdir);
			if (Lf->sf)
				link_lfile();
		}
	/*
	 * Print information on the text file.
	 */
		if (p->p_textp)  {
			if (kread((KA_T)p->p_textp, (char *)&t, sizeof(t)) == 0
			&&  t.x_gptr) {
				alloc_lfile("txt", -1);
				process_node((caddr_t)t.x_gptr);
				if (Lf->sf)
					link_lfile();
			}
		}
	/*
	 * Save information on file descriptors.
	 */

#if     ULTRIXV==20200
		for (i = 0; i <= u->u_lastfile; i++)
#endif  /* ULTRIXV==20200 */

#if     ULTRIXV>=40200
		for (i = j = 0; i <= u->u_lastfile; i++)
#endif  /* ULTRIXV>=40200 */

		{

# if	ULTRIXV==20200
			fp = u->u_ofile[i];
# endif	/* ULTRIXV==20200 */

# if	ULTRIXV>=40200
			if (i < NOFILE_IN_U)
			    fp = u->u_ofile[i];
			else {
			    if (j == 0) {
				if (ufb == NULL) {
				    nufb = u->u_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, p->p_pid);
					exit(1);
				    }
				} else if (u->u_of_count > nufb) {
				    nufb = u->u_of_count;
				    if ((ufb = (struct file **)realloc(
					(MALLOC_P)ufb,
					(nufb * sizeof(struct file *))))
				    == NULL) {
				      (void) sprintf(stderr,
					"%s: PID %d, no realloc file * space\n",
					Pn, p->p_pid);
				      exit(1);
				    }
				}
				if (kread((KA_T)u->u_ofile_of,
					(char *)ufb, u->u_of_count
					* sizeof(struct file *)))
				{
					break;
				}
			    }
			    fp = ufb[j++];
			}
#endif	/* ULTRIXV >= 40200 */

			if (fp) {
				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()
{

/*
 * Get 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));
		exit(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));
			exit(1);
		}
	}

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

/*
 * 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 (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))) {
		(void) fprintf(stderr, "%s: can't read proc table info\n", Pn);
		exit(1);
	}
}


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


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

#if	defined(HASNCACHE)
#define	NCACHE		nch		/* kernel's structure name */
#define	NCACHE_NM	nc_name		/* name in NCACHE */
#define	NCACHE_NMLEN	nc_nlen		/* name length in NCACHE */
#define	NCACHE_NODEADDR	nc_ip		/* node address in NCACHE */
#define	NCACHE_NODEID	nc_id		/* node ID in NCACHE */
#define	NCACHE_NO_ROOT	1		/* can't test for root node */
#endif	/* defined(HASNCACHE) */
