/*
 * pg_top - a top PostgreSQL users display for Unix
 *
 * SYNOPSIS:  SCO UNIX OpenServer5
 *
 * DESCRIPTION:
 * This is the machine-dependent module for SCO OpenServer5.
 * Originally written for BSD4.3 system by Christos Zoulas.
 * Modified to m_sco.c (3.2v4.2)  by Gregory Shilin <shilin@onyx.co.il>
 * Modified to m_sco5.c (3.2v5.*) by Mike Hopkirk <hops@sco.com>
 * Works for:
 * SCO UNIX 3.2v5.*
 *
 * CFLAGS: -DHAVE_GETOPT -DORDER
 *
 * AUTHOR: Mike Hopkirk (hops@sco.com)
 * hops 10-Jul-98 - added sort fields
 *		17-Jul-98 - add philiph's chopped cmd string support
 *					  (define NO_COMMAND_ARGS to enable )
 *		09-Dec-98 - provide RSS calculation
 *		15-Mar-2000 - Fix broken lines and cleanup sysinfo access w macros
 */

#include <sys/types.h>
#include <sys/param.h>

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <nlist.h>
#include <math.h>
#include <signal.h>
#include <string.h>

#include <sys/dir.h>
#include <sys/immu.h>
#include <sys/region.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/sysinfo.h>
#include <sys/systm.h>
#include <sys/sysmacros.h>
#include <sys/var.h>
#include <sys/sysi86.h>

#include "pg_top.h"
#include "machine.h"
#include "utils.h"
#include "loadavg.h"

/*
typedef unsigned long  ulong;
typedef unsigned int   uint;
typedef unsigned short ushort;
*/
typedef unsigned char uchar;

#define VMUNIX	"/unix"
#define KMEM	"/dev/kmem"
#define MEM		"/dev/mem"

#define SI_ACTIVE(p)   p->p_active
#define SI_TOTAL(p)    p->p_total

/* get_process_info passes back a handle. This is what it looks like: */
struct handle
{
	struct proc **next_proc;	/* points to next valid proc pointer */
	int			remaining;		/* number of pointers remaining */
};

/* define what weighted cpu is */
#define weighted_cpu(pct, pp) ((pp)->p_time == 0 ? 0.0 : \
			 ((pct) / (1.0 - exp((pp)->p_time * logcpu))))

#define bytetok(bytes) ((bytes) >> 10)

/* what we consider to be process size: */
#define PROCSIZE(up) bytetok(ctob((up)->u_tsize + (up)->u_dsize+(up)->u_ssize))

/* definitions for indices in the nlist array */
#define X_V				0		/* System configuration information */
#define X_PROC			1		/* process tables */
#define X_FREEMEM		2		/* current free memory */
#define X_AVAILRMEM		3		/* available resident (not swappable) mem in
								 * pages */
#define X_AVAILSMEM		4		/* available swappable memory in pages */
#define X_MAXMEM		5		/* maximum available free memory in clicks */
#define X_PHYSMEM		6		/* physical memory in clicks */
#define X_NSWAP			7		/* size of swap space in blocks */
#define X_HZ			8		/* ticks/second of the clock */
#define X_MPID			9		/* last process id */
#define X_SYSINFO		10		/* system information (cpu states) */
#define X_CUR_CPU		11

static struct nlist nlst[] = {
	{"v"},						/* 0 */
	{"proc"},					/* 1 */
	{"freemem"},				/* 2 */
	{"availrmem"},				/* 3 */
	{"availsmem"},				/* 4 */
	{"maxmem"},					/* 5 */
	{"physmem"},				/* 6 */
	{"nswap"},					/* 7 */
	{"Hz"},						/* 8 */
	{"mpid"},					/* 9 */
	{"sysinfo"},				/* 10 */
	{"cur_cpu"},				/* 11 */
	{NULL}
};

/*
 *	These definitions control the format of the per-process area
 */

static char header[] =
"  PID X        PRI NICE   SIZE   RES  STATE   TIME  COMMAND";

/* 0123456	 -- field to fill in starts at header+6 */
#define UNAME_START 6

#define Proc_format \
	"%5d %-8.8s %3d %4d  %5s %5dK %-5s %6s  %.28s"

static int	kmem,
			mem;

static double logcpu;

/* these are retrieved from the kernel in _init */
static int	Hz;
static struct var v;
static ulong proca;
static load_avg cur_cpu;

/* these are for detailing the process states */
int			process_states[8];
char	   *procstatenames[] = {
	"", " sleeping, ", " running, ", " zombie, ", " stopped, ",
	" created, ", " onproc, ", " xswapped, ",
	NULL
};

/* process state names for the "STATE" column of the display */
char	   *state_abbrev[] = {
	"", "sleep", "run", "zomb", "stop", "create", "onpr", "swap"
};

/* these are for calculating cpu state percentages */
#define CPUSTATES		5		/* definition from struct sysinfo */
static time_t cp_time[CPUSTATES];
static time_t cp_old[CPUSTATES];
static time_t cp_diff[CPUSTATES];

/* these are for detailing the cpu states */
int			cpu_states[CPUSTATES];
char	   *cpustatenames[] = {
	"idle", "user", "system", "wait", "sxbrk",
	NULL
};

/* these are for detailing the memory statistics */
unsigned long memory_stats[6];
char	   *memorynames[] = {
	"K phys, ", "K max, ", "K free, ", "K lck, ", "K unlck, ",
	"K swap,", NULL
};

/* these are for keeping track of the proc array */
static int	bytes;
static int	pref_len;
static struct proc *pbase;
static struct proc **pref;

/* forward definitions for comparison functions */
int			proc_compare();
int			compare_cpu();
int			compare_size();
int			compare_time();

int			(*proc_compares[]) () =
{
	proc_compare,				/* state, pri, time, size */
		compare_cpu,			/* cpu, time, state, pri, size */
		compare_size,			/* size, cpu, time, state pri  */
		compare_time,			/* time, cpu, state, pri, size */
/* compare_res,		/* res,  cpu, time, state pri  */
		NULL
};

/* these are names given to allowed sorting orders -- first is default */
char	   *ordernames[] = {
	"state", "cpu", "size", "time", NULL
};								/* hops */

/* useful externals */
extern int	errno;
extern char *sys_errlist[];

long		time();
long		percentages();

int
machine_init(struct statics *statics)

{
	ulong		ptr;

	if ((kmem = open(KMEM, O_RDONLY)) == -1)
	{
		perror(KMEM);
		return -1;
	}
	if ((mem = open(MEM, O_RDONLY)) == -1)
	{
		perror(MEM);
		return -1;
	}

	/* get the list of symbols we want to access in the kernel */
	if (nlist(VMUNIX, nlst) == -1)
	{
		fprintf(stderr, "pg_top: nlist failed\n");
		return -1;
	}
	/* make sure they were all found */

	/*
	 * ZZ if (check_nlist(nlst) > 0) return -1;
	 */

	proca = nlst[X_PROC].n_value;

	/* get the symbol values out of kmem */
	(void) getkval(nlst[X_CUR_CPU].n_value, (int *) (&cur_cpu), sizeof(cur_cpu),
				   nlst[X_CUR_CPU].n_name);
	(void) getkval(nlst[X_HZ].n_value, (int *) (&Hz), sizeof(Hz),
				   nlst[X_HZ].n_name);
	(void) getkval(nlst[X_V].n_value, (int *) (&v), sizeof(v),
				   nlst[X_V].n_name);

	/* this is used in calculating WCPU -- calculate it ahead of time */
	logcpu = log(fabs(loaddouble(cur_cpu)));

	/* allocate space for proc structure array and array of pointers */
	bytes = v.v_proc * sizeof(struct proc);
	pbase = (struct proc *) malloc(bytes);
	pref = (struct proc **) malloc(v.v_proc * sizeof(struct proc *));
	if (pbase == (struct proc *) NULL || pref == (struct proc **) NULL)
	{
		fprintf(stderr, "pg_top: cannot allocate sufficient memory\n");
		return -1;
	}

	/* fill in the statics information */
	statics->procstate_names = procstatenames;
	statics->cpustate_names = cpustatenames;
	statics->memory_names = memorynames;
	statics->order_names = ordernames;	/* hops */

	return 0;
}

char *
format_header(register char *uname_field)

{
	register char *ptr;

	ptr = header + UNAME_START;
	while (*uname_field != '\0')
	{
		*ptr++ = *uname_field++;
	}

	return (header);
}


/* philiph - get run ave fm /dev/table info */
static int
tab_avenrun(double runave[])
{
	FILE	   *fp = fopen("/dev/table/avenrun", "r");
	int			i;

	for (i = 0; i < 3; i++)
		runave[i] = -1.0;

	if (fp == NULL)
		return -1;
	else
	{
		short		rawave[3];

		if (fread(rawave, sizeof(short), 3, fp) != 3)
		{
			fclose(fp);
			return -1;
		}
		else
		{
			int			i;

			for (i = 0; i < 3; i++)
				runave[i] = (double) (rawave[i] / 256.0);

			fclose(fp);
			return 0;
		}
	}
}

struct pregion *
get_pregion(void *ptr)
{
	static struct pregion preg;
	long		addr = (long) ptr;

	(void) getkval(addr, (struct pregion *) (&preg),
				   sizeof(struct pregion), "pregion");
	return &preg;
}

struct region *
get_region(void *ptr)
{
	static struct region reg;
	long		addr = (long) ptr;

	(void) getkval(addr, (struct region *) (&reg),
				   sizeof(struct region), "region");
	return &reg;
}

static unsigned char shareable[RT_VM86 + 1];	/* 1 if shareable */

/*
 * sum private referenced pages,
 * treat shared pages depending on value of TREAT_SHARABLE_PAGES macro
 *		undefined : ignore (don't account for - default)
 *		1:	divide among # of references
 *		2:	accumulate as if private
 */
/* #define TREAT_SHAREABLE_PAGES 1 */
static long
proc_residentsize(struct proc *pp)
{
	struct pregion *prp;
	struct region *rp;
	long		rtot = 0;
	long		stot = 0;
	long		s1tot = 0;

	/* init shareable region array */
	if (shareable[RT_STEXT] == 0)
		shareable[RT_STEXT] = shareable[RT_SHMEM] = shareable[RT_MAPFILE] = 1
			;

	prp = pp->p_region;

	if (prp == 0)
		return 0;

	for (; prp && (prp = get_pregion((void *) (prp))) &&
		 prp->p_reg && (rp = get_region((void *) (prp->p_reg)));
		 prp = prp->p_next)
	{
		if (shareable[rp->r_type])	/* account for shared pgs separately */
		{
			stot += (rp->r_nvalid / rp->r_refcnt);
			s1tot += rp->r_nvalid;
		}
		else
			rtot += rp->r_nvalid;

	}
#if defined(TREAT_SHAREABLE_PAGES) && TREAT_SHAREABLE_PAGES == 1
	rtot += stot;				/* accumulate and spread over users */
#endif

#if defined(TREAT_SHAREABLE_PAGES) && TREAT_SHAREABLE_PAGES == 1
	rtot += s1tot;				/* accumulate as if private */
#endif

	return rtot * NBPP / 1024;;
}

void
get_system_info(struct system_info *si)

{
	long		total;

	/* get process id of the last process */
	(void) getkval(nlst[X_MPID].n_value, &(si->last_pid),
				   sizeof(si->last_pid),
				   nlst[X_MPID].n_name);
	/* get the cp_time array */
	(void) getkval(nlst[X_SYSINFO].n_value, (int *) cp_time, sizeof(cp_time),
				   nlst[X_SYSINFO].n_name);

	/* convert cp_time counts to persentages */
	total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);

	/* sum memory statistics */
	(void) getkval(nlst[X_PHYSMEM].n_value, &memory_stats[0],
				   sizeof(memory_stats[0]), nlst[X_PHYSMEM].n_name);
	(void) getkval(nlst[X_MAXMEM].n_value, &memory_stats[1],
				   sizeof(memory_stats[1]), nlst[X_MAXMEM].n_name);
	(void) getkval(nlst[X_FREEMEM].n_value, &memory_stats[2],
				   sizeof(memory_stats[2]), nlst[X_FREEMEM].n_name);
	(void) getkval(nlst[X_AVAILRMEM].n_value, &memory_stats[3],
				   sizeof(memory_stats[3]), nlst[X_AVAILRMEM].n_name);
	(void) getkval(nlst[X_AVAILSMEM].n_value, &memory_stats[4],
				   sizeof(memory_stats[4]), nlst[X_AVAILSMEM].n_name);
	(void) getkval(nlst[X_NSWAP].n_value, &memory_stats[5],
				   sizeof(memory_stats[5]), nlst[X_NSWAP].n_name);
	memory_stats[0] = bytetok(ctob(memory_stats[0]));	/* clicks -> bytes */
	memory_stats[1] = bytetok(ctob(memory_stats[1]));	/* clicks -> bytes */
	memory_stats[2] = bytetok(ctob(memory_stats[2]));	/* clicks -> bytes */
	memory_stats[3] = bytetok(memory_stats[3] * NBPP);	/* # bytes per page */
	memory_stats[4] = bytetok(memory_stats[4] * NBPP);	/* # bytes per page */
	memory_stats[5] = bytetok(memory_stats[5] * NBPSCTR);	/* # bytes per sector */

	/* set arrays and strings */

	/*
	 * Note: we keep memory_stats as an unsigned long to avoid sign extension
	 * problems when shifting in bytetok. But the module interface requires an
	 * array of signed longs. So we just cast the pointer here and hope for
	 * the best.   --wnl
	 */
	si->cpustates = cpu_states;
	si->memory = (long *) memory_stats;

	tab_avenrun(si->load_avg);	/* philiph */
}

static struct handle handle;

caddr_t
get_process_info(struct system_info *si,
				 struct process_select *sel,
				 int idx)

{
	register int i;
	register int total_procs;
	register int active_procs;
	register struct proc **prefp;
	register struct proc *pp;

/* set up flags of what we are going to select */
/* these are copied out of sel for simplicity */
	int			show_idle = sel->idle;
	int			show_system = sel->system;
	int			show_uid = sel->uid != -1;
	int			show_command = sel->command != NULL;

	/* read all the proc structures in one fell swoop */
	(void) getkval(proca, (int *) pbase, bytes, "proc array");

	/* get a pointer to the states summary array */
	si->procstates = process_states;

	/* count up process states and get pointers to interesting procs */
	total_procs = active_procs = 0;
	memset((char *) process_states, 0, sizeof(process_states));
	prefp = pref;
	for (pp = pbase, i = 0; i < v.v_proc; pp++, i++)
	{
		/*
		 * Place pointers to each valid proc structure in pref[]. Process
		 * slots that are actually in use have a non-zero status field.
		 * Processes with SSYS set are system processes -- these are ignored
		 * unless show_system is set.
		 */
		if (pp->p_stat && (show_system || ((pp->p_flag & SSYS) == 0)))
		{
			total_procs++;
			process_states[pp->p_stat]++;
			if ((pp->p_stat != SZOMB) &&
				(show_idle || (pp->p_stat == SRUN) || (pp->p_stat == SONPROC)) &&
				(!show_uid || pp->p_uid == (ushort) sel->uid))
			{
				*prefp++ = pp;
				active_procs++;
			}
		}
	}

	/* if requested, sort the "interesting" processes */
	qsort((char *) pref, active_procs, sizeof(struct proc *), proc_compares[idx]);

	/* remember active and total counts */
	SI_TOTAL(si) = total_procs;
	SI_ACTIVE(si) = pref_len = active_procs;

	/* pass back a handle */
	handle.next_proc = pref;
	handle.remaining = active_procs;
	return ((caddr_t) & handle);
}

char		fmt[128];			/* static area where result is built */

char *
format_next_process(caddr_t handle, char *(*get_userid) ())

{
	register struct proc *pp;
	register time_t cputime;
	register double pct;
	int			where;
	struct user u;
	struct handle *hp;
	char		command[29];
	char	   *process;
	char	   *process2;

	/* find and remember the next proc structure */
	hp = (struct handle *) handle;
	pp = *(hp->next_proc++);
	hp->remaining--;

	/* get the process's user struct and set cputime */
	if ((where = sysi86(RDUBLK, pp->p_pid, &u, sizeof(struct user))) != -1)
		where = (pp->p_flag & SLOAD) ? 0 : 1;
	if (where == -1)
	{
		strcpy(command, "<swapped>");
		cputime = 0;
	}
	else
	{
		/* set u_comm for system processes */
		if (u.u_comm[0] == '\0')
		{
			if (pp->p_pid == 0)
				strcpy(command, "Swapper");
			else if (pp->p_pid == 2)
				strcpy(command, "Pager");
			else if (pp->p_pid == 3)
				strcpy(command, "Sync'er");
		}
		else if (where == 1)
		{
			/* print swapped processes as <pname> */
			register char *s1;

			u.u_psargs[28 - 3] = '\0';
			strcpy(command, "<");
			strcat(command, strtok(u.u_psargs, " "));
			strcat(command, ">");
			while (s1 = (char *) strtok(NULL, " "))
				strcat(command, s1);
		}
		else
		{
			sprintf(command, "%s", u.u_psargs);
		}
		cputime = u.u_utime + u.u_stime;
/*	   cputime = pp->p_utime + pp->p_stime;  */
	}
	/* calculate the base for cpu percentages */
	pct = pctdouble(pp->p_cpu);

	/*
	 * psargs gives the absolute path of the process... strip it to only the
	 * command - [Changes by D. Currie & M. Muldner Aitt NS Canada]
	 */
	process = printable(command);
#if NO_COMMAND_ARGS
	strtok(process, " ");
#endif
	process2 = strrchr(process, '/');
	if (process2)
	{
		process = process2;
		process++;
	}


	/* format this entry */
	sprintf(fmt,
			Proc_format,
			pp->p_pid,
			(*get_userid) (pp->p_uid),
			pp->p_pri - PZERO,
			pp->p_nice - NZERO,
			format_k(PROCSIZE(&u)), /* same as	pp->p_size * 4 */
			proc_residentsize(pp),
			state_abbrev[pp->p_stat],
			format_time(cputime / Hz),
			printable(process));

	return (fmt);
}

/*
 * Checks the nlist to see if any symbols were not found.
 * For every symbol that was not found, a one-line message
 * is printed to stderr. The routine returns the number of
 * symbols NOT founded.
 */

int
check_nlist(register struct nlist *nlst)

{
	register int i = 0;

	while (nlst->n_name)
	{
		if (nlst->n_type == 0)
		{
			fprintf(stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
			i++;
		}
		nlst++;
	}
	return i;
}

/*
 *	getkval(offset, ptr, size, refstr) - get a value out of the kernel.
 *		"offset" is the byte offset into the kernel for the desired value,
 *		"ptr" points to a buffer into which the value is retrieved,
 *		"size" is the size of the buffer (and the object to retrieve),
 *		"refstr" is a reference string used when printing error meessages,
 *			if "refstr" starts with a '!', then a failure on read will not
 *			be fatal (this may seem like a silly way to do things, but I
 *			really didn't want the overhead of another argument).
 *
 */

int
getkval(unsigned long offset, int *ptr, int size, char *refstr)

{
	if (lseek(kmem, (long) offset, SEEK_SET) == -1)
	{
		if (*refstr == '!')
			refstr++;
		fprintf(stderr, "%s: lseek to %s: %s\n", KMEM,
				refstr, errmsg(errno));
		quit(23);
	}
	if (read(kmem, (char *) ptr, size) == -1)
	{
		if (*refstr == '!')
			return 0;
		fprintf(stderr, "%s: reading %s: %s\n", KMEM,
				refstr, errmsg(errno));
		quit(23);
	}
	return (1);
}

/* comparison routine for qsort */
/* NOTE: this is specific to the BSD proc structure, but it should
   give you a good place to start. */

/*
 *	proc_compare - comparison function for "qsort"
 *		Compares the resource consumption of two processes using five
 *		distinct keys.	The keys (in descending order of importance) are:
 *		percent cpu, cpu ticks, state, resident set size, total virtual
 *		memory usage.  The process states are ordered as follows (from least
 *		to most important):  WAIT, zombie, sleep, stop, start, run.  The
 *		array declaration below maps a process state index into a number
 *		that reflects this ordering.
 */

static unsigned char sorted_state[] =
{
	0,							/* not used				*/
	5,							/* sleep				*/
	6,							/* run					*/
	2,							/* zombie				*/
	4,							/* stop					*/
	1,							/* start				*/
	7,							/* onpr					*/
	3,							/* swap					*/
};

int
proc_compare(struct proc **pp1, struct proc **pp2)

{
	register struct proc *p1;
	register struct proc *p2;
	register int result;
	register ulong lresult;

	/* remove one level of indirection */
	p1 = *pp1;
	p2 = *pp2;

	/* use process state to break the tie */
	if ((result = sorted_state[p2->p_stat] -
		 sorted_state[p1->p_stat]) == 0)
	{
		/* use priority to break the tie */
		if ((result = p2->p_pri - p1->p_pri) == 0)
		{
			/* use time to break the tie */
			if ((result = (p2->p_utime + p2->p_stime) -
				 (p1->p_utime + p1->p_stime)) == 0)
			{
				/* use resident set size (rssize) to break the tie */
				if ((result = p2->p_size - p1->p_size) == 0)
				{
					result = 0;
				}
			}
		}
	}

	return (result);
}

/* returns uid of owner of process pid */
int
proc_owner(int pid)

{
	register int cnt;
	register struct proc **prefp;
	register struct proc *pp;

	prefp = pref;
	cnt = pref_len;
	while (--cnt >= 0)
	{
		if ((pp = *prefp++)->p_pid == (short) pid)
			return ((int) pp->p_uid);
	}
	return (-1);
}

#if 0
int
setpriority(int dummy, int who, int nicewal)
{
	errno = 1;
	return -1;
}
#endif

/* sigblock is not POSIX conformant */
sigset_t
sigblock(sigset_t mask)
{
	sigset_t	oset;

	sigemptyset(&oset);
	sigprocmask(SIG_BLOCK, &mask, &oset);
	return oset;
}

/* sigsetmask is not POSIX conformant */
sigsetmask(sigset_t mask)
{
	sigset_t	oset;

	sigemptyset(&oset);
	sigprocmask(SIG_SETMASK, &mask, &oset);
	return oset;
}


/* ---------------- hops - comparison/ordering support ---------------- */

#define ORDERKEY_PCTCPU  if (dresult = pctdouble(p2->p_cpu) - pctdouble(p1->p_cpu),\
				 (result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0)
#define ORDERKEY_MEMSIZE if ((result = (p2->p_size - p1->p_size)) == 0)
#define ORDERKEY_CPTIME  if ((result = (long)(p2->p_utime + p2->p_stime) -\
					   (long)(p1->p_utime + p1->p_stime)) == 0)

#define ORDERKEY_STATE	 if ((result = (sorted_state[p2->p_stat] - \
				   sorted_state[p1->p_stat])) == 0)
#define ORDERKEY_PRIO	 if ((result = p2->p_pri - p1->p_pri) == 0)


int
compare_cpu(struct proc **pp1, struct proc **pp2)
{
	register struct proc *p1;
	register struct proc *p2;
	register int result;
	double		dresult;

	/* remove one level of indirection */
	p1 = *pp1;
	p2 = *pp2;

	ORDERKEY_PCTCPU
		ORDERKEY_CPTIME
		ORDERKEY_STATE
		ORDERKEY_PRIO
		ORDERKEY_MEMSIZE
		;

	return (result);
}



/* compare_size - the comparison function for sorting by process size */
int
compare_size(struct proc **pp1, struct proc **pp2)
{
	register struct proc *p1;
	register struct proc *p2;
	register int result;
	double		dresult;

	/* remove one level of indirection */
	p1 = *pp1;
	p2 = *pp2;


	ORDERKEY_MEMSIZE
		ORDERKEY_PCTCPU
		ORDERKEY_CPTIME
		ORDERKEY_STATE
		ORDERKEY_PRIO
		;

	return (result);
}

/* compare_res - the comparison function for sorting by resident set size */
/* TODO: add shadow proc struct updating usr + sys times and RSS for use
 * in comparison rtns, implement compare_res rtn as per compare_size()
 */

/* compare_time - the comparison function for sorting by total cpu time */
/* This is giving wrong results since its using the proc structure vals not
 * the u struct vals we display above
 * TODO: add shadow proc struct updating usr + sys times and RSS for use
 * in comparison rtns
 */
int
compare_time(struct proc **pp1, struct proc **pp2)
{
	register struct proc *p1;
	register struct proc *p2;
	register int result;
	double		dresult;

	/* remove one level of indirection */
	p1 = *pp1;
	p2 = *pp2;

	ORDERKEY_CPTIME
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_PRIO
		ORDERKEY_MEMSIZE
		;

	return (result);
}

void
get_io_info(struct io_info *io_info)
{
	/* Not supported yet */
	memset(io_info, 0, sizeof(*io_info));
}
