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