1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*
28 * The SNMP picl plugin connects to the agent on the SP and creates
29 * and populates the /physical-platform subtree in picl tree for use
30 * by picl consumers.
31 */
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <syslog.h>
37#include <stdarg.h>
38#include <libgen.h>
39#include <libintl.h>
40#include <thread.h>
41#include <synch.h>
42#include <errno.h>
43#include <time.h>
44#include <signal.h>
45
46#include <picldefs.h>
47#include <picl.h>
48#include <picltree.h>
49
50#include "picloids.h"
51#include "libpiclsnmp.h"
52#include "snmpplugin.h"
53
54#pragma init(snmpplugin_register)	/* place in .init section */
55
56picld_plugin_reg_t snmpplugin_reg = {
57	PICLD_PLUGIN_VERSION_1,
58	PICLD_PLUGIN_NON_CRITICAL,
59	"snmp_plugin",
60	snmpplugin_init,
61	snmpplugin_fini
62};
63
64static picl_snmphdl_t	hdl;
65
66/*
67 * The stale_tree_rwlp protects the stale_xxx vars. The 'stale_tree' flag
68 * and the 'rebuild_tree' flag below are both initialized to B_TRUE to
69 * let the tree_builder() thread build the initial tree without blocking.
70 */
71static rwlock_t		stale_tree_rwlp;
72static boolean_t	stale_tree = B_TRUE;
73
74/*
75 * vol_props, volprop_ndx and n_vol_props are protected by the stale_tree
76 * flag.  They are read only when the stale_tree flag is B_FALSE and written
77 * to only when the flag is B_TRUE.
78 *
79 * The change_time (last changed time) is read by only one thread at a
80 * time when stale_tree is B_FALSE (protected by stale_tree_rwlp).  It is
81 * written by only one thread (the tree builder) when stale_tree is B_TRUE.
82 *
83 * Note that strictly speaking, change_time should be uint_t (timeticks32).
84 * But keeping it as int is fine, since we don't do any arithmetic on it
85 * except equality check.
86 */
87static vol_prophdl_t	*vol_props = NULL;
88static int		volprop_ndx = 0, n_vol_props = 0;
89static int		change_time = 0;
90static time_t		change_time_check;
91
92/*
93 * The rebuild_tree_lock and cv are used by the tree builder thread.
94 * rebuild_tree has to be initialized to B_TRUE to let the tree_builder
95 * do the first build without blocking.
96 */
97static mutex_t		rebuild_tree_lock;
98static cond_t		rebuild_tree_cv;
99static boolean_t	rebuild_tree = B_TRUE;
100static boolean_t	tree_builder_thr_exit = B_FALSE;
101static thread_t		tree_builder_thr_id;
102
103/*
104 * The cache_refresh thread periodically queries the snmp cache refresh work
105 * queue and processes jobs from it to keep cache entries from expiring.  It
106 * attempts to run in cycles of CACHE_REFRESH_CYCLE seconds each, first
107 * processing cache refresh jobs and then sleeping for the remainder of the
108 * cycle once the next refresh job expiration is at least
109 * CACHE_REFRESH_MIN_WINDOW seconds in the future.
110 *
111 * NOTE: By using a thread to keep the SNMP cache refreshed in the background,
112 * we are both adding load to the system and reducing the system's ability to
113 * operate in power-saving mode when there is minimal load.  While these
114 * tradeoffs are acceptable at this time in light of customer concerns about
115 * performance, it may be desirable in the future to move this work into the
116 * firmware.  Also, while the current cycle times performed well on the largest
117 * sun4v config currently available (Batoka), they may need to be revisited for
118 * future systems if the number of sensors increases significantly.
119 */
120#define	CACHE_REFRESH_CYCLE		60
121#define	CACHE_REFRESH_MIN_WINDOW	75
122static mutex_t		cache_refresh_lock;
123static cond_t		cache_refresh_cv;
124static boolean_t	cache_refresh_thr_exit = B_FALSE;
125static thread_t		cache_refresh_thr_id;
126
127/*
128 * These two should really not be global
129 */
130static picl_nodehdl_t	*physplat_nodes = NULL;
131static int		n_physplat_nodes = 0;
132
133static char *group1[] = {
134	OID_entPhysicalDescr,
135	OID_entPhysicalContainedIn,
136	OID_entPhysicalClass,
137	OID_entPhysicalName,
138	OID_entPhysicalHardwareRev,
139	OID_entPhysicalFirmwareRev,
140	OID_entPhysicalSerialNum,
141	OID_entPhysicalMfgName,
142	OID_entPhysicalModelName,
143	OID_entPhysicalIsFRU,
144	0
145};
146
147static char *group2[] = {
148	OID_sunPlatEquipmentHolderAcceptableTypes,
149	OID_sunPlatCircuitPackReplaceable,
150	OID_sunPlatCircuitPackHotSwappable,
151	OID_sunPlatPhysicalClass,
152	OID_sunPlatSensorClass,
153	OID_sunPlatSensorType,
154	OID_sunPlatAlarmType,
155	OID_sunPlatPowerSupplyClass,
156	0
157};
158
159static char *group3[] = {
160	OID_sunPlatNumericSensorEnabledThresholds,
161	OID_sunPlatNumericSensorBaseUnits,
162	OID_sunPlatNumericSensorRateUnits,
163	0
164};
165
166static char *group4[] = {
167	OID_sunPlatBinarySensorInterpretTrue,
168	OID_sunPlatBinarySensorInterpretFalse,
169	0
170};
171
172static char *volgroup1[] = {
173	OID_sunPlatBinarySensorCurrent,
174	OID_sunPlatBinarySensorExpected,
175	0
176};
177
178static char *volgroup2[] = {
179	OID_sunPlatNumericSensorExponent,
180	OID_sunPlatNumericSensorCurrent,
181	OID_sunPlatNumericSensorLowerThresholdFatal,
182	OID_sunPlatNumericSensorLowerThresholdCritical,
183	OID_sunPlatNumericSensorLowerThresholdNonCritical,
184	OID_sunPlatNumericSensorUpperThresholdNonCritical,
185	OID_sunPlatNumericSensorUpperThresholdCritical,
186	OID_sunPlatNumericSensorUpperThresholdFatal,
187	0
188};
189
190static char *volgroup3[] = {
191	OID_sunPlatEquipmentOperationalState,
192	0
193};
194
195static char *volgroup4[] = {
196	OID_sunPlatAlarmState,
197	0
198};
199
200static char *volgroup5[] = {
201	OID_sunPlatBatteryStatus,
202	0
203};
204
205/*
206 * The following two items must match the Sun Platform MIB specification
207 * in their indices and values.
208 */
209static char *sensor_baseunits[] = {
210	"", "other", "unknown", "degC", "degF", "degK", "volts", "amps",
211	"watts", "joules", "coulombs", "va", "nits", "lumens", "lux",
212	"candelas", "kPa", "psi", "newtons", "cfm", "rpm", "hertz",
213	"seconds", "minutes", "hours", "days", "weeks", "mils", "inches",
214	"feet", "cubicInches", "cubicFeet", "meters", "cubicCentimeters",
215	"cubicMeters", "liters", "fluidOunces", "radians", "steradians",
216	"revolutions", "cycles", "gravities", "ounces", "pounds", "footPounds",
217	"ounceInches", "gauss", "gilberts", "henries", "farads", "ohms",
218	"siemens", "moles", "becquerels", "ppm", "decibels", "dBA", "dbC",
219	"grays", "sieverts", "colorTemperatureDegK", "bits", "bytes", "words",
220	"doubleWords", "quadWords", "percentage"
221};
222static const int n_baseunits = sizeof (sensor_baseunits) / sizeof (char *);
223
224static char *sensor_rateunits[] = {
225	"",
226	"none",
227	"perMicroSecond",
228	"perMilliSecond",
229	"perSecond",
230	"perMinute",
231	"perHour",
232	"perDay",
233	"perWeek",
234	"perMonth",
235	"perYear"
236};
237static const int n_rateunits = sizeof (sensor_rateunits) / sizeof (char *);
238
239/*
240 * Local declarations
241 */
242static void snmpplugin_register(void);
243static void register_group(char **g, int is_volatile);
244static void *tree_builder(void *arg);
245static int build_physplat(picl_nodehdl_t *subtree_rootp);
246static void free_resources(picl_nodehdl_t subtree_root);
247
248static picl_nodehdl_t make_node(picl_nodehdl_t subtree_root, int row,
249    int *snmp_syserr_p);
250static void save_nodeh(picl_nodehdl_t nodeh, int row);
251static picl_nodehdl_t lookup_nodeh(int row);
252
253static void save_volprop(picl_prophdl_t prop, char *oidstr, int row,
254    int proptype);
255static void check_for_stale_data(boolean_t nocache);
256static int read_volprop(ptree_rarg_t *parg, void *buf);
257
258static void threshold(picl_nodehdl_t node, char *oidstr, int row,
259    char *propname, int *snmp_syserr_p);
260static void add_thresholds(picl_nodehdl_t node, int row, int *snmp_syserr_p);
261
262static char *get_slot_type(int row, int *snmp_syserr_p);
263static int add_volatile_prop(picl_nodehdl_t nodeh, char *name,
264    int type, int access, int size, int (*rdfunc)(ptree_rarg_t *, void *),
265    int (*wrfunc)(ptree_warg_t *, const void *), picl_prophdl_t *propp);
266static int add_string_prop(picl_nodehdl_t node, char *propname, char *propval);
267static int add_void_prop(picl_nodehdl_t node, char *propname);
268static void add_prop(picl_nodehdl_t nodeh, picl_prophdl_t *php, char *label,
269    int row, sp_propid_t pp, int *snmp_syserr_p);
270
271static void *cache_refresher(void *arg);
272static void cache_refresher_fini(void);
273
274static void log_msg(int pri, const char *fmt, ...);
275
276#ifdef SNMPPLUGIN_DEBUG
277static mutex_t	snmpplugin_dbuf_lock;
278static char	*snmpplugin_dbuf = NULL;
279static char	*snmpplugin_dbuf_curp = NULL;
280static int	snmpplugin_dbuf_sz = 0;
281static int	snmpplugin_dbuf_overflow = 0;
282static char	snmpplugin_lbuf[SNMPPLUGIN_DMAX_LINE];
283
284static void	snmpplugin_log_init(void);
285static void	snmpplugin_log(const char *fmt, ...);
286static void	snmpplugin_log_append(void);
287static void	snmpplugin_dbuf_realloc(void);
288#endif
289
290static void
291snmpplugin_register(void)
292{
293	(void) picld_plugin_register(&snmpplugin_reg);
294}
295
296static void
297register_group(char **g, int is_volatile)
298{
299	int	i, len = 0;
300	int	n_oids;
301	char	*p, *oidstrs;
302
303	for (i = 0; g[i]; i++)
304		len += strlen(g[i]) + 1;
305	n_oids = i;
306
307	if ((oidstrs = (char *)calloc(1, len)) == NULL)
308		return;
309
310	for (p = oidstrs, i = 0; g[i]; i++) {
311		(void) strcpy(p, g[i]);
312		p += strlen(g[i]) + 1;
313	}
314
315	snmp_register_group(hdl, oidstrs, n_oids, is_volatile);
316	free(oidstrs);
317}
318
319void
320snmpplugin_init(void)
321{
322	int		ret;
323
324	(void) mutex_init(&rebuild_tree_lock, USYNC_THREAD, NULL);
325	(void) cond_init(&rebuild_tree_cv, USYNC_THREAD, NULL);
326	(void) rwlock_init(&stale_tree_rwlp, USYNC_THREAD, NULL);
327	tree_builder_thr_exit = B_FALSE;
328
329	LOGINIT();
330
331	/*
332	 * Create the tree-builder thread and let it take over
333	 */
334	LOGPRINTF("Tree-builder thread being created.\n");
335	if ((ret = thr_create(NULL, NULL, tree_builder, NULL,
336	    THR_BOUND, &tree_builder_thr_id)) < 0) {
337		log_msg(LOG_ERR, SNMPP_CANT_CREATE_TREE_BUILDER, ret);
338		snmp_fini(hdl);
339		hdl = NULL;
340		(void) rwlock_destroy(&stale_tree_rwlp);
341		(void) cond_destroy(&rebuild_tree_cv);
342		(void) mutex_destroy(&rebuild_tree_lock);
343		tree_builder_thr_exit = B_TRUE;
344
345		return;
346	}
347
348	/*
349	 * While the cache refresher thread does improve performance, it is not
350	 * integral to the proper function of the plugin.  If we fail to create
351	 * the thread for some reason, we will simply continue without
352	 * refreshing.
353	 */
354	(void) mutex_init(&cache_refresh_lock, USYNC_THREAD, NULL);
355	(void) cond_init(&cache_refresh_cv, USYNC_THREAD, NULL);
356	cache_refresh_thr_exit = B_FALSE;
357
358	LOGPRINTF("Cache refresher thread being created.\n");
359	if (thr_create(NULL, NULL, cache_refresher, NULL, THR_BOUND,
360	    &cache_refresh_thr_id) < 0) {
361		(void) cond_destroy(&cache_refresh_cv);
362		(void) mutex_destroy(&cache_refresh_lock);
363		cache_refresh_thr_exit = B_TRUE;
364	}
365}
366
367void
368snmpplugin_fini(void)
369{
370
371	if (tree_builder_thr_exit == B_TRUE)
372		return;
373
374	/*
375	 * Make reads of volatile properties return PICL_PROPUNAVAILABLE
376	 * since we're about to recycle the plug-in.  No need to worry
377	 * about removing /physical-platform since tree_builder() will
378	 * take care of recycling it for us.
379	 */
380	(void) rw_wrlock(&stale_tree_rwlp);
381	stale_tree = B_TRUE;
382	if (vol_props) {
383		free(vol_props);
384	}
385	vol_props = NULL;
386	volprop_ndx = 0;
387	n_vol_props = 0;
388	(void) rw_unlock(&stale_tree_rwlp);
389
390	/* clean up the cache_refresher thread and structures */
391	cache_refresher_fini();
392
393	/* wake up the tree_builder thread, tell it to exit */
394	(void) mutex_lock(&rebuild_tree_lock);
395	rebuild_tree = B_TRUE;
396	tree_builder_thr_exit = B_TRUE;
397	(void) cond_signal(&rebuild_tree_cv);
398	(void) mutex_unlock(&rebuild_tree_lock);
399
400	/* send SIGUSR1 to get tree_builder out of a blocked system call */
401	(void) thr_kill(tree_builder_thr_id, SIGUSR1);
402
403	/* reap the thread */
404	(void) thr_join(tree_builder_thr_id, NULL, NULL);
405
406	/* close the channel */
407	if (hdl != NULL) {
408		snmp_fini(hdl);
409		hdl = NULL;
410	}
411
412	/* finish cleanup... */
413	(void) rwlock_destroy(&stale_tree_rwlp);
414	(void) cond_destroy(&rebuild_tree_cv);
415	(void) mutex_destroy(&rebuild_tree_lock);
416}
417
418/*ARGSUSED*/
419static void
420usr1_handler(int sig, siginfo_t *siginfo, void *sigctx)
421{
422	/*
423	 * Nothing to do here.
424	 * The act of catching the signal causes any cond_wait() or blocked
425	 * system call to return EINTR. This is used to trigger early exit from
426	 * the tree builder thread which may be blocked in snmp_init. More work
427	 * would be required to allow early exit if the tree builder thread is
428	 * already in its main processing loop and not blocked in cond_wait.
429	 */
430}
431
432/*ARGSUSED*/
433static void *
434tree_builder(void *arg)
435{
436	int		ret, rv;
437	picl_nodehdl_t	root_node;
438	picl_nodehdl_t	physplat_root;
439	picl_nodehdl_t	old_physplat_root;
440	struct sigaction	act;
441
442	/*
443	 * catch SIGUSR1 to allow early exit from snmp_init which may block
444	 * indefinitely in a guest domain.
445	 */
446	act.sa_sigaction = usr1_handler;
447	(void) sigemptyset(&act.sa_mask);
448	act.sa_flags = 0;
449	if (sigaction(SIGUSR1, &act, NULL) == -1) {
450		syslog(LOG_ERR, SIGACT_FAILED, strsignal(SIGUSR1),
451		    strerror(errno));
452	}
453
454	/*
455	 * Initialize SNMP service
456	 */
457	LOGPRINTF("Initializing SNMP service.\n");
458	if ((hdl = snmp_init()) == NULL) {
459		log_msg(LOG_ERR, SNMPP_CANT_INIT);
460		return ((void *)-1);
461	}
462
463	/*
464	 * Register OID groupings for BULKGET optimizations
465	 */
466	LOGPRINTF("Registering OID groups.\n");
467	register_group(group1, 0);
468	register_group(group2, 0);
469	register_group(group3, 0);
470	register_group(group4, 0);
471	register_group(volgroup1, 1);
472	register_group(volgroup2, 1);
473	register_group(volgroup3, 1);
474	register_group(volgroup4, 1);
475	register_group(volgroup5, 1);
476
477	(void) mutex_lock(&rebuild_tree_lock);
478
479	for (;;) {
480		LOGPRINTF("tree_builder: check whether to rebuild subtree\n");
481		while (rebuild_tree == B_FALSE)
482			(void) cond_wait(&rebuild_tree_cv, &rebuild_tree_lock);
483
484		LOGPRINTF("tree_builder: woke up\n");
485
486		if (tree_builder_thr_exit == B_TRUE) {
487			(void) mutex_unlock(&rebuild_tree_lock);
488			LOGPRINTF("tree_builder: time to exit\n");
489			return (NULL);
490		}
491
492		old_physplat_root = NULL;
493		physplat_root = NULL;
494
495		LOGPRINTF("tree_builder: getting root node\n");
496		if ((ret = ptree_get_root(&root_node)) != PICL_SUCCESS) {
497			(void) mutex_unlock(&rebuild_tree_lock);
498			log_msg(LOG_ERR, SNMPP_NO_ROOT, ret);
499			return ((void *)-2);
500		}
501
502		LOGPRINTF("tree_builder: getting existing physplat node\n");
503		rv = ptree_find_node(root_node, PICL_PROP_NAME,
504		    PICL_PTYPE_CHARSTRING, PICL_NODE_PHYSPLAT,
505		    sizeof (PICL_NODE_PHYSPLAT), &old_physplat_root);
506
507		LOGPRINTF("tree_builder: building physical-platform\n");
508		if ((ret = build_physplat(&physplat_root)) < 0) {
509			(void) mutex_unlock(&rebuild_tree_lock);
510			log_msg(LOG_ERR, SNMPP_CANT_CREATE_PHYSPLAT, ret);
511			cache_refresher_fini();
512			snmp_fini(hdl);
513			hdl = NULL;
514			return ((void *)-3);
515		}
516
517		if (rv == PICL_SUCCESS && old_physplat_root != NULL) {
518			LOGPRINTF("tree_builder: destroying existing nodes\n");
519			ptree_delete_node(old_physplat_root);
520			ptree_destroy_node(old_physplat_root);
521		}
522
523		LOGPRINTF("tree_builder: attaching new subtree\n");
524		if ((ret = ptree_add_node(root_node, physplat_root)) < 0) {
525			(void) mutex_unlock(&rebuild_tree_lock);
526			free_resources(physplat_root);
527			log_msg(LOG_ERR, SNMPP_CANT_CREATE_PHYSPLAT, ret);
528			cache_refresher_fini();
529			snmp_fini(hdl);
530			hdl = NULL;
531			return ((void *)-4);
532		}
533
534		LOGPRINTF("tree_builder: setting stale_tree to FALSE\n");
535		(void) rw_wrlock(&stale_tree_rwlp);
536		stale_tree = B_FALSE;
537		(void) rw_unlock(&stale_tree_rwlp);
538
539		LOGPRINTF("tree_builder: setting rebuild_tree to FALSE\n");
540		rebuild_tree = B_FALSE;
541	}
542
543	/*NOTREACHED*/
544	return (NULL);
545}
546
547static int
548build_physplat(picl_nodehdl_t *subtree_rootp)
549{
550	int	change_time1;
551	int	row, nxtrow;
552	int	clr_linkreset = 0;
553	int	ret = 0;
554	int	snmp_syserr = 0;
555
556retry:
557	(void) snmp_reinit(hdl, clr_linkreset);
558	clr_linkreset = 0;
559
560	/*
561	 * Record LastChangeTime before we start building the tree
562	 */
563	ret = snmp_get_int(hdl, OID_entLastChangeTime, 0,
564	    &change_time1, &snmp_syserr);
565	if (ret < 0) {
566		if (snmp_syserr == ECANCELED) {
567			LOGPRINTF(SNMPP_LINK_RESET);
568			clr_linkreset = 1;
569			goto retry;
570		}
571		log_msg(LOG_WARNING, SNMPP_CANT_FETCH_OBJECT_VAL,
572		    snmp_syserr ? snmp_syserr : ret, OID_entLastChangeTime, 0);
573	}
574
575	/*
576	 * Create the physical-platform node
577	 */
578	ret = ptree_create_node(PICL_NODE_PHYSPLAT, PICL_CLASS_PICL,
579	    subtree_rootp);
580	if (ret != PICL_SUCCESS)
581		return (-1);
582
583	/*
584	 * Scan entPhysicalTable and build the "physical-platform" subtree
585	 */
586	ret = 0;
587	for (row = -1; ret == 0; row = nxtrow) {
588		ret = snmp_get_nextrow(hdl, OID_entPhysicalDescr,
589		    row, &nxtrow, &snmp_syserr);
590		if (ret == 0)
591			(void) make_node(*subtree_rootp, nxtrow, &snmp_syserr);
592		switch (snmp_syserr) {
593		case ECANCELED:
594			/*
595			 * If we get this error, a link reset must've
596			 * happened and we need to throw away everything
597			 * we have now and rebuild the tree again.
598			 */
599			log_msg(LOG_WARNING, SNMPP_LINK_RESET);
600			free_resources(*subtree_rootp);
601			clr_linkreset = 1;
602			goto retry;
603			/*NOTREACHED*/
604			break;
605		case ENOSPC:	/* end of MIB */
606			LOGPRINTF("build_physplat: end of MIB\n");
607			break;
608		case ENOENT:	/* end of table */
609			LOGPRINTF("build_physplat: end of table\n");
610			break;
611		default:
612			/*
613			 * make_node() will print messages so don't
614			 * repeat that exercise here.
615			 */
616			if (ret == -1) {
617				log_msg(LOG_WARNING,
618				    SNMPP_CANT_FETCH_OBJECT_VAL,
619				    snmp_syserr ? snmp_syserr : ret,
620				    OID_entPhysicalDescr, row);
621			}
622		}
623	}
624
625	/*
626	 * Record LastChangeTime after we're done building the tree
627	 */
628	ret = snmp_get_int(hdl, OID_entLastChangeTime, 0,
629	    &change_time, &snmp_syserr);
630	if (ret < 0) {
631		if (snmp_syserr == ECANCELED) {
632			log_msg(LOG_WARNING, SNMPP_LINK_RESET);
633			free_resources(*subtree_rootp);
634			clr_linkreset = 1;
635			goto retry;
636		} else
637			log_msg(LOG_WARNING, SNMPP_CANT_FETCH_OBJECT_VAL,
638			    snmp_syserr ? snmp_syserr : ret,
639			    OID_entLastChangeTime, row);
640	}
641
642	/*
643	 * If they don't match, some hotplugging must've happened,
644	 * free resources we've created and still holding, then go
645	 * back and retry
646	 */
647	if (change_time != change_time1) {
648		LOGPRINTF("build_physplat: entLastChangeTime has changed!\n");
649		free_resources(*subtree_rootp);
650		change_time1 = change_time;
651		goto retry;
652	}
653
654	/*
655	 * The physplat_nodes table is no longer needed, free it
656	 */
657	if (physplat_nodes) {
658		free(physplat_nodes);
659		physplat_nodes = NULL;
660		n_physplat_nodes = 0;
661	}
662
663	return (0);
664}
665
666/*
667 * Destroy all resources that were created during the building
668 * of the subtree
669 */
670static void
671free_resources(picl_nodehdl_t subtree_root)
672{
673	if (physplat_nodes) {
674		free(physplat_nodes);
675		physplat_nodes = NULL;
676		n_physplat_nodes = 0;
677	}
678
679	if (subtree_root) {
680		(void) ptree_delete_node(subtree_root);
681		(void) ptree_destroy_node(subtree_root);
682	}
683
684	if (vol_props) {
685		free(vol_props);
686		vol_props = NULL;
687		n_vol_props = 0;
688		volprop_ndx = 0;
689	}
690}
691
692static picl_nodehdl_t
693make_node(picl_nodehdl_t subtree_root, int row, int *snmp_syserr_p)
694{
695	picl_nodehdl_t	nodeh, parenth;
696	picl_prophdl_t	proph;
697	char	*phys_name, *node_name;
698	int	parent_row;
699	int	ent_physclass, sunplat_physclass;
700	int	sensor_class, sensor_type;
701	int	alarm_type;
702	int	ps_class;
703	int	ret;
704
705	/*
706	 * If we've already created this picl node, just return it
707	 */
708	if ((nodeh = lookup_nodeh(row)) != NULL)
709		return (nodeh);
710
711	/*
712	 * If we are creating it only now, make sure we have the parent
713	 * created first; if there's no parent, then parent it to the
714	 * subtree's root node
715	 */
716	ret = snmp_get_int(hdl, OID_entPhysicalContainedIn, row,
717	    &parent_row, snmp_syserr_p);
718	CHECK_LINKRESET(snmp_syserr_p, NULL)
719	if (ret < 0 || parent_row <= 0)
720		parenth = subtree_root;
721	else {
722		parenth = make_node(subtree_root, parent_row, snmp_syserr_p);
723		CHECK_LINKRESET(snmp_syserr_p, NULL)
724		if (parenth == NULL)
725			parenth = subtree_root;
726	}
727
728	/*
729	 * Figure out the physical-platform node name from entPhysicalName;
730	 * all rows in the MIB that have a valid entPhysicalIndex should
731	 * have a physical name.
732	 */
733	ret = snmp_get_str(hdl, OID_entPhysicalName, row,
734	    &phys_name, snmp_syserr_p);
735	CHECK_LINKRESET(snmp_syserr_p, NULL)
736	if (ret < 0 || phys_name == NULL) {
737		log_msg(LOG_WARNING, SNMPP_NO_ENTPHYSNAME, row);
738		return (NULL);
739	}
740
741	node_name = basename(phys_name);
742
743	ret = snmp_get_int(hdl, OID_entPhysicalClass, row,
744	    &ent_physclass, snmp_syserr_p);
745	CHECK_LINKRESET(snmp_syserr_p, NULL)
746	if (ret < 0) {
747		log_msg(LOG_WARNING, SNMPP_CANT_FETCH_OBJECT_VAL,
748		    *snmp_syserr_p ? *snmp_syserr_p : ret,
749		    OID_entPhysicalClass, row);
750		free(phys_name);
751		return (NULL);
752	}
753
754	switch (ent_physclass) {
755	case SPC_OTHER:
756		ret = snmp_get_int(hdl, OID_sunPlatPhysicalClass, row,
757		    &sunplat_physclass, snmp_syserr_p);
758		CHECK_LINKRESET(snmp_syserr_p, NULL)
759		if (ret < 0) {
760			log_msg(LOG_WARNING, SNMPP_CANT_FETCH_OBJECT_VAL,
761			    *snmp_syserr_p ? *snmp_syserr_p : ret,
762			    OID_sunPlatPhysicalClass, row);
763			free(phys_name);
764			return (NULL);
765		}
766
767		if (sunplat_physclass == SSPC_ALARM) {
768			ret = snmp_get_int(hdl, OID_sunPlatAlarmType,
769			    row, &alarm_type, snmp_syserr_p);
770			CHECK_LINKRESET(snmp_syserr_p, NULL)
771			if (ret < 0) {
772				log_msg(LOG_WARNING,
773				    SNMPP_CANT_FETCH_OBJECT_VAL,
774				    *snmp_syserr_p ? *snmp_syserr_p : ret,
775				    OID_sunPlatAlarmType, row);
776				free(phys_name);
777				return (NULL);
778			}
779
780			if (alarm_type == SSAT_VISIBLE) {
781				ADD_NODE(PICL_CLASS_LED)
782			} else {
783				ADD_NODE(PICL_CLASS_ALARM)
784			}
785
786			add_prop(nodeh, &proph, node_name, row, PP_STATE,
787			    snmp_syserr_p);
788			CHECK_LINKRESET(snmp_syserr_p, NULL)
789		} else {
790			ADD_NODE(PICL_CLASS_OTHER)
791		}
792
793		add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
794		    snmp_syserr_p);
795		CHECK_LINKRESET(snmp_syserr_p, NULL)
796		break;
797
798	case SPC_UNKNOWN:
799		ADD_NODE(PICL_CLASS_UNKNOWN)
800		break;
801
802	case SPC_CHASSIS:
803		ADD_NODE(PICL_CLASS_CHASSIS)
804		add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
805		    snmp_syserr_p);
806		CHECK_LINKRESET(snmp_syserr_p, NULL)
807		break;
808
809	case SPC_BACKPLANE:
810		ADD_NODE(PICL_CLASS_BACKPLANE)
811		add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
812		    snmp_syserr_p);
813		CHECK_LINKRESET(snmp_syserr_p, NULL)
814		break;
815
816	case SPC_CONTAINER:
817		ADD_NODE(PICL_CLASS_CONTAINER)
818
819		add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
820		    snmp_syserr_p);
821		CHECK_LINKRESET(snmp_syserr_p, NULL)
822
823		add_prop(nodeh, &proph, node_name, row, PP_SLOT_TYPE,
824		    snmp_syserr_p);
825		CHECK_LINKRESET(snmp_syserr_p, NULL)
826		break;
827
828	case SPC_POWERSUPPLY:
829		ret = snmp_get_int(hdl, OID_sunPlatPowerSupplyClass,
830		    row, &ps_class, snmp_syserr_p);
831		CHECK_LINKRESET(snmp_syserr_p, NULL)
832		if (ret < 0) {
833			log_msg(LOG_WARNING, SNMPP_CANT_FETCH_OBJECT_VAL,
834			    *snmp_syserr_p ? *snmp_syserr_p : ret,
835			    OID_sunPlatPowerSupplyClass, row);
836			free(phys_name);
837			return (NULL);
838		}
839
840		if (ps_class == SSPSC_BATTERY) {
841			ADD_NODE(PICL_CLASS_BATTERY)
842			add_prop(nodeh, &proph, node_name, row,
843			    PP_BATT_STATUS, snmp_syserr_p);
844			CHECK_LINKRESET(snmp_syserr_p, NULL)
845		} else {
846			ADD_NODE(PICL_CLASS_POWERSUPPLY)
847		}
848		add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
849		    snmp_syserr_p);
850		CHECK_LINKRESET(snmp_syserr_p, NULL)
851		break;
852
853	case SPC_FAN:
854		ADD_NODE(PICL_CLASS_FAN)
855		add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
856		    snmp_syserr_p);
857		CHECK_LINKRESET(snmp_syserr_p, NULL)
858		break;
859
860	case SPC_SENSOR:
861		ret = snmp_get_int(hdl, OID_sunPlatSensorClass,
862		    row, &sensor_class, snmp_syserr_p);
863		CHECK_LINKRESET(snmp_syserr_p, NULL)
864		if (ret < 0) {
865			log_msg(LOG_WARNING, SNMPP_CANT_FETCH_OBJECT_VAL,
866			    *snmp_syserr_p ? *snmp_syserr_p : ret,
867			    OID_sunPlatSensorClass, row);
868			free(phys_name);
869			return (NULL);
870		}
871
872		ret = snmp_get_int(hdl, OID_sunPlatSensorType,
873		    row, &sensor_type, snmp_syserr_p);
874		CHECK_LINKRESET(snmp_syserr_p, NULL)
875		if (ret < 0) {
876			log_msg(LOG_WARNING, SNMPP_CANT_FETCH_OBJECT_VAL,
877			    *snmp_syserr_p ? *snmp_syserr_p : ret,
878			    OID_sunPlatSensorType, row);
879			free(phys_name);
880			return (NULL);
881		}
882
883		if (sensor_class == SSSC_NUMERIC) {
884			if (sensor_type == SSST_TEMPERATURE) {
885				ADD_NODE(PICL_CLASS_TEMPERATURE_SENSOR)
886				add_prop(nodeh, &proph, node_name, row,
887				    PP_TEMPERATURE, snmp_syserr_p);
888			} else if (sensor_type == SSST_VOLTAGE) {
889				ADD_NODE(PICL_CLASS_VOLTAGE_SENSOR)
890				add_prop(nodeh, &proph, node_name, row,
891				    PP_VOLTAGE, snmp_syserr_p);
892			} else if (sensor_type == SSST_CURRENT) {
893				ADD_NODE(PICL_CLASS_CURRENT_SENSOR)
894				add_prop(nodeh, &proph, node_name, row,
895				    PP_CURRENT, snmp_syserr_p);
896			} else if (sensor_type == SSST_TACHOMETER) {
897				ADD_NODE(PICL_CLASS_RPM_SENSOR)
898				add_prop(nodeh, &proph, node_name, row,
899				    PP_SPEED, snmp_syserr_p);
900			} else {
901				ADD_NODE(PICL_CLASS_SENSOR)
902				add_prop(nodeh, &proph, node_name, row,
903				    PP_SENSOR_VALUE, snmp_syserr_p);
904			}
905			CHECK_LINKRESET(snmp_syserr_p, NULL)
906
907			add_prop(nodeh, &proph, node_name, row,
908			    PP_OPSTATUS, snmp_syserr_p);
909			CHECK_LINKRESET(snmp_syserr_p, NULL)
910
911			add_prop(nodeh, &proph, node_name, row,
912			    PP_BASE_UNITS, snmp_syserr_p);
913			CHECK_LINKRESET(snmp_syserr_p, NULL)
914
915			add_prop(nodeh, &proph, node_name, row,
916			    PP_EXPONENT, snmp_syserr_p);
917			CHECK_LINKRESET(snmp_syserr_p, NULL)
918
919			add_prop(nodeh, &proph, node_name, row,
920			    PP_RATE_UNITS, snmp_syserr_p);
921			CHECK_LINKRESET(snmp_syserr_p, NULL)
922
923			add_thresholds(nodeh, row, snmp_syserr_p);
924			CHECK_LINKRESET(snmp_syserr_p, NULL)
925
926		} else if (sensor_class == SSSC_BINARY) {
927			if (sensor_type == SSST_TEMPERATURE) {
928				ADD_NODE(PICL_CLASS_TEMPERATURE_INDICATOR)
929			} else if (sensor_type == SSST_VOLTAGE) {
930				ADD_NODE(PICL_CLASS_VOLTAGE_INDICATOR)
931			} else if (sensor_type == SSST_CURRENT) {
932				ADD_NODE(PICL_CLASS_CURRENT_INDICATOR)
933			} else if (sensor_type == SSST_TACHOMETER) {
934				ADD_NODE(PICL_CLASS_RPM_INDICATOR)
935			} else if (sensor_type == SSST_PRESENCE) {
936				ADD_NODE(PICL_CLASS_PRESENCE_INDICATOR)
937			} else {
938				ADD_NODE(PICL_CLASS_INDICATOR)
939			}
940
941			add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
942			    snmp_syserr_p);
943			CHECK_LINKRESET(snmp_syserr_p, NULL)
944
945			add_prop(nodeh, &proph, node_name, row, PP_CONDITION,
946			    snmp_syserr_p);
947			CHECK_LINKRESET(snmp_syserr_p, NULL)
948
949			add_prop(nodeh, &proph, node_name, row, PP_EXPECTED,
950			    snmp_syserr_p);
951			CHECK_LINKRESET(snmp_syserr_p, NULL)
952		} else {
953			log_msg(LOG_ERR,
954			    SNMPP_UNSUPP_SENSOR_CLASS, sensor_class, row);
955			return (NULL);
956		}
957		break;
958
959	case SPC_MODULE:
960		ADD_NODE(PICL_CLASS_MODULE)
961
962		add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
963		    snmp_syserr_p);
964		CHECK_LINKRESET(snmp_syserr_p, NULL)
965
966		add_prop(nodeh, &proph, node_name, row, PP_REPLACEABLE,
967		    snmp_syserr_p);
968		CHECK_LINKRESET(snmp_syserr_p, NULL)
969
970		add_prop(nodeh, &proph, node_name, row, PP_HOTSWAPPABLE,
971		    snmp_syserr_p);
972		CHECK_LINKRESET(snmp_syserr_p, NULL)
973		break;
974
975	case SPC_PORT:
976		ADD_NODE(PICL_CLASS_PORT)
977		break;
978
979	case SPC_STACK:
980		ADD_NODE(PICL_CLASS_STACK)
981		break;
982
983	default:
984		log_msg(LOG_WARNING,
985		    SNMPP_UNKNOWN_ENTPHYSCLASS, ent_physclass, row);
986		free(phys_name);
987		return (NULL);
988	}
989
990	add_prop(nodeh, &proph, node_name, row, PP_DESCRIPTION, snmp_syserr_p);
991	CHECK_LINKRESET(snmp_syserr_p, NULL)
992
993	add_prop(nodeh, &proph, node_name, row, PP_LABEL, snmp_syserr_p);
994	CHECK_LINKRESET(snmp_syserr_p, NULL)
995
996	add_prop(nodeh, &proph, node_name, row, PP_HW_REVISION, snmp_syserr_p);
997	CHECK_LINKRESET(snmp_syserr_p, NULL)
998
999	add_prop(nodeh, &proph, node_name, row, PP_FW_REVISION, snmp_syserr_p);
1000	CHECK_LINKRESET(snmp_syserr_p, NULL)
1001
1002	add_prop(nodeh, &proph, node_name, row, PP_SERIAL_NUM, snmp_syserr_p);
1003	CHECK_LINKRESET(snmp_syserr_p, NULL)
1004
1005	add_prop(nodeh, &proph, node_name, row, PP_MFG_NAME, snmp_syserr_p);
1006	CHECK_LINKRESET(snmp_syserr_p, NULL)
1007
1008	add_prop(nodeh, &proph, node_name, row, PP_MODEL_NAME, snmp_syserr_p);
1009	CHECK_LINKRESET(snmp_syserr_p, NULL)
1010
1011	add_prop(nodeh, &proph, node_name, row, PP_IS_FRU, snmp_syserr_p);
1012	CHECK_LINKRESET(snmp_syserr_p, NULL)
1013
1014	free(phys_name);
1015	save_nodeh(nodeh, row);
1016
1017	return (nodeh);
1018}
1019
1020/*
1021 * Saves the node handle and the row id into physplat_nodes[]. If we're
1022 * doing this in response to a hotplug event, we should've freed the
1023 * old physplat_nodes before entering here to save the first node of the
1024 * new physplat subtree.
1025 */
1026static void
1027save_nodeh(picl_nodehdl_t nodeh, int row)
1028{
1029	size_t		sz, count;
1030	picl_nodehdl_t	*p;
1031
1032	if (row >= n_physplat_nodes) {
1033		count = (((size_t)row >> NODE_BLOCK_SHIFT) + 1) *
1034		    N_ELEMS_IN_NODE_BLOCK;
1035		sz = count * sizeof (picl_nodehdl_t);
1036
1037		p = (picl_nodehdl_t *)calloc(count, sizeof (picl_nodehdl_t));
1038		if (p == NULL) {
1039			log_msg(LOG_ERR, SNMPP_NO_MEM, sz);
1040			return;
1041		}
1042
1043		if (physplat_nodes) {
1044			(void) memcpy((void *) p, (void *) physplat_nodes,
1045			    n_physplat_nodes * sizeof (picl_nodehdl_t));
1046			free((void *) physplat_nodes);
1047		}
1048
1049		physplat_nodes = p;
1050		n_physplat_nodes = count;
1051	}
1052
1053	physplat_nodes[row] = nodeh;
1054}
1055
1056static picl_nodehdl_t
1057lookup_nodeh(int row)
1058{
1059	if (row >= n_physplat_nodes)
1060		return (NULL);
1061
1062	return (physplat_nodes[row]);
1063}
1064
1065/*
1066 * We enter this routine only when we are building the physical-platform
1067 * subtree, whether for the first time or in response to a hotplug event.
1068 * If we're here for rebuilding the tree, we have already set stale_tree
1069 * to be B_TRUE, so no one else would be accessing vol_props, n_vol_props
1070 * or volprop_ndx. If we're here to build the tree for the first time,
1071 * picld hasn't yet created doors and is running single-threaded, so no
1072 * one else would be accessing them anyway.
1073 */
1074static void
1075save_volprop(picl_prophdl_t prop, char *oidstr, int row, int proptype)
1076{
1077	vol_prophdl_t	*p;
1078	int		count;
1079
1080	if (volprop_ndx == n_vol_props) {
1081		count = n_vol_props + N_ELEMS_IN_VOLPROP_BLOCK;
1082		p = (vol_prophdl_t *)calloc(count, sizeof (vol_prophdl_t));
1083		if (p == NULL) {
1084			log_msg(LOG_ERR, SNMPP_NO_MEM,
1085			    count * sizeof (vol_prophdl_t));
1086			return;
1087		}
1088
1089		if (vol_props) {
1090			(void) memcpy((void *) p, (void *) vol_props,
1091			    n_vol_props * sizeof (vol_prophdl_t));
1092			free((void *) vol_props);
1093		}
1094
1095		vol_props = p;
1096		n_vol_props += N_ELEMS_IN_VOLPROP_BLOCK;
1097	}
1098
1099	vol_props[volprop_ndx].prop = prop;
1100	vol_props[volprop_ndx].oidstr = oidstr;
1101	vol_props[volprop_ndx].row = row;
1102	vol_props[volprop_ndx].proptype = proptype;
1103
1104	volprop_ndx++;
1105}
1106
1107static void
1108check_for_stale_data(boolean_t nocache)
1109{
1110	int	cur_change_time;
1111	int	ret;
1112	int	snmp_syserr;
1113
1114	(void) rw_wrlock(&stale_tree_rwlp);
1115
1116	/*
1117	 * Check if some other thread beat us to it
1118	 */
1119	if (stale_tree == B_TRUE) {
1120		(void) rw_unlock(&stale_tree_rwlp);
1121		return;
1122	}
1123
1124	/*
1125	 * Cache OID_entLastChangeTime for up to 10 seconds before
1126	 * fetching it from ILOM again.  This prevents us from fetching
1127	 * this value from ILOM when the we're filling or refreshing a
1128	 * whole bunch of items in the cache around the same time.
1129	 */
1130	if (nocache == B_FALSE && time(NULL) - change_time_check <= 10) {
1131		(void) rw_unlock(&stale_tree_rwlp);
1132		return;
1133	}
1134
1135	/*
1136	 * Check if mib data has changed (hotplug? link-reset?)
1137	 */
1138	do {
1139		snmp_syserr = 0;
1140		ret = snmp_get_int(hdl, OID_entLastChangeTime, 0,
1141		    &cur_change_time, &snmp_syserr);
1142		(void) time(&change_time_check);
1143		if ((ret == 0) && (cur_change_time == change_time)) {
1144			(void) rw_unlock(&stale_tree_rwlp);
1145			return;
1146		}
1147	} while (ret != 0 && snmp_syserr == EINTR);
1148
1149	/*
1150	 * If we can't read entLastChangeTime we assume we need to rebuild
1151	 * the tree. This will also cover the case when we need to rebuild
1152	 * the tree because a link reset had happened.
1153	 */
1154	LOGPRINTF2("check_for_stale_data: LastChange times have changed, "
1155	    "(%#x != %#x)\n", change_time, cur_change_time);
1156
1157	/*
1158	 * If the mib data has changed, we need to rebuild the physical-platform
1159	 * subtree. To do this, we set a flag to mark the tree stale,
1160	 * so that any future reads to get value of volatile properties will
1161	 * return PICL_PROPVALUNAVAILABLE, until the stale_tree flag
1162	 * is reset by the tree builder thread.
1163	 */
1164	stale_tree = B_TRUE;
1165	if (vol_props) {
1166		free(vol_props);
1167	}
1168	vol_props = NULL;
1169	volprop_ndx = 0;
1170	n_vol_props = 0;
1171
1172	(void) rw_unlock(&stale_tree_rwlp);
1173
1174	(void) mutex_lock(&rebuild_tree_lock);
1175	rebuild_tree = B_TRUE;
1176	(void) cond_signal(&rebuild_tree_cv);
1177	LOGPRINTF("check_for_stale_data: signalled tree builder\n");
1178	(void) mutex_unlock(&rebuild_tree_lock);
1179}
1180
1181/*
1182 * This is the critical routine.  This callback is invoked by picl whenever
1183 * it needs to fetch the value of a volatile property. The first thing we
1184 * must do, however, is to see if there has been a hotplug or a link-reset
1185 * event since the last time we built the tree and whether we need to
1186 * rebuild the tree. If so, we do whatever is necessary to make that happen,
1187 * but return PICL_PROPVALUNAVAILABLE for now, without making any further
1188 * snmp requests or accessing any globals.
1189 */
1190static int
1191read_volprop(ptree_rarg_t *parg, void *buf)
1192{
1193	char	*pstr;
1194	int	propval;
1195	int	i, ndx;
1196	int	ret;
1197	int	snmp_syserr = 0;
1198
1199	/*
1200	 * First check for any event that would make us throw away
1201	 * the existing /physical-platform subtree and rebuild
1202	 * another one. If we are rebuilding the subtree, we just
1203	 * return the stale value until the tree is fully built.
1204	 */
1205	check_for_stale_data(B_FALSE);
1206
1207	(void) rw_rdlock(&stale_tree_rwlp);
1208
1209	if (stale_tree == B_TRUE) {
1210		(void) rw_unlock(&stale_tree_rwlp);
1211		return (PICL_PROPVALUNAVAILABLE);
1212	}
1213
1214	for (i = 0; i < volprop_ndx; i++) {
1215		if (vol_props[i].prop == parg->proph) {
1216			ndx = i;
1217			break;
1218		}
1219	}
1220	if (i == volprop_ndx) {
1221		(void) rw_unlock(&stale_tree_rwlp);
1222		log_msg(LOG_ERR, SNMPP_CANT_FIND_VOLPROP, parg->proph);
1223		return (PICL_FAILURE);
1224	}
1225
1226	/*
1227	 * If we can't read the value, return failure. Even if this was
1228	 * due to a link reset, between the check for stale data and now,
1229	 * the next volatile callback by picl will initiate a tree-rebuild.
1230	 */
1231	ret = snmp_get_int(hdl, vol_props[ndx].oidstr, vol_props[ndx].row,
1232	    &propval, &snmp_syserr);
1233	if (ret < 0) {
1234		(void) rw_unlock(&stale_tree_rwlp);
1235		check_for_stale_data(B_TRUE);
1236		if (stale_tree == B_TRUE) {
1237			return (PICL_PROPVALUNAVAILABLE);
1238		}
1239		log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1240		    snmp_syserr ? snmp_syserr : ret,
1241		    vol_props[ndx].oidstr, vol_props[ndx].row);
1242		return (PICL_FAILURE);
1243	}
1244
1245	switch (vol_props[ndx].proptype) {
1246	case VPT_PLATOPSTATE:
1247		if (propval == SSOS_DISABLED) {
1248			(void) strlcpy(buf, STR_SSOS_DISABLED, MAX_OPSTATE_LEN);
1249		} else if (propval == SSOS_ENABLED) {
1250			(void) strlcpy(buf, STR_SSOS_ENABLED, MAX_OPSTATE_LEN);
1251		} else {
1252			(void) rw_unlock(&stale_tree_rwlp);
1253			log_msg(LOG_ERR, SNMPP_INV_PLAT_EQUIP_OPSTATE,
1254			    propval, vol_props[ndx].row);
1255			return (PICL_FAILURE);
1256		}
1257		break;
1258
1259	case VPT_NUMSENSOR:
1260		(void) memcpy(buf, &propval, sizeof (propval));
1261		break;
1262
1263	case VPT_BINSENSOR:
1264		if (propval == ST_TRUE) {
1265			ret = snmp_get_str(hdl,
1266			    OID_sunPlatBinarySensorInterpretTrue,
1267			    vol_props[ndx].row, &pstr, &snmp_syserr);
1268			if (snmp_syserr == ECANCELED) {
1269				(void) rw_unlock(&stale_tree_rwlp);
1270				if (pstr)
1271					free(pstr);
1272				return (PICL_FAILURE);
1273			}
1274			if (ret < 0 || pstr == NULL) {
1275				log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1276				    snmp_syserr ? snmp_syserr : ret,
1277				    OID_sunPlatBinarySensorInterpretTrue,
1278				    vol_props[ndx].row);
1279				(void) strlcpy(buf, STR_ST_TRUE,
1280				    MAX_TRUTHVAL_LEN);
1281			} else {
1282				(void) strlcpy(buf, pstr, MAX_TRUTHVAL_LEN);
1283			}
1284			if (pstr)
1285				free(pstr);
1286		} else if (propval == ST_FALSE) {
1287			ret = snmp_get_str(hdl,
1288			    OID_sunPlatBinarySensorInterpretFalse,
1289			    vol_props[ndx].row, &pstr, &snmp_syserr);
1290			if (snmp_syserr == ECANCELED) {
1291				(void) rw_unlock(&stale_tree_rwlp);
1292				if (pstr)
1293					free(pstr);
1294				return (PICL_FAILURE);
1295			}
1296			if (ret < 0 || pstr == NULL) {
1297				log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1298				    snmp_syserr ? snmp_syserr : ret,
1299				    OID_sunPlatBinarySensorInterpretFalse,
1300				    vol_props[ndx].row);
1301				(void) strlcpy(buf, STR_ST_FALSE,
1302				    MAX_TRUTHVAL_LEN);
1303			} else {
1304				(void) strlcpy(buf, pstr, MAX_TRUTHVAL_LEN);
1305			}
1306			if (pstr)
1307				free(pstr);
1308		} else {
1309			(void) rw_unlock(&stale_tree_rwlp);
1310			log_msg(LOG_ERR, SNMPP_INV_PLAT_BINSNSR_CURRENT,
1311			    propval, vol_props[ndx].row);
1312			return (PICL_FAILURE);
1313		}
1314		break;
1315
1316	case VPT_ALARMSTATE:
1317		if (propval == SSAS_OFF) {
1318			(void) strlcpy(buf, STR_SSAS_OFF, MAX_ALARMSTATE_LEN);
1319		} else if (propval == SSAS_STEADY) {
1320			(void) strlcpy(buf, STR_SSAS_STEADY,
1321			    MAX_ALARMSTATE_LEN);
1322		} else if (propval == SSAS_ALTERNATING) {
1323			(void) strlcpy(buf, STR_SSAS_ALTERNATING,
1324			    MAX_ALARMSTATE_LEN);
1325		} else {
1326			(void) strlcpy(buf, STR_SSAS_UNKNOWN,
1327			    MAX_ALARMSTATE_LEN);
1328		}
1329		break;
1330
1331	case VPT_BATTERYSTATUS:
1332		switch (propval) {
1333		case SSBS_OTHER:
1334			(void) strlcpy(buf, STR_SSBS_OTHER,
1335			    MAX_BATTERYSTATUS_LEN);
1336			break;
1337		case SSBS_FULLYCHARGED:
1338			(void) strlcpy(buf, STR_SSBS_FULLYCHARGED,
1339			    MAX_BATTERYSTATUS_LEN);
1340			break;
1341		case SSBS_LOW:
1342			(void) strlcpy(buf, STR_SSBS_LOW,
1343			    MAX_BATTERYSTATUS_LEN);
1344			break;
1345		case SSBS_CRITICAL:
1346			(void) strlcpy(buf, STR_SSBS_CRITICAL,
1347			    MAX_BATTERYSTATUS_LEN);
1348			break;
1349		case SSBS_CHARGING:
1350			(void) strlcpy(buf, STR_SSBS_CHARGING,
1351			    MAX_BATTERYSTATUS_LEN);
1352			break;
1353		case SSBS_CHARGING_AND_LOW:
1354			(void) strlcpy(buf, STR_SSBS_CHARGING_AND_LOW,
1355			    MAX_BATTERYSTATUS_LEN);
1356			break;
1357		case SSBS_CHARGING_AND_HIGH:
1358			(void) strlcpy(buf, STR_SSBS_CHARGING_AND_HIGH,
1359			    MAX_BATTERYSTATUS_LEN);
1360			break;
1361		case SSBS_CHARGING_AND_CRITICAL:
1362			(void) strlcpy(buf, STR_SSBS_CHARGING_AND_CRITICAL,
1363			    MAX_BATTERYSTATUS_LEN);
1364			break;
1365		case SSBS_UNDEFINED:
1366			(void) strlcpy(buf, STR_SSBS_UNDEFINED,
1367			    MAX_BATTERYSTATUS_LEN);
1368			break;
1369		case SSBS_PARTIALLY_CHARGED:
1370			(void) strlcpy(buf, STR_SSBS_PARTIALLY_CHARGED,
1371			    MAX_BATTERYSTATUS_LEN);
1372			break;
1373		case SSBS_UNKNOWN:
1374		default:
1375			(void) strlcpy(buf, STR_SSBS_UNKNOWN,
1376			    MAX_BATTERYSTATUS_LEN);
1377			break;
1378		}
1379		break;
1380	}
1381
1382	(void) rw_unlock(&stale_tree_rwlp);
1383
1384	return (PICL_SUCCESS);
1385}
1386
1387static void
1388threshold(picl_nodehdl_t node, char *oidstr, int row, char *propname,
1389    int *snmp_syserr_p)
1390{
1391	picl_prophdl_t	prop;
1392	int		err;
1393	int		val;
1394
1395	if ((err = snmp_get_int(hdl, oidstr, row, &val, snmp_syserr_p)) != -1) {
1396		err = add_volatile_prop(node, propname, PICL_PTYPE_INT,
1397		    PICL_READ, sizeof (int), read_volprop, NULL, &prop);
1398		if (err == PICL_SUCCESS)
1399			save_volprop(prop, oidstr, row, VPT_NUMSENSOR);
1400	} else
1401		log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1402		    *snmp_syserr_p ? *snmp_syserr_p : err, oidstr, row);
1403}
1404
1405static void
1406add_thresholds(picl_nodehdl_t node, int row, int *snmp_syserr_p)
1407{
1408	uchar_t	*bitstr = NULL;
1409	uchar_t	enabled;
1410	uint_t	nbytes;
1411	int	ret;
1412
1413	ret = snmp_get_str(hdl,
1414	    OID_sunPlatNumericSensorEnabledThresholds,
1415	    row, (char **)&bitstr, snmp_syserr_p);
1416	if (ret == -1) {
1417		log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1418		    *snmp_syserr_p ? *snmp_syserr_p : ret,
1419		    OID_sunPlatNumericSensorEnabledThresholds, row);
1420	} else {
1421		nbytes = strlen((const char *)bitstr);
1422	}
1423
1424	CHECK_LINKRESET_VOID(snmp_syserr_p);
1425
1426	/*
1427	 * No bit string of threshold masks was returned, so we can't
1428	 * assume that any thresholds exist.
1429	 *
1430	 * This mask prevents us from attempting to fetch thresholds
1431	 * which don't apply to the sensor or that aren't there anyway,
1432	 * That speeds up the plug-in significantly since otherwise it
1433	 * takes several seconds to time out.
1434	 */
1435	if (ret < 0 || bitstr == NULL || nbytes == 0 || 2 < nbytes) {
1436		if (bitstr)
1437			free(bitstr);
1438		return;
1439	} else if (nbytes == 1) {
1440		/*
1441		 * The ALOM snmp agent doesn't adhere to the BER rules for
1442		 * encoding bit strings. While the BER states that bitstrings
1443		 * must begin from the second octet after length, and the
1444		 * first octet after length must indicate the number of unused
1445		 * bits in the last octet, the snmp agent simply sends the
1446		 * bitstring data as if it were octet string -- that is, the
1447		 * "unused bits" octet is missing.
1448		 */
1449		enabled = bitstr[0];
1450	} else if (nbytes == 2)
1451		enabled = bitstr[1];
1452
1453	if (bitstr) {
1454		free(bitstr);
1455	}
1456
1457	if (enabled & LOWER_FATAL) {
1458		threshold(node,
1459		    OID_sunPlatNumericSensorLowerThresholdFatal, row,
1460		    PICL_PROP_LOW_POWER_OFF, snmp_syserr_p);
1461		CHECK_LINKRESET_VOID(snmp_syserr_p)
1462	}
1463	if (enabled & LOWER_CRITICAL) {
1464		threshold(node,
1465		    OID_sunPlatNumericSensorLowerThresholdCritical, row,
1466		    PICL_PROP_LOW_SHUTDOWN, snmp_syserr_p);
1467		CHECK_LINKRESET_VOID(snmp_syserr_p)
1468	}
1469	if (enabled & LOWER_NON_CRITICAL) {
1470		threshold(node,
1471		    OID_sunPlatNumericSensorLowerThresholdNonCritical, row,
1472		    PICL_PROP_LOW_WARNING, snmp_syserr_p);
1473		CHECK_LINKRESET_VOID(snmp_syserr_p)
1474	}
1475	if (enabled & UPPER_NON_CRITICAL) {
1476		threshold(node,
1477		    OID_sunPlatNumericSensorUpperThresholdNonCritical, row,
1478		    PICL_PROP_HIGH_WARNING, snmp_syserr_p);
1479		CHECK_LINKRESET_VOID(snmp_syserr_p)
1480	}
1481	if (enabled & UPPER_CRITICAL) {
1482		threshold(node,
1483		    OID_sunPlatNumericSensorUpperThresholdCritical, row,
1484		    PICL_PROP_HIGH_SHUTDOWN, snmp_syserr_p);
1485		CHECK_LINKRESET_VOID(snmp_syserr_p)
1486	}
1487	if (enabled & UPPER_FATAL) {
1488		threshold(node,
1489		    OID_sunPlatNumericSensorUpperThresholdFatal, row,
1490		    PICL_PROP_HIGH_POWER_OFF, snmp_syserr_p);
1491		CHECK_LINKRESET_VOID(snmp_syserr_p)
1492	}
1493}
1494
1495static char *
1496get_slot_type(int row, int *snmp_syserr_p)
1497{
1498	char	*p;
1499	char	*slott = NULL;
1500	int	ret;
1501
1502	ret = snmp_get_str(hdl, OID_sunPlatEquipmentHolderAcceptableTypes,
1503	    row, &p, snmp_syserr_p);
1504	CHECK_LINKRESET(snmp_syserr_p, NULL)
1505
1506	if ((ret == 0) && p && *p) {
1507		slott = p;
1508		if ((p = strchr(slott, '\n')) != NULL)
1509			*p = 0;
1510	} else {
1511		log_msg(LOG_WARNING, SNMPP_NO_SLOT_TYPE, row);
1512		if (p) {
1513			free(p);
1514		}
1515	}
1516
1517	return (slott);
1518}
1519
1520/*
1521 * Create and add the specified volatile property
1522 */
1523static int
1524add_volatile_prop(picl_nodehdl_t node, char *name, int type, int access,
1525    int size, int (*rdfunc)(ptree_rarg_t *, void *),
1526    int (*wrfunc)(ptree_warg_t *, const void *), picl_prophdl_t *propp)
1527{
1528	ptree_propinfo_t	propinfo;
1529	picl_prophdl_t		prop;
1530	int			err;
1531
1532	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
1533	    type, (access|PICL_VOLATILE), size, name, rdfunc, wrfunc);
1534	if (err != PICL_SUCCESS) {
1535		log_msg(LOG_ERR, SNMPP_CANT_INIT_PROPINFO, err);
1536		return (err);
1537	}
1538
1539	err = ptree_create_and_add_prop(node, &propinfo, NULL, &prop);
1540	if (err != PICL_SUCCESS) {
1541		log_msg(LOG_ERR, SNMPP_CANT_ADD_PROP, err, node);
1542		return (err);
1543	}
1544
1545	if (propp)
1546		*propp = prop;
1547
1548	return (PICL_SUCCESS);
1549}
1550
1551/*
1552 * Add the specified string property to the node
1553 */
1554static int
1555add_string_prop(picl_nodehdl_t node, char *propname, char *propval)
1556{
1557	ptree_propinfo_t	propinfo;
1558	int			err;
1559
1560	if (*propval == '\0')
1561		return (PICL_SUCCESS);
1562
1563	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
1564	    PICL_PTYPE_CHARSTRING, PICL_READ, strlen(propval) + 1,
1565	    propname, NULL, NULL);
1566	if (err != PICL_SUCCESS) {
1567		log_msg(LOG_ERR, SNMPP_CANT_INIT_STR_PROPINFO, err);
1568		return (err);
1569	}
1570
1571	err = ptree_create_and_add_prop(node, &propinfo, propval, NULL);
1572	if (err != PICL_SUCCESS) {
1573		log_msg(LOG_ERR, SNMPP_CANT_ADD_STR_PROP, err, node);
1574		return (err);
1575	}
1576
1577	return (PICL_SUCCESS);
1578}
1579
1580/*
1581 * Add the specified void property to the node
1582 */
1583static int
1584add_void_prop(picl_nodehdl_t node, char *propname)
1585{
1586	ptree_propinfo_t	propinfo;
1587	int			err;
1588
1589	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
1590	    PICL_PTYPE_VOID, PICL_READ, 0, propname, NULL, NULL);
1591	if (err != PICL_SUCCESS) {
1592		log_msg(LOG_ERR, SNMPP_CANT_INIT_VOID_PROPINFO, err);
1593		return (err);
1594	}
1595
1596	err = ptree_create_and_add_prop(node, &propinfo, NULL, NULL);
1597	if (err != PICL_SUCCESS) {
1598		log_msg(LOG_ERR, SNMPP_CANT_ADD_VOID_PROP, err, node);
1599		return (err);
1600	}
1601
1602	return (PICL_SUCCESS);
1603}
1604
1605static void
1606add_prop(picl_nodehdl_t nodeh, picl_prophdl_t *php, char *label,
1607    int row, sp_propid_t pp, int *snmp_syserr_p)
1608{
1609	char	*serial_num;
1610	char	*slot_type;
1611	char	*fw_revision, *hw_revision;
1612	char	*mfg_name, *model_name;
1613	char	*phys_descr;
1614	int	val;
1615	int	ret;
1616
1617	switch (pp) {
1618	case PP_SERIAL_NUM:
1619		ret = snmp_get_str(hdl, OID_entPhysicalSerialNum,
1620		    row, &serial_num, snmp_syserr_p);
1621		CHECK_LINKRESET_VOID(snmp_syserr_p)
1622		if ((ret == 0) && serial_num) {
1623			(void) add_string_prop(nodeh,
1624			    PICL_PROP_SERIAL_NUMBER, serial_num);
1625			free((void *) serial_num);
1626		}
1627		if (ret == -1)
1628			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1629			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1630			    OID_entPhysicalSerialNum, row);
1631		break;
1632
1633	case PP_SLOT_TYPE:
1634		if ((slot_type = get_slot_type(row, snmp_syserr_p)) == NULL) {
1635			CHECK_LINKRESET_VOID(snmp_syserr_p)
1636			(void) add_string_prop(nodeh,
1637			    PICL_PROP_SLOT_TYPE, DEFAULT_SLOT_TYPE);
1638		} else {
1639			(void) add_string_prop(nodeh,
1640			    PICL_PROP_SLOT_TYPE, slot_type);
1641			free((void *) slot_type);
1642		}
1643		break;
1644
1645	case PP_STATE:
1646		ret = add_volatile_prop(nodeh, PICL_PROP_STATE,
1647		    PICL_PTYPE_CHARSTRING, PICL_READ, MAX_ALARMSTATE_LEN,
1648		    read_volprop, NULL, php);
1649		if (ret == PICL_SUCCESS) {
1650			save_volprop(*php, OID_sunPlatAlarmState, row,
1651			    VPT_ALARMSTATE);
1652		}
1653		break;
1654
1655	case PP_OPSTATUS:
1656		ret = add_volatile_prop(nodeh, PICL_PROP_OPERATIONAL_STATUS,
1657		    PICL_PTYPE_CHARSTRING, PICL_READ, MAX_OPSTATE_LEN,
1658		    read_volprop, NULL, php);
1659		if (ret == PICL_SUCCESS) {
1660			save_volprop(*php,
1661			    OID_sunPlatEquipmentOperationalState, row,
1662			    VPT_PLATOPSTATE);
1663		}
1664		break;
1665
1666	case PP_BATT_STATUS:
1667		ret = add_volatile_prop(nodeh, PICL_PROP_BATTERY_STATUS,
1668		    PICL_PTYPE_CHARSTRING, PICL_READ, MAX_BATTERYSTATUS_LEN,
1669		    read_volprop, NULL, php);
1670		if (ret == PICL_SUCCESS) {
1671			save_volprop(*php, OID_sunPlatBatteryStatus, row,
1672			    VPT_BATTERYSTATUS);
1673		}
1674		break;
1675
1676	case PP_TEMPERATURE:
1677		ret = add_volatile_prop(nodeh, PICL_PROP_TEMPERATURE,
1678		    PICL_PTYPE_INT, PICL_READ, sizeof (int), read_volprop,
1679		    NULL, php);
1680		if (ret == PICL_SUCCESS) {
1681			save_volprop(*php, OID_sunPlatNumericSensorCurrent,
1682			    row, VPT_NUMSENSOR);
1683		}
1684		break;
1685
1686	case PP_VOLTAGE:
1687		ret = add_volatile_prop(nodeh, PICL_PROP_VOLTAGE,
1688		    PICL_PTYPE_INT, PICL_READ, sizeof (int), read_volprop,
1689		    NULL, php);
1690		if (ret == PICL_SUCCESS) {
1691			save_volprop(*php, OID_sunPlatNumericSensorCurrent,
1692			    row, VPT_NUMSENSOR);
1693		}
1694		break;
1695
1696	case PP_CURRENT:
1697		ret = add_volatile_prop(nodeh, PICL_PROP_CURRENT,
1698		    PICL_PTYPE_INT, PICL_READ, sizeof (int), read_volprop,
1699		    NULL, php);
1700		if (ret == PICL_SUCCESS) {
1701			save_volprop(*php, OID_sunPlatNumericSensorCurrent,
1702			    row, VPT_NUMSENSOR);
1703		}
1704		break;
1705
1706	case PP_SPEED:
1707		ret = add_volatile_prop(nodeh, PICL_PROP_SPEED, PICL_PTYPE_INT,
1708		    PICL_READ, sizeof (int), read_volprop, NULL, php);
1709		if (ret == PICL_SUCCESS) {
1710			save_volprop(*php, OID_sunPlatNumericSensorCurrent,
1711			    row, VPT_NUMSENSOR);
1712		}
1713		break;
1714
1715	case PP_SENSOR_VALUE:
1716		ret = add_volatile_prop(nodeh, PICL_PROP_SENSOR_VALUE,
1717		    PICL_PTYPE_INT, PICL_READ, sizeof (int), read_volprop,
1718		    NULL, php);
1719		if (ret == PICL_SUCCESS) {
1720			save_volprop(*php, OID_sunPlatNumericSensorCurrent,
1721			    row, VPT_NUMSENSOR);
1722		}
1723		break;
1724
1725	case PP_CONDITION:
1726		ret = add_volatile_prop(nodeh, PICL_PROP_CONDITION,
1727		    PICL_PTYPE_CHARSTRING, PICL_READ, MAX_TRUTHVAL_LEN,
1728		    read_volprop, NULL, php);
1729		if (ret == PICL_SUCCESS) {
1730			save_volprop(*php, OID_sunPlatBinarySensorCurrent,
1731			    row, VPT_BINSENSOR);
1732		}
1733		break;
1734
1735	case PP_EXPECTED:
1736		ret = add_volatile_prop(nodeh, PICL_PROP_EXPECTED,
1737		    PICL_PTYPE_CHARSTRING, PICL_READ, MAX_TRUTHVAL_LEN,
1738		    read_volprop, NULL, php);
1739		if (ret == PICL_SUCCESS) {
1740			save_volprop(*php, OID_sunPlatBinarySensorExpected,
1741			    row, VPT_BINSENSOR);
1742		}
1743		break;
1744
1745	case PP_EXPONENT:
1746		ret = add_volatile_prop(nodeh, PICL_PROP_EXPONENT,
1747		    PICL_PTYPE_INT, PICL_READ, sizeof (int), read_volprop,
1748		    NULL, php);
1749		if (ret == PICL_SUCCESS) {
1750			save_volprop(*php, OID_sunPlatNumericSensorExponent,
1751			    row, VPT_NUMSENSOR);
1752		}
1753		break;
1754
1755	case PP_REPLACEABLE:
1756		ret = snmp_get_int(hdl, OID_sunPlatCircuitPackReplaceable,
1757		    row, &val, snmp_syserr_p);
1758		CHECK_LINKRESET_VOID(snmp_syserr_p)
1759		if ((ret == 0) && (val == ST_TRUE))
1760			(void) add_void_prop(nodeh, PICL_PROP_IS_REPLACEABLE);
1761		if (ret == -1)
1762			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1763			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1764			    OID_sunPlatCircuitPackReplaceable, row);
1765		break;
1766
1767	case PP_HOTSWAPPABLE:
1768		ret = snmp_get_int(hdl, OID_sunPlatCircuitPackHotSwappable,
1769		    row, &val, snmp_syserr_p);
1770		CHECK_LINKRESET_VOID(snmp_syserr_p)
1771		if ((ret == 0) && (val == ST_TRUE))
1772			(void) add_void_prop(nodeh, PICL_PROP_IS_HOT_SWAPPABLE);
1773		if (ret == -1)
1774			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1775			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1776			    OID_sunPlatCircuitPackHotSwappable, row);
1777		break;
1778
1779	case PP_IS_FRU:
1780		ret = snmp_get_int(hdl, OID_entPhysicalIsFRU, row,
1781		    &val, snmp_syserr_p);
1782		CHECK_LINKRESET_VOID(snmp_syserr_p)
1783		if ((ret == 0) && (val == ST_TRUE))
1784			(void) add_void_prop(nodeh, PICL_PROP_IS_FRU);
1785		if (ret == -1)
1786			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1787			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1788			    OID_entPhysicalIsFRU, row);
1789		break;
1790
1791	case PP_HW_REVISION:
1792		ret = snmp_get_str(hdl, OID_entPhysicalHardwareRev,
1793		    row, &hw_revision, snmp_syserr_p);
1794		CHECK_LINKRESET_VOID(snmp_syserr_p)
1795		if ((ret == 0) && hw_revision) {
1796			(void) add_string_prop(nodeh,
1797			    PICL_PROP_HW_REVISION, hw_revision);
1798			free((void *) hw_revision);
1799		}
1800		if (ret == -1)
1801			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1802			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1803			    OID_entPhysicalHardwareRev, row);
1804		break;
1805
1806	case PP_FW_REVISION:
1807		ret = snmp_get_str(hdl, OID_entPhysicalFirmwareRev,
1808		    row, &fw_revision, snmp_syserr_p);
1809		CHECK_LINKRESET_VOID(snmp_syserr_p)
1810		if ((ret == 0) && fw_revision) {
1811			(void) add_string_prop(nodeh,
1812			    PICL_PROP_FW_REVISION, fw_revision);
1813			free((void *) fw_revision);
1814		}
1815		if (ret == -1)
1816			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1817			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1818			    OID_entPhysicalFirmwareRev, row);
1819		break;
1820
1821	case PP_MFG_NAME:
1822		ret = snmp_get_str(hdl, OID_entPhysicalMfgName,
1823		    row, &mfg_name, snmp_syserr_p);
1824		CHECK_LINKRESET_VOID(snmp_syserr_p)
1825		if ((ret == 0) && mfg_name) {
1826			(void) add_string_prop(nodeh,
1827			    PICL_PROP_MFG_NAME, mfg_name);
1828			free((void *) mfg_name);
1829		}
1830		if (ret == -1)
1831			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1832			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1833			    OID_entPhysicalMfgName, row);
1834		break;
1835
1836	case PP_MODEL_NAME:
1837		ret = snmp_get_str(hdl, OID_entPhysicalModelName,
1838		    row, &model_name, snmp_syserr_p);
1839		CHECK_LINKRESET_VOID(snmp_syserr_p)
1840		if ((ret == 0) && model_name) {
1841			(void) add_string_prop(nodeh,
1842			    PICL_PROP_MODEL_NAME, model_name);
1843			free((void *) model_name);
1844		}
1845		if (ret == -1)
1846			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1847			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1848			    OID_entPhysicalModelName, row);
1849		break;
1850
1851	case PP_DESCRIPTION:
1852		ret = snmp_get_str(hdl, OID_entPhysicalDescr,
1853		    row, &phys_descr, snmp_syserr_p);
1854		CHECK_LINKRESET_VOID(snmp_syserr_p)
1855		if ((ret == 0) && phys_descr) {
1856			(void) add_string_prop(nodeh,
1857			    PICL_PROP_PHYS_DESCRIPTION, phys_descr);
1858			free((void *) phys_descr);
1859		}
1860		if (ret == -1)
1861			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1862			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1863			    OID_entPhysicalDescr, row);
1864		break;
1865
1866	case PP_LABEL:
1867		if (label && *label)
1868			(void) add_string_prop(nodeh, PICL_PROP_LABEL, label);
1869		break;
1870
1871	case PP_BASE_UNITS:
1872		ret = snmp_get_int(hdl, OID_sunPlatNumericSensorBaseUnits,
1873		    row, &val, snmp_syserr_p);
1874		CHECK_LINKRESET_VOID(snmp_syserr_p)
1875		if ((ret == 0) && (val > 0) && (val < n_baseunits)) {
1876			(void) add_string_prop(nodeh,
1877			    PICL_PROP_BASE_UNITS, sensor_baseunits[val]);
1878		}
1879		if (ret == -1)
1880			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1881			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1882			    OID_sunPlatNumericSensorBaseUnits, row);
1883		break;
1884
1885	case PP_RATE_UNITS:
1886		ret = snmp_get_int(hdl, OID_sunPlatNumericSensorRateUnits,
1887		    row, &val, snmp_syserr_p);
1888		CHECK_LINKRESET_VOID(snmp_syserr_p)
1889		if ((ret == 0) && (val > 0) && (val < n_rateunits)) {
1890			(void) add_string_prop(nodeh,
1891			    PICL_PROP_RATE_UNITS, sensor_rateunits[val]);
1892		}
1893		if (ret == -1)
1894			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1895			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1896			    OID_sunPlatNumericSensorRateUnits, row);
1897		break;
1898	}
1899}
1900
1901/*
1902 * Initialize the SNMP library's cache refresh subsystem, then periodically
1903 * process refresh job to prevent cache entries from expiring.
1904 */
1905/*ARGSUSED*/
1906static void *
1907cache_refresher(void *arg)
1908{
1909	int		jobs;
1910	int		next_expiration;
1911	timestruc_t	to;
1912	hrtime_t	cycle_start, cycle_elapsed;
1913
1914	/*
1915	 * Initialize refresh subsystem
1916	 */
1917	LOGPRINTF("Initializing SNMP refresh subsystem.\n");
1918	if (snmp_refresh_init() < 0) {
1919		return ((void *)-1);
1920	}
1921
1922	(void) mutex_lock(&cache_refresh_lock);
1923
1924
1925	for (;;) {
1926		cycle_start = gethrtime();
1927
1928		/*
1929		 * Process jobs from the snmp cache refresh work queue until one
1930		 * of the following conditions is true:
1931		 * 1) we are told to exit, or
1932		 * 2) we have processed at least as many jobs as recommended by
1933		 * the library, and the next job expiration is at least
1934		 * CACHE_REFRESH_MIN_WINDOW * seconds away.
1935		 */
1936		jobs = snmp_refresh_get_cycle_hint(CACHE_REFRESH_CYCLE);
1937		while ((cache_refresh_thr_exit == B_FALSE) && (jobs > 0)) {
1938			(void) snmp_refresh_process_job();
1939			jobs--;
1940		}
1941
1942		next_expiration = snmp_refresh_get_next_expiration();
1943		while ((cache_refresh_thr_exit == B_FALSE) &&
1944		    ((next_expiration >= 0) &&
1945		    (next_expiration < CACHE_REFRESH_MIN_WINDOW))) {
1946			(void) snmp_refresh_process_job();
1947			next_expiration = snmp_refresh_get_next_expiration();
1948		}
1949
1950		/*
1951		 * As long as we haven't been told to exit, sleep for
1952		 * CACHE_REFRESH_CYCLE seconds minus the amount of time that has
1953		 * elapsed since this cycle started.  If the elapsed time is
1954		 * equal to or greater than 60 seconds, skip sleeping entirely.
1955		 */
1956		cycle_elapsed = (gethrtime() - cycle_start) / NANOSEC;
1957		if ((cache_refresh_thr_exit == B_FALSE) &&
1958		    (cycle_elapsed < CACHE_REFRESH_CYCLE)) {
1959			to.tv_sec = CACHE_REFRESH_CYCLE - cycle_elapsed;
1960			to.tv_nsec = 0;
1961			(void) cond_reltimedwait(&cache_refresh_cv,
1962			    &cache_refresh_lock, &to);
1963		}
1964
1965		/*
1966		 * If we have been told to exit, clean up and bail out.
1967		 */
1968		if (cache_refresh_thr_exit == B_TRUE) {
1969			snmp_refresh_fini();
1970			(void) mutex_unlock(&cache_refresh_lock);
1971			LOGPRINTF("cache_refresher: time to exit\n");
1972			return (NULL);
1973		}
1974
1975	}
1976
1977	/*NOTREACHED*/
1978	return (NULL);
1979}
1980
1981/*
1982 * Check to see if the cache_refresher thread is running.  If it is, signal it
1983 * to terminate and clean up associated data structures.
1984 */
1985void
1986cache_refresher_fini(void)
1987{
1988	/* if the thread isn't running, there is nothing to do */
1989	if (cache_refresh_thr_exit == B_TRUE)
1990		return;
1991
1992	/* wake up the cache_refresher thread, tell it to exit */
1993	(void) mutex_lock(&cache_refresh_lock);
1994	cache_refresh_thr_exit = B_TRUE;
1995	(void) cond_signal(&cache_refresh_cv);
1996	(void) mutex_unlock(&cache_refresh_lock);
1997
1998	/* reap the thread */
1999	(void) thr_join(cache_refresh_thr_id, NULL, NULL);
2000
2001	/* finish cleanup... */
2002	(void) cond_destroy(&cache_refresh_cv);
2003	(void) mutex_destroy(&cache_refresh_lock);
2004}
2005
2006/*VARARGS2*/
2007static void
2008log_msg(int pri, const char *fmt, ...)
2009{
2010	va_list ap;
2011
2012	va_start(ap, fmt);
2013	vsyslog(pri, fmt, ap);
2014	va_end(ap);
2015}
2016
2017#ifdef SNMPPLUGIN_DEBUG
2018
2019static void
2020snmpplugin_log_init(void)
2021{
2022	(void) mutex_init(&snmpplugin_dbuf_lock, USYNC_THREAD, NULL);
2023}
2024
2025static void
2026snmpplugin_log(const char *fmt, ...)
2027{
2028	va_list	ap;
2029
2030	(void) mutex_lock(&snmpplugin_dbuf_lock);
2031
2032	va_start(ap, fmt);
2033	(void) vsnprintf(snmpplugin_lbuf, SNMPPLUGIN_DMAX_LINE, fmt, ap);
2034	snmpplugin_log_append();
2035	va_end(ap);
2036
2037	(void) mutex_unlock(&snmpplugin_dbuf_lock);
2038}
2039
2040static void
2041snmpplugin_log_append(void)
2042{
2043	int	len;
2044
2045	len = strlen(snmpplugin_lbuf);
2046
2047	if ((snmpplugin_dbuf_curp + len) >=
2048	    (snmpplugin_dbuf + snmpplugin_dbuf_sz)) {
2049		snmpplugin_dbuf_realloc();
2050		if (snmpplugin_dbuf == NULL) {
2051			return;
2052		}
2053	}
2054
2055	(void) strcpy(snmpplugin_dbuf_curp, snmpplugin_lbuf);
2056	snmpplugin_dbuf_curp += len;
2057}
2058
2059static void
2060snmpplugin_dbuf_realloc(void)
2061{
2062	char	*p;
2063	size_t	offset = 0;
2064	size_t	count;
2065
2066	count = snmpplugin_dbuf_sz + SNMPPLUGIN_DBLOCK_SZ;
2067	if ((p = (char *)calloc(count, 1)) == NULL) {
2068		snmpplugin_dbuf_overflow++;
2069		snmpplugin_dbuf_curp = snmpplugin_dbuf;
2070		return;
2071	}
2072
2073	if (snmpplugin_dbuf) {
2074		offset = snmpplugin_dbuf_curp - snmpplugin_dbuf;
2075		(void) memcpy(p, snmpplugin_dbuf, snmpplugin_dbuf_sz);
2076		free(snmpplugin_dbuf);
2077	}
2078
2079	snmpplugin_dbuf = p;
2080	snmpplugin_dbuf_sz += SNMPPLUGIN_DBLOCK_SZ;
2081
2082	snmpplugin_dbuf_curp = snmpplugin_dbuf + offset;
2083}
2084#endif
2085