config root man

Current Path : /usr/src/usr.sbin/bsnmpd/modules/snmp_hostres/

FreeBSD hs32.drive.ne.jp 9.1-RELEASE FreeBSD 9.1-RELEASE #1: Wed Jan 14 12:18:08 JST 2015 root@hs32.drive.ne.jp:/sys/amd64/compile/hs32 amd64
Upload File :
Current File : //usr/src/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swrun_tbl.c

/*
 * Copyright (c) 2005-2006 The FreeBSD Project
 * All rights reserved.
 *
 * Author: Victor Cruceru <soc-victor@freebsd.org>
 *
 * Redistribution of this software and documentation and use in source and
 * binary forms, with or without modification, are permitted provided that
 * the following conditions are met:
 *
 * 1. Redistributions of source code or documentation must retain the above
 *    copyright notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $FreeBSD: release/9.1.0/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swrun_tbl.c 160341 2006-07-14 09:07:56Z harti $
 *
 * Host Resources MIB for SNMPd. Implementation for hrSWRunTable
 */

#include <sys/param.h>
#include <sys/proc.h>
#include <sys/sysctl.h>
#include <sys/user.h>
#include <sys/linker.h>

#include <assert.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>

#include "hostres_snmp.h"
#include "hostres_oid.h"
#include "hostres_tree.h"

/*
 * Ugly thing: PID_MAX, NO_PID defined only in kernel
 */
#define	NO_PID		100000

enum SWRunType {
	SRT_UNKNOWN		= 1,
	SRT_OPERATING_SYSTEM	= 2,
	SRT_DEVICE_DRIVER	= 3,
	SRT_APPLICATION		= 4

};

enum SWRunStatus {
	SRS_RUNNING		= 1,
	SRS_RUNNABLE		= 2,
	SRS_NOT_RUNNABLE	= 3,
	SRS_INVALID		= 4
};

/* Maximum lengths for the strings according to the MIB */
#define	SWR_NAME_MLEN	(64 + 1)
#define	SWR_PATH_MLEN	(128 + 1)
#define	SWR_PARAM_MLEN	(128 + 1)

/*
 * This structure is used to hold a SNMP table entry
 * for both hrSWRunTable and hrSWRunPerfTable because
 * hrSWRunPerfTable AUGMENTS hrSWRunTable
 */
struct swrun_entry {
	int32_t		index;
	u_char		*name;		/* it may be NULL */
	const struct asn_oid *id;
	u_char		*path;		/* it may be NULL */
	u_char		*parameters;	/* it may be NULL */
	int32_t		type;		/* enum SWRunType */
	int32_t		status;		/* enum SWRunStatus */
	int32_t		perfCPU;
	int32_t		perfMemory;
#define	HR_SWRUN_FOUND 0x001
	uint32_t	flags;
	uint64_t	r_tick;		/* tick when entry refreshed */
	TAILQ_ENTRY(swrun_entry) link;
};
TAILQ_HEAD(swrun_tbl, swrun_entry);

/* the head of the list with hrSWRunTable's entries */
static struct swrun_tbl swrun_tbl = TAILQ_HEAD_INITIALIZER(swrun_tbl);

/* last (agent) tick when hrSWRunTable and hrSWRunPerTable was updated */
static uint64_t swrun_tick;

/* maximum number of ticks between updates of SWRun and SWRunPerf table */
uint32_t swrun_tbl_refresh = HR_SWRUN_TBL_REFRESH * 100;

/* the value of the MIB object with the same name */
static int32_t SWOSIndex;

/**
 * Malloc a new entry and add it to the list
 * associated to this table. The item identified by
 * the index parameter must not exist in this list.
 */
static struct swrun_entry *
swrun_entry_create(int32_t idx)
{
	struct swrun_entry *entry;

	if ((entry = malloc(sizeof(*entry))) == NULL) {
		syslog(LOG_WARNING, "%s: %m", __func__);
		return (NULL);
	}
	memset(entry, 0, sizeof(*entry));
	entry->index = idx;

	INSERT_OBJECT_INT(entry, &swrun_tbl);
	return (entry);
}

/**
 * Unlink the entry from the list and then free its heap memory
 */
static void
swrun_entry_delete(struct swrun_entry *entry)
{

	assert(entry != NULL);

	TAILQ_REMOVE(&swrun_tbl, entry, link);

	free(entry->name);
	free(entry->path);
	free(entry->parameters);
	free(entry);
}

/**
 * Search one item by its index, return NULL if none found
 */
static struct swrun_entry *
swrun_entry_find_by_index(int32_t idx)
{
	struct swrun_entry *entry;

	TAILQ_FOREACH(entry, &swrun_tbl, link)
		if (entry->index == idx)
			return (entry);
	return (NULL);
}

/**
 * Translate the kernel's process status to SNMP.
 */
static enum SWRunStatus
swrun_OS_get_proc_status(const struct kinfo_proc *kp)
{

	assert(kp != NULL);
	if(kp ==  NULL) {
		return (SRS_INVALID);
	}

	/*
	 * I'm using the old style flags - they look cleaner to me,
	 * at least for the purpose of this SNMP table
	 */
	switch (kp->ki_stat) {

	case SSTOP:
		return (SRS_NOT_RUNNABLE);

	case SWAIT:
	case SLOCK:
	case SSLEEP:
		return (SRS_RUNNABLE);

	case SZOMB:
		return (SRS_INVALID);

	case SIDL:
	case SRUN:
		return (SRS_RUNNING);

	default:
		syslog(LOG_ERR,"Unknown process state: %d", kp->ki_stat);
		return (SRS_INVALID);
	}
}

/**
 * Make an SNMP table entry from a kernel one.
 */
static void
kinfo_proc_to_swrun_entry(const struct kinfo_proc *kp,
    struct swrun_entry *entry)
{
	char **argv = NULL;
	uint64_t cpu_time = 0;
	size_t pname_len;

	pname_len = strlen(kp->ki_comm) + 1;
	entry->name = reallocf(entry->name, pname_len);
	if (entry->name != NULL)
		strlcpy(entry->name, kp->ki_comm, pname_len);

	entry->id = &oid_zeroDotZero; /* unknown id - FIXME */

	assert(hr_kd != NULL);

	argv = kvm_getargv(hr_kd, kp, SWR_PARAM_MLEN - 1);
	if(argv != NULL){
		u_char param[SWR_PARAM_MLEN];

		memset(param, '\0', sizeof(param));

		/*
		 * FIXME
		 * Path seems to not be available.
		 * Try to hack the info in argv[0];
		 * this argv is under control of the program so this info
		 * is not realiable
		 */
		if(*argv != NULL && (*argv)[0] == '/') {
			size_t path_len;

			path_len = strlen(*argv) + 1;
			if (path_len > SWR_PATH_MLEN)
				path_len = SWR_PATH_MLEN;

			entry->path = reallocf(entry->path, path_len);
			if (entry->path != NULL) {
				memset(entry->path, '\0', path_len);
				strlcpy((char*)entry->path, *argv, path_len);
			}
		}

		argv++; /* skip the first one which was used for path */

		while (argv != NULL && *argv != NULL ) {
			if (param[0] != 0)  {
				/*
				 * add a space between parameters,
				 * except before the first one
				 */
				strlcat((char *)param, " ", sizeof(param));
			}
			strlcat((char *)param, *argv, sizeof(param));
			argv++;
		}
		/* reuse pname_len */
		pname_len = strlen(param) + 1;
		if (pname_len > SWR_PARAM_MLEN)
			pname_len = SWR_PARAM_MLEN;

		entry->parameters = reallocf(entry->parameters, pname_len);
		strlcpy(entry->parameters, param, pname_len);
	}

	entry->type = (int32_t)(IS_KERNPROC(kp) ? SRT_OPERATING_SYSTEM :
	    SRT_APPLICATION);

	entry->status = (int32_t)swrun_OS_get_proc_status(kp);
	cpu_time = kp->ki_runtime / 100000; /* centi-seconds */

	/* may overflow the snmp type */
	entry->perfCPU = (cpu_time > (uint64_t)INT_MAX ? INT_MAX : cpu_time);
	entry->perfMemory = kp->ki_size / 1024; /* in kilo-bytes */
	entry->r_tick = get_ticks();
}

/**
 * Create a table entry for a KLD
 */
static void
kld_file_stat_to_swrun(const struct kld_file_stat *kfs,
    struct swrun_entry *entry)
{
	size_t name_len;

	assert(kfs != NULL);
	assert(entry != NULL);

	name_len = strlen(kfs->name) + 1;
	if (name_len > SWR_NAME_MLEN)
		name_len = SWR_NAME_MLEN;

	entry->name = reallocf(entry->name, name_len);
	if (entry->name != NULL)
		strlcpy((char *)entry->name, kfs->name, name_len);

	/* FIXME: can we find the location where the module was loaded from? */
	entry->path = NULL;

	/* no parameters for kernel files (.ko) of for the kernel */
	entry->parameters = NULL;

	entry->id = &oid_zeroDotZero; /* unknown id - FIXME */

	if (strcmp(kfs->name, "kernel") == 0) {
		entry->type = (int32_t)SRT_OPERATING_SYSTEM;
		SWOSIndex = entry->index;
	} else {
		entry->type = (int32_t)SRT_DEVICE_DRIVER; /* well, not really */
	}
	entry->status = (int32_t)SRS_RUNNING;
	entry->perfCPU = 0;			/* Info not available */
	entry->perfMemory = kfs->size / 1024;	/* in kilo-bytes */
	entry->r_tick = get_ticks();
}

/**
 * Get all visible proceses including the kernel visible threads
 */
static void
swrun_OS_get_procs(void)
{
	struct kinfo_proc *plist, *kp;
	int i;
	int nproc;
	struct swrun_entry *entry;

	plist = kvm_getprocs(hr_kd, KERN_PROC_ALL, 0, &nproc);
	if (plist == NULL || nproc < 0) {
		syslog(LOG_ERR, "kvm_getprocs() failed: %m");
		return;
	}
	for (i = 0, kp = plist; i < nproc; i++, kp++) {
		/*
		 * The SNMP table's index must begin from 1 (as specified by
		 * this table definition), the PIDs are starting from 0
		 * so we are translating the PIDs to +1
		 */
		entry = swrun_entry_find_by_index((int32_t)kp->ki_pid + 1);
		if (entry == NULL) {
			/* new entry - get memory for it */
			entry = swrun_entry_create((int32_t)kp->ki_pid + 1);
			if (entry == NULL)
				continue;
		}
		entry->flags |= HR_SWRUN_FOUND;	/* mark it as found */

		kinfo_proc_to_swrun_entry(kp, entry);
	}
}

/*
 * Get kernel items: first the kernel itself, then the loaded modules.
 */
static void
swrun_OS_get_kinfo(void)
{
	int fileid;
	struct swrun_entry *entry;
	struct kld_file_stat stat;

	for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) {
		stat.version = sizeof(struct kld_file_stat);
		if (kldstat(fileid, &stat) < 0) {
			syslog(LOG_ERR, "kldstat() failed: %m");
			continue;
		}

		/*
		 * kernel and kernel files (*.ko) will be indexed starting with
		 * NO_PID + 1; NO_PID is PID_MAX + 1 thus it will be no risk to
		 * overlap with real PIDs which are in range of 1 .. NO_PID
		 */
		entry = swrun_entry_find_by_index(NO_PID + 1 + stat.id);
		if (entry == NULL) {
			/* new entry - get memory for it */
			entry = swrun_entry_create(NO_PID + 1 + stat.id);
			if (entry == NULL)
				continue;
		}
		entry->flags |= HR_SWRUN_FOUND; /* mark it as found */

		kld_file_stat_to_swrun(&stat, entry);
	}
}

/**
 * Refresh the hrSWRun and hrSWRunPert tables.
 */
static void
refresh_swrun_tbl(void)
{

	struct swrun_entry *entry, *entry_tmp;

	if (this_tick - swrun_tick < swrun_tbl_refresh) {
		HRDBG("no refresh needed ");
		return;
	}

	/* mark each entry as missing */
	TAILQ_FOREACH(entry, &swrun_tbl, link)
		entry->flags &= ~HR_SWRUN_FOUND;

	swrun_OS_get_procs();
	swrun_OS_get_kinfo();

	/*
	 * Purge items that disappeared
	 */
	TAILQ_FOREACH_SAFE(entry, &swrun_tbl, link, entry_tmp)
		if (!(entry->flags & HR_SWRUN_FOUND))
			swrun_entry_delete(entry);

	swrun_tick = this_tick;

	HRDBG("refresh DONE");
}

/**
 * Update the information in this entry
 */
static void
fetch_swrun_entry(struct swrun_entry *entry)
{
	struct kinfo_proc *plist;
	int nproc;
	struct kld_file_stat stat;

	assert(entry !=  NULL);

	if (entry->index >= NO_PID + 1)	{
		/*
		 * kernel and kernel files (*.ko) will be indexed
		 * starting with NO_PID + 1; NO_PID is PID_MAX + 1
		 * thus it will be no risk to overlap with real PIDs
		 * which are in range of 1 .. NO_PID
		 */
		stat.version = sizeof(stat);
		if (kldstat(entry->index - NO_PID - 1, &stat) == -1) {
			/*
			 * not found, it's gone. Mark it as invalid for now, it
			 * will be removed from the list at next global refersh
			 */
			 HRDBG("missing item with kid=%d",
			     entry->index -  NO_PID - 1);
			entry->status = (int32_t)SRS_INVALID;
		} else
			kld_file_stat_to_swrun(&stat, entry);

	} else {
		/* this is a process */
		assert(hr_kd != NULL);
		plist = kvm_getprocs(hr_kd, KERN_PROC_PID,
		    entry->index - 1, &nproc);
		if (plist == NULL || nproc != 1) {
			HRDBG("missing item with PID=%d", entry->index - 1);
			entry->status = (int32_t)SRS_INVALID;
		} else
			kinfo_proc_to_swrun_entry(plist, entry);
	}
}

/**
 * Invalidate entry. For KLDs we try to unload it, for processes we SIGKILL it.
 */
static int
invalidate_swrun_entry(struct swrun_entry *entry, int commit)
{
	struct kinfo_proc *plist;
	int nproc;
	struct kld_file_stat stat;

	assert(entry !=  NULL);

	if (entry->index >= NO_PID + 1)	{
		/* this is a kernel item */
		HRDBG("atempt to unload KLD %d",
		    entry->index -  NO_PID - 1);

		if (entry->index == SWOSIndex) {
			/* can't invalidate the kernel itself */
			return (SNMP_ERR_NOT_WRITEABLE);
		}

		stat.version = sizeof(stat);
		if (kldstat(entry->index - NO_PID - 1, &stat) == -1) {
			/*
			 * not found, it's gone. Mark it as invalid for now, it
			 * will be removed from the list at next global
			 * refresh
			 */
			HRDBG("missing item with kid=%d",
			    entry->index - NO_PID - 1);
			entry->status = (int32_t)SRS_INVALID;
			return (SNMP_ERR_NOERROR);
		}
		/*
		 * There is no way to try to unload a module. There seems
		 * also no way to find out whether it is busy without unloading
		 * it. We can assume that it is busy, if the reference count
		 * is larger than 2, but if it is 1 nothing helps.
		 */
		if (!commit) {
			if (stat.refs > 1)
				return (SNMP_ERR_NOT_WRITEABLE);
			return (SNMP_ERR_NOERROR);
		}
		if (kldunload(stat.id) == -1) {
			syslog(LOG_ERR,"kldunload for %d/%s failed: %m",
			    stat.id, stat.name);
			if (errno == EBUSY)
				return (SNMP_ERR_NOT_WRITEABLE);
			else
				return (SNMP_ERR_RES_UNAVAIL);
		}
	} else {
		/* this is a process */
		assert(hr_kd != NULL);

		plist = kvm_getprocs(hr_kd, KERN_PROC_PID,
		    entry->index - 1, &nproc);
		if (plist == NULL || nproc != 1) {
			HRDBG("missing item with PID=%d", entry->index - 1);
			entry->status = (int32_t)SRS_INVALID;
			return (SNMP_ERR_NOERROR);
		}
		if (IS_KERNPROC(plist)) {
			/* you don't want to do this */
			return (SNMP_ERR_NOT_WRITEABLE);
		}
		if (kill(entry->index - 1, commit ? SIGKILL : 0) < 0) {
			syslog(LOG_ERR,"kill (%d, SIGKILL) failed: %m",
			    entry->index - 1);
			if (errno == ESRCH) {
				/* race: just gone */
				entry->status = (int32_t)SRS_INVALID;
				return (SNMP_ERR_NOERROR);
			}
			return (SNMP_ERR_GENERR);
		}
	}
	return (SNMP_ERR_NOERROR);
}

/**
 * Popuplate the hrSWRunTable.
 */
void
init_swrun_tbl(void)
{

	refresh_swrun_tbl();
	HRDBG("done");
}

/**
 * Finalize the hrSWRunTable.
 */
void
fini_swrun_tbl(void)
{
	struct swrun_entry *n1;

	while ((n1 = TAILQ_FIRST(&swrun_tbl)) != NULL) {
		TAILQ_REMOVE(&swrun_tbl, n1, link);
		free(n1);
	}
}

/*
 * This is the implementation for a generated (by a SNMP tool)
 * function prototype, see hostres_tree.h
 * It hanldes the SNMP operations for hrSWRunTable
 */
int
op_hrSWRunTable(struct snmp_context *ctx __unused, struct snmp_value *value,
    u_int sub, u_int iidx __unused, enum snmp_op curr_op)
{
	struct swrun_entry *entry;
	int ret;

	refresh_swrun_tbl();

	switch (curr_op) {

	  case SNMP_OP_GETNEXT:
		if ((entry = NEXT_OBJECT_INT(&swrun_tbl,
		    &value->var, sub)) == NULL)
			return (SNMP_ERR_NOSUCHNAME);
		value->var.len = sub + 1;
		value->var.subs[sub] = entry->index;
		goto get;

	  case SNMP_OP_GET:
		if ((entry = FIND_OBJECT_INT(&swrun_tbl,
		    &value->var, sub)) == NULL)
			return (SNMP_ERR_NOSUCHNAME);
		goto get;

	  case SNMP_OP_SET:
		if ((entry = FIND_OBJECT_INT(&swrun_tbl,
		    &value->var, sub)) == NULL)
			return (SNMP_ERR_NO_CREATION);

		if (entry->r_tick < this_tick)
			fetch_swrun_entry(entry);

		switch (value->var.subs[sub - 1]) {

		case LEAF_hrSWRunStatus:
			if (value->v.integer != (int32_t)SRS_INVALID)
				return (SNMP_ERR_WRONG_VALUE);

			if (entry->status == (int32_t)SRS_INVALID)
				return (SNMP_ERR_NOERROR);

			/*
			 * Here we have a problem with the entire SNMP
			 * model: if we kill now, we cannot rollback.
			 * If we kill in the commit code, we cannot
			 * return an error. Because things may change between
			 * SET and COMMIT this is impossible to handle
			 * correctly.
			 */
			return (invalidate_swrun_entry(entry, 0));
		}
		return (SNMP_ERR_NOT_WRITEABLE);

	  case SNMP_OP_ROLLBACK:
		return (SNMP_ERR_NOERROR);

	  case SNMP_OP_COMMIT:
		if ((entry = FIND_OBJECT_INT(&swrun_tbl,
		    &value->var, sub)) == NULL)
			return (SNMP_ERR_NOERROR);

		switch (value->var.subs[sub - 1]) {

		case LEAF_hrSWRunStatus:
			if (value->v.integer == (int32_t)SRS_INVALID &&
			    entry->status != (int32_t)SRS_INVALID)
				(void)invalidate_swrun_entry(entry, 1);
			return (SNMP_ERR_NOERROR);
		}
		abort();
	}
	abort();

  get:
	ret = SNMP_ERR_NOERROR;
	switch (value->var.subs[sub - 1]) {

	  case LEAF_hrSWRunIndex:
		value->v.integer = entry->index;
		break;

	  case LEAF_hrSWRunName:
		if (entry->name != NULL)
			ret = string_get(value, entry->name, -1);
		else
			ret = string_get(value, "", -1);
		break;

	  case LEAF_hrSWRunID:
		assert(entry->id != NULL);
		value->v.oid = *entry->id;
		break;

	  case LEAF_hrSWRunPath:
		if (entry->path != NULL)
			ret = string_get(value, entry->path, -1);
		else
			ret = string_get(value, "", -1);
		break;

	  case LEAF_hrSWRunParameters:
		if (entry->parameters != NULL)
			ret = string_get(value, entry->parameters, -1);
		else
			ret = string_get(value, "", -1);
		break;

	  case LEAF_hrSWRunType:
		value->v.integer = entry->type;
		break;

	  case LEAF_hrSWRunStatus:
		value->v.integer = entry->status;
		break;

	  default:
		abort();
	}
	return (ret);
}

/**
 * Scalar(s) in the SWRun group
 */
int
op_hrSWRun(struct snmp_context *ctx __unused, struct snmp_value *value,
    u_int sub, u_int iidx __unused, enum snmp_op curr_op)
{

	/* only SNMP GET is possible */
	switch (curr_op) {

	case SNMP_OP_GET:
		goto get;

	case SNMP_OP_SET:
		return (SNMP_ERR_NOT_WRITEABLE);

	case SNMP_OP_ROLLBACK:
	case SNMP_OP_COMMIT:
	case SNMP_OP_GETNEXT:
		abort();
	}
	abort();

  get:
	switch (value->var.subs[sub - 1]) {

	case LEAF_hrSWOSIndex:
		value->v.uint32 = SWOSIndex;
		return (SNMP_ERR_NOERROR);

	default:
		abort();
	}
}

/*
 * This is the implementation for a generated (by a SNMP tool)
 * function prototype, see hostres_tree.h
 * It handles the SNMP operations for hrSWRunPerfTable
 */
int
op_hrSWRunPerfTable(struct snmp_context *ctx __unused,
    struct snmp_value *value, u_int sub, u_int iidx __unused,
    enum snmp_op curr_op )
{
	struct swrun_entry *entry;

	refresh_swrun_tbl();

	switch (curr_op) {

	  case SNMP_OP_GETNEXT:
		if ((entry = NEXT_OBJECT_INT(&swrun_tbl,
		    &value->var, sub)) == NULL)
			return (SNMP_ERR_NOSUCHNAME);
		value->var.len = sub + 1;
		value->var.subs[sub] = entry->index;
		goto get;

	  case SNMP_OP_GET:
		if ((entry = FIND_OBJECT_INT(&swrun_tbl,
		    &value->var, sub)) == NULL)
			return (SNMP_ERR_NOSUCHNAME);
		goto get;

	  case SNMP_OP_SET:
		if ((entry = FIND_OBJECT_INT(&swrun_tbl,
		    &value->var, sub)) == NULL)
			return (SNMP_ERR_NO_CREATION);
		return (SNMP_ERR_NOT_WRITEABLE);

	  case SNMP_OP_ROLLBACK:
	  case SNMP_OP_COMMIT:
		abort();
	}
	abort();

  get:
	switch (value->var.subs[sub - 1]) {

	  case LEAF_hrSWRunPerfCPU:
		value->v.integer = entry->perfCPU;
		return (SNMP_ERR_NOERROR);

	  case LEAF_hrSWRunPerfMem:
		value->v.integer = entry->perfMemory;
		return (SNMP_ERR_NOERROR);
	}
	abort();
}

Man Man