xref: /titanic_51/usr/src/lib/fm/libfmd_snmp/common/problem.c (revision b6917abefc343244b784f0cc34bc65b01469c3bf)
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 2008 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 #include <sys/fm/protocol.h>
30 #include <fm/fmd_adm.h>
31 #include <fm/fmd_snmp.h>
32 #include <net-snmp/net-snmp-config.h>
33 #include <net-snmp/net-snmp-includes.h>
34 #include <net-snmp/agent/net-snmp-agent-includes.h>
35 #include <pthread.h>
36 #include <stddef.h>
37 #include <errno.h>
38 #include <alloca.h>
39 #include <locale.h>
40 #include <libuutil.h>
41 #include <libnvpair.h>
42 #include "sunFM_impl.h"
43 #include "problem.h"
44 
45 /*
46  * We assume that the number of suspect fault events associated with a
47  * particular case will generally be sufficiently small that the overhead
48  * associated with indexing them in a tree would exceed the gain from
49  * not traversing the fault list for each request.
50  */
51 static uu_avl_pool_t	*problem_uuid_avl_pool;
52 static uu_avl_t		*problem_uuid_avl;
53 
54 #define	VALID_AVL_STATE	(problem_uuid_avl_pool != NULL &&	\
55 	problem_uuid_avl != NULL)
56 
57 #define	UPDATE_WAIT_MILLIS	10	/* poll interval in milliseconds */
58 
59 /*
60  * Update types.  Single-index and all are mutually exclusive.
61  */
62 #define	UCT_INDEX	0x1
63 #define	UCT_ALL		0x2
64 #define	UCT_FLAGS	0x3
65 
66 /*
67  * Locking strategy is described in module.c.
68  */
69 static int		valid_stamp;
70 static pthread_mutex_t	update_lock;
71 static pthread_cond_t	update_cv;
72 static volatile enum { US_QUIET, US_NEEDED, US_INPROGRESS } update_status;
73 
74 static Netsnmp_Node_Handler	sunFmProblemTable_handler;
75 static Netsnmp_Node_Handler	sunFmFaultEventTable_handler;
76 
77 static sunFmProblem_data_t *
78 problem_key_build(const char *uuid)
79 {
80 	static sunFmProblem_data_t	key;
81 
82 	key.d_aci_uuid = uuid;
83 
84 	return (&key);
85 }
86 
87 static sunFmProblem_data_t *
88 problem_lookup_uuid_exact(const char *uuid)
89 {
90 	sunFmProblem_data_t	*key, *data;
91 
92 	key = problem_key_build(uuid);
93 
94 	DEBUGMSGTL((MODNAME_STR, "lookup_exact for uuid %s\n", uuid));
95 	data = uu_avl_find(problem_uuid_avl, key, NULL, NULL);
96 
97 	return (data);
98 }
99 
100 static sunFmProblem_data_t *
101 problem_lookup_uuid_next(const char *uuid)
102 {
103 	sunFmProblem_data_t	*key, *data;
104 	uu_avl_index_t		idx;
105 
106 	key = problem_key_build(uuid);
107 
108 	DEBUGMSGTL((MODNAME_STR, "lookup_next for uuid %s\n", uuid));
109 	(void) uu_avl_find(problem_uuid_avl, key, NULL, &idx);
110 
111 	data = uu_avl_nearest_next(problem_uuid_avl, idx);
112 
113 	DEBUGMSGTL((MODNAME_STR, "lookup_next: entry is %p\n", data));
114 
115 	return (data);
116 }
117 
118 static sunFmFaultEvent_data_t *
119 faultevent_lookup_index_exact(sunFmProblem_data_t *data, ulong_t index)
120 {
121 	if (index > data->d_nsuspects)
122 		return (NULL);
123 
124 	if (data->d_suspects == NULL)
125 		return (NULL);
126 
127 	return (data->d_suspects[index - 1]);
128 }
129 
130 static sunFmFaultStatus_data_t
131 faultstatus_lookup_index_exact(sunFmProblem_data_t *data, ulong_t index)
132 {
133 	if (index > data->d_nsuspects)
134 		return (NULL);
135 
136 	if (data->d_statuses == NULL)
137 		return (NULL);
138 
139 	return (data->d_statuses[index - 1]);
140 }
141 
142 /*ARGSUSED*/
143 static int
144 problem_update_one(const fmd_adm_caseinfo_t *acp, void *arg)
145 {
146 	sunFmProblem_data_t		*data;
147 	nvlist_t			*nvl;
148 	int64_t				*diag_time;
149 	uint_t				nelem;
150 	uint32_t			nsusp;
151 	int				err;
152 
153 	DEBUGMSGTL((MODNAME_STR, "update_one\n"));
154 
155 	ASSERT(acp->aci_uuid != NULL);
156 
157 	if ((data = problem_lookup_uuid_exact(acp->aci_uuid)) == NULL) {
158 		uu_avl_index_t idx;
159 
160 		DEBUGMSGTL((MODNAME_STR, "found new problem %s\n",
161 		    acp->aci_uuid));
162 		if ((data = SNMP_MALLOC_TYPEDEF(sunFmProblem_data_t)) == NULL) {
163 			snmp_log(LOG_ERR, MODNAME_STR ": Out of memory for "
164 			    "new problem data at %s:%d\n", __FILE__, __LINE__);
165 			return (0);
166 		}
167 		if ((err = nvlist_dup(acp->aci_event, &data->d_aci_event, 0))
168 		    != 0) {
169 			snmp_log(LOG_ERR, MODNAME_STR ": Problem data setup "
170 			    "failed: %s\n", strerror(err));
171 			SNMP_FREE(data);
172 			return (0);
173 		}
174 
175 		data->d_aci_uuid = data->d_aci_code = data->d_aci_url = "-";
176 		(void) nvlist_lookup_string(data->d_aci_event, FM_SUSPECT_UUID,
177 		    (char **)&data->d_aci_uuid);
178 		(void) nvlist_lookup_string(data->d_aci_event,
179 		    FM_SUSPECT_DIAG_CODE, (char **)&data->d_aci_code);
180 		data->d_aci_url = strdup(acp->aci_url);
181 
182 		if (nvlist_lookup_nvlist(data->d_aci_event, FM_SUSPECT_DE,
183 		    &nvl) == 0)
184 			if ((data->d_diag_engine = sunFm_nvl2str(nvl)) == NULL)
185 				data->d_diag_engine = "-";
186 
187 		if (nvlist_lookup_int64_array(data->d_aci_event,
188 		    FM_SUSPECT_DIAG_TIME, &diag_time, &nelem) == 0 &&
189 		    nelem >= 2) {
190 			data->d_diag_time.tv_sec = (long)diag_time[0];
191 			data->d_diag_time.tv_usec = (long)diag_time[1];
192 		}
193 
194 		(void) nvlist_lookup_uint32(data->d_aci_event,
195 		    FM_SUSPECT_FAULT_SZ, &nsusp);
196 		data->d_nsuspects = (ulong_t)nsusp;
197 
198 		(void) nvlist_lookup_nvlist_array(data->d_aci_event,
199 		    FM_SUSPECT_FAULT_LIST, &data->d_suspects, &nelem);
200 
201 		ASSERT(nelem == data->d_nsuspects);
202 
203 		(void) nvlist_lookup_uint8_array(data->d_aci_event,
204 		    FM_SUSPECT_FAULT_STATUS, &data->d_statuses, &nelem);
205 
206 		ASSERT(nelem == data->d_nsuspects);
207 
208 		uu_avl_node_init(data, &data->d_uuid_avl,
209 		    problem_uuid_avl_pool);
210 		(void) uu_avl_find(problem_uuid_avl, data, NULL, &idx);
211 		uu_avl_insert(problem_uuid_avl, data, idx);
212 
213 		data->d_valid = valid_stamp;
214 
215 		DEBUGMSGTL((MODNAME_STR, "completed new problem %s@%p\n",
216 		    data->d_aci_uuid, data));
217 	}
218 
219 	/*
220 	 * We don't touch problems we've seen before; they shouldn't change
221 	 * in any way we care about, since they've already been solved.  The
222 	 * state, however, could change, and if we later expose that to the
223 	 * client we need to update it here.
224 	 */
225 
226 	return (0);
227 }
228 
229 static int
230 problem_update(sunFmProblem_update_ctx_t *update_ctx)
231 {
232 	fmd_adm_t *adm;
233 
234 	ASSERT(update_ctx != NULL);
235 	ASSERT((update_ctx->uc_type & (UCT_INDEX|UCT_ALL)) !=
236 	    (UCT_INDEX|UCT_ALL));
237 	ASSERT((update_ctx->uc_type & ~UCT_FLAGS) == 0);
238 	ASSERT(VALID_AVL_STATE);
239 
240 	if ((adm = fmd_adm_open(update_ctx->uc_host, update_ctx->uc_prog,
241 	    update_ctx->uc_version)) == NULL) {
242 		snmp_log(LOG_ERR, MODNAME_STR ": Communication with fmd "
243 		    "failed: %s\n", strerror(errno));
244 		return (SNMP_ERR_RESOURCEUNAVAILABLE);
245 	}
246 
247 	++valid_stamp;
248 	if (fmd_adm_case_iter(adm, SNMP_URL_MSG, problem_update_one,
249 	    update_ctx) != 0) {
250 		snmp_log(LOG_ERR, MODNAME_STR ": fmd case information update "
251 		    "failed: %s\n", fmd_adm_errmsg(adm));
252 		fmd_adm_close(adm);
253 		return (SNMP_ERR_RESOURCEUNAVAILABLE);
254 	}
255 
256 	DEBUGMSGTL((MODNAME_STR, "case iteration completed\n"));
257 
258 	fmd_adm_close(adm);
259 	return (SNMP_ERR_NOERROR);
260 }
261 
262 /*ARGSUSED*/
263 static void
264 update_thread(void *arg)
265 {
266 	/*
267 	 * The current problem_update implementation offers minimal savings
268 	 * for the use of index-only updates; therefore we always do a full
269 	 * update.  If it becomes advantageous to limit updates to a single
270 	 * index, the contexts can be queued by the handler instead.
271 	 */
272 	sunFmProblem_update_ctx_t	uc;
273 
274 	uc.uc_host = NULL;
275 	uc.uc_prog = FMD_ADM_PROGRAM;
276 	uc.uc_version = FMD_ADM_VERSION;
277 
278 	uc.uc_index = NULL;
279 	uc.uc_type = UCT_ALL;
280 
281 	for (;;) {
282 		(void) pthread_mutex_lock(&update_lock);
283 		update_status = US_QUIET;
284 		while (update_status == US_QUIET)
285 			(void) pthread_cond_wait(&update_cv, &update_lock);
286 		update_status = US_INPROGRESS;
287 		(void) pthread_mutex_unlock(&update_lock);
288 		(void) problem_update(&uc);
289 	}
290 }
291 
292 static void
293 request_update(void)
294 {
295 	(void) pthread_mutex_lock(&update_lock);
296 	if (update_status != US_QUIET) {
297 		(void) pthread_mutex_unlock(&update_lock);
298 		return;
299 	}
300 	update_status = US_NEEDED;
301 	(void) pthread_cond_signal(&update_cv);
302 	(void) pthread_mutex_unlock(&update_lock);
303 }
304 
305 /*ARGSUSED*/
306 static int
307 problem_compare_uuid(const void *l, const void *r, void *private)
308 {
309 	sunFmProblem_data_t	*l_data = (sunFmProblem_data_t *)l;
310 	sunFmProblem_data_t	*r_data = (sunFmProblem_data_t *)r;
311 
312 	ASSERT(l_data != NULL && r_data != NULL);
313 
314 	return (strcmp(l_data->d_aci_uuid, r_data->d_aci_uuid));
315 }
316 
317 int
318 sunFmProblemTable_init(void)
319 {
320 	static oid sunFmProblemTable_oid[] = { SUNFMPROBLEMTABLE_OID };
321 	netsnmp_table_registration_info *table_info;
322 	netsnmp_handler_registration *handler;
323 	int err;
324 
325 	if ((err = pthread_mutex_init(&update_lock, NULL)) != 0) {
326 		snmp_log(LOG_ERR, MODNAME_STR ": mutex_init failure: %s\n",
327 		    strerror(err));
328 		return (MIB_REGISTRATION_FAILED);
329 	}
330 	if ((err = pthread_cond_init(&update_cv, NULL)) != 0) {
331 		snmp_log(LOG_ERR, MODNAME_STR ": cond_init failure: %s\n",
332 		    strerror(err));
333 		return (MIB_REGISTRATION_FAILED);
334 	}
335 
336 	if ((err = pthread_create(NULL, NULL, (void *(*)(void *))update_thread,
337 	    NULL)) != 0) {
338 		snmp_log(LOG_ERR, MODNAME_STR ": error creating update "
339 		    "thread: %s\n", strerror(err));
340 		return (MIB_REGISTRATION_FAILED);
341 	}
342 
343 	if ((table_info =
344 	    SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info)) == NULL)
345 		return (MIB_REGISTRATION_FAILED);
346 
347 	if ((handler = netsnmp_create_handler_registration("sunFmProblemTable",
348 	    sunFmProblemTable_handler, sunFmProblemTable_oid,
349 	    OID_LENGTH(sunFmProblemTable_oid), HANDLER_CAN_RONLY)) == NULL) {
350 		SNMP_FREE(table_info);
351 		return (MIB_REGISTRATION_FAILED);
352 	}
353 
354 	/*
355 	 * The Net-SNMP template uses add_indexes here, but that
356 	 * function is unsafe because it does not check for failure.
357 	 */
358 	if (netsnmp_table_helper_add_index(table_info, ASN_OCTET_STR) == NULL) {
359 		SNMP_FREE(table_info);
360 		SNMP_FREE(handler);
361 		return (MIB_REGISTRATION_FAILED);
362 	}
363 
364 	table_info->min_column = SUNFMPROBLEM_COLMIN;
365 	table_info->max_column = SUNFMPROBLEM_COLMAX;
366 
367 	if ((problem_uuid_avl_pool = uu_avl_pool_create("problem_uuid",
368 	    sizeof (sunFmProblem_data_t),
369 	    offsetof(sunFmProblem_data_t, d_uuid_avl), problem_compare_uuid,
370 	    UU_AVL_DEBUG)) == NULL) {
371 		snmp_log(LOG_ERR, MODNAME_STR ": problem_uuid avl pool "
372 		    "creation failed: %s\n", uu_strerror(uu_error()));
373 		snmp_free_varbind(table_info->indexes);
374 		SNMP_FREE(table_info);
375 		SNMP_FREE(handler);
376 		return (MIB_REGISTRATION_FAILED);
377 	}
378 
379 	if ((problem_uuid_avl = uu_avl_create(problem_uuid_avl_pool, NULL,
380 	    UU_AVL_DEBUG)) == NULL) {
381 		snmp_log(LOG_ERR, MODNAME_STR ": problem_uuid avl creation "
382 		    "failed: %s\n", uu_strerror(uu_error()));
383 		snmp_free_varbind(table_info->indexes);
384 		SNMP_FREE(table_info);
385 		SNMP_FREE(handler);
386 		uu_avl_pool_destroy(problem_uuid_avl_pool);
387 		return (MIB_REGISTRATION_FAILED);
388 	}
389 
390 	if ((err = netsnmp_register_table(handler, table_info)) !=
391 	    MIB_REGISTERED_OK) {
392 		snmp_free_varbind(table_info->indexes);
393 		SNMP_FREE(table_info);
394 		SNMP_FREE(handler);
395 		uu_avl_destroy(problem_uuid_avl);
396 		uu_avl_pool_destroy(problem_uuid_avl_pool);
397 		return (err);
398 	}
399 
400 	return (MIB_REGISTERED_OK);
401 }
402 
403 int
404 sunFmFaultEventTable_init(void)
405 {
406 	static oid sunFmFaultEventTable_oid[] = { SUNFMFAULTEVENTTABLE_OID };
407 	netsnmp_table_registration_info *table_info;
408 	netsnmp_handler_registration *handler;
409 	int err;
410 
411 	if ((table_info =
412 	    SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info)) == NULL)
413 		return (MIB_REGISTRATION_FAILED);
414 
415 	if ((handler =
416 	    netsnmp_create_handler_registration("sunFmFaultEventTable",
417 	    sunFmFaultEventTable_handler, sunFmFaultEventTable_oid,
418 	    OID_LENGTH(sunFmFaultEventTable_oid), HANDLER_CAN_RONLY)) == NULL) {
419 		SNMP_FREE(table_info);
420 		return (MIB_REGISTRATION_FAILED);
421 	}
422 
423 	/*
424 	 * The Net-SNMP template uses add_indexes here, but that
425 	 * function is unsafe because it does not check for failure.
426 	 */
427 	if (netsnmp_table_helper_add_index(table_info, ASN_OCTET_STR) == NULL) {
428 		SNMP_FREE(table_info);
429 		SNMP_FREE(handler);
430 		return (MIB_REGISTRATION_FAILED);
431 	}
432 	if (netsnmp_table_helper_add_index(table_info, ASN_UNSIGNED) == NULL) {
433 		snmp_free_varbind(table_info->indexes);
434 		SNMP_FREE(table_info);
435 		SNMP_FREE(handler);
436 		return (MIB_REGISTRATION_FAILED);
437 	}
438 
439 	table_info->min_column = SUNFMFAULTEVENT_COLMIN;
440 	table_info->max_column = SUNFMFAULTEVENT_COLMAX;
441 
442 	if ((err = netsnmp_register_table(handler, table_info)) !=
443 	    MIB_REGISTERED_OK) {
444 		snmp_free_varbind(table_info->indexes);
445 		SNMP_FREE(table_info);
446 		SNMP_FREE(handler);
447 		return (err);
448 	}
449 
450 	return (MIB_REGISTERED_OK);
451 }
452 
453 /*
454  * Returns the problem data for the problem whose uuid is next according
455  * to ASN.1 lexical ordering after the request in table_info.  Indexes are
456  * updated to reflect the OID of the value being returned.  This allows
457  * us to implement GETNEXT.
458  */
459 static sunFmProblem_data_t *
460 sunFmProblemTable_nextpr(netsnmp_handler_registration *reginfo,
461     netsnmp_table_request_info *table_info)
462 {
463 	sunFmProblem_data_t	*data;
464 	char			*uuid = "";
465 
466 	if (table_info->number_indexes < 1) {
467 		oid tmpoid[MAX_OID_LEN];
468 
469 		DEBUGMSGTL((MODNAME_STR, "nextpr: no indexes given\n"));
470 
471 		snmp_free_varbind(table_info->indexes);
472 		table_info->indexes =
473 		    SNMP_MALLOC_TYPEDEF(netsnmp_variable_list);
474 		snmp_set_var_typed_value(table_info->indexes, ASN_OCTET_STR,
475 		    (const uchar_t *)uuid, 0);
476 		(void) memcpy(tmpoid, reginfo->rootoid,
477 		    reginfo->rootoid_len * sizeof (oid));
478 		tmpoid[reginfo->rootoid_len] = 1;
479 		tmpoid[reginfo->rootoid_len + 1] = table_info->colnum;
480 		if (build_oid_segment(table_info->indexes) != SNMPERR_SUCCESS) {
481 			snmp_free_varbind(table_info->indexes);
482 			return (NULL);
483 		}
484 		table_info->number_indexes = 1;
485 		table_info->index_oid_len = table_info->indexes->name_length;
486 		(void) memcpy(table_info->index_oid, table_info->indexes->name,
487 		    table_info->indexes->name_length);
488 
489 		DEBUGMSGTL((MODNAME_STR, "nextpr: built fake index:\n"));
490 		DEBUGMSGVAR((MODNAME_STR, table_info->indexes));
491 		DEBUGMSG((MODNAME_STR, "\n"));
492 	} else {
493 		/*
494 		 * Construct the next possible UUID to look for.  We can
495 		 * simply increment the least significant byte of the last
496 		 * UUID because (a) that preserves SNMP lex order and (b)
497 		 * the characters that may appear in a UUID do not include
498 		 * 127 nor 255.
499 		 */
500 		uuid = alloca(table_info->indexes->val_len + 1);
501 		(void) strlcpy(uuid,
502 		    (const char *)table_info->indexes->val.string,
503 		    table_info->indexes->val_len + 1);
504 		++uuid[table_info->indexes->val_len - 1];
505 
506 		DEBUGMSGTL((MODNAME_STR, "nextpr: received index:\n"));
507 		DEBUGMSGVAR((MODNAME_STR, table_info->indexes));
508 		DEBUGMSG((MODNAME_STR, "\n"));
509 	}
510 
511 	if ((data = problem_lookup_uuid_next(uuid)) == NULL) {
512 		DEBUGMSGTL((MODNAME_STR, "nextpr: next match not found for "
513 		    "%s; trying next column\n", uuid));
514 		if (table_info->colnum >=
515 		    netsnmp_find_table_registration_info(reginfo)->max_column) {
516 			snmp_free_varbind(table_info->indexes);
517 			table_info->indexes = NULL;
518 			table_info->number_indexes = 0;
519 			DEBUGMSGTL((MODNAME_STR, "nextpr: out of columns\n"));
520 			return (NULL);
521 		}
522 		table_info->colnum++;
523 		DEBUGMSGTL((MODNAME_STR, "nextpr: search for col %u empty "
524 		    "uuid\n", table_info->colnum, uuid));
525 
526 		if ((data = problem_lookup_uuid_next("")) == NULL) {
527 			DEBUGMSGTL((MODNAME_STR, "nextpr: next match not found "
528 			    "for empty uuid; stopping\n"));
529 			snmp_free_varbind(table_info->indexes);
530 			table_info->indexes = NULL;
531 			table_info->number_indexes = 0;
532 			return (NULL);
533 		}
534 	}
535 
536 	snmp_set_var_typed_value(table_info->indexes, ASN_OCTET_STR,
537 	    (uchar_t *)data->d_aci_uuid, strlen(data->d_aci_uuid));
538 	table_info->number_indexes = 1;
539 
540 	DEBUGMSGTL((MODNAME_STR, "matching data is %s@%p\n", data->d_aci_uuid,
541 	    data));
542 
543 	return (data);
544 }
545 
546 /*
547  * Returns the problem data corresponding to the request in table_info.
548  * All request parameters are unmodified.
549  */
550 /*ARGSUSED*/
551 static sunFmProblem_data_t *
552 sunFmProblemTable_pr(netsnmp_handler_registration *reginfo,
553     netsnmp_table_request_info *table_info)
554 {
555 	char			*uuid;
556 
557 	ASSERT(table_info->number_indexes >= 1);
558 
559 	uuid = alloca(table_info->indexes->val_len + 1);
560 	(void) strlcpy(uuid, (const char *)table_info->indexes->val.string,
561 	    table_info->indexes->val_len + 1);
562 
563 	return (problem_lookup_uuid_exact(uuid));
564 }
565 
566 /*
567  * Returns the ASN.1 lexicographically first fault event after the one
568  * identified by table_info.  Indexes are updated to reflect the OID
569  * of the data returned.  This allows us to implement GETNEXT.
570  */
571 static sunFmFaultEvent_data_t *
572 sunFmFaultEventTable_nextfe(netsnmp_handler_registration *reginfo,
573     netsnmp_table_request_info *table_info)
574 {
575 	sunFmProblem_data_t	*data;
576 	sunFmFaultEvent_data_t	*rv;
577 	netsnmp_variable_list	*var;
578 	ulong_t			index;
579 
580 	for (;;) {
581 		switch (table_info->number_indexes) {
582 		case 2:
583 		default:
584 			DEBUGMSGTL((MODNAME_STR, "nextfe: 2 indices:\n"));
585 			DEBUGMSGVAR((MODNAME_STR, table_info->indexes));
586 			DEBUGMSG((MODNAME_STR, "\n"));
587 			DEBUGMSGVAR((MODNAME_STR,
588 			    table_info->indexes->next_variable));
589 			DEBUGMSG((MODNAME_STR, "\n"));
590 			index = *(ulong_t *)
591 			    table_info->indexes->next_variable->val.integer + 1;
592 
593 			if ((data = sunFmProblemTable_pr(reginfo,
594 			    table_info)) != NULL &&
595 			    (rv = faultevent_lookup_index_exact(data, index)) !=
596 			    NULL) {
597 				snmp_set_var_typed_value(
598 				    table_info->indexes->next_variable,
599 				    ASN_UNSIGNED, (uchar_t *)&index,
600 				    sizeof (index));
601 				return (rv);
602 			}
603 
604 			if (sunFmProblemTable_nextpr(reginfo, table_info) ==
605 			    NULL)
606 				return (NULL);
607 			break;
608 		case 1:
609 			if ((data = sunFmProblemTable_pr(reginfo,
610 			    table_info)) != NULL) {
611 				oid tmpoid[MAX_OID_LEN];
612 				index = 0;
613 
614 				DEBUGMSGTL((MODNAME_STR, "nextfe: 1 index:\n"));
615 				DEBUGMSGVAR((MODNAME_STR, table_info->indexes));
616 				DEBUGMSG((MODNAME_STR, "\n"));
617 				var =
618 				    SNMP_MALLOC_TYPEDEF(netsnmp_variable_list);
619 				snmp_set_var_typed_value(var, ASN_UNSIGNED,
620 				    (uchar_t *)&index, sizeof (index));
621 				(void) memcpy(tmpoid, reginfo->rootoid,
622 				    reginfo->rootoid_len * sizeof (oid));
623 				tmpoid[reginfo->rootoid_len] = 1;
624 				tmpoid[reginfo->rootoid_len + 1] =
625 				    table_info->colnum;
626 				if (build_oid_segment(var) != SNMPERR_SUCCESS) {
627 					snmp_free_varbind(var);
628 					return (NULL);
629 				}
630 				snmp_free_varbind(
631 				    table_info->indexes->next_variable);
632 				table_info->indexes->next_variable = var;
633 				table_info->number_indexes = 2;
634 				DEBUGMSGTL((MODNAME_STR, "nextfe: built fake "
635 				    "index:\n"));
636 				DEBUGMSGVAR((MODNAME_STR, table_info->indexes));
637 				DEBUGMSG((MODNAME_STR, "\n"));
638 				DEBUGMSGVAR((MODNAME_STR,
639 				    table_info->indexes->next_variable));
640 				DEBUGMSG((MODNAME_STR, "\n"));
641 			} else {
642 				if (sunFmProblemTable_nextpr(reginfo,
643 				    table_info) == NULL)
644 					return (NULL);
645 			}
646 			break;
647 		case 0:
648 			if (sunFmProblemTable_nextpr(reginfo, table_info) ==
649 			    NULL)
650 				return (NULL);
651 			break;
652 		}
653 	}
654 }
655 
656 /*
657  * Returns the ASN.1 lexicographically first fault event after the one
658  * identified by table_info.  Indexes are updated to reflect the OID
659  * of the data returned.  This allows us to implement GETNEXT.
660  */
661 static sunFmFaultStatus_data_t
662 sunFmFaultStatusTable_nextfe(netsnmp_handler_registration *reginfo,
663     netsnmp_table_request_info *table_info)
664 {
665 	sunFmProblem_data_t	*data;
666 	sunFmFaultStatus_data_t	rv;
667 	netsnmp_variable_list	*var;
668 	ulong_t			index;
669 
670 	for (;;) {
671 		switch (table_info->number_indexes) {
672 		case 2:
673 		default:
674 			DEBUGMSGTL((MODNAME_STR, "nextfe: 2 indices:\n"));
675 			DEBUGMSGVAR((MODNAME_STR, table_info->indexes));
676 			DEBUGMSG((MODNAME_STR, "\n"));
677 			DEBUGMSGVAR((MODNAME_STR,
678 			    table_info->indexes->next_variable));
679 			DEBUGMSG((MODNAME_STR, "\n"));
680 			index = *(ulong_t *)
681 			    table_info->indexes->next_variable->val.integer + 1;
682 
683 			if ((data = sunFmProblemTable_pr(reginfo,
684 			    table_info)) != NULL &&
685 			    (rv = faultstatus_lookup_index_exact(data,
686 			    index)) != NULL) {
687 				snmp_set_var_typed_value(
688 				    table_info->indexes->next_variable,
689 				    ASN_UNSIGNED, (uchar_t *)&index,
690 				    sizeof (index));
691 				return (rv);
692 			}
693 
694 			if (sunFmProblemTable_nextpr(reginfo, table_info) ==
695 			    NULL)
696 				return (NULL);
697 			break;
698 		case 1:
699 			if ((data = sunFmProblemTable_pr(reginfo,
700 			    table_info)) != NULL) {
701 				oid tmpoid[MAX_OID_LEN];
702 				index = 0;
703 
704 				DEBUGMSGTL((MODNAME_STR, "nextfe: 1 index:\n"));
705 				DEBUGMSGVAR((MODNAME_STR, table_info->indexes));
706 				DEBUGMSG((MODNAME_STR, "\n"));
707 				var =
708 				    SNMP_MALLOC_TYPEDEF(netsnmp_variable_list);
709 				snmp_set_var_typed_value(var, ASN_UNSIGNED,
710 				    (uchar_t *)&index, sizeof (index));
711 				(void) memcpy(tmpoid, reginfo->rootoid,
712 				    reginfo->rootoid_len * sizeof (oid));
713 				tmpoid[reginfo->rootoid_len] = 1;
714 				tmpoid[reginfo->rootoid_len + 1] =
715 				    table_info->colnum;
716 				if (build_oid_segment(var) != SNMPERR_SUCCESS) {
717 					snmp_free_varbind(var);
718 					return (NULL);
719 				}
720 				snmp_free_varbind(
721 				    table_info->indexes->next_variable);
722 				table_info->indexes->next_variable = var;
723 				table_info->number_indexes = 2;
724 				DEBUGMSGTL((MODNAME_STR, "nextfe: built fake "
725 				    "index:\n"));
726 				DEBUGMSGVAR((MODNAME_STR, table_info->indexes));
727 				DEBUGMSG((MODNAME_STR, "\n"));
728 				DEBUGMSGVAR((MODNAME_STR,
729 				    table_info->indexes->next_variable));
730 				DEBUGMSG((MODNAME_STR, "\n"));
731 			} else {
732 				if (sunFmProblemTable_nextpr(reginfo,
733 				    table_info) == NULL)
734 					return (NULL);
735 			}
736 			break;
737 		case 0:
738 			if (sunFmProblemTable_nextpr(reginfo, table_info) ==
739 			    NULL)
740 				return (NULL);
741 			break;
742 		}
743 	}
744 }
745 
746 static sunFmFaultEvent_data_t *
747 sunFmFaultEventTable_fe(netsnmp_handler_registration *reginfo,
748     netsnmp_table_request_info *table_info)
749 {
750 	sunFmProblem_data_t	*data;
751 
752 	ASSERT(table_info->number_indexes == 2);
753 
754 	if ((data = sunFmProblemTable_pr(reginfo, table_info)) == NULL)
755 		return (NULL);
756 
757 	return (faultevent_lookup_index_exact(data,
758 	    *(ulong_t *)table_info->indexes->next_variable->val.integer));
759 }
760 
761 static sunFmFaultStatus_data_t
762 sunFmFaultStatusTable_fe(netsnmp_handler_registration *reginfo,
763     netsnmp_table_request_info *table_info)
764 {
765 	sunFmProblem_data_t	*data;
766 
767 	ASSERT(table_info->number_indexes == 2);
768 
769 	if ((data = sunFmProblemTable_pr(reginfo, table_info)) == NULL)
770 		return (NULL);
771 
772 	return (faultstatus_lookup_index_exact(data,
773 	    *(ulong_t *)table_info->indexes->next_variable->val.integer));
774 }
775 
776 /*ARGSUSED*/
777 static void
778 sunFmProblemTable_return(unsigned int reg, void *arg)
779 {
780 	netsnmp_delegated_cache		*cache = (netsnmp_delegated_cache *)arg;
781 	netsnmp_request_info		*request;
782 	netsnmp_agent_request_info	*reqinfo;
783 	netsnmp_handler_registration	*reginfo;
784 	netsnmp_table_request_info	*table_info;
785 	sunFmProblem_data_t		*data;
786 
787 	ASSERT(netsnmp_handler_check_cache(cache) != NULL);
788 
789 	(void) pthread_mutex_lock(&update_lock);
790 	if (update_status != US_QUIET) {
791 		struct timeval			tv;
792 
793 		tv.tv_sec = UPDATE_WAIT_MILLIS / 1000;
794 		tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000;
795 
796 		(void) snmp_alarm_register_hr(tv, 0, sunFmProblemTable_return,
797 		    cache);
798 		(void) pthread_mutex_unlock(&update_lock);
799 		return;
800 	}
801 
802 	request = cache->requests;
803 	reqinfo = cache->reqinfo;
804 	reginfo = cache->reginfo;
805 
806 	table_info = netsnmp_extract_table_info(request);
807 	request->delegated = 0;
808 
809 	ASSERT(table_info->colnum >= SUNFMPROBLEM_COLMIN);
810 	ASSERT(table_info->colnum <= SUNFMPROBLEM_COLMAX);
811 
812 	/*
813 	 * table_info->colnum contains the column number requested.
814 	 * table_info->indexes contains a linked list of snmp variable
815 	 * bindings for the indexes of the table.  Values in the list
816 	 * have been set corresponding to the indexes of the
817 	 * request.  We have other guarantees as well:
818 	 *
819 	 * - The column number is always within range.
820 	 * - If we have no index data, table_info->index_oid_len is 0.
821 	 * - We will never receive requests outside our table nor
822 	 *   those with the first subid anything other than 1 (Entry)
823 	 *   nor those without a column number.  This is true even
824 	 *   for GETNEXT requests.
825 	 */
826 
827 	switch (reqinfo->mode) {
828 	case MODE_GET:
829 		if ((data = sunFmProblemTable_pr(reginfo, table_info)) ==
830 		    NULL) {
831 			netsnmp_free_delegated_cache(cache);
832 			(void) pthread_mutex_unlock(&update_lock);
833 			return;
834 		}
835 		break;
836 	case MODE_GETNEXT:
837 	case MODE_GETBULK:
838 		if ((data = sunFmProblemTable_nextpr(reginfo, table_info)) ==
839 		    NULL) {
840 			netsnmp_free_delegated_cache(cache);
841 			(void) pthread_mutex_unlock(&update_lock);
842 			return;
843 		}
844 		break;
845 	default:
846 		snmp_log(LOG_ERR, MODNAME_STR ": Unsupported request mode %d\n",
847 		    reqinfo->mode);
848 		netsnmp_free_delegated_cache(cache);
849 		(void) pthread_mutex_unlock(&update_lock);
850 		return;
851 	}
852 
853 	switch (table_info->colnum) {
854 	case SUNFMPROBLEM_COL_UUID:
855 	{
856 		netsnmp_table_build_result(reginfo, request, table_info,
857 		    ASN_OCTET_STR, (uchar_t *)data->d_aci_uuid,
858 		    strlen(data->d_aci_uuid));
859 		break;
860 	}
861 	case SUNFMPROBLEM_COL_CODE:
862 	{
863 		netsnmp_table_build_result(reginfo, request, table_info,
864 		    ASN_OCTET_STR, (uchar_t *)data->d_aci_code,
865 		    strlen(data->d_aci_code));
866 		break;
867 	}
868 	case SUNFMPROBLEM_COL_URL:
869 	{
870 		netsnmp_table_build_result(reginfo, request, table_info,
871 		    ASN_OCTET_STR, (uchar_t *)data->d_aci_url,
872 		    strlen(data->d_aci_url));
873 		break;
874 	}
875 	case SUNFMPROBLEM_COL_DIAGENGINE:
876 	{
877 		netsnmp_table_build_result(reginfo, request, table_info,
878 		    ASN_OCTET_STR, (uchar_t *)data->d_diag_engine,
879 		    strlen(data->d_diag_engine));
880 		break;
881 	}
882 	case SUNFMPROBLEM_COL_DIAGTIME:
883 	{
884 		/*
885 		 * The date_n_time function is not Y2038-safe; this may
886 		 * need to be updated when a suitable Y2038-safe Net-SNMP
887 		 * API is available.
888 		 */
889 		size_t	dt_size;
890 		time_t	dt_time = (time_t)data->d_diag_time.tv_sec;
891 		uchar_t	*dt = date_n_time(&dt_time, &dt_size);
892 
893 		netsnmp_table_build_result(reginfo, request, table_info,
894 		    ASN_OCTET_STR, dt, dt_size);
895 		break;
896 	}
897 	case SUNFMPROBLEM_COL_SUSPECTCOUNT:
898 	{
899 		netsnmp_table_build_result(reginfo, request, table_info,
900 		    ASN_UNSIGNED, (uchar_t *)&data->d_nsuspects,
901 		    sizeof (data->d_nsuspects));
902 		break;
903 	}
904 	default:
905 		break;
906 	}
907 
908 	netsnmp_free_delegated_cache(cache);
909 	(void) pthread_mutex_unlock(&update_lock);
910 }
911 
912 static int
913 sunFmProblemTable_handler(netsnmp_mib_handler *handler,
914     netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo,
915     netsnmp_request_info *requests)
916 {
917 	netsnmp_request_info		*request;
918 	struct timeval			tv;
919 
920 	tv.tv_sec = UPDATE_WAIT_MILLIS / 1000;
921 	tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000;
922 
923 	request_update();
924 
925 	for (request = requests; request; request = request->next) {
926 		if (request->processed != 0)
927 			continue;
928 
929 		if (netsnmp_extract_table_info(request) == NULL)
930 			continue;
931 
932 		request->delegated = 1;
933 		(void) snmp_alarm_register_hr(tv, 0,
934 		    sunFmProblemTable_return,
935 		    (void *) netsnmp_create_delegated_cache(handler, reginfo,
936 		    reqinfo, request, NULL));
937 	}
938 
939 	return (SNMP_ERR_NOERROR);
940 }
941 
942 /*ARGSUSED*/
943 static void
944 sunFmFaultEventTable_return(unsigned int reg, void *arg)
945 {
946 	netsnmp_delegated_cache		*cache = (netsnmp_delegated_cache *)arg;
947 	netsnmp_request_info		*request;
948 	netsnmp_agent_request_info	*reqinfo;
949 	netsnmp_handler_registration	*reginfo;
950 	netsnmp_table_request_info	*table_info;
951 	sunFmProblem_data_t		*pdata;
952 	sunFmFaultEvent_data_t		*data;
953 	sunFmFaultStatus_data_t		status;
954 
955 	ASSERT(netsnmp_handler_check_cache(cache) != NULL);
956 
957 	(void) pthread_mutex_lock(&update_lock);
958 	if (update_status != US_QUIET) {
959 		struct timeval			tv;
960 
961 		tv.tv_sec = UPDATE_WAIT_MILLIS / 1000;
962 		tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000;
963 
964 		(void) snmp_alarm_register_hr(tv, 0,
965 		    sunFmFaultEventTable_return, cache);
966 		(void) pthread_mutex_unlock(&update_lock);
967 		return;
968 	}
969 
970 	request = cache->requests;
971 	reqinfo = cache->reqinfo;
972 	reginfo = cache->reginfo;
973 
974 	table_info = netsnmp_extract_table_info(request);
975 	request->delegated = 0;
976 
977 	ASSERT(table_info->colnum >= SUNFMFAULTEVENT_COLMIN);
978 	ASSERT(table_info->colnum <= SUNFMFAULTEVENT_COLMAX);
979 
980 	/*
981 	 * table_info->colnum contains the column number requested.
982 	 * table_info->indexes contains a linked list of snmp variable
983 	 * bindings for the indexes of the table.  Values in the list
984 	 * have been set corresponding to the indexes of the
985 	 * request.  We have other guarantees as well:
986 	 *
987 	 * - The column number is always within range.
988 	 * - If we have no index data, table_info->index_oid_len is 0.
989 	 * - We will never receive requests outside our table nor
990 	 *   those with the first subid anything other than 1 (Entry)
991 	 *   nor those without a column number.  This is true even
992 	 *   for GETNEXT requests.
993 	 */
994 
995 	if (table_info->colnum == SUNFMFAULTEVENT_COL_STATUS) {
996 		switch (reqinfo->mode) {
997 		case MODE_GET:
998 			if ((status = sunFmFaultStatusTable_fe(reginfo,
999 			    table_info)) == NULL) {
1000 				netsnmp_free_delegated_cache(cache);
1001 				(void) pthread_mutex_unlock(&update_lock);
1002 				return;
1003 			}
1004 			break;
1005 		case MODE_GETNEXT:
1006 		case MODE_GETBULK:
1007 			if ((status = sunFmFaultStatusTable_nextfe(reginfo,
1008 			    table_info)) == NULL) {
1009 				netsnmp_free_delegated_cache(cache);
1010 				(void) pthread_mutex_unlock(&update_lock);
1011 				return;
1012 			}
1013 			break;
1014 		default:
1015 			snmp_log(LOG_ERR, MODNAME_STR
1016 			    ": Unsupported request mode %d\n", reqinfo->mode);
1017 			netsnmp_free_delegated_cache(cache);
1018 			(void) pthread_mutex_unlock(&update_lock);
1019 			return;
1020 		}
1021 	} else {
1022 		switch (reqinfo->mode) {
1023 		case MODE_GET:
1024 			if ((data = sunFmFaultEventTable_fe(reginfo,
1025 			    table_info)) == NULL) {
1026 				netsnmp_free_delegated_cache(cache);
1027 				(void) pthread_mutex_unlock(&update_lock);
1028 				return;
1029 			}
1030 			break;
1031 		case MODE_GETNEXT:
1032 		case MODE_GETBULK:
1033 			if ((data = sunFmFaultEventTable_nextfe(reginfo,
1034 			    table_info)) == NULL) {
1035 				netsnmp_free_delegated_cache(cache);
1036 				(void) pthread_mutex_unlock(&update_lock);
1037 				return;
1038 			}
1039 			break;
1040 		default:
1041 			snmp_log(LOG_ERR, MODNAME_STR
1042 			    ": Unsupported request mode %d\n", reqinfo->mode);
1043 			netsnmp_free_delegated_cache(cache);
1044 			(void) pthread_mutex_unlock(&update_lock);
1045 			return;
1046 		}
1047 	}
1048 
1049 	switch (table_info->colnum) {
1050 	case SUNFMFAULTEVENT_COL_PROBLEMUUID:
1051 	{
1052 		if ((pdata = sunFmProblemTable_pr(reginfo, table_info))
1053 		    == NULL) {
1054 			netsnmp_table_build_result(reginfo, request, table_info,
1055 			    ASN_OCTET_STR, NULL, 0);
1056 			break;
1057 		}
1058 		netsnmp_table_build_result(reginfo, request, table_info,
1059 		    ASN_OCTET_STR, (uchar_t *)pdata->d_aci_uuid,
1060 		    strlen(pdata->d_aci_uuid));
1061 		break;
1062 	}
1063 	case SUNFMFAULTEVENT_COL_CLASS:
1064 	{
1065 		char	*class = "-";
1066 
1067 		(void) nvlist_lookup_string(data, FM_CLASS, &class);
1068 		netsnmp_table_build_result(reginfo, request, table_info,
1069 		    ASN_OCTET_STR, (uchar_t *)class, strlen(class));
1070 		break;
1071 	}
1072 	case SUNFMFAULTEVENT_COL_CERTAINTY:
1073 	{
1074 		uint8_t	pct = 0;
1075 		ulong_t	pl;
1076 
1077 		(void) nvlist_lookup_uint8(data, FM_FAULT_CERTAINTY,
1078 		    &pct);
1079 		pl = (ulong_t)pct;
1080 		netsnmp_table_build_result(reginfo, request, table_info,
1081 		    ASN_UNSIGNED, (uchar_t *)&pl, sizeof (pl));
1082 		break;
1083 	}
1084 	case SUNFMFAULTEVENT_COL_ASRU:
1085 	{
1086 		nvlist_t	*asru = NULL;
1087 		char		*fmri, *str;
1088 
1089 		(void) nvlist_lookup_nvlist(data, FM_FAULT_ASRU, &asru);
1090 		if ((str = sunFm_nvl2str(asru)) == NULL)
1091 			fmri = "-";
1092 		else
1093 			fmri = str;
1094 
1095 		netsnmp_table_build_result(reginfo, request, table_info,
1096 		    ASN_OCTET_STR, (uchar_t *)fmri, strlen(fmri));
1097 		free(str);
1098 		break;
1099 	}
1100 	case SUNFMFAULTEVENT_COL_FRU:
1101 	{
1102 		nvlist_t	*fru = NULL;
1103 		char		*fmri, *str;
1104 
1105 		(void) nvlist_lookup_nvlist(data, FM_FAULT_FRU, &fru);
1106 		if ((str = sunFm_nvl2str(fru)) == NULL)
1107 			fmri = "-";
1108 		else
1109 			fmri = str;
1110 
1111 		netsnmp_table_build_result(reginfo, request, table_info,
1112 		    ASN_OCTET_STR, (uchar_t *)fmri, strlen(fmri));
1113 		free(str);
1114 		break;
1115 	}
1116 	case SUNFMFAULTEVENT_COL_RESOURCE:
1117 	{
1118 		nvlist_t	*rsrc = NULL;
1119 		char		*fmri, *str;
1120 
1121 		(void) nvlist_lookup_nvlist(data, FM_FAULT_RESOURCE, &rsrc);
1122 		if ((str = sunFm_nvl2str(rsrc)) == NULL)
1123 			fmri = "-";
1124 		else
1125 			fmri = str;
1126 
1127 		netsnmp_table_build_result(reginfo, request, table_info,
1128 		    ASN_OCTET_STR, (uchar_t *)fmri, strlen(fmri));
1129 		free(str);
1130 		break;
1131 	}
1132 	case SUNFMFAULTEVENT_COL_STATUS:
1133 	{
1134 		ulong_t	pl;
1135 
1136 		if (status & FM_SUSPECT_FAULTY)
1137 			pl = SUNFMFAULTEVENT_STATE_FAULTY;
1138 		else if (status & FM_SUSPECT_NOT_PRESENT)
1139 			pl = SUNFMFAULTEVENT_STATE_REMOVED;
1140 		else if (status & FM_SUSPECT_REPLACED)
1141 			pl = SUNFMFAULTEVENT_STATE_REPLACED;
1142 		else if (status & FM_SUSPECT_REPAIRED)
1143 			pl = SUNFMFAULTEVENT_STATE_REPAIRED;
1144 		else if (status & FM_SUSPECT_ACQUITTED)
1145 			pl = SUNFMFAULTEVENT_STATE_ACQUITTED;
1146 		netsnmp_table_build_result(reginfo, request, table_info,
1147 		    ASN_UNSIGNED, (uchar_t *)&pl, sizeof (pl));
1148 		break;
1149 	}
1150 	case SUNFMFAULTEVENT_COL_LOCATION:
1151 	{
1152 		char	*location = "-";
1153 
1154 		(void) nvlist_lookup_string(data, FM_FAULT_LOCATION, &location);
1155 		netsnmp_table_build_result(reginfo, request, table_info,
1156 		    ASN_OCTET_STR, (uchar_t *)location, strlen(location));
1157 		break;
1158 	}
1159 	default:
1160 		break;
1161 	}
1162 
1163 	netsnmp_free_delegated_cache(cache);
1164 	(void) pthread_mutex_unlock(&update_lock);
1165 }
1166 
1167 static int
1168 sunFmFaultEventTable_handler(netsnmp_mib_handler *handler,
1169     netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo,
1170     netsnmp_request_info *requests)
1171 {
1172 	netsnmp_request_info		*request;
1173 	struct timeval			tv;
1174 
1175 	tv.tv_sec = UPDATE_WAIT_MILLIS / 1000;
1176 	tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000;
1177 
1178 	request_update();
1179 
1180 	for (request = requests; request; request = request->next) {
1181 		if (request->processed != 0)
1182 			continue;
1183 
1184 		if (netsnmp_extract_table_info(request) == NULL)
1185 			continue;
1186 
1187 		request->delegated = 1;
1188 		(void) snmp_alarm_register_hr(tv, 0,
1189 		    sunFmFaultEventTable_return,
1190 		    (void *) netsnmp_create_delegated_cache(handler, reginfo,
1191 		    reqinfo, request, NULL));
1192 	}
1193 
1194 	return (SNMP_ERR_NOERROR);
1195 }
1196