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