#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/auto_nlist.h>

#include <net-snmp/agent/hardware/cpu.h>
#include "vmstat.h"

FindVarMethod var_extensible_vmstat;



void
init_vmstat(void)
{
    const oid  vmstat_oid[] = { NETSNMP_UCDAVIS_MIB, 11 };

    DEBUGMSGTL(("vmstat", "Initializing\n"));
    netsnmp_register_scalar_group(
        netsnmp_create_handler_registration("vmstat", vmstat_handler,
                             vmstat_oid, OID_LENGTH(vmstat_oid),
                             HANDLER_CAN_RONLY),
        MIBINDEX, CPUNUMCPUS);
}


int
vmstat_handler(netsnmp_mib_handler          *handler,
               netsnmp_handler_registration *reginfo,
               netsnmp_agent_request_info   *reqinfo,
               netsnmp_request_info         *requests)
{
    oid  obj;
    unsigned long long value = 0;
    char cp[300];
    netsnmp_cpu_info *info = netsnmp_cpu_get_byIdx( -1, 0 );

    switch (reqinfo->mode) {
    case MODE_GET:
        obj = requests->requestvb->name[ requests->requestvb->name_length-2 ];

        switch (obj) {
        case MIBINDEX:             /* dummy value */
             snmp_set_var_typed_integer(requests->requestvb, ASN_INTEGER, 1);
             break;
             
        case ERRORNAME:            /* dummy name */
             sprintf(cp, "systemStats");
             snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
                                      cp, strlen(cp));
             break;

/*
        case IOSENT:
            long_ret = vmstat(iosent);
            return ((u_char *) (&long_ret));
        case IORECEIVE:
            long_ret = vmstat(ioreceive);
            return ((u_char *) (&long_ret));
        case IORAWSENT:
            long_ret = vmstat(rawiosent);
            return ((u_char *) (&long_ret));
        case IORAWRECEIVE:
            long_ret = vmstat(rawioreceive);
            return ((u_char *) (&long_ret));
*/

        /*
         *  Raw CPU statistics
         *  Taken directly from the (overall) cpu_info structure.
         *
         *  XXX - Need some form of flag to skip objects that
         *        aren't supported on a given architecture.
         */
        case CPURAWUSER:
             snmp_set_var_typed_integer(requests->requestvb, ASN_COUNTER,
                                        info->user_ticks & 0xffffffff);
             break;
        case CPURAWNICE:
             snmp_set_var_typed_integer(requests->requestvb, ASN_COUNTER,
                                        info->nice_ticks & 0xffffffff);
             break;
        case CPURAWSYSTEM:
             /*
              * Some architecture have traditionally reported a
              *   combination of CPU statistics for this object.
              * The CPU HAL module uses 'sys2_ticks' for this,
              *   so use this value in preference to 'sys_ticks'
              *   if it has a non-zero value.
              */
             snmp_set_var_typed_integer(requests->requestvb, ASN_COUNTER,
                                       (info->sys2_ticks ?
                                        info->sys2_ticks :
                                        info->sys_ticks ) & 0xffffffff);
             break;
        case CPURAWIDLE:
             snmp_set_var_typed_integer(requests->requestvb, ASN_COUNTER,
                                        info->idle_ticks & 0xffffffff);
             break;
        case CPURAWWAIT:
             snmp_set_var_typed_integer(requests->requestvb, ASN_COUNTER,
                                        info->wait_ticks & 0xffffffff);
             break;
        case CPURAWKERNEL:
             snmp_set_var_typed_integer(requests->requestvb, ASN_COUNTER,
                                        info->kern_ticks & 0xffffffff);
             break;
        case CPURAWINTR:
             snmp_set_var_typed_integer(requests->requestvb, ASN_COUNTER,
                                        info->intrpt_ticks & 0xffffffff);
             break;
        case CPURAWSOFTIRQ:
             snmp_set_var_typed_integer(requests->requestvb, ASN_COUNTER,
                                        info->sirq_ticks & 0xffffffff);
             break;
        case CPURAWSTEAL:
             snmp_set_var_typed_integer(requests->requestvb, ASN_COUNTER,
                                        info->steal_ticks & 0xffffffff);
             break;
        case CPURAWGUEST:
             snmp_set_var_typed_integer(requests->requestvb, ASN_COUNTER,
                                        info->guest_ticks & 0xffffffff);
             break;
        case CPURAWGUESTNICE:
             snmp_set_var_typed_integer(requests->requestvb, ASN_COUNTER,
                                        info->guestnice_ticks & 0xffffffff);
             break;
        case CPUNUMCPUS:
             snmp_set_var_typed_integer(requests->requestvb, ASN_INTEGER,
                                        cpu_num & 0x7fffffff);
             break;

        /*
         *  'Cooked' CPU statistics
         *     Percentage usage of the specified statistic calculated
         *     over the period (1 min) that history is being kept for.
         *
         *   This is actually a change of behaviour for some architectures,
         *     but:
         *        a)  It ensures consistency across all systems
         *        a)  It matches the definition of the MIB objects
         *
         *   Note that this value will only be reported once the agent
         *     has a full minute's history collected.
         */
        case CPUUSER:
             if ( info->history && info->history[0].total_hist ) {
                 value  = (info->user_ticks  - info->history[0].user_hist)*100;
                 if ( info->total_ticks - info->history[0].total_hist)
                     value /= (info->total_ticks - info->history[0].total_hist);
                 else
                     value = 0;    /* or skip this entry */
                 snmp_set_var_typed_integer(requests->requestvb,
                                            ASN_INTEGER, value & 0x7fffffff);
             }
             break;
        case CPUSYSTEM:
             if ( info->history && info->history[0].total_hist ) {
                     /* or sys2_ticks ??? */
                 value  = (info->sys_ticks  - info->history[0].sys_hist)*100;
                 if ( info->total_ticks - info->history[0].total_hist)
                     value /= (info->total_ticks - info->history[0].total_hist);
                 else
                     value = 0;    /* or skip this entry */
                 snmp_set_var_typed_integer(requests->requestvb,
                                            ASN_INTEGER, value & 0x7fffffff);
             }
             break;
        case CPUIDLE:
             if ( info->history && info->history[0].total_hist ) {
                 value  = (info->idle_ticks  - info->history[0].idle_hist)*100;
                 if ( info->total_ticks - info->history[0].total_hist)
                     value /= (info->total_ticks - info->history[0].total_hist);
                 else
                     value = 0;    /* or skip this entry */
                 snmp_set_var_typed_integer(requests->requestvb,
                                            ASN_INTEGER, value & 0x7fffffff);
             }
             break;
		
        /*
         * Similarly for the Interrupt and Context switch statistics
         *   (raw and per-second, calculated over the last minute)
         */
        case SYSRAWINTERRUPTS:
             snmp_set_var_typed_integer(requests->requestvb, ASN_COUNTER,
                                        info->nInterrupts & 0xffffffff);
             break;
        case SYSRAWCONTEXT:
             snmp_set_var_typed_integer(requests->requestvb, ASN_COUNTER,
                                        info->nCtxSwitches & 0xffffffff);
             break;
        case SYSINTERRUPTS:
             if ( info->history && info->history[0].total_hist ) {
                 value  = (info->nInterrupts - info->history[0].intr_hist)/60;
                 snmp_set_var_typed_integer(requests->requestvb,
                                            ASN_INTEGER, value & 0x7fffffff);
             }
             break;
        case SYSCONTEXT:
             if ( info->history && info->history[0].total_hist ) {
                 value  = (info->nCtxSwitches - info->history[0].ctx_hist)/60;
                 snmp_set_var_typed_integer(requests->requestvb,
                                            ASN_INTEGER, value & 0x7fffffff);
             }
             break;

        /*
         * Similarly for the Swap statistics...
         */
        case RAWSWAPIN:
             snmp_set_var_typed_integer(requests->requestvb, ASN_COUNTER,
                                        info->swapIn & 0xffffffff);
             break;
        case RAWSWAPOUT:
             snmp_set_var_typed_integer(requests->requestvb, ASN_COUNTER,
                                        info->swapOut & 0xffffffff);
             break;
        case SWAPIN:
             if ( info->history && info->history[0].total_hist ) {
                 value  = (info->swapIn - info->history[0].swpi_hist)/60;
                 /* ??? value *= PAGE_SIZE;  */
                 snmp_set_var_typed_integer(requests->requestvb,
                                            ASN_INTEGER, value & 0x7fffffff);
             }
             break;
        case SWAPOUT:
             if ( info->history && info->history[0].total_hist ) {
                 value  = (info->swapOut - info->history[0].swpo_hist)/60;
                 /* ??? value *= PAGE_SIZE;  */
                 snmp_set_var_typed_integer(requests->requestvb,
                                            ASN_INTEGER, value & 0x7fffffff);
             }
             break;

        /*
         * ... and the I/O statistics.
         */
        case IORAWSENT:
             snmp_set_var_typed_integer(requests->requestvb, ASN_COUNTER,
                                        info->pageOut & 0xffffffff);
             break;
        case IORAWRECEIVE:
             snmp_set_var_typed_integer(requests->requestvb, ASN_COUNTER,
                                        info->pageIn & 0xffffffff);
             break;
        case IOSENT:
             if ( info->history && info->history[0].total_hist ) {
                 value  = (info->pageOut - info->history[0].pageo_hist)/60;
                 snmp_set_var_typed_integer(requests->requestvb,
                                            ASN_INTEGER, value & 0x7fffffff);
             }
             break;
        case IORECEIVE:
             if ( info->history && info->history[0].total_hist ) {
                 value  = (info->pageIn - info->history[0].pagei_hist)/60;
                 snmp_set_var_typed_integer(requests->requestvb,
                                            ASN_INTEGER, value & 0x7fffffff);
             }
             break;

        default:
/*
   XXX - The systemStats group is "holely", so walking it would
         trigger this message repeatedly.  We really need a form
         of the table column registration mechanism, that would
         work with scalar groups.
               snmp_log(LOG_ERR,
                   "unknown object (%d) in vmstat_handler\n", (int)obj);
 */
             break;
        }
        break;

    default:
        snmp_log(LOG_ERR,
                 "unknown mode (%d) in vmstat_handler\n",
                 reqinfo->mode);
        return SNMP_ERR_GENERR;
    }

    return SNMP_ERR_NOERROR;
}
