view usr/src/cmd/picl/plugins/sun4v/snmp/snmpplugin.c @ 3941:328be6a20f20

FWARC/2007/133 SNMP Domain Service FWARC/2007/138 Updates to PRI structures 6438074 customer requests ability to query power/fan status info from OS 6526169 prtdiag output doesn't have Memory Configuration Information 6531453 sun4v picl needs device labels in the devtree 6534449 Unable to send a domain services message larger than 4K
author venki
date Sat, 31 Mar 2007 18:24:05 -0700
parents
children d8366a443311
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * The SNMP picl plugin connects to the agent on the SP and creates
 * and populates the /physical-platform subtree in picl tree for use
 * by picl consumers.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <stdarg.h>
#include <libgen.h>
#include <libintl.h>
#include <thread.h>
#include <synch.h>
#include <errno.h>

#include <picldefs.h>
#include <picl.h>
#include <picltree.h>

#include "picloids.h"
#include "libpiclsnmp.h"
#include "snmpplugin.h"

#pragma init(snmpplugin_register)	/* place in .init section */

picld_plugin_reg_t snmpplugin_reg = {
	PICLD_PLUGIN_VERSION_1,
	PICLD_PLUGIN_NON_CRITICAL,
	"snmp_plugin",
	snmpplugin_init,
	snmpplugin_fini
};

static picl_snmphdl_t	hdl;

/*
 * The stale_tree_rwlp protects the stale_xxx vars. The 'stale_tree' flag
 * and the 'rebuild_tree' flag below are both initialized to B_TRUE to
 * let the tree_builder() thread build the initial tree without blocking.
 */
static rwlock_t		stale_tree_rwlp;
static boolean_t	stale_tree = B_TRUE;

/*
 * vol_props, volprop_ndx and n_vol_props are protected by the stale_tree
 * flag.  They are read only when the stale_tree flag is B_FALSE and written
 * to only when the flag is B_TRUE.
 *
 * The change_time (last changed time) is read by only one thread at a
 * time when stale_tree is B_FALSE (protected by stale_tree_rwlp).  It is
 * written by only one thread (the tree builder) when stale_tree is B_TRUE.
 *
 * Note that strictly speaking, change_time should be uint_t (timeticks32).
 * But keeping it as int is fine, since we don't do any arithmetic on it
 * except equality check.
 */
static vol_prophdl_t	*vol_props = NULL;
static int		volprop_ndx = 0, n_vol_props = 0;
static int		change_time = 0;

/*
 * The rebuild_tree_lock and cv are used by the tree builder thread.
 * rebuild_tree has to be initialized to B_TRUE to let the tree_builder
 * do the first build without blocking.
 */
static mutex_t		rebuild_tree_lock;
static cond_t		rebuild_tree_cv;
static boolean_t	rebuild_tree = B_TRUE;

/*
 * These two should really not be global
 */
static picl_nodehdl_t	*physplat_nodes = NULL;
static int		n_physplat_nodes = 0;

static char *group1[] = {
	OID_entPhysicalDescr,
	OID_entPhysicalContainedIn,
	OID_entPhysicalClass,
	OID_entPhysicalName,
	OID_entPhysicalHardwareRev,
	OID_entPhysicalFirmwareRev,
	OID_entPhysicalSerialNum,
	OID_entPhysicalMfgName,
	OID_entPhysicalModelName,
	OID_entPhysicalIsFRU,
	0
};

static char *group2[] = {
	OID_sunPlatEquipmentHolderAcceptableTypes,
	OID_sunPlatCircuitPackReplaceable,
	OID_sunPlatCircuitPackHotSwappable,
	OID_sunPlatPhysicalClass,
	OID_sunPlatSensorClass,
	OID_sunPlatSensorType,
	OID_sunPlatAlarmType,
	OID_sunPlatPowerSupplyClass,
	0
};

static char *volgroup1[] = {
	OID_sunPlatBinarySensorCurrent,
	OID_sunPlatBinarySensorExpected,
	OID_sunPlatBinarySensorInterpretTrue,
	OID_sunPlatBinarySensorInterpretFalse,
	0
};

static char *volgroup2[] = {
	OID_sunPlatNumericSensorBaseUnits,
	OID_sunPlatNumericSensorExponent,
	OID_sunPlatNumericSensorRateUnits,
	OID_sunPlatNumericSensorCurrent,
	OID_sunPlatNumericSensorLowerThresholdFatal,
	OID_sunPlatNumericSensorLowerThresholdCritical,
	OID_sunPlatNumericSensorLowerThresholdNonCritical,
	OID_sunPlatNumericSensorUpperThresholdNonCritical,
	OID_sunPlatNumericSensorUpperThresholdCritical,
	OID_sunPlatNumericSensorUpperThresholdFatal,
	0
};

/*
 * The following two items must match the Sun Platform MIB specification
 * in their indices and values.
 */
static char *sensor_baseunits[] = {
	"", "other", "unknown", "degC", "degF", "degK", "volts", "amps",
	"watts", "joules", "coulombs", "va", "nits", "lumens", "lux",
	"candelas", "kPa", "psi", "newtons", "cfm", "rpm", "hertz",
	"seconds", "minutes", "hours", "days", "weeks", "mils", "inches",
	"feet", "cubicInches", "cubicFeet", "meters", "cubicCentimeters",
	"cubicMeters", "liters", "fluidOunces", "radians", "steradians",
	"revolutions", "cycles", "gravities", "ounces", "pounds", "footPounds",
	"ounceInches", "gauss", "gilberts", "henries", "farads", "ohms",
	"siemens", "moles", "becquerels", "ppm", "decibels", "dBA", "dbC",
	"grays", "sieverts", "colorTemperatureDegK", "bits", "bytes", "words",
	"doubleWords", "quadWords", "percentage"
};
static const int n_baseunits = sizeof (sensor_baseunits) / sizeof (char *);

static char *sensor_rateunits[] = {
	"",
	"none",
	"perMicroSecond",
	"perMilliSecond",
	"perSecond",
	"perMinute",
	"perHour",
	"perDay",
	"perWeek",
	"perMonth",
	"perYear"
};
static const int n_rateunits = sizeof (sensor_rateunits) / sizeof (char *);

/*
 * Local declarations
 */
static void snmpplugin_register(void);
static void register_group(char **g, int is_volatile);
static void *tree_builder(void *arg);
static int build_physplat(picl_nodehdl_t *subtree_rootp);
static void free_resources(picl_nodehdl_t subtree_root);

static picl_nodehdl_t make_node(picl_nodehdl_t subtree_root, int row,
    int *snmp_syserr_p);
static void save_nodeh(picl_nodehdl_t nodeh, int row);
static picl_nodehdl_t lookup_nodeh(int row);

static void save_volprop(picl_prophdl_t prop, char *oidstr, int row,
    int proptype);
static void check_for_stale_data(void);
static int read_volprop(ptree_rarg_t *parg, void *buf);

static void threshold(picl_nodehdl_t node, char *oidstr, int row,
    char *propname, int *snmp_syserr_p);
static void add_thresholds(picl_nodehdl_t node, int row, int *snmp_syserr_p);

static char *get_slot_type(int row, int *snmp_syserr_p);
static int add_volatile_prop(picl_nodehdl_t nodeh, char *name,
    int type, int access, int size, int (*rdfunc)(ptree_rarg_t *, void *),
    int (*wrfunc)(ptree_warg_t *, const void *), picl_prophdl_t *propp);
static int add_string_prop(picl_nodehdl_t node, char *propname, char *propval);
static int add_void_prop(picl_nodehdl_t node, char *propname);
static int add_int_prop(picl_nodehdl_t node, char *propname, int val);
static void add_prop(picl_nodehdl_t nodeh, picl_prophdl_t *php, char *label,
    int row, sp_propid_t pp, int *snmp_syserr_p);

static void log_msg(int pri, const char *fmt, ...);

#ifdef SNMPPLUGIN_DEBUG
static mutex_t	snmpplugin_dbuf_lock;
static char	*snmpplugin_dbuf = NULL;
static char	*snmpplugin_dbuf_curp = NULL;
static int	snmpplugin_dbuf_sz = 0;
static int	snmpplugin_dbuf_overflow = 0;
static char	snmpplugin_lbuf[SNMPPLUGIN_DMAX_LINE];

static void	snmpplugin_log_init(void);
static void	snmpplugin_log(const char *fmt, ...);
static void	snmpplugin_log_append(void);
static void	snmpplugin_dbuf_realloc(void);
#endif

static void
snmpplugin_register(void)
{
	(void) picld_plugin_register(&snmpplugin_reg);
}

static void
register_group(char **g, int is_volatile)
{
	int	i, len = 0;
	int	n_oids;
	char	*p, *oidstrs;

	for (i = 0; g[i]; i++)
		len += strlen(g[i]) + 1;
	n_oids = i;

	if ((oidstrs = (char *)calloc(1, len)) == NULL)
		return;

	for (p = oidstrs, i = 0; g[i]; i++) {
		(void) strcpy(p, g[i]);
		p += strlen(g[i]) + 1;
	}

	snmp_register_group(hdl, oidstrs, n_oids, is_volatile);
}

void
snmpplugin_init(void)
{
	int		ret;

	(void) mutex_init(&rebuild_tree_lock, USYNC_THREAD, NULL);
	(void) cond_init(&rebuild_tree_cv, USYNC_THREAD, NULL);
	(void) rwlock_init(&stale_tree_rwlp, USYNC_THREAD, NULL);
	LOGINIT();

	/*
	 * Create the tree-builder thread and let it take over
	 */
	LOGPRINTF("Tree-builder thread being created.\n");
	if ((ret = thr_create(NULL, NULL, tree_builder, NULL,
	    THR_BOUND, NULL)) < 0) {
		log_msg(LOG_ERR, SNMPP_CANT_CREATE_TREE_BUILDER, ret);
		snmp_fini(hdl);
		return;
	}
}

void
snmpplugin_fini(void)
{
	snmp_fini(hdl);

	(void) rwlock_destroy(&stale_tree_rwlp);
	(void) cond_destroy(&rebuild_tree_cv);
	(void) mutex_destroy(&rebuild_tree_lock);
}

/*ARGSUSED*/
static void *
tree_builder(void *arg)
{
	int		ret, rv;
	picl_nodehdl_t	root_node;
	picl_nodehdl_t	physplat_root;
	picl_nodehdl_t	old_physplat_root;

	/*
	 * Initialize SNMP service
	 */
	LOGPRINTF("Initializing SNMP service.\n");
	if ((hdl = snmp_init()) == NULL) {
		log_msg(LOG_ERR, SNMPP_CANT_INIT);
		return ((void *)-1);
	}

	/*
	 * Register OID groupings for BULKGET optimizations
	 */
	LOGPRINTF("Registering OID groups.\n");
	register_group(group1, 0);
	register_group(group2, 0);
	register_group(volgroup1, 1);
	register_group(volgroup2, 1);

	(void) mutex_lock(&rebuild_tree_lock);

	for (;;) {
		LOGPRINTF("tree_builder: check whether to rebuild subtree\n");
		while (rebuild_tree == B_FALSE)
			(void) cond_wait(&rebuild_tree_cv, &rebuild_tree_lock);

		LOGPRINTF("tree_builder: woke up\n");

		old_physplat_root = NULL;
		physplat_root = NULL;

		LOGPRINTF("tree_builder: getting root node\n");
		if ((ret = ptree_get_root(&root_node)) != PICL_SUCCESS) {
			log_msg(LOG_ERR, SNMPP_NO_ROOT, ret);
			return ((void *)-2);
		}

		LOGPRINTF("tree_builder: getting existing physplat node\n");
		rv = ptree_find_node(root_node, PICL_PROP_NAME,
		    PICL_PTYPE_CHARSTRING, PICL_NODE_PHYSPLAT,
		    sizeof (PICL_NODE_PHYSPLAT), &old_physplat_root);

		LOGPRINTF("tree_builder: building physical-platform\n");
		if ((ret = build_physplat(&physplat_root)) < 0) {
			log_msg(LOG_ERR, SNMPP_CANT_CREATE_PHYSPLAT, ret);
			snmp_fini(hdl);
			return ((void *)-3);
		}

		if (rv == PICL_SUCCESS && old_physplat_root != NULL) {
			LOGPRINTF("tree_builder: destroying existing nodes\n");
			ptree_delete_node(old_physplat_root);
			ptree_destroy_node(old_physplat_root);
		}

		LOGPRINTF("tree_builder: attaching new subtree\n");
		if ((ret = ptree_add_node(root_node, physplat_root)) < 0) {
			free_resources(physplat_root);
			log_msg(LOG_ERR, SNMPP_CANT_CREATE_PHYSPLAT, ret);
			snmp_fini(hdl);
			return ((void *)-4);
		}

		LOGPRINTF("tree_builder: setting stale_tree to FALSE\n");
		(void) rw_wrlock(&stale_tree_rwlp);
		stale_tree = B_FALSE;
		(void) rw_unlock(&stale_tree_rwlp);

		LOGPRINTF("tree_builder: setting rebuild_tree to FALSE\n");
		rebuild_tree = B_FALSE;
	}

	/*NOTREACHED*/
	return (NULL);
}

static int
build_physplat(picl_nodehdl_t *subtree_rootp)
{
	int	change_time1;
	int	row, nxtrow;
	int	clr_linkreset = 0;
	int	ret = 0;
	int	snmp_syserr = 0;

retry:
	(void) snmp_reinit(hdl, clr_linkreset);
	clr_linkreset = 0;

	/*
	 * Record LastChangeTime before we start building the tree
	 */
	ret = snmp_get_int(hdl, OID_entLastChangeTime, 0,
	    &change_time1, &snmp_syserr);
	if (ret < 0) {
		if (snmp_syserr == ECANCELED) {
			log_msg(LOG_WARNING, SNMPP_LINK_RESET);
			clr_linkreset = 1;
			goto retry;
		} else
			log_msg(LOG_WARNING, SNMPP_CANT_FETCH_OBJECT_VAL, ret);
	}

	/*
	 * Create the physical-platform node
	 */
	ret = ptree_create_node(PICL_NODE_PHYSPLAT, PICL_CLASS_PICL,
	    subtree_rootp);
	if (ret != PICL_SUCCESS)
		return (-1);

	/*
	 * Scan entPhysicalTable and build the "physical-platform" subtree
	 */
	ret = 0;
	for (row = -1; ret == 0; row = nxtrow) {
		ret = snmp_get_nextrow(hdl, OID_entPhysicalDescr,
		    row, &nxtrow, &snmp_syserr);
		if (ret == 0) {
			(void) make_node(*subtree_rootp, nxtrow, &snmp_syserr);
		}

		if (snmp_syserr == ECANCELED) {
			/*
			 * If we get this error, a link reset must've
			 * happened and we need to throw away everything
			 * we have now and rebuild the tree again.
			 */
			log_msg(LOG_WARNING, SNMPP_LINK_RESET);
			free_resources(*subtree_rootp);
			clr_linkreset = 1;
			goto retry;
		}
	}

	/*
	 * Record LastChangeTime after we're done building the tree
	 */
	ret = snmp_get_int(hdl, OID_entLastChangeTime, 0,
	    &change_time, &snmp_syserr);
	if (ret < 0) {
		if (snmp_syserr == ECANCELED) {
			log_msg(LOG_WARNING, SNMPP_LINK_RESET);
			free_resources(*subtree_rootp);
			clr_linkreset = 1;
			goto retry;
		} else
			log_msg(LOG_WARNING, SNMPP_CANT_FETCH_OBJECT_VAL, ret);
	}

	/*
	 * If they don't match, some hotplugging must've happened,
	 * free resources we've created and still holding, then go
	 * back and retry
	 */
	if (change_time != change_time1) {
		LOGPRINTF("build_physplat: entLastChangeTime has changed!\n");
		free_resources(*subtree_rootp);
		change_time1 = change_time;
		goto retry;
	}

	/*
	 * The physplat_nodes table is no longer needed, free it
	 */
	if (physplat_nodes) {
		free(physplat_nodes);
		physplat_nodes = NULL;
		n_physplat_nodes = 0;
	}

	return (0);
}

/*
 * Destroy all resources that were created during the building
 * of the subtree
 */
static void
free_resources(picl_nodehdl_t subtree_root)
{
	if (physplat_nodes) {
		free(physplat_nodes);
		physplat_nodes = NULL;
		n_physplat_nodes = 0;
	}

	if (subtree_root) {
		(void) ptree_delete_node(subtree_root);
		(void) ptree_destroy_node(subtree_root);
	}

	if (vol_props) {
		free(vol_props);
		n_vol_props = 0;
		volprop_ndx = 0;
	}
}

static picl_nodehdl_t
make_node(picl_nodehdl_t subtree_root, int row, int *snmp_syserr_p)
{
	picl_nodehdl_t	nodeh, parenth;
	picl_prophdl_t	proph;
	char	*phys_name, *node_name;
	int	parent_row;
	int	ent_physclass, sunplat_physclass;
	int	sensor_class, sensor_type;
	int	alarm_type;
	int	ps_class;
	int	ret;

	/*
	 * If we've already created this picl node, just return it
	 */
	if ((nodeh = lookup_nodeh(row)) != NULL)
		return (nodeh);

	/*
	 * If we are creating it only now, make sure we have the parent
	 * created first; if there's no parent, then parent it to the
	 * subtree's root node
	 */
	ret = snmp_get_int(hdl, OID_entPhysicalContainedIn, row,
	    &parent_row, snmp_syserr_p);
	CHECK_LINKRESET(snmp_syserr_p, NULL)
	if (ret < 0 || parent_row <= 0)
		parenth = subtree_root;
	else {
		parenth = make_node(subtree_root, parent_row, snmp_syserr_p);
		CHECK_LINKRESET(snmp_syserr_p, NULL)
		if (parenth == NULL)
			parenth = subtree_root;
	}

	/*
	 * Figure out the physical-platform node name from entPhysicalName;
	 * all rows in the MIB that have a valid entPhysicalIndex should
	 * have a physical name.
	 */
	ret = snmp_get_str(hdl, OID_entPhysicalName, row,
	    &phys_name, snmp_syserr_p);
	CHECK_LINKRESET(snmp_syserr_p, NULL)
	if (ret < 0 || phys_name == NULL) {
		log_msg(LOG_WARNING, SNMPP_NO_ENTPHYSNAME, row);
		return (NULL);
	}

	node_name = basename(phys_name);

	ret = snmp_get_int(hdl, OID_entPhysicalClass, row,
	    &ent_physclass, snmp_syserr_p);
	CHECK_LINKRESET(snmp_syserr_p, NULL)
	if (ret < 0) {
		log_msg(LOG_WARNING, SNMPP_CANT_FETCH_OBJECT_VAL, ret);
		free(phys_name);
		return (NULL);
	}

	switch (ent_physclass) {
	case SPC_OTHER:
		ret = snmp_get_int(hdl, OID_sunPlatPhysicalClass, row,
		    &sunplat_physclass, snmp_syserr_p);
		CHECK_LINKRESET(snmp_syserr_p, NULL)
		if (ret < 0) {
			log_msg(LOG_WARNING, SNMPP_CANT_FETCH_OBJECT_VAL, ret);
			free(phys_name);
			return (NULL);
		}

		if (sunplat_physclass == SSPC_ALARM) {
			ret = snmp_get_int(hdl, OID_sunPlatAlarmType,
			    row, &alarm_type, snmp_syserr_p);
			CHECK_LINKRESET(snmp_syserr_p, NULL)
			if (ret < 0) {
				log_msg(LOG_WARNING,
				    SNMPP_CANT_FETCH_OBJECT_VAL, ret);
				free(phys_name);
				return (NULL);
			}

			if (alarm_type == SSAT_VISIBLE) {
			    ADD_NODE(PICL_CLASS_LED)
			} else {
			    ADD_NODE(PICL_CLASS_ALARM)
			}

			add_prop(nodeh, &proph, node_name, row, PP_STATE,
			    snmp_syserr_p);
			CHECK_LINKRESET(snmp_syserr_p, NULL)
		} else {
			ADD_NODE(PICL_CLASS_OTHER)
		}

		add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
		    snmp_syserr_p);
		CHECK_LINKRESET(snmp_syserr_p, NULL)
		break;

	case SPC_UNKNOWN:
		ADD_NODE(PICL_CLASS_UNKNOWN)
		break;

	case SPC_CHASSIS:
		ADD_NODE(PICL_CLASS_CHASSIS)
		add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
		    snmp_syserr_p);
		CHECK_LINKRESET(snmp_syserr_p, NULL)
		break;

	case SPC_BACKPLANE:
		ADD_NODE(PICL_CLASS_BACKPLANE)
		add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
		    snmp_syserr_p);
		CHECK_LINKRESET(snmp_syserr_p, NULL)
		break;

	case SPC_CONTAINER:
		ADD_NODE(PICL_CLASS_CONTAINER)

		add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
		    snmp_syserr_p);
		CHECK_LINKRESET(snmp_syserr_p, NULL)

		add_prop(nodeh, &proph, node_name, row, PP_SLOT_TYPE,
		    snmp_syserr_p);
		CHECK_LINKRESET(snmp_syserr_p, NULL)
		break;

	case SPC_POWERSUPPLY:
		ret = snmp_get_int(hdl, OID_sunPlatPowerSupplyClass,
		    row, &ps_class, snmp_syserr_p);
		CHECK_LINKRESET(snmp_syserr_p, NULL)
		if (ret < 0) {
			log_msg(LOG_WARNING, SNMPP_CANT_FETCH_OBJECT_VAL, ret);
			free(phys_name);
			return (NULL);
		}

		if (ps_class == SSPSC_BATTERY) {
			ADD_NODE(PICL_CLASS_BATTERY)
			add_prop(nodeh, &proph, node_name, row,
			    PP_BATT_STATUS, snmp_syserr_p);
			CHECK_LINKRESET(snmp_syserr_p, NULL)
		} else {
			ADD_NODE(PICL_CLASS_POWERSUPPLY)
		}
		add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
		    snmp_syserr_p);
		CHECK_LINKRESET(snmp_syserr_p, NULL)
		break;

	case SPC_FAN:
		ADD_NODE(PICL_CLASS_FAN)
		add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
		    snmp_syserr_p);
		CHECK_LINKRESET(snmp_syserr_p, NULL)
		break;

	case SPC_SENSOR:
		ret = snmp_get_int(hdl, OID_sunPlatSensorClass,
		    row, &sensor_class, snmp_syserr_p);
		CHECK_LINKRESET(snmp_syserr_p, NULL)
		if (ret < 0) {
			log_msg(LOG_WARNING, SNMPP_CANT_FETCH_OBJECT_VAL, ret);
			free(phys_name);
			return (NULL);
		}

		ret = snmp_get_int(hdl, OID_sunPlatSensorType,
		    row, &sensor_type, snmp_syserr_p);
		CHECK_LINKRESET(snmp_syserr_p, NULL)
		if (ret < 0) {
			log_msg(LOG_WARNING, SNMPP_CANT_FETCH_OBJECT_VAL, ret);
			free(phys_name);
			return (NULL);
		}

		if (sensor_class == SSSC_NUMERIC) {
			if (sensor_type == SSST_TEMPERATURE) {
				ADD_NODE(PICL_CLASS_TEMPERATURE_SENSOR)
				add_prop(nodeh, &proph, node_name, row,
				    PP_TEMPERATURE, snmp_syserr_p);
			} else if (sensor_type == SSST_VOLTAGE) {
				ADD_NODE(PICL_CLASS_VOLTAGE_SENSOR)
				add_prop(nodeh, &proph, node_name, row,
				    PP_VOLTAGE, snmp_syserr_p);
			} else if (sensor_type == SSST_CURRENT) {
				ADD_NODE(PICL_CLASS_CURRENT_SENSOR)
				add_prop(nodeh, &proph, node_name, row,
				    PP_CURRENT, snmp_syserr_p);
			} else if (sensor_type == SSST_TACHOMETER) {
				ADD_NODE(PICL_CLASS_RPM_SENSOR)
				add_prop(nodeh, &proph, node_name, row,
				    PP_SPEED, snmp_syserr_p);
			} else {
				ADD_NODE(PICL_CLASS_SENSOR)
				add_prop(nodeh, &proph, node_name, row,
				    PP_SENSOR_VALUE, snmp_syserr_p);
			}
			CHECK_LINKRESET(snmp_syserr_p, NULL)

			add_prop(nodeh, &proph, node_name, row,
			    PP_OPSTATUS, snmp_syserr_p);
			CHECK_LINKRESET(snmp_syserr_p, NULL)

			add_prop(nodeh, &proph, node_name, row,
			    PP_BASE_UNITS, snmp_syserr_p);
			CHECK_LINKRESET(snmp_syserr_p, NULL)

			add_prop(nodeh, &proph, node_name, row,
			    PP_EXPONENT, snmp_syserr_p);
			CHECK_LINKRESET(snmp_syserr_p, NULL)

			add_prop(nodeh, &proph, node_name, row,
			    PP_RATE_UNITS, snmp_syserr_p);
			CHECK_LINKRESET(snmp_syserr_p, NULL)

			add_thresholds(nodeh, row, snmp_syserr_p);
			CHECK_LINKRESET(snmp_syserr_p, NULL)

		} else if (sensor_class == SSSC_BINARY) {
			if (sensor_type == SSST_TEMPERATURE) {
				ADD_NODE(PICL_CLASS_TEMPERATURE_INDICATOR)
			} else if (sensor_type == SSST_VOLTAGE) {
				ADD_NODE(PICL_CLASS_VOLTAGE_INDICATOR)
			} else if (sensor_type == SSST_CURRENT) {
				ADD_NODE(PICL_CLASS_CURRENT_INDICATOR)
			} else if (sensor_type == SSST_TACHOMETER) {
				ADD_NODE(PICL_CLASS_RPM_INDICATOR)
			} else if (sensor_type == SSST_PRESENCE) {
				ADD_NODE(PICL_CLASS_PRESENCE_INDICATOR)
			} else {
				ADD_NODE(PICL_CLASS_INDICATOR)
			}

			add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
			    snmp_syserr_p);
			CHECK_LINKRESET(snmp_syserr_p, NULL)

			add_prop(nodeh, &proph, node_name, row, PP_CONDITION,
			    snmp_syserr_p);
			CHECK_LINKRESET(snmp_syserr_p, NULL)

			add_prop(nodeh, &proph, node_name, row, PP_EXPECTED,
			    snmp_syserr_p);
			CHECK_LINKRESET(snmp_syserr_p, NULL)
		} else {
			log_msg(LOG_ERR,
			    SNMPP_UNSUPP_SENSOR_CLASS, sensor_class, row);
			return (NULL);
		}
		break;

	case SPC_MODULE:
		ADD_NODE(PICL_CLASS_MODULE)

		add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
		    snmp_syserr_p);
		CHECK_LINKRESET(snmp_syserr_p, NULL)

		add_prop(nodeh, &proph, node_name, row, PP_REPLACEABLE,
		    snmp_syserr_p);
		CHECK_LINKRESET(snmp_syserr_p, NULL)

		add_prop(nodeh, &proph, node_name, row, PP_HOTSWAPPABLE,
		    snmp_syserr_p);
		CHECK_LINKRESET(snmp_syserr_p, NULL)
		break;

	case SPC_PORT:
		ADD_NODE(PICL_CLASS_PORT)
		break;

	case SPC_STACK:
		ADD_NODE(PICL_CLASS_STACK)
		break;

	default:
		log_msg(LOG_WARNING,
		    SNMPP_UNKNOWN_ENTPHYSCLASS, ent_physclass, row);
		free(phys_name);
		return (NULL);
	}

	add_prop(nodeh, &proph, node_name, row, PP_DESCRIPTION, snmp_syserr_p);
	CHECK_LINKRESET(snmp_syserr_p, NULL)

	add_prop(nodeh, &proph, node_name, row, PP_LABEL, snmp_syserr_p);
	CHECK_LINKRESET(snmp_syserr_p, NULL)

	add_prop(nodeh, &proph, node_name, row, PP_HW_REVISION, snmp_syserr_p);
	CHECK_LINKRESET(snmp_syserr_p, NULL)

	add_prop(nodeh, &proph, node_name, row, PP_FW_REVISION, snmp_syserr_p);
	CHECK_LINKRESET(snmp_syserr_p, NULL)

	add_prop(nodeh, &proph, node_name, row, PP_SERIAL_NUM, snmp_syserr_p);
	CHECK_LINKRESET(snmp_syserr_p, NULL)

	add_prop(nodeh, &proph, node_name, row, PP_MFG_NAME, snmp_syserr_p);
	CHECK_LINKRESET(snmp_syserr_p, NULL)

	add_prop(nodeh, &proph, node_name, row, PP_MODEL_NAME, snmp_syserr_p);
	CHECK_LINKRESET(snmp_syserr_p, NULL)

	add_prop(nodeh, &proph, node_name, row, PP_IS_FRU, snmp_syserr_p);
	CHECK_LINKRESET(snmp_syserr_p, NULL)

	free(phys_name);
	save_nodeh(nodeh, row);

	return (nodeh);
}

/*
 * Saves the node handle and the row id into physplat_nodes[]. If we're
 * doing this in response to a hotplug event, we should've freed the
 * old physplat_nodes before entering here to save the first node of the
 * new physplat subtree.
 */
static void
save_nodeh(picl_nodehdl_t nodeh, int row)
{
	size_t		sz, count;
	picl_nodehdl_t	*p;

	if (row >= n_physplat_nodes) {
		count = (((size_t)row >> NODE_BLOCK_SHIFT) + 1) *
		    N_ELEMS_IN_NODE_BLOCK;
		sz = count * sizeof (picl_nodehdl_t);

		p = (picl_nodehdl_t *)calloc(count, sizeof (picl_nodehdl_t));
		if (p == NULL) {
			log_msg(LOG_ERR, SNMPP_NO_MEM, sz);
			return;
		}

		if (physplat_nodes) {
			(void) memcpy((void *) p, (void *) physplat_nodes,
			    n_physplat_nodes * sizeof (picl_nodehdl_t));
			free((void *) physplat_nodes);
		}

		physplat_nodes = p;
		n_physplat_nodes = count;
	}

	physplat_nodes[row] = nodeh;
}

static picl_nodehdl_t
lookup_nodeh(int row)
{
	if (row >= n_physplat_nodes)
		return (NULL);

	return (physplat_nodes[row]);
}

/*
 * We enter this routine only when we are building the physical-platform
 * subtree, whether for the first time or in response to a hotplug event.
 * If we're here for rebuilding the tree, we have already set stale_tree
 * to be B_TRUE, so no one else would be accessing vol_props, n_vol_props
 * or volprop_ndx. If we're here to build the tree for the first time,
 * picld hasn't yet created doors and is running single-threaded, so no
 * one else would be accessing them anyway.
 */
static void
save_volprop(picl_prophdl_t prop, char *oidstr, int row, int proptype)
{
	vol_prophdl_t	*p;
	int		count;

	if (volprop_ndx == n_vol_props) {
		count = n_vol_props + N_ELEMS_IN_VOLPROP_BLOCK;
		p = (vol_prophdl_t *)calloc(count, sizeof (vol_prophdl_t));
		if (p == NULL) {
			log_msg(LOG_ERR, SNMPP_NO_MEM,
			    count * sizeof (vol_prophdl_t));
			return;
		}

		if (vol_props) {
			(void) memcpy((void *) p, (void *) vol_props,
			    n_vol_props * sizeof (vol_prophdl_t));
			free((void *) vol_props);
		}

		vol_props = p;
		n_vol_props += N_ELEMS_IN_VOLPROP_BLOCK;
	}

	vol_props[volprop_ndx].prop = prop;
	vol_props[volprop_ndx].oidstr = oidstr;
	vol_props[volprop_ndx].row = row;
	vol_props[volprop_ndx].proptype = proptype;

	volprop_ndx++;
}

static void
check_for_stale_data(void)
{
	int	cur_change_time;
	int	ret;
	int	snmp_syserr;

	(void) rw_wrlock(&stale_tree_rwlp);

	/*
	 * Check if some other thread beat us to it
	 */
	if (stale_tree == B_TRUE) {
		(void) rw_unlock(&stale_tree_rwlp);
		return;
	}

	/*
	 * Check if mib data has changed (hotplug? link-reset?)
	 */
	ret = snmp_get_int(hdl, OID_entLastChangeTime, 0, &cur_change_time,
	    &snmp_syserr);
	if ((ret == 0) && (cur_change_time == change_time)) {
		(void) rw_unlock(&stale_tree_rwlp);
		return;
	}

	/*
	 * If we can't read entLastChangeTime we assume we need to rebuild
	 * the tree. This will also cover the case when we need to rebuild
	 * the tree because a link reset had happened.
	 */
	LOGPRINTF2("check_for_stale_data: LastChange times have changed, "
	    "(%#x != %#x)\n", change_time, cur_change_time);

	/*
	 * If the mib data has changed, we need to rebuild the physical-platform
	 * subtree. To do this, we set a flag to mark the tree stale,
	 * so that any future reads to get value of volatile properties will
	 * return PICL_PROPVALUNAVAILABLE, until the stale_tree flag
	 * is reset by the tree builder thread.
	 */
	stale_tree = B_TRUE;
	if (vol_props) {
		free(vol_props);
	}
	vol_props = NULL;
	volprop_ndx = 0;
	n_vol_props = 0;

	(void) rw_unlock(&stale_tree_rwlp);

	(void) mutex_lock(&rebuild_tree_lock);
	rebuild_tree = B_TRUE;
	(void) cond_signal(&rebuild_tree_cv);
	LOGPRINTF("check_for_stale_data: signalled tree builder\n");
	(void) mutex_unlock(&rebuild_tree_lock);
}

/*
 * This is the critical routine.  This callback is invoked by picl whenever
 * it needs to fetch the value of a volatile property. The first thing we
 * must do, however, is to see if there has been a hotplug or a link-reset
 * event since the last time we built the tree and whether we need to
 * rebuild the tree. If so, we do whatever is necessary to make that happen,
 * but return PICL_PROPVALUNAVAILABLE for now, without making any further
 * snmp requests or accessing any globals.
 */
static int
read_volprop(ptree_rarg_t *parg, void *buf)
{
	char	*pstr;
	int	propval;
	int	i, ndx;
	int	ret;
	int	snmp_syserr = 0;

	/*
	 * First check for any event that would make us throw away
	 * the existing /physical-platform subtree and rebuild
	 * another one. If we are rebuilding the subtree, we just
	 * return the stale value until the tree is fully built.
	 */
	check_for_stale_data();

	(void) rw_rdlock(&stale_tree_rwlp);

	if (stale_tree == B_TRUE) {
		(void) rw_unlock(&stale_tree_rwlp);
		return (PICL_PROPVALUNAVAILABLE);
	}

	for (i = 0; i < volprop_ndx; i++) {
		if (vol_props[i].prop == parg->proph) {
			ndx = i;
			break;
		}
	}
	if (i == volprop_ndx) {
		log_msg(LOG_ERR, SNMPP_CANT_FIND_VOLPROP, parg->proph);
		return (PICL_FAILURE);
	}

	/*
	 * If we can't read the value, return failure. Even if this was
	 * due to a link reset, between the check for stale data and now,
	 * the next volatile callback by picl will initiate a tree-rebuild.
	 */
	ret = snmp_get_int(hdl, vol_props[ndx].oidstr, vol_props[ndx].row,
	    &propval, &snmp_syserr);
	if (ret < 0) {
		log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL, ret);
		return (PICL_FAILURE);
	}

	switch (vol_props[ndx].proptype) {
	case VPT_PLATOPSTATE:
		if (propval == SSOS_DISABLED) {
			(void) strlcpy(buf, STR_SSOS_DISABLED, MAX_OPSTATE_LEN);
		} else if (propval == SSOS_ENABLED) {
			(void) strlcpy(buf, STR_SSOS_ENABLED, MAX_OPSTATE_LEN);
		} else {
			log_msg(LOG_ERR, SNMPP_INV_PLAT_EQUIP_OPSTATE,
			    propval, vol_props[ndx].row);
			return (PICL_FAILURE);
		}
		break;

	case VPT_NUMSENSOR:
		(void) memcpy(buf, &propval, sizeof (propval));
		break;

	case VPT_BINSENSOR:
		if (propval == ST_TRUE) {
			ret = snmp_get_str(hdl,
			    OID_sunPlatBinarySensorInterpretTrue,
			    vol_props[ndx].row, &pstr, &snmp_syserr);
			if (snmp_syserr == ECANCELED)
				return (PICL_FAILURE);
			if (ret < 0 || pstr == NULL) {
				(void) strlcpy(buf, STR_ST_TRUE,
				    MAX_TRUTHVAL_LEN);
			} else {
				(void) strlcpy(buf, pstr, MAX_TRUTHVAL_LEN);
				free(pstr);
			}
		} else if (propval == ST_FALSE) {
			ret = snmp_get_str(hdl,
			    OID_sunPlatBinarySensorInterpretFalse,
			    vol_props[ndx].row, &pstr, &snmp_syserr);
			if (snmp_syserr == ECANCELED)
				return (PICL_FAILURE);
			if (ret < 0 || pstr == NULL) {
				(void) strlcpy(buf, STR_ST_FALSE,
				    MAX_TRUTHVAL_LEN);
			} else {
				(void) strlcpy(buf, pstr, MAX_TRUTHVAL_LEN);
				free(pstr);
			}
		} else {
			log_msg(LOG_ERR, SNMPP_INV_PLAT_BINSNSR_CURRENT,
			    propval, vol_props[ndx].row);
			return (PICL_FAILURE);
		}
		break;

	case VPT_ALARMSTATE:
		if (propval == SSAS_OFF) {
			(void) strlcpy(buf, STR_SSAS_OFF, MAX_ALARMSTATE_LEN);
		} else if (propval == SSAS_STEADY) {
			(void) strlcpy(buf, STR_SSAS_STEADY,
			    MAX_ALARMSTATE_LEN);
		} else if (propval == SSAS_ALTERNATING) {
			(void) strlcpy(buf, STR_SSAS_ALTERNATING,
			    MAX_ALARMSTATE_LEN);
		} else {
			(void) strlcpy(buf, STR_SSAS_UNKNOWN,
			    MAX_ALARMSTATE_LEN);
		}
		break;

	case VPT_BATTERYSTATUS:
		switch (propval) {
		case SSBS_OTHER:
			(void) strlcpy(buf, STR_SSBS_OTHER,
			    MAX_BATTERYSTATUS_LEN);
			break;
		case SSBS_FULLYCHARGED:
			(void) strlcpy(buf, STR_SSBS_FULLYCHARGED,
			    MAX_BATTERYSTATUS_LEN);
			break;
		case SSBS_LOW:
			(void) strlcpy(buf, STR_SSBS_LOW,
			    MAX_BATTERYSTATUS_LEN);
			break;
		case SSBS_CRITICAL:
			(void) strlcpy(buf, STR_SSBS_CRITICAL,
			    MAX_BATTERYSTATUS_LEN);
			break;
		case SSBS_CHARGING:
			(void) strlcpy(buf, STR_SSBS_CHARGING,
			    MAX_BATTERYSTATUS_LEN);
			break;
		case SSBS_CHARGING_AND_LOW:
			(void) strlcpy(buf, STR_SSBS_CHARGING_AND_LOW,
			    MAX_BATTERYSTATUS_LEN);
			break;
		case SSBS_CHARGING_AND_HIGH:
			(void) strlcpy(buf, STR_SSBS_CHARGING_AND_HIGH,
			    MAX_BATTERYSTATUS_LEN);
			break;
		case SSBS_CHARGING_AND_CRITICAL:
			(void) strlcpy(buf, STR_SSBS_CHARGING_AND_CRITICAL,
			    MAX_BATTERYSTATUS_LEN);
			break;
		case SSBS_UNDEFINED:
			(void) strlcpy(buf, STR_SSBS_UNDEFINED,
			    MAX_BATTERYSTATUS_LEN);
			break;
		case SSBS_PARTIALLY_CHARGED:
			(void) strlcpy(buf, STR_SSBS_PARTIALLY_CHARGED,
			    MAX_BATTERYSTATUS_LEN);
			break;
		case SSBS_UNKNOWN:
		default:
			(void) strlcpy(buf, STR_SSBS_UNKNOWN,
			    MAX_BATTERYSTATUS_LEN);
			break;
		}
		break;
	}

	(void) rw_unlock(&stale_tree_rwlp);

	return (PICL_SUCCESS);
}

static void
threshold(picl_nodehdl_t node, char *oidstr, int row, char *propname,
    int *snmp_syserr_p)
{
	picl_prophdl_t	prop;
	int		err;
	int		val;

	if (snmp_get_int(hdl, oidstr, row, &val, snmp_syserr_p) != -1) {
		err = add_volatile_prop(node, propname, PICL_PTYPE_INT,
		    PICL_READ, sizeof (int), read_volprop, NULL, &prop);
		if (err == PICL_SUCCESS)
			save_volprop(prop, oidstr, row, VPT_NUMSENSOR);
	}
}

static void
add_thresholds(picl_nodehdl_t node, int row, int *snmp_syserr_p)
{
	uchar_t	*bitstr = NULL;
	uchar_t	enabled;
	uint_t	nbytes;
	int	ret;

	ret = snmp_get_bitstr(hdl, OID_sunPlatNumericSensorEnabledThresholds,
	    row, &bitstr, &nbytes, snmp_syserr_p);
	CHECK_LINKRESET_VOID(snmp_syserr_p)

	if (ret < 0 || bitstr == NULL || nbytes > 2)
		enabled = 0xff;
	else if (nbytes == 1) {
		/*
		 * The ALOM snmp agent doesn't adhere to the BER rules for
		 * encoding bit strings. While the BER states that bitstrings
		 * must begin from the second octet after length, and the
		 * first octet after length must indicate the number of unused
		 * bits in the last octet, the snmp agent simply sends the
		 * bitstring data as if it were octet string -- that is, the
		 * "unused bits" octet is missing.
		 */
		enabled = bitstr[0];
	} else if (nbytes == 2)
		enabled = bitstr[1];

	if (bitstr) {
		free(bitstr);
	}

	if (enabled & LOWER_FATAL) {
		threshold(node,
		    OID_sunPlatNumericSensorLowerThresholdFatal, row,
		    PICL_PROP_LOW_POWER_OFF, snmp_syserr_p);
		CHECK_LINKRESET_VOID(snmp_syserr_p)
	}
	if (enabled & LOWER_CRITICAL) {
		threshold(node,
		    OID_sunPlatNumericSensorLowerThresholdCritical, row,
		    PICL_PROP_LOW_SHUTDOWN, snmp_syserr_p);
		CHECK_LINKRESET_VOID(snmp_syserr_p)
	}
	if (enabled & LOWER_NON_CRITICAL) {
		threshold(node,
		    OID_sunPlatNumericSensorLowerThresholdNonCritical, row,
		    PICL_PROP_LOW_WARNING, snmp_syserr_p);
		CHECK_LINKRESET_VOID(snmp_syserr_p)
	}
	if (enabled & UPPER_NON_CRITICAL) {
		threshold(node,
		    OID_sunPlatNumericSensorUpperThresholdNonCritical, row,
		    PICL_PROP_HIGH_POWER_OFF, snmp_syserr_p);
		CHECK_LINKRESET_VOID(snmp_syserr_p)
	}
	if (enabled & UPPER_CRITICAL) {
		threshold(node,
		    OID_sunPlatNumericSensorUpperThresholdCritical, row,
		    PICL_PROP_HIGH_SHUTDOWN, snmp_syserr_p);
		CHECK_LINKRESET_VOID(snmp_syserr_p)
	}
	if (enabled & UPPER_FATAL) {
		threshold(node,
		    OID_sunPlatNumericSensorUpperThresholdFatal, row,
		    PICL_PROP_HIGH_WARNING, snmp_syserr_p);
		CHECK_LINKRESET_VOID(snmp_syserr_p)
	}
}

static char *
get_slot_type(int row, int *snmp_syserr_p)
{
	char	*p;
	char	*slott = NULL;
	int	ret;

	ret = snmp_get_str(hdl, OID_sunPlatEquipmentHolderAcceptableTypes,
	    row, &p, snmp_syserr_p);
	CHECK_LINKRESET(snmp_syserr_p, NULL)

	if ((ret == 0) && p && *p) {
		slott = p;
		if ((p = strchr(slott, '\n')) != NULL)
			*p = 0;
	} else {
		log_msg(LOG_WARNING, SNMPP_NO_SLOT_TYPE, row);
		if (p) {
			free(p);
		}
	}

	return (slott);
}

/*
 * Create and add the specified volatile property
 */
static int
add_volatile_prop(picl_nodehdl_t node, char *name, int type, int access,
    int size, int (*rdfunc)(ptree_rarg_t *, void *),
    int (*wrfunc)(ptree_warg_t *, const void *), picl_prophdl_t *propp)
{
	ptree_propinfo_t	propinfo;
	picl_prophdl_t		prop;
	int			err;

	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
	    type, (access|PICL_VOLATILE), size, name, rdfunc, wrfunc);
	if (err != PICL_SUCCESS) {
		log_msg(LOG_ERR, SNMPP_CANT_INIT_PROPINFO, err);
		return (err);
	}

	err = ptree_create_and_add_prop(node, &propinfo, NULL, &prop);
	if (err != PICL_SUCCESS) {
		log_msg(LOG_ERR, SNMPP_CANT_ADD_PROP, err, node);
		return (err);
	}

	if (propp)
		*propp = prop;

	return (PICL_SUCCESS);
}

/*
 * Add the specified string property to the node
 */
static int
add_string_prop(picl_nodehdl_t node, char *propname, char *propval)
{
	ptree_propinfo_t	propinfo;
	int			err;

	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
	    PICL_PTYPE_CHARSTRING, PICL_READ, strlen(propval) + 1,
	    propname, NULL, NULL);
	if (err != PICL_SUCCESS) {
		log_msg(LOG_ERR, SNMPP_CANT_INIT_STR_PROPINFO, err);
		return (err);
	}

	err = ptree_create_and_add_prop(node, &propinfo, propval, NULL);
	if (err != PICL_SUCCESS) {
		log_msg(LOG_ERR, SNMPP_CANT_ADD_STR_PROP, err, node);
		return (err);
	}

	return (PICL_SUCCESS);
}

/*
 * Add the specified void property to the node
 */
static int
add_void_prop(picl_nodehdl_t node, char *propname)
{
	ptree_propinfo_t	propinfo;
	int			err;

	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
	    PICL_PTYPE_VOID, PICL_READ, 0, propname, NULL, NULL);
	if (err != PICL_SUCCESS) {
		log_msg(LOG_ERR, SNMPP_CANT_INIT_VOID_PROPINFO, err);
		return (err);
	}

	err = ptree_create_and_add_prop(node, &propinfo, NULL, NULL);
	if (err != PICL_SUCCESS) {
		log_msg(LOG_ERR, SNMPP_CANT_ADD_VOID_PROP, err, node);
		return (err);
	}

	return (PICL_SUCCESS);
}

static int
add_int_prop(picl_nodehdl_t node, char *propname, int val)
{
	ptree_propinfo_t	propinfo;
	int			propval = val;
	int			err;

	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
	    PICL_PTYPE_INT, PICL_READ, sizeof (int),
	    propname, NULL, NULL);
	if (err != PICL_SUCCESS) {
		log_msg(LOG_ERR, SNMPP_CANT_INIT_INT_PROPINFO, err);
		return (err);
	}

	err = ptree_create_and_add_prop(node, &propinfo, &propval, NULL);
	if (err != PICL_SUCCESS) {
		log_msg(LOG_ERR, SNMPP_CANT_ADD_INT_PROP, err, node);
		return (err);
	}

	return (PICL_SUCCESS);
}

static void
add_prop(picl_nodehdl_t nodeh, picl_prophdl_t *php, char *label,
    int row, sp_propid_t pp, int *snmp_syserr_p)
{
	char	*serial_num;
	char	*slot_type;
	char	*fw_revision, *hw_revision;
	char	*mfg_name, *model_name;
	char	*phys_descr;
	int	val;
	int	ret;

	switch (pp) {
	case PP_SERIAL_NUM:
		ret = snmp_get_str(hdl, OID_entPhysicalSerialNum,
		    row, &serial_num, snmp_syserr_p);
		CHECK_LINKRESET_VOID(snmp_syserr_p)
		if ((ret == 0) && serial_num && *serial_num) {
			(void) add_string_prop(nodeh,
			    PICL_PROP_SERIAL_NUMBER, serial_num);
			free((void *) serial_num);
		}
		break;

	case PP_SLOT_TYPE:
		if ((slot_type = get_slot_type(row, snmp_syserr_p)) == NULL) {
			CHECK_LINKRESET_VOID(snmp_syserr_p)
			(void) add_string_prop(nodeh,
			    PICL_PROP_SLOT_TYPE, DEFAULT_SLOT_TYPE);
		} else {
			(void) add_string_prop(nodeh,
			    PICL_PROP_SLOT_TYPE, slot_type);
			free((void *) slot_type);
		}
		break;

	case PP_STATE:
		ret = add_volatile_prop(nodeh, PICL_PROP_STATE,
		    PICL_PTYPE_CHARSTRING, PICL_READ, MAX_ALARMSTATE_LEN,
		    read_volprop, NULL, php);
		if (ret == PICL_SUCCESS) {
			save_volprop(*php, OID_sunPlatAlarmState, row,
			    VPT_ALARMSTATE);
		}
		break;

	case PP_OPSTATUS:
		ret = add_volatile_prop(nodeh, PICL_PROP_OPERATIONAL_STATUS,
		    PICL_PTYPE_CHARSTRING, PICL_READ, MAX_OPSTATE_LEN,
		    read_volprop, NULL, php);
		if (ret == PICL_SUCCESS) {
			save_volprop(*php,
			    OID_sunPlatEquipmentOperationalState, row,
			    VPT_PLATOPSTATE);
		}
		break;

	case PP_BATT_STATUS:
		ret = add_volatile_prop(nodeh, PICL_PROP_BATTERY_STATUS,
			PICL_PTYPE_CHARSTRING, PICL_READ, MAX_BATTERYSTATUS_LEN,
			read_volprop, NULL, php);
		if (ret == PICL_SUCCESS) {
			save_volprop(*php, OID_sunPlatBatteryStatus, row,
			    VPT_BATTERYSTATUS);
		}
		break;

	case PP_TEMPERATURE:
		ret = add_volatile_prop(nodeh, PICL_PROP_TEMPERATURE,
		    PICL_PTYPE_INT, PICL_READ, sizeof (int), read_volprop,
		    NULL, php);
		if (ret == PICL_SUCCESS) {
			save_volprop(*php, OID_sunPlatNumericSensorCurrent,
			    row, VPT_NUMSENSOR);
		}
		break;

	case PP_VOLTAGE:
		ret = add_volatile_prop(nodeh, PICL_PROP_VOLTAGE,
		    PICL_PTYPE_INT, PICL_READ, sizeof (int), read_volprop,
		    NULL, php);
		if (ret == PICL_SUCCESS) {
			save_volprop(*php, OID_sunPlatNumericSensorCurrent,
			    row, VPT_NUMSENSOR);
		}
		break;

	case PP_CURRENT:
		ret = add_volatile_prop(nodeh, PICL_PROP_CURRENT,
		    PICL_PTYPE_INT, PICL_READ, sizeof (int), read_volprop,
		    NULL, php);
		if (ret == PICL_SUCCESS) {
			save_volprop(*php, OID_sunPlatNumericSensorCurrent,
			    row, VPT_NUMSENSOR);
		}
		break;

	case PP_SPEED:
		ret = add_volatile_prop(nodeh, PICL_PROP_SPEED, PICL_PTYPE_INT,
		    PICL_READ, sizeof (int), read_volprop, NULL, php);
		if (ret == PICL_SUCCESS) {
			save_volprop(*php, OID_sunPlatNumericSensorCurrent,
			    row, VPT_NUMSENSOR);
		}
		break;

	case PP_SENSOR_VALUE:
		ret = add_volatile_prop(nodeh, PICL_PROP_SENSOR_VALUE,
		    PICL_PTYPE_INT, PICL_READ, sizeof (int), read_volprop,
		    NULL, php);
		if (ret == PICL_SUCCESS) {
			save_volprop(*php, OID_sunPlatNumericSensorCurrent,
			    row, VPT_NUMSENSOR);
		}
		break;

	case PP_CONDITION:
		ret = add_volatile_prop(nodeh, PICL_PROP_CONDITION,
		    PICL_PTYPE_CHARSTRING, PICL_READ, MAX_TRUTHVAL_LEN,
		    read_volprop, NULL, php);
		if (ret == PICL_SUCCESS) {
			save_volprop(*php, OID_sunPlatBinarySensorCurrent,
			    row, VPT_BINSENSOR);
		}
		break;

	case PP_EXPECTED:
		ret = add_volatile_prop(nodeh, PICL_PROP_EXPECTED,
		    PICL_PTYPE_CHARSTRING, PICL_READ, MAX_TRUTHVAL_LEN,
		    read_volprop, NULL, php);
		if (ret == PICL_SUCCESS) {
			save_volprop(*php, OID_sunPlatBinarySensorExpected,
			    row, VPT_BINSENSOR);
		}
		break;

	case PP_REPLACEABLE:
		ret = snmp_get_int(hdl, OID_sunPlatCircuitPackReplaceable,
		    row, &val, snmp_syserr_p);
		CHECK_LINKRESET_VOID(snmp_syserr_p)
		if ((ret == 0) && (val == ST_TRUE))
			(void) add_void_prop(nodeh, PICL_PROP_IS_REPLACEABLE);
		break;

	case PP_HOTSWAPPABLE:
		ret = snmp_get_int(hdl, OID_sunPlatCircuitPackHotSwappable,
		    row, &val, snmp_syserr_p);
		CHECK_LINKRESET_VOID(snmp_syserr_p)
		if ((ret == 0) && (val == ST_TRUE))
			(void) add_void_prop(nodeh, PICL_PROP_IS_HOT_SWAPPABLE);
		break;

	case PP_IS_FRU:
		ret = snmp_get_int(hdl, OID_entPhysicalIsFRU, row,
		    &val, snmp_syserr_p);
		CHECK_LINKRESET_VOID(snmp_syserr_p)
		if ((ret == 0) && (val == ST_TRUE))
			(void) add_void_prop(nodeh, PICL_PROP_IS_FRU);
		break;

	case PP_HW_REVISION:
		ret = snmp_get_str(hdl, OID_entPhysicalHardwareRev,
		    row, &hw_revision, snmp_syserr_p);
		CHECK_LINKRESET_VOID(snmp_syserr_p)
		if ((ret == 0) && hw_revision && *hw_revision) {
			(void) add_string_prop(nodeh,
			    PICL_PROP_HW_REVISION, hw_revision);
			free((void *) hw_revision);
		}
		break;

	case PP_FW_REVISION:
		ret = snmp_get_str(hdl, OID_entPhysicalFirmwareRev,
		    row, &fw_revision, snmp_syserr_p);
		CHECK_LINKRESET_VOID(snmp_syserr_p)
		if ((ret == 0) && fw_revision && *fw_revision) {
			(void) add_string_prop(nodeh,
			    PICL_PROP_FW_REVISION, fw_revision);
			free((void *) fw_revision);
		}
		break;

	case PP_MFG_NAME:
		ret = snmp_get_str(hdl, OID_entPhysicalMfgName,
		    row, &mfg_name, snmp_syserr_p);
		CHECK_LINKRESET_VOID(snmp_syserr_p)
		if ((ret == 0) && mfg_name && *mfg_name) {
			(void) add_string_prop(nodeh,
			    PICL_PROP_MFG_NAME, mfg_name);
			free((void *) mfg_name);
		}
		break;

	case PP_MODEL_NAME:
		ret = snmp_get_str(hdl, OID_entPhysicalModelName,
		    row, &model_name, snmp_syserr_p);
		CHECK_LINKRESET_VOID(snmp_syserr_p)
		if ((ret == 0) && model_name && *model_name) {
			(void) add_string_prop(nodeh,
			    PICL_PROP_MODEL_NAME, model_name);
			free((void *) model_name);
		}
		break;

	case PP_DESCRIPTION:
		ret = snmp_get_str(hdl, OID_entPhysicalDescr,
		    row, &phys_descr, snmp_syserr_p);
		CHECK_LINKRESET_VOID(snmp_syserr_p)
		if ((ret == 0) && phys_descr && *phys_descr) {
		    (void) add_string_prop(nodeh,
			PICL_PROP_PHYS_DESCRIPTION, phys_descr);
		    free((void *) phys_descr);
		}
		break;

	case PP_LABEL:
		if (label && *label)
			(void) add_string_prop(nodeh, PICL_PROP_LABEL, label);
		break;

	case PP_BASE_UNITS:
		ret = snmp_get_int(hdl, OID_sunPlatNumericSensorBaseUnits,
		    row, &val, snmp_syserr_p);
		CHECK_LINKRESET_VOID(snmp_syserr_p)
		if ((ret == 0) && (val > 0) && (val < n_baseunits)) {
			(void) add_string_prop(nodeh,
			    PICL_PROP_BASE_UNITS, sensor_baseunits[val]);
		}
		break;

	case PP_RATE_UNITS:
		ret = snmp_get_int(hdl, OID_sunPlatNumericSensorRateUnits,
		    row, &val, snmp_syserr_p);
		CHECK_LINKRESET_VOID(snmp_syserr_p)
		if ((ret == 0) && (val > 0) && (val < n_rateunits)) {
			(void) add_string_prop(nodeh,
			    PICL_PROP_RATE_UNITS, sensor_rateunits[val]);
		}
		break;

	case PP_EXPONENT:
		ret = snmp_get_int(hdl, OID_sunPlatNumericSensorExponent,
		    row, &val, snmp_syserr_p);
		CHECK_LINKRESET_VOID(snmp_syserr_p)
		if (ret == 0)
			(void) add_int_prop(nodeh, PICL_PROP_EXPONENT, val);
		break;
	}
}

/*VARARGS2*/
static void
log_msg(int pri, const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	vsyslog(pri, fmt, ap);
	va_end(ap);
}

#ifdef SNMPPLUGIN_DEBUG

static void
snmpplugin_log_init(void)
{
	(void) mutex_init(&snmpplugin_dbuf_lock, USYNC_THREAD, NULL);
}

static void
snmpplugin_log(const char *fmt, ...)
{
	va_list	ap;

	(void) mutex_lock(&snmpplugin_dbuf_lock);

	va_start(ap, fmt);
	(void) vsnprintf(snmpplugin_lbuf, SNMPPLUGIN_DMAX_LINE, fmt, ap);
	snmpplugin_log_append();
	va_end(ap);

	(void) mutex_unlock(&snmpplugin_dbuf_lock);
}

static void
snmpplugin_log_append(void)
{
	int	len;

	len = strlen(snmpplugin_lbuf);

	if ((snmpplugin_dbuf_curp + len) >=
	    (snmpplugin_dbuf + snmpplugin_dbuf_sz)) {
		snmpplugin_dbuf_realloc();
		if (snmpplugin_dbuf == NULL) {
			(void) mutex_unlock(&snmpplugin_dbuf_lock);
			return;
		}
	}

	(void) strcpy(snmpplugin_dbuf_curp, snmpplugin_lbuf);
	snmpplugin_dbuf_curp += len;
}

static void
snmpplugin_dbuf_realloc(void)
{
	char	*p;
	size_t	offset = 0;
	size_t	count;

	count = snmpplugin_dbuf_sz + SNMPPLUGIN_DBLOCK_SZ;
	if ((p = (char *)calloc(count, 1)) == NULL) {
		snmpplugin_dbuf_overflow++;
		snmpplugin_dbuf_curp = snmpplugin_dbuf;
		return;
	}

	if (snmpplugin_dbuf) {
		offset = snmpplugin_dbuf_curp - snmpplugin_dbuf;
		(void) memcpy(p, snmpplugin_dbuf, snmpplugin_dbuf_sz);
		free(snmpplugin_dbuf);
	}

	snmpplugin_dbuf = p;
	snmpplugin_dbuf_sz += SNMPPLUGIN_DBLOCK_SZ;

	snmpplugin_dbuf_curp = snmpplugin_dbuf + offset;
}
#endif