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