/*
 *   sysinfo (sysget/sysmp) interface
 *     e.g. IRIX 6.x
 */
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include <net-snmp/agent/hardware/cpu.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/sysmp.h>
#include <sys/sysinfo.h>
#include <sys/sysget.h>

int cpu_count;
int sinfo_ksize;
struct sysinfo* sinfo_cpus;   /* individual cpu stats */
struct sysinfo* sinfo_gen;    /* global stats */

/*
* Initialise the list of CPUs on the system
*   (including descriptions)
*/
void init_cpu_sysinfo( void )
{
    int i;
    netsnmp_cpu_info *cpu;
    char tstr[1024];

    cpu_count = sysmp(MP_NPROCS);

    /* fetch struct sysinfo size in kernel */

    if ((sinfo_ksize = sysmp(MP_SASZ, MPSA_SINFO)) < 0) {
        snmp_log_perror("init_cpu_sysinfo: sysinfo scall interface not supported");
        exit(1);
    }

    if (sizeof(struct sysinfo) != sinfo_ksize)
    {
        snmp_log_perror("init_cpu_sysinfo: size mismatch between userland and kernel sysinfo struct");
	exit(1);
    }

    /* allocate sysinfo for all cpus + global stats */

    sinfo_cpus = (struct sysinfo *) calloc(cpu_count, sinfo_ksize);
    if (!sinfo_cpus) {
        snmp_log_perror("init_cpu_sysinfo: Couldn't allocate memory bytes for sinfo_cpus");
        exit(1);
    }

    sinfo_gen = (struct sysinfo *) calloc(1, sinfo_ksize);
    if (!sinfo_gen) {
        snmp_log_perror("init_cpu_sysinfo: Couldn't allocate memory bytes for sinfo_gen");
        exit(1);
    }

    /* register cpus */

    cpu = netsnmp_cpu_get_byIdx(-1, 1);
    strcpy(cpu->name, "Overall CPU statistics");

    for (i = 0; i < cpu_count ; ++i)
    {
       cpu = netsnmp_cpu_get_byIdx(i, 1);
       sprintf(tstr, "cpu%d",i);
       strlcpy(cpu->name, tstr, sizeof(cpu->name));
       strcpy(cpu->descr, "Central Processing Unit");
    }
}

/*
* Load the latest CPU usage statistics
*/
int netsnmp_cpu_arch_load( netsnmp_cache *cache, void *magic )
{
    int i;
    netsnmp_cpu_info* cpu;
    sgt_cookie_t ck;

    /* fetch global stats */

    cpu = netsnmp_cpu_get_byIdx(-1, 0);

    if (sysmp(MP_SAGET, MPSA_SINFO, (char *) sinfo_gen, sinfo_ksize) < 0)
    {
        snmp_log_perror("netsnmp_cpu_arch_load: sysmp() failed");
	exit(1);
    }

    DEBUGMSGTL(("cpu_sysinfo", "total cpu kernel: %lu\n", sinfo_gen->cpu[CPU_KERNEL]));
    cpu->sys2_ticks  = (unsigned long long) sinfo_gen->cpu[CPU_KERNEL] + (unsigned long long) sinfo_gen->cpu[CPU_SXBRK] + (unsigned long long) sinfo_gen->cpu[CPU_INTR];
    cpu->kern_ticks  = (unsigned long long) sinfo_gen->cpu[CPU_KERNEL];
    cpu->intrpt_ticks = (unsigned long long) sinfo_gen->cpu[CPU_INTR];
    cpu->user_ticks = (unsigned long long) sinfo_gen->cpu[CPU_USER];
    cpu->wait_ticks   = (unsigned long long) sinfo_gen->cpu[CPU_WAIT];
    cpu->idle_ticks = (unsigned long long) sinfo_gen->cpu[CPU_IDLE];

    /* XXX - Do these really belong here ? */
    cpu->pageIn  = (unsigned long long)0;
    cpu->pageOut = (unsigned long long)0;
    cpu->swapIn  = (unsigned long long)sinfo_gen->swapin;
    cpu->swapOut = (unsigned long long)sinfo_gen->swapout;
    cpu->nInterrupts = (unsigned long long)sinfo_gen->intr_svcd;
    cpu->nCtxSwitches = (unsigned long long)sinfo_gen->pswitch;

    /* fetch individual cpu stats */

    SGT_COOKIE_INIT(&ck); 
    if (sysget(MPSA_SINFO, (char *) sinfo_cpus, sinfo_ksize * cpu_count, SGT_READ | SGT_CPUS | SGT_SUM, &ck) < 0)
    {
        snmp_log_perror("netsnmp_cpu_arch_load: sysget() failed");
	exit(1);
    }

    for (i = 0; i < cpu_count ; ++i)
    {
        cpu = netsnmp_cpu_get_byIdx(i, 0);

        DEBUGMSGTL(("cpu_sysinfo", "cpu %u kernel: %lu\n", i, sinfo_cpus[i].cpu[CPU_KERNEL]));
        cpu->sys2_ticks  = (unsigned long long)sinfo_cpus[i].cpu[CPU_KERNEL] + (unsigned long long) sinfo_cpus[i].cpu[CPU_SXBRK] + (unsigned long long)sinfo_cpus[i].cpu[CPU_INTR];
        cpu->intrpt_ticks = (unsigned long long)sinfo_cpus[i].cpu[CPU_INTR];
        cpu->user_ticks = (unsigned long long)sinfo_cpus[i].cpu[CPU_USER];
        cpu->wait_ticks   = (unsigned long long)sinfo_cpus[i].cpu[CPU_WAIT];
        cpu->idle_ticks = (unsigned long long)sinfo_cpus[i].cpu[CPU_IDLE];
    }

    return 0;
}

