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 2018 Nexenta Systems, Inc.
  29  */
  30 
  31 #include <fm/fmd_adm.h>
  32 #include <fm/fmd_snmp.h>
  33 
  34 #include <net-snmp/net-snmp-config.h>
  35 #include <net-snmp/net-snmp-includes.h>
  36 #include <net-snmp/agent/net-snmp-agent-includes.h>
  37 
  38 #include <errno.h>
  39 #include <libuutil.h>
  40 #include <pthread.h>
  41 #include <stddef.h>
  42 
  43 #include "sunFM_impl.h"
  44 #include "module.h"
  45 
  46 static uu_avl_pool_t    *mod_name_avl_pool;
  47 static uu_avl_pool_t    *mod_index_avl_pool;
  48 static uu_avl_t         *mod_name_avl;
  49 static uu_avl_t         *mod_index_avl;
  50 
  51 #define VALID_AVL_STATE (mod_name_avl_pool != NULL &&           \
  52         mod_index_avl_pool != NULL && mod_name_avl != NULL &&   \
  53         mod_index_avl != NULL)
  54 
  55 /*
  56  * Update types.  Single-index and all are mutually exclusive.
  57  */
  58 #define UCT_INDEX       0x1
  59 #define UCT_ALL         0x2
  60 #define UCT_FLAGS       0x3
  61 
  62 #define MODULE_DATA_VALID(d)    ((d)->d_valid == valid_stamp)
  63 
  64 static ulong_t          max_index;
  65 static int              valid_stamp;
  66 static pthread_mutex_t  update_lock;
  67 
  68 static Netsnmp_Node_Handler     sunFmModuleTable_handler;
  69 
  70 static sunFmModule_data_t *
  71 key_build(const char *name, const ulong_t index)
  72 {
  73         static sunFmModule_data_t       key;
  74 
  75         key.d_index = index;
  76         if (name)
  77                 (void) strlcpy(key.d_ami_name, name, sizeof (key.d_ami_name));
  78         else
  79                 key.d_ami_name[0] = '\0';
  80 
  81         return (&key);
  82 }
  83 
  84 /*
  85  * If name is the name of a module we have previously seen and indexed, return
  86  * data for it.  Otherwise, return NULL.  Note that the module may not be
  87  * valid; that is, it may have been removed from the fault manager since its
  88  * information was last updated.
  89  */
  90 static sunFmModule_data_t *
  91 module_lookup_name(const char *name)
  92 {
  93         sunFmModule_data_t      *key;
  94 
  95         key = key_build(name, 0);
  96         return (uu_avl_find(mod_name_avl, key, NULL, NULL));
  97 }
  98 
  99 /*
 100  * If index corresponds to a module we have previously seen and indexed, return
 101  * data for it.  Otherwise, return NULL.  Note that the module may not be
 102  * valid; that is, it may have been removed from the fault manager since its
 103  * information was last updated.
 104  */
 105 static sunFmModule_data_t *
 106 module_lookup_index_exact(const ulong_t index)
 107 {
 108         sunFmModule_data_t      *key;
 109 
 110         key = key_build(NULL, index);
 111         return (uu_avl_find(mod_index_avl, key, NULL, NULL));
 112 }
 113 
 114 /*
 115  * If index corresponds to a valid (that is, extant as of latest information
 116  * from the fault manager) fmd module, return the data for that module.
 117  * Otherwise, return the data for the valid module whose index is as close as
 118  * possible to index but not lower.  This preserves the lexicographical
 119  * ordering required for GETNEXT processing.
 120  */
 121 static sunFmModule_data_t *
 122 module_lookup_index_nextvalid(const ulong_t index)
 123 {
 124         sunFmModule_data_t      *key, *data;
 125         uu_avl_index_t          idx;
 126 
 127         key = key_build(NULL, index);
 128 
 129         if ((data = uu_avl_find(mod_index_avl, key, NULL, &idx)) != NULL &&
 130             MODULE_DATA_VALID(data))
 131                 return (data);
 132 
 133         data = uu_avl_nearest_next(mod_index_avl, idx);
 134 
 135         while (data != NULL && !MODULE_DATA_VALID(data))
 136                 data = uu_avl_next(mod_index_avl, data);
 137 
 138         return (data);
 139 }
 140 
 141 /*
 142  * Possible update the contents of a single module within the cache.  This
 143  * is our callback from fmd_module_iter.
 144  */
 145 static int
 146 modinfo_update_one(const fmd_adm_modinfo_t *modinfo, void *arg)
 147 {
 148         const sunFmModule_update_ctx_t *update_ctx =
 149             (sunFmModule_update_ctx_t *)arg;
 150         sunFmModule_data_t *data = module_lookup_name(modinfo->ami_name);
 151 
 152         /*
 153          * An fmd module we haven't seen before.  We're obligated to index
 154          * it and link it into our cache so that we can find it, but we're
 155          * not obligated to fill it in completely unless we're doing a
 156          * thorough update or this is the module we were asked for.  This
 157          * avoids unnecessary iteration and memory manipulation for data
 158          * we're not going to return for this request.
 159          */
 160         if (data == NULL) {
 161                 uu_avl_index_t idx;
 162 
 163                 DEBUGMSGTL((MODNAME_STR, "found new fmd module %s\n",
 164                     modinfo->ami_name));
 165                 if ((data = SNMP_MALLOC_TYPEDEF(sunFmModule_data_t)) == NULL) {
 166                         (void) snmp_log(LOG_ERR, MODNAME_STR ": Out of memory "
 167                             "for new module data at %s:%d\n", __FILE__,
 168                             __LINE__);
 169                         return (1);
 170                 }
 171                 /*
 172                  * We allocate indices sequentially and never reuse them.
 173                  * This ensures we can always return valid GETNEXT responses
 174                  * without having to reindex, and it provides the user a
 175                  * more consistent view of the fault manager.
 176                  */
 177                 data->d_index = ++max_index;
 178                 DEBUGMSGTL((MODNAME_STR, "index %lu is %s@%p\n", data->d_index,
 179                     modinfo->ami_name, data));
 180 
 181                 (void) strlcpy(data->d_ami_name, modinfo->ami_name,
 182                     sizeof (data->d_ami_name));
 183 
 184                 uu_avl_node_init(data, &data->d_name_avl, mod_name_avl_pool);
 185                 (void) uu_avl_find(mod_name_avl, data, NULL, &idx);
 186                 uu_avl_insert(mod_name_avl, data, idx);
 187 
 188                 uu_avl_node_init(data, &data->d_index_avl, mod_index_avl_pool);
 189                 (void) uu_avl_find(mod_index_avl, data, NULL, &idx);
 190                 uu_avl_insert(mod_index_avl, data, idx);
 191 
 192                 DEBUGMSGTL((MODNAME_STR, "completed new module %lu/%s@%p\n",
 193                     data->d_index, data->d_ami_name, data));
 194         }
 195 
 196         data->d_valid = valid_stamp;
 197 
 198         DEBUGMSGTL((MODNAME_STR, "timestamp updated for %lu/%s@%p: %d\n",
 199             data->d_index, data->d_ami_name, data, data->d_valid));
 200 
 201         if ((update_ctx->uc_type & UCT_ALL) ||
 202             update_ctx->uc_index == data->d_index) {
 203                 (void) strlcpy(data->d_ami_vers, modinfo->ami_vers,
 204                     sizeof (data->d_ami_vers));
 205                 (void) strlcpy(data->d_ami_desc, modinfo->ami_desc,
 206                     sizeof (data->d_ami_desc));
 207                 data->d_ami_flags = modinfo->ami_flags;
 208         }
 209 
 210         return (!(update_ctx->uc_type & UCT_ALL) &&
 211             update_ctx->uc_index == data->d_index);
 212 }
 213 
 214 /*
 215  * Update some or all module data from fmd.  If thorough is set, all modules
 216  * will be indexed and their data cached.  Otherwise, updates will stop once
 217  * the module matching index has been updated.
 218  *
 219  * Returns appropriate SNMP error codes.
 220  */
 221 static int
 222 modinfo_update(sunFmModule_update_ctx_t *update_ctx)
 223 {
 224         fmd_adm_t *adm;
 225 
 226         ASSERT(update_ctx != NULL);
 227         ASSERT((update_ctx->uc_type & (UCT_INDEX|UCT_ALL)) !=
 228             (UCT_INDEX|UCT_ALL));
 229         ASSERT((update_ctx->uc_type & ~UCT_FLAGS) == 0);
 230         ASSERT(VALID_AVL_STATE);
 231 
 232         if ((adm = fmd_adm_open(update_ctx->uc_host, update_ctx->uc_prog,
 233             update_ctx->uc_version)) == NULL) {
 234                 (void) snmp_log(LOG_ERR, MODNAME_STR ": Communication with fmd "
 235                     "failed: %s\n", strerror(errno));
 236                 return (SNMP_ERR_RESOURCEUNAVAILABLE);
 237         }
 238 
 239         ++valid_stamp;
 240         if (fmd_adm_module_iter(adm, modinfo_update_one, update_ctx) != 0) {
 241                 (void) snmp_log(LOG_ERR, MODNAME_STR ": fmd module information "
 242                     "update failed: %s\n", fmd_adm_errmsg(adm));
 243                 fmd_adm_close(adm);
 244                 return (SNMP_ERR_RESOURCEUNAVAILABLE);
 245         }
 246 
 247         DEBUGMSGTL((MODNAME_STR, "module iteration completed\n"));
 248 
 249         fmd_adm_close(adm);
 250         return (SNMP_ERR_NOERROR);
 251 }
 252 
 253 static void
 254 request_update(void)
 255 {
 256         sunFmModule_update_ctx_t        uc;
 257 
 258         /*
 259          * The current modinfo_update implementation offers minimal savings
 260          * for the use of index-only updates; therefore we always do a full
 261          * update.  If it becomes advantageous to limit updates to a single
 262          * index, the contexts can be queued by the handler instead.
 263          */
 264         uc.uc_host = NULL;
 265         uc.uc_prog = FMD_ADM_PROGRAM;
 266         uc.uc_version = FMD_ADM_VERSION;
 267         uc.uc_index = 0;
 268         uc.uc_type = UCT_ALL;
 269 
 270         (void) modinfo_update(&uc);
 271 }
 272 
 273 /*ARGSUSED*/
 274 static int
 275 module_compare_name(const void *l, const void *r, void *private)
 276 {
 277         sunFmModule_data_t      *l_data = (sunFmModule_data_t *)l;
 278         sunFmModule_data_t      *r_data = (sunFmModule_data_t *)r;
 279 
 280         ASSERT(l_data != NULL && r_data != NULL);
 281 
 282         return (strcmp(l_data->d_ami_name, r_data->d_ami_name));
 283 }
 284 
 285 /*ARGSUSED*/
 286 static int
 287 module_compare_index(const void *l, const void *r, void *private)
 288 {
 289         sunFmModule_data_t      *l_data = (sunFmModule_data_t *)l;
 290         sunFmModule_data_t      *r_data = (sunFmModule_data_t *)r;
 291 
 292         ASSERT(l_data != NULL && r_data != NULL);
 293 
 294         return (l_data->d_index < r_data->d_index ? -1 :
 295             l_data->d_index > r_data->d_index ? 1 : 0);
 296 }
 297 
 298 int
 299 sunFmModuleTable_init(void)
 300 {
 301         static oid sunFmModuleTable_oid[] = { SUNFMMODULETABLE_OID };
 302         netsnmp_table_registration_info *table_info;
 303         netsnmp_handler_registration *handler;
 304         int err;
 305 
 306         if ((err = pthread_mutex_init(&update_lock, NULL)) != 0) {
 307                 (void) snmp_log(LOG_ERR, MODNAME_STR ": mutex_init failure: "
 308                     "%s\n", strerror(err));
 309                 return (MIB_REGISTRATION_FAILED);
 310         }
 311 
 312         if ((table_info =
 313             SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info)) == NULL)
 314                 return (MIB_REGISTRATION_FAILED);
 315 
 316         if ((handler = netsnmp_create_handler_registration("sunFmModuleTable",
 317             sunFmModuleTable_handler, sunFmModuleTable_oid,
 318             OID_LENGTH(sunFmModuleTable_oid), HANDLER_CAN_RONLY)) == NULL) {
 319                 SNMP_FREE(table_info);
 320                 return (MIB_REGISTRATION_FAILED);
 321         }
 322 
 323         /*
 324          * The Net-SNMP template uses add_indexes here, but that
 325          * function is unsafe because it does not check for failure.
 326          */
 327         if (netsnmp_table_helper_add_index(table_info, ASN_UNSIGNED) == NULL) {
 328                 SNMP_FREE(table_info);
 329                 SNMP_FREE(handler);
 330                 return (MIB_REGISTRATION_FAILED);
 331         }
 332 
 333         table_info->min_column = SUNFMMODULE_COLMIN;
 334         table_info->max_column = SUNFMMODULE_COLMAX;
 335 
 336         if ((mod_name_avl_pool = uu_avl_pool_create("mod_name",
 337             sizeof (sunFmModule_data_t),
 338             offsetof(sunFmModule_data_t, d_name_avl), module_compare_name,
 339             UU_AVL_DEBUG)) == NULL) {
 340                 snmp_free_varbind(table_info->indexes);
 341                 SNMP_FREE(table_info);
 342                 SNMP_FREE(handler);
 343         }
 344 
 345         if ((mod_name_avl = uu_avl_create(mod_name_avl_pool, NULL,
 346             UU_AVL_DEBUG)) == NULL) {
 347                 (void) snmp_log(LOG_ERR, MODNAME_STR ": mod_name_avl creation "
 348                     "failed: %s\n", uu_strerror(uu_error()));
 349                 snmp_free_varbind(table_info->indexes);
 350                 SNMP_FREE(table_info);
 351                 SNMP_FREE(handler);
 352                 uu_avl_pool_destroy(mod_name_avl_pool);
 353                 return (MIB_REGISTRATION_FAILED);
 354         }
 355 
 356         if ((mod_index_avl_pool = uu_avl_pool_create("mod_index",
 357             sizeof (sunFmModule_data_t),
 358             offsetof(sunFmModule_data_t, d_index_avl),
 359             module_compare_index, UU_AVL_DEBUG)) == NULL) {
 360                 snmp_free_varbind(table_info->indexes);
 361                 SNMP_FREE(table_info);
 362                 SNMP_FREE(handler);
 363                 uu_avl_destroy(mod_name_avl);
 364                 uu_avl_pool_destroy(mod_name_avl_pool);
 365         }
 366 
 367         if ((mod_index_avl = uu_avl_create(mod_index_avl_pool, NULL,
 368             UU_AVL_DEBUG)) == NULL) {
 369                 (void) snmp_log(LOG_ERR, MODNAME_STR ": mod_index_avl creation "
 370                     "failed: %s\n", uu_strerror(uu_error()));
 371                 snmp_free_varbind(table_info->indexes);
 372                 SNMP_FREE(table_info);
 373                 SNMP_FREE(handler);
 374                 uu_avl_destroy(mod_name_avl);
 375                 uu_avl_pool_destroy(mod_name_avl_pool);
 376                 uu_avl_pool_destroy(mod_index_avl_pool);
 377                 return (MIB_REGISTRATION_FAILED);
 378         }
 379 
 380         if ((err = netsnmp_register_table(handler, table_info)) !=
 381             MIB_REGISTERED_OK) {
 382                 snmp_free_varbind(table_info->indexes);
 383                 SNMP_FREE(table_info);
 384                 SNMP_FREE(handler);
 385                 uu_avl_destroy(mod_name_avl);
 386                 uu_avl_pool_destroy(mod_name_avl_pool);
 387                 uu_avl_destroy(mod_index_avl);
 388                 uu_avl_pool_destroy(mod_index_avl_pool);
 389                 return (err);
 390         }
 391 
 392         return (MIB_REGISTERED_OK);
 393 }
 394 
 395 /*
 396  * These two functions form the core of GET/GETNEXT handling (the
 397  * only kind we do).  They perform two functions:
 398  *
 399  * - First, frob the request to set all the index variables to correspond
 400  *   to the value that's going to be returned.  For GET, this is a nop;
 401  *   for GETNEXT it always requires some work.
 402  * - Second, find and return the fmd module information corresponding to
 403  *   the (possibly updated) indices.
 404  *
 405  * These should be as fast as possible; they run in the agent thread.
 406  */
 407 static sunFmModule_data_t *
 408 sunFmModuleTable_nextmod(netsnmp_handler_registration *reginfo,
 409     netsnmp_table_request_info *table_info)
 410 {
 411         sunFmModule_data_t      *data;
 412         netsnmp_variable_list   *var;
 413         ulong_t index;
 414 
 415         /*
 416          * If we have no index, we must make one.
 417          */
 418         if (table_info->number_indexes < 1) {
 419                 oid tmpoid[MAX_OID_LEN];
 420                 index = 1;
 421 
 422                 DEBUGMSGTL((MODNAME_STR, "nextmod: no indexes given\n"));
 423                 var = SNMP_MALLOC_TYPEDEF(netsnmp_variable_list);
 424                 (void) snmp_set_var_typed_value(var, ASN_UNSIGNED,
 425                     (uchar_t *)&index, sizeof (index));
 426                 (void) memcpy(tmpoid, reginfo->rootoid,
 427                     reginfo->rootoid_len * sizeof (oid));
 428                 tmpoid[reginfo->rootoid_len] = 1;    /* Entry is .1 */
 429                 tmpoid[reginfo->rootoid_len + 1] = table_info->colnum;
 430                 if (build_oid(&var->name, &var->name_length, tmpoid,
 431                     reginfo->rootoid_len + 2, var) != SNMPERR_SUCCESS) {
 432                         snmp_free_varbind(var);
 433                         return (NULL);
 434                 }
 435                 DEBUGMSGTL((MODNAME_STR, "nextmod: built fake index: "));
 436                 DEBUGMSGVAR((MODNAME_STR, var));
 437                 DEBUGMSG((MODNAME_STR, "\n"));
 438         } else {
 439                 var = snmp_clone_varbind(table_info->indexes);
 440                 index = *var->val.integer;
 441                 DEBUGMSGTL((MODNAME_STR, "nextmod: received index: "));
 442                 DEBUGMSGVAR((MODNAME_STR, var));
 443                 DEBUGMSG((MODNAME_STR, "\n"));
 444                 index++;
 445         }
 446 
 447         snmp_free_varbind(table_info->indexes);
 448         table_info->indexes = NULL;
 449         table_info->number_indexes = 0;
 450 
 451         if ((data = module_lookup_index_nextvalid(index)) == NULL) {
 452                 DEBUGMSGTL((MODNAME_STR, "nextmod: exact match not found for "
 453                     "index %lu; trying next column\n", index));
 454                 if (table_info->colnum >=
 455                     netsnmp_find_table_registration_info(reginfo)->max_column) {
 456                         snmp_free_varbind(var);
 457                         DEBUGMSGTL((MODNAME_STR, "nextmod: out of columns\n"));
 458                         return (NULL);
 459                 }
 460                 table_info->colnum++;
 461                 index = 1;
 462 
 463                 data = module_lookup_index_nextvalid(index);
 464         }
 465 
 466         if (data == NULL) {
 467                 DEBUGMSGTL((MODNAME_STR, "nextmod: exact match not found for "
 468                     "index %lu; stopping\n", index));
 469                 snmp_free_varbind(var);
 470                 return (NULL);
 471         }
 472 
 473         *var->val.integer = data->d_index;
 474         table_info->indexes = var;
 475         table_info->number_indexes = 1;
 476 
 477         DEBUGMSGTL((MODNAME_STR, "matching data is %lu/%s@%p\n", data->d_index,
 478             data->d_ami_name, data));
 479 
 480         return (data);
 481 }
 482 
 483 /*ARGSUSED*/
 484 static sunFmModule_data_t *
 485 sunFmModuleTable_mod(netsnmp_handler_registration *reginfo,
 486     netsnmp_table_request_info *table_info)
 487 {
 488         ASSERT(table_info->number_indexes == 1);
 489 
 490         return (module_lookup_index_exact(table_info->index_oid[0]));
 491 }
 492 
 493 /*ARGSUSED*/
 494 static int
 495 sunFmModuleTable_handler(netsnmp_mib_handler *handler,
 496     netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo,
 497     netsnmp_request_info *request)
 498 {
 499         netsnmp_table_request_info      *table_info;
 500         sunFmModule_data_t              *data;
 501         ulong_t                         modstate;
 502         int                             ret = SNMP_ERR_NOERROR;
 503 
 504         /*
 505          * We don't support MODE_GETBULK directly, so all bulk requests should
 506          * come through bulk_to_next helper.  Make sure it stays that way.
 507          */
 508         ASSERT(reqinfo->mode == MODE_GET || reqinfo->mode == MODE_GETNEXT);
 509 
 510         (void) pthread_mutex_lock(&update_lock);
 511         request_update();
 512 
 513         for (; request != NULL; request = request->next) {
 514                 table_info = netsnmp_extract_table_info(request);
 515                 if (table_info == NULL)
 516                         continue;
 517 
 518                 ASSERT(table_info->colnum >= SUNFMMODULE_COLMIN);
 519                 ASSERT(table_info->colnum <= SUNFMMODULE_COLMAX);
 520 
 521                 /*
 522                  * table_info->colnum contains the column number requested.
 523                  * table_info->indexes contains a linked list of snmp variable
 524                  * bindings for the indexes of the table.  Values in the list
 525                  * have been set corresponding to the indexes of the
 526                  * request.  We have other guarantees as well:
 527                  *
 528                  * - The column number is always within range.
 529                  * - If we have no index data, table_info->index_oid_len is 0.
 530                  * - We will never receive requests outside our table nor
 531                  *   those with the first subid anything other than 1 (Entry)
 532                  *   nor those without a column number.  This is true even
 533                  *   for GETNEXT requests.
 534                  */
 535                 switch (reqinfo->mode) {
 536                 case MODE_GET:
 537                         data = sunFmModuleTable_mod(reginfo, table_info);
 538                         if (data == NULL)
 539                                 goto out;
 540                         break;
 541                 case MODE_GETNEXT:
 542                         data = sunFmModuleTable_nextmod(reginfo, table_info);
 543                         if (data == NULL)
 544                                 goto out;
 545                         break;
 546                 default:
 547                         (void) snmp_log(LOG_ERR, MODNAME_STR
 548                             ": unsupported request mode: %d\n", reqinfo->mode);
 549                         ret = SNMP_ERR_GENERR;
 550                         goto out;
 551                 }
 552 
 553                 switch (table_info->colnum) {
 554                 case SUNFMMODULE_COL_NAME:
 555                         (void) netsnmp_table_build_result(reginfo, request,
 556                             table_info, ASN_OCTET_STR,
 557                             (uchar_t *)data->d_ami_name,
 558                             strlen(data->d_ami_name));
 559                         break;
 560                 case SUNFMMODULE_COL_VERSION:
 561                         (void) netsnmp_table_build_result(reginfo, request,
 562                             table_info, ASN_OCTET_STR,
 563                             (uchar_t *)data->d_ami_vers,
 564                             strlen(data->d_ami_vers));
 565                         break;
 566                 case SUNFMMODULE_COL_STATUS:
 567                         modstate = (data->d_ami_flags & FMD_ADM_MOD_FAILED) ?
 568                             SUNFMMODULE_STATE_FAILED : SUNFMMODULE_STATE_ACTIVE;
 569                         (void) netsnmp_table_build_result(reginfo, request,
 570                             table_info, ASN_INTEGER, (uchar_t *)&modstate,
 571                             sizeof (modstate));
 572                         break;
 573                 case SUNFMMODULE_COL_DESCRIPTION:
 574                         (void) netsnmp_table_build_result(reginfo, request,
 575                             table_info, ASN_OCTET_STR,
 576                             (uchar_t *)data->d_ami_desc,
 577                             strlen(data->d_ami_desc));
 578                         break;
 579                 default:
 580                         break;
 581                 }
 582         }
 583 
 584 out:
 585         (void) pthread_mutex_unlock(&update_lock);
 586         return (ret);
 587 }