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));
 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 }