| #include <net-snmp/net-snmp-config.h> |
| #include <net-snmp/net-snmp-features.h> |
| |
| #if HAVE_LIMITS_H |
| #include <limits.h> |
| #endif |
| #if HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| #if HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #if HAVE_FCNTL_H |
| #include <fcntl.h> |
| #endif |
| #include <ctype.h> |
| #include <signal.h> |
| #if HAVE_MACHINE_PARAM_H |
| #include <machine/param.h> |
| #endif |
| #if HAVE_SYS_VMMETER_H |
| #if !defined(bsdi2) && !defined(netbsd1) |
| #include <sys/vmmeter.h> |
| #endif |
| #endif |
| #if HAVE_SYS_CONF_H |
| #include <sys/conf.h> |
| #endif |
| #if HAVE_SYS_FS_H |
| #include <sys/fs.h> |
| #else |
| #if HAVE_UFS_FS_H |
| #include <ufs/fs.h> |
| #else |
| #ifdef HAVE_SYS_STAT_H |
| #include <sys/stat.h> |
| #endif |
| #ifdef HAVE_SYS_VNODE_H |
| #include <sys/vnode.h> |
| #endif |
| #ifdef HAVE_UFS_UFS_QUOTA_H |
| #include <ufs/ufs/quota.h> |
| #endif |
| #ifdef HAVE_UFS_UFS_INODE_H |
| #include <ufs/ufs/inode.h> |
| #endif |
| #if HAVE_UFS_FFS_FS_H |
| #include <ufs/ffs/fs.h> |
| #endif |
| #endif |
| #endif |
| #if HAVE_MTAB_H |
| #include <mtab.h> |
| #endif |
| #include <sys/stat.h> |
| #include <errno.h> |
| #if HAVE_FSTAB_H |
| #include <fstab.h> |
| #endif |
| #if HAVE_SYS_STATVFS_H |
| #include <sys/statvfs.h> |
| #endif |
| #if HAVE_SYS_VFS_H |
| #include <sys/vfs.h> |
| #endif |
| #if (!defined(HAVE_STATVFS)) && defined(HAVE_STATFS) |
| #if HAVE_SYS_PARAM_H |
| #include <sys/param.h> |
| #endif |
| #if HAVE_SYS_MOUNT_H |
| #include <sys/mount.h> |
| #endif |
| #if HAVE_SYS_SYSCTL_H |
| #include <sys/sysctl.h> |
| #endif |
| #define statvfs statfs |
| #endif |
| #if HAVE_VM_SWAP_PAGER_H |
| #include <vm/swap_pager.h> |
| #endif |
| #if HAVE_SYS_FIXPOINT_H |
| #include <sys/fixpoint.h> |
| #endif |
| #if HAVE_MALLOC_H |
| #include <malloc.h> |
| #endif |
| #if HAVE_STRING_H |
| #include <string.h> |
| #else |
| #include <strings.h> |
| #endif |
| #if TIME_WITH_SYS_TIME |
| # include <sys/time.h> |
| # include <time.h> |
| #else |
| # if HAVE_SYS_TIME_H |
| # include <sys/time.h> |
| # else |
| # include <time.h> |
| # endif |
| #endif |
| |
| #include <sys/utsname.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 "mibdefs.h" |
| #include "struct.h" |
| #include "util_funcs/header_generic.h" |
| #include "vmstat.h" |
| |
| netsnmp_feature_require(hardware_cpu_load) |
| |
| |
| FindVarMethod var_extensible_vmstat; |
| |
| static int has_vmstat = 1; |
| static int has_cpu_26 = 1; |
| static time_t cache_time; |
| #define CACHE_TIMEOUT 5 |
| #define MAX_INT32 0x7fffffff |
| #define MAX_COUNTER 0xffffffff |
| |
| #define STAT_FILE "/proc/stat" |
| #define VMSTAT_FILE "/proc/vmstat" |
| |
| |
| void |
| init_vmstat(void) |
| { |
| struct variable2 extensible_vmstat_variables[] = { |
| {MIBINDEX, ASN_INTEGER, NETSNMP_OLDAPI_RONLY, |
| var_extensible_vmstat, 1, {MIBINDEX}}, |
| {ERRORNAME, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY, |
| var_extensible_vmstat, 1, {ERRORNAME}}, |
| {SWAPIN, ASN_INTEGER, NETSNMP_OLDAPI_RONLY, |
| var_extensible_vmstat, 1, {SWAPIN}}, |
| {SWAPOUT, ASN_INTEGER, NETSNMP_OLDAPI_RONLY, |
| var_extensible_vmstat, 1, {SWAPOUT}}, |
| {RAWSWAPIN, ASN_COUNTER, NETSNMP_OLDAPI_RONLY, |
| var_extensible_vmstat, 1, {RAWSWAPIN}}, |
| {RAWSWAPOUT, ASN_COUNTER, NETSNMP_OLDAPI_RONLY, |
| var_extensible_vmstat, 1, {RAWSWAPOUT}}, |
| {IOSENT, ASN_INTEGER, NETSNMP_OLDAPI_RONLY, |
| var_extensible_vmstat, 1, {IOSENT}}, |
| {IORECEIVE, ASN_INTEGER, NETSNMP_OLDAPI_RONLY, |
| var_extensible_vmstat, 1, {IORECEIVE}}, |
| {IORAWSENT, ASN_COUNTER, NETSNMP_OLDAPI_RONLY, |
| var_extensible_vmstat, 1, {IORAWSENT}}, |
| {IORAWRECEIVE, ASN_COUNTER, NETSNMP_OLDAPI_RONLY, |
| var_extensible_vmstat, 1, {IORAWRECEIVE}}, |
| {SYSINTERRUPTS, ASN_INTEGER, NETSNMP_OLDAPI_RONLY, |
| var_extensible_vmstat, 1, {SYSINTERRUPTS}}, |
| {SYSCONTEXT, ASN_INTEGER, NETSNMP_OLDAPI_RONLY, |
| var_extensible_vmstat, 1, {SYSCONTEXT}}, |
| {CPUUSER, ASN_INTEGER, NETSNMP_OLDAPI_RONLY, |
| var_extensible_vmstat, 1, {CPUUSER}}, |
| {CPUSYSTEM, ASN_INTEGER, NETSNMP_OLDAPI_RONLY, |
| var_extensible_vmstat, 1, {CPUSYSTEM}}, |
| {CPUIDLE, ASN_INTEGER, NETSNMP_OLDAPI_RONLY, |
| var_extensible_vmstat, 1, {CPUIDLE}}, |
| {CPURAWUSER, ASN_COUNTER, NETSNMP_OLDAPI_RONLY, |
| var_extensible_vmstat, 1, {CPURAWUSER}}, |
| {CPURAWNICE, ASN_COUNTER, NETSNMP_OLDAPI_RONLY, |
| var_extensible_vmstat, 1, {CPURAWNICE}}, |
| {CPURAWSYSTEM, ASN_COUNTER, NETSNMP_OLDAPI_RONLY, |
| var_extensible_vmstat, 1, {CPURAWSYSTEM}}, |
| {CPURAWKERNEL, ASN_COUNTER, NETSNMP_OLDAPI_RONLY, |
| var_extensible_vmstat, 1, {CPURAWKERNEL}}, |
| {CPURAWIDLE, ASN_COUNTER, NETSNMP_OLDAPI_RONLY, |
| var_extensible_vmstat, 1, {CPURAWIDLE}}, |
| {SYSRAWINTERRUPTS, ASN_COUNTER, NETSNMP_OLDAPI_RONLY, |
| var_extensible_vmstat, 1, {SYSRAWINTERRUPTS}}, |
| {SYSRAWCONTEXT, ASN_COUNTER, NETSNMP_OLDAPI_RONLY, |
| var_extensible_vmstat, 1, {SYSRAWCONTEXT}}, |
| {CPURAWWAIT, ASN_COUNTER, NETSNMP_OLDAPI_RONLY, |
| var_extensible_vmstat, 1, {CPURAWWAIT}}, |
| {CPURAWINTR, ASN_COUNTER, NETSNMP_OLDAPI_RONLY, |
| var_extensible_vmstat, 1, {CPURAWINTR}}, |
| {CPURAWSOFTIRQ, ASN_COUNTER, NETSNMP_OLDAPI_RONLY, |
| var_extensible_vmstat, 1, {CPURAWSOFTIRQ}}, |
| /* |
| * Future use: |
| */ |
| /* |
| * {ERRORFLAG, ASN_INTEGER, NETSNMP_OLDAPI_RONLY, |
| * var_extensible_vmstat, 1, {ERRORFLAG }}, |
| * {ERRORMSG, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY, |
| * var_extensible_vmstat, 1, {ERRORMSG }} |
| */ |
| }; |
| |
| /* |
| * Define the OID pointer to the top of the mib tree that we're |
| * registering underneath |
| */ |
| oid vmstat_variables_oid[] = { NETSNMP_UCDAVIS_MIB, 11 }; |
| |
| /* |
| * register ourselves with the agent to handle our mib tree |
| */ |
| REGISTER_MIB("ucd-snmp/vmstat", extensible_vmstat_variables, variable2, |
| vmstat_variables_oid); |
| } |
| |
| |
| static void |
| getstat(unsigned long *cuse, unsigned long *cice, unsigned long *csys, |
| unsigned long *cide, unsigned *pin, unsigned *pout, |
| unsigned *swpin, unsigned *swpout, unsigned *itot, unsigned *i1, |
| unsigned *ct, unsigned long *ciow, unsigned long *cirq, |
| unsigned long *csoft) |
| { |
| int statfd, vmstatfd; |
| static int first = 1; |
| static char *buff = NULL, *vmbuff = NULL; |
| static int bsize = 0, vmbsize = 0; |
| char *b, *c; |
| time_t now; |
| unsigned long long cpunum; |
| unsigned long long cusell = 0, cicell = 0, csysll = 0, cidell = 0, |
| ciowll = 0, cirqll = 0, csoftll = 0, ctll = 0, itotll = 0, i1ll = 0; |
| |
| time(&now); |
| if (cache_time + CACHE_TIMEOUT < now) { |
| if ((statfd = open(STAT_FILE, O_RDONLY, 0)) == -1) { |
| snmp_log_perror(STAT_FILE); |
| return; |
| } |
| if (bsize == 0) { |
| bsize = 256; |
| buff = malloc(bsize); |
| } |
| while (read(statfd, buff, bsize) == bsize) { |
| bsize += 256; |
| buff = realloc(buff, bsize); |
| DEBUGMSGTL(("vmstat", "/proc/stat buffer increased to %d\n", bsize)); |
| close(statfd); |
| statfd = open(STAT_FILE, O_RDONLY, 0); |
| } |
| close(statfd); |
| |
| 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 = 256; |
| vmbuff = malloc(vmbsize); |
| } |
| while (read(vmstatfd, vmbuff, vmbsize) == vmbsize) { |
| vmbsize += 256; |
| vmbuff = realloc(vmbuff, vmbsize); |
| close(vmstatfd); |
| vmstatfd = open(VMSTAT_FILE, O_RDONLY, 0); |
| } |
| close(vmstatfd); |
| } |
| } |
| cache_time = now; |
| } |
| |
| *itot = 0; |
| *i1 = 1; |
| c = buff; |
| while ((c = strstr(c+1, "cpu")) != NULL) |
| b = c; |
| sscanf(b, "cpu%llu", &cpunum); |
| cpunum++; |
| b = strstr(buff, "cpu "); |
| if (b) { |
| if (!has_cpu_26 || |
| sscanf(b, "cpu %llu %llu %llu %llu %llu %llu %llu", &cusell, |
| &cicell, &csysll, &cidell, &ciowll, &cirqll, &csoftll) != 7) { |
| has_cpu_26 = 0; |
| sscanf(b, "cpu %llu %llu %llu %llu", &cusell, &cicell, &csysll, |
| &cidell); |
| *ciow = *cirq = *csoft = 0; |
| } else { |
| *ciow = (unsigned long)(ciowll/cpunum); |
| *cirq = (unsigned long)(cirqll/cpunum); |
| *csoft = (unsigned long)(csoftll/cpunum); |
| } |
| *cuse = (unsigned long)(cusell/cpunum); |
| *cice = (unsigned long)(cicell/cpunum); |
| *csys = (unsigned long)(csysll/cpunum); |
| *cide = (unsigned long)(cidell/cpunum); |
| |
| } |
| else { |
| if (first) |
| snmp_log(LOG_ERR, "No cpu line in %s\n", STAT_FILE); |
| *cuse = *cice = *csys = *cide = *ciow = *cirq = *csoft = 0; |
| } |
| if (has_vmstat) { |
| b = strstr(vmbuff, "pgpgin "); |
| if (b) |
| sscanf(b, "pgpgin %u", pin); |
| else { |
| if (first) |
| snmp_log(LOG_ERR, "No pgpgin line in %s\n", VMSTAT_FILE); |
| *pin = 0; |
| } |
| b = strstr(vmbuff, "pgpgout "); |
| if (b) |
| sscanf(b, "pgpgout %u", pout); |
| else { |
| if (first) |
| snmp_log(LOG_ERR, "No pgpgout line in %s\n", VMSTAT_FILE); |
| *pout = 0; |
| } |
| b = strstr(vmbuff, "pswpin "); |
| if (b) |
| sscanf(b, "pswpin %u", swpin); |
| else { |
| if (first) |
| snmp_log(LOG_ERR, "No pswpin line in %s\n", VMSTAT_FILE); |
| *swpin = 0; |
| } |
| b = strstr(vmbuff, "pswpout "); |
| if (b) |
| sscanf(b, "pswpout %u", swpout); |
| else { |
| if (first) |
| snmp_log(LOG_ERR, "No pswpout line in %s\n", VMSTAT_FILE); |
| *swpout = 0; |
| } |
| } |
| else { |
| b = strstr(buff, "page "); |
| if (b) |
| sscanf(b, "page %u %u", pin, pout); |
| else { |
| if (first) |
| snmp_log(LOG_ERR, "No page line in %s\n", STAT_FILE); |
| *pin = *pout = 0; |
| } |
| b = strstr(buff, "swap "); |
| if (b) |
| sscanf(b, "swap %u %u", swpin, swpout); |
| else { |
| if (first) |
| snmp_log(LOG_ERR, "No swap line in %s\n", STAT_FILE); |
| *swpin = *swpout = 0; |
| } |
| } |
| b = strstr(buff, "intr "); |
| if (b) { |
| sscanf(b, "intr %llu %llu", &itotll, &i1ll); |
| *itot = (unsigned)itotll; |
| *i1 = (unsigned)i1ll; |
| } |
| else { |
| if (first) |
| snmp_log(LOG_ERR, "No intr line in %s\n", STAT_FILE); |
| *itot = 0; |
| } |
| b = strstr(buff, "ctxt "); |
| if (b) { |
| sscanf(b, "ctxt %llu", &ctll); |
| *ct = (unsigned long)ctll; |
| } |
| else { |
| if (first) |
| snmp_log(LOG_ERR, "No ctxt line in %s\n", STAT_FILE); |
| *ct = 0; |
| } |
| first = 0; |
| } |
| |
| enum vmstat_index { swapin = 0, swapout, |
| rawswapin, rawswapout, |
| iosent, ioreceive, |
| rawiosent, rawioreceive, |
| sysinterrupts, syscontext, |
| cpuuser, cpusystem, cpuidle, |
| cpurawuser, cpurawnice, |
| cpurawsystem, cpurawidle, |
| cpurawinter, cpurawsoft, cpurawwait, |
| rawinterrupts, rawcontext |
| }; |
| |
| static unsigned |
| vmstat(int iindex) |
| { |
| double duse, dsys, didl, ddiv, divo2; |
| double druse, drnic, drsys, dridl; |
| unsigned int hertz; |
| double ddiv2; |
| |
| netsnmp_cpu_info *cpu; |
| netsnmp_cpu_load(); |
| cpu = netsnmp_cpu_get_byIdx( -1, 0 ); |
| |
| duse = cpu->user_ticks + cpu->nice_ticks; |
| dsys = cpu->sys_ticks; |
| didl = cpu->idle_ticks; |
| ddiv = duse + dsys + didl; |
| hertz = sysconf(_SC_CLK_TCK); /* get ticks/s from system */ |
| divo2 = ddiv / 2; |
| druse = cpu->user_ticks; |
| drnic = cpu->nice_ticks; |
| drsys = cpu->sys_ticks; |
| dridl = cpu->idle_ticks; |
| |
| ddiv2 = ddiv + cpu->wait_ticks |
| + cpu->intrpt_ticks |
| + cpu->sirq_ticks; |
| if (cpu->history) { |
| duse -= (cpu->history[0].user_hist + cpu->history[0].nice_hist); |
| dsys -= cpu->history[0].sys_hist; |
| didl -= cpu->history[0].idle_hist; |
| ddiv2 -= cpu->history[0].total_hist; |
| } |
| if (!ddiv) ddiv=1; /* Protect against division-by-0 */ |
| |
| switch (iindex) { |
| case swapin: |
| return (cpu->swapIn * 4 * hertz + divo2) / ddiv; |
| case swapout: |
| return (cpu->swapOut * 4 * hertz + divo2) / ddiv; |
| case iosent: |
| return (cpu->pageIn * hertz + divo2) / ddiv; |
| case ioreceive: |
| return (cpu->pageOut * hertz + divo2) / ddiv; |
| case sysinterrupts: |
| return (cpu->nInterrupts * hertz + divo2) / ddiv; |
| case syscontext: |
| return (cpu->nCtxSwitches * hertz + divo2) / ddiv; |
| case cpuuser: |
| return (ddiv2 ? 100 * duse / ddiv2 : 0); |
| case cpusystem: |
| return (ddiv2 ? 100 * dsys / ddiv2 : 0); |
| case cpuidle: |
| return (ddiv2 ? 100 * didl / ddiv2 : 0); |
| case cpurawuser: |
| return druse; |
| case cpurawnice: |
| return drnic; |
| case cpurawsystem: |
| return drsys; |
| case cpurawidle: |
| return dridl; |
| case rawinterrupts: |
| return cpu->nInterrupts; |
| case rawcontext: |
| return cpu->nCtxSwitches; |
| case cpurawwait: |
| return cpu->wait_ticks; |
| case cpurawinter: |
| return cpu->intrpt_ticks; |
| case cpurawsoft: |
| return cpu->sirq_ticks; |
| case rawiosent: |
| return cpu->pageOut*2; |
| case rawioreceive: |
| return cpu->pageIn*2; |
| case rawswapin: |
| return cpu->swapIn; |
| case rawswapout: |
| return cpu->swapOut; |
| default: |
| return -1; |
| } |
| } |
| |
| unsigned char * |
| var_extensible_vmstat(struct variable *vp, |
| oid * name, |
| size_t * length, |
| int exact, |
| size_t * var_len, WriteMethod ** write_method) |
| { |
| |
| static long long_ret; |
| static char errmsg[300]; |
| |
| long_ret = 0; /* set to 0 as default */ |
| |
| if (header_generic(vp, name, length, exact, var_len, write_method)) |
| return (NULL); |
| switch (vp->magic) { |
| case MIBINDEX: |
| long_ret = 1; |
| return ((u_char *) (&long_ret)); |
| case ERRORNAME: /* dummy name */ |
| sprintf(errmsg, "systemStats"); |
| *var_len = strlen(errmsg); |
| return ((u_char *) (errmsg)); |
| case SWAPIN: |
| long_ret = vmstat(swapin) & MAX_INT32; |
| return ((u_char *) (&long_ret)); |
| case SWAPOUT: |
| long_ret = vmstat(swapout) & MAX_INT32; |
| return ((u_char *) (&long_ret)); |
| case RAWSWAPIN: |
| long_ret = vmstat(rawswapin) & MAX_COUNTER; |
| return ((u_char *) (&long_ret)); |
| case RAWSWAPOUT: |
| long_ret = vmstat(rawswapout) & MAX_COUNTER; |
| return ((u_char *) (&long_ret)); |
| case IOSENT: |
| long_ret = vmstat(iosent) & MAX_INT32; |
| return ((u_char *) (&long_ret)); |
| case IORECEIVE: |
| long_ret = vmstat(ioreceive) & MAX_INT32; |
| return ((u_char *) (&long_ret)); |
| case IORAWSENT: |
| long_ret = vmstat(rawiosent) & MAX_COUNTER; |
| return ((u_char *) (&long_ret)); |
| case IORAWRECEIVE: |
| long_ret = vmstat(rawioreceive) & MAX_COUNTER; |
| return ((u_char *) (&long_ret)); |
| case SYSINTERRUPTS: |
| long_ret = vmstat(sysinterrupts) & MAX_INT32; |
| return ((u_char *) (&long_ret)); |
| case SYSCONTEXT: |
| long_ret = vmstat(syscontext) & MAX_INT32; |
| return ((u_char *) (&long_ret)); |
| case CPUUSER: |
| long_ret = vmstat(cpuuser) & MAX_INT32; |
| return ((u_char *) (&long_ret)); |
| case CPUSYSTEM: |
| long_ret = vmstat(cpusystem) & MAX_INT32; |
| return ((u_char *) (&long_ret)); |
| case CPUIDLE: |
| long_ret = vmstat(cpuidle) & MAX_INT32; |
| return ((u_char *) (&long_ret)); |
| case CPURAWUSER: |
| long_ret = vmstat(cpurawuser) & MAX_COUNTER; |
| return ((u_char *) (&long_ret)); |
| case CPURAWNICE: |
| long_ret = vmstat(cpurawnice) & MAX_COUNTER; |
| return ((u_char *) (&long_ret)); |
| case CPURAWSYSTEM: |
| long_ret = (vmstat(cpurawsystem)+vmstat(cpurawinter)+vmstat(cpurawsoft)) & MAX_COUNTER; |
| return ((u_char *) (&long_ret)); |
| case CPURAWKERNEL: |
| long_ret = vmstat(cpurawsystem) & MAX_COUNTER; |
| return ((u_char *) (&long_ret)); |
| case CPURAWIDLE: |
| long_ret = vmstat(cpurawidle) & MAX_COUNTER; |
| return ((u_char *) (&long_ret)); |
| case SYSRAWINTERRUPTS: |
| long_ret = vmstat(rawinterrupts) & MAX_COUNTER; |
| return (u_char *)&long_ret; |
| case SYSRAWCONTEXT: |
| long_ret = vmstat(rawcontext) & MAX_COUNTER; |
| return (u_char *)&long_ret; |
| case CPURAWWAIT: |
| if (!has_cpu_26) return NULL; |
| long_ret = vmstat(cpurawwait) & MAX_COUNTER; |
| return ((u_char *) (&long_ret)); |
| case CPURAWINTR: |
| if (!has_cpu_26) return NULL; |
| long_ret = vmstat(cpurawinter) & MAX_COUNTER; |
| return ((u_char *) (&long_ret)); |
| case CPURAWSOFTIRQ: |
| if (!has_cpu_26) return NULL; |
| long_ret = vmstat(cpurawsoft) & MAX_COUNTER; |
| return ((u_char *) (&long_ret)); |
| |
| /* |
| * reserved for future use |
| */ |
| /* |
| * case ERRORFLAG: |
| * return((u_char *) (&long_ret)); |
| * case ERRORMSG: |
| * return((u_char *) (&long_ret)); |
| */ |
| default: |
| snmp_log(LOG_ERR, "vmstat.c: don't know how to handle %d request\n", |
| vp->magic); |
| } |
| return NULL; |
| } |