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