/*
 * dproc.c - /dev/kmem-based 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 2001/02/13 13:54:36 abe Exp $";
#endif

#if	defined(HPUXKERNBITS)
# if	HPUXKERNBITS>=64
#define _INO_T
typedef int ino_t;
#define _TIME_T
typedef int time_t;
# else	/* HPUXKERNBITS<64 */
#define	_RLIM_T
#  if !defined(__STDC_32_MODE__)
typedef unsigned long long rlim_t;
#  else	/* defined(__STDC_32_MODE__) */
typedef	unsigned long rlim_t;
#  endif	/* !defined(__STDC_32_MODE__) */
# endif	/* HPUXKERNBITS>=64 */
#endif	/* defined(HPUXKERNBITS) */

#include "lsof.h"

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


#if	HPUXV>=1010
/*
 * HP doesn't include a definition for the proc structure in HP-UX 10.10
 * or above in an attempt to force use of pstat(2).  Unfortunately, pstat(2)
 * doesn't return the information lsof needs.  Hence, this private proc
 * structure definition.
 */

#include <sys/vas.h>

#define	SZOMB	3

# if	HPUXV<1020
struct proc {
	caddr_t d1[2];			/* dummy to occupy space */
	caddr_t p_firstthreadp;		/* thread pointer */
	caddr_t d2[4];			/* dummy to occupy space */
	int p_stat;			/* process status */
	caddr_t d3[9];			/* dummy to occupy space */
	uid_t p_uid;			/* UID */
	caddr_t d4[2];			/* dummy to occupy space */
	gid_t p_pgid;			/* process group ID */
	pid_t p_pid;			/* PID */
	pid_t p_ppid;			/* parent PID */
	caddr_t d5[9];			/* dummy to occupy space */
	vas_t *p_vas;			/* virtual address space */
	caddr_t d6[16];			/* dummy to occupy space */
	int p_maxof;			/* max open files allowed */
	struct vnode *p_cdir;		/* current directory */
	struct vnode *p_rdir;		/* root directory */
	struct ofile_t **p_ofilep;	/* file descriptor chunks */
	caddr_t d7[43];			/* dummy to occupy space */
};
# endif	/* HPUXV<1020 */

# if	HPUXV>=1020 && HPUXV<1030
struct proc {
	caddr_t d1[2];			/* dummy to occupy space */
	caddr_t p_firstthreadp;		/* thread pointer */
	caddr_t d2[6];			/* dummy to occupy space */
	int p_stat;			/* process status */
	caddr_t d3[14];			/* dummy to occupy space */
	uid_t p_uid;			/* real UID */
	uid_t p_suid;			/* effective UID */
	caddr_t d4;			/* dummy to occupy space */
	gid_t p_pgid;			/* process group ID */
	pid_t p_pid;			/* PID */
	pid_t p_ppid;			/* parent PID */
	caddr_t d5[9];			/* dummy to occupy space */
	vas_t *p_vas;			/* virtual address space */
	caddr_t d6[16];			/* dummy to occupy space */
	int p_maxof;			/* max open files allowed */
	struct vnode *p_cdir;		/* current directory */
	struct vnode *p_rdir;		/* root directory */
	struct ofile_t **p_ofilep;	/* file descriptor chunks */
	caddr_t d7[84];			/* dummy to occupy space */
};
# endif	/* HPUXV>=1020 && HPUXV<1030 */
#endif	/* HPUXV<1010 */


/*
 * 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 Nva = 0;		/* number of entries allocated to
					 * vnode address cache */
static KA_T *Vp = (KA_T *)NULL;		/* vnode address cache */
#endif	/* HPUXV>=800 */


_PROTOTYPE(static void get_kernel_access,(void));

#if	HPUXV>=800
_PROTOTYPE(static void process_text,(KA_T vasp));
#endif	/* HPUXV>=800 */


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

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

#if	HPUXV>=1020
struct ofile_t {
	struct ofa {
		KA_T ofile;
		int d1;
		int pofile;
	} ofa[SFDCHUNK];
};
#endif	/* HPUXV>=1020 */

#if	HPUXV>=800
	char *c, *s;
	KA_T pfp, ofp;
	struct ofile_t oft;
	struct pst_status ps;

# if	HPUXV<1010
	struct user us;
# else	/* HPUXV>=1010 */
	struct user {
		char u_comm[PST_CLEN];
	} us;
# endif	/* HPUXV<1010 */
#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;
	struct proc pbuf;
	short pss, sf;
	int px;
	struct user *u;

#if	defined(HASFSTRUCT)
# if	HPUXV>=1020 || (HPUXV>=900 && HPUXV<1000)
#define	USESPOFILE	1
	long pof;
# endif	/* HPUXV>=1020 || (HPUXV>=900 && HPUXV<1000) */
#endif	/* defined(HASFSTRUCT) */

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

#if	HPUXV>=800
	u = &us;
	(void) zeromem((char *)u, U_SIZE);
	for (p = &pbuf, px = 0; px < Np; px++)
#else	/* HPUXV<800 */
	for (p = &pbuf, px = 0, u = (struct user *)us; px < Np; px++)
#endif	/* HPUXV>=800 */

	{
	    Kpa = Kp + (KA_T)(px * sizeof(struct proc));
	    if (kread(Kpa, (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_pgid, (UID_ARG)p->p_uid,
			     &pss, &sf))
		continue;

#if	HPUXV>=1010
	/*
	 * Save the kernel thread pointer.
	 */
	    Ktp = (KA_T)p->p_firstthreadp;
#endif	/* HPUXV>=1010 */

	/*
	 * Read the user area.
	 */

#if	HPUXV>=800
	/*
	 * Use the pstat() syscall to read process status.
	 */

	    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;
	    }
	/*
	 * Use the pst_cmd command buffer.
	 */
	    c = ps.pst_cmd;
	    ps.pst_cmd[PST_CLEN - 1] = '\0';	/* paranoia */
	/*
	 * Skip to the last component of the first path name.  Also skip any
	 * leading `-', signifying a login shell.  Copy the result to u_comm[].
	 */
	    if (*c == '-')
		c++;
	    for (s = c; *c && (*c != ' '); c++) {
		if (*c == '/')
		    s = c + 1;
	    }
	    for (i = 0; i < MAXCOMLEN; i++) {
		if (*s == '\0' || *s == ' ' || *s == '/')
		    break;
		u->u_comm[i] = *s++;
	    }
	    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 *)Kpa), (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_pgid, (int)p->p_ppid,
			(UID_ARG)p->p_uid, u->u_comm, (int)pss, (int)sf);
	    Plf = (struct lfile *)NULL;
	/*
	 * Save current working directory information.
	 */
	    if (CURDIR) {
		alloc_lfile(CWD, -1);
		process_node((KA_T)CURDIR);
		if (Lf->sf)
		    link_lfile();
	    }
	/*
	 * Save root directory information.
	 */
	    if (ROOTDIR) {
		alloc_lfile(RTD, -1);
		process_node((KA_T)ROOTDIR);
		if (Lf->sf)
		    link_lfile();
	    }

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

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

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

	    {

#if	HPUXV>=800
		if (j >= SFDCHUNK) {
		    if (!pfp
		    ||  kread((KA_T)pfp, (char *)&ofp, sizeof(ofp))
		    ||  !ofp
		    ||  kread((KA_T)ofp, (char *)&oft, sizeof(oft)))
			break;
		    j = 0;
		    pfp += sizeof(KA_T);
		}
		j++;

# if	HPUXV>=1020
#  if	defined(USESPOFILE)
		pof = (long)oft.ofa[j - 1].pofile;
#  endif	/* defined(USESPOFILE) */

		if ((fp = (KA_T)oft.ofa[j - 1].ofile))
# else	/* HPUXV<1020 */
#  if	defined(USESPOFILE)
		pof = (long)oft.pofile[j - 1];
#  endif	/* defined(USESPOFILE) */

		if ((fp = (KA_T)oft.ofile[j - 1]))
# endif	/* HPUXV>=1020 */
#else	/* HPUXV<800 */
		if (j >= SFDCHUNK) {

		/*
		 * Get next file pointer "chunk".
		 */
		    while (++k < NFDCHUNKS && !u->u_ofilep[k])
			;
		    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 ((fp = (KA_T)u->u_ofile.ofile[j - 1]))
#endif	/* HPUXV>=800 */

		/*
		 * Process the file pointer.
		 */

		{
		    alloc_lfile(NULL, i);
		    process_file(fp);
		    if (Lf->sf) {

#if	defined(USESPOFILE)
			if (Fsv & FSV_FG)
			    Lf->pof = pof;
#endif	/* defined(USESPOFILE) */

			link_lfile();
		    }
		}
	    }
	/*
	 * Examine results.
	 */
	    if (examine_lproc())
		return;
	}
}


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

static void
get_kernel_access()
{
	KA_T v;
/*
 * Check the kernel version.
 */
	(void) ckkv("HP-UX", LSOF_VSTR, (char *)NULL, (char *)NULL);

#if	HPUXV>=1030
/*
 * See if build and run bit sizes match.  Exit if they don't.
 */
	{
	    long rv;

	    if ((rv = sysconf(_SC_KERNEL_BITS)) < 0) {
		(void) fprintf(stderr,
		    "%s: sysconf(_SC_KERNEL_BITS) returns: %s\n",
		    Pn, strerror(errno));
		Exit(1);
	    }
	    if (rv != (long)HPUXKERNBITS) {
		(void) fprintf(stderr,
		    "%s: FATAL: %s was built for a %d bit kernel, but this\n",
		    Pn, Pn, HPUXKERNBITS);
		(void) fprintf(stderr, "      is a %ld bit kernel.\n", rv);
		Exit(1);
	    }
	}
#endif	/* HPUXV>=1030 */

#if	defined(HAS_AFS)
	struct NLIST_TYPE *nl = (struct NLIST_TYPE *)NULL;
#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 || 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 ((Kd = open(Memory ? Memory : KMEM, O_RDONLY, 0)) < 0) {
	    int errno_save = errno;

	    (void) fprintf(stderr, "%s: can't open ", Pn);
	    safestrprt(Memory ? Memory : KMEM, stderr, 0);
	    (void) fprintf(stderr, ": %s\n", strerror(errno_save));
	    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) */

	(void) build_Nl(Drive_Nl);

#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_TYPE *)malloc(Nll))) {
		(void) fprintf(stderr,
		    "%s: no space (%d) for Nl[] copy\n", Pn, Nll);
		Exit(1);
	    }
	    (void) memcpy((void *)nl, (void *)Nl, (size_t)Nll);
	}
#endif	/* defined(HAS_AFS) */

/*
 * Access kernel symbols.
 */
	if (NLIST_TYPE(Nmlst ? Nmlst : N_UNIX, Nl) < 0) {
	    (void) fprintf(stderr, "%s: can't read namelist from: ", Pn);
	    safestrprt(Nmlst ? Nmlst : N_UNIX, stderr, 1);
            Exit(1);
	}
	if (get_Nl_value("proc", Drive_Nl, &v) < 0 || !v
	||  kread((KA_T)v, (char *)&Kp, sizeof(Kp))
	||  get_Nl_value("nproc", Drive_Nl, &v) < 0 || !v
	||  kread((KA_T)v, (char *)&Np, sizeof(Np))
	||  !Kp || Np < 1) {
	    (void) fprintf(stderr, "%s: can't read proc table info\n", Pn);
	    Exit(1);
	}
	if (get_Nl_value("vfops", Drive_Nl, (KA_T *)&Vnfops) < 0)
	    Vnfops = (KA_T)NULL;

#if	HPUXV<800 && defined(hp9000s300)
	if (get_Nl_value("upmap", Drive_Nl, (unsigned long *)&Usrptmap) < 0) {
	    (void) fprintf(stderr, "%s: can't get kernel's Usrptmap\n", Pn);
	    Exit(1);
	}
	if (get_Nl_value("upt", Drive_Nl, (unsigned long *)&usrpt) < 0) {
	    (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 (get_Nl_value("ubase", Drive_Nl, (unsigned long *)&ubase) < 0) {
	    (void) fprintf(stderr, "%s: can't get kernel's ubase\n", Pn);
	    Exit(1);
	}
	if (get_Nl_value("npids", Drive_Nl, &v) < 0 || !v
	||  kread((KA_T)v, (char *)&npids, sizeof(npids))) {
	    (void) fprintf(stderr, "%s: can't get kernel's npids\n", Pn);
	    Exit(1);
	}
#endif	/* HPUXV<800 && defined(hp9000s800) */

#if	HPUXV>=1030
	if (get_Nl_value("clmaj", Drive_Nl, &v) < 0 || !v
	||  kread((KA_T)v, (char *)&CloneMaj, sizeof(CloneMaj)))
	    HaveCloneMaj = 0;
	else
	    HaveCloneMaj = 1;
#endif	/* HPUXV>=1030 */

#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 ((get_Nl_value("arFid", Drive_Nl, &v) >= 0 && !v)
	    ||  (get_Nl_value("avops", Drive_Nl, &v) >= 0 && !v)
	    ||  (get_Nl_value("avol", Drive_Nl, &v) >= 0 && !v))
		(void) ckAFSsym(nl);
	    (void) free((FREE_P *)nl);
	}
#endif	/* defined(HAS_AFS) */

}


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

void
initialize()
{
	get_kernel_access();
}


/*
 * 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(Kd, addr, L_SET) == (off_t)-1L)
	    return(-1);
	br = read(Kd, 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)
	KA_T vasp;			/* kernel's virtual address space
					 * pointer */
{
	char fd[FDLEN];
	int i, j, lm;
	MALLOC_S len;
	struct pregion p;
	KA_T prp;
	struct region r;
	struct vas v;
	KA_T va;
/*
 * Read virtual address space pointer.
 */
	if (kread(vasp, (char *)&v, sizeof(v)))
	    return;
/*
 * Follow the virtual address space pregion structure chain.
 */
	for (i = lm = 0, prp = (KA_T)v.va_next;
	     prp != vasp;
	     prp = (KA_T)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(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 = (KA_T)r.r_fstore))
		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 (i >= Nva) {
		Nva += 10;
		len = (MALLOC_S)(Nva * sizeof(KA_T));
		if (!Vp)
		    Vp = (KA_T *)malloc(len);
		else
		    Vp = (KA_T *)realloc((MALLOC_P *)Vp, len);
		if (!Vp) {
		    (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) snpf(fd, sizeof(fd), "R%02d", p.p_type);
		alloc_lfile(fd, -1);
	    }
	/*
	 * Save vnode information.
	 */
	    process_node(va);
	    if (Lf->sf)
		link_lfile();
	}
}
#endif	/* HPUXV>=800 */
