xref: /onnv-gate/usr/src/lib/fm/libfmd_snmp/common/module.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 "module.h"
40*1303Swesolows 
41*1303Swesolows static uu_avl_pool_t	*mod_name_avl_pool;
42*1303Swesolows static uu_avl_pool_t	*mod_index_avl_pool;
43*1303Swesolows static uu_avl_t		*mod_name_avl;
44*1303Swesolows static uu_avl_t		*mod_index_avl;
45*1303Swesolows 
46*1303Swesolows #define	VALID_AVL_STATE	(mod_name_avl_pool != NULL &&		\
47*1303Swesolows 	mod_index_avl_pool != NULL && mod_name_avl != NULL &&	\
48*1303Swesolows 	mod_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.
54*1303Swesolows  */
55*1303Swesolows #define	UCT_INDEX	0x1
56*1303Swesolows #define	UCT_ALL		0x2
57*1303Swesolows #define	UCT_FLAGS	0x3
58*1303Swesolows 
59*1303Swesolows #define	MODULE_DATA_VALID(d)	((d)->d_valid == valid_stamp)
60*1303Swesolows 
61*1303Swesolows /*
62*1303Swesolows  * Locking rules are straightforward.  There is only one updater thread
63*1303Swesolows  * for each table, and requests for update that are received while
64*1303Swesolows  * another update is in progress are ignored.  The single per-table lock
65*1303Swesolows  * protects all the data for the table, the valid_stamp and max_index
66*1303Swesolows  * tags for new data, and - importantly - all the hidden static data
67*1303Swesolows  * used by the Net-SNMP library.  The result return callbacks are always
68*1303Swesolows  * called in the master agent thread; holding the table lock is
69*1303Swesolows  * therefore sufficient since only one table's callback can be run at
70*1303Swesolows  * any one time.  Finer-grained locking is possible here but
71*1303Swesolows  * substantially more difficult because nearly all Net-SNMP functions
72*1303Swesolows  * are unsafe.
73*1303Swesolows  *
74*1303Swesolows  * In practice this is more than adequate, since the purpose of
75*1303Swesolows  * threading out the update is to prevent excessively time-consuming
76*1303Swesolows  * data collection from bottlenecking the entire agent, not to improve
77*1303Swesolows  * result throughput (SNMP is not intended to be used for applications
78*1303Swesolows  * requiring high throughput anyway).  If the agent itself ever becomes
79*1303Swesolows  * multithreaded, locking requirements become limited to our local
80*1303Swesolows  * per-table data (the tree, max_index, and valid_stamp), and the
81*1303Swesolows  * implementation could be revisited for better performance.
82*1303Swesolows  */
83*1303Swesolows 
84*1303Swesolows static ulong_t		max_index;
85*1303Swesolows static int		valid_stamp;
86*1303Swesolows static pthread_mutex_t	update_lock;
87*1303Swesolows static pthread_cond_t	update_cv;
88*1303Swesolows static volatile enum { US_QUIET, US_NEEDED, US_INPROGRESS } update_status;
89*1303Swesolows 
90*1303Swesolows static Netsnmp_Node_Handler	sunFmModuleTable_handler;
91*1303Swesolows 
92*1303Swesolows static sunFmModule_data_t *
93*1303Swesolows key_build(const char *name, const ulong_t index)
94*1303Swesolows {
95*1303Swesolows 	static sunFmModule_data_t	key;
96*1303Swesolows 
97*1303Swesolows 	key.d_index = index;
98*1303Swesolows 	if (name)
99*1303Swesolows 		strlcpy(key.d_ami_name, name, sizeof (key.d_ami_name));
100*1303Swesolows 	else
101*1303Swesolows 		key.d_ami_name[0] = '\0';
102*1303Swesolows 
103*1303Swesolows 	return (&key);
104*1303Swesolows }
105*1303Swesolows 
106*1303Swesolows /*
107*1303Swesolows  * If name is the name of a module we have previously seen and indexed, return
108*1303Swesolows  * data for it.  Otherwise, return NULL.  Note that the module may not be
109*1303Swesolows  * valid; that is, it may have been removed from the fault manager since its
110*1303Swesolows  * information was last updated.
111*1303Swesolows  */
112*1303Swesolows static sunFmModule_data_t *
113*1303Swesolows module_lookup_name(const char *name)
114*1303Swesolows {
115*1303Swesolows 	sunFmModule_data_t	*key;
116*1303Swesolows 
117*1303Swesolows 	key = key_build(name, 0);
118*1303Swesolows 	return (uu_avl_find(mod_name_avl, key, NULL, NULL));
119*1303Swesolows }
120*1303Swesolows 
121*1303Swesolows /*
122*1303Swesolows  * If index corresponds to a module we have previously seen and indexed, return
123*1303Swesolows  * data for it.  Otherwise, return NULL.  Note that the module may not be
124*1303Swesolows  * valid; that is, it may have been removed from the fault manager since its
125*1303Swesolows  * information was last updated.
126*1303Swesolows  */
127*1303Swesolows static sunFmModule_data_t *
128*1303Swesolows module_lookup_index_exact(const ulong_t index)
129*1303Swesolows {
130*1303Swesolows 	sunFmModule_data_t	*key;
131*1303Swesolows 
132*1303Swesolows 	key = key_build(NULL, index);
133*1303Swesolows 	return (uu_avl_find(mod_index_avl, key, NULL, NULL));
134*1303Swesolows }
135*1303Swesolows 
136*1303Swesolows /*
137*1303Swesolows  * If index corresponds to a valid (that is, extant as of latest information
138*1303Swesolows  * from the fault manager) fmd module, return the data for that module.
139*1303Swesolows  * Otherwise, return the data for the valid module whose index is as close as
140*1303Swesolows  * possible to index but not lower.  This preserves the lexicographical
141*1303Swesolows  * ordering required for GETNEXT processing.
142*1303Swesolows  */
143*1303Swesolows static sunFmModule_data_t *
144*1303Swesolows module_lookup_index_nextvalid(const ulong_t index)
145*1303Swesolows {
146*1303Swesolows 	sunFmModule_data_t	*key, *data;
147*1303Swesolows 	uu_avl_index_t		idx;
148*1303Swesolows 
149*1303Swesolows 	key = key_build(NULL, index);
150*1303Swesolows 
151*1303Swesolows 	if ((data = uu_avl_find(mod_index_avl, key, NULL, &idx)) != NULL &&
152*1303Swesolows 	    MODULE_DATA_VALID(data))
153*1303Swesolows 		return (data);
154*1303Swesolows 
155*1303Swesolows 	data = uu_avl_nearest_next(mod_index_avl, idx);
156*1303Swesolows 
157*1303Swesolows 	while (data != NULL && !MODULE_DATA_VALID(data)) {
158*1303Swesolows 		(void) uu_avl_find(mod_index_avl, data, NULL, &idx);
159*1303Swesolows 		data = uu_avl_nearest_next(mod_index_avl, idx);
160*1303Swesolows 	}
161*1303Swesolows 
162*1303Swesolows 	return (data);
163*1303Swesolows }
164*1303Swesolows 
165*1303Swesolows /*
166*1303Swesolows  * Possible update the contents of a single module within the cache.  This
167*1303Swesolows  * is our callback from fmd_module_iter.
168*1303Swesolows  */
169*1303Swesolows static int
170*1303Swesolows modinfo_update_one(const fmd_adm_modinfo_t *modinfo, void *arg)
171*1303Swesolows {
172*1303Swesolows 	const sunFmModule_update_ctx_t *update_ctx =
173*1303Swesolows 	    (sunFmModule_update_ctx_t *)arg;
174*1303Swesolows 	sunFmModule_data_t *data = module_lookup_name(modinfo->ami_name);
175*1303Swesolows 
176*1303Swesolows 	/*
177*1303Swesolows 	 * An fmd module we haven't seen before.  We're obligated to index
178*1303Swesolows 	 * it and link it into our cache so that we can find it, but we're
179*1303Swesolows 	 * not obligated to fill it in completely unless we're doing a
180*1303Swesolows 	 * thorough update or this is the module we were asked for.  This
181*1303Swesolows 	 * avoids unnecessary iteration and memory manipulation for data
182*1303Swesolows 	 * we're not going to return for this request.
183*1303Swesolows 	 */
184*1303Swesolows 	if (data == NULL) {
185*1303Swesolows 		uu_avl_index_t idx;
186*1303Swesolows 
187*1303Swesolows 		DEBUGMSGTL((MODNAME_STR, "found new fmd module %s\n",
188*1303Swesolows 		    modinfo->ami_name));
189*1303Swesolows 		if ((data = SNMP_MALLOC_TYPEDEF(sunFmModule_data_t)) == NULL) {
190*1303Swesolows 			snmp_log(LOG_ERR, MODNAME_STR ": Out of memory for "
191*1303Swesolows 			    "new module data at %s:%d\n", __FILE__, __LINE__);
192*1303Swesolows 			return (1);
193*1303Swesolows 		}
194*1303Swesolows 		/*
195*1303Swesolows 		 * We allocate indices sequentially and never reuse them.
196*1303Swesolows 		 * This ensures we can always return valid GETNEXT responses
197*1303Swesolows 		 * without having to reindex, and it provides the user a
198*1303Swesolows 		 * more consistent view of the fault manager.
199*1303Swesolows 		 */
200*1303Swesolows 		data->d_index = ++max_index;
201*1303Swesolows 		DEBUGMSGTL((MODNAME_STR, "index %lu is %s@%p\n", data->d_index,
202*1303Swesolows 		    modinfo->ami_name, data));
203*1303Swesolows 
204*1303Swesolows 		strlcpy(data->d_ami_name, modinfo->ami_name,
205*1303Swesolows 		    sizeof (data->d_ami_name));
206*1303Swesolows 
207*1303Swesolows 		uu_avl_node_init(data, &data->d_name_avl, mod_name_avl_pool);
208*1303Swesolows 		(void) uu_avl_find(mod_name_avl, data, NULL, &idx);
209*1303Swesolows 		uu_avl_insert(mod_name_avl, data, idx);
210*1303Swesolows 
211*1303Swesolows 		uu_avl_node_init(data, &data->d_index_avl, mod_index_avl_pool);
212*1303Swesolows 		(void) uu_avl_find(mod_index_avl, data, NULL, &idx);
213*1303Swesolows 		uu_avl_insert(mod_index_avl, data, idx);
214*1303Swesolows 
215*1303Swesolows 		DEBUGMSGTL((MODNAME_STR, "completed new module %lu/%s@%p\n",
216*1303Swesolows 		    data->d_index, data->d_ami_name, data));
217*1303Swesolows 	}
218*1303Swesolows 
219*1303Swesolows 	data->d_valid = valid_stamp;
220*1303Swesolows 
221*1303Swesolows 	DEBUGMSGTL((MODNAME_STR, "timestamp updated for %lu/%s@%p: %lu\n",
222*1303Swesolows 	    data->d_index, data->d_ami_name, data, data->d_valid));
223*1303Swesolows 
224*1303Swesolows 	if ((update_ctx->uc_type & UCT_ALL) ||
225*1303Swesolows 	    update_ctx->uc_index == data->d_index) {
226*1303Swesolows 		strlcpy(data->d_ami_vers, modinfo->ami_vers,
227*1303Swesolows 		    sizeof (data->d_ami_vers));
228*1303Swesolows 		strlcpy(data->d_ami_desc, modinfo->ami_desc,
229*1303Swesolows 		    sizeof (data->d_ami_desc));
230*1303Swesolows 		data->d_ami_flags = modinfo->ami_flags;
231*1303Swesolows 	}
232*1303Swesolows 
233*1303Swesolows 	return (!(update_ctx->uc_type & UCT_ALL) &&
234*1303Swesolows 	    update_ctx->uc_index == data->d_index);
235*1303Swesolows }
236*1303Swesolows 
237*1303Swesolows /*
238*1303Swesolows  * Update some or all module data from fmd.  If thorough is set, all modules
239*1303Swesolows  * will be indexed and their data cached.  Otherwise, updates will stop once
240*1303Swesolows  * the module matching index has been updated.
241*1303Swesolows  *
242*1303Swesolows  * Returns appropriate SNMP error codes.
243*1303Swesolows  */
244*1303Swesolows static int
245*1303Swesolows modinfo_update(sunFmModule_update_ctx_t *update_ctx)
246*1303Swesolows {
247*1303Swesolows 	fmd_adm_t *adm;
248*1303Swesolows 
249*1303Swesolows 	ASSERT(update_ctx != NULL);
250*1303Swesolows 	ASSERT((update_ctx->uc_type & (UCT_INDEX|UCT_ALL)) !=
251*1303Swesolows 	    (UCT_INDEX|UCT_ALL));
252*1303Swesolows 	ASSERT((update_ctx->uc_type & ~UCT_FLAGS) == 0);
253*1303Swesolows 	ASSERT(VALID_AVL_STATE);
254*1303Swesolows 
255*1303Swesolows 	if ((adm = fmd_adm_open(update_ctx->uc_host, update_ctx->uc_prog,
256*1303Swesolows 	    update_ctx->uc_version)) == NULL) {
257*1303Swesolows 		snmp_log(LOG_ERR, MODNAME_STR ": Communication with fmd "
258*1303Swesolows 		    "failed: %s\n", strerror(errno));
259*1303Swesolows 		return (SNMP_ERR_RESOURCEUNAVAILABLE);
260*1303Swesolows 	}
261*1303Swesolows 
262*1303Swesolows 	++valid_stamp;
263*1303Swesolows 	if (fmd_adm_module_iter(adm, modinfo_update_one, update_ctx) != 0) {
264*1303Swesolows 		snmp_log(LOG_ERR, MODNAME_STR ": fmd module information update "
265*1303Swesolows 		    "failed: %s\n", fmd_adm_errmsg(adm));
266*1303Swesolows 		fmd_adm_close(adm);
267*1303Swesolows 		return (SNMP_ERR_RESOURCEUNAVAILABLE);
268*1303Swesolows 	}
269*1303Swesolows 
270*1303Swesolows 	DEBUGMSGTL((MODNAME_STR, "module iteration completed\n"));
271*1303Swesolows 
272*1303Swesolows 	fmd_adm_close(adm);
273*1303Swesolows 	return (SNMP_ERR_NOERROR);
274*1303Swesolows }
275*1303Swesolows 
276*1303Swesolows /*ARGSUSED*/
277*1303Swesolows static void
278*1303Swesolows update_thread(void *arg)
279*1303Swesolows {
280*1303Swesolows 	/*
281*1303Swesolows 	 * The current modinfo_update implementation offers minimal savings
282*1303Swesolows 	 * for the use of index-only updates; therefore we always do a full
283*1303Swesolows 	 * update.  If it becomes advantageous to limit updates to a single
284*1303Swesolows 	 * index, the contexts can be queued by the handler instead.
285*1303Swesolows 	 */
286*1303Swesolows 	sunFmModule_update_ctx_t	uc;
287*1303Swesolows 
288*1303Swesolows 	uc.uc_host = NULL;
289*1303Swesolows 	uc.uc_prog = FMD_ADM_PROGRAM;
290*1303Swesolows 	uc.uc_version = FMD_ADM_VERSION;
291*1303Swesolows 
292*1303Swesolows 	uc.uc_index = 0;
293*1303Swesolows 	uc.uc_type = UCT_ALL;
294*1303Swesolows 
295*1303Swesolows 	for (;;) {
296*1303Swesolows 		(void) pthread_mutex_lock(&update_lock);
297*1303Swesolows 		update_status = US_QUIET;
298*1303Swesolows 		while (update_status == US_QUIET)
299*1303Swesolows 			(void) pthread_cond_wait(&update_cv, &update_lock);
300*1303Swesolows 		update_status = US_INPROGRESS;
301*1303Swesolows 		(void) pthread_mutex_unlock(&update_lock);
302*1303Swesolows 		(void) modinfo_update(&uc);
303*1303Swesolows 	}
304*1303Swesolows }
305*1303Swesolows 
306*1303Swesolows static void
307*1303Swesolows request_update(void)
308*1303Swesolows {
309*1303Swesolows 	(void) pthread_mutex_lock(&update_lock);
310*1303Swesolows 	if (update_status != US_QUIET) {
311*1303Swesolows 		(void) pthread_mutex_unlock(&update_lock);
312*1303Swesolows 		return;
313*1303Swesolows 	}
314*1303Swesolows 	update_status = US_NEEDED;
315*1303Swesolows 	(void) pthread_cond_signal(&update_cv);
316*1303Swesolows 	(void) pthread_mutex_unlock(&update_lock);
317*1303Swesolows }
318*1303Swesolows 
319*1303Swesolows /*ARGSUSED*/
320*1303Swesolows static int
321*1303Swesolows module_compare_name(const void *l, const void *r, void *private)
322*1303Swesolows {
323*1303Swesolows 	sunFmModule_data_t	*l_data = (sunFmModule_data_t *)l;
324*1303Swesolows 	sunFmModule_data_t	*r_data = (sunFmModule_data_t *)r;
325*1303Swesolows 
326*1303Swesolows 	ASSERT(l_data != NULL && r_data != NULL);
327*1303Swesolows 
328*1303Swesolows 	return (strcmp(l_data->d_ami_name, r_data->d_ami_name));
329*1303Swesolows }
330*1303Swesolows 
331*1303Swesolows /*ARGSUSED*/
332*1303Swesolows static int
333*1303Swesolows module_compare_index(const void *l, const void *r, void *private)
334*1303Swesolows {
335*1303Swesolows 	sunFmModule_data_t	*l_data = (sunFmModule_data_t *)l;
336*1303Swesolows 	sunFmModule_data_t	*r_data = (sunFmModule_data_t *)r;
337*1303Swesolows 
338*1303Swesolows 	ASSERT(l_data != NULL && r_data != NULL);
339*1303Swesolows 
340*1303Swesolows 	return (l_data->d_index < r_data->d_index ? -1 :
341*1303Swesolows 	    l_data->d_index > r_data->d_index ? 1 : 0);
342*1303Swesolows }
343*1303Swesolows 
344*1303Swesolows int
345*1303Swesolows sunFmModuleTable_init(void)
346*1303Swesolows {
347*1303Swesolows 	static oid sunFmModuleTable_oid[] = { SUNFMMODULETABLE_OID };
348*1303Swesolows 	netsnmp_table_registration_info *table_info;
349*1303Swesolows 	netsnmp_handler_registration *handler;
350*1303Swesolows 	int err;
351*1303Swesolows 
352*1303Swesolows 	if ((err = pthread_mutex_init(&update_lock, NULL)) != 0) {
353*1303Swesolows 		snmp_log(LOG_ERR, MODNAME_STR ": mutex_init failure: %s\n",
354*1303Swesolows 		    strerror(err));
355*1303Swesolows 		return (MIB_REGISTRATION_FAILED);
356*1303Swesolows 	}
357*1303Swesolows 	if ((err = pthread_cond_init(&update_cv, NULL)) != 0) {
358*1303Swesolows 		snmp_log(LOG_ERR, MODNAME_STR ": cond_init failure: %s\n",
359*1303Swesolows 		    strerror(err));
360*1303Swesolows 		return (MIB_REGISTRATION_FAILED);
361*1303Swesolows 	}
362*1303Swesolows 
363*1303Swesolows 	if ((err = pthread_create(NULL, NULL, (void *(*)(void *))update_thread,
364*1303Swesolows 	    NULL)) != 0) {
365*1303Swesolows 		snmp_log(LOG_ERR, MODNAME_STR ": error creating update "
366*1303Swesolows 		    "thread: %s\n", strerror(err));
367*1303Swesolows 		return (MIB_REGISTRATION_FAILED);
368*1303Swesolows 	}
369*1303Swesolows 
370*1303Swesolows 	if ((table_info =
371*1303Swesolows 	    SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info)) == NULL)
372*1303Swesolows 		return (MIB_REGISTRATION_FAILED);
373*1303Swesolows 
374*1303Swesolows 	if ((handler = netsnmp_create_handler_registration("sunFmModuleTable",
375*1303Swesolows 	    sunFmModuleTable_handler, sunFmModuleTable_oid,
376*1303Swesolows 	    OID_LENGTH(sunFmModuleTable_oid), HANDLER_CAN_RONLY)) == NULL) {
377*1303Swesolows 		SNMP_FREE(table_info);
378*1303Swesolows 		return (MIB_REGISTRATION_FAILED);
379*1303Swesolows 	}
380*1303Swesolows 
381*1303Swesolows 	/*
382*1303Swesolows 	 * The Net-SNMP template uses add_indexes here, but that
383*1303Swesolows 	 * function is unsafe because it does not check for failure.
384*1303Swesolows 	 */
385*1303Swesolows 	if (netsnmp_table_helper_add_index(table_info, ASN_UNSIGNED) == NULL) {
386*1303Swesolows 		SNMP_FREE(table_info);
387*1303Swesolows 		SNMP_FREE(handler);
388*1303Swesolows 		return (MIB_REGISTRATION_FAILED);
389*1303Swesolows 	}
390*1303Swesolows 
391*1303Swesolows 	table_info->min_column = SUNFMMODULE_COLMIN;
392*1303Swesolows 	table_info->max_column = SUNFMMODULE_COLMAX;
393*1303Swesolows 
394*1303Swesolows 	if ((mod_name_avl_pool = uu_avl_pool_create("mod_name",
395*1303Swesolows 	    sizeof (sunFmModule_data_t),
396*1303Swesolows 	    offsetof(sunFmModule_data_t, d_name_avl), module_compare_name,
397*1303Swesolows 	    UU_AVL_DEBUG)) == NULL) {
398*1303Swesolows 		snmp_free_varbind(table_info->indexes);
399*1303Swesolows 		SNMP_FREE(table_info);
400*1303Swesolows 		SNMP_FREE(handler);
401*1303Swesolows 	}
402*1303Swesolows 
403*1303Swesolows 	if ((mod_name_avl = uu_avl_create(mod_name_avl_pool, NULL,
404*1303Swesolows 	    UU_AVL_DEBUG)) == NULL) {
405*1303Swesolows 		snmp_log(LOG_ERR, MODNAME_STR ": mod_name_avl creation "
406*1303Swesolows 		    "failed: %s\n", uu_strerror(uu_error()));
407*1303Swesolows 		snmp_free_varbind(table_info->indexes);
408*1303Swesolows 		SNMP_FREE(table_info);
409*1303Swesolows 		SNMP_FREE(handler);
410*1303Swesolows 		uu_avl_pool_destroy(mod_name_avl_pool);
411*1303Swesolows 		return (MIB_REGISTRATION_FAILED);
412*1303Swesolows 	}
413*1303Swesolows 
414*1303Swesolows 	if ((mod_index_avl_pool = uu_avl_pool_create("mod_index",
415*1303Swesolows 	    sizeof (sunFmModule_data_t),
416*1303Swesolows 	    offsetof(sunFmModule_data_t, d_index_avl),
417*1303Swesolows 	    module_compare_index, UU_AVL_DEBUG)) == NULL) {
418*1303Swesolows 		snmp_free_varbind(table_info->indexes);
419*1303Swesolows 		SNMP_FREE(table_info);
420*1303Swesolows 		SNMP_FREE(handler);
421*1303Swesolows 		uu_avl_destroy(mod_name_avl);
422*1303Swesolows 		uu_avl_pool_destroy(mod_name_avl_pool);
423*1303Swesolows 	}
424*1303Swesolows 
425*1303Swesolows 	if ((mod_index_avl = uu_avl_create(mod_index_avl_pool, NULL,
426*1303Swesolows 	    UU_AVL_DEBUG)) == NULL) {
427*1303Swesolows 		snmp_log(LOG_ERR, MODNAME_STR ": mod_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(mod_name_avl);
433*1303Swesolows 		uu_avl_pool_destroy(mod_name_avl_pool);
434*1303Swesolows 		uu_avl_pool_destroy(mod_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(mod_name_avl);
444*1303Swesolows 		uu_avl_pool_destroy(mod_name_avl_pool);
445*1303Swesolows 		uu_avl_destroy(mod_index_avl);
446*1303Swesolows 		uu_avl_pool_destroy(mod_index_avl_pool);
447*1303Swesolows 		return (err);
448*1303Swesolows 	}
449*1303Swesolows 
450*1303Swesolows 	return (MIB_REGISTERED_OK);
451*1303Swesolows }
452*1303Swesolows 
453*1303Swesolows /*
454*1303Swesolows  * These two functions form the core of GET/GETNEXT/GETBULK handling (the
455*1303Swesolows  * only kind we do).  They perform two functions:
456*1303Swesolows  *
457*1303Swesolows  * - First, frob the request to set all the index variables to correspond
458*1303Swesolows  *   to the value that's going to be returned.  For GET, this is a nop;
459*1303Swesolows  *   for GETNEXT/GETBULK it always requires some work.
460*1303Swesolows  * - Second, find and return the fmd module information corresponding to
461*1303Swesolows  *   the (possibly updated) indices.
462*1303Swesolows  *
463*1303Swesolows  * These should be as fast as possible; they run in the agent thread.
464*1303Swesolows  */
465*1303Swesolows static sunFmModule_data_t *
466*1303Swesolows sunFmModuleTable_nextmod(netsnmp_handler_registration *reginfo,
467*1303Swesolows     netsnmp_table_request_info *table_info)
468*1303Swesolows {
469*1303Swesolows 	sunFmModule_data_t	*data;
470*1303Swesolows 	netsnmp_variable_list	*var;
471*1303Swesolows 	ulong_t index;
472*1303Swesolows 
473*1303Swesolows 	/*
474*1303Swesolows 	 * If we have no index, we must make one.
475*1303Swesolows 	 */
476*1303Swesolows 	if (table_info->number_indexes < 1) {
477*1303Swesolows 		oid tmpoid[MAX_OID_LEN];
478*1303Swesolows 		index = 1;
479*1303Swesolows 
480*1303Swesolows 		DEBUGMSGTL((MODNAME_STR, "nextmod: no indexes given\n"));
481*1303Swesolows 		var = SNMP_MALLOC_TYPEDEF(netsnmp_variable_list);
482*1303Swesolows 		snmp_set_var_typed_value(var, ASN_UNSIGNED, (uchar_t *)&index,
483*1303Swesolows 		    sizeof (index));
484*1303Swesolows 		memcpy(tmpoid, reginfo->rootoid,
485*1303Swesolows 		    reginfo->rootoid_len * sizeof (oid));
486*1303Swesolows 		tmpoid[reginfo->rootoid_len] = 1;	/* Entry is .1 */
487*1303Swesolows 		tmpoid[reginfo->rootoid_len + 1] = table_info->colnum;
488*1303Swesolows 		if (build_oid(&var->name, &var->name_length, tmpoid,
489*1303Swesolows 		    reginfo->rootoid_len + 2, var) != SNMPERR_SUCCESS)
490*1303Swesolows 			return (NULL);
491*1303Swesolows 		DEBUGMSGTL((MODNAME_STR, "nextmod: built fake index:\n"));
492*1303Swesolows 		DEBUGMSGVAR((MODNAME_STR, var));
493*1303Swesolows 		DEBUGMSG((MODNAME_STR, "\n"));
494*1303Swesolows 	} else {
495*1303Swesolows 		var = table_info->indexes;
496*1303Swesolows 		index = *var->val.integer;
497*1303Swesolows 		DEBUGMSGTL((MODNAME_STR, "nextmod: received index:\n"));
498*1303Swesolows 		DEBUGMSGVAR((MODNAME_STR, var));
499*1303Swesolows 		DEBUGMSG((MODNAME_STR, "\n"));
500*1303Swesolows 		index++;
501*1303Swesolows 	}
502*1303Swesolows 
503*1303Swesolows 	if ((data = module_lookup_index_nextvalid(index)) == NULL) {
504*1303Swesolows 		DEBUGMSGTL((MODNAME_STR, "nextmod: exact match not found for "
505*1303Swesolows 		    "index %lu; trying next column\n", index));
506*1303Swesolows 		if (table_info->colnum >= SUNFMMODULE_COLMAX) {
507*1303Swesolows 			snmp_free_varbind(var);
508*1303Swesolows 			table_info->indexes = NULL;
509*1303Swesolows 			table_info->number_indexes = 0;
510*1303Swesolows 			DEBUGMSGTL((MODNAME_STR, "nextmod: out of columns\n"));
511*1303Swesolows 			return (NULL);
512*1303Swesolows 		}
513*1303Swesolows 		table_info->colnum++;
514*1303Swesolows 		index = 1;
515*1303Swesolows 
516*1303Swesolows 		data = module_lookup_index_nextvalid(index);
517*1303Swesolows 	}
518*1303Swesolows 
519*1303Swesolows 	if (data == NULL) {
520*1303Swesolows 		DEBUGMSGTL((MODNAME_STR, "nextmod: exact match not found for "
521*1303Swesolows 		    "index %lu; stopping\n", index));
522*1303Swesolows 		snmp_free_varbind(var);
523*1303Swesolows 		table_info->indexes = NULL;
524*1303Swesolows 		table_info->number_indexes = 0;
525*1303Swesolows 		return (NULL);
526*1303Swesolows 	}
527*1303Swesolows 
528*1303Swesolows 	*var->val.integer = index;
529*1303Swesolows 	table_info->indexes = var;
530*1303Swesolows 
531*1303Swesolows 	DEBUGMSGTL((MODNAME_STR, "matching data is %lu/%s@%p\n", data->d_index,
532*1303Swesolows 	    data->d_ami_name, data));
533*1303Swesolows 
534*1303Swesolows 	return (data);
535*1303Swesolows }
536*1303Swesolows 
537*1303Swesolows /*ARGSUSED*/
538*1303Swesolows static sunFmModule_data_t *
539*1303Swesolows sunFmModuleTable_mod(netsnmp_handler_registration *reginfo,
540*1303Swesolows     netsnmp_table_request_info *table_info)
541*1303Swesolows {
542*1303Swesolows 	sunFmModule_data_t	*data;
543*1303Swesolows 	netsnmp_variable_list	*var;
544*1303Swesolows 
545*1303Swesolows 	ASSERT(table_info->number_indexes == 1);
546*1303Swesolows 
547*1303Swesolows 	return (module_lookup_index_exact(table_info->index_oid[0]));
548*1303Swesolows }
549*1303Swesolows 
550*1303Swesolows static void
551*1303Swesolows sunFmModuleTable_return(unsigned int reg, void *arg)
552*1303Swesolows {
553*1303Swesolows 	netsnmp_delegated_cache		*cache = (netsnmp_delegated_cache *)arg;
554*1303Swesolows 	netsnmp_request_info		*request;
555*1303Swesolows 	netsnmp_agent_request_info	*reqinfo;
556*1303Swesolows 	netsnmp_handler_registration	*reginfo;
557*1303Swesolows 	netsnmp_mib_handler		*handler;
558*1303Swesolows 	netsnmp_table_request_info	*table_info;
559*1303Swesolows 	netsnmp_variable_list		*var;
560*1303Swesolows 	sunFmModule_data_t		*data;
561*1303Swesolows 	ulong_t				modstate;
562*1303Swesolows 
563*1303Swesolows 	ASSERT(netsnmp_handler_check_cache(cache) != NULL);
564*1303Swesolows 
565*1303Swesolows 	(void) pthread_mutex_lock(&update_lock);
566*1303Swesolows 	if (update_status != US_QUIET) {
567*1303Swesolows 		struct timeval			tv;
568*1303Swesolows 
569*1303Swesolows 		tv.tv_sec = UPDATE_WAIT_MILLIS / 1000;
570*1303Swesolows 		tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000;
571*1303Swesolows 
572*1303Swesolows 		(void) snmp_alarm_register_hr(tv, 0, sunFmModuleTable_return,
573*1303Swesolows 		    cache);
574*1303Swesolows 		(void) pthread_mutex_unlock(&update_lock);
575*1303Swesolows 		return;
576*1303Swesolows 	}
577*1303Swesolows 
578*1303Swesolows 	request = cache->requests;
579*1303Swesolows 	reqinfo = cache->reqinfo;
580*1303Swesolows 	reginfo = cache->reginfo;
581*1303Swesolows 	handler = cache->handler;
582*1303Swesolows 
583*1303Swesolows 	var = request->requestvb;
584*1303Swesolows 	table_info = netsnmp_extract_table_info(request);
585*1303Swesolows 	request->delegated = 0;
586*1303Swesolows 
587*1303Swesolows 	ASSERT(table_info->colnum >= SUNFMMODULE_COLMIN);
588*1303Swesolows 	ASSERT(table_info->colnum <= SUNFMMODULE_COLMAX);
589*1303Swesolows 
590*1303Swesolows 	/*
591*1303Swesolows 	 * table_info->colnum contains the column number requested.
592*1303Swesolows 	 * table_info->indexes contains a linked list of snmp variable
593*1303Swesolows 	 * bindings for the indexes of the table.  Values in the list
594*1303Swesolows 	 * have been set corresponding to the indexes of the
595*1303Swesolows 	 * request.  We have other guarantees as well:
596*1303Swesolows 	 *
597*1303Swesolows 	 * - The column number is always within range.
598*1303Swesolows 	 * - If we have no index data, table_info->index_oid_len is 0.
599*1303Swesolows 	 * - We will never receive requests outside our table nor
600*1303Swesolows 	 *   those with the first subid anything other than 1 (Entry)
601*1303Swesolows 	 *   nor those without a column number.  This is true even
602*1303Swesolows 	 *   for GETNEXT requests.
603*1303Swesolows 	 */
604*1303Swesolows 
605*1303Swesolows 	switch (reqinfo->mode) {
606*1303Swesolows 	case MODE_GET:
607*1303Swesolows 		if ((data = sunFmModuleTable_mod(reginfo, table_info)) ==
608*1303Swesolows 		    NULL) {
609*1303Swesolows 			netsnmp_free_delegated_cache(cache);
610*1303Swesolows 			(void) pthread_mutex_unlock(&update_lock);
611*1303Swesolows 			return;
612*1303Swesolows 		}
613*1303Swesolows 		break;
614*1303Swesolows 	case MODE_GETNEXT:
615*1303Swesolows 	case MODE_GETBULK:
616*1303Swesolows 		if ((data = sunFmModuleTable_nextmod(reginfo, table_info)) ==
617*1303Swesolows 		    NULL) {
618*1303Swesolows 			netsnmp_free_delegated_cache(cache);
619*1303Swesolows 			(void) pthread_mutex_unlock(&update_lock);
620*1303Swesolows 			return;
621*1303Swesolows 		}
622*1303Swesolows 		break;
623*1303Swesolows 	default:
624*1303Swesolows 		snmp_log(LOG_ERR, MODNAME_STR ": Unsupported request "
625*1303Swesolows 		    "mode %d\n", reqinfo->mode);
626*1303Swesolows 		netsnmp_free_delegated_cache(cache);
627*1303Swesolows 		(void) pthread_mutex_unlock(&update_lock);
628*1303Swesolows 		return;
629*1303Swesolows 	}
630*1303Swesolows 
631*1303Swesolows 	switch (table_info->colnum) {
632*1303Swesolows 	case SUNFMMODULE_COL_NAME:
633*1303Swesolows 		netsnmp_table_build_result(reginfo, request, table_info,
634*1303Swesolows 		    ASN_OCTET_STR, (uchar_t *)data->d_ami_name,
635*1303Swesolows 		    strlen(data->d_ami_name));
636*1303Swesolows 		break;
637*1303Swesolows 	case SUNFMMODULE_COL_VERSION:
638*1303Swesolows 		netsnmp_table_build_result(reginfo, request, table_info,
639*1303Swesolows 		    ASN_OCTET_STR, (uchar_t *)data->d_ami_vers,
640*1303Swesolows 		    strlen(data->d_ami_vers));
641*1303Swesolows 		break;
642*1303Swesolows 	case SUNFMMODULE_COL_STATUS:
643*1303Swesolows 		modstate = (data->d_ami_flags & FMD_ADM_MOD_FAILED) ?
644*1303Swesolows 		    SUNFMMODULE_STATE_FAILED : SUNFMMODULE_STATE_ACTIVE;
645*1303Swesolows 		netsnmp_table_build_result(reginfo, request, table_info,
646*1303Swesolows 		    ASN_INTEGER, (uchar_t *)&modstate,
647*1303Swesolows 		    sizeof (modstate));
648*1303Swesolows 		break;
649*1303Swesolows 	case SUNFMMODULE_COL_DESCRIPTION:
650*1303Swesolows 		netsnmp_table_build_result(reginfo, request, table_info,
651*1303Swesolows 		    ASN_OCTET_STR, (uchar_t *)data->d_ami_desc,
652*1303Swesolows 		    strlen(data->d_ami_desc));
653*1303Swesolows 		break;
654*1303Swesolows 	default:
655*1303Swesolows 		break;
656*1303Swesolows 	}
657*1303Swesolows 	netsnmp_free_delegated_cache(cache);
658*1303Swesolows 	(void) pthread_mutex_unlock(&update_lock);
659*1303Swesolows }
660*1303Swesolows 
661*1303Swesolows static int
662*1303Swesolows sunFmModuleTable_handler(netsnmp_mib_handler *handler,
663*1303Swesolows     netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo,
664*1303Swesolows     netsnmp_request_info *requests)
665*1303Swesolows {
666*1303Swesolows 	netsnmp_request_info		*request;
667*1303Swesolows 	struct timeval			tv;
668*1303Swesolows 
669*1303Swesolows 	tv.tv_sec = UPDATE_WAIT_MILLIS / 1000;
670*1303Swesolows 	tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000;
671*1303Swesolows 
672*1303Swesolows 	request_update();
673*1303Swesolows 
674*1303Swesolows 	for (request = requests; request; request = request->next) {
675*1303Swesolows 		if (request->processed != 0)
676*1303Swesolows 			continue;
677*1303Swesolows 
678*1303Swesolows 		if (netsnmp_extract_table_info(request) == NULL)
679*1303Swesolows 			continue;
680*1303Swesolows 
681*1303Swesolows 		request->delegated = 1;
682*1303Swesolows 		(void) snmp_alarm_register_hr(tv, 0, sunFmModuleTable_return,
683*1303Swesolows 		    (void *) netsnmp_create_delegated_cache(handler, reginfo,
684*1303Swesolows 		    reqinfo, request, NULL));
685*1303Swesolows 	}
686*1303Swesolows 
687*1303Swesolows 	return (SNMP_ERR_NOERROR);
688*1303Swesolows }
689