#ifndef lint
static char rcsid[] = "$Header: hpux-10.c,v 1.3 1999/01/05 16:24:33 forys Exp $";
#endif

/*
**  This program may be freely redistributed for noncommercial purposes.
**  This entire comment MUST remain intact.
**
**  HP-UX 10 support by Aaron Denney <wnoise@ugcs.caltech.edu> and Jeff Forys.
**
**  Copyright 1994, 1997 by Jeff Forys (jeff@forys.cranbury.nj.us)
*/

#define	_XPG4_EXTENDED

#define	NO_MEXTERN
#include "conf.h"
#undef	NO_MEXTERN

#include <unistd.h>
#include <stdio.h>
#include <string.h>

#include <sys/resource.h>

/*
** Note: HP-UX finally supports setpriority(2); snice has been changed to
** use this, rather than rtprio(2).  If you need the old behavior, you can
** #define OLD_RTPRIO.
*/

#ifdef	OLD_RTPRIO
#include <sys/rtprio.h>
#endif

/*
 * Define SigNames, NSig, and TtyDevDir here; they are used by other
 * routines and must be global.  Everyone seems to have their own
 * idea as to what NSIG should be.  Here, `NSig' is the number of
 * signals available, not counting zero.
 */
char *SigMap[] = { "0",
	"HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT",		/*  1 -  6 */
	"EMT", "FPE", "KILL", "BUS", "SEGV", "SYS",		/*  7 - 12 */
	"PIPE", "ALRM", "TERM", "USR1", "USR2", "CHLD",		/* 13 - 18 */
	"PWR", "VTALRM", "PROF", "IO", "WINCH", "STOP",		/* 19 - 24 */
	"TSTP", "CONT", "TTIN", "TTOU", "URG", "LOST",		/* 25 - 30 */
	"31", "32", "CPU", "FSZ", "CANCEL", "GFAULT",		/* 31 - 36 */
	"RTMIN", "RTMIN+1", "RTMIN+2", "RTMIN+3", "RTMAX-3",	/* 37 - 41 */
	"RTMAX-2", "RTMAX-1", "RTMAX"				/* 42 - 44 */
};
int NSig = NSIG-1;

#define	SETCMD(dst,src,maxlen) {			\
	extern char *strrchr();				\
	if (maxlen > 0) src[maxlen] = '\0';		\
	dst = (dst = strrchr(src, '/')) ? ++dst: src;	\
}

static char *TtyDevDir = "/dev";

int	Skill;			/* set 1 if running `skill', 0 if `snice' */
int	PrioMin, PrioMax;	/* min and max process priorities */
int	SigPri;			/* signal to send or priority to set */
pid_T	MyPid;			/* pid of this process */
uid_T	MyUid;			/* uid of this process */
char	*ProgName;		/* program name */

/*
 * This is the machine-dependent initialization routine.
 *
 *   - The following global variables must be initialized:
 *     MyPid, MyUid, ProgName, Skill, PrioMin, PrioMax, SigPri
 *   - The working directory will be changed to that which contains the
 *     tty devices (`TtyDevDir'); this makes argument parsing go faster.
 *   - If possible, this routine should raise the priority of this process.
 */
void
MdepInit(pname)
	char *pname;
{
	extern char *rindex(), *SysErr();

	MyPid = (pid_T) getpid();
	MyUid = (uid_T) getuid();
	SETCMD(ProgName, pname, 0)

	/*
	 * If we are running as root, raise our priority to better
	 * catch runaway processes.
	 */
	if (MyUid == ROOTUID)
#ifdef OLD_RTPRIO
		(void) rtprio(MyPid, RTPRIO_MIN);
#else
		(void) setpriority(PRIO_PROCESS, MyPid, PRIO_MIN);
#endif

	/*
	 * Determine what we are doing to processes we find.  We will
	 * either send them a signal (skill), or renice them (snice).
	 */
	Skill = (strstr(ProgName, "snice") == NULL);

	/*
	 * chdir to `TtyDevDir' to speed up tty argument parsing.
	 */
	if (chdir(TtyDevDir) < 0) {
		fprintf(stderr, "%s: chdir(%s): %s\n", ProgName, TtyDevDir,
		        SysErr());
		exit(EX_SERR);
	}

	/*
	 * Set up minimum and maximum process priorities.
	 * Initialize SigPri to either default signal (`skill') or
	 * default priority (`snice').
	 */
#ifdef OLD_RTPRIO
	PrioMin = RTPRIO_MIN;
	PrioMax = RTPRIO_MAX;
	SigPri = Skill? SIGTERM: RTPRIO_RTOFF;
#else
	PrioMin = PRIO_MIN;
	PrioMax = PRIO_MAX;
	SigPri = Skill? SIGTERM: 4;
#endif
}

/*
 * Carry out an action on a particular process.  If this is `skill',
 * then send the process a signal, otherwise this is `snice' so change
 * it's priority.
 *
 * If 0 is returned, the operation was successful, otherwise -1 is
 * returned and `errno' set.
 */
int
MdepAction(pid)
	pid_T pid;
{
	if (Skill)
		return(kill((int)pid, SigPri));
	else
#ifdef OLD_RTPRIO
		return(rtprio((int)pid, SigPri));
#else
		return(setpriority(PRIO_PROCESS, (int)pid, SigPri));
#endif
}

/*
 * Now, set up everything we need to write a GetProc() routine.
 */

#include <sys/pstat.h>

#define	PROCSLOP	32	/* additional process slots to alloc */

/*
 * GetProc()
 *
 * Fill in and return a `struct ProcInfo' with information about the
 * next process.  If no processes are left, return NULL.
 */
struct ProcInfo *
GetProc()
{
	extern char *SysErr();
	static struct ProcInfo procinfo;
	static struct pst_status *proclist = NULL, *aproc, *e_proclist;
	struct pst_dynamic pdyn;

	if (proclist == NULL) {
		register int pret;
		int nprocs;

		if ((pret = pstat_getdynamic(&pdyn, sizeof(pdyn), 1, 0)) < 0) {
			fprintf(stderr, "%s: pstat_getdynamic: %s\n",
				ProgName, SysErr());
			exit(2);
		}

		nprocs = pdyn.psd_activeprocs + PROCSLOP;
		if ((proclist = (struct pst_status *)
				calloc(nprocs, sizeof(*aproc))) == NULL) {
			fprintf(stderr,
				"%s: calloc: can't alloc %d proc tables\n",
				ProgName, nprocs);
			exit(2);
		}
		if ((pret=pstat_getproc(proclist,sizeof(*aproc),nprocs,0)) < 0){
			fprintf(stderr, "%s: pstat_getproc: %s\n",
				ProgName, SysErr());
			exit(2);
		}
		aproc = proclist;
		e_proclist = proclist + pret;
	}

	if (aproc == e_proclist) {
		free(proclist);
		proclist = NULL;
		return NULL;
	}

	procinfo.pi_cmd = aproc->pst_ucomm;
	procinfo.pi_pid = aproc->pst_pid;
	procinfo.pi_uid = aproc->pst_uid;
	procinfo.pi_flags = 0;

	/*
	 * If parent proc is pid 0, ask user for confirmation.
	 */
	if (aproc->pst_ppid == 0)
		procinfo.pi_flags |= PI_ASKUSR;

	if (aproc->pst_term.psd_major != -1) {
		procinfo.pi_flags |= PI_CTLTTY;
		procinfo.pi_tty = makedev(aproc->pst_term.psd_major,
			aproc->pst_term.psd_minor);
	}

	if (aproc->pst_stat == PS_ZOMBIE)
		procinfo.pi_flags |= PI_ZOMBIE;

	/*
	 * N.B. PS_OTHER pst_stat can be either forking or exiting.
	 * Since there is apparently no way to differentiate, we
	 * ignore ths flag rather than risk a misclassification.
	 */

	aproc++;
	return(&procinfo);
}
