xref: /titanic_50/usr/src/cmd/picl/plugins/sun4v/snmp/snmpplugin.c (revision 1787f50304cd7e85910a3e14f639ac892c0d76b7)
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 		n_vol_props = 0;
656 		volprop_ndx = 0;
657 	}
658 }
659 
660 static picl_nodehdl_t
661 make_node(picl_nodehdl_t subtree_root, int row, int *snmp_syserr_p)
662 {
663 	picl_nodehdl_t	nodeh, parenth;
664 	picl_prophdl_t	proph;
665 	char	*phys_name, *node_name;
666 	int	parent_row;
667 	int	ent_physclass, sunplat_physclass;
668 	int	sensor_class, sensor_type;
669 	int	alarm_type;
670 	int	ps_class;
671 	int	ret;
672 
673 	/*
674 	 * If we've already created this picl node, just return it
675 	 */
676 	if ((nodeh = lookup_nodeh(row)) != NULL)
677 		return (nodeh);
678 
679 	/*
680 	 * If we are creating it only now, make sure we have the parent
681 	 * created first; if there's no parent, then parent it to the
682 	 * subtree's root node
683 	 */
684 	ret = snmp_get_int(hdl, OID_entPhysicalContainedIn, row,
685 	    &parent_row, snmp_syserr_p);
686 	CHECK_LINKRESET(snmp_syserr_p, NULL)
687 	if (ret < 0 || parent_row <= 0)
688 		parenth = subtree_root;
689 	else {
690 		parenth = make_node(subtree_root, parent_row, snmp_syserr_p);
691 		CHECK_LINKRESET(snmp_syserr_p, NULL)
692 		if (parenth == NULL)
693 			parenth = subtree_root;
694 	}
695 
696 	/*
697 	 * Figure out the physical-platform node name from entPhysicalName;
698 	 * all rows in the MIB that have a valid entPhysicalIndex should
699 	 * have a physical name.
700 	 */
701 	ret = snmp_get_str(hdl, OID_entPhysicalName, row,
702 	    &phys_name, snmp_syserr_p);
703 	CHECK_LINKRESET(snmp_syserr_p, NULL)
704 	if (ret < 0 || phys_name == NULL) {
705 		log_msg(LOG_WARNING, SNMPP_NO_ENTPHYSNAME, row);
706 		return (NULL);
707 	}
708 
709 	node_name = basename(phys_name);
710 
711 	ret = snmp_get_int(hdl, OID_entPhysicalClass, row,
712 	    &ent_physclass, snmp_syserr_p);
713 	CHECK_LINKRESET(snmp_syserr_p, NULL)
714 	if (ret < 0) {
715 		log_msg(LOG_WARNING, SNMPP_CANT_FETCH_OBJECT_VAL,
716 		    *snmp_syserr_p ? *snmp_syserr_p : ret,
717 		    OID_entPhysicalClass, row);
718 		free(phys_name);
719 		return (NULL);
720 	}
721 
722 	switch (ent_physclass) {
723 	case SPC_OTHER:
724 		ret = snmp_get_int(hdl, OID_sunPlatPhysicalClass, row,
725 		    &sunplat_physclass, snmp_syserr_p);
726 		CHECK_LINKRESET(snmp_syserr_p, NULL)
727 		if (ret < 0) {
728 			log_msg(LOG_WARNING, SNMPP_CANT_FETCH_OBJECT_VAL,
729 			    *snmp_syserr_p ? *snmp_syserr_p : ret,
730 			    OID_sunPlatPhysicalClass, row);
731 			free(phys_name);
732 			return (NULL);
733 		}
734 
735 		if (sunplat_physclass == SSPC_ALARM) {
736 			ret = snmp_get_int(hdl, OID_sunPlatAlarmType,
737 			    row, &alarm_type, snmp_syserr_p);
738 			CHECK_LINKRESET(snmp_syserr_p, NULL)
739 			if (ret < 0) {
740 				log_msg(LOG_WARNING,
741 				    SNMPP_CANT_FETCH_OBJECT_VAL,
742 				    *snmp_syserr_p ? *snmp_syserr_p : ret,
743 				    OID_sunPlatAlarmType, row);
744 				free(phys_name);
745 				return (NULL);
746 			}
747 
748 			if (alarm_type == SSAT_VISIBLE) {
749 				ADD_NODE(PICL_CLASS_LED)
750 			} else {
751 				ADD_NODE(PICL_CLASS_ALARM)
752 			}
753 
754 			add_prop(nodeh, &proph, node_name, row, PP_STATE,
755 			    snmp_syserr_p);
756 			CHECK_LINKRESET(snmp_syserr_p, NULL)
757 		} else {
758 			ADD_NODE(PICL_CLASS_OTHER)
759 		}
760 
761 		add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
762 		    snmp_syserr_p);
763 		CHECK_LINKRESET(snmp_syserr_p, NULL)
764 		break;
765 
766 	case SPC_UNKNOWN:
767 		ADD_NODE(PICL_CLASS_UNKNOWN)
768 		break;
769 
770 	case SPC_CHASSIS:
771 		ADD_NODE(PICL_CLASS_CHASSIS)
772 		add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
773 		    snmp_syserr_p);
774 		CHECK_LINKRESET(snmp_syserr_p, NULL)
775 		break;
776 
777 	case SPC_BACKPLANE:
778 		ADD_NODE(PICL_CLASS_BACKPLANE)
779 		add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
780 		    snmp_syserr_p);
781 		CHECK_LINKRESET(snmp_syserr_p, NULL)
782 		break;
783 
784 	case SPC_CONTAINER:
785 		ADD_NODE(PICL_CLASS_CONTAINER)
786 
787 		add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
788 		    snmp_syserr_p);
789 		CHECK_LINKRESET(snmp_syserr_p, NULL)
790 
791 		add_prop(nodeh, &proph, node_name, row, PP_SLOT_TYPE,
792 		    snmp_syserr_p);
793 		CHECK_LINKRESET(snmp_syserr_p, NULL)
794 		break;
795 
796 	case SPC_POWERSUPPLY:
797 		ret = snmp_get_int(hdl, OID_sunPlatPowerSupplyClass,
798 		    row, &ps_class, snmp_syserr_p);
799 		CHECK_LINKRESET(snmp_syserr_p, NULL)
800 		if (ret < 0) {
801 			log_msg(LOG_WARNING, SNMPP_CANT_FETCH_OBJECT_VAL,
802 			    *snmp_syserr_p ? *snmp_syserr_p : ret,
803 			    OID_sunPlatPowerSupplyClass, row);
804 			free(phys_name);
805 			return (NULL);
806 		}
807 
808 		if (ps_class == SSPSC_BATTERY) {
809 			ADD_NODE(PICL_CLASS_BATTERY)
810 			add_prop(nodeh, &proph, node_name, row,
811 			    PP_BATT_STATUS, snmp_syserr_p);
812 			CHECK_LINKRESET(snmp_syserr_p, NULL)
813 		} else {
814 			ADD_NODE(PICL_CLASS_POWERSUPPLY)
815 		}
816 		add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
817 		    snmp_syserr_p);
818 		CHECK_LINKRESET(snmp_syserr_p, NULL)
819 		break;
820 
821 	case SPC_FAN:
822 		ADD_NODE(PICL_CLASS_FAN)
823 		add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
824 		    snmp_syserr_p);
825 		CHECK_LINKRESET(snmp_syserr_p, NULL)
826 		break;
827 
828 	case SPC_SENSOR:
829 		ret = snmp_get_int(hdl, OID_sunPlatSensorClass,
830 		    row, &sensor_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_sunPlatSensorClass, row);
836 			free(phys_name);
837 			return (NULL);
838 		}
839 
840 		ret = snmp_get_int(hdl, OID_sunPlatSensorType,
841 		    row, &sensor_type, snmp_syserr_p);
842 		CHECK_LINKRESET(snmp_syserr_p, NULL)
843 		if (ret < 0) {
844 			log_msg(LOG_WARNING, SNMPP_CANT_FETCH_OBJECT_VAL,
845 			    *snmp_syserr_p ? *snmp_syserr_p : ret,
846 			    OID_sunPlatSensorType, row);
847 			free(phys_name);
848 			return (NULL);
849 		}
850 
851 		if (sensor_class == SSSC_NUMERIC) {
852 			if (sensor_type == SSST_TEMPERATURE) {
853 				ADD_NODE(PICL_CLASS_TEMPERATURE_SENSOR)
854 				add_prop(nodeh, &proph, node_name, row,
855 				    PP_TEMPERATURE, snmp_syserr_p);
856 			} else if (sensor_type == SSST_VOLTAGE) {
857 				ADD_NODE(PICL_CLASS_VOLTAGE_SENSOR)
858 				add_prop(nodeh, &proph, node_name, row,
859 				    PP_VOLTAGE, snmp_syserr_p);
860 			} else if (sensor_type == SSST_CURRENT) {
861 				ADD_NODE(PICL_CLASS_CURRENT_SENSOR)
862 				add_prop(nodeh, &proph, node_name, row,
863 				    PP_CURRENT, snmp_syserr_p);
864 			} else if (sensor_type == SSST_TACHOMETER) {
865 				ADD_NODE(PICL_CLASS_RPM_SENSOR)
866 				add_prop(nodeh, &proph, node_name, row,
867 				    PP_SPEED, snmp_syserr_p);
868 			} else if (sensor_type == SSST_HUMIDITY) {
869 				ADD_NODE(PICL_CLASS_HUMIDITY_SENSOR)
870 				add_prop(nodeh, &proph, node_name, row,
871 				    PP_HUMIDITY, snmp_syserr_p);
872 			} else {
873 				ADD_NODE(PICL_CLASS_SENSOR)
874 				add_prop(nodeh, &proph, node_name, row,
875 				    PP_SENSOR_VALUE, snmp_syserr_p);
876 			}
877 			CHECK_LINKRESET(snmp_syserr_p, NULL)
878 
879 			add_prop(nodeh, &proph, node_name, row,
880 			    PP_OPSTATUS, snmp_syserr_p);
881 			CHECK_LINKRESET(snmp_syserr_p, NULL)
882 
883 			add_prop(nodeh, &proph, node_name, row,
884 			    PP_BASE_UNITS, snmp_syserr_p);
885 			CHECK_LINKRESET(snmp_syserr_p, NULL)
886 
887 			add_prop(nodeh, &proph, node_name, row,
888 			    PP_EXPONENT, snmp_syserr_p);
889 			CHECK_LINKRESET(snmp_syserr_p, NULL)
890 
891 			add_prop(nodeh, &proph, node_name, row,
892 			    PP_RATE_UNITS, snmp_syserr_p);
893 			CHECK_LINKRESET(snmp_syserr_p, NULL)
894 
895 			add_thresholds(nodeh, row, snmp_syserr_p);
896 			CHECK_LINKRESET(snmp_syserr_p, NULL)
897 
898 		} else if (sensor_class == SSSC_BINARY) {
899 			if (sensor_type == SSST_TEMPERATURE) {
900 				ADD_NODE(PICL_CLASS_TEMPERATURE_INDICATOR)
901 			} else if (sensor_type == SSST_VOLTAGE) {
902 				ADD_NODE(PICL_CLASS_VOLTAGE_INDICATOR)
903 			} else if (sensor_type == SSST_CURRENT) {
904 				ADD_NODE(PICL_CLASS_CURRENT_INDICATOR)
905 			} else if (sensor_type == SSST_TACHOMETER) {
906 				ADD_NODE(PICL_CLASS_RPM_INDICATOR)
907 			} else if (sensor_type == SSST_PRESENCE) {
908 				ADD_NODE(PICL_CLASS_PRESENCE_INDICATOR)
909 			} else if (sensor_type == SSST_HUMIDITY) {
910 				ADD_NODE(PICL_CLASS_HUMIDITY_INDICATOR)
911 			} else {
912 				ADD_NODE(PICL_CLASS_INDICATOR)
913 			}
914 
915 			add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
916 			    snmp_syserr_p);
917 			CHECK_LINKRESET(snmp_syserr_p, NULL)
918 
919 			add_prop(nodeh, &proph, node_name, row, PP_CONDITION,
920 			    snmp_syserr_p);
921 			CHECK_LINKRESET(snmp_syserr_p, NULL)
922 
923 			add_prop(nodeh, &proph, node_name, row, PP_EXPECTED,
924 			    snmp_syserr_p);
925 			CHECK_LINKRESET(snmp_syserr_p, NULL)
926 		} else {
927 			log_msg(LOG_ERR,
928 			    SNMPP_UNSUPP_SENSOR_CLASS, sensor_class, row);
929 			return (NULL);
930 		}
931 		break;
932 
933 	case SPC_MODULE:
934 		ADD_NODE(PICL_CLASS_MODULE)
935 
936 		add_prop(nodeh, &proph, node_name, row, PP_OPSTATUS,
937 		    snmp_syserr_p);
938 		CHECK_LINKRESET(snmp_syserr_p, NULL)
939 
940 		add_prop(nodeh, &proph, node_name, row, PP_REPLACEABLE,
941 		    snmp_syserr_p);
942 		CHECK_LINKRESET(snmp_syserr_p, NULL)
943 
944 		add_prop(nodeh, &proph, node_name, row, PP_HOTSWAPPABLE,
945 		    snmp_syserr_p);
946 		CHECK_LINKRESET(snmp_syserr_p, NULL)
947 		break;
948 
949 	case SPC_PORT:
950 		ADD_NODE(PICL_CLASS_PORT)
951 		break;
952 
953 	case SPC_STACK:
954 		ADD_NODE(PICL_CLASS_STACK)
955 		break;
956 
957 	default:
958 		log_msg(LOG_WARNING,
959 		    SNMPP_UNKNOWN_ENTPHYSCLASS, ent_physclass, row);
960 		free(phys_name);
961 		return (NULL);
962 	}
963 
964 	add_prop(nodeh, &proph, node_name, row, PP_DESCRIPTION, snmp_syserr_p);
965 	CHECK_LINKRESET(snmp_syserr_p, NULL)
966 
967 	add_prop(nodeh, &proph, node_name, row, PP_LABEL, snmp_syserr_p);
968 	CHECK_LINKRESET(snmp_syserr_p, NULL)
969 
970 	add_prop(nodeh, &proph, node_name, row, PP_HW_REVISION, snmp_syserr_p);
971 	CHECK_LINKRESET(snmp_syserr_p, NULL)
972 
973 	add_prop(nodeh, &proph, node_name, row, PP_FW_REVISION, snmp_syserr_p);
974 	CHECK_LINKRESET(snmp_syserr_p, NULL)
975 
976 	add_prop(nodeh, &proph, node_name, row, PP_SERIAL_NUM, snmp_syserr_p);
977 	CHECK_LINKRESET(snmp_syserr_p, NULL)
978 
979 	add_prop(nodeh, &proph, node_name, row, PP_MFG_NAME, snmp_syserr_p);
980 	CHECK_LINKRESET(snmp_syserr_p, NULL)
981 
982 	add_prop(nodeh, &proph, node_name, row, PP_MODEL_NAME, snmp_syserr_p);
983 	CHECK_LINKRESET(snmp_syserr_p, NULL)
984 
985 	add_prop(nodeh, &proph, node_name, row, PP_IS_FRU, snmp_syserr_p);
986 	CHECK_LINKRESET(snmp_syserr_p, NULL)
987 
988 	free(phys_name);
989 	save_nodeh(nodeh, row);
990 
991 	return (nodeh);
992 }
993 
994 /*
995  * Saves the node handle and the row id into physplat_nodes[]. If we're
996  * doing this in response to a hotplug event, we should've freed the
997  * old physplat_nodes before entering here to save the first node of the
998  * new physplat subtree.
999  */
1000 static void
1001 save_nodeh(picl_nodehdl_t nodeh, int row)
1002 {
1003 	size_t		sz, count;
1004 	picl_nodehdl_t	*p;
1005 
1006 	if (row >= n_physplat_nodes) {
1007 		count = (((size_t)row >> NODE_BLOCK_SHIFT) + 1) *
1008 		    N_ELEMS_IN_NODE_BLOCK;
1009 		sz = count * sizeof (picl_nodehdl_t);
1010 
1011 		p = (picl_nodehdl_t *)calloc(count, sizeof (picl_nodehdl_t));
1012 		if (p == NULL) {
1013 			log_msg(LOG_ERR, SNMPP_NO_MEM, sz);
1014 			return;
1015 		}
1016 
1017 		if (physplat_nodes) {
1018 			(void) memcpy((void *) p, (void *) physplat_nodes,
1019 			    n_physplat_nodes * sizeof (picl_nodehdl_t));
1020 			free((void *) physplat_nodes);
1021 		}
1022 
1023 		physplat_nodes = p;
1024 		n_physplat_nodes = count;
1025 	}
1026 
1027 	physplat_nodes[row] = nodeh;
1028 }
1029 
1030 static picl_nodehdl_t
1031 lookup_nodeh(int row)
1032 {
1033 	if (row >= n_physplat_nodes)
1034 		return (NULL);
1035 
1036 	return (physplat_nodes[row]);
1037 }
1038 
1039 /*
1040  * We enter this routine only when we are building the physical-platform
1041  * subtree, whether for the first time or in response to a hotplug event.
1042  * If we're here for rebuilding the tree, we have already set stale_tree
1043  * to be B_TRUE, so no one else would be accessing vol_props, n_vol_props
1044  * or volprop_ndx. If we're here to build the tree for the first time,
1045  * picld hasn't yet created doors and is running single-threaded, so no
1046  * one else would be accessing them anyway.
1047  */
1048 static void
1049 save_volprop(picl_prophdl_t prop, char *oidstr, int row, int proptype)
1050 {
1051 	vol_prophdl_t	*p;
1052 	int		count;
1053 
1054 	if (volprop_ndx == n_vol_props) {
1055 		count = n_vol_props + N_ELEMS_IN_VOLPROP_BLOCK;
1056 		p = (vol_prophdl_t *)calloc(count, sizeof (vol_prophdl_t));
1057 		if (p == NULL) {
1058 			log_msg(LOG_ERR, SNMPP_NO_MEM,
1059 			    count * sizeof (vol_prophdl_t));
1060 			return;
1061 		}
1062 
1063 		if (vol_props) {
1064 			(void) memcpy((void *) p, (void *) vol_props,
1065 			    n_vol_props * sizeof (vol_prophdl_t));
1066 			free((void *) vol_props);
1067 		}
1068 
1069 		vol_props = p;
1070 		n_vol_props += N_ELEMS_IN_VOLPROP_BLOCK;
1071 	}
1072 
1073 	vol_props[volprop_ndx].prop = prop;
1074 	vol_props[volprop_ndx].oidstr = oidstr;
1075 	vol_props[volprop_ndx].row = row;
1076 	vol_props[volprop_ndx].proptype = proptype;
1077 
1078 	volprop_ndx++;
1079 }
1080 
1081 static void
1082 check_for_stale_data(boolean_t nocache)
1083 {
1084 	int	cur_change_time;
1085 	int	ret;
1086 	int	snmp_syserr;
1087 
1088 	(void) rw_wrlock(&stale_tree_rwlp);
1089 
1090 	/*
1091 	 * Check if some other thread beat us to it
1092 	 */
1093 	if (stale_tree == B_TRUE) {
1094 		(void) rw_unlock(&stale_tree_rwlp);
1095 		return;
1096 	}
1097 
1098 	/*
1099 	 * Cache OID_entLastChangeTime for up to 10 seconds before
1100 	 * fetching it from ILOM again.  This prevents us from fetching
1101 	 * this value from ILOM when the we're filling or refreshing a
1102 	 * whole bunch of items in the cache around the same time.
1103 	 */
1104 	if (nocache == B_FALSE && time(NULL) - change_time_check <= 10) {
1105 		(void) rw_unlock(&stale_tree_rwlp);
1106 		return;
1107 	}
1108 
1109 	/*
1110 	 * Check if mib data has changed (hotplug? link-reset?)
1111 	 */
1112 	do {
1113 		snmp_syserr = 0;
1114 		ret = snmp_get_int(hdl, OID_entLastChangeTime, 0,
1115 		    &cur_change_time, &snmp_syserr);
1116 		(void) time(&change_time_check);
1117 		if ((ret == 0) && (cur_change_time == change_time)) {
1118 			(void) rw_unlock(&stale_tree_rwlp);
1119 			return;
1120 		}
1121 	} while (ret != 0 && snmp_syserr == EINTR);
1122 
1123 	/*
1124 	 * If we can't read entLastChangeTime we assume we need to rebuild
1125 	 * the tree. This will also cover the case when we need to rebuild
1126 	 * the tree because a link reset had happened.
1127 	 */
1128 	LOGPRINTF2("check_for_stale_data: LastChange times have changed, "
1129 	    "(%#x != %#x)\n", change_time, cur_change_time);
1130 
1131 	/*
1132 	 * If the mib data has changed, we need to rebuild the physical-platform
1133 	 * subtree. To do this, we set a flag to mark the tree stale,
1134 	 * so that any future reads to get value of volatile properties will
1135 	 * return PICL_PROPVALUNAVAILABLE, until the stale_tree flag
1136 	 * is reset by the tree builder thread.
1137 	 */
1138 	stale_tree = B_TRUE;
1139 	if (vol_props) {
1140 		free(vol_props);
1141 	}
1142 	vol_props = NULL;
1143 	volprop_ndx = 0;
1144 	n_vol_props = 0;
1145 
1146 	(void) rw_unlock(&stale_tree_rwlp);
1147 
1148 	(void) mutex_lock(&rebuild_tree_lock);
1149 	rebuild_tree = B_TRUE;
1150 	(void) cond_signal(&rebuild_tree_cv);
1151 	LOGPRINTF("check_for_stale_data: signalled tree builder\n");
1152 	(void) mutex_unlock(&rebuild_tree_lock);
1153 }
1154 
1155 /*
1156  * This is the critical routine.  This callback is invoked by picl whenever
1157  * it needs to fetch the value of a volatile property. The first thing we
1158  * must do, however, is to see if there has been a hotplug or a link-reset
1159  * event since the last time we built the tree and whether we need to
1160  * rebuild the tree. If so, we do whatever is necessary to make that happen,
1161  * but return PICL_PROPVALUNAVAILABLE for now, without making any further
1162  * snmp requests or accessing any globals.
1163  */
1164 static int
1165 read_volprop(ptree_rarg_t *parg, void *buf)
1166 {
1167 	char	*pstr;
1168 	int	propval;
1169 	int	i, ndx;
1170 	int	ret;
1171 	int	snmp_syserr = 0;
1172 
1173 	/*
1174 	 * First check for any event that would make us throw away
1175 	 * the existing /physical-platform subtree and rebuild
1176 	 * another one. If we are rebuilding the subtree, we just
1177 	 * return the stale value until the tree is fully built.
1178 	 */
1179 	check_for_stale_data(B_FALSE);
1180 
1181 	(void) rw_rdlock(&stale_tree_rwlp);
1182 
1183 	if (stale_tree == B_TRUE) {
1184 		(void) rw_unlock(&stale_tree_rwlp);
1185 		return (PICL_PROPVALUNAVAILABLE);
1186 	}
1187 
1188 	for (i = 0; i < volprop_ndx; i++) {
1189 		if (vol_props[i].prop == parg->proph) {
1190 			ndx = i;
1191 			break;
1192 		}
1193 	}
1194 	if (i == volprop_ndx) {
1195 		(void) rw_unlock(&stale_tree_rwlp);
1196 		log_msg(LOG_ERR, SNMPP_CANT_FIND_VOLPROP, parg->proph);
1197 		return (PICL_FAILURE);
1198 	}
1199 
1200 	/*
1201 	 * If we can't read the value, return failure. Even if this was
1202 	 * due to a link reset, between the check for stale data and now,
1203 	 * the next volatile callback by picl will initiate a tree-rebuild.
1204 	 */
1205 	ret = snmp_get_int(hdl, vol_props[ndx].oidstr, vol_props[ndx].row,
1206 	    &propval, &snmp_syserr);
1207 	if (ret < 0) {
1208 		(void) rw_unlock(&stale_tree_rwlp);
1209 		check_for_stale_data(B_TRUE);
1210 		if (stale_tree == B_TRUE) {
1211 			return (PICL_PROPVALUNAVAILABLE);
1212 		}
1213 		log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1214 		    snmp_syserr ? snmp_syserr : ret,
1215 		    vol_props[ndx].oidstr, vol_props[ndx].row);
1216 		return (PICL_FAILURE);
1217 	}
1218 
1219 	switch (vol_props[ndx].proptype) {
1220 	case VPT_PLATOPSTATE:
1221 		if (propval == SSOS_DISABLED) {
1222 			(void) strlcpy(buf, STR_SSOS_DISABLED, MAX_OPSTATE_LEN);
1223 		} else if (propval == SSOS_ENABLED) {
1224 			(void) strlcpy(buf, STR_SSOS_ENABLED, MAX_OPSTATE_LEN);
1225 		} else {
1226 			(void) rw_unlock(&stale_tree_rwlp);
1227 			log_msg(LOG_ERR, SNMPP_INV_PLAT_EQUIP_OPSTATE,
1228 			    propval, vol_props[ndx].row);
1229 			return (PICL_FAILURE);
1230 		}
1231 		break;
1232 
1233 	case VPT_NUMSENSOR:
1234 		(void) memcpy(buf, &propval, sizeof (propval));
1235 		break;
1236 
1237 	case VPT_BINSENSOR:
1238 		if (propval == ST_TRUE) {
1239 			ret = snmp_get_str(hdl,
1240 			    OID_sunPlatBinarySensorInterpretTrue,
1241 			    vol_props[ndx].row, &pstr, &snmp_syserr);
1242 			if (snmp_syserr == ECANCELED) {
1243 				(void) rw_unlock(&stale_tree_rwlp);
1244 				if (pstr)
1245 					free(pstr);
1246 				return (PICL_FAILURE);
1247 			}
1248 			if (ret < 0 || pstr == NULL) {
1249 				log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1250 				    snmp_syserr ? snmp_syserr : ret,
1251 				    OID_sunPlatBinarySensorInterpretTrue,
1252 				    vol_props[ndx].row);
1253 				(void) strlcpy(buf, STR_ST_TRUE,
1254 				    MAX_TRUTHVAL_LEN);
1255 			} else {
1256 				(void) strlcpy(buf, pstr, MAX_TRUTHVAL_LEN);
1257 			}
1258 			if (pstr)
1259 				free(pstr);
1260 		} else if (propval == ST_FALSE) {
1261 			ret = snmp_get_str(hdl,
1262 			    OID_sunPlatBinarySensorInterpretFalse,
1263 			    vol_props[ndx].row, &pstr, &snmp_syserr);
1264 			if (snmp_syserr == ECANCELED) {
1265 				(void) rw_unlock(&stale_tree_rwlp);
1266 				if (pstr)
1267 					free(pstr);
1268 				return (PICL_FAILURE);
1269 			}
1270 			if (ret < 0 || pstr == NULL) {
1271 				log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1272 				    snmp_syserr ? snmp_syserr : ret,
1273 				    OID_sunPlatBinarySensorInterpretFalse,
1274 				    vol_props[ndx].row);
1275 				(void) strlcpy(buf, STR_ST_FALSE,
1276 				    MAX_TRUTHVAL_LEN);
1277 			} else {
1278 				(void) strlcpy(buf, pstr, MAX_TRUTHVAL_LEN);
1279 			}
1280 			if (pstr)
1281 				free(pstr);
1282 		} else {
1283 			(void) rw_unlock(&stale_tree_rwlp);
1284 			log_msg(LOG_ERR, SNMPP_INV_PLAT_BINSNSR_CURRENT,
1285 			    propval, vol_props[ndx].row);
1286 			return (PICL_FAILURE);
1287 		}
1288 		break;
1289 
1290 	case VPT_ALARMSTATE:
1291 		if (propval == SSAS_OFF) {
1292 			(void) strlcpy(buf, STR_SSAS_OFF, MAX_ALARMSTATE_LEN);
1293 		} else if (propval == SSAS_STEADY) {
1294 			(void) strlcpy(buf, STR_SSAS_STEADY,
1295 			    MAX_ALARMSTATE_LEN);
1296 		} else if (propval == SSAS_ALTERNATING) {
1297 			(void) strlcpy(buf, STR_SSAS_ALTERNATING,
1298 			    MAX_ALARMSTATE_LEN);
1299 		} else {
1300 			(void) strlcpy(buf, STR_SSAS_UNKNOWN,
1301 			    MAX_ALARMSTATE_LEN);
1302 		}
1303 		break;
1304 
1305 	case VPT_BATTERYSTATUS:
1306 		switch (propval) {
1307 		case SSBS_OTHER:
1308 			(void) strlcpy(buf, STR_SSBS_OTHER,
1309 			    MAX_BATTERYSTATUS_LEN);
1310 			break;
1311 		case SSBS_FULLYCHARGED:
1312 			(void) strlcpy(buf, STR_SSBS_FULLYCHARGED,
1313 			    MAX_BATTERYSTATUS_LEN);
1314 			break;
1315 		case SSBS_LOW:
1316 			(void) strlcpy(buf, STR_SSBS_LOW,
1317 			    MAX_BATTERYSTATUS_LEN);
1318 			break;
1319 		case SSBS_CRITICAL:
1320 			(void) strlcpy(buf, STR_SSBS_CRITICAL,
1321 			    MAX_BATTERYSTATUS_LEN);
1322 			break;
1323 		case SSBS_CHARGING:
1324 			(void) strlcpy(buf, STR_SSBS_CHARGING,
1325 			    MAX_BATTERYSTATUS_LEN);
1326 			break;
1327 		case SSBS_CHARGING_AND_LOW:
1328 			(void) strlcpy(buf, STR_SSBS_CHARGING_AND_LOW,
1329 			    MAX_BATTERYSTATUS_LEN);
1330 			break;
1331 		case SSBS_CHARGING_AND_HIGH:
1332 			(void) strlcpy(buf, STR_SSBS_CHARGING_AND_HIGH,
1333 			    MAX_BATTERYSTATUS_LEN);
1334 			break;
1335 		case SSBS_CHARGING_AND_CRITICAL:
1336 			(void) strlcpy(buf, STR_SSBS_CHARGING_AND_CRITICAL,
1337 			    MAX_BATTERYSTATUS_LEN);
1338 			break;
1339 		case SSBS_UNDEFINED:
1340 			(void) strlcpy(buf, STR_SSBS_UNDEFINED,
1341 			    MAX_BATTERYSTATUS_LEN);
1342 			break;
1343 		case SSBS_PARTIALLY_CHARGED:
1344 			(void) strlcpy(buf, STR_SSBS_PARTIALLY_CHARGED,
1345 			    MAX_BATTERYSTATUS_LEN);
1346 			break;
1347 		case SSBS_UNKNOWN:
1348 		default:
1349 			(void) strlcpy(buf, STR_SSBS_UNKNOWN,
1350 			    MAX_BATTERYSTATUS_LEN);
1351 			break;
1352 		}
1353 		break;
1354 	}
1355 
1356 	(void) rw_unlock(&stale_tree_rwlp);
1357 
1358 	return (PICL_SUCCESS);
1359 }
1360 
1361 static void
1362 threshold(picl_nodehdl_t node, char *oidstr, int row, char *propname,
1363     int *snmp_syserr_p)
1364 {
1365 	picl_prophdl_t	prop;
1366 	int		err;
1367 	int		val;
1368 
1369 	if ((err = snmp_get_int(hdl, oidstr, row, &val, snmp_syserr_p)) != -1) {
1370 		err = add_volatile_prop(node, propname, PICL_PTYPE_INT,
1371 		    PICL_READ, sizeof (int), read_volprop, NULL, &prop);
1372 		if (err == PICL_SUCCESS)
1373 			save_volprop(prop, oidstr, row, VPT_NUMSENSOR);
1374 	} else
1375 		log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1376 		    *snmp_syserr_p ? *snmp_syserr_p : err, oidstr, row);
1377 }
1378 
1379 static void
1380 add_thresholds(picl_nodehdl_t node, int row, int *snmp_syserr_p)
1381 {
1382 	uchar_t	*bitstr = NULL;
1383 	uchar_t	enabled;
1384 	uint_t	nbytes;
1385 	int	ret;
1386 
1387 	ret = snmp_get_str(hdl,
1388 	    OID_sunPlatNumericSensorEnabledThresholds,
1389 	    row, (char **)&bitstr, snmp_syserr_p);
1390 	if (ret == -1) {
1391 		log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1392 		    *snmp_syserr_p ? *snmp_syserr_p : ret,
1393 		    OID_sunPlatNumericSensorEnabledThresholds, row);
1394 	} else {
1395 		nbytes = strlen((const char *)bitstr);
1396 	}
1397 
1398 	CHECK_LINKRESET_VOID(snmp_syserr_p);
1399 
1400 	/*
1401 	 * No bit string of threshold masks was returned, so we can't
1402 	 * assume that any thresholds exist.
1403 	 *
1404 	 * This mask prevents us from attempting to fetch thresholds
1405 	 * which don't apply to the sensor or that aren't there anyway,
1406 	 * That speeds up the plug-in significantly since otherwise it
1407 	 * takes several seconds to time out.
1408 	 */
1409 	if (ret < 0 || bitstr == NULL || nbytes == 0 || 2 < nbytes) {
1410 		if (bitstr)
1411 			free(bitstr);
1412 		return;
1413 	} else if (nbytes == 1) {
1414 		/*
1415 		 * The ALOM snmp agent doesn't adhere to the BER rules for
1416 		 * encoding bit strings. While the BER states that bitstrings
1417 		 * must begin from the second octet after length, and the
1418 		 * first octet after length must indicate the number of unused
1419 		 * bits in the last octet, the snmp agent simply sends the
1420 		 * bitstring data as if it were octet string -- that is, the
1421 		 * "unused bits" octet is missing.
1422 		 */
1423 		enabled = bitstr[0];
1424 	} else if (nbytes == 2)
1425 		enabled = bitstr[1];
1426 
1427 	if (bitstr) {
1428 		free(bitstr);
1429 	}
1430 
1431 	if (enabled & LOWER_FATAL) {
1432 		threshold(node,
1433 		    OID_sunPlatNumericSensorLowerThresholdFatal, row,
1434 		    PICL_PROP_LOW_POWER_OFF, snmp_syserr_p);
1435 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1436 	}
1437 	if (enabled & LOWER_CRITICAL) {
1438 		threshold(node,
1439 		    OID_sunPlatNumericSensorLowerThresholdCritical, row,
1440 		    PICL_PROP_LOW_SHUTDOWN, snmp_syserr_p);
1441 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1442 	}
1443 	if (enabled & LOWER_NON_CRITICAL) {
1444 		threshold(node,
1445 		    OID_sunPlatNumericSensorLowerThresholdNonCritical, row,
1446 		    PICL_PROP_LOW_WARNING, snmp_syserr_p);
1447 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1448 	}
1449 	if (enabled & UPPER_NON_CRITICAL) {
1450 		threshold(node,
1451 		    OID_sunPlatNumericSensorUpperThresholdNonCritical, row,
1452 		    PICL_PROP_HIGH_WARNING, snmp_syserr_p);
1453 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1454 	}
1455 	if (enabled & UPPER_CRITICAL) {
1456 		threshold(node,
1457 		    OID_sunPlatNumericSensorUpperThresholdCritical, row,
1458 		    PICL_PROP_HIGH_SHUTDOWN, snmp_syserr_p);
1459 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1460 	}
1461 	if (enabled & UPPER_FATAL) {
1462 		threshold(node,
1463 		    OID_sunPlatNumericSensorUpperThresholdFatal, row,
1464 		    PICL_PROP_HIGH_POWER_OFF, snmp_syserr_p);
1465 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1466 	}
1467 }
1468 
1469 static char *
1470 get_slot_type(int row, int *snmp_syserr_p)
1471 {
1472 	char	*p;
1473 	char	*slott = NULL;
1474 	int	ret;
1475 
1476 	ret = snmp_get_str(hdl, OID_sunPlatEquipmentHolderAcceptableTypes,
1477 	    row, &p, snmp_syserr_p);
1478 	CHECK_LINKRESET(snmp_syserr_p, NULL)
1479 
1480 	if ((ret == 0) && p && *p) {
1481 		slott = p;
1482 		if ((p = strchr(slott, '\n')) != NULL)
1483 			*p = 0;
1484 	} else {
1485 		log_msg(LOG_WARNING, SNMPP_NO_SLOT_TYPE, row);
1486 		if (p) {
1487 			free(p);
1488 		}
1489 	}
1490 
1491 	return (slott);
1492 }
1493 
1494 /*
1495  * Create and add the specified volatile property
1496  */
1497 static int
1498 add_volatile_prop(picl_nodehdl_t node, char *name, int type, int access,
1499     int size, int (*rdfunc)(ptree_rarg_t *, void *),
1500     int (*wrfunc)(ptree_warg_t *, const void *), picl_prophdl_t *propp)
1501 {
1502 	ptree_propinfo_t	propinfo;
1503 	picl_prophdl_t		prop;
1504 	int			err;
1505 
1506 	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
1507 	    type, (access|PICL_VOLATILE), size, name, rdfunc, wrfunc);
1508 	if (err != PICL_SUCCESS) {
1509 		log_msg(LOG_ERR, SNMPP_CANT_INIT_PROPINFO, err);
1510 		return (err);
1511 	}
1512 
1513 	err = ptree_create_and_add_prop(node, &propinfo, NULL, &prop);
1514 	if (err != PICL_SUCCESS) {
1515 		log_msg(LOG_ERR, SNMPP_CANT_ADD_PROP, err, node);
1516 		return (err);
1517 	}
1518 
1519 	if (propp)
1520 		*propp = prop;
1521 
1522 	return (PICL_SUCCESS);
1523 }
1524 
1525 /*
1526  * Add the specified string property to the node
1527  */
1528 static int
1529 add_string_prop(picl_nodehdl_t node, char *propname, char *propval)
1530 {
1531 	ptree_propinfo_t	propinfo;
1532 	int			err;
1533 
1534 	if (*propval == '\0')
1535 		return (PICL_SUCCESS);
1536 
1537 	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
1538 	    PICL_PTYPE_CHARSTRING, PICL_READ, strlen(propval) + 1,
1539 	    propname, NULL, NULL);
1540 	if (err != PICL_SUCCESS) {
1541 		log_msg(LOG_ERR, SNMPP_CANT_INIT_STR_PROPINFO, err);
1542 		return (err);
1543 	}
1544 
1545 	err = ptree_create_and_add_prop(node, &propinfo, propval, NULL);
1546 	if (err != PICL_SUCCESS) {
1547 		log_msg(LOG_ERR, SNMPP_CANT_ADD_STR_PROP, err, node);
1548 		return (err);
1549 	}
1550 
1551 	return (PICL_SUCCESS);
1552 }
1553 
1554 /*
1555  * Add the specified void property to the node
1556  */
1557 static int
1558 add_void_prop(picl_nodehdl_t node, char *propname)
1559 {
1560 	ptree_propinfo_t	propinfo;
1561 	int			err;
1562 
1563 	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
1564 	    PICL_PTYPE_VOID, PICL_READ, 0, propname, NULL, NULL);
1565 	if (err != PICL_SUCCESS) {
1566 		log_msg(LOG_ERR, SNMPP_CANT_INIT_VOID_PROPINFO, err);
1567 		return (err);
1568 	}
1569 
1570 	err = ptree_create_and_add_prop(node, &propinfo, NULL, NULL);
1571 	if (err != PICL_SUCCESS) {
1572 		log_msg(LOG_ERR, SNMPP_CANT_ADD_VOID_PROP, err, node);
1573 		return (err);
1574 	}
1575 
1576 	return (PICL_SUCCESS);
1577 }
1578 
1579 static void
1580 add_prop(picl_nodehdl_t nodeh, picl_prophdl_t *php, char *label,
1581     int row, sp_propid_t pp, int *snmp_syserr_p)
1582 {
1583 	char	*serial_num;
1584 	char	*slot_type;
1585 	char	*fw_revision, *hw_revision;
1586 	char	*mfg_name, *model_name;
1587 	char	*phys_descr;
1588 	int	val;
1589 	int	ret;
1590 
1591 	switch (pp) {
1592 	case PP_SERIAL_NUM:
1593 		ret = snmp_get_str(hdl, OID_entPhysicalSerialNum,
1594 		    row, &serial_num, snmp_syserr_p);
1595 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1596 		if ((ret == 0) && serial_num) {
1597 			(void) add_string_prop(nodeh,
1598 			    PICL_PROP_SERIAL_NUMBER, serial_num);
1599 			free((void *) serial_num);
1600 		}
1601 		if (ret == -1)
1602 			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1603 			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1604 			    OID_entPhysicalSerialNum, row);
1605 		break;
1606 
1607 	case PP_SLOT_TYPE:
1608 		if ((slot_type = get_slot_type(row, snmp_syserr_p)) == NULL) {
1609 			CHECK_LINKRESET_VOID(snmp_syserr_p)
1610 			(void) add_string_prop(nodeh,
1611 			    PICL_PROP_SLOT_TYPE, DEFAULT_SLOT_TYPE);
1612 		} else {
1613 			(void) add_string_prop(nodeh,
1614 			    PICL_PROP_SLOT_TYPE, slot_type);
1615 			free((void *) slot_type);
1616 		}
1617 		break;
1618 
1619 	case PP_STATE:
1620 		ret = add_volatile_prop(nodeh, PICL_PROP_STATE,
1621 		    PICL_PTYPE_CHARSTRING, PICL_READ, MAX_ALARMSTATE_LEN,
1622 		    read_volprop, NULL, php);
1623 		if (ret == PICL_SUCCESS) {
1624 			save_volprop(*php, OID_sunPlatAlarmState, row,
1625 			    VPT_ALARMSTATE);
1626 		}
1627 		break;
1628 
1629 	case PP_OPSTATUS:
1630 		ret = add_volatile_prop(nodeh, PICL_PROP_OPERATIONAL_STATUS,
1631 		    PICL_PTYPE_CHARSTRING, PICL_READ, MAX_OPSTATE_LEN,
1632 		    read_volprop, NULL, php);
1633 		if (ret == PICL_SUCCESS) {
1634 			save_volprop(*php,
1635 			    OID_sunPlatEquipmentOperationalState, row,
1636 			    VPT_PLATOPSTATE);
1637 		}
1638 		break;
1639 
1640 	case PP_BATT_STATUS:
1641 		ret = add_volatile_prop(nodeh, PICL_PROP_BATTERY_STATUS,
1642 		    PICL_PTYPE_CHARSTRING, PICL_READ, MAX_BATTERYSTATUS_LEN,
1643 		    read_volprop, NULL, php);
1644 		if (ret == PICL_SUCCESS) {
1645 			save_volprop(*php, OID_sunPlatBatteryStatus, row,
1646 			    VPT_BATTERYSTATUS);
1647 		}
1648 		break;
1649 
1650 	case PP_TEMPERATURE:
1651 		ret = add_volatile_prop(nodeh, PICL_PROP_TEMPERATURE,
1652 		    PICL_PTYPE_INT, PICL_READ, sizeof (int), read_volprop,
1653 		    NULL, php);
1654 		if (ret == PICL_SUCCESS) {
1655 			save_volprop(*php, OID_sunPlatNumericSensorCurrent,
1656 			    row, VPT_NUMSENSOR);
1657 		}
1658 		break;
1659 
1660 	case PP_VOLTAGE:
1661 		ret = add_volatile_prop(nodeh, PICL_PROP_VOLTAGE,
1662 		    PICL_PTYPE_INT, PICL_READ, sizeof (int), read_volprop,
1663 		    NULL, php);
1664 		if (ret == PICL_SUCCESS) {
1665 			save_volprop(*php, OID_sunPlatNumericSensorCurrent,
1666 			    row, VPT_NUMSENSOR);
1667 		}
1668 		break;
1669 
1670 	case PP_CURRENT:
1671 		ret = add_volatile_prop(nodeh, PICL_PROP_CURRENT,
1672 		    PICL_PTYPE_INT, PICL_READ, sizeof (int), read_volprop,
1673 		    NULL, php);
1674 		if (ret == PICL_SUCCESS) {
1675 			save_volprop(*php, OID_sunPlatNumericSensorCurrent,
1676 			    row, VPT_NUMSENSOR);
1677 		}
1678 		break;
1679 
1680 	case PP_SPEED:
1681 		ret = add_volatile_prop(nodeh, PICL_PROP_SPEED, PICL_PTYPE_INT,
1682 		    PICL_READ, sizeof (int), read_volprop, NULL, php);
1683 		if (ret == PICL_SUCCESS) {
1684 			save_volprop(*php, OID_sunPlatNumericSensorCurrent,
1685 			    row, VPT_NUMSENSOR);
1686 		}
1687 		break;
1688 
1689 	case PP_SENSOR_VALUE:
1690 		ret = add_volatile_prop(nodeh, PICL_PROP_SENSOR_VALUE,
1691 		    PICL_PTYPE_INT, PICL_READ, sizeof (int), read_volprop,
1692 		    NULL, php);
1693 		if (ret == PICL_SUCCESS) {
1694 			save_volprop(*php, OID_sunPlatNumericSensorCurrent,
1695 			    row, VPT_NUMSENSOR);
1696 		}
1697 		break;
1698 
1699 	case PP_CONDITION:
1700 		ret = add_volatile_prop(nodeh, PICL_PROP_CONDITION,
1701 		    PICL_PTYPE_CHARSTRING, PICL_READ, MAX_TRUTHVAL_LEN,
1702 		    read_volprop, NULL, php);
1703 		if (ret == PICL_SUCCESS) {
1704 			save_volprop(*php, OID_sunPlatBinarySensorCurrent,
1705 			    row, VPT_BINSENSOR);
1706 		}
1707 		break;
1708 
1709 	case PP_EXPECTED:
1710 		ret = add_volatile_prop(nodeh, PICL_PROP_EXPECTED,
1711 		    PICL_PTYPE_CHARSTRING, PICL_READ, MAX_TRUTHVAL_LEN,
1712 		    read_volprop, NULL, php);
1713 		if (ret == PICL_SUCCESS) {
1714 			save_volprop(*php, OID_sunPlatBinarySensorExpected,
1715 			    row, VPT_BINSENSOR);
1716 		}
1717 		break;
1718 
1719 	case PP_EXPONENT:
1720 		ret = add_volatile_prop(nodeh, PICL_PROP_EXPONENT,
1721 		    PICL_PTYPE_INT, PICL_READ, sizeof (int), read_volprop,
1722 		    NULL, php);
1723 		if (ret == PICL_SUCCESS) {
1724 			save_volprop(*php, OID_sunPlatNumericSensorExponent,
1725 			    row, VPT_NUMSENSOR);
1726 		}
1727 		break;
1728 
1729 	case PP_REPLACEABLE:
1730 		ret = snmp_get_int(hdl, OID_sunPlatCircuitPackReplaceable,
1731 		    row, &val, snmp_syserr_p);
1732 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1733 		if ((ret == 0) && (val == ST_TRUE))
1734 			(void) add_void_prop(nodeh, PICL_PROP_IS_REPLACEABLE);
1735 		if (ret == -1)
1736 			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1737 			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1738 			    OID_sunPlatCircuitPackReplaceable, row);
1739 		break;
1740 
1741 	case PP_HOTSWAPPABLE:
1742 		ret = snmp_get_int(hdl, OID_sunPlatCircuitPackHotSwappable,
1743 		    row, &val, snmp_syserr_p);
1744 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1745 		if ((ret == 0) && (val == ST_TRUE))
1746 			(void) add_void_prop(nodeh, PICL_PROP_IS_HOT_SWAPPABLE);
1747 		if (ret == -1)
1748 			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1749 			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1750 			    OID_sunPlatCircuitPackHotSwappable, row);
1751 		break;
1752 
1753 	case PP_IS_FRU:
1754 		ret = snmp_get_int(hdl, OID_entPhysicalIsFRU, row,
1755 		    &val, snmp_syserr_p);
1756 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1757 		if ((ret == 0) && (val == ST_TRUE))
1758 			(void) add_void_prop(nodeh, PICL_PROP_IS_FRU);
1759 		if (ret == -1)
1760 			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1761 			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1762 			    OID_entPhysicalIsFRU, row);
1763 		break;
1764 
1765 	case PP_HW_REVISION:
1766 		ret = snmp_get_str(hdl, OID_entPhysicalHardwareRev,
1767 		    row, &hw_revision, snmp_syserr_p);
1768 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1769 		if ((ret == 0) && hw_revision) {
1770 			(void) add_string_prop(nodeh,
1771 			    PICL_PROP_HW_REVISION, hw_revision);
1772 			free((void *) hw_revision);
1773 		}
1774 		if (ret == -1)
1775 			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1776 			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1777 			    OID_entPhysicalHardwareRev, row);
1778 		break;
1779 
1780 	case PP_FW_REVISION:
1781 		ret = snmp_get_str(hdl, OID_entPhysicalFirmwareRev,
1782 		    row, &fw_revision, snmp_syserr_p);
1783 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1784 		if ((ret == 0) && fw_revision) {
1785 			(void) add_string_prop(nodeh,
1786 			    PICL_PROP_FW_REVISION, fw_revision);
1787 			free((void *) fw_revision);
1788 		}
1789 		if (ret == -1)
1790 			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1791 			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1792 			    OID_entPhysicalFirmwareRev, row);
1793 		break;
1794 
1795 	case PP_MFG_NAME:
1796 		ret = snmp_get_str(hdl, OID_entPhysicalMfgName,
1797 		    row, &mfg_name, snmp_syserr_p);
1798 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1799 		if ((ret == 0) && mfg_name) {
1800 			(void) add_string_prop(nodeh,
1801 			    PICL_PROP_MFG_NAME, mfg_name);
1802 			free((void *) mfg_name);
1803 		}
1804 		if (ret == -1)
1805 			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1806 			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1807 			    OID_entPhysicalMfgName, row);
1808 		break;
1809 
1810 	case PP_MODEL_NAME:
1811 		ret = snmp_get_str(hdl, OID_entPhysicalModelName,
1812 		    row, &model_name, snmp_syserr_p);
1813 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1814 		if ((ret == 0) && model_name) {
1815 			(void) add_string_prop(nodeh,
1816 			    PICL_PROP_MODEL_NAME, model_name);
1817 			free((void *) model_name);
1818 		}
1819 		if (ret == -1)
1820 			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1821 			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1822 			    OID_entPhysicalModelName, row);
1823 		break;
1824 
1825 	case PP_DESCRIPTION:
1826 		ret = snmp_get_str(hdl, OID_entPhysicalDescr,
1827 		    row, &phys_descr, snmp_syserr_p);
1828 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1829 		if ((ret == 0) && phys_descr) {
1830 			(void) add_string_prop(nodeh,
1831 			    PICL_PROP_PHYS_DESCRIPTION, phys_descr);
1832 			free((void *) phys_descr);
1833 		}
1834 		if (ret == -1)
1835 			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1836 			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1837 			    OID_entPhysicalDescr, row);
1838 		break;
1839 
1840 	case PP_LABEL:
1841 		if (label && *label)
1842 			(void) add_string_prop(nodeh, PICL_PROP_LABEL, label);
1843 		break;
1844 
1845 	case PP_BASE_UNITS:
1846 		ret = snmp_get_int(hdl, OID_sunPlatNumericSensorBaseUnits,
1847 		    row, &val, snmp_syserr_p);
1848 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1849 		if ((ret == 0) && (val > 0) && (val < n_baseunits)) {
1850 			(void) add_string_prop(nodeh,
1851 			    PICL_PROP_BASE_UNITS, sensor_baseunits[val]);
1852 		}
1853 		if (ret == -1)
1854 			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1855 			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1856 			    OID_sunPlatNumericSensorBaseUnits, row);
1857 		break;
1858 
1859 	case PP_RATE_UNITS:
1860 		ret = snmp_get_int(hdl, OID_sunPlatNumericSensorRateUnits,
1861 		    row, &val, snmp_syserr_p);
1862 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1863 		if ((ret == 0) && (val > 0) && (val < n_rateunits)) {
1864 			(void) add_string_prop(nodeh,
1865 			    PICL_PROP_RATE_UNITS, sensor_rateunits[val]);
1866 		}
1867 		if (ret == -1)
1868 			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1869 			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1870 			    OID_sunPlatNumericSensorRateUnits, row);
1871 		break;
1872 	}
1873 }
1874 
1875 /*
1876  * Initialize the SNMP library's cache refresh subsystem, then periodically
1877  * process refresh job to prevent cache entries from expiring.
1878  */
1879 /*ARGSUSED*/
1880 static void *
1881 cache_refresher(void *arg)
1882 {
1883 	int		jobs;
1884 	int		next_expiration;
1885 	timestruc_t	to;
1886 	hrtime_t	cycle_start, cycle_elapsed;
1887 
1888 	/*
1889 	 * Initialize refresh subsystem
1890 	 */
1891 	LOGPRINTF("Initializing SNMP refresh subsystem.\n");
1892 	if (snmp_refresh_init() < 0) {
1893 		return ((void *)-1);
1894 	}
1895 
1896 	(void) mutex_lock(&cache_refresh_lock);
1897 
1898 
1899 	for (;;) {
1900 		cycle_start = gethrtime();
1901 
1902 		/*
1903 		 * Process jobs from the snmp cache refresh work queue until one
1904 		 * of the following conditions is true:
1905 		 * 1) we are told to exit, or
1906 		 * 2) we have processed at least as many jobs as recommended by
1907 		 * the library, and the next job expiration is at least
1908 		 * CACHE_REFRESH_MIN_WINDOW * seconds away.
1909 		 */
1910 		jobs = snmp_refresh_get_cycle_hint(CACHE_REFRESH_CYCLE);
1911 		while ((cache_refresh_thr_exit == B_FALSE) && (jobs > 0)) {
1912 			(void) snmp_refresh_process_job();
1913 			jobs--;
1914 		}
1915 
1916 		next_expiration = snmp_refresh_get_next_expiration();
1917 		while ((cache_refresh_thr_exit == B_FALSE) &&
1918 		    ((next_expiration >= 0) &&
1919 		    (next_expiration < CACHE_REFRESH_MIN_WINDOW))) {
1920 			(void) snmp_refresh_process_job();
1921 			next_expiration = snmp_refresh_get_next_expiration();
1922 		}
1923 
1924 		/*
1925 		 * As long as we haven't been told to exit, sleep for
1926 		 * CACHE_REFRESH_CYCLE seconds minus the amount of time that has
1927 		 * elapsed since this cycle started.  If the elapsed time is
1928 		 * equal to or greater than 60 seconds, skip sleeping entirely.
1929 		 */
1930 		cycle_elapsed = (gethrtime() - cycle_start) / NANOSEC;
1931 		if ((cache_refresh_thr_exit == B_FALSE) &&
1932 		    (cycle_elapsed < CACHE_REFRESH_CYCLE)) {
1933 			to.tv_sec = CACHE_REFRESH_CYCLE - cycle_elapsed;
1934 			to.tv_nsec = 0;
1935 			(void) cond_reltimedwait(&cache_refresh_cv,
1936 			    &cache_refresh_lock, &to);
1937 		}
1938 
1939 		/*
1940 		 * If we have been told to exit, clean up and bail out.
1941 		 */
1942 		if (cache_refresh_thr_exit == B_TRUE) {
1943 			snmp_refresh_fini();
1944 			(void) mutex_unlock(&cache_refresh_lock);
1945 			LOGPRINTF("cache_refresher: time to exit\n");
1946 			return (NULL);
1947 		}
1948 
1949 	}
1950 
1951 	/*NOTREACHED*/
1952 	return (NULL);
1953 }
1954 
1955 /*
1956  * Check to see if the cache_refresher thread is running.  If it is, signal it
1957  * to terminate and clean up associated data structures.
1958  */
1959 void
1960 cache_refresher_fini(void)
1961 {
1962 	/* if the thread isn't running, there is nothing to do */
1963 	if (cache_refresh_thr_exit == B_TRUE)
1964 		return;
1965 
1966 	/* wake up the cache_refresher thread, tell it to exit */
1967 	(void) mutex_lock(&cache_refresh_lock);
1968 	cache_refresh_thr_exit = B_TRUE;
1969 	(void) cond_signal(&cache_refresh_cv);
1970 	(void) mutex_unlock(&cache_refresh_lock);
1971 
1972 	/* reap the thread */
1973 	(void) thr_join(cache_refresh_thr_id, NULL, NULL);
1974 
1975 	/* finish cleanup... */
1976 	(void) cond_destroy(&cache_refresh_cv);
1977 	(void) mutex_destroy(&cache_refresh_lock);
1978 }
1979 
1980 /*VARARGS2*/
1981 static void
1982 log_msg(int pri, const char *fmt, ...)
1983 {
1984 	va_list ap;
1985 
1986 	va_start(ap, fmt);
1987 	vsyslog(pri, fmt, ap);
1988 	va_end(ap);
1989 }
1990 
1991 #ifdef SNMPPLUGIN_DEBUG
1992 
1993 static void
1994 snmpplugin_log_init(void)
1995 {
1996 	(void) mutex_init(&snmpplugin_dbuf_lock, USYNC_THREAD, NULL);
1997 }
1998 
1999 static void
2000 snmpplugin_log(const char *fmt, ...)
2001 {
2002 	va_list	ap;
2003 
2004 	(void) mutex_lock(&snmpplugin_dbuf_lock);
2005 
2006 	va_start(ap, fmt);
2007 	(void) vsnprintf(snmpplugin_lbuf, SNMPPLUGIN_DMAX_LINE, fmt, ap);
2008 	snmpplugin_log_append();
2009 	va_end(ap);
2010 
2011 	(void) mutex_unlock(&snmpplugin_dbuf_lock);
2012 }
2013 
2014 static void
2015 snmpplugin_log_append(void)
2016 {
2017 	int	len;
2018 
2019 	len = strlen(snmpplugin_lbuf);
2020 
2021 	if ((snmpplugin_dbuf_curp + len) >=
2022 	    (snmpplugin_dbuf + snmpplugin_dbuf_sz)) {
2023 		snmpplugin_dbuf_realloc();
2024 		if (snmpplugin_dbuf == NULL) {
2025 			return;
2026 		}
2027 	}
2028 
2029 	(void) strcpy(snmpplugin_dbuf_curp, snmpplugin_lbuf);
2030 	snmpplugin_dbuf_curp += len;
2031 }
2032 
2033 static void
2034 snmpplugin_dbuf_realloc(void)
2035 {
2036 	char	*p;
2037 	size_t	offset = 0;
2038 	size_t	count;
2039 
2040 	count = snmpplugin_dbuf_sz + SNMPPLUGIN_DBLOCK_SZ;
2041 	if ((p = (char *)calloc(count, 1)) == NULL) {
2042 		snmpplugin_dbuf_overflow++;
2043 		snmpplugin_dbuf_curp = snmpplugin_dbuf;
2044 		return;
2045 	}
2046 
2047 	if (snmpplugin_dbuf) {
2048 		offset = snmpplugin_dbuf_curp - snmpplugin_dbuf;
2049 		(void) memcpy(p, snmpplugin_dbuf, snmpplugin_dbuf_sz);
2050 		free(snmpplugin_dbuf);
2051 	}
2052 
2053 	snmpplugin_dbuf = p;
2054 	snmpplugin_dbuf_sz += SNMPPLUGIN_DBLOCK_SZ;
2055 
2056 	snmpplugin_dbuf_curp = snmpplugin_dbuf + offset;
2057 }
2058 #endif
2059