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