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

#ifndef LINUX
#include <sys/statvfs.h>
#include <vm/anon.h>
#include <utmpx.h>
#include <nlist.h>
#include <sys/param.h>
#include <kvm.h>
static struct nlist lst[6];
static kvm_t *kd;
#else
#include <sys/vfs.h>
#endif

#include "loadmeter.h"

int fd, ud;

void Doinit(char *label)
{
struct utsname *host;
#ifndef LINUX
int i;
struct utmpx *utmpx;
struct utmpx *utmps;
struct utmpx utmp;
struct timeval tmv;


/* Open the kernel to read values for mem and load */
    seteuid(0);
    kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "Loadmeter");
    if (kd == (kvm_t *)0) {
        printf("cannot get access to kernel address space\n");
		exit(1);
    }
        
    lst[0].n_name = "avenrun";
    lst[1].n_name = "freemem";
    lst[2].n_name = "maxmem";
    lst[3].n_name = "anoninfo";
    lst[4].n_name = "availrmem";
    lst[5].n_name = "swapfs_minfree";
    lst[6].n_name = NULL;
        
    if (kvm_nlist(kd, lst) != 0) {
        printf("cannot get name list\n");
    }
    
    for(i=0;i<6;i++)
            if (lst[0].n_value == 0) {
                printf("Cannot find address for %s in the kernel\n", lst[i].n_name);
            }
/* Get the time at bootup, used for uptime */
   utmps = getutxent();
   memset(&utmp, 0, sizeof(utmp));
   utmp.ut_type = BOOT_TIME;
   setutxent();
   utmpx = getutxid(&utmp);
   if (utmpx == NULL)
        printf("Error getting utmp entry\n");
   gettimeofday(&tmv, NULL);
   boot_time = utmpx->ut_tv.tv_sec;
#else
/* Dont bother checking for failure */
	fd = open("/proc/loadavg", O_RDONLY);
	ud = open("/proc/uptime", O_RDONLY);
#endif
	/* Get the hostname of the machine */
	if(label==NULL)
	{
		host = (void *)malloc(sizeof(struct utsname));
		uname(host);
		strcpy(hostname, host->nodename);
		free(host);
	}
	else
		strcpy(hostname, label);
}

/* Read /proc/loadavg and get 1&5 min load average numbers */
void GetLoadPoint(avg, oavg, davg, numps)
double *avg; /* 1 minute average */
double *oavg;	/* 5 minute average */
double *davg;	/* 15 minute average */
int *numps;
{
#ifndef LINUX
long temp[5];

    if (kvm_read(kd, lst[0].n_value, (char *)temp, sizeof (temp)) != 
        sizeof (temp)) {
        printf("Kernel read error on %s\n", lst[0].n_name);
    }
    *avg = (double)temp[0]/(1<<8);
    *oavg = (double)temp[1]/(1<<8);
    *davg = (double)temp[2]/(1<<8);
    *numps = 0;
#else
char buf[25];
int crap;

      lseek(fd, 0, 0);
      read(fd, buf, sizeof(buf)-1);
      sscanf(buf, "%lf %lf %lf %d/%d", avg, oavg, davg, &crap, numps);
#endif
      return;
}

void GetUptime(char *ut)
{
static char buf[35];
double uptime;
int hr=0, min=0, num;
char *plural = "";
#ifndef LINUX
   struct timeval tmv;
   gettimeofday(&tmv, NULL);
   uptime = tmv.tv_sec - boot_time;
#else
      bzero(ut, 20);
      lseek(ud, 0, 0);
/* Read the /proc/uptime file */
      read(ud, buf, sizeof(buf)-1);
      sscanf(buf, "%lf", (double *)&uptime);
#endif
      sprintf(ut, "%s up ", hostname);
/* Convert uptime in secs to a readable format */
      if (uptime > 86400)
      {
      	num = (int) uptime/86400;
      	/* If > 1 day do the plural 'days' thang */
      	if (num > 1)
      		plural = "s";
      	sprintf(buf, "%d day%s, ", num, plural);
      	uptime = uptime - num * 86400;
      	strcat(ut, buf);
      }
      if(uptime > 3600)
      {
      	hr = (int) uptime / 3600;
      	uptime = uptime - hr * 3600;
      }
      min = (int) uptime/60;
      if(hr > 0)
      	sprintf(buf, "%d:%02d", hr, min);
      else
      	sprintf(buf, "%02d min", min);
      strcat(ut, buf);
      return;
}
void GetMemInfo()
{
#ifdef LINUX
FILE *meminfo;
char *what;
long use=0;
#endif
long stot=0, sfree=0, mtot=0, mfree=0, mcache=0, mbuf=0;

#ifndef LINUX
int i;
unsigned int temp;
unsigned int stats[6];
struct anoninfo anoninfo;

    for(i=1;i<6;i++) {
        if(i==3) {
            if (kvm_read(kd, lst[i].n_value, (char *)(&anoninfo), sizeof (anoninfo)) !=
                sizeof (anoninfo)) {
                printf("Kernel read error on %s\n", lst[i].n_name);
            }  
        } else {
            if (kvm_read(kd, lst[i].n_value, (char *)(&temp), sizeof (temp)) !=
                sizeof (temp)) {
                printf("Kernel read error on %s\n", lst[i].n_name);
            }  
            stats[i] = temp;
        }
   }
   sfree = (anoninfo.ani_resv > anoninfo.ani_max ? 0 : anoninfo.ani_resv-anoninfo.ani_max) + stats[4] - stats[5];
   sfree *= 4;
   stot = sfree + 4*anoninfo.ani_resv;
   mtot = 4*stats[2];
   mfree = 4*stats[1];
   mbuf = 0;
   mcache = 0;
#else
	meminfo = fopen("/proc/meminfo", "r");
	what = (void *)malloc(25);
	while(fscanf(meminfo, "%s %ld", what, &use) != EOF)
	{
		if(strncmp(what, "SwapTotal", 9) == 0)
			stot = use;
		else if(strncmp(what, "SwapFree", 8) == 0)
			sfree = use;
		else if(strncmp(what, "MemTotal", 8) == 0)
			mtot = use;
		else if(strncmp(what, "MemFree", 7) == 0)
			mfree = use;
		else if(strncmp(what, "MemShared", 8) == 0)
			mbuf = use;
		else if(strcmp(what, "Cached:") == 0)
			mcache = use;
	}
#endif
	memory.swaptotal = stot;
	memory.swapfree = sfree;
	memory.memtotal = mtot;
	memory.memfree = mfree;
	memory.cached = mcache;
	memory.shared = mbuf;
	sprintf(memory.muse, "Memory used: %ld%%", (mtot-mfree-mcache)*100/mtot);
	sprintf(memory.suse, "Swap used:   %ld%%", stot ? (stot-sfree)*100/stot : 0);
#ifdef LINUX
	free(what);
	fclose(meminfo);
#endif
}

static void safefree(void **ptr)
{
	if (*ptr != 0)
	{
		free(*ptr);
		*ptr = 0;
	}
}


/*
   Disk usages are obtained by looking in /proc/mounts and
   calling statfs on each of the filsystem mount points
*/
void GetDiskUsage()
{
FILE *mtab;
#ifdef LINUX
struct statfs buf;
#else
struct statvfs buf;
#endif
int which;
static time_t tm=0;
char dev[256], path[256], type[256];
#ifdef LINUX
#define MTAB "/proc/mounts"
#else
char flags[256];
long tme;
#define MTAB "/etc/mnttab"
#endif

	if(time(NULL) - tm < DUINTERVAL)
		return;
	mtab = fopen(MTAB, "r");
	if(mtab == NULL)
	{
		printf("Cannot open %s!\n", MTAB);
		exit(1);
	}
	if(dosync)
		sync();
	for(which = 0 ; which < MAXFILESYSTEMS ; which++)
	{
		safefree((void **)&usages[which].path);
		safefree((void **)&usages[which].dev);
		safefree((void **)&usages[which].type);
	}

	which = 0;
#ifdef LINUX
	while(fscanf(mtab, "%s %s %s ", dev, path, type) != EOF)
#else
	while(fscanf(mtab, "%s %s %s %s %ld", dev, path, type, flags, &tme) != EOF)
#endif
	{
		/* Only statfs() if the path contains '/' */
		if (strcmp(type, "proc") == 0)
			continue;
		if (strchr(dev, '/') == NULL && strcmp(dev, "rootfs") != 0)
			continue;
#ifdef LINUX
		if(statfs(path, &buf) != 0)
#else
		if(statvfs(path, &buf) != 0)
#endif
			continue;
		if (which >= MAXFILESYSTEMS) {
			fprintf(stderr, "More than %d filesystems - ignoring the rest.\n(Edit MAXFILESYSTEMS in loadmeter.h to fix)\n", MAXFILESYSTEMS);
			break;
		}
		usages[which].path = strdup(path);
		usages[which].dev = strdup(dev);
		usages[which].type = strdup(type);
#ifdef LINUX
		usages[which].blocks = buf.f_blocks * (float)buf.f_bsize / 1024; 
		usages[which].bfree = buf.f_bavail * (float)buf.f_bsize / 1024; 
#else
		usages[which].blocks = buf.f_blocks * (float)buf.f_frsize / 1024; 
		if (buf.f_bfree != -1) /* -1 of unsigned gives highest value */
			usages[which].bfree = buf.f_bavail * (float)buf.f_frsize / 1024; 
		else
			usages[which].bfree = 0;
#endif
		which++;
	}
	usages[which].blocks = 0;
	usages[which].path = NULL;
	tm = time(NULL);
	fclose(mtab);
}
