/*
 * load-average getter for the ZMailer
 *
 * By Matti Aarnio <mea@utu.fi> Dec-94
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <sys/types.h>
#include <errno.h>
extern int errno;
#ifndef NO_LOADAVER

/* ================================================================ */
#ifdef	__linux__
#include	<fcntl.h>
int loadavg_current()
{
	/*
	   $ cat /proc/loadavg 
	   0.01 0.01 0.00
	 */

	int rc, loadavg = 0;
	char buf[20];
	int fd = open("/proc/loadavg",O_RDONLY,0);

	if (fd < 0) return 0;	/* Not available.. */

	do {
	  rc = read(fd, buf, sizeof(buf)-1);
	  if (rc < 0) {
	    if (errno == EINTR) continue;
	    close(fd);
	    return 0; /* Some other error */
	  } else
	    break;
	} while (1);

	close(fd);
	buf[rc] = 0;

	loadavg = atoi(buf);
	if (loadavg < 0) return 0; /* Error ?? */
	return loadavg;
}
#else
/* ================================================================ */
#if	defined(__svr4__) || defined(sun)
	/* These routines are pulled from  TOP ... */

	/* Linkage:
		SVR4:      -lkvm -lelf
		SunOS4.x:  -lkvm
	 */
#include <fcntl.h>
extern char *sys_errlist[];

#include <nlist.h>
#include <kvm.h>

static struct nlist nlst[] =
{
#if	defined(i386) || defined(__svr4__)
  {"avenrun"},
#else
  {"_avenrun"},	/* Sparc and 68k SunOS 4.x have it this way.. */
#endif
  {NULL}
};
static unsigned long avenrun_offset = 0;
static int kd_inited = 0;
static kvm_t *kd;

static void
machine_init()
{
	kd_inited = 1;
	kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "(loadaver)");
	if (kd == NULL) return;

	if (kvm_nlist (kd, nlst) < 0) {
	  kvm_close(kd);
	  kd = NULL;
	  return;
	}
	if (nlst[0].n_type == 0) { /* It wasn't found.. Our only! */
	  kvm_close(kd);
	  kd = NULL;
	  return;
	}
	avenrun_offset = nlst[0].n_value; /* 0:th offset */
}

/*
 *  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).
 *
 */
static int
getkval (offset, ptr, size, refstr)
unsigned long offset;
int *ptr;
int size;
char *refstr;
{
  if (kvm_read (kd, offset, (char *) ptr, size) != size) {
    fprintf(stderr,
	      "top: kvm_read for %s: %s\n", refstr, sys_errlist[errno]);
      return 0;
  }
  return 1;
}

int loadavg_current()
{
	long avenrun[3];

	if (!kd_inited) machine_init();
	if (kd == NULL) return 0;	/* No point.. */

	/* get load average array */
	if (getkval(avenrun_offset,
		    (int *) avenrun, sizeof(avenrun),
		    "avenrun") == 0) {
	  kvm_close(kd);
	  return 0; /* Failed.. */
	}
	return (int)(avenrun[0] >> 8); /* a fixed-point decimal number */
}

#else
/* ================================================================ */
int loadavg_current()
{
	return 0;	/* Dummy.. */
}
/* ================================================================ */
#endif
#endif
#endif
