blob: 7cce6e7dd3456e046be67b09ce5b92de91a2c38b [file] [log] [blame]
/*
* swrun_kinfo.c:
* hrSWRunTable data access:
* kvm_getprocs() interface - FreeBSD, NetBSD, OpenBSD
*
* NB: later FreeBSD and OpenBSD use different kinfo_proc structures
*/
#include <net-snmp/net-snmp-config.h>
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#ifdef HAVE_KVM_H
#include <kvm.h>
#endif
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef HAVE_SYS_SYSCTL_H
#include <sys/sysctl.h>
#endif
#ifdef HAVE_SYS_PROC_H
#include <sys/proc.h>
#endif
#ifdef HAVE_SYS_USER_H
#include <sys/user.h>
#endif
#ifdef HAVE_UVM_UVM_EXTERNAL_H
#include <uvm/uvm_external.h>
#endif
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include <net-snmp/library/container.h>
#include <net-snmp/library/snmp_debug.h>
#include <net-snmp/data_access/swrun.h>
extern kvm_t *kd;
#if defined(freebsd5) && __FreeBSD_version >= 500014
/*
* later FreeBSD kinfo_proc field names
*/
#define SWRUN_TABLE kinfo_proc
#define SWRUN_K_STAT ki_stat
#define SWRUN_K_PID ki_pid
#define SWRUN_K_PPID ki_ppid
#define SWRUN_K_TID ki_tid
#define SWRUN_K_COMM ki_comm
#define SWRUN_K_FLAG ki_flag
#define SWRUN_K_CLASS ki_pri.pri_class
#elif HAVE_KVM_GETPROC2 || defined(openbsd5)
/*
* newer NetBSD, OpenBSD kinfo_proc2 field names
*/
#if defined(openbsd5)
#define SWRUN_TABLE kinfo_proc
#else
#define SWRUN_TABLE kinfo_proc2
#endif
#define SWRUN_K_STAT p_stat
#define SWRUN_K_PID p_pid
#define SWRUN_K_COMM p_comm
#define SWRUN_K_FLAG p_flag
/* SWRUN_K_CLASS not defined */
#elif defined(dragonfly)
/*
* DragonFly is different ...
*/
#define SWRUN_TABLE kinfo_proc
#define SWRUN_K_STAT kp_stat
#define SWRUN_K_PID kp_pid
#define SWRUN_K_COMM kp_comm
#define SWRUN_K_FLAG kp_flags
/* SWRUN_K_CLASS not defined */
#else
/*
* early FreeBSD, NetBSD, OpenBSD kinfo_proc field names
*/
#define SWRUN_TABLE kinfo_proc
#define SWRUN_K_STAT kp_proc.p_stat
#define SWRUN_K_PID kp_proc.p_pid
#define SWRUN_K_COMM kp_proc.p_comm
#define SWRUN_K_FLAG kp_proc.p_flag
/* SWRUN_K_CLASS not defined */
#endif
/*
* Define dummy values if not already provided by the system
*/
#ifdef dragonfly
/*
* DragonFly is special. p_stat is an enum!
*/
#define SRUN 200
#define SSLEEP 202
#define SWAIT 203
#define SLOCK 205
#define SDEAD 208
#define SONPROC 209
#else
#ifndef SRUN
#define SRUN 200 /* Defined by FreeBSD/OpenBSD, missing in NetBSD */
#endif
#ifndef SACTIVE
#define SACTIVE 201 /* Defined by NetBSD, missing in FreeBSD/OpenBSD */
#endif
#ifndef SSLEEP
#define SSLEEP 202 /* Defined by FreeBSD/OpenBSD, missing in NetBSD */
#endif
#ifndef SWAIT
#define SWAIT 203 /* Defined by FreeBSD, missing in NetBSD/OpenBSD */
#endif
#ifndef SSTOP
#define SSTOP 204 /* Defined by FreeBSD/NetBSD/OpenBSD */
#endif
#ifndef SLOCK
#define SLOCK 205 /* Defined by FreeBSD, missing in NetBSD/OpenBSD */
#endif
#ifndef SIDL
#define SIDL 206 /* Defined by FreeBSD/NetBSD/OpenBSD */
#endif
#ifndef SZOMB
#define SZOMB 207 /* Defined by FreeBSD/NetBSD/OpenBSD */
#endif
#ifndef SDEAD
#define SDEAD 208 /* Defined by OpenBSD, missing in FreeBSD/NetBSD */
#endif
#ifndef SONPROC
#define SONPROC 209 /* Defined by OpenBSD, missing in FreeBSD/NetBSD */
#endif
#endif
/* ---------------------------------------------------------------------
*/
void
netsnmp_arch_swrun_init(void)
{
#if NETSNMP_CAN_USE_SYSCTL && defined(CTL_KERN) && defined(KERN_MAXPROC)
extern int _swrun_max;
size_t max_size = sizeof(_swrun_max);
int maxproc_mib[] = { CTL_KERN, KERN_MAXPROC };
sysctl(maxproc_mib, 2, &_swrun_max, &max_size, NULL, 0);
#endif
return;
}
/* ---------------------------------------------------------------------
*/
int
netsnmp_arch_swrun_container_load( netsnmp_container *container, u_int flags)
{
struct SWRUN_TABLE *proc_table;
int nprocs, i, rc;
char buf[BUFSIZ+1], **argv;
netsnmp_swrun_entry *entry;
if ( 0 == kd ) {
DEBUGMSGTL(("swrun:load:arch"," Can't query kvm info\n"));
return 1; /* No handle for retrieving process table */
}
#if defined(openbsd5)
proc_table = kvm_getprocs(kd, KERN_PROC_KTHREAD, 0, sizeof(struct kinfo_proc), &nprocs );
#elif defined(HAVE_KVM_GETPROC2)
proc_table = kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc2), &nprocs );
#else
proc_table = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nprocs );
#endif
for ( i=0 ; i<nprocs; i++ ) {
if ( 0 == proc_table[i].SWRUN_K_STAT )
continue;
if ( -1 == proc_table[i].SWRUN_K_PID )
continue;
#ifdef SWRUN_K_TID
if ( 0 == proc_table[i].SWRUN_K_PPID )
proc_table[i].SWRUN_K_PID = proc_table[i].SWRUN_K_TID;
#endif
entry = netsnmp_swrun_entry_create(proc_table[i].SWRUN_K_PID);
if (NULL == entry)
continue; /* error already logged by function */
rc = CONTAINER_INSERT(container, entry);
/*
* There are two possible sources for the command being run:
* - SWRUN_K_COMM (from the proc_table entry directly)
* - running kvm_getargv on the process entry.
*
* We'll use argv[0] (if set) for hrSWRunPath,
* since that might potentially contain the
* absolute path to the running binary.
* We'll use SWRUN_K_COMM for hrSWRunName,
* and as an alternative for hrSWRunPath
*/
#if HAVE_KVM_GETPROC2
argv = kvm_getargv2( kd, &(proc_table[i]), 0);
#else
argv = kvm_getargv( kd, &(proc_table[i]), BUFSIZ);
#endif
entry->hrSWRunName_len = sprintf(entry->hrSWRunName, "%.*s",
(int)sizeof(entry->hrSWRunName) - 1,
proc_table[i].SWRUN_K_COMM);
if (argv && *argv) {
entry->hrSWRunPath_len = sprintf(entry->hrSWRunPath, "%.*s",
(int)sizeof(entry->hrSWRunPath)-1,
argv[0]);
} else {
memcpy( entry->hrSWRunPath, entry->hrSWRunName,
entry->hrSWRunName_len );
entry->hrSWRunPath_len = entry->hrSWRunName_len;
}
/*
* Stitch together the rest of argv[] to build hrSWRunParameters
*
* Note:
* We add a separating space before each argv[] parameter,
* *including* the first one. So we need to skip this
* leading space (buf[0]) when setting hrSWRunParameters.
* This is also why we cleared the first *two* characters
* in the buffer initially. If there were no command-line
* arguments, then buf[1] would still be a null string.
*/
buf[0] = '\0';
buf[1] = '\0';
if (argv)
argv++; /* Skip argv[0] */
while ( argv && *argv ) {
strlcat(buf, " ", sizeof(buf));
strlcat(buf, *argv, sizeof(buf));
argv++;
}
if (strlen(buf) >= BUFSIZ-10)
snmp_log(LOG_ERR, "params %lu/%d %s\n", strlen(buf), BUFSIZ, buf);
entry->hrSWRunParameters_len =
sprintf(entry->hrSWRunParameters, "%.*s",
(int)sizeof(entry->hrSWRunParameters) - 1, buf+1);
entry->hrSWRunType = (P_SYSTEM & proc_table[i].SWRUN_K_FLAG)
#ifdef SWRUN_K_CLASS
? ((PRI_ITHD == proc_table[i].SWRUN_K_CLASS)
? 3 /* device driver */
: 2 /* operating system */
)
: 4 /* application */
#else
? 2 /* operating system */
: 4 /* application */
#endif
;
#ifdef netbsd5
switch (proc_table[i].SWRUN_K_STAT) {
case LSONPROC:
case LSRUN: entry->hrSWRunStatus = HRSWRUNSTATUS_RUNNING;
break;
case LSSLEEP: entry->hrSWRunStatus = HRSWRUNSTATUS_RUNNABLE;
break;
case LSIDL:
case LSSUSPENDED:
case LSSTOP: entry->hrSWRunStatus = HRSWRUNSTATUS_NOTRUNNABLE;
break;
case LSDEAD:
case LSZOMB: entry->hrSWRunStatus = HRSWRUNSTATUS_INVALID;
break;
default:
entry->hrSWRunStatus = HRSWRUNSTATUS_INVALID;
snmp_log(LOG_ERR, "Bad process status %c (0x%x)\n", proc_table[i].SWRUN_K_STAT, proc_table[i].SWRUN_K_STAT);
break;
}
#else
switch (proc_table[i].SWRUN_K_STAT & 0xFF) {
case SONPROC:
case SRUN: entry->hrSWRunStatus = HRSWRUNSTATUS_RUNNING;
break;
case SSLEEP:
case SACTIVE:
case SWAIT: entry->hrSWRunStatus = HRSWRUNSTATUS_RUNNABLE;
break;
case SIDL:
case SSTOP:
case SLOCK: entry->hrSWRunStatus = HRSWRUNSTATUS_NOTRUNNABLE;
break;
case SDEAD:
case SZOMB: entry->hrSWRunStatus = HRSWRUNSTATUS_INVALID; /* i.e. "not loaded" */
break;
default: entry->hrSWRunStatus = HRSWRUNSTATUS_INVALID; /* Actually invalid */
snmp_log(LOG_ERR, "Bad process status %c (0x%x)\n", proc_table[i].SWRUN_K_STAT, proc_table[i].SWRUN_K_STAT);
break;
}
#endif
#if defined(freebsd5) && __FreeBSD_version >= 500014
entry->hrSWRunPerfCPU = (proc_table[i].ki_rusage.ru_utime.tv_sec*1000000 + proc_table[i].ki_rusage.ru_utime.tv_usec) / 10000;
entry->hrSWRunPerfCPU += (proc_table[i].ki_rusage.ru_stime.tv_sec*1000000 + proc_table[i].ki_rusage.ru_stime.tv_usec) / 10000;
entry->hrSWRunPerfCPU += (proc_table[i].ki_rusage_ch.ru_utime.tv_sec*1000000 + proc_table[i].ki_rusage_ch.ru_utime.tv_usec) / 10000;
entry->hrSWRunPerfCPU += (proc_table[i].ki_rusage_ch.ru_stime.tv_sec*1000000 + proc_table[i].ki_rusage_ch.ru_stime.tv_usec) / 10000;
entry->hrSWRunPerfMem = proc_table[i].ki_rssize * (getpagesize()/1024); /* in kB */
#elif defined(HAVE_KVM_GETPROC2) || defined(openbsd5)
/*
* newer NetBSD, OpenBSD
*/
entry->hrSWRunPerfCPU = proc_table[i].p_rtime_sec*100;
entry->hrSWRunPerfCPU += proc_table[i].p_rtime_usec / 10000;
entry->hrSWRunPerfMem = proc_table[i].p_vm_rssize;
entry->hrSWRunPerfMem *= (getpagesize() / 1024);
#elif defined(dragonfly) && __DragonFly_version >= 190000
entry->hrSWRunPerfCPU = proc_table[i].kp_lwp.kl_uticks;
entry->hrSWRunPerfCPU += proc_table[i].kp_lwp.kl_sticks;
entry->hrSWRunPerfCPU += proc_table[i].kp_lwp.kl_iticks;
entry->hrSWRunPerfCPU = entry->hrSWRunPerfCPU / 10000;
entry->hrSWRunPerfMem = proc_table[i].kp_vm_map_size / 1024;
#elif defined(dragonfly)
entry->hrSWRunPerfCPU = proc_table[i].kp_eproc.e_uticks;
entry->hrSWRunPerfCPU += proc_table[i].kp_eproc.e_sticks;
entry->hrSWRunPerfCPU += proc_table[i].kp_eproc.e_iticks;
entry->hrSWRunPerfMem = proc_table[i].kp_vm_map_size / 1024;
#else
/*
* early FreeBSD, NetBSD, OpenBSD
*/
entry->hrSWRunPerfCPU = proc_table[i].kp_proc.p_uticks;
entry->hrSWRunPerfCPU += proc_table[i].kp_proc.p_sticks;
entry->hrSWRunPerfCPU += proc_table[i].kp_proc.p_iticks;
entry->hrSWRunPerfMem = proc_table[i].kp_eproc.e_vm.vm_rssize;
entry->hrSWRunPerfMem *= (getpagesize() / 1024);
#endif
}
/*
* 'proc_table' is owned by the kvm library,
* so shouldn't be freed here.
*/
DEBUGMSGTL(("swrun:load:arch","loaded %d entries\n",
(int)CONTAINER_SIZE(container)));
return 0;
}