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 2006 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 /*
  30  * After having been declared, events, FMRIs and authorities must be defined
  31  * (instantiated) before they can be used as the subjects of commands.
  32  */
  33 
  34 #include <sys/sysmacros.h>
  35 #include <libnvpair.h>
  36 #include <string.h>
  37 #include <assert.h>
  38 
  39 #include <inj_event.h>
  40 #include <inj_err.h>
  41 #include <inj_lex.h>
  42 #include <inj_string.h>
  43 #include <inj.h>
  44 
  45 static inj_hash_t inj_defns[3];
  46 static int inj_defns_initialized;
  47 
  48 /* Intrinsics (signed and unsigned integer integer constants) */
  49 typedef struct intr {
  50         uchar_t ei_signed;
  51         uchar_t ei_width;
  52 } intr_t;
  53 
  54 static inj_hash_t *
  55 item2hash(inj_itemtype_t item)
  56 {
  57         int i;
  58 
  59         assert(item >= 0 && item < sizeof (inj_defns) / sizeof (inj_hash_t));
  60 
  61         if (!inj_defns_initialized) {
  62                 for (i = 0; i < sizeof (inj_defns) / sizeof (inj_hash_t); i++)
  63                         inj_strhash_create(&inj_defns[i]);
  64                 inj_defns_initialized = 1;
  65         }
  66 
  67         return (&inj_defns[item]);
  68 }
  69 
  70 inj_defn_t *
  71 inj_defn_lookup(const char *name, inj_memtype_t type)
  72 {
  73         inj_hash_t *hash = item2hash(inj_mem2item(type));
  74         inj_var_t *v;
  75 
  76         if ((v = inj_strhash_lookup(hash, name)) == NULL)
  77                 return (NULL);
  78 
  79         return (inj_hash_get_cookie(v));
  80 }
  81 
  82 static void
  83 inj_defn_destroy_memlist(inj_defnmem_t *m)
  84 {
  85         inj_defnmem_t *n;
  86 
  87         for (/* */; m != NULL; m = n) {
  88                 n = inj_list_next(m);
  89 
  90                 switch (m->dfm_type) {
  91                 case DEFNMEM_ARRAY:
  92                 case DEFNMEM_LIST:
  93                         inj_defn_destroy_memlist(inj_list_next(&m->dfm_list));
  94                         break;
  95                 default:
  96                         inj_strfree(m->dfm_str);
  97                 }
  98         }
  99 }
 100 
 101 void
 102 inj_defn_destroy(inj_defn_t *defn)
 103 {
 104         if (defn->defn_name != NULL)
 105                 inj_strfree(defn->defn_name);
 106 
 107         if (defn->defn_nvl != NULL)
 108                 nvlist_free(defn->defn_nvl);
 109 
 110         inj_defn_destroy_memlist(inj_list_next(&defn->defn_members));
 111 }
 112 
 113 static inj_defnmem_t *
 114 inj_defn_mem_create_common(inj_defnmemtype_t type)
 115 {
 116         inj_defnmem_t *dfm = inj_zalloc(sizeof (inj_defnmem_t));
 117 
 118         dfm->dfm_type = type;
 119         dfm->dfm_lineno = yylineno;
 120 
 121         return (dfm);
 122 }
 123 
 124 inj_defnmem_t *
 125 inj_defn_mem_create(const char *str, inj_defnmemtype_t type)
 126 {
 127         inj_defnmem_t *dfm = inj_defn_mem_create_common(type);
 128 
 129         dfm->dfm_str = str;
 130 
 131         return (dfm);
 132 }
 133 
 134 inj_defnmem_t *
 135 inj_defn_mem_create_list(inj_defn_t *list, inj_defnmemtype_t type)
 136 {
 137         inj_defnmem_t *dfm = inj_defn_mem_create_common(type);
 138 
 139         dfm->dfm_list = list->defn_members;
 140 
 141         inj_free(list, sizeof (inj_defn_t));
 142 
 143         return (dfm);
 144 }
 145 
 146 inj_defn_t *
 147 inj_defn_create(inj_defnmem_t *dfm)
 148 {
 149         inj_defn_t *defn = inj_zalloc(sizeof (inj_defn_t));
 150 
 151         defn->defn_lineno = yylineno;
 152 
 153         inj_list_append(&defn->defn_members, dfm);
 154 
 155         return (defn);
 156 }
 157 
 158 void
 159 inj_defn_addmem(inj_defn_t *defn, inj_defnmem_t *dfm)
 160 {
 161         inj_list_append(&defn->defn_members, dfm);
 162 }
 163 
 164 /*
 165  * Validate the dimensions of an array.  If the declared array size was zero,
 166  * accept (and return) whatever the definition used.  If fewer cells were
 167  * defined than were declared, return the declared size - the calling code will
 168  * fill the remaining cells with zeros.  The definition of more than the
 169  * declared number of cells triggers an error.  We print and error message in
 170  * this case and return the declared number.  This will allow processing to
 171  * continue.  The act of emitting the error will guarantee that we never
 172  * pass from parsing to program execution.
 173  */
 174 static size_t
 175 array_dim_check(inj_declmem_t *dlm, inj_defnmem_t *dfm)
 176 {
 177         inj_list_t *l;
 178         size_t dfnelems;
 179 
 180         for (dfnelems = 0, l = inj_list_next(&dfm->dfm_list); l != NULL;
 181             l = inj_list_next(l), dfnelems++);
 182 
 183         if (dlm->dlm_arrdim != 0 && dlm->dlm_arrdim != dfnelems) {
 184                 yyerror(" %d: defined array has %d elements, expected %d\n",
 185                     dfm->dfm_lineno, dfnelems, dlm->dlm_arrdim);
 186                 dfnelems = dlm->dlm_arrdim;
 187         }
 188 
 189         return (MAX(dfnelems, dlm->dlm_arrdim));
 190 }
 191 
 192 /*
 193  * The inj_defn_memcmp_* routines serve two purposes.  First, they compare a
 194  * given defined member with the corresponding declared member, signalling an
 195  * error if the two are incompatible.
 196  *
 197  * Assuming that validation succeeds, an entry is added to the passed nvlist
 198  * for the defined member.
 199  */
 200 
 201 /* Used to ease signed and unsigned integer validation */
 202 static const intr_t inj_intrinsics[] = {
 203         { 0, 0 }, /* MEMTYPE_UNKNOWN */
 204         { 1, 8 }, { 1, 16 }, { 1, 32 }, { 1, 64 },
 205         { 0, 8 }, { 0, 16 }, { 0, 32 }, { 0, 64 }
 206 };
 207 
 208 static int
 209 inj_defn_memcmp_signed(const intr_t *intr, inj_declmem_t *dlm,
 210     inj_defnmem_t *dfm, nvlist_t *nvl)
 211 {
 212         longlong_t val;
 213 
 214         if (dfm->dfm_type != DEFNMEM_IMM && dfm->dfm_type != DEFNMEM_IDENT)
 215                 return (inj_set_errno(EINVAL));
 216 
 217         if (inj_strtoll(dfm->dfm_str, intr->ei_width, &val) < 0)
 218                 return (-1); /* errno is set for us */
 219 
 220         switch (dlm->dlm_type) {
 221         case MEMTYPE_INT8:
 222                 errno = nvlist_add_int8(nvl, (char *)dlm->dlm_name,
 223                     (int8_t)val);
 224                 break;
 225         case MEMTYPE_INT16:
 226                 errno = nvlist_add_int16(nvl, (char *)dlm->dlm_name,
 227                     (int16_t)val);
 228                 break;
 229         case MEMTYPE_INT32:
 230                 errno = nvlist_add_int32(nvl, (char *)dlm->dlm_name,
 231                     (int32_t)val);
 232                 break;
 233         case MEMTYPE_INT64:
 234                 errno = nvlist_add_int64(nvl, (char *)dlm->dlm_name,
 235                     (int64_t)val);
 236         }
 237 
 238         if (errno != 0)
 239                 die("failed to add member %s\n", dlm->dlm_name);
 240 
 241         return (0);
 242 }
 243 
 244 static int
 245 inj_defn_memcmp_unsigned(const intr_t *intr, inj_declmem_t *dlm,
 246     inj_defnmem_t *dfm, nvlist_t *nvl)
 247 {
 248         u_longlong_t val;
 249 
 250         if (dfm->dfm_type != DEFNMEM_IMM && dfm->dfm_type != DEFNMEM_IDENT)
 251                 return (inj_set_errno(EINVAL));
 252 
 253         if (inj_strtoull(dfm->dfm_str, intr->ei_width, &val) < 0)
 254                 return (-1); /* errno is set for us */
 255 
 256         switch (dlm->dlm_type) {
 257         case MEMTYPE_UINT8:
 258                 errno = nvlist_add_uint8(nvl, (char *)dlm->dlm_name,
 259                     (uint8_t)val);
 260                 break;
 261         case MEMTYPE_UINT16:
 262                 errno = nvlist_add_uint16(nvl, (char *)dlm->dlm_name,
 263                     (uint16_t)val);
 264                 break;
 265         case MEMTYPE_UINT32:
 266                 errno = nvlist_add_uint32(nvl, (char *)dlm->dlm_name,
 267                     (uint32_t)val);
 268                 break;
 269         case MEMTYPE_UINT64:
 270                 errno = nvlist_add_uint64(nvl, (char *)dlm->dlm_name,
 271                     (uint64_t)val);
 272         }
 273 
 274         if (errno != 0)
 275                 die("failed to add member %s\n", dlm->dlm_name);
 276 
 277         return (0);
 278 }
 279 
 280 /* Validate an array of (un)signed integers. */
 281 static int
 282 inj_defn_memcmp_intr_array(const intr_t *cont, inj_declmem_t *dlm,
 283     inj_defnmem_t *dfm, nvlist_t *nvl)
 284 {
 285         typedef int (*adder_t)();
 286         static const adder_t signed_adders[] = {
 287                 NULL, nvlist_add_int8_array, nvlist_add_int16_array,
 288                 NULL, nvlist_add_int32_array, NULL, NULL, NULL,
 289                 nvlist_add_int64_array
 290         };
 291         static const adder_t unsigned_adders[] = {
 292                 NULL,
 293                 nvlist_add_uint8_array, nvlist_add_uint16_array,
 294                 NULL, nvlist_add_uint32_array, NULL, NULL, NULL,
 295                 nvlist_add_uint64_array
 296         };
 297 
 298         union {
 299                 char *a;
 300                 int8_t *a8; uint8_t *au8;
 301                 int16_t *a16; uint16_t *au16;
 302                 int32_t *a32; uint32_t *au32;
 303                 int64_t *a64; uint64_t *au64;
 304         } a;
 305 
 306         int (*adder)(nvlist_t *, const char *, char *, uint_t);
 307         size_t nelems;
 308         inj_defnmem_t *elem;
 309         char *arrbase, *arr;
 310         size_t arrsz;
 311         int err = 0;
 312         int i;
 313 
 314         if (dfm->dfm_type != DEFNMEM_ARRAY)
 315                 return (inj_set_errno(EINVAL));
 316 
 317         /*
 318          * Each nvlist array adder wants an array of its own type as input,
 319          * which is reasonable, but it complicates our general implementation.
 320          * We fight back with casting magic.
 321          */
 322 
 323         nelems = array_dim_check(dlm, dfm);
 324         arrsz = (nelems + 1) * (cont->ei_width / NBBY);
 325         arrbase = inj_zalloc(arrsz);
 326         a.a = arr = (char *)P2ROUNDUP((uintptr_t)arrbase,
 327             cont->ei_width / NBBY);
 328 
 329         adder = (cont->ei_signed ? signed_adders :
 330             unsigned_adders)[cont->ei_width / NBBY];
 331         assert(adder != NULL);
 332 
 333         for (i = 1, elem = inj_list_next(&dfm->dfm_list); elem != NULL;
 334             elem = inj_list_next(elem), i++) {
 335                 if (elem->dfm_type != DEFNMEM_IMM &&
 336                     elem->dfm_type != DEFNMEM_IDENT) {
 337                         yyerror(" %d: array cell %d is invalid\n",
 338                             dfm->dfm_lineno, i);
 339                         err++;
 340                         continue;
 341                 }
 342 
 343                 if (cont->ei_signed) {
 344                         longlong_t val;
 345 
 346                         if (inj_strtoll(elem->dfm_str, cont->ei_width,
 347                             &val) < 0) {
 348                                 yyerror(" %d: array cell %d %s\n",
 349                                     dfm->dfm_lineno, i, (errno == ERANGE ?
 350                                     "out of range for type" : "invalid"));
 351                                 err++;
 352                                 continue;
 353                         }
 354 
 355                         switch (cont->ei_width) {
 356                         case 8:
 357                                 *a.a8++ = (int8_t)val;
 358                                 break;
 359                         case 16:
 360                                 *a.a16++ = (int16_t)val;
 361                                 break;
 362                         case 32:
 363                                 *a.a32++ = (int32_t)val;
 364                                 break;
 365                         default:
 366                                 *a.a64++ = (int64_t)val;
 367                         }
 368 
 369                 } else {
 370                         u_longlong_t val;
 371 
 372                         if (inj_strtoull(elem->dfm_str, cont->ei_width,
 373                             &val) < 0) {
 374                                 yyerror(" %d: array cell %d %s\n",
 375                                     dfm->dfm_lineno, i, (errno == ERANGE ?
 376                                     "out of range for type" : "invalid"));
 377                                 err++;
 378                                 continue;
 379                         }
 380 
 381                         switch (cont->ei_width) {
 382                         case 8:
 383                                 *a.au8++ = (uint8_t)val;
 384                                 break;
 385                         case 16:
 386                                 *a.au16++ = (uint16_t)val;
 387                                 break;
 388                         case 32:
 389                                 *a.au32++ = (uint32_t)val;
 390                                 break;
 391                         default:
 392                                 *a.au64++ = (uint64_t)val;
 393                         }
 394                 }
 395         }
 396 
 397         if (err == 0 && (errno = adder(nvl, dlm->dlm_name, arr, nelems)) != 0)
 398                 die("failed to add array member %s", dlm->dlm_name);
 399 
 400         inj_free(arrbase, arrsz);
 401 
 402         if (err != 0)
 403                 return (inj_set_errno(EINVAL));
 404 
 405         return (0);
 406 }
 407 
 408 static int
 409 bool2val(const char *str, boolean_t *valp)
 410 {
 411         if (strcasecmp(str, "true") == 0)
 412                 *valp = 1;
 413         else if (strcasecmp(str, "false") == 0)
 414                 *valp = 0;
 415         else
 416                 return (-1);
 417 
 418         return (0);
 419 }
 420 
 421 static int
 422 inj_defn_memcmp_bool(inj_declmem_t *dlm, inj_defnmem_t *dfm, nvlist_t *nvl)
 423 {
 424         boolean_t val;
 425 
 426         if (dfm->dfm_type != DEFNMEM_IDENT)
 427                 return (inj_set_errno(EINVAL));
 428 
 429         if (bool2val(dfm->dfm_str, &val) < 0)
 430                 return (inj_set_errno(EINVAL));
 431 
 432         if ((errno = nvlist_add_boolean_value(nvl, (char *)dlm->dlm_name,
 433             val)) != 0)
 434                 die("failed to add boolean member %s", dlm->dlm_name);
 435 
 436         return (0);
 437 }
 438 
 439 static int
 440 inj_defn_memcmp_bool_array(inj_declmem_t *dlm, inj_defnmem_t *dfm,
 441     nvlist_t *nvl)
 442 {
 443         inj_defnmem_t *elem;
 444         boolean_t *arr;
 445         size_t nelems, arrsz;
 446         int err = 0;
 447         int i;
 448 
 449         if (dfm->dfm_type != DEFNMEM_ARRAY)
 450                 return (inj_set_errno(EINVAL));
 451 
 452         nelems = array_dim_check(dlm, dfm);
 453         arrsz = nelems * sizeof (boolean_t);
 454         arr = inj_zalloc(arrsz);
 455 
 456         for (i = 0, elem = inj_list_next(&dfm->dfm_list); elem != NULL;
 457             elem = inj_list_next(elem), i++) {
 458                 if (elem->dfm_type != DEFNMEM_IDENT) {
 459                         yyerror(" %d: array cell %d is invalid\n",
 460                             dfm->dfm_lineno, i + 1);
 461                         err++;
 462                         continue;
 463                 }
 464 
 465                 if (bool2val(elem->dfm_str, &arr[i]) < 0)
 466                         return (inj_set_errno(EINVAL));
 467         }
 468 
 469         if (err == 0 && (errno = nvlist_add_boolean_array(nvl,
 470             (char *)dlm->dlm_name, arr, nelems)) != 0)
 471                 die("failed to add boolean array member %s", dlm->dlm_name);
 472 
 473         inj_free(arr, arrsz);
 474 
 475         return (0);
 476 }
 477 
 478 /* Used for both strings and enums */
 479 static int
 480 inj_defn_memcmp_strenum(inj_declmem_t *dlm, inj_defnmem_t *dfm, nvlist_t *nvl)
 481 {
 482         inj_defnmemtype_t defnmemtype = (dlm->dlm_type == MEMTYPE_ENUM ?
 483             DEFNMEM_IDENT : DEFNMEM_QSTRING);
 484         const char *strenum = (dlm->dlm_type == MEMTYPE_ENUM ? "enum" :
 485             "string");
 486 
 487         if (dfm->dfm_type != defnmemtype)
 488                 return (inj_set_errno(EINVAL));
 489 
 490         if ((errno = nvlist_add_string(nvl, (char *)dlm->dlm_name,
 491             (char *)dfm->dfm_str)) != 0)
 492                 die("failed to add %s member %s", strenum, dlm->dlm_name);
 493 
 494         return (0);
 495 }
 496 
 497 static int
 498 inj_defn_memcmp_strenum_array(inj_declmem_t *dlm, inj_defnmem_t *dfm,
 499     nvlist_t *nvl)
 500 {
 501         inj_defnmemtype_t defnmemtype = (dlm->dlm_type == MEMTYPE_ENUM ?
 502             DEFNMEM_IDENT : DEFNMEM_QSTRING);
 503         const char *strenum = (dlm->dlm_type == MEMTYPE_ENUM ? "enum" :
 504             "string");
 505 
 506         inj_defnmem_t *elem;
 507         size_t nelems, arrsz;
 508         const char **arr;
 509         int err = 0;
 510         int i;
 511 
 512         if (dfm->dfm_type != DEFNMEM_ARRAY)
 513                 return (inj_set_errno(EINVAL));
 514 
 515         nelems = array_dim_check(dlm, dfm);
 516         arrsz = nelems * sizeof (char *);
 517         arr = inj_zalloc(arrsz);
 518 
 519         for (i = 0, elem = inj_list_next(&dfm->dfm_list); elem != NULL;
 520             elem = inj_list_next(elem), i++) {
 521                 if (elem->dfm_type != defnmemtype) {
 522                         yyerror(" %d: array cell %d is invalid\n",
 523                             dfm->dfm_lineno, i + 1);
 524                         err++;
 525                         continue;
 526                 }
 527 
 528                 if (dlm->dlm_type == MEMTYPE_ENUM &&
 529                     inj_strhash_lookup(dlm->dlm_enumvals, elem->dfm_str) ==
 530                     NULL) {
 531                         yyerror(" %d: invalid enum value %s\n",
 532                             dfm->dfm_lineno, elem->dfm_str);
 533                         err++;
 534                         continue;
 535                 }
 536 
 537                 arr[i] = elem->dfm_str;
 538         }
 539 
 540         if (err == 0 && (errno = nvlist_add_string_array(nvl,
 541             dlm->dlm_name, (char **)arr, nelems)) != 0)
 542                 die("failed to add %s array member %s", strenum, dlm->dlm_name);
 543 
 544         inj_free(arr, arrsz);
 545         return (0);
 546 }
 547 
 548 /*
 549  * Validator for embedded lists (events, fmris, authorities, lists, etc.).
 550  * There are two cases to deal with here.  The user could either have provided
 551  * the name of a previously-defined list, in which case we just make a copy of
 552  * said list for insertion into ours.  Alternatively, the user could simply
 553  * define a new list here.  In that case, we recursively invoke the member
 554  * comparator, but against the list type for the member being defined.
 555  */
 556 static nvlist_t *inj_defn_validate_memlist(inj_declmem_t *, inj_defnmem_t *);
 557 
 558 /* Embedded definition */
 559 static nvlist_t *
 560 inj_defn_memcmp_sub_list(inj_declmem_t *dlm, inj_defnmem_t *dfm)
 561 {
 562         inj_declmem_t *subdlm = inj_list_next(&dlm->dlm_decl->decl_members);
 563         inj_defnmem_t *subdfm = inj_list_next(&dfm->dfm_list);
 564 
 565         return (inj_defn_validate_memlist(subdlm, subdfm));
 566 }
 567 
 568 /* Reference to previously-defined thing */
 569 static nvlist_t *
 570 inj_defn_memcmp_sub_defined(inj_declmem_t *dlm, inj_defnmem_t *dfm)
 571 {
 572         inj_defn_t *subdefn;
 573         nvlist_t *new;
 574 
 575         if ((subdefn = inj_defn_lookup(dfm->dfm_str, dlm->dlm_type)) == NULL) {
 576                 yyerror(" %d: reference to undefined %s %s\n", dfm->dfm_lineno,
 577                     inj_mem2str(dlm->dlm_type), dfm->dfm_str);
 578                 (void) inj_set_errno(EINVAL);
 579                 return (NULL);
 580         }
 581 
 582         if (subdefn->defn_decl != dlm->dlm_decl) {
 583                 yyerror(" %d: %s %s is not a(n) %s\n", dfm->dfm_lineno,
 584                     inj_mem2str(dlm->dlm_type), dfm->dfm_str,
 585                     subdefn->defn_decl->decl_name);
 586                 (void) inj_set_errno(EINVAL);
 587                 return (NULL);
 588         }
 589 
 590         assert(subdefn->defn_nvl != NULL);
 591 
 592         if ((errno = nvlist_dup(subdefn->defn_nvl, &new, 0)) != 0) {
 593                 die("failed to duplicate %s list %s",
 594                     inj_item2str(subdefn->defn_decl->decl_type), dfm->dfm_str);
 595         }
 596 
 597         return (new);
 598 }
 599 
 600 static nvlist_t *
 601 inj_defn_memcmp_sub_makenvl(inj_declmem_t *dlm, inj_defnmem_t *dfm)
 602 {
 603         inj_defnmemtype_t dftype = dfm->dfm_type;
 604         inj_memtype_t dltype = dlm->dlm_type;
 605         nvlist_t *new = NULL;
 606 
 607         if (dftype == DEFNMEM_LIST)
 608                 new = inj_defn_memcmp_sub_list(dlm, dfm);
 609         else if (dftype == DEFNMEM_IDENT && (dltype == MEMTYPE_EVENT ||
 610             dltype == MEMTYPE_FMRI || dltype == MEMTYPE_AUTH))
 611                 new = inj_defn_memcmp_sub_defined(dlm, dfm);
 612         else
 613                 (void) inj_set_errno(EINVAL);
 614 
 615         return (new);
 616 }
 617 
 618 /* A single sub-list */
 619 static int
 620 inj_defn_memcmp_sub(inj_declmem_t *dlm, inj_defnmem_t *dfm, nvlist_t *nvl)
 621 {
 622         nvlist_t *new;
 623 
 624         if ((new = inj_defn_memcmp_sub_makenvl(dlm, dfm)) == NULL)
 625                 return (-1); /* errno is set for us */
 626 
 627         if ((errno = nvlist_add_nvlist(nvl, (char *)dlm->dlm_name,
 628             new)) != 0)
 629                 die("failed to add list member %s", dlm->dlm_name);
 630 
 631         return (0);
 632 }
 633 
 634 /* An array of sub-lists (for example, an array of events of a given type) */
 635 static int
 636 inj_defn_memcmp_sub_array(inj_declmem_t *dlm, inj_defnmem_t *dfm, nvlist_t *nvl)
 637 {
 638         size_t nelems, arrsz;
 639         inj_defnmem_t *elem;
 640         nvlist_t **arr;
 641         int err = 0;
 642         int i;
 643 
 644         if (dfm->dfm_type != DEFNMEM_ARRAY)
 645                 return (inj_set_errno(EINVAL));
 646 
 647         nelems = array_dim_check(dlm, dfm);
 648         arrsz = nelems * sizeof (char *);
 649         arr = inj_zalloc(arrsz);
 650 
 651         for (i = 0, elem = inj_list_next(&dfm->dfm_list); elem != NULL;
 652             elem = inj_list_next(elem), i++) {
 653                 if ((arr[i] = inj_defn_memcmp_sub_makenvl(dlm, elem)) == NULL) {
 654                         yyerror(" %d: array cell %d is invalid\n",
 655                             elem->dfm_lineno, i + 1);
 656                         err++;
 657                         continue;
 658                 }
 659         }
 660 
 661         if (err == 0 && (errno = nvlist_add_nvlist_array(nvl,
 662             (char *)dlm->dlm_name, arr, nelems)) != 0)
 663                 die("failed to add nvlist list member %s", dlm->dlm_name);
 664 
 665         inj_free(arr, arrsz);
 666 
 667         return (0);
 668 }
 669 
 670 /*
 671  * The declaration-definition member comparator.  Designed to recursive
 672  * invocation to allow for the validation of embedded/referenced lists.
 673  */
 674 nvlist_t *
 675 inj_defn_validate_memlist(inj_declmem_t *dlm, inj_defnmem_t *dfm)
 676 {
 677         const intr_t *intr;
 678         nvlist_t *nvl;
 679         int rc, nmem, dlnmem, dfnmem;
 680         int err = 0;
 681 
 682         if ((errno = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0)) != 0)
 683                 die("failed to allocate nvl for event");
 684 
 685         for (nmem = 1; dlm != NULL && dfm != NULL;
 686             dlm = inj_list_next(dlm), dfm = inj_list_next(dfm), nmem++) {
 687 
 688                 switch (dlm->dlm_type) {
 689                 case MEMTYPE_INT8:
 690                 case MEMTYPE_INT16:
 691                 case MEMTYPE_INT32:
 692                 case MEMTYPE_INT64:
 693                         intr = &inj_intrinsics[dlm->dlm_type];
 694 
 695                         if (dlm->dlm_flags & DECLMEM_F_ARRAY) {
 696                                 rc = inj_defn_memcmp_intr_array(intr, dlm, dfm,
 697                                     nvl);
 698                         } else {
 699                                 rc = inj_defn_memcmp_signed(intr, dlm, dfm,
 700                                     nvl);
 701                         }
 702                         break;
 703 
 704                 case MEMTYPE_UINT8:
 705                 case MEMTYPE_UINT16:
 706                 case MEMTYPE_UINT32:
 707                 case MEMTYPE_UINT64:
 708                         intr = &inj_intrinsics[dlm->dlm_type];
 709 
 710                         if (dlm->dlm_flags & DECLMEM_F_ARRAY) {
 711                                 rc = inj_defn_memcmp_intr_array(intr, dlm, dfm,
 712                                     nvl);
 713                         } else {
 714                                 rc = inj_defn_memcmp_unsigned(intr, dlm, dfm,
 715                                     nvl);
 716                         }
 717                         break;
 718 
 719                 case MEMTYPE_BOOL:
 720                         if (dlm->dlm_flags & DECLMEM_F_ARRAY)
 721                                 rc = inj_defn_memcmp_bool_array(dlm, dfm, nvl);
 722                         else
 723                                 rc = inj_defn_memcmp_bool(dlm, dfm, nvl);
 724                         break;
 725 
 726                 case MEMTYPE_STRING:
 727                         if (dlm->dlm_flags & DECLMEM_F_ARRAY) {
 728                                 rc = inj_defn_memcmp_strenum_array(dlm, dfm,
 729                                     nvl);
 730                         } else
 731                                 rc = inj_defn_memcmp_strenum(dlm, dfm, nvl);
 732                         break;
 733 
 734                 case MEMTYPE_ENUM:
 735                         if (dlm->dlm_flags & DECLMEM_F_ARRAY) {
 736                                 rc = inj_defn_memcmp_strenum_array(dlm, dfm,
 737                                     nvl);
 738                         } else
 739                                 rc = inj_defn_memcmp_strenum(dlm, dfm, nvl);
 740                         break;
 741 
 742                 case MEMTYPE_EVENT:
 743                 case MEMTYPE_FMRI:
 744                 case MEMTYPE_AUTH:
 745                 case MEMTYPE_LIST:
 746                         if (dlm->dlm_flags & DECLMEM_F_ARRAY)
 747                                 rc = inj_defn_memcmp_sub_array(dlm, dfm, nvl);
 748                         else
 749                                 rc = inj_defn_memcmp_sub(dlm, dfm, nvl);
 750                         break;
 751 
 752                 default:
 753                         die("unknown decl member type %d on member %s\n",
 754                             dlm->dlm_type, dlm->dlm_name);
 755                 }
 756 
 757                 if (rc < 0) {
 758                         yyerror(" %d: %s for member %s\n", dfm->dfm_lineno,
 759                             (errno == ERANGE ? "value out of range" :
 760                             "invalid value"), dlm->dlm_name);
 761                         err++;
 762                 }
 763         }
 764 
 765         dlnmem = dfnmem = nmem;
 766 
 767         while (dlm != NULL) {
 768                 dlm = inj_list_next(dlm);
 769                 dlnmem++;
 770         }
 771 
 772         while (dfm != NULL) {
 773                 dfm = inj_list_next(dfm);
 774                 dfnmem++;
 775         }
 776 
 777         if (dlnmem != dfnmem) {
 778                 yyerror("%d members found, expected %d", dfnmem, dlnmem);
 779                 err++;
 780         }
 781 
 782         if (err > 0) {
 783                 nvlist_free(nvl);
 784                 return (NULL);
 785         }
 786 
 787         return (nvl);
 788 }
 789 
 790 /*
 791  * The members have all been defined.  Validate the members against the
 792  * declaration, and add it to the appropriate "defined" list.
 793  */
 794 void
 795 inj_defn_finish(inj_defn_t *defn, const char *declnm, const char *name,
 796     inj_itemtype_t type)
 797 {
 798         inj_decl_t *decl = inj_decl_lookup(declnm, type);
 799         inj_hash_t *hash = item2hash(type);
 800         inj_declmem_t *dlm;
 801         inj_defnmem_t *dfm;
 802         inj_var_t *v;
 803 
 804         defn->defn_name = name;
 805         defn->defn_decl = decl;
 806 
 807         if (decl == NULL) {
 808                 yyerror("unknown %s type %s\n", inj_item2str(type), declnm);
 809                 inj_defn_destroy(defn);
 810                 return;
 811         }
 812 
 813         dlm = inj_list_next(&decl->decl_members);
 814         dfm = inj_list_next(&defn->defn_members);
 815 
 816         if ((defn->defn_nvl = inj_defn_validate_memlist(dlm, dfm)) == NULL) {
 817                 inj_defn_destroy(defn);
 818                 return;
 819         }
 820 
 821         if (type == ITEMTYPE_EVENT) {
 822                 if ((errno = nvlist_add_string(defn->defn_nvl, "class",
 823                     (char *)defn->defn_decl->decl_name)) != 0)
 824                         die("failed to add class to %s", name);
 825         }
 826 
 827         if ((v = inj_strhash_lookup(hash, name)) != NULL) {
 828                 inj_defn_t *other = inj_hash_get_cookie(v);
 829 
 830                 yyerror("duplicate %s name %s (other on line %d)\n",
 831                     inj_item2str(type), name, other->defn_lineno);
 832                 inj_defn_destroy(defn);
 833                 return;
 834         }
 835 
 836         (void) inj_strhash_insert(hash, name, (uintptr_t)defn);
 837 }