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