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