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