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