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