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