xref: /onnv-gate/usr/src/lib/fm/libfmd_snmp/common/resource.c (revision 1303:6e5751a0b831)
1*1303Swesolows /*
2*1303Swesolows  * CDDL HEADER START
3*1303Swesolows  *
4*1303Swesolows  * The contents of this file are subject to the terms of the
5*1303Swesolows  * Common Development and Distribution License (the "License").
6*1303Swesolows  * You may not use this file except in compliance with the License.
7*1303Swesolows  *
8*1303Swesolows  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*1303Swesolows  * or http://www.opensolaris.org/os/licensing.
10*1303Swesolows  * See the License for the specific language governing permissions
11*1303Swesolows  * and limitations under the License.
12*1303Swesolows  *
13*1303Swesolows  * When distributing Covered Code, include this CDDL HEADER in each
14*1303Swesolows  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*1303Swesolows  * If applicable, add the following below this CDDL HEADER, with the
16*1303Swesolows  * fields enclosed by brackets "[]" replaced with your own identifying
17*1303Swesolows  * information: Portions Copyright [yyyy] [name of copyright owner]
18*1303Swesolows  *
19*1303Swesolows  * CDDL HEADER END
20*1303Swesolows  */
21*1303Swesolows 
22*1303Swesolows /*
23*1303Swesolows  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24*1303Swesolows  * Use is subject to license terms.
25*1303Swesolows  */
26*1303Swesolows 
27*1303Swesolows #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*1303Swesolows 
29*1303Swesolows #include <fm/fmd_adm.h>
30*1303Swesolows #include <fm/fmd_snmp.h>
31*1303Swesolows #include <net-snmp/net-snmp-config.h>
32*1303Swesolows #include <net-snmp/net-snmp-includes.h>
33*1303Swesolows #include <net-snmp/agent/net-snmp-agent-includes.h>
34*1303Swesolows #include <pthread.h>
35*1303Swesolows #include <stddef.h>
36*1303Swesolows #include <errno.h>
37*1303Swesolows #include <libuutil.h>
38*1303Swesolows #include "sunFM_impl.h"
39*1303Swesolows #include "resource.h"
40*1303Swesolows 
41*1303Swesolows static uu_avl_pool_t	*rsrc_fmri_avl_pool;
42*1303Swesolows static uu_avl_pool_t	*rsrc_index_avl_pool;
43*1303Swesolows static uu_avl_t		*rsrc_fmri_avl;
44*1303Swesolows static uu_avl_t		*rsrc_index_avl;
45*1303Swesolows 
46*1303Swesolows #define	VALID_AVL_STATE	(rsrc_fmri_avl_pool != NULL &&		\
47*1303Swesolows 	rsrc_index_avl_pool != NULL && rsrc_fmri_avl != NULL &&	\
48*1303Swesolows 	rsrc_index_avl != NULL)
49*1303Swesolows 
50*1303Swesolows #define	UPDATE_WAIT_MILLIS	10	/* poll interval in milliseconds */
51*1303Swesolows 
52*1303Swesolows /*
53*1303Swesolows  * Update types: single-index and all are mutually exclusive; a count
54*1303Swesolows  * update is optional.
55*1303Swesolows  */
56*1303Swesolows #define	UCT_INDEX	0x1
57*1303Swesolows #define	UCT_ALL		0x2
58*1303Swesolows #define	UCT_COUNT	0x4
59*1303Swesolows #define	UCT_FLAGS	0x7
60*1303Swesolows 
61*1303Swesolows #define	RESOURCE_DATA_VALID(d)	((d)->d_valid == valid_stamp)
62*1303Swesolows 
63*1303Swesolows /*
64*1303Swesolows  * Locking strategy is described in module.c.
65*1303Swesolows  */
66*1303Swesolows static ulong_t		max_index;
67*1303Swesolows static int		valid_stamp;
68*1303Swesolows static uint32_t		rsrc_count;
69*1303Swesolows static pthread_mutex_t	update_lock;
70*1303Swesolows static pthread_cond_t	update_cv;
71*1303Swesolows static volatile enum { US_QUIET, US_NEEDED, US_INPROGRESS } update_status;
72*1303Swesolows 
73*1303Swesolows static Netsnmp_Node_Handler	sunFmResourceTable_handler;
74*1303Swesolows static Netsnmp_Node_Handler	sunFmResourceCount_handler;
75*1303Swesolows 
76*1303Swesolows static sunFmResource_data_t *
77*1303Swesolows key_build(const char *fmri, const ulong_t index)
78*1303Swesolows {
79*1303Swesolows 	static sunFmResource_data_t	key;
80*1303Swesolows 
81*1303Swesolows 	key.d_index = index;
82*1303Swesolows 	if (fmri)
83*1303Swesolows 		strlcpy(key.d_ari_fmri, fmri, sizeof (key.d_ari_fmri));
84*1303Swesolows 	else
85*1303Swesolows 		key.d_ari_fmri[0] = '\0';
86*1303Swesolows 
87*1303Swesolows 	return (&key);
88*1303Swesolows }
89*1303Swesolows 
90*1303Swesolows /*
91*1303Swesolows  * If fmri is the fmri of a resource we have previously seen and indexed, return
92*1303Swesolows  * data for it.  Otherwise, return NULL.  Note that the resource may not be
93*1303Swesolows  * valid; that is, it may have been removed from the fault manager since its
94*1303Swesolows  * information was last updated.
95*1303Swesolows  */
96*1303Swesolows static sunFmResource_data_t *
97*1303Swesolows resource_lookup_fmri(const char *fmri)
98*1303Swesolows {
99*1303Swesolows 	sunFmResource_data_t	*key;
100*1303Swesolows 
101*1303Swesolows 	key = key_build(fmri, 0);
102*1303Swesolows 	return (uu_avl_find(rsrc_fmri_avl, key, NULL, NULL));
103*1303Swesolows }
104*1303Swesolows 
105*1303Swesolows /*
106*1303Swesolows  * If index corresponds to a resource we have previously seen and indexed,
107*1303Swesolows  * return data for it.  Otherwise, return NULL.  Note that the resource may
108*1303Swesolows  * not be valid; that is, it may have been expired from the fault manager
109*1303Swesolows  * since its information was last updated.
110*1303Swesolows  */
111*1303Swesolows static sunFmResource_data_t *
112*1303Swesolows resource_lookup_index_exact(const ulong_t index)
113*1303Swesolows {
114*1303Swesolows 	sunFmResource_data_t	*key;
115*1303Swesolows 
116*1303Swesolows 	key = key_build(NULL, index);
117*1303Swesolows 	return (uu_avl_find(rsrc_index_avl, key, NULL, NULL));
118*1303Swesolows }
119*1303Swesolows 
120*1303Swesolows /*
121*1303Swesolows  * If index corresponds to a valid (that is, extant as of latest information
122*1303Swesolows  * from the fault manager) resource, return the data for that resource.
123*1303Swesolows  * Otherwise, return the data for the valid resource whose index is as close as
124*1303Swesolows  * possible to index but not lower.  This preserves the lexicographical
125*1303Swesolows  * ordering required for GETNEXT processing.
126*1303Swesolows  */
127*1303Swesolows static sunFmResource_data_t *
128*1303Swesolows resource_lookup_index_nextvalid(const ulong_t index)
129*1303Swesolows {
130*1303Swesolows 	sunFmResource_data_t	*key, *data;
131*1303Swesolows 	uu_avl_index_t		idx;
132*1303Swesolows 
133*1303Swesolows 	key = key_build(NULL, index);
134*1303Swesolows 
135*1303Swesolows 	if ((data = uu_avl_find(rsrc_index_avl, key, NULL, &idx)) != NULL &&
136*1303Swesolows 	    RESOURCE_DATA_VALID(data))
137*1303Swesolows 			return (data);
138*1303Swesolows 
139*1303Swesolows 	data = uu_avl_nearest_next(rsrc_index_avl, idx);
140*1303Swesolows 
141*1303Swesolows 	while (data != NULL && !RESOURCE_DATA_VALID(data)) {
142*1303Swesolows 		(void) uu_avl_find(rsrc_index_avl, data, NULL, &idx);
143*1303Swesolows 		data = uu_avl_nearest_next(rsrc_index_avl, idx);
144*1303Swesolows 	}
145*1303Swesolows 
146*1303Swesolows 	return (data);
147*1303Swesolows }
148*1303Swesolows 
149*1303Swesolows /*
150*1303Swesolows  * Possible update the contents of a single resource within the cache.  This
151*1303Swesolows  * is our callback from fmd_rsrc_iter.
152*1303Swesolows  */
153*1303Swesolows static int
154*1303Swesolows rsrcinfo_update_one(const fmd_adm_rsrcinfo_t *rsrcinfo, void *arg)
155*1303Swesolows {
156*1303Swesolows 	const sunFmResource_update_ctx_t *update_ctx =
157*1303Swesolows 	    (sunFmResource_update_ctx_t *)arg;
158*1303Swesolows 	sunFmResource_data_t *data = resource_lookup_fmri(rsrcinfo->ari_fmri);
159*1303Swesolows 
160*1303Swesolows 	++rsrc_count;
161*1303Swesolows 
162*1303Swesolows 	/*
163*1303Swesolows 	 * A resource we haven't seen before.  We're obligated to index
164*1303Swesolows 	 * it and link it into our cache so that we can find it, but we're
165*1303Swesolows 	 * not obligated to fill it in completely unless we're doing a
166*1303Swesolows 	 * full update or this is the resource we were asked for.  This
167*1303Swesolows 	 * avoids unnecessary iteration and memory manipulation for data
168*1303Swesolows 	 * we're not going to return for this request.
169*1303Swesolows 	 */
170*1303Swesolows 	if (data == NULL) {
171*1303Swesolows 		uu_avl_index_t idx;
172*1303Swesolows 
173*1303Swesolows 		DEBUGMSGTL((MODNAME_STR, "found new resource %s\n",
174*1303Swesolows 		    rsrcinfo->ari_fmri));
175*1303Swesolows 		if ((data = SNMP_MALLOC_TYPEDEF(sunFmResource_data_t)) ==
176*1303Swesolows 		    NULL) {
177*1303Swesolows 			snmp_log(LOG_ERR, MODNAME_STR ": Out of memory for "
178*1303Swesolows 			    "new resource data at %s:%d\n", __FILE__, __LINE__);
179*1303Swesolows 			return (1);
180*1303Swesolows 		}
181*1303Swesolows 		/*
182*1303Swesolows 		 * We allocate indices sequentially and never reuse them.
183*1303Swesolows 		 * This ensures we can always return valid GETNEXT responses
184*1303Swesolows 		 * without having to reindex, and it provides the user a
185*1303Swesolows 		 * more consistent view of the fault manager.
186*1303Swesolows 		 */
187*1303Swesolows 		data->d_index = ++max_index;
188*1303Swesolows 		DEBUGMSGTL((MODNAME_STR, "index %lu is %s@%p\n", data->d_index,
189*1303Swesolows 		    rsrcinfo->ari_fmri, data));
190*1303Swesolows 
191*1303Swesolows 		strlcpy(data->d_ari_fmri, rsrcinfo->ari_fmri,
192*1303Swesolows 		    sizeof (data->d_ari_fmri));
193*1303Swesolows 
194*1303Swesolows 		uu_avl_node_init(data, &data->d_fmri_avl, rsrc_fmri_avl_pool);
195*1303Swesolows 		(void) uu_avl_find(rsrc_fmri_avl, data, NULL, &idx);
196*1303Swesolows 		uu_avl_insert(rsrc_fmri_avl, data, idx);
197*1303Swesolows 
198*1303Swesolows 		uu_avl_node_init(data, &data->d_index_avl, rsrc_index_avl_pool);
199*1303Swesolows 		(void) uu_avl_find(rsrc_index_avl, data, NULL, &idx);
200*1303Swesolows 		uu_avl_insert(rsrc_index_avl, data, idx);
201*1303Swesolows 
202*1303Swesolows 		DEBUGMSGTL((MODNAME_STR, "completed new resource %lu/%s@%p\n",
203*1303Swesolows 		    data->d_index, data->d_ari_fmri, data));
204*1303Swesolows 	}
205*1303Swesolows 
206*1303Swesolows 	data->d_valid = valid_stamp;
207*1303Swesolows 
208*1303Swesolows 	DEBUGMSGTL((MODNAME_STR, "timestamp updated for %lu/%s@%p: %lu\n",
209*1303Swesolows 	    data->d_index, data->d_ari_fmri, data, data->d_valid));
210*1303Swesolows 
211*1303Swesolows 	if ((update_ctx->uc_type & UCT_ALL) ||
212*1303Swesolows 	    update_ctx->uc_index == data->d_index) {
213*1303Swesolows 		strlcpy(data->d_ari_case, rsrcinfo->ari_case,
214*1303Swesolows 		    sizeof (data->d_ari_case));
215*1303Swesolows 		data->d_ari_flags = rsrcinfo->ari_flags;
216*1303Swesolows 	}
217*1303Swesolows 
218*1303Swesolows 	return (!(update_ctx->uc_type & UCT_ALL) &&
219*1303Swesolows 	    update_ctx->uc_index == data->d_index);
220*1303Swesolows }
221*1303Swesolows 
222*1303Swesolows /*
223*1303Swesolows  * Update some or all resource data from fmd.  If type includes UCT_ALL, all
224*1303Swesolows  * resources will be indexed and their data cached.  If type includes
225*1303Swesolows  * UCT_INDEX, updates will stop once the resource matching index has been
226*1303Swesolows  * updated.  If UCT_COUNT is set, the number of faulted resources will be
227*1303Swesolows  * set.
228*1303Swesolows  *
229*1303Swesolows  * Returns appropriate SNMP error codes.
230*1303Swesolows  */
231*1303Swesolows static int
232*1303Swesolows rsrcinfo_update(sunFmResource_update_ctx_t *update_ctx)
233*1303Swesolows {
234*1303Swesolows 	fmd_adm_t *adm;
235*1303Swesolows 	int err;
236*1303Swesolows 
237*1303Swesolows 	ASSERT(update_ctx != NULL);
238*1303Swesolows 	ASSERT((update_ctx->uc_type & (UCT_ALL|UCT_INDEX)) !=
239*1303Swesolows 	    (UCT_ALL|UCT_INDEX));
240*1303Swesolows 	ASSERT((update_ctx->uc_type & ~UCT_FLAGS) == 0);
241*1303Swesolows 	ASSERT(VALID_AVL_STATE);
242*1303Swesolows 
243*1303Swesolows 	if ((adm = fmd_adm_open(update_ctx->uc_host, update_ctx->uc_prog,
244*1303Swesolows 	    update_ctx->uc_version)) == NULL) {
245*1303Swesolows 		snmp_log(LOG_ERR, MODNAME_STR ": Communication with fmd "
246*1303Swesolows 		    "failed: %s\n", strerror(errno));
247*1303Swesolows 		return (SNMP_ERR_RESOURCEUNAVAILABLE);
248*1303Swesolows 	}
249*1303Swesolows 
250*1303Swesolows 	if (update_ctx->uc_type == UCT_COUNT) {
251*1303Swesolows 		err = fmd_adm_rsrc_count(adm, update_ctx->uc_all, &rsrc_count);
252*1303Swesolows 	} else {
253*1303Swesolows 		++valid_stamp;
254*1303Swesolows 		rsrc_count = 0;
255*1303Swesolows 		err = fmd_adm_rsrc_iter(adm, update_ctx->uc_all,
256*1303Swesolows 		    rsrcinfo_update_one, update_ctx);
257*1303Swesolows 		DEBUGMSGTL((MODNAME_STR, "resource iteration completed\n"));
258*1303Swesolows 	}
259*1303Swesolows 
260*1303Swesolows 	fmd_adm_close(adm);
261*1303Swesolows 
262*1303Swesolows 	if (err != 0) {
263*1303Swesolows 		snmp_log(LOG_ERR, MODNAME_STR ": fmd resource information "
264*1303Swesolows 		    "update failed: %s\n", fmd_adm_errmsg(adm));
265*1303Swesolows 		return (SNMP_ERR_RESOURCEUNAVAILABLE);
266*1303Swesolows 	}
267*1303Swesolows 
268*1303Swesolows 	return (SNMP_ERR_NOERROR);
269*1303Swesolows }
270*1303Swesolows 
271*1303Swesolows /*ARGSUSED*/
272*1303Swesolows static void
273*1303Swesolows update_thread(void *arg)
274*1303Swesolows {
275*1303Swesolows 	/*
276*1303Swesolows 	 * The current rsrcinfo_update implementation offers minimal savings
277*1303Swesolows 	 * for the use of index-only updates; therefore we always do a full
278*1303Swesolows 	 * update.  If it becomes advantageous to limit updates to a single
279*1303Swesolows 	 * index, the contexts can be queued by the handler instead.
280*1303Swesolows 	 */
281*1303Swesolows 	sunFmResource_update_ctx_t	uc;
282*1303Swesolows 
283*1303Swesolows 	uc.uc_host = NULL;
284*1303Swesolows 	uc.uc_prog = FMD_ADM_PROGRAM;
285*1303Swesolows 	uc.uc_version = FMD_ADM_VERSION;
286*1303Swesolows 
287*1303Swesolows 	uc.uc_index = 0;
288*1303Swesolows 	uc.uc_type = UCT_ALL;
289*1303Swesolows 
290*1303Swesolows 	for (;;) {
291*1303Swesolows 		(void) pthread_mutex_lock(&update_lock);
292*1303Swesolows 		update_status = US_QUIET;
293*1303Swesolows 		while (update_status == US_QUIET)
294*1303Swesolows 			(void) pthread_cond_wait(&update_cv, &update_lock);
295*1303Swesolows 		update_status = US_INPROGRESS;
296*1303Swesolows 		(void) pthread_mutex_unlock(&update_lock);
297*1303Swesolows 		(void) rsrcinfo_update(&uc);
298*1303Swesolows 	}
299*1303Swesolows }
300*1303Swesolows 
301*1303Swesolows static void
302*1303Swesolows request_update(void)
303*1303Swesolows {
304*1303Swesolows 	(void) pthread_mutex_lock(&update_lock);
305*1303Swesolows 	if (update_status != US_QUIET) {
306*1303Swesolows 		(void) pthread_mutex_unlock(&update_lock);
307*1303Swesolows 		return;
308*1303Swesolows 	}
309*1303Swesolows 	update_status = US_NEEDED;
310*1303Swesolows 	(void) pthread_cond_signal(&update_cv);
311*1303Swesolows 	(void) pthread_mutex_unlock(&update_lock);
312*1303Swesolows }
313*1303Swesolows 
314*1303Swesolows /*ARGSUSED*/
315*1303Swesolows static int
316*1303Swesolows resource_compare_fmri(const void *l, const void *r, void *private)
317*1303Swesolows {
318*1303Swesolows 	sunFmResource_data_t	*l_data = (sunFmResource_data_t *)l;
319*1303Swesolows 	sunFmResource_data_t	*r_data = (sunFmResource_data_t *)r;
320*1303Swesolows 
321*1303Swesolows 	ASSERT(l_data != NULL && r_data != NULL);
322*1303Swesolows 
323*1303Swesolows 	return (strcmp(l_data->d_ari_fmri, r_data->d_ari_fmri));
324*1303Swesolows }
325*1303Swesolows 
326*1303Swesolows /*ARGSUSED*/
327*1303Swesolows static int
328*1303Swesolows resource_compare_index(const void *l, const void *r, void *private)
329*1303Swesolows {
330*1303Swesolows 	sunFmResource_data_t	*l_data = (sunFmResource_data_t *)l;
331*1303Swesolows 	sunFmResource_data_t	*r_data = (sunFmResource_data_t *)r;
332*1303Swesolows 
333*1303Swesolows 	ASSERT(l_data != NULL && r_data != NULL);
334*1303Swesolows 
335*1303Swesolows 	return (l_data->d_index < r_data->d_index ? -1 :
336*1303Swesolows 	    l_data->d_index > r_data->d_index ? 1 : 0);
337*1303Swesolows }
338*1303Swesolows 
339*1303Swesolows int
340*1303Swesolows sunFmResourceTable_init(void)
341*1303Swesolows {
342*1303Swesolows 	static oid sunFmResourceTable_oid[] = { SUNFMRESOURCETABLE_OID };
343*1303Swesolows 	static oid sunFmResourceCount_oid[] = { SUNFMRESOURCECOUNT_OID, 0 };
344*1303Swesolows 	netsnmp_table_registration_info *table_info;
345*1303Swesolows 	netsnmp_handler_registration *handler;
346*1303Swesolows 	int err;
347*1303Swesolows 
348*1303Swesolows 	if ((err = pthread_mutex_init(&update_lock, NULL)) != 0) {
349*1303Swesolows 		snmp_log(LOG_ERR, MODNAME_STR ": mutex_init failure: %s\n",
350*1303Swesolows 		    strerror(err));
351*1303Swesolows 		return (MIB_REGISTRATION_FAILED);
352*1303Swesolows 	}
353*1303Swesolows 	if ((err = pthread_cond_init(&update_cv, NULL)) != 0) {
354*1303Swesolows 		snmp_log(LOG_ERR, MODNAME_STR ": cond_init failure: %s\n",
355*1303Swesolows 		    strerror(err));
356*1303Swesolows 		return (MIB_REGISTRATION_FAILED);
357*1303Swesolows 	}
358*1303Swesolows 
359*1303Swesolows 	if ((err = pthread_create(NULL, NULL, (void *(*)(void *))update_thread,
360*1303Swesolows 	    NULL)) != 0) {
361*1303Swesolows 		snmp_log(LOG_ERR, MODNAME_STR ": error creating update "
362*1303Swesolows 		    "thread: %s\n", strerror(err));
363*1303Swesolows 		return (MIB_REGISTRATION_FAILED);
364*1303Swesolows 	}
365*1303Swesolows 
366*1303Swesolows 	if ((table_info =
367*1303Swesolows 	    SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info)) == NULL)
368*1303Swesolows 		return (MIB_REGISTRATION_FAILED);
369*1303Swesolows 
370*1303Swesolows 	if ((handler = netsnmp_create_handler_registration("sunFmResourceTable",
371*1303Swesolows 	    sunFmResourceTable_handler, sunFmResourceTable_oid,
372*1303Swesolows 	    OID_LENGTH(sunFmResourceTable_oid), HANDLER_CAN_RONLY)) == NULL) {
373*1303Swesolows 		SNMP_FREE(table_info);
374*1303Swesolows 		return (MIB_REGISTRATION_FAILED);
375*1303Swesolows 	}
376*1303Swesolows 
377*1303Swesolows 	/*
378*1303Swesolows 	 * The Net-SNMP template uses add_indexes here, but that
379*1303Swesolows 	 * function is unsafe because it does not check for failure.
380*1303Swesolows 	 */
381*1303Swesolows 	if (netsnmp_table_helper_add_index(table_info, ASN_UNSIGNED) == NULL) {
382*1303Swesolows 		SNMP_FREE(table_info);
383*1303Swesolows 		SNMP_FREE(handler);
384*1303Swesolows 		return (MIB_REGISTRATION_FAILED);
385*1303Swesolows 	}
386*1303Swesolows 
387*1303Swesolows 	table_info->min_column = SUNFMRESOURCE_COLMIN;
388*1303Swesolows 	table_info->max_column = SUNFMRESOURCE_COLMAX;
389*1303Swesolows 
390*1303Swesolows 	if ((rsrc_fmri_avl_pool = uu_avl_pool_create("rsrc_fmri",
391*1303Swesolows 	    sizeof (sunFmResource_data_t),
392*1303Swesolows 	    offsetof(sunFmResource_data_t, d_fmri_avl), resource_compare_fmri,
393*1303Swesolows 	    UU_AVL_DEBUG)) == NULL) {
394*1303Swesolows 		snmp_log(LOG_ERR, MODNAME_STR ": rsrc_fmri avl pool creation "
395*1303Swesolows 		    "failed: %s\n", uu_strerror(uu_error()));
396*1303Swesolows 		snmp_free_varbind(table_info->indexes);
397*1303Swesolows 		SNMP_FREE(table_info);
398*1303Swesolows 		SNMP_FREE(handler);
399*1303Swesolows 	}
400*1303Swesolows 
401*1303Swesolows 	if ((rsrc_fmri_avl = uu_avl_create(rsrc_fmri_avl_pool, NULL,
402*1303Swesolows 	    UU_AVL_DEBUG)) == NULL) {
403*1303Swesolows 		snmp_log(LOG_ERR, MODNAME_STR ": rsrc_fmri avl creation "
404*1303Swesolows 		    "failed: %s\n", uu_strerror(uu_error()));
405*1303Swesolows 		snmp_free_varbind(table_info->indexes);
406*1303Swesolows 		SNMP_FREE(table_info);
407*1303Swesolows 		SNMP_FREE(handler);
408*1303Swesolows 		uu_avl_pool_destroy(rsrc_fmri_avl_pool);
409*1303Swesolows 		return (MIB_REGISTRATION_FAILED);
410*1303Swesolows 	}
411*1303Swesolows 
412*1303Swesolows 	if ((rsrc_index_avl_pool = uu_avl_pool_create("rsrc_index",
413*1303Swesolows 	    sizeof (sunFmResource_data_t),
414*1303Swesolows 	    offsetof(sunFmResource_data_t, d_index_avl),
415*1303Swesolows 	    resource_compare_index, UU_AVL_DEBUG)) == NULL) {
416*1303Swesolows 		snmp_log(LOG_ERR, MODNAME_STR ": rsrc_index avl pool creation "
417*1303Swesolows 		    "failed: %s\n", uu_strerror(uu_error()));
418*1303Swesolows 		snmp_free_varbind(table_info->indexes);
419*1303Swesolows 		SNMP_FREE(table_info);
420*1303Swesolows 		SNMP_FREE(handler);
421*1303Swesolows 		uu_avl_destroy(rsrc_fmri_avl);
422*1303Swesolows 		uu_avl_pool_destroy(rsrc_fmri_avl_pool);
423*1303Swesolows 	}
424*1303Swesolows 
425*1303Swesolows 	if ((rsrc_index_avl = uu_avl_create(rsrc_index_avl_pool, NULL,
426*1303Swesolows 	    UU_AVL_DEBUG)) == NULL) {
427*1303Swesolows 		snmp_log(LOG_ERR, MODNAME_STR ": rsrc_index avl creation "
428*1303Swesolows 		    "failed: %s\n", uu_strerror(uu_error()));
429*1303Swesolows 		snmp_free_varbind(table_info->indexes);
430*1303Swesolows 		SNMP_FREE(table_info);
431*1303Swesolows 		SNMP_FREE(handler);
432*1303Swesolows 		uu_avl_destroy(rsrc_fmri_avl);
433*1303Swesolows 		uu_avl_pool_destroy(rsrc_fmri_avl_pool);
434*1303Swesolows 		uu_avl_pool_destroy(rsrc_index_avl_pool);
435*1303Swesolows 		return (MIB_REGISTRATION_FAILED);
436*1303Swesolows 	}
437*1303Swesolows 
438*1303Swesolows 	if ((err = netsnmp_register_table(handler, table_info)) !=
439*1303Swesolows 	    MIB_REGISTERED_OK) {
440*1303Swesolows 		snmp_free_varbind(table_info->indexes);
441*1303Swesolows 		SNMP_FREE(table_info);
442*1303Swesolows 		SNMP_FREE(handler);
443*1303Swesolows 		uu_avl_destroy(rsrc_fmri_avl);
444*1303Swesolows 		uu_avl_pool_destroy(rsrc_fmri_avl_pool);
445*1303Swesolows 		uu_avl_destroy(rsrc_index_avl);
446*1303Swesolows 		uu_avl_pool_destroy(rsrc_index_avl_pool);
447*1303Swesolows 		return (err);
448*1303Swesolows 	}
449*1303Swesolows 
450*1303Swesolows 	if ((err = netsnmp_register_read_only_instance(
451*1303Swesolows 	    netsnmp_create_handler_registration("sunFmResourceCount",
452*1303Swesolows 		sunFmResourceCount_handler, sunFmResourceCount_oid,
453*1303Swesolows 		OID_LENGTH(sunFmResourceCount_oid), HANDLER_CAN_RONLY))) !=
454*1303Swesolows 	    MIB_REGISTERED_OK) {
455*1303Swesolows 		/*
456*1303Swesolows 		 * There's no way to unregister the table handler, so we
457*1303Swesolows 		 * can't free any of the data, either.
458*1303Swesolows 		 */
459*1303Swesolows 		return (err);
460*1303Swesolows 	}
461*1303Swesolows 
462*1303Swesolows 	return (MIB_REGISTERED_OK);
463*1303Swesolows }
464*1303Swesolows 
465*1303Swesolows /*
466*1303Swesolows  * These two functions form the core of GET/GETNEXT/GETBULK handling (the
467*1303Swesolows  * only kind we do).  They perform two functions:
468*1303Swesolows  *
469*1303Swesolows  * - First, frob the request to set all the index variables to correspond
470*1303Swesolows  *   to the value that's going to be returned.  For GET, this is a nop;
471*1303Swesolows  *   for GETNEXT/GETBULK it always requires some work.
472*1303Swesolows  * - Second, find and return the fmd resource information corresponding to
473*1303Swesolows  *   the (possibly updated) indices.
474*1303Swesolows  *
475*1303Swesolows  * These should be as fast as possible; they run in the agent thread.
476*1303Swesolows  */
477*1303Swesolows static sunFmResource_data_t *
478*1303Swesolows sunFmResourceTable_nextrsrc(netsnmp_handler_registration *reginfo,
479*1303Swesolows     netsnmp_table_request_info *table_info)
480*1303Swesolows {
481*1303Swesolows 	sunFmResource_data_t	*data;
482*1303Swesolows 	netsnmp_variable_list	*var;
483*1303Swesolows 	ulong_t index;
484*1303Swesolows 
485*1303Swesolows 	/*
486*1303Swesolows 	 * If we have no index, we must make one.
487*1303Swesolows 	 */
488*1303Swesolows 	if (table_info->number_indexes < 1) {
489*1303Swesolows 		oid tmpoid[MAX_OID_LEN];
490*1303Swesolows 		index = 1;
491*1303Swesolows 
492*1303Swesolows 		DEBUGMSGTL((MODNAME_STR, "nextrsrc: no indexes given\n"));
493*1303Swesolows 		var = SNMP_MALLOC_TYPEDEF(netsnmp_variable_list);
494*1303Swesolows 		snmp_set_var_typed_value(var, ASN_UNSIGNED, (uchar_t *)&index,
495*1303Swesolows 		    sizeof (index));
496*1303Swesolows 		memcpy(tmpoid, reginfo->rootoid,
497*1303Swesolows 		    reginfo->rootoid_len * sizeof (oid));
498*1303Swesolows 		tmpoid[reginfo->rootoid_len] = 1;
499*1303Swesolows 		tmpoid[reginfo->rootoid_len + 1] = table_info->colnum;
500*1303Swesolows 		if (build_oid(&var->name, &var->name_length, tmpoid,
501*1303Swesolows 		    reginfo->rootoid_len + 2, var) != SNMPERR_SUCCESS) {
502*1303Swesolows 			snmp_free_varbind(var);
503*1303Swesolows 			return (NULL);
504*1303Swesolows 		}
505*1303Swesolows 		DEBUGMSGTL((MODNAME_STR, "nextrsrc: built fake index:\n"));
506*1303Swesolows 		DEBUGMSGVAR((MODNAME_STR, var));
507*1303Swesolows 		DEBUGMSG((MODNAME_STR, "\n"));
508*1303Swesolows 	} else {
509*1303Swesolows 		var = table_info->indexes;
510*1303Swesolows 		index = *var->val.integer;
511*1303Swesolows 		DEBUGMSGTL((MODNAME_STR, "nextrsrc: received index:\n"));
512*1303Swesolows 		DEBUGMSGVAR((MODNAME_STR, var));
513*1303Swesolows 		DEBUGMSG((MODNAME_STR, "\n"));
514*1303Swesolows 		index++;
515*1303Swesolows 	}
516*1303Swesolows 
517*1303Swesolows 	if ((data = resource_lookup_index_nextvalid(index)) == NULL) {
518*1303Swesolows 		DEBUGMSGTL((MODNAME_STR, "nextrsrc: exact match not found for "
519*1303Swesolows 		    "index %lu; trying next column\n", index));
520*1303Swesolows 		if (table_info->colnum >= SUNFMRESOURCE_COLMAX) {
521*1303Swesolows 			snmp_free_varbind(var);
522*1303Swesolows 			table_info->indexes = NULL;
523*1303Swesolows 			table_info->number_indexes = 0;
524*1303Swesolows 			DEBUGMSGTL((MODNAME_STR, "nextrsrc: out of columns\n"));
525*1303Swesolows 			return (NULL);
526*1303Swesolows 		}
527*1303Swesolows 		table_info->colnum++;
528*1303Swesolows 		index = 1;
529*1303Swesolows 
530*1303Swesolows 		data = resource_lookup_index_nextvalid(index);
531*1303Swesolows 	}
532*1303Swesolows 
533*1303Swesolows 	if (data == NULL) {
534*1303Swesolows 		DEBUGMSGTL((MODNAME_STR, "nextrsrc: exact match not found for "
535*1303Swesolows 		    "index %lu; stopping\n", index));
536*1303Swesolows 		snmp_free_varbind(var);
537*1303Swesolows 		table_info->indexes = NULL;
538*1303Swesolows 		table_info->number_indexes = 0;
539*1303Swesolows 		return (NULL);
540*1303Swesolows 	}
541*1303Swesolows 
542*1303Swesolows 	*var->val.integer = index;
543*1303Swesolows 	table_info->indexes = var;
544*1303Swesolows 
545*1303Swesolows 	DEBUGMSGTL((MODNAME_STR, "matching data is %lu/%s@%p\n", data->d_index,
546*1303Swesolows 	    data->d_ari_fmri, data));
547*1303Swesolows 
548*1303Swesolows 	return (data);
549*1303Swesolows }
550*1303Swesolows 
551*1303Swesolows /*ARGSUSED*/
552*1303Swesolows static sunFmResource_data_t *
553*1303Swesolows sunFmResourceTable_rsrc(netsnmp_handler_registration *reginfo,
554*1303Swesolows     netsnmp_table_request_info *table_info)
555*1303Swesolows {
556*1303Swesolows 	sunFmResource_data_t	*data;
557*1303Swesolows 	netsnmp_variable_list	*var;
558*1303Swesolows 
559*1303Swesolows 	ASSERT(table_info->number_indexes == 1);
560*1303Swesolows 
561*1303Swesolows 	return (resource_lookup_index_exact(table_info->index_oid[0]));
562*1303Swesolows }
563*1303Swesolows 
564*1303Swesolows static void
565*1303Swesolows sunFmResourceTable_return(unsigned int reg, void *arg)
566*1303Swesolows {
567*1303Swesolows 	netsnmp_delegated_cache		*cache = (netsnmp_delegated_cache *)arg;
568*1303Swesolows 	netsnmp_request_info		*request;
569*1303Swesolows 	netsnmp_agent_request_info	*reqinfo;
570*1303Swesolows 	netsnmp_handler_registration	*reginfo;
571*1303Swesolows 	netsnmp_mib_handler		*handler;
572*1303Swesolows 	netsnmp_table_request_info	*table_info;
573*1303Swesolows 	netsnmp_variable_list		*var;
574*1303Swesolows 	sunFmResource_data_t		*data;
575*1303Swesolows 	ulong_t				rsrcstate;
576*1303Swesolows 
577*1303Swesolows 	ASSERT(netsnmp_handler_check_cache(cache) != NULL);
578*1303Swesolows 
579*1303Swesolows 	(void) pthread_mutex_lock(&update_lock);
580*1303Swesolows 	if (update_status != US_QUIET) {
581*1303Swesolows 		struct timeval			tv;
582*1303Swesolows 
583*1303Swesolows 		tv.tv_sec = UPDATE_WAIT_MILLIS / 1000;
584*1303Swesolows 		tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000;
585*1303Swesolows 
586*1303Swesolows 		(void) snmp_alarm_register_hr(tv, 0, sunFmResourceTable_return,
587*1303Swesolows 		    cache);
588*1303Swesolows 		(void) pthread_mutex_unlock(&update_lock);
589*1303Swesolows 		return;
590*1303Swesolows 	}
591*1303Swesolows 
592*1303Swesolows 	request = cache->requests;
593*1303Swesolows 	reqinfo = cache->reqinfo;
594*1303Swesolows 	reginfo = cache->reginfo;
595*1303Swesolows 	handler = cache->handler;
596*1303Swesolows 
597*1303Swesolows 	var = request->requestvb;
598*1303Swesolows 	table_info = netsnmp_extract_table_info(request);
599*1303Swesolows 	request->delegated = 0;
600*1303Swesolows 
601*1303Swesolows 	ASSERT(table_info->colnum >= SUNFMRESOURCE_COLMIN);
602*1303Swesolows 	ASSERT(table_info->colnum <= SUNFMRESOURCE_COLMAX);
603*1303Swesolows 
604*1303Swesolows 	/*
605*1303Swesolows 	 * table_info->colnum contains the column number requested.
606*1303Swesolows 	 * table_info->indexes contains a linked list of snmp variable
607*1303Swesolows 	 * bindings for the indexes of the table.  Values in the list
608*1303Swesolows 	 * have been set corresponding to the indexes of the
609*1303Swesolows 	 * request.  We have other guarantees as well:
610*1303Swesolows 	 *
611*1303Swesolows 	 * - The column number is always within range.
612*1303Swesolows 	 * - If we have no index data, table_info->index_oid_len is 0.
613*1303Swesolows 	 * - We will never receive requests outside our table nor
614*1303Swesolows 	 *   those with the first subid anything other than 1 (Entry)
615*1303Swesolows 	 *   nor those without a column number.  This is true even
616*1303Swesolows 	 *   for GETNEXT requests.
617*1303Swesolows 	 */
618*1303Swesolows 
619*1303Swesolows 	switch (reqinfo->mode) {
620*1303Swesolows 	case MODE_GET:
621*1303Swesolows 		if ((data = sunFmResourceTable_rsrc(reginfo, table_info)) ==
622*1303Swesolows 		    NULL) {
623*1303Swesolows 			netsnmp_free_delegated_cache(cache);
624*1303Swesolows 			(void) pthread_mutex_unlock(&update_lock);
625*1303Swesolows 			return;
626*1303Swesolows 		}
627*1303Swesolows 		break;
628*1303Swesolows 	case MODE_GETNEXT:
629*1303Swesolows 	case MODE_GETBULK:
630*1303Swesolows 		if ((data = sunFmResourceTable_nextrsrc(reginfo, table_info)) ==
631*1303Swesolows 		    NULL) {
632*1303Swesolows 			netsnmp_free_delegated_cache(cache);
633*1303Swesolows 			(void) pthread_mutex_unlock(&update_lock);
634*1303Swesolows 			return;
635*1303Swesolows 		}
636*1303Swesolows 		break;
637*1303Swesolows 	default:
638*1303Swesolows 		snmp_log(LOG_ERR, MODNAME_STR ": Unsupported request mode %d\n",
639*1303Swesolows 		    reqinfo->mode);
640*1303Swesolows 		netsnmp_free_delegated_cache(cache);
641*1303Swesolows 		(void) pthread_mutex_unlock(&update_lock);
642*1303Swesolows 		return;
643*1303Swesolows 	}
644*1303Swesolows 
645*1303Swesolows 	switch (table_info->colnum) {
646*1303Swesolows 	case SUNFMRESOURCE_COL_FMRI:
647*1303Swesolows 		netsnmp_table_build_result(reginfo, request, table_info,
648*1303Swesolows 		    ASN_OCTET_STR, (uchar_t *)data->d_ari_fmri,
649*1303Swesolows 		    strlen(data->d_ari_fmri));
650*1303Swesolows 		break;
651*1303Swesolows 	case SUNFMRESOURCE_COL_STATUS:
652*1303Swesolows 		switch (data->d_ari_flags &
653*1303Swesolows 		    (FMD_ADM_RSRC_FAULTY|FMD_ADM_RSRC_UNUSABLE)) {
654*1303Swesolows 		default:
655*1303Swesolows 			rsrcstate = SUNFMRESOURCE_STATE_OK;
656*1303Swesolows 			break;
657*1303Swesolows 		case FMD_ADM_RSRC_FAULTY:
658*1303Swesolows 			rsrcstate = SUNFMRESOURCE_STATE_DEGRADED;
659*1303Swesolows 			break;
660*1303Swesolows 		case FMD_ADM_RSRC_UNUSABLE:
661*1303Swesolows 			rsrcstate = SUNFMRESOURCE_STATE_UNKNOWN;
662*1303Swesolows 			break;
663*1303Swesolows 		case FMD_ADM_RSRC_FAULTY | FMD_ADM_RSRC_UNUSABLE:
664*1303Swesolows 			rsrcstate = SUNFMRESOURCE_STATE_FAULTED;
665*1303Swesolows 			break;
666*1303Swesolows 		}
667*1303Swesolows 		netsnmp_table_build_result(reginfo, request, table_info,
668*1303Swesolows 		    ASN_INTEGER, (uchar_t *)&rsrcstate,
669*1303Swesolows 		    sizeof (rsrcstate));
670*1303Swesolows 		break;
671*1303Swesolows 	case SUNFMRESOURCE_COL_DIAGNOSISUUID:
672*1303Swesolows 		netsnmp_table_build_result(reginfo, request, table_info,
673*1303Swesolows 		    ASN_OCTET_STR, (uchar_t *)data->d_ari_case,
674*1303Swesolows 		    strlen(data->d_ari_case));
675*1303Swesolows 		break;
676*1303Swesolows 	default:
677*1303Swesolows 		break;
678*1303Swesolows 	}
679*1303Swesolows 	netsnmp_free_delegated_cache(cache);
680*1303Swesolows 	(void) pthread_mutex_unlock(&update_lock);
681*1303Swesolows }
682*1303Swesolows 
683*1303Swesolows static int
684*1303Swesolows sunFmResourceTable_handler(netsnmp_mib_handler *handler,
685*1303Swesolows     netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo,
686*1303Swesolows     netsnmp_request_info *requests)
687*1303Swesolows {
688*1303Swesolows 	netsnmp_request_info		*request;
689*1303Swesolows 	struct timeval			tv;
690*1303Swesolows 
691*1303Swesolows 	tv.tv_sec = UPDATE_WAIT_MILLIS / 1000;
692*1303Swesolows 	tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000;
693*1303Swesolows 
694*1303Swesolows 	request_update();
695*1303Swesolows 
696*1303Swesolows 	for (request = requests; request; request = request->next) {
697*1303Swesolows 		if (request->processed != 0)
698*1303Swesolows 			continue;
699*1303Swesolows 
700*1303Swesolows 		if (netsnmp_extract_table_info(request) == NULL)
701*1303Swesolows 			continue;
702*1303Swesolows 
703*1303Swesolows 		request->delegated = 1;
704*1303Swesolows 		(void) snmp_alarm_register_hr(tv, 0, sunFmResourceTable_return,
705*1303Swesolows 		    (void *) netsnmp_create_delegated_cache(handler, reginfo,
706*1303Swesolows 		    reqinfo, request, NULL));
707*1303Swesolows 	}
708*1303Swesolows 
709*1303Swesolows 	return (SNMP_ERR_NOERROR);
710*1303Swesolows }
711*1303Swesolows 
712*1303Swesolows static void
713*1303Swesolows sunFmResourceCount_return(unsigned int reg, void *arg)
714*1303Swesolows {
715*1303Swesolows 	netsnmp_delegated_cache		*cache = (netsnmp_delegated_cache *)arg;
716*1303Swesolows 	netsnmp_request_info		*request;
717*1303Swesolows 	netsnmp_agent_request_info	*reqinfo;
718*1303Swesolows 	netsnmp_handler_registration	*reginfo;
719*1303Swesolows 	netsnmp_mib_handler		*handler;
720*1303Swesolows 	ulong_t				rsrc_count_long;
721*1303Swesolows 	int				err;
722*1303Swesolows 
723*1303Swesolows 	ASSERT(netsnmp_handler_check_cache(cache) != NULL);
724*1303Swesolows 
725*1303Swesolows 	(void) pthread_mutex_lock(&update_lock);
726*1303Swesolows 	if (update_status != US_QUIET) {
727*1303Swesolows 		struct timeval	tv;
728*1303Swesolows 
729*1303Swesolows 		tv.tv_sec = UPDATE_WAIT_MILLIS / 1000;
730*1303Swesolows 		tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000;
731*1303Swesolows 
732*1303Swesolows 		(void) snmp_alarm_register_hr(tv, 0, sunFmResourceCount_return,
733*1303Swesolows 		    cache);
734*1303Swesolows 		(void) pthread_mutex_unlock(&update_lock);
735*1303Swesolows 		return;
736*1303Swesolows 	}
737*1303Swesolows 
738*1303Swesolows 	request = cache->requests;
739*1303Swesolows 	reqinfo = cache->reqinfo;
740*1303Swesolows 	reginfo = cache->reginfo;
741*1303Swesolows 	handler = cache->handler;
742*1303Swesolows 
743*1303Swesolows 	request->delegated = 0;
744*1303Swesolows 
745*1303Swesolows 	switch (reqinfo->mode) {
746*1303Swesolows 	case MODE_GET:
747*1303Swesolows 		DEBUGMSGTL((MODNAME_STR, "resource count is %u\n", rsrc_count));
748*1303Swesolows 		rsrc_count_long = (ulong_t)rsrc_count;
749*1303Swesolows 		snmp_set_var_typed_value(request->requestvb, ASN_GAUGE,
750*1303Swesolows 		    (uchar_t *)&rsrc_count_long, sizeof (rsrc_count_long));
751*1303Swesolows 		break;
752*1303Swesolows 	default:
753*1303Swesolows 		snmp_log(LOG_ERR, MODNAME_STR ": Unsupported request mode %d\n",
754*1303Swesolows 		    reqinfo->mode);
755*1303Swesolows 	}
756*1303Swesolows 
757*1303Swesolows 	netsnmp_free_delegated_cache(cache);
758*1303Swesolows 	(void) pthread_mutex_unlock(&update_lock);
759*1303Swesolows }
760*1303Swesolows 
761*1303Swesolows static int
762*1303Swesolows sunFmResourceCount_handler(netsnmp_mib_handler *handler,
763*1303Swesolows     netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo,
764*1303Swesolows     netsnmp_request_info *requests)
765*1303Swesolows {
766*1303Swesolows 	struct timeval	tv;
767*1303Swesolows 
768*1303Swesolows 	tv.tv_sec = UPDATE_WAIT_MILLIS / 1000;
769*1303Swesolows 	tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000;
770*1303Swesolows 
771*1303Swesolows 	request_update();
772*1303Swesolows 
773*1303Swesolows 	/*
774*1303Swesolows 	 * We are never called for a GETNEXT when registered as an
775*1303Swesolows 	 * instance; it's handled for us and converted to a GET.
776*1303Swesolows 	 * Also, an instance handler is given only one request at a time, so
777*1303Swesolows 	 * we don't need to loop over a list of requests.
778*1303Swesolows 	 */
779*1303Swesolows 
780*1303Swesolows 	if (requests->processed != 0)
781*1303Swesolows 		return (SNMP_ERR_NOERROR);
782*1303Swesolows 
783*1303Swesolows 	requests->delegated = 1;
784*1303Swesolows 	(void) snmp_alarm_register_hr(tv, 0, sunFmResourceCount_return,
785*1303Swesolows 	    (void *) netsnmp_create_delegated_cache(handler, reginfo,
786*1303Swesolows 	    reqinfo, requests, NULL));
787*1303Swesolows 
788*1303Swesolows 	return (SNMP_ERR_NOERROR);
789*1303Swesolows }
790