xref: /illumos-gate/usr/src/cmd/fm/fminject/common/inj_defn.c (revision 355b4669e025ff377602b6fc7caaf30dbc218371)
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 }
838