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