blob: 4b29dbc3b74dbeaf7801db463e8536c987c1808d [file] [log] [blame]
#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>
extern NetsnmpCacheLoad netsnmp_cpu_arch_load;
static void _cpu_update_stats( unsigned int, void* );
static int _cpuAutoUpdate = 5;
static int _cpuHistoryLen;
int cpu_num = 0;
static netsnmp_cpu_info *_cpu_head = NULL;
static netsnmp_cpu_info *_cpu_tail = NULL;
static netsnmp_cache *_cpu_cache = NULL;
void init_cpu( void ) {
oid nsCPU[] = { 1, 3, 6, 1, 4, 1, 8072, 1, 33 };
/*
* If we're sampling the CPU statistics automatically,
* then arrange for this to be triggered regularly,
* keeping sufficient samples to cover the last minute.
* If the system-specific code has already initialised
* the list of CPU entries, then retrieve the first set
* of stats immediately.
* Otherwise, wait until the regular sampling kicks in.
*
* If we're not sampling these statistics regularly,
* create a suitable cache handler instead.
*/
if ( _cpuAutoUpdate ) {
_cpuHistoryLen = 60/_cpuAutoUpdate;
snmp_alarm_register( _cpuAutoUpdate, SA_REPEAT, _cpu_update_stats,
NULL );
if ( _cpu_head )
_cpu_update_stats( 0, NULL );
} else
_cpu_cache = netsnmp_cache_create( 5, netsnmp_cpu_arch_load, NULL,
nsCPU, OID_LENGTH(nsCPU));
}
void shutdown_cpu( void ) {
while ( _cpu_head ) {
netsnmp_cpu_info *tmp = _cpu_head;
_cpu_head = _cpu_head->next;
SNMP_FREE(tmp->history);
SNMP_FREE(tmp);
}
_cpu_tail = NULL;
}
netsnmp_cpu_info *netsnmp_cpu_get_first( void ) {
return _cpu_head;
}
netsnmp_cpu_info *netsnmp_cpu_get_next( netsnmp_cpu_info *this_ptr ) {
return ( this_ptr ? this_ptr->next : NULL );
}
/*
* Work with a list of CPU entries, indexed numerically
*/
netsnmp_cpu_info *netsnmp_cpu_get_byIdx( int idx, int create ) {
netsnmp_cpu_info *cpu, *cpu2;
/*
* Find the specified CPU entry
*/
DEBUGMSGTL(("cpu", "cpu_get_byIdx %d ", idx));
for ( cpu=_cpu_head; cpu; cpu=cpu->next ) {
if ( cpu->idx == idx ) {
DEBUGMSG(("cpu", "(found)\n"));
return cpu;
}
}
if (!create) {
DEBUGMSG(("cpu", "(not found)\n"));
return NULL;
}
/*
* Create a new CPU entry, and insert it into the list....
*/
cpu = SNMP_MALLOC_TYPEDEF( netsnmp_cpu_info );
if (!cpu) {
return NULL;
DEBUGMSG(("cpu", "(failed)\n"));
}
DEBUGMSG(("cpu", "(created)\n"));
cpu->idx = idx;
/* ... either as the first (or only) entry.... */
if ( !_cpu_head || _cpu_head->idx > idx ) {
cpu->next = _cpu_head;
_cpu_head = cpu;
if (!_cpu_tail)
_cpu_tail = cpu;
return cpu;
}
/* ... or in the appropriate position */
for ( cpu2=_cpu_head; cpu2; cpu2=cpu2->next ) {
if ( !cpu2->next || cpu2->next->idx > idx ) {
cpu->next = cpu2->next;
cpu2->next = cpu;
if (!cpu->next)
_cpu_tail = cpu;
return cpu;
}
}
if (cpu)
SNMP_FREE(cpu); /* just in case */
return NULL; /* Shouldn't happen! */
}
/*
* Work with a list of CPU entries, indexed by name
*/
netsnmp_cpu_info *netsnmp_cpu_get_byName( char *name, int create ) {
netsnmp_cpu_info *cpu;
/*
* Find the specified CPU entry
*/
for ( cpu=_cpu_head; cpu; cpu=cpu->next ) {
if ( !strcmp(cpu->name, name))
return cpu;
}
if (!create)
return NULL;
/*
* Create a new CPU entry, and append it to the list
*/
cpu = SNMP_MALLOC_TYPEDEF( netsnmp_cpu_info );
if (!cpu)
return NULL;
if (strlen(name) >= sizeof(cpu->name)) {
free(cpu);
snmp_log(LOG_ERR, "Name of CPU is too large: %s\n", name);
return NULL;
}
strcpy(cpu->name, name);
if ( _cpu_tail ) {
cpu->idx = _cpu_tail->idx+1;
_cpu_tail->next = cpu;
_cpu_tail = cpu;
} else {
cpu->idx = 0;
_cpu_head = cpu;
_cpu_tail = cpu;
}
return cpu;
}
netsnmp_cache *netsnmp_cpu_get_cache( void ) {
return _cpu_cache;
}
int netsnmp_cpu_load( void ) {
/*
* If we're automatically updating the stats regularly,
* then don't invoke the cache handling.
*/
return ( _cpuAutoUpdate ? 1
: netsnmp_cache_check_and_reload( _cpu_cache ));
}
/*
* Call the system-specific load routine regularly,
* keeping track of the relevant earlier results.
*/
static void
_cpu_update_stats( unsigned int reg, void* magic ) {
netsnmp_cpu_info *cpu;
int i;
for ( cpu=_cpu_head; cpu; cpu=cpu->next ) {
if ( !cpu->history ) {
/*
* First time through, we need to create buffers
* for the historical stats
*/
cpu->history = calloc( _cpuHistoryLen, sizeof(struct netsnmp_cpu_history));
} else {
/*
* Otherwise, rotate these values - in descending order
* with the earliest (relevant) statistics in entry 0.
* This means that the code to calculate the rolling averages
* is independent of the number of historical samples saved.
*/
for (i=0; i<_cpuHistoryLen-2; i++) {
cpu->history[i] = cpu->history[i+1];
}
cpu->history[i].user_hist = cpu->user_ticks;
cpu->history[i].sys_hist = cpu->sys_ticks;
cpu->history[i].idle_hist = cpu->idle_ticks;
cpu->history[i].nice_hist = cpu->nice_ticks;
cpu->history[i].total_hist = cpu->total_ticks;
cpu->history[i].ctx_hist = cpu->nCtxSwitches;
cpu->history[i].intr_hist = cpu->nInterrupts;
cpu->history[i].swpi_hist = cpu->swapIn;
cpu->history[i].swpo_hist = cpu->swapOut;
cpu->history[i].pagei_hist = cpu->pageIn;
cpu->history[i].pageo_hist = cpu->pageOut;
}
}
/*
* Now call the system-specific load routine, to
* retrieve the latest set of data.
*/
netsnmp_cpu_arch_load( NULL, NULL );
for ( cpu=_cpu_head; cpu; cpu=cpu->next ) {
cpu->total_ticks = cpu->user_ticks +
cpu->nice_ticks +
cpu->sys_ticks +
cpu->idle_ticks +
cpu->wait_ticks +
cpu->kern_ticks +
cpu->intrpt_ticks +
cpu->sirq_ticks;
}
}
void _cpu_copy_stats( netsnmp_cpu_info *cpu )
{
netsnmp_cpu_info *cpu2;
/*
* Copy "overall" statistics to the 'cpu0' entry
* on single CPU systems where this isn't done automatically
*/
cpu2 = netsnmp_cpu_get_byIdx( 0, 1 );
if (!cpu || !cpu2) return;
cpu2->user_ticks = cpu->user_ticks;
cpu2->nice_ticks = cpu->nice_ticks;
cpu2->sys_ticks = cpu->sys_ticks;
cpu2->sys2_ticks = cpu->sys2_ticks;
cpu2->idle_ticks = cpu->idle_ticks;
cpu2->wait_ticks = cpu->wait_ticks;
cpu2->kern_ticks = cpu->kern_ticks;
cpu2->intrpt_ticks = cpu->intrpt_ticks;
cpu2->sirq_ticks = cpu->sirq_ticks;
cpu2->nInterrupts = cpu->nInterrupts;
cpu2->nCtxSwitches = cpu->nCtxSwitches;
cpu2->swapIn = cpu->swapIn;
cpu2->swapOut = cpu->swapOut;
cpu2->pageIn = cpu->pageIn;
cpu2->pageOut = cpu->pageOut;
}