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