blob: b1dc30832400acae616576a86caf51cc9104cf19 [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>
#include <unistd.h>
#include <fcntl.h>
#define CPU_FILE "/proc/cpuinfo"
#define STAT_FILE "/proc/stat"
#define VMSTAT_FILE "/proc/vmstat"
/* Which field(s) describe the type of CPU */
#if defined(__i386__) || defined(__x86_64__)
#define DESCR_FIELD "vendor_id"
#define DESCR2_FIELD "model name"
#endif
#if defined(__powerpc__) || defined(__powerpc64__)
#define DESCR_FIELD "cpu\t"
#endif
#if defined(__ia64__)
/* since vendor is always Intel ... we don't parse vendor */
#define DESCR_FIELD "family"
#endif
/*
* Initialise the list of CPUs on the system
* (including descriptions)
*
* XXX - Assumes x86-style /proc/cpuinfo format
* See CPUinfo database at
* http://www.rush3d.com/gcc/
* for info on alternative styles
*/
void init_cpu_linux( void ) {
FILE *fp;
char buf[1024], *cp;
int i, n = 0;
netsnmp_cpu_info *cpu = netsnmp_cpu_get_byIdx( -1, 1 );
strcpy(cpu->name, "Overall CPU statistics");
fp = fopen( CPU_FILE, "r" );
if (!fp) {
snmp_log(LOG_ERR, "Can't open procinfo file %s\n", CPU_FILE);
return;
}
while ( fgets( buf, sizeof(buf), fp)) {
if ( sscanf( buf, "processor : %d", &i ) == 1) {
n++;
cpu = netsnmp_cpu_get_byIdx( i, 1 );
cpu->status = 2; /* running */
sprintf( cpu->name, "cpu%d", i );
#if defined(__s390__) || defined(__s390x__)
strcat( cpu->descr, "An S/390 CPU" );
#endif
}
#if defined(__s390__) || defined(__s390x__)
/* s390 may have different format of CPU_FILE */
else {
if (sscanf( buf, "processor %d:", &i ) == 1) {
n++;
cpu = netsnmp_cpu_get_byIdx( i, 1 );
cpu->status = 2; /* running */
sprintf( cpu->name, "cpu%d", i );
strcat( cpu->descr, "An S/390 CPU" );
}
}
#endif
#ifdef DESCR_FIELD
if (!strncmp( buf, DESCR_FIELD, strlen(DESCR_FIELD))) {
cp = strchr( buf, ':' );
strcpy( cpu->descr, cp+2 );
cp = strchr( cpu->descr, '\n' );
*cp = 0;
}
#endif
#ifdef DESCR2_FIELD
if (!strncmp( buf, DESCR2_FIELD, strlen(DESCR2_FIELD))) {
cp = strchr( buf, ':' );
strcat( cpu->descr, cp );
cp = strchr( cpu->descr, '\n' );
*cp = 0;
}
#endif
}
fclose(fp);
cpu_num = n;
}
void _cpu_load_swap_etc( char *buff, netsnmp_cpu_info *cpu );
/*
* Load the latest CPU usage statistics
*/
int netsnmp_cpu_arch_load( netsnmp_cache *cache, void *magic ) {
static char *buff = NULL;
static int bsize = 0;
static int first = 1;
static int num_cpuline_elem = 0;
int bytes_read, statfd, i;
char *b1, *b2;
unsigned long long cusell = 0, cicell = 0, csysll = 0, cidell = 0,
ciowll = 0, cirqll = 0, csoftll = 0, cstealll = 0,
cguestll = 0, cguest_nicell = 0;
netsnmp_cpu_info* cpu;
if ((statfd = open(STAT_FILE, O_RDONLY, 0)) == -1) {
snmp_log_perror(STAT_FILE);
return -1;
}
if (bsize == 0) {
bsize = getpagesize()-1;
buff = (char*)malloc(bsize+1);
}
while ((bytes_read = read(statfd, buff, bsize)) == bsize) {
bsize += BUFSIZ;
buff = (char*)realloc(buff, bsize+1);
DEBUGMSGTL(("cpu", "/proc/stat buffer increased to %d\n", bsize));
close(statfd);
statfd = open(STAT_FILE, O_RDONLY, 0);
if (statfd == -1) {
snmp_log_perror(STAT_FILE);
return -1;
}
}
close(statfd);
if ( bytes_read < 0 ) {
snmp_log_perror(STAT_FILE "read error");
return -1;
}
buff[bytes_read] = '\0';
/*
* CPU statistics (overall and per-CPU)
*/
b1 = buff;
while ((b2 = strstr( b1, "cpu" ))) {
if (b2[3] == ' ') {
cpu = netsnmp_cpu_get_byIdx( -1, 0 );
if (!cpu) {
snmp_log_perror("No (overall) CPU info entry");
return -1;
}
b1 = b2+4; /* Skip "cpu " */
} else {
sscanf( b2, "cpu%d", &i );
/* Create on the fly to support non-x86 systems - see init */
cpu = netsnmp_cpu_get_byIdx( i, 1 );
if (!cpu) {
snmp_log_perror("Missing CPU info entry");
break;
}
b1 = b2; /* Skip "cpuN " */
while(*b1 != ' ') b1++;
b1++;
}
num_cpuline_elem = sscanf(b1, "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
&cusell, &cicell, &csysll, &cidell, &ciowll, &cirqll, &csoftll, &cstealll, &cguestll, &cguest_nicell);
DEBUGMSGTL(("cpu", "/proc/stat cpu line number of elements: %i\n", num_cpuline_elem));
/* kernel 2.6.33 and above */
if (num_cpuline_elem == 10) {
cpu->guestnice_ticks = cguest_nicell;
}
/* kernel 2.6.24 and above */
if (num_cpuline_elem >= 9) {
cpu->guest_ticks = cguestll;
}
/* kernel 2.6.11 and above */
if (num_cpuline_elem >= 8) {
cpu->steal_ticks = cstealll;
}
/* kernel 2.6 */
if (num_cpuline_elem >= 5) {
cpu->wait_ticks = ciowll;
cpu->intrpt_ticks = cirqll;
cpu->sirq_ticks = csoftll;
}
/* rest */
cpu->user_ticks = cusell;
cpu->nice_ticks = cicell;
cpu->sys_ticks = csysll;
cpu->idle_ticks = cidell;
}
if ( b1 == buff ) {
if (first)
snmp_log(LOG_ERR, "No cpu line in %s\n", STAT_FILE);
}
/*
* Interrupt/Context Switch statistics
* XXX - Do these really belong here ?
*/
cpu = netsnmp_cpu_get_byIdx( -1, 0 );
_cpu_load_swap_etc( buff, cpu );
/*
* XXX - TODO: extract per-CPU statistics
* (Into separate netsnmp_cpu_info data structures)
*/
first = 0;
return 0;
}
/*
* Interrupt/Context Switch statistics
* XXX - Do these really belong here ?
*/
void _cpu_load_swap_etc( char *buff, netsnmp_cpu_info *cpu ) {
static int has_vmstat = 1;
static char *vmbuff = NULL;
static int vmbsize = 0;
static int first = 1;
int bytes_read, vmstatfd;
char *b;
unsigned long long pin, pout, swpin, swpout;
unsigned long long itot, iticks, ctx;
if (has_vmstat) {
vmstatfd = open(VMSTAT_FILE, O_RDONLY, 0);
if (vmstatfd == -1 ) {
snmp_log(LOG_ERR, "cannot open %s\n", VMSTAT_FILE);
has_vmstat = 0;
} else {
if (vmbsize == 0) {
vmbsize = getpagesize()-1;
vmbuff = (char*)malloc(vmbsize+1);
}
while ((bytes_read = read(vmstatfd, vmbuff, vmbsize)) == vmbsize) {
vmbsize += BUFSIZ;
vmbuff = (char*)realloc(vmbuff, vmbsize+1);
close(vmstatfd);
vmstatfd = open(VMSTAT_FILE, O_RDONLY, 0);
if (vmstatfd == -1) {
snmp_log_perror("cannot open " VMSTAT_FILE);
return;
}
}
close(vmstatfd);
if ( bytes_read < 0 ) {
snmp_log_perror(VMSTAT_FILE "read error");
return;
}
vmbuff[bytes_read] = '\0';
}
}
if (has_vmstat) {
b = strstr(vmbuff, "pgpgin ");
if (b) {
sscanf(b, "pgpgin %llu", &pin);
cpu->pageIn = (unsigned long long)pin*2; /* ??? */
} else {
if (first)
snmp_log(LOG_ERR, "No pgpgin line in %s\n", VMSTAT_FILE);
cpu->pageIn = 0;
}
b = strstr(vmbuff, "pgpgout ");
if (b) {
sscanf(b, "pgpgout %llu", &pout);
cpu->pageOut = (unsigned long long)pout*2; /* ??? */
} else {
if (first)
snmp_log(LOG_ERR, "No pgpgout line in %s\n", VMSTAT_FILE);
cpu->pageOut = 0;
}
b = strstr(vmbuff, "pswpin ");
if (b) {
sscanf(b, "pswpin %llu", &swpin);
cpu->swapIn = (unsigned long long)swpin;
} else {
if (first)
snmp_log(LOG_ERR, "No pswpin line in %s\n", VMSTAT_FILE);
cpu->swapIn = 0;
}
b = strstr(vmbuff, "pswpout ");
if (b) {
sscanf(b, "pswpout %llu", &swpout);
cpu->swapOut = (unsigned long long)swpout;
} else {
if (first)
snmp_log(LOG_ERR, "No pswpout line in %s\n", VMSTAT_FILE);
cpu->swapOut = 0;
}
}
else {
b = strstr(buff, "page ");
if (b) {
sscanf(b, "page %llu %llu", &pin, &pout);
cpu->pageIn = (unsigned long long)pin;
cpu->pageOut = (unsigned long long)pout;
} else {
if (first)
snmp_log(LOG_ERR, "No page line in %s\n", STAT_FILE);
cpu->pageIn = cpu->pageOut = 0;
}
b = strstr(buff, "swap ");
if (b) {
sscanf(b, "swap %llu %llu", &swpin, &swpout);
cpu->swapIn = (unsigned long long)swpin;
cpu->swapOut = (unsigned long long)swpout;
} else {
if (first)
snmp_log(LOG_ERR, "No swap line in %s\n", STAT_FILE);
cpu->swapIn = cpu->swapOut = 0;
}
}
b = strstr(buff, "intr ");
if (b) {
sscanf(b, "intr %llu %llu", &itot, &iticks);
cpu->nInterrupts = (unsigned long long)itot;
/* iticks not used? */
} else {
if (first)
snmp_log(LOG_ERR, "No intr line in %s\n", STAT_FILE);
}
b = strstr(buff, "ctxt ");
if (b) {
sscanf(b, "ctxt %llu", &ctx);
cpu->nCtxSwitches = (unsigned long long)ctx;
} else {
if (first)
snmp_log(LOG_ERR, "No ctxt line in %s\n", STAT_FILE);
}
first = 0;
}