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