snmpplugin.c revision 0d63ce2b32a9e1cc8ed71d4d92536c44d66a530a
/*
* 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
* 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"
"snmp_plugin",
};
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;
/*
* 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 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;
/*
* These two should really not be global
*/
static int n_physplat_nodes = 0;
static char *group1[] = {
0
};
static char *group2[] = {
0
};
static char *volgroup1[] = {
0
};
static char *volgroup2[] = {
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);
int *snmp_syserr_p);
int proptype);
static void check_for_stale_data(void);
char *propname, int *snmp_syserr_p);
#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++)
n_oids = i;
return;
for (p = oidstrs, i = 0; g[i]; i++) {
(void) strcpy(p, g[i]);
p += strlen(g[i]) + 1;
}
}
void
snmpplugin_init(void)
{
int ret;
LOGINIT();
/*
* Create the tree-builder thread and let it take over
*/
LOGPRINTF("Tree-builder thread being created.\n");
return;
}
}
void
snmpplugin_fini(void)
{
(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)
{
/*
* Initialize SNMP service
*/
LOGPRINTF("Initializing SNMP service.\n");
return ((void *)-1);
}
/*
* Register OID groupings for BULKGET optimizations
*/
LOGPRINTF("Registering OID groups.\n");
register_group(group1, 0);
register_group(group2, 0);
(void) mutex_lock(&rebuild_tree_lock);
for (;;) {
LOGPRINTF("tree_builder: check whether to rebuild subtree\n");
while (rebuild_tree == B_FALSE)
LOGPRINTF("tree_builder: woke up\n");
LOGPRINTF("tree_builder: getting root node\n");
return ((void *)-2);
}
LOGPRINTF("tree_builder: getting existing physplat node\n");
sizeof (PICL_NODE_PHYSPLAT), &old_physplat_root);
LOGPRINTF("tree_builder: building physical-platform\n");
return ((void *)-3);
}
LOGPRINTF("tree_builder: destroying existing nodes\n");
}
LOGPRINTF("tree_builder: attaching new subtree\n");
return ((void *)-4);
}
LOGPRINTF("tree_builder: setting stale_tree to FALSE\n");
(void) rw_wrlock(&stale_tree_rwlp);
(void) rw_unlock(&stale_tree_rwlp);
LOGPRINTF("tree_builder: setting rebuild_tree to FALSE\n");
}
/*NOTREACHED*/
return (NULL);
}
static int
{
int change_time1;
int clr_linkreset = 0;
int ret = 0;
int snmp_syserr = 0;
clr_linkreset = 0;
/*
* Record LastChangeTime before we start building the tree
*/
&change_time1, &snmp_syserr);
if (ret < 0) {
if (snmp_syserr == ECANCELED) {
clr_linkreset = 1;
goto retry;
} else
}
/*
* Create the physical-platform node
*/
if (ret != PICL_SUCCESS)
return (-1);
/*
* Scan entPhysicalTable and build the "physical-platform" subtree
*/
ret = 0;
if (ret == 0) {
}
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.
*/
clr_linkreset = 1;
goto retry;
}
}
/*
* Record LastChangeTime after we're done building the tree
*/
&change_time, &snmp_syserr);
if (ret < 0) {
if (snmp_syserr == ECANCELED) {
clr_linkreset = 1;
goto retry;
} else
}
/*
* 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");
goto retry;
}
/*
* The physplat_nodes table is no longer needed, free it
*/
if (physplat_nodes) {
n_physplat_nodes = 0;
}
return (0);
}
/*
* Destroy all resources that were created during the building
* of the subtree
*/
static void
{
if (physplat_nodes) {
n_physplat_nodes = 0;
}
if (subtree_root) {
(void) ptree_delete_node(subtree_root);
(void) ptree_destroy_node(subtree_root);
}
if (vol_props) {
n_vol_props = 0;
volprop_ndx = 0;
}
}
static picl_nodehdl_t
{
int parent_row;
int sensor_class, sensor_type;
int alarm_type;
int ps_class;
int ret;
/*
* If we've already created this picl node, just return it
*/
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
*/
if (ret < 0 || parent_row <= 0)
else {
}
/*
* Figure out the physical-platform node name from entPhysicalName;
* all rows in the MIB that have a valid entPhysicalIndex should
* have a physical name.
*/
return (NULL);
}
if (ret < 0) {
return (NULL);
}
switch (ent_physclass) {
case SPC_OTHER:
if (ret < 0) {
return (NULL);
}
if (sunplat_physclass == SSPC_ALARM) {
if (ret < 0) {
return (NULL);
}
if (alarm_type == SSAT_VISIBLE) {
} else {
}
} else {
}
break;
case SPC_UNKNOWN:
break;
case SPC_CHASSIS:
break;
case SPC_BACKPLANE:
break;
case SPC_CONTAINER:
break;
case SPC_POWERSUPPLY:
if (ret < 0) {
return (NULL);
}
if (ps_class == SSPSC_BATTERY) {
} else {
}
break;
case SPC_FAN:
break;
case SPC_SENSOR:
if (ret < 0) {
return (NULL);
}
if (ret < 0) {
return (NULL);
}
if (sensor_class == SSSC_NUMERIC) {
if (sensor_type == SSST_TEMPERATURE) {
} else if (sensor_type == SSST_VOLTAGE) {
} else if (sensor_type == SSST_CURRENT) {
} else if (sensor_type == SSST_TACHOMETER) {
} else {
}
} else if (sensor_class == SSSC_BINARY) {
if (sensor_type == SSST_TEMPERATURE) {
} else if (sensor_type == SSST_VOLTAGE) {
} else if (sensor_type == SSST_CURRENT) {
} else if (sensor_type == SSST_TACHOMETER) {
} else if (sensor_type == SSST_PRESENCE) {
} else {
}
} else {
return (NULL);
}
break;
case SPC_MODULE:
break;
case SPC_PORT:
break;
case SPC_STACK:
break;
default:
return (NULL);
}
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
{
picl_nodehdl_t *p;
if (row >= n_physplat_nodes) {
if (p == NULL) {
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;
}
}
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
{
vol_prophdl_t *p;
int count;
if (volprop_ndx == n_vol_props) {
if (p == NULL) {
count * sizeof (vol_prophdl_t));
return;
}
if (vol_props) {
n_vol_props * sizeof (vol_prophdl_t));
}
vol_props = p;
}
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?)
*/
&snmp_syserr);
(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, "
/*
* 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) {
}
volprop_ndx = 0;
n_vol_props = 0;
(void) rw_unlock(&stale_tree_rwlp);
(void) mutex_lock(&rebuild_tree_lock);
(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
{
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.
*/
(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++) {
ndx = i;
break;
}
}
if (i == volprop_ndx) {
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.
*/
&propval, &snmp_syserr);
if (ret < 0) {
return (PICL_FAILURE);
}
case VPT_PLATOPSTATE:
if (propval == SSOS_DISABLED) {
} else if (propval == SSOS_ENABLED) {
} else {
return (PICL_FAILURE);
}
break;
case VPT_NUMSENSOR:
break;
case VPT_BINSENSOR:
if (snmp_syserr == ECANCELED)
return (PICL_FAILURE);
} else {
}
if (snmp_syserr == ECANCELED)
return (PICL_FAILURE);
} else {
}
} else {
return (PICL_FAILURE);
}
break;
case VPT_ALARMSTATE:
} else if (propval == SSAS_STEADY) {
} else if (propval == SSAS_ALTERNATING) {
} else {
}
break;
case VPT_BATTERYSTATUS:
switch (propval) {
case SSBS_OTHER:
break;
case SSBS_FULLYCHARGED:
break;
case SSBS_LOW:
break;
case SSBS_CRITICAL:
break;
case SSBS_CHARGING:
break;
case SSBS_CHARGING_AND_LOW:
break;
case SSBS_CHARGING_AND_HIGH:
break;
break;
case SSBS_UNDEFINED:
break;
case SSBS_PARTIALLY_CHARGED:
break;
case SSBS_UNKNOWN:
default:
break;
}
break;
}
(void) rw_unlock(&stale_tree_rwlp);
return (PICL_SUCCESS);
}
static void
int *snmp_syserr_p)
{
int err;
int val;
if (err == PICL_SUCCESS)
}
}
static void
{
int ret;
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.
*/
} else if (nbytes == 2)
if (bitstr) {
}
if (enabled & LOWER_FATAL) {
}
if (enabled & LOWER_CRITICAL) {
}
if (enabled & LOWER_NON_CRITICAL) {
}
if (enabled & UPPER_NON_CRITICAL) {
}
if (enabled & UPPER_CRITICAL) {
}
if (enabled & UPPER_FATAL) {
}
}
static char *
{
char *p;
int ret;
row, &p, snmp_syserr_p);
if ((ret == 0) && p && *p) {
slott = p;
*p = 0;
} else {
if (p) {
free(p);
}
}
return (slott);
}
/*
* Create and add the specified volatile property
*/
static int
{
int err;
if (err != PICL_SUCCESS) {
return (err);
}
if (err != PICL_SUCCESS) {
return (err);
}
if (propp)
return (PICL_SUCCESS);
}
/*
* Add the specified string property to the node
*/
static int
{
int err;
if (err != PICL_SUCCESS) {
return (err);
}
if (err != PICL_SUCCESS) {
return (err);
}
return (PICL_SUCCESS);
}
/*
* Add the specified void property to the node
*/
static int
{
int err;
if (err != PICL_SUCCESS) {
return (err);
}
if (err != PICL_SUCCESS) {
return (err);
}
return (PICL_SUCCESS);
}
static int
{
int err;
PICL_PTYPE_INT, PICL_READ, sizeof (int),
if (err != PICL_SUCCESS) {
return (err);
}
if (err != PICL_SUCCESS) {
return (err);
}
return (PICL_SUCCESS);
}
static void
{
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:
(void) add_string_prop(nodeh,
free((void *) serial_num);
}
break;
case PP_SLOT_TYPE:
(void) add_string_prop(nodeh,
} else {
(void) add_string_prop(nodeh,
}
break;
case PP_STATE:
if (ret == PICL_SUCCESS) {
}
break;
case PP_OPSTATUS:
if (ret == PICL_SUCCESS) {
}
break;
case PP_BATT_STATUS:
if (ret == PICL_SUCCESS) {
}
break;
case PP_TEMPERATURE:
if (ret == PICL_SUCCESS) {
row, VPT_NUMSENSOR);
}
break;
case PP_VOLTAGE:
if (ret == PICL_SUCCESS) {
row, VPT_NUMSENSOR);
}
break;
case PP_CURRENT:
if (ret == PICL_SUCCESS) {
row, VPT_NUMSENSOR);
}
break;
case PP_SPEED:
if (ret == PICL_SUCCESS) {
row, VPT_NUMSENSOR);
}
break;
case PP_SENSOR_VALUE:
if (ret == PICL_SUCCESS) {
row, VPT_NUMSENSOR);
}
break;
case PP_CONDITION:
if (ret == PICL_SUCCESS) {
row, VPT_BINSENSOR);
}
break;
case PP_EXPECTED:
if (ret == PICL_SUCCESS) {
row, VPT_BINSENSOR);
}
break;
case PP_REPLACEABLE:
break;
case PP_HOTSWAPPABLE:
break;
case PP_IS_FRU:
&val, snmp_syserr_p);
break;
case PP_HW_REVISION:
(void) add_string_prop(nodeh,
free((void *) hw_revision);
}
break;
case PP_FW_REVISION:
(void) add_string_prop(nodeh,
free((void *) fw_revision);
}
break;
case PP_MFG_NAME:
(void) add_string_prop(nodeh,
}
break;
case PP_MODEL_NAME:
(void) add_string_prop(nodeh,
free((void *) model_name);
}
break;
case PP_DESCRIPTION:
(void) add_string_prop(nodeh,
free((void *) phys_descr);
}
break;
case PP_LABEL:
break;
case PP_BASE_UNITS:
(void) add_string_prop(nodeh,
}
break;
case PP_RATE_UNITS:
(void) add_string_prop(nodeh,
}
break;
case PP_EXPONENT:
if (ret == 0)
break;
}
}
/*VARARGS2*/
static void
{
}
#ifdef SNMPPLUGIN_DEBUG
static void
snmpplugin_log_init(void)
{
}
static void
snmpplugin_log(const char *fmt, ...)
{
(void) mutex_lock(&snmpplugin_dbuf_lock);
(void) mutex_unlock(&snmpplugin_dbuf_lock);
}
static void
snmpplugin_log_append(void)
{
int len;
if ((snmpplugin_dbuf_curp + len) >=
if (snmpplugin_dbuf == NULL) {
(void) mutex_unlock(&snmpplugin_dbuf_lock);
return;
}
}
}
static void
snmpplugin_dbuf_realloc(void)
{
char *p;
return;
}
if (snmpplugin_dbuf) {
}
snmpplugin_dbuf = p;
}
#endif