snmpplugin.c revision 9d0d62ad2e60e8f742a2e723d06e88352ee6a1f3
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* 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 <time.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;
static time_t change_time_check;
/*
* 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;
static boolean_t tree_builder_thr_exit = B_FALSE;
static thread_t tree_builder_thr_id;
/*
* The cache_refresh thread periodically queries the snmp cache refresh work
* queue and processes jobs from it to keep cache entries from expiring. It
* attempts to run in cycles of CACHE_REFRESH_CYCLE seconds each, first
* processing cache refresh jobs and then sleeping for the remainder of the
* cycle once the next refresh job expiration is at least
* CACHE_REFRESH_MIN_WINDOW seconds in the future.
*
* NOTE: By using a thread to keep the SNMP cache refreshed in the background,
* we are both adding load to the system and reducing the system's ability to
* operate in power-saving mode when there is minimal load. While these
* tradeoffs are acceptable at this time in light of customer concerns about
* performance, it may be desirable in the future to move this work into the
* firmware. Also, while the current cycle times performed well on the largest
* sun4v config currently available (Batoka), they may need to be revisited for
* future systems if the number of sensors increases significantly.
*/
#define CACHE_REFRESH_CYCLE 60
#define CACHE_REFRESH_MIN_WINDOW 75
static mutex_t cache_refresh_lock;
static cond_t cache_refresh_cv;
static boolean_t cache_refresh_thr_exit = B_FALSE;
static thread_t cache_refresh_thr_id;
/*
* 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 *group3[] = {
OID_sunPlatNumericSensorEnabledThresholds,
OID_sunPlatNumericSensorBaseUnits,
OID_sunPlatNumericSensorRateUnits,
0
};
static char *group4[] = {
OID_sunPlatBinarySensorInterpretTrue,
OID_sunPlatBinarySensorInterpretFalse,
0
};
static char *volgroup1[] = {
OID_sunPlatBinarySensorCurrent,
OID_sunPlatBinarySensorExpected,
0
};
static char *volgroup2[] = {
OID_sunPlatNumericSensorExponent,
OID_sunPlatNumericSensorCurrent,
OID_sunPlatNumericSensorLowerThresholdFatal,
OID_sunPlatNumericSensorLowerThresholdCritical,
OID_sunPlatNumericSensorLowerThresholdNonCritical,
OID_sunPlatNumericSensorUpperThresholdNonCritical,
OID_sunPlatNumericSensorUpperThresholdCritical,
OID_sunPlatNumericSensorUpperThresholdFatal,
0
};
static char *volgroup3[] = {
OID_sunPlatEquipmentOperationalState,
0
};
static char *volgroup4[] = {
OID_sunPlatAlarmState,
0
};
static char *volgroup5[] = {
OID_sunPlatBatteryStatus,
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(boolean_t nocache);
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 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 *cache_refresher(void *arg);
static void cache_refresher_fini(void);
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);
free(oidstrs);
}
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);
tree_builder_thr_exit = B_FALSE;
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, &tree_builder_thr_id)) < 0) {
log_msg(LOG_ERR, SNMPP_CANT_CREATE_TREE_BUILDER, ret);
snmp_fini(hdl);
hdl = NULL;
(void) rwlock_destroy(&stale_tree_rwlp);
(void) cond_destroy(&rebuild_tree_cv);
(void) mutex_destroy(&rebuild_tree_lock);
tree_builder_thr_exit = B_TRUE;
return;
}
/*
* While the cache refresher thread does improve performance, it is not
* integral to the proper function of the plugin. If we fail to create
* the thread for some reason, we will simply continue without
* refreshing.
*/
(void) mutex_init(&cache_refresh_lock, USYNC_THREAD, NULL);
(void) cond_init(&cache_refresh_cv, USYNC_THREAD, NULL);
cache_refresh_thr_exit = B_FALSE;
LOGPRINTF("Cache refresher thread being created.\n");
if (thr_create(NULL, NULL, cache_refresher, NULL, THR_BOUND,
&cache_refresh_thr_id) < 0) {
(void) cond_destroy(&cache_refresh_cv);
(void) mutex_destroy(&cache_refresh_lock);
cache_refresh_thr_exit = B_TRUE;
}
}
void
snmpplugin_fini(void)
{
if (tree_builder_thr_exit == B_TRUE)
return;
/*
* Make reads of volatile properties return PICL_PROPUNAVAILABLE
* since we're about to recycle the plug-in. No need to worry
* about removing /physical-platform since tree_builder() will
* take care of recycling it for us.
*/
(void) rw_wrlock(&stale_tree_rwlp);
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);
/* clean up the cache_refresher thread and structures */
cache_refresher_fini();
/* wake up the tree_builder thread, tell it to exit */
(void) mutex_lock(&rebuild_tree_lock);
rebuild_tree = B_TRUE;
tree_builder_thr_exit = B_TRUE;
(void) cond_signal(&rebuild_tree_cv);
(void) mutex_unlock(&rebuild_tree_lock);
/* reap the thread */
(void) thr_join(tree_builder_thr_id, NULL, NULL);
/* close the channel */
if (hdl != NULL) {
snmp_fini(hdl);
hdl = NULL;
}
/* finish cleanup... */
(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(group3, 0);
register_group(group4, 0);
register_group(volgroup1, 1);
register_group(volgroup2, 1);
register_group(volgroup3, 1);
register_group(volgroup4, 1);
register_group(volgroup5, 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");
if (tree_builder_thr_exit == B_TRUE) {
(void) mutex_unlock(&rebuild_tree_lock);
LOGPRINTF("tree_builder: time to exit\n");
return (NULL);
}
old_physplat_root = NULL;
physplat_root = NULL;
LOGPRINTF("tree_builder: getting root node\n");
if ((ret = ptree_get_root(&root_node)) != PICL_SUCCESS) {
(void) mutex_unlock(&rebuild_tree_lock);
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) {
(void) mutex_unlock(&rebuild_tree_lock);
log_msg(LOG_ERR, SNMPP_CANT_CREATE_PHYSPLAT, ret);
cache_refresher_fini();
snmp_fini(hdl);
hdl = NULL;
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) {
(void) mutex_unlock(&rebuild_tree_lock);
free_resources(physplat_root);
log_msg(LOG_ERR, SNMPP_CANT_CREATE_PHYSPLAT, ret);
cache_refresher_fini();
snmp_fini(hdl);
hdl = NULL;
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) {
LOGPRINTF(SNMPP_LINK_RESET);
clr_linkreset = 1;
goto retry;
}
log_msg(LOG_WARNING, SNMPP_CANT_FETCH_OBJECT_VAL,
snmp_syserr ? snmp_syserr : ret, OID_entLastChangeTime, 0);
}
/*
* 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);
switch (snmp_syserr) {
case 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;
/*NOTREACHED*/
break;
case ENOSPC: /* end of MIB */
LOGPRINTF("build_physplat: end of MIB\n");
break;
case ENOENT: /* end of table */
LOGPRINTF("build_physplat: end of table\n");
break;
default:
/*
* make_node() will print messages so don't
* repeat that exercise here.
*/
if (ret == -1) {
log_msg(LOG_WARNING,
SNMPP_CANT_FETCH_OBJECT_VAL,
snmp_syserr ? snmp_syserr : ret,
OID_entPhysicalDescr, row);
}
}
}
/*
* 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,
snmp_syserr ? snmp_syserr : ret,
OID_entLastChangeTime, row);
}
/*
* 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,
*snmp_syserr_p ? *snmp_syserr_p : ret,
OID_entPhysicalClass, row);
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,
*snmp_syserr_p ? *snmp_syserr_p : ret,
OID_sunPlatPhysicalClass, row);
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,
*snmp_syserr_p ? *snmp_syserr_p : ret,
OID_sunPlatAlarmType, row);
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,
*snmp_syserr_p ? *snmp_syserr_p : ret,
OID_sunPlatPowerSupplyClass, row);
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,
*snmp_syserr_p ? *snmp_syserr_p : ret,
OID_sunPlatSensorClass, row);
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,
*snmp_syserr_p ? *snmp_syserr_p : ret,
OID_sunPlatSensorType, row);
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(boolean_t nocache)
{
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;
}
/*
* Cache OID_entLastChangeTime for up to 10 seconds before
* fetching it from ILOM again. This prevents us from fetching
* this value from ILOM when the we're filling or refreshing a
* whole bunch of items in the cache around the same time.
*/
if (nocache == B_FALSE && time(NULL) - change_time_check <= 10) {
(void) rw_unlock(&stale_tree_rwlp);
return;
}
/*
* Check if mib data has changed (hotplug? link-reset?)
*/
do {
snmp_syserr = 0;
ret = snmp_get_int(hdl, OID_entLastChangeTime, 0,
&cur_change_time, &snmp_syserr);
(void) time(&change_time_check);
if ((ret == 0) && (cur_change_time == change_time)) {
(void) rw_unlock(&stale_tree_rwlp);
return;
}
} while (ret != 0 && snmp_syserr == EINTR);
/*
* 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(B_FALSE);
(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) {
(void) rw_unlock(&stale_tree_rwlp);
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) {
(void) rw_unlock(&stale_tree_rwlp);
check_for_stale_data(B_TRUE);
if (stale_tree == B_TRUE) {
return (PICL_PROPVALUNAVAILABLE);
}
log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
snmp_syserr ? snmp_syserr : ret,
vol_props[ndx].oidstr, vol_props[ndx].row);
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 {
(void) rw_unlock(&stale_tree_rwlp);
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) {
(void) rw_unlock(&stale_tree_rwlp);
if (pstr)
free(pstr);
return (PICL_FAILURE);
}
if (ret < 0 || pstr == NULL) {
log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
snmp_syserr ? snmp_syserr : ret,
OID_sunPlatBinarySensorInterpretTrue,
vol_props[ndx].row);
(void) strlcpy(buf, STR_ST_TRUE,
MAX_TRUTHVAL_LEN);
} else {
(void) strlcpy(buf, pstr, MAX_TRUTHVAL_LEN);
}
if (pstr)
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) {
(void) rw_unlock(&stale_tree_rwlp);
if (pstr)
free(pstr);
return (PICL_FAILURE);
}
if (ret < 0 || pstr == NULL) {
log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
snmp_syserr ? snmp_syserr : ret,
OID_sunPlatBinarySensorInterpretFalse,
vol_props[ndx].row);
(void) strlcpy(buf, STR_ST_FALSE,
MAX_TRUTHVAL_LEN);
} else {
(void) strlcpy(buf, pstr, MAX_TRUTHVAL_LEN);
}
if (pstr)
free(pstr);
} else {
(void) rw_unlock(&stale_tree_rwlp);
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 ((err = 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);
} else
log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
*snmp_syserr_p ? *snmp_syserr_p : err, oidstr, row);
}
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_str(hdl,
OID_sunPlatNumericSensorEnabledThresholds,
row, (char **)&bitstr, snmp_syserr_p);
if (ret == -1) {
log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
*snmp_syserr_p ? *snmp_syserr_p : ret,
OID_sunPlatNumericSensorEnabledThresholds, row);
} else {
nbytes = strlen((const char *)bitstr);
}
CHECK_LINKRESET_VOID(snmp_syserr_p);
/*
* No bit string of threshold masks was returned, so we can't
* assume that any thresholds exist.
*
* This mask prevents us from attempting to fetch thresholds
* which don't apply to the sensor or that aren't there anyway,
* That speeds up the plug-in significantly since otherwise it
* takes several seconds to time out.
*/
if (ret < 0 || bitstr == NULL || nbytes == 0 || 2 < nbytes) {
if (bitstr)
free(bitstr);
return;
} 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_WARNING, 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_POWER_OFF, 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;
if (*propval == '\0')
return (PICL_SUCCESS);
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 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) {
(void) add_string_prop(nodeh,
PICL_PROP_SERIAL_NUMBER, serial_num);
free((void *) serial_num);
}
if (ret == -1)
log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
*snmp_syserr_p ? *snmp_syserr_p : ret,
OID_entPhysicalSerialNum, row);
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_EXPONENT:
ret = add_volatile_prop(nodeh, PICL_PROP_EXPONENT,
PICL_PTYPE_INT, PICL_READ, sizeof (int), read_volprop,
NULL, php);
if (ret == PICL_SUCCESS) {
save_volprop(*php, OID_sunPlatNumericSensorExponent,
row, VPT_NUMSENSOR);
}
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);
if (ret == -1)
log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
*snmp_syserr_p ? *snmp_syserr_p : ret,
OID_sunPlatCircuitPackReplaceable, row);
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);
if (ret == -1)
log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
*snmp_syserr_p ? *snmp_syserr_p : ret,
OID_sunPlatCircuitPackHotSwappable, row);
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);
if (ret == -1)
log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
*snmp_syserr_p ? *snmp_syserr_p : ret,
OID_entPhysicalIsFRU, row);
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) {
(void) add_string_prop(nodeh,
PICL_PROP_HW_REVISION, hw_revision);
free((void *) hw_revision);
}
if (ret == -1)
log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
*snmp_syserr_p ? *snmp_syserr_p : ret,
OID_entPhysicalHardwareRev, row);
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) {
(void) add_string_prop(nodeh,
PICL_PROP_FW_REVISION, fw_revision);
free((void *) fw_revision);
}
if (ret == -1)
log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
*snmp_syserr_p ? *snmp_syserr_p : ret,
OID_entPhysicalFirmwareRev, row);
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) {
(void) add_string_prop(nodeh,
PICL_PROP_MFG_NAME, mfg_name);
free((void *) mfg_name);
}
if (ret == -1)
log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
*snmp_syserr_p ? *snmp_syserr_p : ret,
OID_entPhysicalMfgName, row);
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) {
(void) add_string_prop(nodeh,
PICL_PROP_MODEL_NAME, model_name);
free((void *) model_name);
}
if (ret == -1)
log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
*snmp_syserr_p ? *snmp_syserr_p : ret,
OID_entPhysicalModelName, row);
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) {
(void) add_string_prop(nodeh,
PICL_PROP_PHYS_DESCRIPTION, phys_descr);
free((void *) phys_descr);
}
if (ret == -1)
log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
*snmp_syserr_p ? *snmp_syserr_p : ret,
OID_entPhysicalDescr, row);
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]);
}
if (ret == -1)
log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
*snmp_syserr_p ? *snmp_syserr_p : ret,
OID_sunPlatNumericSensorBaseUnits, row);
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]);
}
if (ret == -1)
log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
*snmp_syserr_p ? *snmp_syserr_p : ret,
OID_sunPlatNumericSensorRateUnits, row);
break;
}
}
/*
* Initialize the SNMP library's cache refresh subsystem, then periodically
* process refresh job to prevent cache entries from expiring.
*/
/*ARGSUSED*/
static void *
cache_refresher(void *arg)
{
int jobs;
int next_expiration;
timestruc_t to;
hrtime_t cycle_start, cycle_elapsed;
/*
* Initialize refresh subsystem
*/
LOGPRINTF("Initializing SNMP refresh subsystem.\n");
if (snmp_refresh_init() < 0) {
return ((void *)-1);
}
(void) mutex_lock(&cache_refresh_lock);
for (;;) {
cycle_start = gethrtime();
/*
* Process jobs from the snmp cache refresh work queue until one
* of the following conditions is true:
* 1) we are told to exit, or
* 2) we have processed at least as many jobs as recommended by
* the library, and the next job expiration is at least
* CACHE_REFRESH_MIN_WINDOW * seconds away.
*/
jobs = snmp_refresh_get_cycle_hint(CACHE_REFRESH_CYCLE);
while ((cache_refresh_thr_exit == B_FALSE) && (jobs > 0)) {
(void) snmp_refresh_process_job();
jobs--;
}
next_expiration = snmp_refresh_get_next_expiration();
while ((cache_refresh_thr_exit == B_FALSE) &&
((next_expiration >= 0) &&
(next_expiration < CACHE_REFRESH_MIN_WINDOW))) {
(void) snmp_refresh_process_job();
next_expiration = snmp_refresh_get_next_expiration();
}
/*
* As long as we haven't been told to exit, sleep for
* CACHE_REFRESH_CYCLE seconds minus the amount of time that has
* elapsed since this cycle started. If the elapsed time is
* equal to or greater than 60 seconds, skip sleeping entirely.
*/
cycle_elapsed = (gethrtime() - cycle_start) / NANOSEC;
if ((cache_refresh_thr_exit == B_FALSE) &&
(cycle_elapsed < CACHE_REFRESH_CYCLE)) {
to.tv_sec = CACHE_REFRESH_CYCLE - cycle_elapsed;
to.tv_nsec = 0;
(void) cond_reltimedwait(&cache_refresh_cv,
&cache_refresh_lock, &to);
}
/*
* If we have been told to exit, clean up and bail out.
*/
if (cache_refresh_thr_exit == B_TRUE) {
snmp_refresh_fini();
(void) mutex_unlock(&cache_refresh_lock);
LOGPRINTF("cache_refresher: time to exit\n");
return (NULL);
}
}
/*NOTREACHED*/
return (NULL);
}
/*
* Check to see if the cache_refresher thread is running. If it is, signal it
* to terminate and clean up associated data structures.
*/
void
cache_refresher_fini(void)
{
/* if the thread isn't running, there is nothing to do */
if (cache_refresh_thr_exit == B_TRUE)
return;
/* wake up the cache_refresher thread, tell it to exit */
(void) mutex_lock(&cache_refresh_lock);
cache_refresh_thr_exit = B_TRUE;
(void) cond_signal(&cache_refresh_cv);
(void) mutex_unlock(&cache_refresh_lock);
/* reap the thread */
(void) thr_join(cache_refresh_thr_id, NULL, NULL);
/* finish cleanup... */
(void) cond_destroy(&cache_refresh_cv);
(void) mutex_destroy(&cache_refresh_lock);
}
/*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) {
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