/***********************************************************************
 *
 * PROJECT:	  PMake
 * MODULE:	  Customs -- SunOS 5.x dependencies
 * FILE:	  os-sunos5.c
 *
 * AUTHOR:  	  Adam de Boor: Sep  9, 1989
 *		  Andreas Stolcke: Dec 17, 1993
 *
 * 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
 *	----	  ----	    -----------
 *	12/93     stolcke   derived from os-sunos4.c
 *	6/99      stolcke   updated for SunOS5.[67]
 *
 * DESCRIPTION:
 *	OS-dependent functions for SunOS 5.x.
 *
 *	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-sunos5.c,v 1.8 1999/06/29 21:13:24 stolcke Exp $ ICSI (Berkeley)";
#endif /* not lint */

#include    <sys/types.h>
#include    <sys/param.h>
#include    <fcntl.h>
#include    <sys/stat.h>
#ifdef HAVE_GETLOADAVG
#include    <sys/loadavg.h>
#else /* HAVE_GETLOADAVG */
#include    <nlist.h>
#include    <kvm.h>
#endif /* HAVE_GETLOADAVG */
#include    <sys/time.h>
#include    <time.h>
#include    <sys/file.h>
#include    <utmpx.h>
#include    <sys/swap.h>

extern int errno;

#include    "customsInt.h"

#ifndef HAVE_GETLOADAVG
static struct nlist kAddrs[] = {
{	"avenrun"   },	    /* Load averages	    	    	*/
#define AVENRUN	    0
{	""  	    }
};
#endif /* !HAVE_GETLOADAVG */

#define DEV_CONSOLE	"/dev/console"
#ifdef i386
# define DEV_KEYBOARD	"/dev/kdmouse"
#else /* !i386 */
# define DEV_KEYBOARD	"/dev/kbd"
# define DEV_MOUSE	"/dev/mouse"
#endif /* i386 */

#undef UTMP_FILE
#define UTMP_FILE	UTMPX_FILE
#define utmp		utmpx

#ifndef HAVE_GETLOADAVG
static kvm_t	*kmem = NULL;	 /* handle for reading /dev/kmem */
#endif /* !HAVE_GETLOADAVG */
static int	utmpf;		 /* Descriptor to /etc/utmp */

#define MAX(x,y)	((x) > (y) ? (x) : (y))


/***********************************************************************
 *				OS_Init
 ***********************************************************************
 * SYNOPSIS:	    Initialize this module
 * CALLED BY:	    Avail_Init
 * RETURN:	    Mask of bits indicating what things we can check
 * SIDE EFFECTS:    kmem and kbd are set. kAddrs filled in.
 *
 * STRATEGY:
 *
 * REVISION HISTORY:
 *	Name	Date		Description
 *	----	----		-----------
 *	ardeb	9/10/89		Initial Revision
 *
 ***********************************************************************/
int
OS_Init()
{
    int	retMask = AVAIL_EVERYTHING;

    /*
     * No process table checks
     */
    retMask &= ~AVAIL_PROC;

    /*
     * 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;
    }

#ifndef HAVE_GETLOADAVG
    /*
     * Open the kernel. If that fails, kvm_open will have given the
     * error for us, so we can just exit.
     */
    kmem = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL);
    if (kmem == NULL) {
	xlog (XLOG_WARNING,
		"OS_Init: cannot read kernel: disabling load checks");
	return (retMask & ~AVAIL_LOAD);
    }

    /*
     * Locate the other structures in the kernel that we need.
     */
    switch (kvm_nlist(kmem, kAddrs)) {
	case -1:
	    xlog (XLOG_WARNING,
		    "OS_Init: cannot locate kernel symbols: disabling load checks");
	    retMask &= ~AVAIL_LOAD;
	    break;
	case 0:
	    /*
	     * No symbols unfound -- this is good.
	     */
	    break;
	default:
	    if (kAddrs[AVENRUN].n_type == 0) {
		/*
		 * Couldn't find _avenrun symbol, so can't determine load
		 * average.
		 */
	        xlog (XLOG_WARNING,
			"OS_Init: cannot locate %s: disabling load check",
			kAddrs[AVENRUN].n_name);
		retMask &= ~AVAIL_LOAD;
	    }
	    break;
    }
#endif /* !HAVE_GETLOADAVG */

#ifdef NO_SWAP
    retMask &= ~AVAIL_SWAP;
#endif
    
    return (retMask);
}

/***********************************************************************
 *				OS_Exit
 ***********************************************************************
 * SYNOPSIS:	    Deinitialize this module
 * CALLED BY:	    CustomsRestart()
 * RETURN:	    Nothing
 * SIDE EFFECTS:    kmem, kbd and mouse are closed.
 *
 * STRATEGY:
 *
 * REVISION HISTORY:
 *	Name	Date		Description
 *	----	----		-----------
 *	stolcke	2/20/92		Initial Revision
 *
 ***********************************************************************/
void
OS_Exit()
{
#ifndef HAVE_GETLOADAVG
    if (kmem) {
	kvm_close(kmem);
    }
#endif /* HAVE_GETLOADAVG */
    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:
 *	Use the swapctl(2) system call to get total available/used space.
 *
 * REVISION HISTORY:
 *	Name	Date		Description
 *	----	----		-----------
 *	ardeb	9/10/89		Initial Revision
 *	stolcke	12/17/93	adjustments for SunOS 5.x
 *	stolcke	8/13/96		fixed free computation
 *	stolcke 6/29/99		switched to using swapctl(2)
 *
 ***********************************************************************/
int
OS_Swap()
{
#ifdef NO_SWAP
    return (0);
#else /* !NO_SWAP */
    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;
#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
 *	stolcke	12/17/93	replaced with dummy
 *
 ***********************************************************************/
int
OS_Proc()
{
    /*
     * This function is never called since the system doesn't impose
     * a maximum number of processes, it seems (nproc is always equal
     * to the number of existing processes).
     */
    return (0);
}


/***********************************************************************
 *				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 6/29/99		Use getloadavg
 *
 ***********************************************************************/
unsigned int
OS_Load()
{
    double          result;

#ifdef HAVE_GETLOADAVG
    double	    avenrun[3];

    if (getloadavg(avenrun, 3) < 3) {
	xlog (XLOG_ERROR, "OS_Load: getloadavg failed");
	return (0);
    }

#define FLOAT(v)	(v)

#else /*  HAVE_GETLOADAVG */
    unsigned long   avenrun[3];

    if (kvm_read(kmem,
		 kAddrs[AVENRUN].n_value,
		 (char *)avenrun,
		 sizeof(avenrun)) != sizeof(avenrun))
    {
	xlog (XLOG_ERROR,
		"OS_Load: can no longer read %s", kAddrs[AVENRUN].n_name);
	return (0);
    }

#define FLOAT(v) ((double)(v)/FSCALE)

#endif /* HAVE_GETLOADAVG */

#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)
	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"
