xref: /illumos-gate/usr/src/cmd/picl/plugins/sun4v/snmp/snmpplugin.c (revision 968633ad8faee931821fd6b656eb0d96d4b186c0)
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 	do {
1106 		snmp_syserr = 0;
1107 		ret = snmp_get_int(hdl, OID_entLastChangeTime, 0,
1108 		    &cur_change_time, &snmp_syserr);
1109 		(void) time(&change_time_check);
1110 		if ((ret == 0) && (cur_change_time == change_time)) {
1111 			(void) rw_unlock(&stale_tree_rwlp);
1112 			return;
1113 		}
1114 	} while (ret != 0 && snmp_syserr == EINTR);
1115 
1116 	/*
1117 	 * If we can't read entLastChangeTime we assume we need to rebuild
1118 	 * the tree. This will also cover the case when we need to rebuild
1119 	 * the tree because a link reset had happened.
1120 	 */
1121 	LOGPRINTF2("check_for_stale_data: LastChange times have changed, "
1122 	    "(%#x != %#x)\n", change_time, cur_change_time);
1123 
1124 	/*
1125 	 * If the mib data has changed, we need to rebuild the physical-platform
1126 	 * subtree. To do this, we set a flag to mark the tree stale,
1127 	 * so that any future reads to get value of volatile properties will
1128 	 * return PICL_PROPVALUNAVAILABLE, until the stale_tree flag
1129 	 * is reset by the tree builder thread.
1130 	 */
1131 	stale_tree = B_TRUE;
1132 	if (vol_props) {
1133 		free(vol_props);
1134 	}
1135 	vol_props = NULL;
1136 	volprop_ndx = 0;
1137 	n_vol_props = 0;
1138 
1139 	(void) rw_unlock(&stale_tree_rwlp);
1140 
1141 	(void) mutex_lock(&rebuild_tree_lock);
1142 	rebuild_tree = B_TRUE;
1143 	(void) cond_signal(&rebuild_tree_cv);
1144 	LOGPRINTF("check_for_stale_data: signalled tree builder\n");
1145 	(void) mutex_unlock(&rebuild_tree_lock);
1146 }
1147 
1148 /*
1149  * This is the critical routine.  This callback is invoked by picl whenever
1150  * it needs to fetch the value of a volatile property. The first thing we
1151  * must do, however, is to see if there has been a hotplug or a link-reset
1152  * event since the last time we built the tree and whether we need to
1153  * rebuild the tree. If so, we do whatever is necessary to make that happen,
1154  * but return PICL_PROPVALUNAVAILABLE for now, without making any further
1155  * snmp requests or accessing any globals.
1156  */
1157 static int
1158 read_volprop(ptree_rarg_t *parg, void *buf)
1159 {
1160 	char	*pstr;
1161 	int	propval;
1162 	int	i, ndx;
1163 	int	ret;
1164 	int	snmp_syserr = 0;
1165 
1166 	/*
1167 	 * First check for any event that would make us throw away
1168 	 * the existing /physical-platform subtree and rebuild
1169 	 * another one. If we are rebuilding the subtree, we just
1170 	 * return the stale value until the tree is fully built.
1171 	 */
1172 	check_for_stale_data(B_FALSE);
1173 
1174 	(void) rw_rdlock(&stale_tree_rwlp);
1175 
1176 	if (stale_tree == B_TRUE) {
1177 		(void) rw_unlock(&stale_tree_rwlp);
1178 		return (PICL_PROPVALUNAVAILABLE);
1179 	}
1180 
1181 	for (i = 0; i < volprop_ndx; i++) {
1182 		if (vol_props[i].prop == parg->proph) {
1183 			ndx = i;
1184 			break;
1185 		}
1186 	}
1187 	if (i == volprop_ndx) {
1188 		(void) rw_unlock(&stale_tree_rwlp);
1189 		log_msg(LOG_ERR, SNMPP_CANT_FIND_VOLPROP, parg->proph);
1190 		return (PICL_FAILURE);
1191 	}
1192 
1193 	/*
1194 	 * If we can't read the value, return failure. Even if this was
1195 	 * due to a link reset, between the check for stale data and now,
1196 	 * the next volatile callback by picl will initiate a tree-rebuild.
1197 	 */
1198 	ret = snmp_get_int(hdl, vol_props[ndx].oidstr, vol_props[ndx].row,
1199 	    &propval, &snmp_syserr);
1200 	if (ret < 0) {
1201 		(void) rw_unlock(&stale_tree_rwlp);
1202 		check_for_stale_data(B_TRUE);
1203 		if (stale_tree == B_TRUE) {
1204 			return (PICL_PROPVALUNAVAILABLE);
1205 		}
1206 		log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1207 		    snmp_syserr ? snmp_syserr : ret,
1208 		    vol_props[ndx].oidstr, vol_props[ndx].row);
1209 		return (PICL_FAILURE);
1210 	}
1211 
1212 	switch (vol_props[ndx].proptype) {
1213 	case VPT_PLATOPSTATE:
1214 		if (propval == SSOS_DISABLED) {
1215 			(void) strlcpy(buf, STR_SSOS_DISABLED, MAX_OPSTATE_LEN);
1216 		} else if (propval == SSOS_ENABLED) {
1217 			(void) strlcpy(buf, STR_SSOS_ENABLED, MAX_OPSTATE_LEN);
1218 		} else {
1219 			(void) rw_unlock(&stale_tree_rwlp);
1220 			log_msg(LOG_ERR, SNMPP_INV_PLAT_EQUIP_OPSTATE,
1221 			    propval, vol_props[ndx].row);
1222 			return (PICL_FAILURE);
1223 		}
1224 		break;
1225 
1226 	case VPT_NUMSENSOR:
1227 		(void) memcpy(buf, &propval, sizeof (propval));
1228 		break;
1229 
1230 	case VPT_BINSENSOR:
1231 		if (propval == ST_TRUE) {
1232 			ret = snmp_get_str(hdl,
1233 			    OID_sunPlatBinarySensorInterpretTrue,
1234 			    vol_props[ndx].row, &pstr, &snmp_syserr);
1235 			if (snmp_syserr == ECANCELED) {
1236 				(void) rw_unlock(&stale_tree_rwlp);
1237 				if (pstr)
1238 					free(pstr);
1239 				return (PICL_FAILURE);
1240 			}
1241 			if (ret < 0 || pstr == NULL) {
1242 				log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1243 				    snmp_syserr ? snmp_syserr : ret,
1244 				    OID_sunPlatBinarySensorInterpretTrue,
1245 				    vol_props[ndx].row);
1246 				(void) strlcpy(buf, STR_ST_TRUE,
1247 				    MAX_TRUTHVAL_LEN);
1248 			} else {
1249 				(void) strlcpy(buf, pstr, MAX_TRUTHVAL_LEN);
1250 			}
1251 			if (pstr)
1252 				free(pstr);
1253 		} else if (propval == ST_FALSE) {
1254 			ret = snmp_get_str(hdl,
1255 			    OID_sunPlatBinarySensorInterpretFalse,
1256 			    vol_props[ndx].row, &pstr, &snmp_syserr);
1257 			if (snmp_syserr == ECANCELED) {
1258 				(void) rw_unlock(&stale_tree_rwlp);
1259 				if (pstr)
1260 					free(pstr);
1261 				return (PICL_FAILURE);
1262 			}
1263 			if (ret < 0 || pstr == NULL) {
1264 				log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1265 				    snmp_syserr ? snmp_syserr : ret,
1266 				    OID_sunPlatBinarySensorInterpretFalse,
1267 				    vol_props[ndx].row);
1268 				(void) strlcpy(buf, STR_ST_FALSE,
1269 				    MAX_TRUTHVAL_LEN);
1270 			} else {
1271 				(void) strlcpy(buf, pstr, MAX_TRUTHVAL_LEN);
1272 			}
1273 			if (pstr)
1274 				free(pstr);
1275 		} else {
1276 			(void) rw_unlock(&stale_tree_rwlp);
1277 			log_msg(LOG_ERR, SNMPP_INV_PLAT_BINSNSR_CURRENT,
1278 			    propval, vol_props[ndx].row);
1279 			return (PICL_FAILURE);
1280 		}
1281 		break;
1282 
1283 	case VPT_ALARMSTATE:
1284 		if (propval == SSAS_OFF) {
1285 			(void) strlcpy(buf, STR_SSAS_OFF, MAX_ALARMSTATE_LEN);
1286 		} else if (propval == SSAS_STEADY) {
1287 			(void) strlcpy(buf, STR_SSAS_STEADY,
1288 			    MAX_ALARMSTATE_LEN);
1289 		} else if (propval == SSAS_ALTERNATING) {
1290 			(void) strlcpy(buf, STR_SSAS_ALTERNATING,
1291 			    MAX_ALARMSTATE_LEN);
1292 		} else {
1293 			(void) strlcpy(buf, STR_SSAS_UNKNOWN,
1294 			    MAX_ALARMSTATE_LEN);
1295 		}
1296 		break;
1297 
1298 	case VPT_BATTERYSTATUS:
1299 		switch (propval) {
1300 		case SSBS_OTHER:
1301 			(void) strlcpy(buf, STR_SSBS_OTHER,
1302 			    MAX_BATTERYSTATUS_LEN);
1303 			break;
1304 		case SSBS_FULLYCHARGED:
1305 			(void) strlcpy(buf, STR_SSBS_FULLYCHARGED,
1306 			    MAX_BATTERYSTATUS_LEN);
1307 			break;
1308 		case SSBS_LOW:
1309 			(void) strlcpy(buf, STR_SSBS_LOW,
1310 			    MAX_BATTERYSTATUS_LEN);
1311 			break;
1312 		case SSBS_CRITICAL:
1313 			(void) strlcpy(buf, STR_SSBS_CRITICAL,
1314 			    MAX_BATTERYSTATUS_LEN);
1315 			break;
1316 		case SSBS_CHARGING:
1317 			(void) strlcpy(buf, STR_SSBS_CHARGING,
1318 			    MAX_BATTERYSTATUS_LEN);
1319 			break;
1320 		case SSBS_CHARGING_AND_LOW:
1321 			(void) strlcpy(buf, STR_SSBS_CHARGING_AND_LOW,
1322 			    MAX_BATTERYSTATUS_LEN);
1323 			break;
1324 		case SSBS_CHARGING_AND_HIGH:
1325 			(void) strlcpy(buf, STR_SSBS_CHARGING_AND_HIGH,
1326 			    MAX_BATTERYSTATUS_LEN);
1327 			break;
1328 		case SSBS_CHARGING_AND_CRITICAL:
1329 			(void) strlcpy(buf, STR_SSBS_CHARGING_AND_CRITICAL,
1330 			    MAX_BATTERYSTATUS_LEN);
1331 			break;
1332 		case SSBS_UNDEFINED:
1333 			(void) strlcpy(buf, STR_SSBS_UNDEFINED,
1334 			    MAX_BATTERYSTATUS_LEN);
1335 			break;
1336 		case SSBS_PARTIALLY_CHARGED:
1337 			(void) strlcpy(buf, STR_SSBS_PARTIALLY_CHARGED,
1338 			    MAX_BATTERYSTATUS_LEN);
1339 			break;
1340 		case SSBS_UNKNOWN:
1341 		default:
1342 			(void) strlcpy(buf, STR_SSBS_UNKNOWN,
1343 			    MAX_BATTERYSTATUS_LEN);
1344 			break;
1345 		}
1346 		break;
1347 	}
1348 
1349 	(void) rw_unlock(&stale_tree_rwlp);
1350 
1351 	return (PICL_SUCCESS);
1352 }
1353 
1354 static void
1355 threshold(picl_nodehdl_t node, char *oidstr, int row, char *propname,
1356     int *snmp_syserr_p)
1357 {
1358 	picl_prophdl_t	prop;
1359 	int		err;
1360 	int		val;
1361 
1362 	if ((err = snmp_get_int(hdl, oidstr, row, &val, snmp_syserr_p)) != -1) {
1363 		err = add_volatile_prop(node, propname, PICL_PTYPE_INT,
1364 		    PICL_READ, sizeof (int), read_volprop, NULL, &prop);
1365 		if (err == PICL_SUCCESS)
1366 			save_volprop(prop, oidstr, row, VPT_NUMSENSOR);
1367 	} else
1368 		log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1369 		    *snmp_syserr_p ? *snmp_syserr_p : err, oidstr, row);
1370 }
1371 
1372 static void
1373 add_thresholds(picl_nodehdl_t node, int row, int *snmp_syserr_p)
1374 {
1375 	uchar_t	*bitstr = NULL;
1376 	uchar_t	enabled;
1377 	uint_t	nbytes;
1378 	int	ret;
1379 
1380 	ret = snmp_get_str(hdl,
1381 	    OID_sunPlatNumericSensorEnabledThresholds,
1382 	    row, (char **)&bitstr, snmp_syserr_p);
1383 	if (ret == -1) {
1384 		log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1385 		    *snmp_syserr_p ? *snmp_syserr_p : ret,
1386 		    OID_sunPlatNumericSensorEnabledThresholds, row);
1387 	} else {
1388 		nbytes = strlen((const char *)bitstr);
1389 	}
1390 
1391 	CHECK_LINKRESET_VOID(snmp_syserr_p);
1392 
1393 	/*
1394 	 * No bit string of threshold masks was returned, so we can't
1395 	 * assume that any thresholds exist.
1396 	 *
1397 	 * This mask prevents us from attempting to fetch thresholds
1398 	 * which don't apply to the sensor or that aren't there anyway,
1399 	 * That speeds up the plug-in significantly since otherwise it
1400 	 * takes several seconds to time out.
1401 	 */
1402 	if (ret < 0 || bitstr == NULL || nbytes == 0 || 2 < nbytes) {
1403 		if (bitstr)
1404 			free(bitstr);
1405 		return;
1406 	} else if (nbytes == 1) {
1407 		/*
1408 		 * The ALOM snmp agent doesn't adhere to the BER rules for
1409 		 * encoding bit strings. While the BER states that bitstrings
1410 		 * must begin from the second octet after length, and the
1411 		 * first octet after length must indicate the number of unused
1412 		 * bits in the last octet, the snmp agent simply sends the
1413 		 * bitstring data as if it were octet string -- that is, the
1414 		 * "unused bits" octet is missing.
1415 		 */
1416 		enabled = bitstr[0];
1417 	} else if (nbytes == 2)
1418 		enabled = bitstr[1];
1419 
1420 	if (bitstr) {
1421 		free(bitstr);
1422 	}
1423 
1424 	if (enabled & LOWER_FATAL) {
1425 		threshold(node,
1426 		    OID_sunPlatNumericSensorLowerThresholdFatal, row,
1427 		    PICL_PROP_LOW_POWER_OFF, snmp_syserr_p);
1428 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1429 	}
1430 	if (enabled & LOWER_CRITICAL) {
1431 		threshold(node,
1432 		    OID_sunPlatNumericSensorLowerThresholdCritical, row,
1433 		    PICL_PROP_LOW_SHUTDOWN, snmp_syserr_p);
1434 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1435 	}
1436 	if (enabled & LOWER_NON_CRITICAL) {
1437 		threshold(node,
1438 		    OID_sunPlatNumericSensorLowerThresholdNonCritical, row,
1439 		    PICL_PROP_LOW_WARNING, snmp_syserr_p);
1440 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1441 	}
1442 	if (enabled & UPPER_NON_CRITICAL) {
1443 		threshold(node,
1444 		    OID_sunPlatNumericSensorUpperThresholdNonCritical, row,
1445 		    PICL_PROP_HIGH_WARNING, snmp_syserr_p);
1446 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1447 	}
1448 	if (enabled & UPPER_CRITICAL) {
1449 		threshold(node,
1450 		    OID_sunPlatNumericSensorUpperThresholdCritical, row,
1451 		    PICL_PROP_HIGH_SHUTDOWN, snmp_syserr_p);
1452 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1453 	}
1454 	if (enabled & UPPER_FATAL) {
1455 		threshold(node,
1456 		    OID_sunPlatNumericSensorUpperThresholdFatal, row,
1457 		    PICL_PROP_HIGH_POWER_OFF, snmp_syserr_p);
1458 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1459 	}
1460 }
1461 
1462 static char *
1463 get_slot_type(int row, int *snmp_syserr_p)
1464 {
1465 	char	*p;
1466 	char	*slott = NULL;
1467 	int	ret;
1468 
1469 	ret = snmp_get_str(hdl, OID_sunPlatEquipmentHolderAcceptableTypes,
1470 	    row, &p, snmp_syserr_p);
1471 	CHECK_LINKRESET(snmp_syserr_p, NULL)
1472 
1473 	if ((ret == 0) && p && *p) {
1474 		slott = p;
1475 		if ((p = strchr(slott, '\n')) != NULL)
1476 			*p = 0;
1477 	} else {
1478 		log_msg(LOG_WARNING, SNMPP_NO_SLOT_TYPE, row);
1479 		if (p) {
1480 			free(p);
1481 		}
1482 	}
1483 
1484 	return (slott);
1485 }
1486 
1487 /*
1488  * Create and add the specified volatile property
1489  */
1490 static int
1491 add_volatile_prop(picl_nodehdl_t node, char *name, int type, int access,
1492     int size, int (*rdfunc)(ptree_rarg_t *, void *),
1493     int (*wrfunc)(ptree_warg_t *, const void *), picl_prophdl_t *propp)
1494 {
1495 	ptree_propinfo_t	propinfo;
1496 	picl_prophdl_t		prop;
1497 	int			err;
1498 
1499 	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
1500 	    type, (access|PICL_VOLATILE), size, name, rdfunc, wrfunc);
1501 	if (err != PICL_SUCCESS) {
1502 		log_msg(LOG_ERR, SNMPP_CANT_INIT_PROPINFO, err);
1503 		return (err);
1504 	}
1505 
1506 	err = ptree_create_and_add_prop(node, &propinfo, NULL, &prop);
1507 	if (err != PICL_SUCCESS) {
1508 		log_msg(LOG_ERR, SNMPP_CANT_ADD_PROP, err, node);
1509 		return (err);
1510 	}
1511 
1512 	if (propp)
1513 		*propp = prop;
1514 
1515 	return (PICL_SUCCESS);
1516 }
1517 
1518 /*
1519  * Add the specified string property to the node
1520  */
1521 static int
1522 add_string_prop(picl_nodehdl_t node, char *propname, char *propval)
1523 {
1524 	ptree_propinfo_t	propinfo;
1525 	int			err;
1526 
1527 	if (*propval == '\0')
1528 		return (PICL_SUCCESS);
1529 
1530 	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
1531 	    PICL_PTYPE_CHARSTRING, PICL_READ, strlen(propval) + 1,
1532 	    propname, NULL, NULL);
1533 	if (err != PICL_SUCCESS) {
1534 		log_msg(LOG_ERR, SNMPP_CANT_INIT_STR_PROPINFO, err);
1535 		return (err);
1536 	}
1537 
1538 	err = ptree_create_and_add_prop(node, &propinfo, propval, NULL);
1539 	if (err != PICL_SUCCESS) {
1540 		log_msg(LOG_ERR, SNMPP_CANT_ADD_STR_PROP, err, node);
1541 		return (err);
1542 	}
1543 
1544 	return (PICL_SUCCESS);
1545 }
1546 
1547 /*
1548  * Add the specified void property to the node
1549  */
1550 static int
1551 add_void_prop(picl_nodehdl_t node, char *propname)
1552 {
1553 	ptree_propinfo_t	propinfo;
1554 	int			err;
1555 
1556 	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
1557 	    PICL_PTYPE_VOID, PICL_READ, 0, propname, NULL, NULL);
1558 	if (err != PICL_SUCCESS) {
1559 		log_msg(LOG_ERR, SNMPP_CANT_INIT_VOID_PROPINFO, err);
1560 		return (err);
1561 	}
1562 
1563 	err = ptree_create_and_add_prop(node, &propinfo, NULL, NULL);
1564 	if (err != PICL_SUCCESS) {
1565 		log_msg(LOG_ERR, SNMPP_CANT_ADD_VOID_PROP, err, node);
1566 		return (err);
1567 	}
1568 
1569 	return (PICL_SUCCESS);
1570 }
1571 
1572 static void
1573 add_prop(picl_nodehdl_t nodeh, picl_prophdl_t *php, char *label,
1574     int row, sp_propid_t pp, int *snmp_syserr_p)
1575 {
1576 	char	*serial_num;
1577 	char	*slot_type;
1578 	char	*fw_revision, *hw_revision;
1579 	char	*mfg_name, *model_name;
1580 	char	*phys_descr;
1581 	int	val;
1582 	int	ret;
1583 
1584 	switch (pp) {
1585 	case PP_SERIAL_NUM:
1586 		ret = snmp_get_str(hdl, OID_entPhysicalSerialNum,
1587 		    row, &serial_num, snmp_syserr_p);
1588 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1589 		if ((ret == 0) && serial_num) {
1590 			(void) add_string_prop(nodeh,
1591 			    PICL_PROP_SERIAL_NUMBER, serial_num);
1592 			free((void *) serial_num);
1593 		}
1594 		if (ret == -1)
1595 			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1596 			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1597 			    OID_entPhysicalSerialNum, row);
1598 		break;
1599 
1600 	case PP_SLOT_TYPE:
1601 		if ((slot_type = get_slot_type(row, snmp_syserr_p)) == NULL) {
1602 			CHECK_LINKRESET_VOID(snmp_syserr_p)
1603 			(void) add_string_prop(nodeh,
1604 			    PICL_PROP_SLOT_TYPE, DEFAULT_SLOT_TYPE);
1605 		} else {
1606 			(void) add_string_prop(nodeh,
1607 			    PICL_PROP_SLOT_TYPE, slot_type);
1608 			free((void *) slot_type);
1609 		}
1610 		break;
1611 
1612 	case PP_STATE:
1613 		ret = add_volatile_prop(nodeh, PICL_PROP_STATE,
1614 		    PICL_PTYPE_CHARSTRING, PICL_READ, MAX_ALARMSTATE_LEN,
1615 		    read_volprop, NULL, php);
1616 		if (ret == PICL_SUCCESS) {
1617 			save_volprop(*php, OID_sunPlatAlarmState, row,
1618 			    VPT_ALARMSTATE);
1619 		}
1620 		break;
1621 
1622 	case PP_OPSTATUS:
1623 		ret = add_volatile_prop(nodeh, PICL_PROP_OPERATIONAL_STATUS,
1624 		    PICL_PTYPE_CHARSTRING, PICL_READ, MAX_OPSTATE_LEN,
1625 		    read_volprop, NULL, php);
1626 		if (ret == PICL_SUCCESS) {
1627 			save_volprop(*php,
1628 			    OID_sunPlatEquipmentOperationalState, row,
1629 			    VPT_PLATOPSTATE);
1630 		}
1631 		break;
1632 
1633 	case PP_BATT_STATUS:
1634 		ret = add_volatile_prop(nodeh, PICL_PROP_BATTERY_STATUS,
1635 		    PICL_PTYPE_CHARSTRING, PICL_READ, MAX_BATTERYSTATUS_LEN,
1636 		    read_volprop, NULL, php);
1637 		if (ret == PICL_SUCCESS) {
1638 			save_volprop(*php, OID_sunPlatBatteryStatus, row,
1639 			    VPT_BATTERYSTATUS);
1640 		}
1641 		break;
1642 
1643 	case PP_TEMPERATURE:
1644 		ret = add_volatile_prop(nodeh, PICL_PROP_TEMPERATURE,
1645 		    PICL_PTYPE_INT, PICL_READ, sizeof (int), read_volprop,
1646 		    NULL, php);
1647 		if (ret == PICL_SUCCESS) {
1648 			save_volprop(*php, OID_sunPlatNumericSensorCurrent,
1649 			    row, VPT_NUMSENSOR);
1650 		}
1651 		break;
1652 
1653 	case PP_VOLTAGE:
1654 		ret = add_volatile_prop(nodeh, PICL_PROP_VOLTAGE,
1655 		    PICL_PTYPE_INT, PICL_READ, sizeof (int), read_volprop,
1656 		    NULL, php);
1657 		if (ret == PICL_SUCCESS) {
1658 			save_volprop(*php, OID_sunPlatNumericSensorCurrent,
1659 			    row, VPT_NUMSENSOR);
1660 		}
1661 		break;
1662 
1663 	case PP_CURRENT:
1664 		ret = add_volatile_prop(nodeh, PICL_PROP_CURRENT,
1665 		    PICL_PTYPE_INT, PICL_READ, sizeof (int), read_volprop,
1666 		    NULL, php);
1667 		if (ret == PICL_SUCCESS) {
1668 			save_volprop(*php, OID_sunPlatNumericSensorCurrent,
1669 			    row, VPT_NUMSENSOR);
1670 		}
1671 		break;
1672 
1673 	case PP_SPEED:
1674 		ret = add_volatile_prop(nodeh, PICL_PROP_SPEED, PICL_PTYPE_INT,
1675 		    PICL_READ, sizeof (int), read_volprop, NULL, php);
1676 		if (ret == PICL_SUCCESS) {
1677 			save_volprop(*php, OID_sunPlatNumericSensorCurrent,
1678 			    row, VPT_NUMSENSOR);
1679 		}
1680 		break;
1681 
1682 	case PP_SENSOR_VALUE:
1683 		ret = add_volatile_prop(nodeh, PICL_PROP_SENSOR_VALUE,
1684 		    PICL_PTYPE_INT, PICL_READ, sizeof (int), read_volprop,
1685 		    NULL, php);
1686 		if (ret == PICL_SUCCESS) {
1687 			save_volprop(*php, OID_sunPlatNumericSensorCurrent,
1688 			    row, VPT_NUMSENSOR);
1689 		}
1690 		break;
1691 
1692 	case PP_CONDITION:
1693 		ret = add_volatile_prop(nodeh, PICL_PROP_CONDITION,
1694 		    PICL_PTYPE_CHARSTRING, PICL_READ, MAX_TRUTHVAL_LEN,
1695 		    read_volprop, NULL, php);
1696 		if (ret == PICL_SUCCESS) {
1697 			save_volprop(*php, OID_sunPlatBinarySensorCurrent,
1698 			    row, VPT_BINSENSOR);
1699 		}
1700 		break;
1701 
1702 	case PP_EXPECTED:
1703 		ret = add_volatile_prop(nodeh, PICL_PROP_EXPECTED,
1704 		    PICL_PTYPE_CHARSTRING, PICL_READ, MAX_TRUTHVAL_LEN,
1705 		    read_volprop, NULL, php);
1706 		if (ret == PICL_SUCCESS) {
1707 			save_volprop(*php, OID_sunPlatBinarySensorExpected,
1708 			    row, VPT_BINSENSOR);
1709 		}
1710 		break;
1711 
1712 	case PP_EXPONENT:
1713 		ret = add_volatile_prop(nodeh, PICL_PROP_EXPONENT,
1714 		    PICL_PTYPE_INT, PICL_READ, sizeof (int), read_volprop,
1715 		    NULL, php);
1716 		if (ret == PICL_SUCCESS) {
1717 			save_volprop(*php, OID_sunPlatNumericSensorExponent,
1718 			    row, VPT_NUMSENSOR);
1719 		}
1720 		break;
1721 
1722 	case PP_REPLACEABLE:
1723 		ret = snmp_get_int(hdl, OID_sunPlatCircuitPackReplaceable,
1724 		    row, &val, snmp_syserr_p);
1725 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1726 		if ((ret == 0) && (val == ST_TRUE))
1727 			(void) add_void_prop(nodeh, PICL_PROP_IS_REPLACEABLE);
1728 		if (ret == -1)
1729 			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1730 			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1731 			    OID_sunPlatCircuitPackReplaceable, row);
1732 		break;
1733 
1734 	case PP_HOTSWAPPABLE:
1735 		ret = snmp_get_int(hdl, OID_sunPlatCircuitPackHotSwappable,
1736 		    row, &val, snmp_syserr_p);
1737 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1738 		if ((ret == 0) && (val == ST_TRUE))
1739 			(void) add_void_prop(nodeh, PICL_PROP_IS_HOT_SWAPPABLE);
1740 		if (ret == -1)
1741 			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1742 			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1743 			    OID_sunPlatCircuitPackHotSwappable, row);
1744 		break;
1745 
1746 	case PP_IS_FRU:
1747 		ret = snmp_get_int(hdl, OID_entPhysicalIsFRU, row,
1748 		    &val, snmp_syserr_p);
1749 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1750 		if ((ret == 0) && (val == ST_TRUE))
1751 			(void) add_void_prop(nodeh, PICL_PROP_IS_FRU);
1752 		if (ret == -1)
1753 			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1754 			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1755 			    OID_entPhysicalIsFRU, row);
1756 		break;
1757 
1758 	case PP_HW_REVISION:
1759 		ret = snmp_get_str(hdl, OID_entPhysicalHardwareRev,
1760 		    row, &hw_revision, snmp_syserr_p);
1761 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1762 		if ((ret == 0) && hw_revision) {
1763 			(void) add_string_prop(nodeh,
1764 			    PICL_PROP_HW_REVISION, hw_revision);
1765 			free((void *) hw_revision);
1766 		}
1767 		if (ret == -1)
1768 			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1769 			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1770 			    OID_entPhysicalHardwareRev, row);
1771 		break;
1772 
1773 	case PP_FW_REVISION:
1774 		ret = snmp_get_str(hdl, OID_entPhysicalFirmwareRev,
1775 		    row, &fw_revision, snmp_syserr_p);
1776 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1777 		if ((ret == 0) && fw_revision) {
1778 			(void) add_string_prop(nodeh,
1779 			    PICL_PROP_FW_REVISION, fw_revision);
1780 			free((void *) fw_revision);
1781 		}
1782 		if (ret == -1)
1783 			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1784 			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1785 			    OID_entPhysicalFirmwareRev, row);
1786 		break;
1787 
1788 	case PP_MFG_NAME:
1789 		ret = snmp_get_str(hdl, OID_entPhysicalMfgName,
1790 		    row, &mfg_name, snmp_syserr_p);
1791 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1792 		if ((ret == 0) && mfg_name) {
1793 			(void) add_string_prop(nodeh,
1794 			    PICL_PROP_MFG_NAME, mfg_name);
1795 			free((void *) mfg_name);
1796 		}
1797 		if (ret == -1)
1798 			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1799 			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1800 			    OID_entPhysicalMfgName, row);
1801 		break;
1802 
1803 	case PP_MODEL_NAME:
1804 		ret = snmp_get_str(hdl, OID_entPhysicalModelName,
1805 		    row, &model_name, snmp_syserr_p);
1806 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1807 		if ((ret == 0) && model_name) {
1808 			(void) add_string_prop(nodeh,
1809 			    PICL_PROP_MODEL_NAME, model_name);
1810 			free((void *) model_name);
1811 		}
1812 		if (ret == -1)
1813 			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1814 			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1815 			    OID_entPhysicalModelName, row);
1816 		break;
1817 
1818 	case PP_DESCRIPTION:
1819 		ret = snmp_get_str(hdl, OID_entPhysicalDescr,
1820 		    row, &phys_descr, snmp_syserr_p);
1821 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1822 		if ((ret == 0) && phys_descr) {
1823 			(void) add_string_prop(nodeh,
1824 			    PICL_PROP_PHYS_DESCRIPTION, phys_descr);
1825 			free((void *) phys_descr);
1826 		}
1827 		if (ret == -1)
1828 			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1829 			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1830 			    OID_entPhysicalDescr, row);
1831 		break;
1832 
1833 	case PP_LABEL:
1834 		if (label && *label)
1835 			(void) add_string_prop(nodeh, PICL_PROP_LABEL, label);
1836 		break;
1837 
1838 	case PP_BASE_UNITS:
1839 		ret = snmp_get_int(hdl, OID_sunPlatNumericSensorBaseUnits,
1840 		    row, &val, snmp_syserr_p);
1841 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1842 		if ((ret == 0) && (val > 0) && (val < n_baseunits)) {
1843 			(void) add_string_prop(nodeh,
1844 			    PICL_PROP_BASE_UNITS, sensor_baseunits[val]);
1845 		}
1846 		if (ret == -1)
1847 			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1848 			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1849 			    OID_sunPlatNumericSensorBaseUnits, row);
1850 		break;
1851 
1852 	case PP_RATE_UNITS:
1853 		ret = snmp_get_int(hdl, OID_sunPlatNumericSensorRateUnits,
1854 		    row, &val, snmp_syserr_p);
1855 		CHECK_LINKRESET_VOID(snmp_syserr_p)
1856 		if ((ret == 0) && (val > 0) && (val < n_rateunits)) {
1857 			(void) add_string_prop(nodeh,
1858 			    PICL_PROP_RATE_UNITS, sensor_rateunits[val]);
1859 		}
1860 		if (ret == -1)
1861 			log_msg(LOG_ERR, SNMPP_CANT_FETCH_OBJECT_VAL,
1862 			    *snmp_syserr_p ? *snmp_syserr_p : ret,
1863 			    OID_sunPlatNumericSensorRateUnits, row);
1864 		break;
1865 	}
1866 }
1867 
1868 /*
1869  * Initialize the SNMP library's cache refresh subsystem, then periodically
1870  * process refresh job to prevent cache entries from expiring.
1871  */
1872 /*ARGSUSED*/
1873 static void *
1874 cache_refresher(void *arg)
1875 {
1876 	int		jobs;
1877 	int		next_expiration;
1878 	timestruc_t	to;
1879 	hrtime_t	cycle_start, cycle_elapsed;
1880 
1881 	/*
1882 	 * Initialize refresh subsystem
1883 	 */
1884 	LOGPRINTF("Initializing SNMP refresh subsystem.\n");
1885 	if (snmp_refresh_init() < 0) {
1886 		return ((void *)-1);
1887 	}
1888 
1889 	(void) mutex_lock(&cache_refresh_lock);
1890 
1891 
1892 	for (;;) {
1893 		cycle_start = gethrtime();
1894 
1895 		/*
1896 		 * Process jobs from the snmp cache refresh work queue until one
1897 		 * of the following conditions is true:
1898 		 * 1) we are told to exit, or
1899 		 * 2) we have processed at least as many jobs as recommended by
1900 		 * the library, and the next job expiration is at least
1901 		 * CACHE_REFRESH_MIN_WINDOW * seconds away.
1902 		 */
1903 		jobs = snmp_refresh_get_cycle_hint(CACHE_REFRESH_CYCLE);
1904 		while ((cache_refresh_thr_exit == B_FALSE) && (jobs > 0)) {
1905 			(void) snmp_refresh_process_job();
1906 			jobs--;
1907 		}
1908 
1909 		next_expiration = snmp_refresh_get_next_expiration();
1910 		while ((cache_refresh_thr_exit == B_FALSE) &&
1911 		    ((next_expiration >= 0) &&
1912 		    (next_expiration < CACHE_REFRESH_MIN_WINDOW))) {
1913 			(void) snmp_refresh_process_job();
1914 			next_expiration = snmp_refresh_get_next_expiration();
1915 		}
1916 
1917 		/*
1918 		 * As long as we haven't been told to exit, sleep for
1919 		 * CACHE_REFRESH_CYCLE seconds minus the amount of time that has
1920 		 * elapsed since this cycle started.  If the elapsed time is
1921 		 * equal to or greater than 60 seconds, skip sleeping entirely.
1922 		 */
1923 		cycle_elapsed = (gethrtime() - cycle_start) / NANOSEC;
1924 		if ((cache_refresh_thr_exit == B_FALSE) &&
1925 		    (cycle_elapsed < CACHE_REFRESH_CYCLE)) {
1926 			to.tv_sec = CACHE_REFRESH_CYCLE - cycle_elapsed;
1927 			to.tv_nsec = 0;
1928 			(void) cond_reltimedwait(&cache_refresh_cv,
1929 			    &cache_refresh_lock, &to);
1930 		}
1931 
1932 		/*
1933 		 * If we have been told to exit, clean up and bail out.
1934 		 */
1935 		if (cache_refresh_thr_exit == B_TRUE) {
1936 			snmp_refresh_fini();
1937 			(void) mutex_unlock(&cache_refresh_lock);
1938 			LOGPRINTF("cache_refresher: time to exit\n");
1939 			return (NULL);
1940 		}
1941 
1942 	}
1943 
1944 	/*NOTREACHED*/
1945 	return (NULL);
1946 }
1947 
1948 /*
1949  * Check to see if the cache_refresher thread is running.  If it is, signal it
1950  * to terminate and clean up associated data structures.
1951  */
1952 void
1953 cache_refresher_fini(void)
1954 {
1955 	/* if the thread isn't running, there is nothing to do */
1956 	if (cache_refresh_thr_exit == B_TRUE)
1957 		return;
1958 
1959 	/* wake up the cache_refresher thread, tell it to exit */
1960 	(void) mutex_lock(&cache_refresh_lock);
1961 	cache_refresh_thr_exit = B_TRUE;
1962 	(void) cond_signal(&cache_refresh_cv);
1963 	(void) mutex_unlock(&cache_refresh_lock);
1964 
1965 	/* reap the thread */
1966 	(void) thr_join(cache_refresh_thr_id, NULL, NULL);
1967 
1968 	/* finish cleanup... */
1969 	(void) cond_destroy(&cache_refresh_cv);
1970 	(void) mutex_destroy(&cache_refresh_lock);
1971 }
1972 
1973 /*VARARGS2*/
1974 static void
1975 log_msg(int pri, const char *fmt, ...)
1976 {
1977 	va_list ap;
1978 
1979 	va_start(ap, fmt);
1980 	vsyslog(pri, fmt, ap);
1981 	va_end(ap);
1982 }
1983 
1984 #ifdef SNMPPLUGIN_DEBUG
1985 
1986 static void
1987 snmpplugin_log_init(void)
1988 {
1989 	(void) mutex_init(&snmpplugin_dbuf_lock, USYNC_THREAD, NULL);
1990 }
1991 
1992 static void
1993 snmpplugin_log(const char *fmt, ...)
1994 {
1995 	va_list	ap;
1996 
1997 	(void) mutex_lock(&snmpplugin_dbuf_lock);
1998 
1999 	va_start(ap, fmt);
2000 	(void) vsnprintf(snmpplugin_lbuf, SNMPPLUGIN_DMAX_LINE, fmt, ap);
2001 	snmpplugin_log_append();
2002 	va_end(ap);
2003 
2004 	(void) mutex_unlock(&snmpplugin_dbuf_lock);
2005 }
2006 
2007 static void
2008 snmpplugin_log_append(void)
2009 {
2010 	int	len;
2011 
2012 	len = strlen(snmpplugin_lbuf);
2013 
2014 	if ((snmpplugin_dbuf_curp + len) >=
2015 	    (snmpplugin_dbuf + snmpplugin_dbuf_sz)) {
2016 		snmpplugin_dbuf_realloc();
2017 		if (snmpplugin_dbuf == NULL) {
2018 			return;
2019 		}
2020 	}
2021 
2022 	(void) strcpy(snmpplugin_dbuf_curp, snmpplugin_lbuf);
2023 	snmpplugin_dbuf_curp += len;
2024 }
2025 
2026 static void
2027 snmpplugin_dbuf_realloc(void)
2028 {
2029 	char	*p;
2030 	size_t	offset = 0;
2031 	size_t	count;
2032 
2033 	count = snmpplugin_dbuf_sz + SNMPPLUGIN_DBLOCK_SZ;
2034 	if ((p = (char *)calloc(count, 1)) == NULL) {
2035 		snmpplugin_dbuf_overflow++;
2036 		snmpplugin_dbuf_curp = snmpplugin_dbuf;
2037 		return;
2038 	}
2039 
2040 	if (snmpplugin_dbuf) {
2041 		offset = snmpplugin_dbuf_curp - snmpplugin_dbuf;
2042 		(void) memcpy(p, snmpplugin_dbuf, snmpplugin_dbuf_sz);
2043 		free(snmpplugin_dbuf);
2044 	}
2045 
2046 	snmpplugin_dbuf = p;
2047 	snmpplugin_dbuf_sz += SNMPPLUGIN_DBLOCK_SZ;
2048 
2049 	snmpplugin_dbuf_curp = snmpplugin_dbuf + offset;
2050 }
2051 #endif
2052