/*
 *      Network Queueing System (NQS)
 *  This version of NQS is Copyright (C) 1992  John Roman
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 1, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
/*
*  PROJECT:     Network Queueing System
*  AUTHOR:      John Roman
*
*  Modification history:
*
*       Version Who     When            Description
*       -------+-------+---------------+-------------------------
*       V01.10  JRR     16-Nov-1992     Initial version.
*	V01.2	JRR	28-Dec-1992	Fixes for SUNOS.
*	V01.3	JRR	08-Mar-1993	Added Boeing enhancement for files.
*	V01.4	JRR	05-Aug-1993	Added HPUX support.
*	V01.5	JRR			Placeholder.
*	V01.6	JRR	28-Feb-1994	Added support for SOLARIS.
*
*/


/* loadavgd.c -- load average daemon for AIX 3.1
** Copyright 1991 Jussi M{ki. All Rights reserved.
** NON-COMMERCIAL USE ALLOWED. YOU MAY USE, COPY AND DISTRIBUTE 
** THIS PROGRAM FREELY AS LONG AS ORIGINAL COPYRIGHTS ARE LEFT
** AS THEY ARE.
**
*/

/*
 * This program gets the load information and passes it along to the
 * NQS scheduler node.  The information currently passed with
 * version 1 of the message is the number of NQS jobs running and
 * the 1, 5, and 15 minute load averages.  Currently,  IRIX and
 * AIX are supported.  
 */

#if  SGI | HPUX | IBMRS | DECOSF | SOLARIS
#define HAVE_MONITOR 1
#endif

#include "nqs.h"                        /* Include NQS constants/types */
#include "nqsvars.h"                    /* NQS global vars */
#include "nqspacket.h"                  /* NQS packet types */
#include "netpacket.h"                  /* NQS network packet types */

#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#if	SGI
#include <sys/sysinfo.h>
#include <sys/syssgi.h>
#include <sys/user.h>
#include <sys/sysmp.h>
#include <sys/proc.h>
#include <sys/var.h>
#include <sys/file.h>
#else
#if	IBMRS
#include <sys/sysinfo.h>
#include <nlist.h>
/*
 * These two must be coordinated with the monitor package!!!
 */
#ifndef LOADAVGD_PORT
#define LOADAVGD_PORT 2112
#endif
#ifndef LOADAVGD_LOCATION
#define LOADAVGD_LOCATION "/usr/local/bin/loadavgd"
#endif
#else
#if	HPUX | SOLARIS 
#include <sys/sysinfo.h>
#include <nlist.h>
#else
#if	ULTRIX
#include <sys/sysinfo.h>
#else
#if	DECOSF | BSD43
#include <nlist.h>
#endif
#endif
#endif
#endif
#endif

#ifndef __CEXTRACT__
#if __STDC__

static void calcloadavg ( double *load );
static int get_req_count ( void );
static void handle_loadavg ( void );
static void init_data ( void );
static loadavg_init ( int observ[1] );
static loadavg_put ( int observ[1] );
static void load_daeshutdown ( void );
static void load_abort ( void );
static void reload ( void );
static void send_load ( void );
static void server_abort ( void );
#if SGI
#else
static update_loadavg ( int observ[1] );
#endif

#else /* __STDC__ */

static void calcloadavg (/* double *load */);
static int get_req_count ( /* void */ );
static void handle_loadavg (/* void */);
static void init_data (/* void */);
static loadavg_init (/* int observ[1] */);
static loadavg_put (/* int observ[1] */);
static void load_daeshutdown (/* void */);
static void load_abort (/* void */);
static void reload (/* void */);
static void send_load (/* void */);
static void server_abort (/* void */);
#if SGI
#else
static update_loadavg (/* int observ[1] */);
#endif

#endif /* __STDC__ */
#endif /* __CEXTRACT__ */

/*
 * This defines the protocol level.  If the messages that pass from the
 * load daemon to the net daemon change,  then this must change.
 */
#define LOAD_VERSION 1
/* 
 * We can read the SGI values from the kernel and don't need
 * to keep track on our own.  For others (i.e. IBM without monitor) we need to
 * keep the information here.
 * 
 * update load-values after this time 
 */
#if	HAVE_MONITOR
#define LOAD_UPDATE_SECS 60
#else
#define LOAD_UPDATE_SECS 5
/* ring buffers for runque and runocc values */
#define SIZE_STORE ((60/LOAD_UPDATE_SECS)*5)
static int data[3][SIZE_STORE];
static int sum[3];
/* ring buffer index values */
static int rbi_current = -1;
static int rbi_tail = SIZE_STORE-(5*60)/LOAD_UPDATE_SECS;
#endif

Mid_t my_mid;
static FILE *logfile;

time_t last_report_time = 0;

/*** main
 *
 *
 *      int main():
 *      Start of the NQS load daemon.
 */
main(int argc, char *argv[])
{
    static char logfile_buffer [BUFSIZ];    /* logfile output buffer */

    int logfd;                      /* logfile file-descriptor */
    int sleep_seconds;		    /* seconds to sleep */
    char    *root_dir;

    printf("I$Loaddaemon version %s starting up.\n",  NQS_VERSION);
    fflush(stdout);
    localmid( &my_mid );
    /*
     *  Set the name of this process to a more appropriate string.
     *
     *  We use sprintf() to pad with spaces rather than null
     *  characters so that slimy programs like ps(1) which
     *  walk the stack will not get confused.
     */
    Argv0 = argv[0];		/* Save pointer to daemon argv[0] */
    Argv0size = strlen (Argv0);       /* and size for later use */
#if     TEST
    sprintf (Argv0, "%-*s", Argv0size, "xNQS Loaddaemon");
#else
    sprintf (Argv0, "%-*s", Argv0size, "NQS Loaddaemon");
#endif
    /*
     *  Create a file stream in place of stdout and stderr that
     *  writes to the NQS log process, since stdout and stderr
     *  might be directed back on the socket connection below.
     */
    if ((logfd = fcntl (1, F_DUPFD, 4)) == -1) { 
       printf ("F$Loaddaemon unable to duplicate stdout.\n"); 
       logfile = stdout;       /* Set pointer so that output */
       load_abort();           /* is correctly produced in */
    }                               /* load_abort(). */
    if ((logfile = fdopen (logfd, "w")) == NULL) {
        printf ("F$Loaddaemon unable to create logfile stream.\n");
        logfile = stdout;       /* Set pointer so that output */
        load_abort();           /* is correctly produced in */
    }                               /* load_abort(). */
    /*
     *  Buffer output to the log process. Messages must appear
     *  in coherent chunks, and many processes write output to
     *  the NQS log process.  Therefore, we must flush before
     *  our buffers get full.
     */
#if     BSD43 | ULTRIX | DECOSF
        setbuffer (logfile, logfile_buffer, BUFSIZ);
#else
#if     HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
        setvbuf (logfile, logfile_buffer, _IOFBF, BUFSIZ);
#else
BAD SYSTEM TYPE
#endif
#endif
    /*
     *  On some UNIX implementations, stdio functions alter errno to
     *  ENOTTY when they first do something where the file descriptor
     *  corresponding to the stream happens to be a pipe (which is
     *  the case here).
     *
     *  The fflush() calls are added to get this behavior out of the
     *  way, since bugs have occurred in the past when a server ran
     *  into difficultly, and printed out an errno value in situations
     *  where the diagnostic printf() displaying errno occurred AFTER
     *  the first stdio function call invoked.
     */
    fflush (logfile);               /* Logfile is a pipe */
    reload();

    /*
     *  Disable certain signals.
     */
    signal (SIGQUIT, SIG_IGN);
    signal (SIGPIPE, SIG_IGN);
#if     BSD43 | ULTRIX | DECOSF
    signal (SIGCHLD, SIG_IGN);
#else
#if     HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
    signal (SIGUSR1, SIG_IGN);
    signal (SIGUSR2, SIG_IGN);
    signal (SIGCLD, SIG_IGN); 
#else
BAD SYSTEM TYPE
#endif
#endif
    signal (SIGTERM, load_daeshutdown); /* Shutdown on receipt of SIGTERM */ 
    /*
     *  All files are to be created with the stated permissions.
     *  No additional permission bits are to be turned off.
     */
    umask (0);
    /*
     *  Set up the environment variables necessary for directory
     *  independent installation.  If these variables do not exist,
     *  NQS will use the compiled defaults.  The $NQS_HOME/rnqs.config
     *  file is read to create environment variables that define where
     *  major NQS directories reside.  Major directories are as
     *  follows:
     *    NQS_LIBEXE - NQS daemon programs and qmgr help file.
     *    NQS_LIBUSR - NQS user interface programs.
     *    NQS_MAN - NQS man pages directory.
     *    NQS_NMAP - NQS network map directory.
     *    NQS_SPOOL - NQS spooling directories.
     */
    if ( ! buildenv()) {
        printf ("F$Loaddaemon unable to establish directory independent environment.\n");
        logfile = stdout;       /* Set pointer so that output */
        load_abort();           /* is correctly produced in */
    }
    root_dir = getfilnam (Nqs_root, SPOOLDIR);
    if (root_dir == (char *)NULL) {
        printf ("F$Loaddaemon unable to determine root directory name.\n");
        logfile = stdout;       /* Set pointer so that output */
        load_abort();           /* is correctly produced in */
    }
    /*
     *  Change directory to the NQS "root" directory.
     */
    if (chdir (root_dir) == -1) {
        fprintf (logfile, "F$Loaddaemon: Unable to chdir to %s.\n",
                        Nqs_root);
        load_abort();            /* Abort execution */
    }
    relfilnam (root_dir);
    /*
     *  Determine the machine-id of the local host.
     */
    if (localmid (&Locmid) != 0) {
        fprintf (logfile, "F$Loaddaemon: unable to determine ");
        fprintf (logfile, "machine-id of local host.\n");
        load_abort();            /* Abort execution */
    }
#if	!HAVE_MONITOR
    handle_loadavg();
#endif
    while (1) {
	sleep(LOAD_UPDATE_SECS);
#if	!HAVE_MONITOR
	handle_loadavg();
#endif
	send_load();
    }
}

static void
send_load()
{
    int status;
    char input_buf[BUFSIZ];
    static char output_buf[BUFSIZ];
    double loadv[3];
    int i;
    int sd;                          /* Socket descriptor */
    short timeout;                   /* Seconds between tries */
    long transactcc;                 /* Holds establish() return value */
    Mid_t my_mid;                    /* Local mid */
    time_t time_now;
    int	nqs_jobs;		     /* Number of jobs currently running */


    time(&time_now);
    if (last_report_time + Defloadint > time_now) return;
    last_report_time = time_now;
    calcloadavg(loadv);
    sprintf(output_buf,"%f %f %f", loadv[0],loadv[1],loadv[2]);
    nqs_jobs = get_req_count();
    if (Debug > 2) {
        fprintf (logfile, "D$Loaddaemon: %s jobs %d.\n", output_buf,  nqs_jobs);
        fflush(logfile);
    }
    interclear();
    interw32i(LOAD_VERSION);
    interw32i(nqs_jobs);
    interwstr (output_buf);

    sd = establish (NPK_LOAD, LB_Scheduler, 0,  "root", &transactcc);
    if (Debug > 2) {
        tcmmsgs (transactcc, logfile, "D$loaddaemon: establish ");
	fflush(logfile);
    }
    
    if (sd == -2) {
        /*
         * Retry is in order.
         */
        timeout = 1;
        do {
            nqssleep (timeout);
            interclear ();
            interw32i (LOAD_VERSION);
            interw32i(nqs_jobs);
            interwstr (output_buf);
            sd = establish (NPK_LOAD, LB_Scheduler, 0, "root", &transactcc);
            timeout *= 2;
        } while (sd == -2 && timeout <= 16);
    }
    if (Debug > 2) {
	fprintf(logfile,  "D$loaddaemon: closing socket %d\n", sd);
	fflush(logfile);
    }
    if ( sd > 0 ) {
	i = close (sd);			/* We don't need it any more */
	if (i) {
	    fprintf(logfile,  "E$loaddaemon: error closing socket, errno %d\n", errno);
	    fflush(logfile);
	}
    }
}

/*
 * Get the count of requests on the system.
 * Returns either -1 for a failure,  or 0 or positive number for count
 * of jobs on the system.
 */
static int get_req_count()
{
    struct confd *queuefile;            /* MUST be the NQS queue file */
    struct gendescr *descr;
    int req_count;
    
    if ((queuefile = opendb (Nqs_queues, O_RDONLY)) == NULL) {
        fprintf (logfile, 
	    "F$Loadaemon: Unable to open the NQS queue database, errno: %d\n",  errno);
	fflush(logfile);
	return (-1);
    }
    descr = nextdb (queuefile);

    req_count = 0;
    while (descr != (struct gendescr *)0) {
	req_count += descr->v.que.runcount + descr->v.que.arrivecount;
        descr = nextdb (queuefile);  /* Get the next queue */
    }
    closedb (queuefile);
    return(req_count);
}


/*** server_abort
 *
 *
 *      void server_abort():
 *
 *      Abort Network server execution, writing a message to
 *      the NQS log process.
 */
static void server_abort ()
{
        if (errno) fprintf (logfile, "I$%s.\n", asciierrno());
        fprintf (logfile, "I$Loaddaemon aborting.\n");
        fflush(logfile);
        exit (1);
}

/*** reload
 *
 *
 *      void reload ():
 *      When the local daemon updates a parameter,
 *      it will send a SIGINT to the load daemon.  The load daemon
 *      should then update its copy of the parameters off the disk.
 *
 */
static void reload ()
{
        signal (SIGINT, reload);
	if (Debug > 2) {
	    fprintf(logfile, "D$Loaddaemon: reloading parameters.\n");
	    fflush(logfile);
	}
        /*
         *  Initialize the in-core copy of the parameters.
         *  The NQS local daemon uses SIGINT to let us know
         *  when the parameters have changed. 
         */
        if ((Paramfile = opendb (Nqs_params, O_RDONLY)) == NULL) {
                fprintf (logfile, "F$Loaddaemon: unable to open general ");
                fprintf (logfile, "parameters file.\n");
                load_abort();            /* Abort execution */
        }
	/*
	 * Get the Scheduler mid and debug information.
	 */
        seekdb (Paramfile, 0);
        ldparam ();
	closedb(Paramfile);
	if (Debug > 2) {
	    fprintf(logfile, "D$Loaddaemon: Defloadint now %d.\n", Defloadint);
	    fflush(logfile);
	}
}


/*** load_abort
 *
 *
 *      void load_abort():
 *
 *      Abort Network daemon execution, writing a message to
 *      the NQS log process.
 */
static void load_abort()
{
        if (errno) fprintf (logfile, "I$%s.\n", asciierrno());
        fprintf (logfile, "I$Loaddaemon aborted.\n");
        fflush(logfile);
        exit (1);
}


/*** load_daeshutdown
 *
 *
 *      void net_daeshutdown():
 *      Catch SIGTERM signal to shutdown.
 */
static void load_daeshutdown()
{
        signal (SIGTERM, SIG_IGN);
        fprintf (logfile, "I$Loaddaemon shutting down.\n");
        fflush(logfile);
        exit (0);
}

#if HAVE_MONITOR
#if SGI
static
void calcloadavg(double load[3])
{
    unsigned long	kernaddr;
    static int kmem = -1;
    long avenrun[3];
    int	i, j;	
    
    kernaddr = sysmp(MP_KERNADDR, MPKA_AVENRUN) & 0x7fffffff;

    if (kmem == -1) { 
        if( (kmem = open("/dev/kmem", 0) ) == -1) {
            fprintf (logfile, "F$Loaddaemon: kmem open error %d\n", errno);
            fflush(logfile);
	    server_abort();
        }
    }
    if( lseek(kmem, (off_t)kernaddr, 0) == -1 ){
        fprintf (logfile, "F$Loaddaemon: kmem seek error %d\n", errno);
        fprintf (logfile, "F$loaddaemon: kmem = %d,  kernaddr = %x\n", 
			    kmem,  kernaddr);
        fflush(logfile);
	server_abort();
    }
    i = read(kmem, (char *)avenrun, sizeof(avenrun));
    if( i != sizeof(avenrun) ){
        fprintf (logfile, "F$Loaddaemon: kmem read error %d\n", errno);
        fprintf (logfile,  "F$Loaddaemon: read %d\n",  i);
        fflush(logfile);
	server_abort();
    }
    for (i = 0; i < 3; i++)
            load[i] = (double) avenrun[i] / 1024.;
}
#else
#if IBMRS

/* getloadavg.c -- routine to get loadavgd-values
** this version is used with loadavgd-server in aix3
*/


#ifndef LOADAVGD_PORT
#define LOADAVGD_PORT 2112
#endif
#ifndef LOADAVGD_LOCATION
#define LOADAVGD_LOCATION "/usr/local/bin/loadavgd"
#endif

static int signal_handler(int sig, int code, struct sigcontext *scp)
{
    /* set this signal-handler again */
    signal(sig,signal_handler);
}

static void calcloadavg(double loadv[])
{
    static int initted=0;
    static int sock;
    static struct sockaddr_in server;
    static struct hostent *hp;
    static long alarm_sig;
    char buf[80];
    int status;
    int server_len = sizeof(struct sockaddr_in);
    int errno2;
    int i;

    if (! initted) {
	initted=1;
        sock = socket(AF_INET, SOCK_DGRAM, 0);
	if (sock < 0) {
	    fprintf(logfile, "F$loadaemon: opening datagram socket\n");
	    fflush(logfile);
	    load_abort(); 
	}
	server.sin_family = AF_INET;
	hp = gethostbyname("localhost");
	if (hp == 0) {
	    fprintf(logfile,"F$loadaemon: localhost unknown\n");
	    fflush(logfile);
	    load_abort(); 
	}
	bcopy(hp->h_addr, &server.sin_addr, hp->h_length);
	server.sin_port = htons(LOADAVGD_PORT);
    }
    
    /* send something to loadavgd-server to get reply back */
    status = sendto(sock,"\n\000",2,0, &server,sizeof(server));
    if (status == -1) {
        fprintf(logfile, "F$loadaemon: getloadavg: sendto loadavgd\n");
	fflush(logfile);
        load_abort();
    }    
    alarm_sig = signal(SIGALRM,signal_handler);
    alarm(2); /* lets wait for 2 second to get respond from server */
    status = recvfrom(sock, buf, 80,0, &server, &server_len);
    errno2 = errno;
    alarm(0);
    signal(SIGALRM,alarm_sig);

    if (status==-1) {
	if (errno2 == EINTR) { /* if recvfrom was interrupted by alarm */
	    if (Debug > 2) {
		fprintf(logfile,"\n***getloadavg: starting loadavgd-daemon ***\n");
	        fflush(logfile);
	    }
	    launch_loadavgd();
	    sleep(1);
	    status = sendto(sock,"\n\000",2,0, &server,sizeof(server));
	    if (status == -1) {
	        fprintf(logfile, "F$getloadavg: sendto loadavgd\n");
		fflush(logfile);
		load_abort();
	    }
	    status = recvfrom(sock, buf, 80,0, &server, &server_len);
	}
	if (status == -1) {
	    fprintf(logfile, "F$getloadavg: recvfrom from loadavgd daemon\n");
	    fflush(logfile);
	    load_abort();
	}
    }
    sscanf(buf,"%lf %lf %lf",&loadv[0],&loadv[1],&loadv[2]);
}


/* launch_loadavgd.c -- start loadavgd process 
** Called from getloadavg if loadavgd server is not running
** or accepting connections to port.
**
*/

launch_loadavgd()
{
    char buf[32];
    int i;
    
    sprintf(buf,"%d",LOADAVGD_PORT);
    switch (fork()) {
      case 0: /* child */
	execl(LOADAVGD_LOCATION,"loadavgd",buf,0);
	fprintf(logfile, "F$getloadavg: cannot exec %s\n",
				LOADAVGD_LOCATION);
	fflush(logfile);
	load_abort();
	/* never returns */
      case -1: /* error */
	fprintf(logfile, "F$getloadavg: cannot fork \n");
	fflush(logfile);
	load_abort();
      default: /*parent */
	break;
    }
}

#else
#if	HPUX
struct  nlist nl[] = {
# define unixpath "/hp-ux"
#ifdef __hppa       /* series 700 & 800 */
        { "avenrun" },
#else               /* series 300 & 400 */
        { "_avenrun" },
#endif
        { 0 },
};
static
void calcloadavg(double load[3])
{
    static int kmem = -1;
    double avenrun[3];
    int	i;
    
    if(kmem == -1) {
        nlist(unixpath, nl);
        if (nl[0].n_type==0) {
            fprintf (logfile, "F$Loaddaemon: namelist error\n");
            fflush(logfile);
	    server_abort();
        }
        if((kmem = open("/dev/kmem", 0)) == -1) {
            fprintf (logfile, "F$Loaddaemon: kmem open error %d\n", errno);
            fflush(logfile);
	    server_abort();
        }
    }
    if( lseek(kmem, (off_t)nl[0].n_value, 0) == -1 ){
        fprintf (logfile, "F$Loaddaemon: kmem seek error %d\n", errno);
        fprintf (logfile, "F$loaddaemon: kmem = %d,  kernaddr = %x\n", 
			    kmem,  nl[0].n_value);
        fflush(logfile);
	server_abort();
    }
    if( read(kmem, (char *)avenrun, sizeof(avenrun)) != sizeof(avenrun) ){
        fprintf (logfile, "F$Loaddaemon: kmem read error %d\n", errno);
        fprintf (logfile, "F$Loaddaemon: read %d\n",  i);
        fflush(logfile);
	server_abort();
    }
    for (i = 0; i < 3; i++)
        load[i] = avenrun[i];

}
#else
#if   DECOSF
struct  nlist nl[] = {
#define unixpath "/vmunix"
        { "_avenrun" },
        { 0 },
};

static
void calcloadavg(double load[3])
{
    static int kmem = -1;
    long avenrun[3];
    int       i;

    if(kmem == -1) {
        nlist(unixpath, nl);
        if (nl[0].n_type==0) {
            fprintf (logfile, "F$Loaddaemon: namelist error\n");
            fflush(logfile);
          server_abort();
        }
    if((kmem = open("/dev/kmem", 0)) == -1) {
        fprintf (logfile, "F$Loaddaemon: kmem open error %d\n", errno);
        fprintf (logfile, "F$loaddaemon: kmem = %d,  kernaddr = %x\n",
                          kmem,  nl[0].n_value);
        fflush(logfile);
        server_abort();
    }
    if ( lseek (kmem, (off_t) nl[0].n_value, 0) == -1) {
	fprintf(logfile, "f$Loaddaemon: kmem seek error %d\n", errno);
	fflush (logfile);
	server_abort();
    }
    if( read(kmem, (char *)avenrun, sizeof(avenrun)) != sizeof(avenrun) ){
        fprintf (logfile, "F$Loaddaemon: kmem read error %d\n", errno);
        fprintf (logfile,  "F$Loaddaemon: read %d\n",  i);
        fflush(logfile);
        server_abort();
    }
    for (i = 0; i < 3; i++)
        if(avenrun[i] != 0)
        load[i] = ((double) avenrun[i]) / (double) 1000.;
        else
        load[i] = 0;
    }
}
#else
#if SOLARIS | BSD43
#ifdef SOLARIS
struct  nlist nl[] = {
        { "avenrun" },
        { 0 },
};
#define unixpath "/kernel/unix"
#else
#define unixpath "/vmunix"
struct  nlist nl[] = {
        { "_avenrun" },
        { 0 },
};
#endif
static
void calcloadavg(double load[3])
{
    static int kmem = -1;
    long avenrun[3];
    int	i;
    
    if(kmem == -1) {
        nlist(unixpath, nl);
        if (nl[0].n_type==0) {
            fprintf (logfile, "F$Loaddaemon: namelist error\n");
            fflush(logfile);
	    server_abort();
        }
        if((kmem = open("/dev/kmem", O_RDONLY )) == -1) {
            fprintf (logfile, "F$Loaddaemon: kmem open error %d\n", errno);
            fflush(logfile);
	    server_abort();
        }
    }
    if( lseek(kmem, (off_t)nl[0].n_value, 0) == -1 ){
        fprintf (logfile, "F$Loaddaemon: kmem seek error %d\n", errno);
        fprintf (logfile, "F$loaddaemon: kmem = %d,  kernaddr = %x\n", 
			    kmem,  nl[0].n_value);
        fflush(logfile);
	server_abort();
    }
    if( read(kmem, (void *)avenrun, sizeof(avenrun)) != sizeof(avenrun) ){
        fprintf (logfile, "F$Loaddaemon: kmem read error %d\n", errno);
        fprintf (logfile, "F$Loaddaemon: read %d\n",  i);
        fflush(logfile);
	server_abort();
    } 
    for (i = 0; i < 3; i++)
        load[i] = ((double)avenrun[i]) / 1024.0;

}
#endif /* SOLARIS or Sun (BSD43)*/
#endif  /* DECOSF */
#endif  /* HPUX */
#endif  /* IBMRS */
#endif  /* SGI */
#else      /* HAVE_MONITOR */
#if 	IBMRS
static
void calcloadavg(double load [3])
{
  int iparam;

  for (iparam=0; iparam<3; iparam++) {
    load[iparam] = (double ) sum[iparam] / (double) SIZE_STORE;
  }
}

static loadavg_init(int observ[3])
{
  int iobs, iparam;
  for (iparam=0;iparam<3;iparam++) {
    sum[iparam] = 0;
  }
  for (iparam=0;iparam<3;iparam++) {
    for (iobs=0;iobs<SIZE_STORE;iobs++) {
      data[iparam][SIZE_STORE-iobs-1] = observ[iparam];
      sum[iparam] += observ[iparam];
    }
  }
}

static
update_loadavg(int observ[3])
{
  static int prev_update=0;

  if (prev_update==0) loadavg_init(observ);
  if (time(0)-prev_update > LOAD_UPDATE_SECS) {
    loadavg_put(observ);
    prev_update=time(0);
  }
}

#define RB_INC(index) ((index+1>=(SIZE_STORE))?(0):(index+1))

static
loadavg_put(int observ[3])
{
  int iparam;

  rbi_current = RB_INC(rbi_current);
  rbi_tail   = RB_INC(rbi_tail);
  for (iparam = 0; iparam < 3; iparam++) {
      data[iparam][rbi_current]=observ[iparam];
      sum[iparam] += observ[iparam] - data[iparam][rbi_tail];
  }
}
static
void handle_loadavg()
{
    static int initted=0;
    static int fd;
    static struct sysinfo si;
    static struct nlist kernelnames[] = {
	{"sysinfo", 0, 0, 0, 0, 0},
	{NULL, 0, 0, 0, 0, 0},
        };

    if (!initted) {
	initted = 1;
	fd = open("/dev/kmem", O_RDONLY);
	if (fd < 0) {
            fprintf (logfile, "F$Loaddaemon: kmem open error %d\n", errno);
            fflush(logfile);
	    server_abort();
	}
	if (knlist(kernelnames,1,sizeof(struct nlist)) == -1) {
            fprintf (logfile, "F$Loaddaemon: knlist entry not found\n");
            fflush(logfile);
	}
    }
    lseek(fd, kernelnames[0].n_value,SEEK_SET);
    read(fd,&si,sizeof(si));
    update_loadavg(si.runque,si.runocc);
}
#else
/*
 * Code for other architectures / operating systems goes here.
 * For now just return 9999 for the load average.
 */
static
void calcloadavg(double load[3])
{
	load[0] = 9999.;
	load[1] = 9999.;
	load[2] = 9999.;

}
static void
handle_loadavg()
{
}
#endif
#endif
