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 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * Copyright 2019 Nexenta Systems, Inc.
29 */
30
31 #include <sys/fm/protocol.h>
32
33 #include <fm/fmd_adm.h>
34 #include <fm/fmd_snmp.h>
35 #include <fm/libfmevent.h>
36 #include <fm/libtopo.h>
37
38 #include <net-snmp/net-snmp-config.h>
39 #include <net-snmp/net-snmp-includes.h>
40 #include <net-snmp/agent/net-snmp-agent-includes.h>
41
42 #include <alloca.h>
43 #include <errno.h>
44 #include <libnvpair.h>
45 #include <libuutil.h>
46 #include <locale.h>
47 #include <netdb.h>
48 #include <pthread.h>
49 #include <stddef.h>
50
51 #include "sunFM_impl.h"
52 #include "problem.h"
53
54 /*
55 * We assume that the number of suspect fault events associated with a
56 * particular case will generally be sufficiently small that the overhead
57 * associated with indexing them in a tree would exceed the gain from
58 * not traversing the fault list for each request.
59 */
60 static uu_avl_pool_t *problem_uuid_avl_pool = NULL;
61 static uu_avl_t *problem_uuid_avl = NULL;
62
63 #define VALID_AVL_STATE (problem_uuid_avl_pool != NULL && \
64 problem_uuid_avl != NULL)
65
66 static int valid_stamp;
67 static pthread_mutex_t update_lock;
68 static pthread_cond_t update_cv;
69 static fmev_shdl_t evhdl;
70
71 static Netsnmp_Node_Handler sunFmProblemTable_handler;
72 static Netsnmp_Node_Handler sunFmFaultEventTable_handler;
73
74 static char *
75 nvl2fmri(nvlist_t *nvl)
76 {
77 topo_hdl_t *thp;
78 int topoerr;
79 char *fmri, *ret = NULL;
80
81 thp = topo_open(TOPO_VERSION, NULL, &topoerr);
82 if (thp == NULL)
83 return (NULL);
84
85 if (topo_fmri_nvl2str(thp, nvl, &fmri, &topoerr) == 0) {
86 ret = strdup(fmri);
87 topo_hdl_strfree(thp, fmri);
88 }
89
90 topo_close(thp);
91 return (ret);
92 }
93
94 static sunFmProblem_data_t *
95 problem_key_build(const char *uuid)
96 {
97 static sunFmProblem_data_t key;
98
99 key.d_aci_uuid = uuid;
100
101 return (&key);
102 }
103
104 static sunFmProblem_data_t *
105 problem_lookup_uuid_exact(const char *uuid)
106 {
107 sunFmProblem_data_t *key, *data;
108
109 key = problem_key_build(uuid);
110
111 DEBUGMSGTL((MODNAME_STR, "lookup_exact for uuid %s\n", uuid));
112 data = uu_avl_find(problem_uuid_avl, key, NULL, NULL);
113
114 return (data);
115 }
116
117 static sunFmProblem_data_t *
118 problem_lookup_uuid_next(const char *uuid)
119 {
120 sunFmProblem_data_t *key, *data;
121 uu_avl_index_t idx;
122
123 key = problem_key_build(uuid);
124
125 DEBUGMSGTL((MODNAME_STR, "lookup_next for uuid %s\n", uuid));
126 (void) uu_avl_find(problem_uuid_avl, key, NULL, &idx);
127
128 data = uu_avl_nearest_next(problem_uuid_avl, idx);
129
130 DEBUGMSGTL((MODNAME_STR, "lookup_next: entry is %p\n", data));
131
132 return (data);
133 }
134
135 static sunFmFaultEvent_data_t *
136 faultevent_lookup_index_exact(sunFmProblem_data_t *data, ulong_t index)
137 {
138 if (index > data->d_nsuspects)
139 return (NULL);
140
141 if (data->d_suspects == NULL)
142 return (NULL);
143
144 return (data->d_suspects[index - 1]);
145 }
146
147 static sunFmFaultStatus_data_t
148 faultstatus_lookup_index_exact(sunFmProblem_data_t *data, ulong_t index)
149 {
150 if (index > data->d_nsuspects)
151 return (0);
152
153 if (data->d_statuses == NULL)
154 return (0);
155
156 if (data->d_valid != valid_stamp)
157 return (0);
158
159 return (data->d_statuses[index - 1]);
160 }
161
162 #define FM_SUSPECT_SKIP \
163 (FM_SUSPECT_NOT_PRESENT | FM_SUSPECT_REPAIRED | \
164 FM_SUSPECT_REPLACED | FM_SUSPECT_ACQUITTED)
165
166 /*ARGSUSED*/
167 static int
168 problem_update_one(const fmd_adm_caseinfo_t *acp, void *arg)
169 {
170 sunFmProblem_data_t *data;
171 nvlist_t *nvl;
172 int64_t *diag_time;
173 uint_t nelem;
174 int err;
175 int i;
176 int cr = 0;
177 uint8_t *statuses;
178
179 ASSERT(acp->aci_uuid != NULL);
180
181 if ((data = problem_lookup_uuid_exact(acp->aci_uuid)) == NULL) {
182 uu_avl_index_t idx;
183 nvlist_t **fnvl;
184 nvlist_t *snvl;
185 uint_t nnvl;
186
187 /* Lookup statuses early so we could skip resolved problems */
188 if (nvlist_lookup_uint8_array(acp->aci_event,
189 FM_SUSPECT_FAULT_STATUS, &statuses, &nelem) != 0)
190 return (0);
191
192 for (i = 0; i < nelem; i++) {
193 if (statuses[i] & FM_SUSPECT_SKIP)
194 cr++;
195 }
196 if (cr == nelem) {
197 DEBUGMSGTL((MODNAME_STR,
198 "problem %s is resolved, skipping\n",
199 acp->aci_uuid));
200 return (0);
201 }
202
203 DEBUGMSGTL((MODNAME_STR, "found new problem %s\n",
204 acp->aci_uuid));
205 if ((data = SNMP_MALLOC_TYPEDEF(sunFmProblem_data_t)) == NULL) {
206 (void) snmp_log(LOG_ERR, MODNAME_STR
207 ": out of memory for new problem data\n");
208 return (0);
209 }
210 if ((err = nvlist_dup(acp->aci_event, &data->d_aci_event, 0))
211 != 0) {
212 (void) snmp_log(LOG_ERR, MODNAME_STR
213 ": problem data setup failed: %s\n", strerror(err));
214 SNMP_FREE(data);
215 return (0);
216 }
217
218 data->d_aci_uuid = data->d_aci_code = data->d_aci_type =
219 data->d_aci_severity = data->d_aci_url =
220 data->d_aci_desc = "-";
221 (void) nvlist_lookup_string(data->d_aci_event, FM_SUSPECT_UUID,
222 (char **)&data->d_aci_uuid);
223 (void) nvlist_lookup_string(data->d_aci_event,
224 FM_SUSPECT_DIAG_CODE, (char **)&data->d_aci_code);
225 (void) nvlist_lookup_string(data->d_aci_event,
226 FM_SUSPECT_TYPE, (char **)&data->d_aci_type);
227 (void) nvlist_lookup_string(data->d_aci_event,
228 FM_SUSPECT_SEVERITY, (char **)&data->d_aci_severity);
229 if (acp->aci_url != NULL)
230 data->d_aci_url = strdup(acp->aci_url);
231 (void) nvlist_lookup_string(data->d_aci_event,
232 FM_SUSPECT_DESC, (char **)&data->d_aci_desc);
233
234 /*
235 * NOTE: This should match the logic in libfmnotify.
236 *
237 * Extract the fault-list, and use the following order
238 * of nested nvlists from its first element to make up FMRI:
239 * - FRU
240 * - ASRU
241 * - resource
242 */
243 if (nvlist_lookup_nvlist_array(data->d_aci_event,
244 FM_SUSPECT_FAULT_LIST, &fnvl, &nnvl) == 0 && nnvl == 1 &&
245 (nvlist_lookup_nvlist(fnvl[0], FM_FAULT_FRU, &snvl) == 0 ||
246 nvlist_lookup_nvlist(fnvl[0], FM_FAULT_ASRU, &snvl) == 0 ||
247 nvlist_lookup_nvlist(fnvl[0], FM_FAULT_RESOURCE,
248 &snvl) == 0))
249 data->d_aci_fmri = nvl2fmri(snvl);
250 if (data->d_aci_fmri == NULL)
251 data->d_aci_fmri = "-";
252
253 if (nvlist_lookup_nvlist(data->d_aci_event, FM_SUSPECT_DE,
254 &nvl) == 0)
255 data->d_diag_engine = nvl2fmri(nvl);
256 if (data->d_diag_engine == NULL)
257 data->d_diag_engine = "-";
258
259 if (nvlist_lookup_int64_array(data->d_aci_event,
260 FM_SUSPECT_DIAG_TIME, &diag_time, &nelem) == 0 &&
261 nelem >= 2) {
262 data->d_diag_time.tv_sec = (long)diag_time[0];
263 data->d_diag_time.tv_usec = (long)diag_time[1];
264 }
265
266 (void) nvlist_lookup_uint32(data->d_aci_event,
267 FM_SUSPECT_FAULT_SZ, &data->d_nsuspects);
268 (void) nvlist_lookup_nvlist_array(data->d_aci_event,
269 FM_SUSPECT_FAULT_LIST, &data->d_suspects, &nelem);
270 (void) nvlist_lookup_uint8_array(data->d_aci_event,
271 FM_SUSPECT_FAULT_STATUS, &data->d_statuses, &nelem);
272
273 uu_avl_node_init(data, &data->d_uuid_avl,
274 problem_uuid_avl_pool);
275 (void) uu_avl_find(problem_uuid_avl, data, NULL, &idx);
276 uu_avl_insert(problem_uuid_avl, data, idx);
277
278 data->d_valid = valid_stamp;
279
280 DEBUGMSGTL((MODNAME_STR, "completed new problem %s@%p\n",
281 data->d_aci_uuid, data));
282 } else {
283 if (nvlist_lookup_uint8_array(acp->aci_event,
284 FM_SUSPECT_FAULT_STATUS, &statuses, &nelem) != 0)
285 return (0);
286
287 if (nelem != data->d_nsuspects) {
288 DEBUGMSGTL((MODNAME_STR,
289 "problem %s is malformed; deleting\n",
290 data->d_aci_uuid));
291 goto delete;
292 }
293
294 for (i = 0; i < nelem; i++) {
295 if (statuses[i] & FM_SUSPECT_SKIP)
296 cr++;
297 data->d_statuses[i] = statuses[i];
298 }
299 if (cr == nelem) {
300 DEBUGMSGTL((MODNAME_STR,
301 "problem %s is now resolved; deleting\n",
302 data->d_aci_uuid));
303 goto delete;
304 } else {
305 data->d_valid = valid_stamp;
306 }
307 }
308
309 return (0);
310
311 delete:
312 uu_avl_remove(problem_uuid_avl, data);
313 uu_avl_node_fini(data, &data->d_uuid_avl,
314 problem_uuid_avl_pool);
315 nvlist_free(data->d_aci_event);
316 SNMP_FREE(data);
317 return (0);
318 }
319
320 /*ARGSUSED*/
321 static void *
322 update_thread(void *arg)
323 {
324 fmd_adm_t *adm;
325 static struct timespec tv;
326
327 /* Do a 1-minute checks for changes */
328 tv.tv_sec = 60;
329 tv.tv_nsec = 0;
330
331 for (;;) {
332 ASSERT(VALID_AVL_STATE);
333
334 (void) pthread_mutex_lock(&update_lock);
335 /* We don't care if we were awaken explicitly or by timeout */
336 (void) pthread_cond_reltimedwait_np(&update_cv, &update_lock,
337 &tv);
338 if ((adm = fmd_adm_open(NULL, FMD_ADM_PROGRAM,
339 FMD_ADM_VERSION)) == NULL) {
340 (void) pthread_mutex_unlock(&update_lock);
341 (void) snmp_log(LOG_ERR, MODNAME_STR
342 ": communication with fmd failed: %s\n",
343 strerror(errno));
344 continue;
345 }
346
347 valid_stamp++;
348
349 DEBUGMSGTL((MODNAME_STR, "case iteration started\n"));
350 if (fmd_adm_case_iter(adm, SNMP_URL_MSG, problem_update_one,
351 NULL) != 0) {
352 (void) pthread_mutex_unlock(&update_lock);
353 (void) snmp_log(LOG_ERR, MODNAME_STR
354 ": fmd case information update failed: %s\n",
355 fmd_adm_errmsg(adm));
356 fmd_adm_close(adm);
357 continue;
358 }
359
360 fmd_adm_close(adm);
361 (void) pthread_mutex_unlock(&update_lock);
362
363 DEBUGMSGTL((MODNAME_STR, "case iteration completed\n"));
364 }
365
366 /*NOTREACHED*/
367 return (NULL);
368 }
369
370 /*ARGSUSED*/
371 static void
372 event_cb(fmev_t ev, const char *class, nvlist_t *nvl, void *arg)
373 {
374 (void) pthread_mutex_lock(&update_lock);
375 (void) pthread_cond_signal(&update_cv);
376 (void) pthread_mutex_unlock(&update_lock);
377 }
378
379 /*ARGSUSED*/
380 static int
381 problem_compare_uuid(const void *l, const void *r, void *private)
382 {
383 sunFmProblem_data_t *l_data = (sunFmProblem_data_t *)l;
384 sunFmProblem_data_t *r_data = (sunFmProblem_data_t *)r;
385
386 ASSERT(l_data != NULL && r_data != NULL);
387
388 return (strcmp(l_data->d_aci_uuid, r_data->d_aci_uuid));
389 }
390
391 /* ARGSUSED */
392 void *
393 pid_thread(void *arg)
394 {
395 pid_t pid = getpid();
396 int wait = 0;
397
398 /*
399 * Workaround the forking madness in net-snmp -- we need to
400 * subscribe from the *forked* process so that event notifications
401 * get our PID correctly.
402 *
403 * We also limit the wait to arbitrary long time of 10 seconds so that
404 * we subscribe to event notifications when running with -f (don't fork)
405 * specified.
406 */
407 for (;;) {
408 if (getpid() != pid || wait == 10) {
409 /* Subscribe to fault event notifications */
410 evhdl = fmev_shdl_init(LIBFMEVENT_VERSION_2, NULL, NULL,
411 NULL);
412 (void) fmev_shdl_subscribe(evhdl, "list.*", event_cb,
413 NULL);
414 break;
415 }
416 wait++;
417 (void) sleep(1);
418 }
419
420 return (NULL);
421 }
422
423 int
424 sunFmProblemTable_init(void)
425 {
426 static oid sunFmFaultEventTable_oid[] = { SUNFMFAULTEVENTTABLE_OID };
427 netsnmp_table_registration_info *ftinfo = NULL;
428 netsnmp_handler_registration *fhandler = NULL;
429 static oid sunFmProblemTable_oid[] = { SUNFMPROBLEMTABLE_OID };
430 netsnmp_table_registration_info *ptinfo = NULL;
431 netsnmp_handler_registration *phandler = NULL;
432 pthread_t ptid;
433 pthread_t utid;
434 int ret = MIB_REGISTRATION_FAILED;
435
436 /* Create fault event table and handler */
437 if ((ftinfo =
438 SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info)) == NULL ||
439 netsnmp_table_helper_add_index(ftinfo, ASN_OCTET_STR) == NULL ||
440 netsnmp_table_helper_add_index(ftinfo, ASN_UNSIGNED) == NULL ||
441 (fhandler =
442 netsnmp_create_handler_registration("sunFmFaultEventTable",
443 sunFmFaultEventTable_handler, sunFmFaultEventTable_oid,
444 OID_LENGTH(sunFmFaultEventTable_oid), HANDLER_CAN_RONLY)) == NULL)
445 goto fail;
446
447 ftinfo->min_column = SUNFMFAULTEVENT_COLMIN;
448 ftinfo->max_column = SUNFMFAULTEVENT_COLMAX;
449
450 /* Register fault event handler */
451 if ((ret = netsnmp_register_table(fhandler, ftinfo)) !=
452 MIB_REGISTERED_OK)
453 goto fail;
454
455 /* Create problem table, data pool, and handler */
456 if ((problem_uuid_avl_pool = uu_avl_pool_create("problem_uuid",
457 sizeof (sunFmProblem_data_t), offsetof(sunFmProblem_data_t,
458 d_uuid_avl), problem_compare_uuid, UU_AVL_DEBUG)) == NULL ||
459 (problem_uuid_avl = uu_avl_create(problem_uuid_avl_pool, NULL,
460 UU_AVL_DEBUG)) == NULL ||
461 (ptinfo =
462 SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info)) == NULL ||
463 netsnmp_table_helper_add_index(ptinfo, ASN_OCTET_STR) == NULL ||
464 (phandler =
465 netsnmp_create_handler_registration("sunFmProblemTable",
466 sunFmProblemTable_handler, sunFmProblemTable_oid,
467 OID_LENGTH(sunFmProblemTable_oid), HANDLER_CAN_RONLY)) == NULL)
468 goto fail;
469
470 ptinfo->min_column = SUNFMPROBLEM_COLMIN;
471 ptinfo->max_column = SUNFMPROBLEM_COLMAX;
472
473 /* Register problem handler */
474 if ((ret = netsnmp_register_table(phandler, ptinfo)) !=
475 MIB_REGISTERED_OK)
476 goto fail;
477
478 /* Create PID change waiter thread */
479 if (pthread_create(&ptid, NULL, pid_thread, 0) != 0) {
480 (void) snmp_log(LOG_ERR, MODNAME_STR
481 ": failed to create pid thread: %s\n", strerror(ret));
482 goto fail;
483 }
484
485 /* Create update thread */
486 if ((ret = pthread_mutex_init(&update_lock, NULL)) != 0 ||
487 (ret = pthread_cond_init(&update_cv, NULL)) != 0 ||
488 (ret = pthread_create(&utid, NULL, update_thread, 0)) != 0) {
489 (void) snmp_log(LOG_ERR, MODNAME_STR
490 ": failed to create update thread: %s\n", strerror(ret));
491 goto fail;
492 }
493
494 return (MIB_REGISTERED_OK);
495
496 fail:
497 (void) pthread_mutex_destroy(&update_lock);
498 if (problem_uuid_avl != NULL)
499 uu_avl_destroy(problem_uuid_avl);
500 if (problem_uuid_avl_pool != NULL)
501 uu_avl_pool_destroy(problem_uuid_avl_pool);
502 if (ftinfo->indexes != NULL)
503 snmp_free_varbind(ftinfo->indexes);
504 if (ftinfo != NULL)
505 SNMP_FREE(ftinfo);
506 if (ptinfo->indexes != NULL)
507 snmp_free_varbind(ptinfo->indexes);
508 if (ptinfo != NULL)
509 SNMP_FREE(ptinfo);
510 if (fhandler != NULL)
511 SNMP_FREE(fhandler);
512 if (phandler != NULL)
513 SNMP_FREE(phandler);
514
515 return (ret);
516 }
517
518 /*
519 * Returns the problem data for the problem whose uuid is next according
520 * to ASN.1 lexical ordering after the request in table_info. Indexes are
521 * updated to reflect the OID of the value being returned. This allows
522 * us to implement GETNEXT.
523 */
524 static sunFmProblem_data_t *
525 sunFmProblemTable_nextpr(netsnmp_handler_registration *reginfo,
526 netsnmp_table_request_info *table_info)
527 {
528 sunFmProblem_data_t *data;
529 char *uuid = "";
530
531 if (table_info->number_indexes < 1) {
532 oid tmpoid[MAX_OID_LEN];
533
534 DEBUGMSGTL((MODNAME_STR, "nextpr: no indexes given\n"));
535
536 snmp_free_varbind(table_info->indexes);
537 table_info->indexes =
538 SNMP_MALLOC_TYPEDEF(netsnmp_variable_list);
539 (void) snmp_set_var_typed_value(table_info->indexes,
540 ASN_OCTET_STR, (const uchar_t *)uuid, 0);
541 (void) memcpy(tmpoid, reginfo->rootoid,
542 reginfo->rootoid_len * sizeof (oid));
543 tmpoid[reginfo->rootoid_len] = 1;
544 tmpoid[reginfo->rootoid_len + 1] = table_info->colnum;
545 if (build_oid_segment(table_info->indexes) != SNMPERR_SUCCESS) {
546 snmp_free_varbind(table_info->indexes);
547 return (NULL);
548 }
549 table_info->number_indexes = 1;
550 table_info->index_oid_len = table_info->indexes->name_length;
551 (void) memcpy(table_info->index_oid, table_info->indexes->name,
552 table_info->indexes->name_length);
553
554 DEBUGMSGTL((MODNAME_STR, "nextpr: built fake index: "));
555 DEBUGMSGVAR((MODNAME_STR, table_info->indexes));
556 DEBUGMSG((MODNAME_STR, "\n"));
557 } else {
558 /*
559 * Construct the next possible UUID to look for. We can
560 * simply increment the least significant byte of the last
561 * UUID because (a) that preserves SNMP lex order and (b)
562 * the characters that may appear in a UUID do not include
563 * 127 nor 255.
564 */
565 uuid = alloca(table_info->indexes->val_len + 1);
566 (void) strlcpy(uuid,
567 (const char *)table_info->indexes->val.string,
568 table_info->indexes->val_len + 1);
569 ++uuid[table_info->indexes->val_len - 1];
570
571 DEBUGMSGTL((MODNAME_STR, "nextpr: received index:\n"));
572 DEBUGMSGVAR((MODNAME_STR, table_info->indexes));
573 DEBUGMSG((MODNAME_STR, "\n"));
574 }
575
576 if ((data = problem_lookup_uuid_next(uuid)) == NULL) {
577 DEBUGMSGTL((MODNAME_STR, "nextpr: next match not found for "
578 "%s; trying next column\n", uuid));
579 if (table_info->colnum >=
580 netsnmp_find_table_registration_info(reginfo)->max_column) {
581 snmp_free_varbind(table_info->indexes);
582 table_info->indexes = NULL;
583 table_info->number_indexes = 0;
584 DEBUGMSGTL((MODNAME_STR, "nextpr: out of columns\n"));
585 return (NULL);
586 }
587 table_info->colnum++;
588 DEBUGMSGTL((MODNAME_STR, "nextpr: search for col %u empty "
589 "uuid\n", table_info->colnum));
590
591 if ((data = problem_lookup_uuid_next("")) == NULL) {
592 DEBUGMSGTL((MODNAME_STR, "nextpr: next match not found "
593 "for empty uuid; stopping\n"));
594 snmp_free_varbind(table_info->indexes);
595 table_info->indexes = NULL;
596 table_info->number_indexes = 0;
597 return (NULL);
598 }
599 }
600
601 (void) snmp_set_var_typed_value(table_info->indexes, ASN_OCTET_STR,
602 (uchar_t *)data->d_aci_uuid, strlen(data->d_aci_uuid));
603 table_info->number_indexes = 1;
604
605 DEBUGMSGTL((MODNAME_STR, "matching data is %s@%p\n", data->d_aci_uuid,
606 data));
607
608 return (data);
609 }
610
611 /*
612 * Returns the problem data corresponding to the request in table_info.
613 * All request parameters are unmodified.
614 */
615 /*ARGSUSED*/
616 static sunFmProblem_data_t *
617 sunFmProblemTable_pr(netsnmp_handler_registration *reginfo,
618 netsnmp_table_request_info *table_info)
619 {
620 char *uuid;
621
622 ASSERT(table_info->number_indexes >= 1);
623
624 uuid = alloca(table_info->indexes->val_len + 1);
625 (void) strlcpy(uuid, (const char *)table_info->indexes->val.string,
626 table_info->indexes->val_len + 1);
627
628 return (problem_lookup_uuid_exact(uuid));
629 }
630
631 /*
632 * Returns the ASN.1 lexicographically first fault event after the one
633 * identified by table_info. Indexes are updated to reflect the OID
634 * of the data returned. This allows us to implement GETNEXT.
635 */
636 static sunFmFaultEvent_data_t *
637 sunFmFaultEventTable_nextfe(netsnmp_handler_registration *reginfo,
638 netsnmp_table_request_info *table_info, sunFmFaultStatus_data_t *statusp)
639 {
640 sunFmProblem_data_t *data;
641 sunFmFaultEvent_data_t *rv;
642 netsnmp_variable_list *var;
643 ulong_t index;
644
645 for (;;) {
646 switch (table_info->number_indexes) {
647 case 2:
648 default:
649 DEBUGMSGTL((MODNAME_STR, "nextfe: 2 indices:\n"));
650 DEBUGMSGVAR((MODNAME_STR, table_info->indexes));
651 DEBUGMSG((MODNAME_STR, "\n"));
652 DEBUGMSGVAR((MODNAME_STR,
653 table_info->indexes->next_variable));
654 DEBUGMSG((MODNAME_STR, "\n"));
655 index = *(ulong_t *)
656 table_info->indexes->next_variable->val.integer + 1;
657
658 if ((data = sunFmProblemTable_pr(reginfo,
659 table_info)) != NULL &&
660 (*statusp = faultstatus_lookup_index_exact(data,
661 index)) != 0 &&
662 (rv = faultevent_lookup_index_exact(data, index)) !=
663 NULL) {
664 (void) snmp_set_var_typed_value(
665 table_info->indexes->next_variable,
666 ASN_UNSIGNED, (uchar_t *)&index,
667 sizeof (index));
668 return (rv);
669 }
670
671 if (sunFmProblemTable_nextpr(reginfo, table_info) ==
672 NULL)
673 return (NULL);
674 break;
675 case 1:
676 if ((data = sunFmProblemTable_pr(reginfo,
677 table_info)) != NULL) {
678 oid tmpoid[MAX_OID_LEN];
679 index = 0;
680
681 DEBUGMSGTL((MODNAME_STR, "nextfe: 1 index:\n"));
682 DEBUGMSGVAR((MODNAME_STR, table_info->indexes));
683 DEBUGMSG((MODNAME_STR, "\n"));
684 var =
685 SNMP_MALLOC_TYPEDEF(netsnmp_variable_list);
686 (void) snmp_set_var_typed_value(var,
687 ASN_UNSIGNED, (uchar_t *)&index,
688 sizeof (index));
689 (void) memcpy(tmpoid, reginfo->rootoid,
690 reginfo->rootoid_len * sizeof (oid));
691 tmpoid[reginfo->rootoid_len] = 1;
692 tmpoid[reginfo->rootoid_len + 1] =
693 table_info->colnum;
694 if (build_oid_segment(var) != SNMPERR_SUCCESS) {
695 snmp_free_varbind(var);
696 return (NULL);
697 }
698 snmp_free_varbind(
699 table_info->indexes->next_variable);
700 table_info->indexes->next_variable = var;
701 table_info->number_indexes = 2;
702 DEBUGMSGTL((MODNAME_STR,
703 "nextfe: built fake index: "));
704 DEBUGMSGVAR((MODNAME_STR, table_info->indexes));
705 DEBUGMSG((MODNAME_STR, "\n"));
706 DEBUGMSGVAR((MODNAME_STR,
707 table_info->indexes->next_variable));
708 DEBUGMSG((MODNAME_STR, "\n"));
709 } else {
710 if (sunFmProblemTable_nextpr(reginfo,
711 table_info) == NULL)
712 return (NULL);
713 }
714 break;
715 case 0:
716 if (sunFmProblemTable_nextpr(reginfo, table_info) ==
717 NULL)
718 return (NULL);
719 break;
720 }
721 }
722 }
723
724 static sunFmFaultEvent_data_t *
725 sunFmFaultEventTable_fe(netsnmp_handler_registration *reginfo,
726 netsnmp_table_request_info *table_info, sunFmFaultStatus_data_t *statusp)
727 {
728 sunFmProblem_data_t *data;
729
730 ASSERT(table_info->number_indexes == 2);
731
732 if ((data = sunFmProblemTable_pr(reginfo, table_info)) == NULL)
733 return (NULL);
734
735 *statusp = faultstatus_lookup_index_exact(data,
736 *(ulong_t *)table_info->indexes->next_variable->val.integer);
737 if (*statusp == 0)
738 return (NULL);
739 return (faultevent_lookup_index_exact(data,
740 *(ulong_t *)table_info->indexes->next_variable->val.integer));
741 }
742
743 /*ARGSUSED*/
744 static int
745 sunFmProblemTable_handler(netsnmp_mib_handler *handler,
746 netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo,
747 netsnmp_request_info *request)
748 {
749 netsnmp_table_request_info *table_info;
750 sunFmProblem_data_t *data;
751 int ret = SNMP_ERR_NOERROR;
752
753 /*
754 * We don't support MODE_GETBULK directly, so all bulk requests should
755 * come through bulk_to_next helper. Make sure it stays that way.
756 */
757 ASSERT(reqinfo->mode == MODE_GET || reqinfo->mode == MODE_GETNEXT);
758
759 (void) pthread_mutex_lock(&update_lock);
760
761 for (; request != NULL; request = request->next) {
762 table_info = netsnmp_extract_table_info(request);
763 if (table_info == NULL)
764 continue;
765
766 /*
767 * table_info->colnum contains the column number requested.
768 * table_info->indexes contains a linked list of snmp variable
769 * bindings for the indexes of the table. Values in the list
770 * have been set corresponding to the indexes of the
771 * request. We have other guarantees as well:
772 *
773 * - The column number is always within range.
774 * - If we have no index data, table_info->index_oid_len is 0.
775 * - We will never receive requests outside our table nor
776 * those with the first subid anything other than 1 (Entry)
777 * nor those without a column number. This is true even
778 * for GETNEXT requests.
779 */
780 switch (reqinfo->mode) {
781 case MODE_GET:
782 data = sunFmProblemTable_pr(reginfo, table_info);
783 if (data == NULL)
784 goto out;
785 break;
786 case MODE_GETNEXT:
787 data = sunFmProblemTable_nextpr(reginfo, table_info);
788 if (data == NULL)
789 goto out;
790 break;
791 default:
792 (void) snmp_log(LOG_ERR, MODNAME_STR
793 ": unsupported request mode: %d\n", reqinfo->mode);
794 ret = SNMP_ERR_GENERR;
795 goto out;
796 }
797
798 switch (table_info->colnum) {
799 case SUNFMPROBLEM_COL_UUID:
800 (void) netsnmp_table_build_result(reginfo, request,
801 table_info, ASN_OCTET_STR,
802 (uchar_t *)data->d_aci_uuid,
803 strlen(data->d_aci_uuid));
804 break;
805 case SUNFMPROBLEM_COL_HOSTNAME: {
806 char hostname[MAXHOSTNAMELEN+1];
807
808 (void) gethostname(hostname, sizeof (hostname) - 1);
809 (void) netsnmp_table_build_result(reginfo, request,
810 table_info, ASN_OCTET_STR, (uchar_t *)hostname,
811 strlen(hostname));
812 break;
813 }
814 case SUNFMPROBLEM_COL_CODE:
815 (void) netsnmp_table_build_result(reginfo, request,
816 table_info, ASN_OCTET_STR,
817 (uchar_t *)data->d_aci_code,
818 strlen(data->d_aci_code));
819 break;
820 case SUNFMPROBLEM_COL_TYPE:
821 (void) netsnmp_table_build_result(reginfo, request,
822 table_info, ASN_OCTET_STR,
823 (uchar_t *)data->d_aci_type,
824 strlen(data->d_aci_type));
825 break;
826 case SUNFMPROBLEM_COL_SEVERITY:
827 (void) netsnmp_table_build_result(reginfo, request,
828 table_info, ASN_OCTET_STR,
829 (uchar_t *)data->d_aci_severity,
830 strlen(data->d_aci_severity));
831 break;
832 case SUNFMPROBLEM_COL_URL:
833 (void) netsnmp_table_build_result(reginfo, request,
834 table_info, ASN_OCTET_STR,
835 (uchar_t *)data->d_aci_url,
836 strlen(data->d_aci_url));
837 break;
838 case SUNFMPROBLEM_COL_DESC:
839 (void) netsnmp_table_build_result(reginfo, request,
840 table_info, ASN_OCTET_STR,
841 (uchar_t *)data->d_aci_desc,
842 strlen(data->d_aci_desc));
843 break;
844 case SUNFMPROBLEM_COL_FMRI:
845 (void) netsnmp_table_build_result(reginfo, request,
846 table_info, ASN_OCTET_STR,
847 (uchar_t *)data->d_aci_fmri,
848 strlen(data->d_aci_fmri));
849 break;
850 case SUNFMPROBLEM_COL_DIAGENGINE:
851 (void) netsnmp_table_build_result(reginfo, request,
852 table_info, ASN_OCTET_STR,
853 (uchar_t *)data->d_diag_engine,
854 strlen(data->d_diag_engine));
855 break;
856 case SUNFMPROBLEM_COL_DIAGTIME: {
857 /*
858 * The date_n_time function is not Y2038-safe; this may
859 * need to be updated when a suitable Y2038-safe
860 * Net-SNMP API is available.
861 */
862 size_t dt_size;
863 time_t dt_time = (time_t)data->d_diag_time.tv_sec;
864 uchar_t *dt = date_n_time(&dt_time, &dt_size);
865
866 (void) netsnmp_table_build_result(reginfo, request,
867 table_info, ASN_OCTET_STR, dt, dt_size);
868 break;
869 }
870 case SUNFMPROBLEM_COL_SUSPECTCOUNT:
871 (void) netsnmp_table_build_result(reginfo, request,
872 table_info, ASN_UNSIGNED,
873 (uchar_t *)&data->d_nsuspects,
874 sizeof (data->d_nsuspects));
875 break;
876 default:
877 (void) netsnmp_table_build_result(reginfo, request,
878 table_info, ASN_OCTET_STR, (uchar_t *)"-",
879 strlen("-"));
880 break;
881 }
882 }
883
884 out:
885 (void) pthread_mutex_unlock(&update_lock);
886 return (ret);
887 }
888
889 /*ARGSUSED*/
890 static int
891 sunFmFaultEventTable_handler(netsnmp_mib_handler *handler,
892 netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo,
893 netsnmp_request_info *request)
894 {
895 netsnmp_table_request_info *table_info;
896 sunFmFaultEvent_data_t *data;
897 sunFmFaultStatus_data_t status;
898 sunFmProblem_data_t *pdata;
899 int ret = SNMP_ERR_NOERROR;
900
901 /*
902 * We don't support MODE_GETBULK directly, so all bulk requests should
903 * come through bulk_to_next helper. Make sure it stays that way.
904 */
905 ASSERT(reqinfo->mode == MODE_GET || reqinfo->mode == MODE_GETNEXT);
906
907 (void) pthread_mutex_lock(&update_lock);
908
909 for (; request != NULL; request = request->next) {
910 table_info = netsnmp_extract_table_info(request);
911 if (table_info == NULL)
912 continue;
913
914 ASSERT(table_info->colnum >= SUNFMFAULTEVENT_COLMIN);
915 ASSERT(table_info->colnum <= SUNFMFAULTEVENT_COLMAX);
916
917 /*
918 * table_info->colnum contains the column number requested.
919 * table_info->indexes contains a linked list of snmp variable
920 * bindings for the indexes of the table. Values in the list
921 * have been set corresponding to the indexes of the
922 * request. We have other guarantees as well:
923 *
924 * - The column number is always within range.
925 * - If we have no index data, table_info->index_oid_len is 0.
926 * - We will never receive requests outside our table nor
927 * those with the first subid anything other than 1 (Entry)
928 * nor those without a column number. This is true even
929 * for GETNEXT requests.
930 */
931 switch (reqinfo->mode) {
932 case MODE_GET:
933 data = sunFmFaultEventTable_fe(reginfo, table_info,
934 &status);
935 if (data == NULL)
936 goto out;
937 break;
938 case MODE_GETNEXT:
939 data = sunFmFaultEventTable_nextfe(reginfo, table_info,
940 &status);
941 if (data == NULL)
942 goto out;
943 break;
944 default:
945 (void) snmp_log(LOG_ERR, MODNAME_STR
946 ": unsupported request mode: %d\n", reqinfo->mode);
947 ret = SNMP_ERR_GENERR;
948 goto out;
949 }
950
951 switch (table_info->colnum) {
952 case SUNFMFAULTEVENT_COL_PROBLEMUUID:
953 if ((pdata = sunFmProblemTable_pr(reginfo, table_info))
954 == NULL) {
955 (void) netsnmp_table_build_result(reginfo,
956 request, table_info, ASN_OCTET_STR,
957 NULL, 0);
958 break;
959 }
960 (void) netsnmp_table_build_result(reginfo, request,
961 table_info, ASN_OCTET_STR,
962 (uchar_t *)pdata->d_aci_uuid,
963 strlen(pdata->d_aci_uuid));
964 break;
965 case SUNFMFAULTEVENT_COL_CLASS: {
966 char *class = "-";
967
968 (void) nvlist_lookup_string(data, FM_CLASS, &class);
969 (void) netsnmp_table_build_result(reginfo, request,
970 table_info, ASN_OCTET_STR, (uchar_t *)class,
971 strlen(class));
972 break;
973 }
974 case SUNFMFAULTEVENT_COL_CERTAINTY: {
975 uint8_t pct = 0;
976 ulong_t pl;
977
978 (void) nvlist_lookup_uint8(data, FM_FAULT_CERTAINTY,
979 &pct);
980 pl = (ulong_t)pct;
981 (void) netsnmp_table_build_result(reginfo, request,
982 table_info, ASN_UNSIGNED, (uchar_t *)&pl,
983 sizeof (pl));
984 break;
985 }
986 case SUNFMFAULTEVENT_COL_ASRU: {
987 nvlist_t *asru = NULL;
988 char *fmri = "-", *str;
989
990 (void) nvlist_lookup_nvlist(data, FM_FAULT_ASRU, &asru);
991 if ((str = nvl2fmri(asru)) != NULL)
992 fmri = str;
993
994 (void) netsnmp_table_build_result(reginfo, request,
995 table_info, ASN_OCTET_STR, (uchar_t *)fmri,
996 strlen(fmri));
997 free(str);
998 break;
999 }
1000 case SUNFMFAULTEVENT_COL_FRU: {
1001 nvlist_t *fru = NULL;
1002 char *fmri = "-", *str;
1003
1004 (void) nvlist_lookup_nvlist(data, FM_FAULT_FRU, &fru);
1005 if ((str = nvl2fmri(fru)) != NULL)
1006 fmri = str;
1007
1008 (void) netsnmp_table_build_result(reginfo, request,
1009 table_info, ASN_OCTET_STR, (uchar_t *)fmri,
1010 strlen(fmri));
1011 free(str);
1012 break;
1013 }
1014 case SUNFMFAULTEVENT_COL_RESOURCE: {
1015 nvlist_t *rsrc = NULL;
1016 char *fmri = "-", *str;
1017
1018 (void) nvlist_lookup_nvlist(data, FM_FAULT_RESOURCE,
1019 &rsrc);
1020 if ((str = nvl2fmri(rsrc)) != NULL)
1021 fmri = str;
1022
1023 (void) netsnmp_table_build_result(reginfo, request,
1024 table_info, ASN_OCTET_STR, (uchar_t *)fmri,
1025 strlen(fmri));
1026 free(str);
1027 break;
1028 }
1029 case SUNFMFAULTEVENT_COL_STATUS: {
1030 ulong_t pl = SUNFMFAULTEVENT_STATE_OTHER;
1031
1032 if (status & FM_SUSPECT_FAULTY)
1033 pl = SUNFMFAULTEVENT_STATE_FAULTY;
1034 else if (status & FM_SUSPECT_NOT_PRESENT)
1035 pl = SUNFMFAULTEVENT_STATE_REMOVED;
1036 else if (status & FM_SUSPECT_REPLACED)
1037 pl = SUNFMFAULTEVENT_STATE_REPLACED;
1038 else if (status & FM_SUSPECT_REPAIRED)
1039 pl = SUNFMFAULTEVENT_STATE_REPAIRED;
1040 else if (status & FM_SUSPECT_ACQUITTED)
1041 pl = SUNFMFAULTEVENT_STATE_ACQUITTED;
1042 (void) netsnmp_table_build_result(reginfo, request,
1043 table_info, ASN_INTEGER, (uchar_t *)&pl,
1044 sizeof (pl));
1045 break;
1046 }
1047 case SUNFMFAULTEVENT_COL_LOCATION: {
1048 char *location = "-";
1049
1050 (void) nvlist_lookup_string(data, FM_FAULT_LOCATION,
1051 &location);
1052 (void) netsnmp_table_build_result(reginfo, request,
1053 table_info, ASN_OCTET_STR, (uchar_t *)location,
1054 strlen(location));
1055 break;
1056 }
1057 default:
1058 break;
1059 }
1060 }
1061
1062 out:
1063 (void) pthread_mutex_unlock(&update_lock);
1064 return (ret);
1065 }