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