/*
 * dproc.c - SunOS (Solaris 1.x and 2.x)  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.22 96/02/21 08:10:43 abe Exp $";
#endif

#include "lsof.h"

#if	defined(solaris) && solaris<20500
#include "kernelbase.h"
#endif	/* defined(solaris) && solaris<20500 */

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


/*
 * Local definitions
 */

#if	defined(solaris) && solaris>=20500
u_long Kb = 0l;				/* KERNELBASE for Solaris 2.5 */
#endif	/* defined(solaris) && solaris>=20500 */

static int Knp = PROCDFLT;		/* numer of proc structures known
					 * to the kernel */
static int Np;				/* number of P[], Pgrp[], and Pid[]
					 * entries  */
static struct proc *P = NULL;		/* local proc structure table */

#if	defined(solaris)
static int *Pid = NULL;			/* PIDs for P[] entries */
static int *Pgrp = NULL;		/* process group IDs for P[] entries */
static gid_t Savedgid;			/* saved (effective) GID */
static int Switchgid = 0;		/* must switch GIDs for kvm_open() */
#endif	/* defined(solaris) */


_PROTOTYPE(static void close_kvm,(void));
_PROTOTYPE(static void get_kernel_access,(void));

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

_PROTOTYPE(static void open_kvm,(void));
_PROTOTYPE(static void process_text,(struct as *pa));
_PROTOTYPE(static void read_proc,(void));

#if	defined(solaris)
_PROTOTYPE(static void readfsinfo,(void));
#endif	/* defined(solaris) */



/*
 * close_kvm() - close kernel virtual memory access
 */

static void
close_kvm()
{
	if (Kd == NULL)
		return;
	if (kvm_close(Kd) != 0) {
		(void) fprintf(stderr, "%s: kvm_close failed\n", Pn);
		exit(1);
	}
	Kd = NULL;
}


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

void
gather_proc_info()
{
	int i, j;
	struct proc *p;
	int pgrp, pid, px;
	short pss, sf;
	struct user *u;
	uid_t uid;

#if	defined(solaris)
# if	solaris>=20400
	int k;
	uf_entry_t uf[NFPCHUNK];
# endif	/* solaris>=20400 */
# if	solaris>=20500
	struct cred pc;
# endif	/* solaris>=20500 */
#else	/* !solaris */
	struct ucwd cd;
	int dl, nf;
	struct file **uf;
	unsigned ui;
	static int xnf = 0;
	static struct file **xuf = NULL;
#endif	/* solaris */

/*
 * Read kernel name cache and process table.
 */

#if	defined(HASNCACHE)
# if	defined(solaris)
	open_kvm();
# endif	/* defined(solaris) */
	ncache_load();
# if	defined(solaris)
	close_kvm();
# endif	/* defined(solaris) */
#endif	/* defined(HASNCACHE) */

	read_proc();
/*
 * Loop through processes.
 */
	for (p = P, px = 0; px < Np; p++, px++) {

	/*
	 * Get the process ID.
	 */

#if	defined(solaris)
		if (Fpgrp)
			pgrp = Pgrp[px];
		else
			pgrp = 0;
		pid = Pid[px];
# if solaris<20500
		uid = p->p_uid;
# else	/* solaris >=20500 */
	/*
	 * Read credentials for Solaris 2.5 and above process.
	 */
		if ( ! p->p_cred
		||  kread((KA_T)p->p_cred, (char *)&pc, sizeof(pc)))
			continue;
		uid = pc.cr_ruid;
# endif	/* solaris<20500 */
#else	/* !defined(solaris) */
		pgrp = (int)p->p_pgrp;
		pid = p->p_pid;
		uid = p->p_uid;
#endif	/* defined(solaris) */

	/*
	 * See if the process is excluded.
	 */
		if  (is_proc_excl(pid, pgrp, (UID_ARG)uid, &pss, &sf))
			continue;
	/*
	 * Get the user area associated with the process.
	 */

#if	defined(solaris)
		u = &p->p_user;
#else	/* !defined(solaris) */
		if ((u = kvm_getu(Kd, p)) == NULL)
			continue;
#endif	/* defined(solaris) */

	/*
	 * Allocate a local process structure and start filling it.
	 */
		if (is_cmd_excl(u->u_comm, &pss, &sf))
			continue;
		alloc_lproc(pid, pgrp, (UID_ARG)uid, u->u_comm,
			(int)pss, (int)sf);
		Plf = NULL;

#if	!defined(solaris)
	/*
	 * Read the SunOS current working directory and root directory
	 * paths.
	 */
		*Cwd = *Rtd = '\0';
		if (u->u_cwd && kread((KA_T)u->u_cwd, (char *)&cd, sizeof(cd))
		== 0) {
			if (cd.cw_dir) {
				dl = (int)((char *)u->u_cwd + cd.cw_len
				   - cd.cw_dir);
				if (dl >= MAXPATHLEN)
					dl = MAXPATHLEN - 1;
				if (dl < 2 || kread((KA_T)cd.cw_dir, Cwd, dl))
					*Cwd = '\0';
				else
					Cwd[dl] = '\0';
			}
			if (cd.cw_root) {
				if (cd.cw_dir >= cd.cw_root)
					dl = (int)(cd.cw_dir - cd.cw_root);
				else
					dl = (int)((char *)u->u_cwd + cd.cw_len
					   - cd.cw_root);
				if (dl >= MAXPATHLEN)
					dl = MAXPATHLEN - 1;
				if (dl < 2 || kread((KA_T)cd.cw_root, Rtd, dl))
					*Rtd = '\0';
				else
					Rtd[dl] = '\0';
			}
		}
#endif	/* ! solaris */

	/*
	 * 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();
		}
	/*
	 * Save information on text files.
	 */
		if (p->p_as && Nl[X_SEGVN].n_value)
			process_text(p->p_as);
	/*
	 * Save information on file descriptors.
	 */

#if	defined(solaris)
	/*
	 * Under Solaris the file pointers are stored in dynamically-linked
	 * ufchunk structures, each containing NFPCHUNK file pointers.  The
	 * first ufchunk structure is in the user area.
	 *
	 * Under Solaris 2.4 the file pointers are in a dynamically allocated,
	 * contiguous memory block.
	 */
# if	solaris<20400
		for (i = 0, j = 0; i < u->u_nofiles; i++) {
		    if (++j > NFPCHUNK) {
			if ( ! u->u_flist.uf_next)
				break;
			if (kread((KA_T)u->u_flist.uf_next,
			    (char *)&u->u_flist, sizeof(struct ufchunk)))
				break;
			j = 1;
		    }
		    if (u->u_flist.uf_ofile[j-1] == NULL)
# else	/* solaris>=20400 */
		for (i = 0, j = NFPCHUNK; i < u->u_nofiles; i++) {
		    if (++j > NFPCHUNK) {
			k = u->u_nofiles - i;
			if (k > NFPCHUNK)
			    k = NFPCHUNK;
			if (kread((KA_T)((unsigned long)u->u_flist +
					i * sizeof(uf_entry_t)),
			    (char*)&uf, k * sizeof(uf_entry_t)))
				    break;
			j = 1;
		    }
		    if (uf[j-1].uf_ofile == NULL)
# endif	/* solaris<20400 */
#else	/* !solaris */

	/*
	 * Under SunOS 4.1.[123], the file pointers may be located in the
	 * user structure or in a separately allocated area.  Determine
	 * which is the case and read the pointers.
	 */
		if ((caddr_t)u->u_ofile
		== ((caddr_t)&u->u_ofile_arr[0] - (caddr_t)&u
		+ (caddr_t)p->p_uarea)) {
			nf = NOFILE_IN_U;
			uf = &u->u_ofile_arr[0];
		} else {
			nf = u->u_lastfile + 1;
			ui = nf * sizeof(struct file);
			if (xuf == NULL) {
				xnf = nf;
				if ((xuf = (struct file **)malloc(ui))
				== NULL) {
					(void) fprintf(stderr,
						"%s: PID %d, malloc file[]\n",
						Pn, Lp->pid);
					exit(1);
				}
			} else if (nf > xnf) {
				xnf = nf;
				if ((xuf = (struct file **)realloc(
					   (MALLOC_P *)xuf, ui))
				== NULL) {
					(void) fprintf(stderr,
						"%s: PID %d, realloc file[]\n",
						Pn, Lp->pid);
					exit(1);
				}
			}
			if (kread((KA_T)u->u_ofile, (char *)xuf, (int)ui))
				nf = 0;
			uf = xuf;
		}
		*Cwd = *Rtd = '\0';
		for (i = 0; i < nf; i++) {
			if (!uf[i])
#endif	/* solaris */
				continue;
			alloc_lfile(NULL, i);

#if	defined(solaris)
# if	solaris<20400
			process_file(u->u_flist.uf_ofile[j-1]);
# else	/* solaris>=20400 */
			process_file(uf[j-1].uf_ofile);
# endif	/* solaris <20400 */
#else	/* !solaris */
			process_file(uf[i]);
#endif	/* solaris */

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

/*
 * Get kernel symbols.
 */
	if (Nmlst && !is_readable(Nmlst, 1))
		exit(1);

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

# if	defined(solaris)
		(void) memcpy((void *)nl, (void *)Nl, (size_t)NLSIZE);
# else	/* !defined(solaris) */
		(void) memcpy((char *)nl, (char *)Nl, (int)NLSIZE);
# endif	/* defined(solaris) */

	}
#endif	/* defined(HAS_AFS) */

	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	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 modload 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((MALLOC_P *)nl);
	}
#endif	/* defined(HAS_AFS) */

#if	defined(WILLDROPGID)

# if	defined(solaris)
/*
 * If Solaris kernel memory is coming from KMEM, and the process is willing
 * to surrender GID permission, set up for GID switching after the first
 * call to open_kvm().
 */
	if (!Memory) {
		Savedgid = getegid();
		if (Setgid)
			Switchgid = 1;
	}
# endif	/* defined(solaris) */

/*
 * 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 access to kernel memory.
 */
	open_kvm();
/*
 * Get a proc structure count estimate.
 */
	if (Nl[X_NPROC].n_value
	&&  kread((KA_T)Nl[X_NPROC].n_value, (char *)&Knp, sizeof(Knp))
	||  Knp < 1)
		Knp = PROCDFLT;

#if	defined(solaris)
# if	solaris>=20500
/*
 * Get the kernel's KERNELBASE value for Solaris 2.5 and above.
 */
	if ( ! Nl[X_KERNELBASE].n_value
	||  kread((KA_T)Nl[X_KERNELBASE].n_value, (char *)&Kb, sizeof(Kb))) {
		(void) fprintf(stderr, "%s: can't read %s from %#lx\n", Pn,
			Nl[X_KERNELBASE].n_name, Nl[X_KERNELBASE].n_value);
		exit(1);
	}
# endif	/* solaris >=20500 */
/*
 * Get the Solaris clone major device number, if possible.
 */
	if (Nl[X_CLONEMAJ].n_value
	&&  kread((KA_T)Nl[X_CLONEMAJ].n_value, (char *)&CloneMaj,
		  sizeof(CloneMaj)) == 0)
	{
		HaveCloneMaj = 1;
	}
/*
 * The Solaris process table is a linked list whose head pointer is acquired
 * by open_kvm()'s call to kvm_open().  We call close_kvm() here so that the
 * process table head pointer can be re-acquired by a call to open_kvm()
 * immediately before the actual reading of the process table.
 *
 * Since the SunOS 4.1.x process table is sequential, we need not worry
 * about a stale or incorrect list head pointer.  Thus we don't have to call
 * close_kvm().  Instead, we call kvm_setproc() each time we read it.
 */
	close_kvm();
#endif	/* defined(solaris) */

}


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

void
initialize()
{
	get_kernel_access();
	iuidcache(UIDCACHEL);

#if	defined(solaris)
/*
 * Read Solaris file system information and construct the clone table.
 *
 * The clone table is needed to identify sockets.
 */
	readfsinfo();

# if	defined(HASDCACHE)
	readdev();
# else	/* !defined(HASDCACHE) */
	read_clone();
# endif	/*defined(HASDCACHE) */
#else	/* !defined(solaris) */
/*
 * Create current working and root directory buffers.
 */
	if ((Cwd = (char *)malloc(MAXPATHLEN)) == NULL) {
		(void) fprintf(stderr, "%s: no space for cwd buffer\n", Pn);
		exit(1);
	}
	if ((Rtd = (char *)malloc(MAXPATHLEN)) == NULL) {
		(void) fprintf(stderr,
			"%s: no space for root directory buffer\n", Pn);
		exit(1);
	}
	*Cwd = *Rtd = '\0';
#endif	/* solaris */

}


/*
 * 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;
/*
 * Because lsof reads kernel data and follows pointers found there at a
 * rate considerably slower than the kernel, lsof sometimes acquires
 * invalid pointers.  If the invalid pointers are fed to kvm_read(),
 * a segmentation violation may result, so legal kernel addresses are
 * limited by the value of the KERNELBASE symbol (Kb value from the
 * kernel's _kernelbase variable for Solaris 2.5 and above).
 */

#if	defined(solaris) && solaris>=20500
	if ((u_long)addr < Kb)
#else	/* !defined(solaris) || solaris<20500 */
	if ((u_long)addr < (u_long)KERNELBASE)
#endif	/* defined(solaris) && solaris>=20500 */

		return(1);
	br = kvm_read(Kd, (u_long)addr, buf, len);
	return((br == len) ? 0 : 1);
}


/*
 * open_kvm() - open kernel virtual memory access
 */

static void
open_kvm()
{
	if (Kd != NULL)
		return;

#if	defined(solaris) && defined(WILLDROPGID)
/*
 * If this Solaris process began with setgid permission and its been
 * surrendered, regain it.
 */
	if (Switchgid == 2 && !Setgid) {
		if (setgid(Savedgid) != 0) {
			(void) fprintf(stderr,
				"%s: can't set effective GID to %d: %s\n",
				Pn, Savedgid, strerror(errno));
			exit(1);
		}
		Setgid = 1;
	}
#endif	/* defined(solaris) && defined(WILLDROPGID) */

	if ((Kd = kvm_open(Nmlst, Memory, NULL, O_RDONLY, NULL)) == NULL) {
		(void) fprintf(stderr,
			"%s: kvm_open (namelist=%s, core=%s): %s\n",
			Pn,
			Nmlst ? Nmlst : "default",
			Memory  ? Memory  : "default",
			strerror(errno));
		exit(1);
	}

#if	defined(WILLDROPGID)
/*
 * If this process has setgid permission, and is willing to surrender it,
 * do so.
 */
	(void) dropgid();

# if	defined(solaris)
/*
 * If this Solaris process must switch GIDs, enable switching after the
 * first call to this function.
 */
	if (Switchgid == 1)
		Switchgid = 2;
# endif	/* defined(solaris) */
#endif	/* define(WILLDROPGID) */

}


/*
 * process_text() - process text access information
 */

#if	defined(solaris) && solaris>=20400
#define S_NEXT s_next.list
#else	/* !defined(solaris) || solaris<20400 */
#define S_NEXT s_next
#endif	/* defined(solaris) && solaris>=20400 */

static void
process_text(pa)
	struct as *pa;			/* address space description pointer */
{
	struct as as;
	int i, j, k, l;
	struct seg s;
	union segdata {
		struct segvn_data vn;
		struct segdev_data dev;
		struct segmap_data map;
	} sd;
	struct vnode *v[MAXSEGS];
/*
 * Get address space description.
 */
	if (kread((KA_T)pa, (char *)&as, sizeof(as))) {
		alloc_lfile(" txt", -1);
		(void) sprintf(Namech, "can't read text segment list (%#x)",
			pa);
		enter_nm(Namech);
		link_lfile();
		return;
	}
/*
 * Loop through the segments.  The loop should stop when the segment
 * pointer returns to its starting point, but just in case, it's stopped
 * when MAXSEGS have been recorded or 2*MAXSEGS have been examined.
 */
	s.s_next = as.a_segs;
	for (i = j = k = 0; i < MAXSEGS && j < 2*MAXSEGS; j++) {
		if (s.S_NEXT == NULL
		||  kread((KA_T)s.S_NEXT, (char *)&s, sizeof(s)))
			break;
		if ((u_long)s.s_ops == Nl[X_SEGVN].n_value
		&&  s.s_data != NULL) {
			if (kread((KA_T)s.s_data, (char *)&sd, sizeof(sd)))
				break;
			if (sd.vn.vp) {
			
			/*
			 * This is a virtual node segment.
			 *
			 * If its vnode pointer has not been seen already,
			 * print its information.
			 */
				for (l = 0; l < k; l++) {
					if (v[l] == sd.vn.vp)
						break;
				}
				if (l >= k) {
					alloc_lfile(" txt", -1);
					process_node((caddr_t)sd.vn.vp);
					if (Lf->sf) {
						link_lfile();
						i++;
					}
					v[k++] = sd.vn.vp;
				}
			}
		}
	/*
	 * Follow the segment link to the starting point in the address
	 * space description.  (The i and j counters place an absolute
	 * limit on the loop.)
	 */

#if	!defined(solaris) || solaris<20400
		if (s.s_next == as.a_segs)
#else	/* solaris>=20400 */
		if (s.s_next.list == as.a_segs.list)
#endif	/* !defined(solaris) && solaris<20400 */

			break;
	}
}


#if	defined(solaris)
/*
 * readfsinfo() - read file system information
 */

static void
readfsinfo()
{
	char buf[FSTYPSZ+1];
	int i;

	if ((Fsinfomax = sysfs(GETNFSTYP)) == -1) {
		(void) fprintf(stderr, "%s: sysfs(GETNFSTYP) error: %s\n",
			Pn, strerror(errno));
		exit(1);
	} 
	if (Fsinfomax == 0)
		return;
	if ((Fsinfo = (char **)malloc((MALLOC_S)(Fsinfomax * sizeof(char *))))
	== NULL) {
		(void) fprintf(stderr, "%s: no space for sysfs info\n", Pn);
		exit(1);
	}
	for (i = 1; i <= Fsinfomax; i++) {
		if (sysfs(GETFSTYP, i, buf) == -1) {
			(void) fprintf(stderr,
				"%s: sysfs(GETFSTYP) error: %s\n",
				Pn, strerror(errno));
			exit(1);
		}
		if (buf[0] == '\0') {
			Fsinfo[i-1] = "";
			continue;
		}
		buf[FSTYPSZ] = '\0';
		if ((Fsinfo[i-1] = (char *)malloc((MALLOC_S)(strlen(buf) + 1)))
		== NULL) {
			(void) fprintf(stderr,
				"%s: no space for file system entry %s\n",
				Pn, buf);
			exit(1);
		}
		(void) strcpy(Fsinfo[i-1], buf);

# if	defined(HAS_AFS)
		if (strcasecmp(buf, "afs") == 0)
			AFSfstype = i;
# endif	/* defined(HAS_AFS) */

	}
}
#endif	/* solaris */


/*
 * read_proc() - read proc structures
 *
 * As a side-effect, Kd is set by a call to kvm_open().
 */

static void
read_proc()
{
	MALLOC_S len;
	static int sz = 0;
	int try;
	struct proc *p;

#if	defined(solaris)
	struct pid pg, pids;
#endif	/* defined(solaris) */

/*
 * Try PROCTRYLM times to read a valid proc table.
 */
	for (try = 0; try < PROCTRYLM; try++) {

	/*
	 * Pre-allocate proc structure space.
	 */
	    if (sz == 0) {
		sz = Knp + PROCDFLT/4;
		len = (sz * sizeof(struct proc));
		if ((P = (struct proc *)malloc(len)) == (struct proc *)NULL) {
		    (void) fprintf(stderr, "%s: no proc table space\n", Pn);
		    exit(1);
		}

#if	defined(solaris)
	/*
	 * Pre-allocate space for Solaris PGRP and PID numbers.
	 */
		len = (MALLOC_S)(sz * sizeof(int));
		if (Fpgrp) {
		    if ((Pgrp = (int *)malloc(len)) == (int *)NULL) {
			(void) fprintf(stderr, "%s: no PGRP table space\n", Pn);
			exit(1);
		    }
		}
		if ((Pid = (int *)malloc(len)) == (int *)NULL) {
		    (void) fprintf(stderr, "%s: no PID table space\n", Pn);
		    exit(1);
		}
#endif	/* defined(solaris) */

	    }

	/*
	 * Prepare for a proc table scan.
	 */
	    open_kvm();
	    if (kvm_setproc(Kd) != 0) {
		(void) fprintf(stderr, "%s: kvm_setproc: %s\n", Pn,
		    strerror(errno));
		exit(1);
	    }
	/*
	 * Accumulate proc structures.
	 */
	    Np = 0;
	    while ((p = kvm_nextproc(Kd)) != NULL) {
		if (p->p_stat == 0 || p->p_stat == SZOMB)
		    continue;

#if	defined(solaris)
		/*
		 * Read Solaris PGRP and PID numbers.
		 */
		if (Fpgrp) {
		    if (p->p_pgidp == NULL
		    ||  kread((KA_T)p->p_pgidp, (char *)&pg, sizeof(pg)))
			continue;
		}
		if (p->p_pidp == NULL
		||  kread((KA_T)p->p_pidp, (char *)&pids, sizeof(pids)))
		    continue;
#endif	/* defined(solaris) */

		if (Np >= sz) {

		/*
		 * Expand the local proc table.
		 */
		    sz += PROCDFLT/2;
		    len = (MALLOC_S)(sz * sizeof(struct proc));
		    if ((P = (struct proc *)realloc((MALLOC_P *)P, len))
		    == NULL) {
			(void) fprintf(stderr,
			    "%s: no more (%d) proc space\n", Pn, sz);
			exit(1);
		    }

#if	defined(solaris)
		/*
		 * Expand the Solaris PGRP and PID tables.
		 */
		    len = (MALLOC_S)(sz * sizeof(int));
		    if (Fpgrp) {
			if ((Pgrp = (int *)realloc((MALLOC_P *)Pgrp, len))
			== NULL) {
			    (void) fprintf(stderr,
				"%s: no more (%d) PGRP space\n", Pn, sz);
			    exit(1);
			}
		    }
		    if ((Pid = (int *)realloc((MALLOC_P *)Pid, len))
		    == NULL) {
			(void) fprintf(stderr,
			    "%s: no more (%d) PID space\n", Pn, sz);
			exit(1);
		    }
#endif	/* defined(solaris) */

		}

#if	defined(solaris)
	    /*
	     * Save the Solaris PGRP and PID numbers in
	     * local tables.
	     */
		if (Fpgrp)
		    Pgrp[Np] = (int) pg.pid_id;
		Pid[Np] = (int) pids.pid_id;
#endif	/* defined(solaris) */

	    /*
	     * Save the proc structure in a local table.
	     */
		P[Np++] = *p;
	    }
	/*
	 * If not enough processes were saved in the local table, try again.
	 */
	    if (Np >= PROCMIN)
		break;

#if	defined(solaris)
	    close_kvm();
#endif	/* defined(solaris) */

	}
/*
 * Quit if no proc structures were stored in the local table.
 */
	if (try >= PROCTRYLM) {
	    (void) fprintf(stderr, "%s: can't read proc table\n", Pn);
	    exit(1);
	}
	if (Np < sz && !RptTm) {

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

#if	defined(solaris)
	/*
	 * Reduce the Solaris PGRP and PID tables to their minimum if
	 * not in repeat mode.
	 */
	    len = (MALLOC_S)(Np * sizeof(int));
	    if (Fpgrp) {
		if ((Pgrp = (int *)realloc((MALLOC_P *)Pgrp, len)) == NULL) {
		    (void) fprintf(stderr,
			"%s: can't reduce PGRP table to %d\n", Pn, Np);
		    exit(1);
		}
	    }
	    if ((Pid = (int *)realloc((MALLOC_P *)Pid, len)) == NULL) {
		(void) fprintf(stderr,
		    "%s: can't reduce PID table to %d\n", Pn, Np);
		exit(1);
	    }
#endif	/* defined(solaris) */

	}
}


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


