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