/***********************************************************************
 *
 * PROJECT:	  PMake
 * MODULE:	  Customs -- BSD UNIX dependencies
 * FILE:	  os-bsd.c
 *
 * AUTHOR:  	  Adam de Boor: Sep  9, 1989
 *
 * ROUTINES:
 *	Name	  	    Description
 *	----	  	    -----------
 *	OS_Init	    	    Initialize module and return mask of criteria
 *	    	    	    to be considered in determination.
 *	OS_Exit	    	    Undo any initialization.
 *	OS_Idle	    	    Return machine idle time, in seconds
 *	OS_Load	    	    Return machine load factor
 *	OS_Swap	    	    Return free swap space
 *	OS_Proc	    	    Return number of free processes
 *
 * REVISION HISTORY:
 *	Date	  Name	    Description
 *	----	  ----	    -----------
 *	9/ 9/89	  ardeb	    Initial version
 *	7/91      stolcke   Extension for NeXT/Mach and Mips/Ultrix
 *	9/24/91   stolcke   Free processes support added
 *	2/19/92   stolcke   HP-UX port
 *	2/92      stolcke   OS_Exit added
 *	3/93	  stolcke   SGI port
 *
 * DESCRIPTION:
 *	OS-dependent functions for BSD-related systems. This includes:
 *	    SunOS 3.x, 4.x, 5.x
 *	    Ultrix
 *	    BSD 4.2 and 4.3
 *	    Mach on the NeXT
 *          HP-UX (believe it or not)
 *          SGI IRIX
 *
 *	These functions are responsible for the determination of the
 *	current state of the system, returning it as a set of numbers
 *	in a customs-standard form, whatever form they may be in in the
 *	system itself.
 *
 *	The format used is the same as that transmitted for the AVAIL_SET
 *	RPC.
 *
 * 	Copyright (c) Berkeley Softworks 1989
 * 	Copyright (c) Adam de Boor 1989
 *
 * 	Permission to use, copy, modify, and distribute this
 * 	software and its documentation for any non-commercial purpose
 *	and without fee is hereby granted, provided that the above copyright
 * 	notice appears in all copies.  Neither Berkeley Softworks nor
 * 	Adam de Boor makes any representations about the suitability of this
 * 	software for any purpose.  It is provided "as is" without
 * 	express or implied warranty.
 *
 *
 ***********************************************************************/
#ifndef lint
static char *rcsid =
"$Id: os-bsd.c,v 1.29 1999/05/12 02:22:19 stolcke Exp $ ICSI (Berkeley)";
#endif /* not lint */

#include    <sys/param.h>
#include    <sys/stat.h>
#include    <time.h>
#ifdef sgi
# define itimerspec OLD_itimerspec
# define itimerspec_t OLD_itimerspec_t
#endif
#include    <sys/proc.h>
#ifdef sgi
# undef itimerspec
# undef itimerspec_t
#endif
#include    <sys/conf.h>
#include    <nlist.h>
#include    <fcntl.h>
#include    <sys/file.h>
#include    <utmp.h>

extern int errno;

#include    "customsInt.h"

#ifdef __MACH__		/* Mach has slighly different struct nlist */
# undef FSCALE
# define FSCALE			1000 /* LOAD_SCALE from <sys/processor_info.h>*/
# define n_symbol		n_un.n_name
# define NLIST_AVENRUN		{"_avenrun"}
# define NLIST_PROC		{"_proc"}
# define NLIST_NPROC		{"_nproc"}
# define NLIST_END		{NULL}
# define KERNEL_FILE		"/mach"
# define NO_SWAP		/* doesn't have fixed swap space */
#endif /* __MACH_ */

#ifdef hpux
# define NLIST_AVENRUN		"avenrun"
# define NLIST_PROC		"proc"
# define NLIST_NPROC		"nproc"
# define KERNEL_FILE		"/hp-ux"
# define DEV_KEYBOARD		"/dev/hil1"
# define DEV_MOUSE	        "/dev/hil2"
# define NO_SWAP		/* doesn't have BSD kernel structures */
#endif /* hpux */

#ifdef sgi
# include    <sys/var.h>
# define FSCALE			1024.0
# define NLIST_AVENRUN		"avenrun"
# define NLIST_PROC		"proc"
# define NLIST_VAR		"v"
# define KERNEL_FILE		"/unix"
# define NO_UTHOST		/* no ut_host field in struct utmp */
# ifdef SVR4
#  define HAVE_SWAPCTL
# else
#  define NO_SWAP		/* doesn't have BSD kernel structures */
# endif /* SVR4 */
#else /* !sgi */

#ifdef mips
# include    <sys/fixpoint.h>	/* long/float conversions */
#endif /* mips */

#endif /* sgi */

#ifdef sun
# ifdef i386
#  define DEV_KEYBOARD		"/dev/kdmouse"
# else /* !i386 */
#  define DEV_KEYBOARD		"/dev/kbd"
#  define DEV_MOUSE		"/dev/mouse"
# endif /* i386 */
# ifdef SVR4
#  define KERNEL_FILE		"/dev/ksyms"
#  define NLIST_AVENRUN		"avenrun"
#  define NLIST_PROC		"proc"
#  define NLIST_NPROC		"nproc"
#  define HAVE_SWAPCTL
# else
#  define NO_SWAP		/* doesn't have BSD kernel structures */
# endif /* SVR4 */
#endif /* sun */

#if defined(__alpha) && defined(__osf__)
# undef FSCALE
# define FSCALE 1024		/* <sys/param.h> defines an incorrect value
				 * for FSCALE on Alpha OSF/1, according to
				 * ghazi@noc.rutgers.edu.  */
# define HAVE_SWAPCTL
#endif /* __alpha__ && __osf__ */

#ifdef bsdi
# define KERNEL_FILE		"/bsd"
# define NLIST_AVENRUN		"_averunnable"
# define NLIST_NPROC		"_maxproc"
# define NLIST_NPROCS		"_nprocs"
# define UTMP_FILE		_PATH_UTMP
# define NO_SWAP		/* doesn't have old BSD kernel structures */
#endif /* bsdi */

				/* defaults for most BSD systems */
#ifndef n_symbol
# define n_symbol		n_name
#endif

#ifndef KERNEL_FILE
# define KERNEL_FILE		"/vmunix"
#endif

#ifdef SVR4
# include <utmpx.h>
# undef UTMP_FILE
# define UTMP_FILE		UTMPX_FILE
# undef NO_UTHOST
# define utmp			utmpx
#endif /* SVR4 */

#ifndef UTMP_FILE
# define UTMP_FILE		"/etc/utmp"
#endif /* UTMP_FILE */

#ifndef KMEM
# define KMEM			"/dev/kmem"
#endif

#ifndef DEV_CONSOLE
# define DEV_CONSOLE		"/dev/console"
#endif

#ifndef NLIST_AVENRUN
# define NLIST_AVENRUN		"_avenrun"
#endif

#ifndef NLIST_PROC
# define NLIST_PROC		"_proc"
#endif

#ifndef NLIST_NPROC
# define NLIST_NPROC		"_nproc"
#endif

#ifndef NLIST_END
# define NLIST_END		NULL
#endif

#ifndef NO_SWAP
#ifdef HAVE_SWAPCTL
# include <sys/swap.h>
#else /* HAVE_SWAPCTL */
# include    <sys/map.h>
# ifndef mapstart
/*
 * Apparently, Ultrix doesn't have these macros...
 */
# define mapstart(X)	(struct mapent *)((X)+1)
# define mapfree(X)	(X)->m_free
# define mapwant(X)	(X)->m_want
# define mapname(X)	((struct maplast *)(X))->m_nam
# endif /* mapstart */
#endif /* HAVE_SWAPCTL */
#endif /* !NO_SWAP */

#ifdef IRIX64
# define nlist nlist64
# define lseek lseek64
# define off_t off64_t
#endif

static struct nlist kAddrs[] = {
{	NLIST_AVENRUN	},   	/* Load averages    	    	*/
#define AVENRUN	  	0
#ifdef NLIST_NPROCS
{	NLIST_NPROCS	},   	/* Current number of processes 	*/
#else
{	NLIST_PROC	},   	/* Process table base  	    	*/
#endif
#define PROCBASE	1
#ifdef NLIST_VAR
{	NLIST_VAR	},   	/* System configuration info    */
#else
{	NLIST_NPROC	},   	/* Process table size  	    	*/
#endif
#define NPROC		2
#ifndef NO_SWAP
# ifndef HAVE_SWAPCTL
{	"_nswapmap"	},   	/* Number of swap resource maps */
#define NSWAPMAP  	3
{	"_nswdev" 	},   	/* Number of swap devices   	*/
#define NSWDEV	  	4
{	"_swapmap"	},   	/* The swap resource maps   	*/
#define SWAPMAP	  	5
{	"_swdevt" 	},   	/* The swap devices 	    	*/
#define SWDEVT	  	6
# endif /* !HAVE_SWAPCTL */
#endif /* !NO_SWAP */
{	NLIST_END   	}
};

static int  	  	kmem;	    	/* Descriptor to /dev/kmem */
static int		utmpf;		/* Descriptor to /etc/utmp */

#ifndef NO_SWAP
static int  	  	swblocks; 	/* Total swap space available */
static int  	  	nSwapMap; 	/* Number of entries in the swap map */
static off_t	  	swapMapAddr;	/* Address in sysspace of the map */
static struct map 	*swapMap; 	/* Space for swap map */
static int  	  	swapMapSize;	/* Size of the swap map (bytes) */
#endif /* NO_SWAP */


/*
 * read data from kernel memory
 */
static int
kmem_read(addr, buffer, nbytes)
	off_t addr;
	char *buffer;
	unsigned nbytes;
{
#if defined(mips) && defined(SYSV) && !defined(IRIX64)
    /*
     * Things didn't work right on SGIs until I found this gem in xload ...
     */
    addr &= 0x7fffffff;
#endif /* mips && SYSV */

    /*
     * XXX: Check lseek == -1, rather != addr, since the IRIX64 lseek
     * truncates the upper half of a 64bit address.
     */
    if (lseek (kmem, addr, L_SET) == -1) {
	xlog (XLOG_ERROR, "lseek %s: %s", KMEM, strerror(errno));
	return (-1);
    } else if (read (kmem, buffer, nbytes) != nbytes) {
	xlog (XLOG_ERROR, "read %s: %s", KMEM, strerror(errno));
	return (-1);
    } else {
	return (0);
    }
}


/***********************************************************************
 *				OS_Init
 ***********************************************************************
 * SYNOPSIS:	    Initialize this module
 * CALLED BY:	    Avail_Init
 * RETURN:	    Mask of AVAIL_* bits indicating what criteria are
 *	    	    to be examined.
 * SIDE EFFECTS:
 *
 * STRATEGY:
 *
 * REVISION HISTORY:
 *	Name	Date		Description
 *	----	----		-----------
 *	ardeb	9/ 9/89		Initial Revision
 *	stolcke	7/31/91		more robustness against unavailable info
 *	stolcke	9/24/91		Rewritten in terms of kmem_read()
 *
 ***********************************************************************/
int
OS_Init()
{
    struct swdevt   *swapDevices,
		    *sw;
    int	    	    numSwapDevices;
    int	    	    retMask;
    
    /*
     * Default to everything.
     */
    retMask = AVAIL_EVERYTHING;

    /*
     * Open the utmp file.  If it's not there we cannot determine idle times.
     */
    if ((utmpf = open (UTMP_FILE, O_RDONLY, 0)) < 0) {
	/*
	 * If couldn't open/stat keyboard, we can't tell how long the machine's
	 * been idle.
	 */
	xlog (XLOG_WARNING,
		"OS_Init: cannot open or stat %s: disabling idle time check", 
		UTMP_FILE
	     );
	retMask &= ~AVAIL_IDLE;
    }

    /*
     * Extract the addresses for the various data structures that we examine.
     * XXX: Perhaps this thing should allow some other name than /vmunix?
     */
    if (nlist (KERNEL_FILE, kAddrs) < 0)
    {
        xlog (XLOG_WARNING,
		"OS_Init: cannot locate %s symbols: disabling load/swap/proc checks",
		KERNEL_FILE);
	return (retMask & ~(AVAIL_LOAD | AVAIL_SWAP | AVAIL_PROC));
    }

    /*
     * Open a stream to the kernel's memory so we can actually look at the
     * data structures.
     */
    if ((kmem = open (KMEM, O_RDONLY, 0)) < 0) {
        xlog (XLOG_WARNING,
		"OS_Init: cannot open %s: disabling load/swap/proc checks",
		KMEM);
	return (retMask & ~(AVAIL_LOAD | AVAIL_SWAP | AVAIL_PROC));
    }

    /*
     * Check for presence of avenrun symbol
     */
    if (!kAddrs[AVENRUN].n_type) {
        xlog (XLOG_WARNING,
		"OS_Init: cannot locate %s: disabling load check",
		kAddrs[AVENRUN].n_symbol);
	retMask &= ~AVAIL_LOAD;
    }

    /*
     * Check for presence of process table info
     */
    if (!kAddrs[PROCBASE].n_type || !kAddrs[NPROC].n_type) {
	xlog (XLOG_WARNING,
		"OS_Init: cannot locate %s: disabling proc check",
		(!kAddrs[PROCBASE].n_type ? kAddrs[PROCBASE].n_symbol :
		                            kAddrs[NPROC].n_symbol));
	retMask &= ~AVAIL_PROC;
    }

#ifndef NO_SWAP
# ifndef HAVE_SWAPCTL
    /*
     * Check for presence of swap device symbols
     */
    if (!kAddrs[NSWAPMAP].n_type || !kAddrs[NSWDEV].n_type ||
        !kAddrs[SWAPMAP].n_type || !kAddrs[SWDEVT].n_type ) {
	xlog (XLOG_WARNING,
		"OS_Init: cannot locate %s: disabling swap check",
		(!kAddrs[NSWAPMAP].n_type ? kAddrs[NSWAPMAP].n_symbol :
		 (!kAddrs[NSWDEV].n_type ? kAddrs[NSWDEV].n_symbol :
		  (!kAddrs[SWAPMAP].n_type ? kAddrs[SWAPMAP].n_symbol :
		                             kAddrs[SWDEVT].n_symbol))));
	retMask &= ~AVAIL_SWAP;
    } else {
	/*
	 * Find the total number of swap blocks available to the machine
	 * by summing the amounts in the swdevt descriptors
	 */
	if (kmem_read((off_t)kAddrs[NSWDEV].n_value,
	              (char *)&numSwapDevices, sizeof(numSwapDevices)) < 0) {
		xlog (XLOG_WARNING,
			"OS_Init: cannot read %s: disabling swap check",
			kAddrs[NSWDEV].n_symbol);
		return (retMask & ~AVAIL_SWAP);
	}

	swapDevices =
	    (struct swdevt *)emalloc (numSwapDevices * sizeof (struct swdevt));

	if (kmem_read((off_t)kAddrs[SWDEVT].n_value,
	      (char *)swapDevices, numSwapDevices*sizeof(struct swdevt)) < 0) {
		free ((Address) swapDevices);
		xlog (XLOG_WARNING,
			"OS_Init: cannot read %s: disabling swap check",
			kAddrs[SWDEVT].n_symbol);
		return (retMask & ~AVAIL_SWAP);
	}

	for (swblocks=0, sw=swapDevices; numSwapDevices!=0; sw++, numSwapDevices--)
	{
	    if (sw->sw_freed) {
		swblocks += sw->sw_nblks;
	    }
	}
	free ((Address) swapDevices);

	/*
	 * Find and save the number and location of the swap maps for
	 * the local machine, then allocate enough room to hold them
	 * all, pointing 'swapMap' to the space.
	 */
	if (kmem_read((off_t)kAddrs[NSWAPMAP].n_value,
	              (char *)&nSwapMap, sizeof(nSwapMap)) < 0 ||
	    kmem_read((off_t) kAddrs[SWAPMAP].n_value,
	              (char *)&swapMapAddr, sizeof(swapMapAddr)) < 0) {
		xlog (XLOG_WARNING,
			"OS_Init: cannot read %s/%s: disabling swap check",
			kAddrs[NSWAPMAP].n_symbol, kAddrs[SWAPMAP].n_symbol);
		return (retMask & ~AVAIL_SWAP);
	}
	
	if (verbose)
	    xlog (XLOG_DEBUG,
		    "OS_Init: %d swap blocks total allocated among %d maps at 0x%08x",
		    swblocks, nSwapMap, swapMapAddr);
	swapMapSize = nSwapMap * sizeof (struct map);

	swapMap = (struct map *)emalloc (swapMapSize);
    }
# endif /* HAVE_SWAPCTL */
#else /* NO_SWAP */
    retMask &= ~AVAIL_SWAP;
#endif /* NO_SWAP */

    return (retMask);
}

/***********************************************************************
 *				OS_Exit
 ***********************************************************************
 * SYNOPSIS:	    Deinitialize this module
 * CALLED BY:	    CustomsRestart()
 * RETURN:	    Nothing
 * SIDE EFFECTS:    kmem and kbd are closed.
 *
 * STRATEGY:
 *
 * REVISION HISTORY:
 *	Name	Date		Description
 *	----	----		-----------
 *	stolcke	2/20/92		Initial Revision
 *
 ***********************************************************************/
void
OS_Exit()
{
    if (kmem) {
	close(kmem);
    }
    if (utmpf >= 0) {
	close(utmpf);
    }
    return;
}

/***********************************************************************
 *				OS_Swap
 ***********************************************************************
 * SYNOPSIS:	    Find the percentage of the system's swap space that
 *	    	    isn't being used.
 * CALLED BY:	    Avail_Local
 * RETURN:	    The percentage of free swap space
 * SIDE EFFECTS:    None
 *
 * STRATEGY:
 *	The number of free blocks is simple the number of blocks described
 *	by the system's swap maps, whose address we've got in swapMapAddr.
 *
 * REVISION HISTORY:
 *	Name	Date		Description
 *	----	----		-----------
 *	ardeb	9/10/89		Initial Revision
 *	stolcke	9/24/91		Rewritten in terms of kmem_read()
 *	stolcke	12/31/95	Added support for swapctl(2)
 *
 ***********************************************************************/
int
OS_Swap()
{
#ifdef NO_SWAP
    return (0);
#else
# ifdef HAVE_SWAPCTL
    int			nresources, i;
    long		totalSwap;
    long		totalFree;
    struct swaptable	*swapTable;
    char		pathBuf[MAXPATHLEN];

    if ((nresources = swapctl(SC_GETNSWP, (void *)0)) < 0) {
	xlog (XLOG_ERROR, "OS_Swap: couldn't get number of swap resources: %s",
		strerror(errno));
	return (0);
    }

    swapTable =
	(struct swaptable *)emalloc(sizeof(struct swaptable) +
				    nresources * sizeof(struct swapent));

    swapTable->swt_n = nresources; 
    /*
     * This is badly documented, but it turns out you have to point ste_path
     * to an allocated buffer.  Since we don't care about the swap files names
     * we just use a single buffer that can fit all filenames.
     */
    for (i = 0; i < nresources; i++) {
	swapTable->swt_ent[i].ste_path = pathBuf;
    }

    if (swapctl(SC_LIST, swapTable) < 0) {
	xlog (XLOG_ERROR, "OS_Swap: couldn't get swap table: %s",
		strerror(errno));
	free(swapTable);
	return (0);
    }

    totalSwap = 0;
    totalFree = 0;
    for (i = 0; i < nresources; i++) {
	if (!(swapTable->swt_ent[i].ste_flags & ST_INDEL)) {
	     totalSwap += swapTable->swt_ent[i].ste_pages;
	     totalFree += swapTable->swt_ent[i].ste_free;
	}
    }

    if (verbose) {
	xlog (XLOG_DEBUG, "OS_Swap: max swap = %ld, free pages = %ld (%ld%%)",
		totalSwap, totalFree, (totalFree * 100) / totalSwap);
    }

    free(swapTable);
    return (totalFree * 100) / totalSwap;
# else /* HAVE_SWAPCTL */
    int	    		free;		/* Number of free blocks so far */
    struct mapent	*mapEnd;	/* End of swap maps */
    struct mapent	*mapEntry;	/* Current map */
    
    if (kmem_read ((off_t)swapMapAddr,
	           (char *)swapMap, swapMapSize) < 0)
    {
	xlog (XLOG_ERROR, "OS_Swap: can no longer read %s",
		kAddrs[SWAPMAP].n_symbol);
	return (0);
    }

    mapEnd = (struct mapent *) &swapMap[nSwapMap];
    free = 0;
    for (mapEntry = mapstart(swapMap); mapEntry < mapEnd; mapEntry++) {
	free += mapEntry->m_size;
    }

    if (verbose) {
	xlog (XLOG_DEBUG, "OS_Swap: max swap = %d, free blocks = %d (%d%%)",
		swblocks, free, (free * 100) / swblocks);
    }

    return ((free * 100) / swblocks);
# endif /* HAVE_SWAPCTL */
#endif /* NO_SWAP */
}


/***********************************************************************
 *				OS_Proc
 ***********************************************************************
 * SYNOPSIS:	    Find the number of additional processes that can be
 *	    	    created.
 * CALLED BY:	    Avail_Local
 * RETURN:	    Number of free process slots
 * SIDE EFFECTS:    None
 *
 * STRATEGY:
 *	Count current no. of processes by scanning process list,
 *	then subtract from max. no. of processes.
 *
 * REVISION HISTORY:
 *	Name	Date		Description
 *	----	----		-----------
 *	stolcke	9/21/91		Initial Revision (dummy version)
 *	stolcke	9/24/91		Working version
 *
 ***********************************************************************/
int
OS_Proc()
{
    int cproc = 0;
    int nproc;
#ifdef NLIST_VAR
    struct var var;
#endif
#ifndef NLIST_NPROCS
    off_t procbase;	/* address of process table */
    struct proc p;
    int i;
#endif

    /*
     * Get process table size
     */
#ifdef NLIST_VAR
    if (kmem_read((off_t)kAddrs[NPROC].n_value,
		  (char *)&var, sizeof(var)) < 0 ||
	(nproc = var.v_proc, FALSE))
#else
    if (kmem_read((off_t)kAddrs[NPROC].n_value,
		  (char *)&nproc, sizeof(nproc)) < 0)
#endif 
    {
	xlog (XLOG_ERROR, "OS_Proc: can no longer read %s",
		kAddrs[NPROC].n_symbol);
	return (0);
    }

    /*
     * Get process table address or current number of processes
     */
#ifdef NLIST_VAR
    procbase = kAddrs[PROCBASE].n_value;
#else 
#ifdef NLIST_NPROCS
    if (kmem_read((off_t)kAddrs[PROCBASE].n_value,
		  (char *)&cproc, sizeof(cproc)) < 0)
#else 
    if (kmem_read((off_t)kAddrs[PROCBASE].n_value,
	          (char *)&procbase, sizeof(procbase)) < 0)
#endif
    {
	xlog (XLOG_ERROR, "OS_Proc: can no longer read %s",
		kAddrs[PROCBASE].n_symbol);
	return (0);
    }
#endif /* NLIST_VAR */

#ifndef NLIST_NPROCS
    /*
     * Count used process table slots
     */
    for (i = 0; i < nproc; i++) {
	if (kmem_read((off_t)procbase + i*sizeof(p),
	              (char *)&p, sizeof(p)) < 0)
		break;
	if (p.p_stat != 0 && p.p_stat != SIDL)
		cproc++;
    }
#endif /* ! NLIST_NPROCS */

    if (verbose) {
	xlog (XLOG_DEBUG, "OS_Proc: nproc = %d, #proc = %d, free = %d",
		nproc, cproc, nproc - cproc);
    }

    return (nproc - cproc);
}


/***********************************************************************
 *				OS_Load
 ***********************************************************************
 * SYNOPSIS:	    Return the current load average in standard form
 * CALLED BY:	    Avail_Local
 * RETURN:	    The current load as a 32-bit fixed-point number
 * SIDE EFFECTS:    None
 *
 * STRATEGY:
 *
 * REVISION HISTORY:
 *	Name	Date		Description
 *	----	----		-----------
 *	ardeb	9/10/89		Initial Revision
 *	stolcke	7/31/91		MIPS load format support added
 *	stolcke	9/24/91		Rewritten in terms of kmem_read()
 *	stolcke	2/19/92		Some trivial changes for HP-UX
 *
 ***********************************************************************/
unsigned int
OS_Load()
{
/*
 * The existence of the constant FSCALE is used to determine in what format
 * the system's load average is stored. If FSCALE is defined, it indicates
 * a 32-bit fixed-point number is being used. The number is divided by
 * FSCALE using floating point arithmetic to obtain the actual load
 * average.
 *
 * If FSCALE isn't defined (standard BSD), the calculations are performed using
 * double floating point math.
 */
#ifdef FSCALE
    int 	  	avenrun[3];	/* Load averages */
#define FLOAT(v)	((double)(v)/FSCALE)
#else
#ifdef FIX_TO_DBL
    fix 	  	avenrun[3];	/* Load averages */
#define FLOAT(v)	FIX_TO_DBL(v)
#else
    double		avenrun[3];	/* Load averages */
#define FLOAT(v)	(v)
#endif /* FIX_TO_DBL */
#endif /* FSCALE */
    double              result;

    if (kmem_read((off_t)kAddrs[AVENRUN].n_value,
	          (char *)avenrun, sizeof (avenrun)) < 0)
    {
	xlog (XLOG_ERROR, "OS_Load: can no longer read %s",
		kAddrs[AVENRUN].n_symbol);
	return (0);
    }
    
#ifdef ALL_LOAD
    /*
     * Find largest of the three averages and return that
     */
    if (avenrun[0] > avenrun[1]) {
	if (avenrun[0] > avenrun[2]) {
	    result = FLOAT(avenrun[0]);
	} else {
	    result = FLOAT(avenrun[2]);
	}
    } else if (avenrun[1] > avenrun[2]) {
	result = FLOAT(avenrun[1]);
    } else {
	result = FLOAT(avenrun[2]);
    }
#else
    /*
     * Just return the 1-minute average.
     */
    result = FLOAT(avenrun[0]);
#endif
    if (verbose) {
	/*
	 * XXX: On IRIX64 systems it won't work to pass double arguments
	 * through a generic char * argument.
	 */
	xlog (XLOG_DEBUG, "OS_Load: avenrun = %.2lf, %.2lf, %.2lf",
	       FLOAT(avenrun[0]), FLOAT(avenrun[1]), FLOAT(avenrun[2]));
    }

    return (result * LOADSCALE);
}

/*
 * Use generic code for OS_Idle()
 */
#include "os-idle.c"
