xref: /illumos-gate/usr/src/cmd/fm/eversholt/common/check.c (revision 42277d8633e9474e6bea7c26e2e06fa71441981b)
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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * check.c -- routines for checking the prop tree
26  *
27  * this module provides semantic checks on the parse tree.  most of
28  * these checks happen during the construction of the parse tree,
29  * when the various tree_X() routines call the various check_X()
30  * routines.  in a couple of special cases, a check function will
31  * process the parse tree after it has been fully constructed.  these
32  * cases are noted in the comments above the check function.
33  */
34 
35 #pragma ident	"%Z%%M%	%I%	%E% SMI"
36 
37 #include <stdio.h>
38 #include "out.h"
39 #include "stable.h"
40 #include "literals.h"
41 #include "lut.h"
42 #include "tree.h"
43 #include "ptree.h"
44 #include "check.h"
45 
46 static int check_reportlist(enum nodetype t, const char *s, struct node *np);
47 static int check_num(enum nodetype t, const char *s, struct node *np);
48 static int check_quote(enum nodetype t, const char *s, struct node *np);
49 static int check_action(enum nodetype t, const char *s, struct node *np);
50 static int check_num_func(enum nodetype t, const char *s, struct node *np);
51 static int check_fru_asru(enum nodetype t, const char *s, struct node *np);
52 static int check_engine(enum nodetype t, const char *s, struct node *np);
53 static int check_count(enum nodetype t, const char *s, struct node *np);
54 static int check_timeval(enum nodetype t, const char *s, struct node *np);
55 static int check_id(enum nodetype t, const char *s, struct node *np);
56 static int check_serd_method(enum nodetype t, const char *s, struct node *np);
57 static int check_serd_id(enum nodetype t, const char *s, struct node *np);
58 static int check_nork(struct node *np);
59 static void check_cycle_lhs(struct node *stmtnp, struct node *arrow);
60 static void check_cycle_lhs_try(struct node *stmtnp, struct node *lhs,
61     struct node *rhs);
62 static void check_cycle_rhs(struct node *rhs);
63 static void check_proplists_lhs(enum nodetype t, struct node *lhs);
64 
65 static struct {
66 	enum nodetype t;
67 	const char *name;
68 	int required;
69 	int (*checker)(enum nodetype t, const char *s, struct node *np);
70 	int outflags;
71 } Allowednames[] = {
72 	{ T_FAULT, "FITrate", 0, check_num_func, O_ERR },
73 	{ T_FAULT, "FRU", 0, check_fru_asru, O_ERR },
74 	{ T_FAULT, "ASRU", 0, check_fru_asru, O_ERR },
75 	{ T_FAULT, "message", 0, check_num_func, O_ERR },
76 	{ T_FAULT, "retire", 0, check_num_func, O_ERR },
77 	{ T_FAULT, "response", 0, check_num_func, O_ERR },
78 	{ T_FAULT, "action", 0, check_action, O_ERR },
79 	{ T_FAULT, "count", 0, check_count, O_ERR },
80 	{ T_FAULT, "engine", 0, check_engine, O_ERR },
81 	{ T_UPSET, "engine", 0, check_engine, O_ERR },
82 	{ T_DEFECT, "FRU", 0, check_fru_asru, O_ERR },
83 	{ T_DEFECT, "ASRU", 0, check_fru_asru, O_ERR },
84 	{ T_DEFECT, "engine", 0, check_engine, O_ERR },
85 	{ T_EREPORT, "poller", 0, check_id, O_ERR },
86 	{ T_EREPORT, "delivery", 0, check_timeval, O_ERR },
87 	{ T_EREPORT, "discard_if_config_unknown", 0, check_num, O_ERR },
88 	{ T_SERD, "N", 1, check_num, O_ERR },
89 	{ T_SERD, "T", 1, check_timeval, O_ERR },
90 	{ T_SERD, "method", 0, check_serd_method, O_ERR },
91 	{ T_SERD, "trip", 0, check_reportlist, O_ERR },
92 	{ T_SERD, "FRU", 0, check_fru_asru, O_ERR },
93 	{ T_SERD, "id", 0, check_serd_id, O_ERR },
94 	{ T_ERROR, "ASRU", 0, check_fru_asru, O_ERR },
95 	{ T_CONFIG, NULL, 0, check_quote, O_ERR },
96 	{ 0, NULL, 0 },
97 };
98 
99 void
100 check_init(void)
101 {
102 	int i;
103 
104 	for (i = 0; Allowednames[i].t; i++)
105 		if (Allowednames[i].name != NULL)
106 			Allowednames[i].name = stable(Allowednames[i].name);
107 }
108 
109 void
110 check_fini(void)
111 {
112 }
113 
114 /*ARGSUSED*/
115 void
116 check_report_combination(struct node *np)
117 {
118 	/* nothing to check for here.  poller is only prop and it is optional */
119 }
120 
121 /*
122  * check_path_iterators -- verify all iterators are explicit
123  */
124 static void
125 check_path_iterators(struct node *np)
126 {
127 	if (np == NULL)
128 		return;
129 
130 	switch (np->t) {
131 		case T_ARROW:
132 			check_path_iterators(np->u.arrow.lhs);
133 			check_path_iterators(np->u.arrow.rhs);
134 			break;
135 
136 		case T_LIST:
137 			check_path_iterators(np->u.expr.left);
138 			check_path_iterators(np->u.expr.right);
139 			break;
140 
141 		case T_EVENT:
142 			check_path_iterators(np->u.event.epname);
143 			break;
144 
145 		case T_NAME:
146 			if (np->u.name.child == NULL)
147 				outfl(O_DIE, np->file, np->line,
148 				    "internal error: check_path_iterators: "
149 				    "unexpected implicit iterator: %s",
150 				    np->u.name.s);
151 			check_path_iterators(np->u.name.next);
152 			break;
153 
154 		default:
155 			outfl(O_DIE, np->file, np->line,
156 			    "internal error: check_path_iterators: "
157 			    "unexpected type: %s",
158 			    ptree_nodetype2str(np->t));
159 	}
160 }
161 
162 void
163 check_arrow(struct node *np)
164 {
165 	ASSERTinfo(np->t == T_ARROW, ptree_nodetype2str(np->t));
166 
167 	if (np->u.arrow.lhs->t != T_ARROW &&
168 	    np->u.arrow.lhs->t != T_LIST &&
169 	    np->u.arrow.lhs->t != T_EVENT) {
170 		outfl(O_ERR,
171 		    np->u.arrow.lhs->file, np->u.arrow.lhs->line,
172 		    "%s not allowed on left-hand side of arrow",
173 		    ptree_nodetype2str(np->u.arrow.lhs->t));
174 	}
175 
176 	if (!check_nork(np->u.arrow.nnp) ||
177 	    !check_nork(np->u.arrow.knp))
178 		outfl(O_ERR, np->file, np->line,
179 		    "counts associated with propagation arrows "
180 		    "must be integers");
181 
182 	check_path_iterators(np);
183 }
184 
185 /*
186  * make sure the nork values are valid.
187  * Nork values must be "A" for all(T_NAME),
188  * a number(T_NUM), or a simple
189  * expression(T_SUB, T_ADD, T_MUL, T_DIV)
190  */
191 static int
192 check_nork(struct node *np)
193 {
194 	int rval = 0;
195 
196 	/* NULL means no nork value which is allowed */
197 	if (np == NULL) {
198 		rval = 1;
199 	}
200 	else
201 	{
202 		/* if the nork is a name it must be A for "All" */
203 		if (np->t == T_NAME)
204 			if (*np->u.name.s == 'A')
205 				return (1);
206 
207 		/*  T_NUM allowed */
208 		if (np->t == T_NUM)
209 			rval = 1;
210 
211 		/*  simple expressions allowed */
212 		if (np->t == T_SUB ||
213 		    np->t == T_ADD ||
214 		    np->t == T_MUL ||
215 		    np->t == T_DIV)
216 			rval = 1;
217 	}
218 
219 	return (rval);
220 }
221 
222 static int
223 check_reportlist(enum nodetype t, const char *s, struct node *np)
224 {
225 	if (np == NULL)
226 		return (1);
227 	else if (np->t == T_EVENT) {
228 		if (np->u.event.ename->u.name.t != N_EREPORT) {
229 			outfl(O_ERR, np->file, np->line,
230 			    "%s %s property must begin with \"ereport.\"",
231 			    ptree_nodetype2str(t), s);
232 		} else if (tree_event2np_lut_lookup(Ereports, np) == NULL) {
233 			outfl(O_ERR, np->file, np->line,
234 			    "%s %s property contains undeclared name",
235 			    ptree_nodetype2str(t), s);
236 		}
237 		check_type_iterator(np);
238 	} else if (np->t == T_LIST) {
239 		(void) check_reportlist(t, s, np->u.expr.left);
240 		(void) check_reportlist(t, s, np->u.expr.right);
241 	}
242 	return (1);
243 }
244 
245 static int
246 check_num(enum nodetype t, const char *s, struct node *np)
247 {
248 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
249 	if (np->t != T_NUM)
250 		outfl(O_ERR, np->file, np->line,
251 		    "%s %s property must be a single number",
252 		    ptree_nodetype2str(t), s);
253 	return (1);
254 }
255 
256 /*ARGSUSED1*/
257 static int
258 check_quote(enum nodetype t, const char *s, struct node *np)
259 {
260 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
261 	if (np->t != T_QUOTE)
262 		outfl(O_ERR, np->file, np->line,
263 		    "%s properties must be quoted strings",
264 		    ptree_nodetype2str(t));
265 	return (1);
266 }
267 
268 static int
269 check_action(enum nodetype t, const char *s, struct node *np)
270 {
271 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
272 
273 	if (np->t != T_FUNC)
274 		outfl(O_ERR, np->file, np->line,
275 		    "%s %s property must be a function or list of functions",
276 		    ptree_nodetype2str(t), s);
277 	return (1);
278 }
279 
280 static int
281 check_num_func(enum nodetype t, const char *s, struct node *np)
282 {
283 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
284 	if (np->t != T_NUM && np->t != T_FUNC)
285 		outfl(O_ERR, np->file, np->line,
286 		    "%s %s property must be a number or function",
287 		    ptree_nodetype2str(t), s);
288 	return (1);
289 }
290 
291 static int
292 check_fru_asru(enum nodetype t, const char *s, struct node *np)
293 {
294 	ASSERT(s != NULL);
295 
296 	/* make sure it is a node type T_NAME? */
297 	if (np->t == T_NAME) {
298 		if (s == L_ASRU) {
299 			if (tree_name2np_lut_lookup_name(ASRUs, np) == NULL)
300 				outfl(O_ERR, np->file, np->line,
301 				    "ASRU property contains undeclared asru");
302 		} else if (s == L_FRU) {
303 			if (tree_name2np_lut_lookup_name(FRUs, np) == NULL)
304 				outfl(O_ERR, np->file, np->line,
305 				    "FRU property contains undeclared fru");
306 		} else {
307 			outfl(O_ERR, np->file, np->line,
308 			    "illegal property name in %s declaration: %s",
309 			    ptree_nodetype2str(t), s);
310 		}
311 		check_type_iterator(np);
312 	} else
313 		outfl(O_ERR, np->file, np->line,
314 		    "illegal type used for %s property: %s",
315 		    s, ptree_nodetype2str(np->t));
316 	return (1);
317 }
318 
319 static int
320 check_engine(enum nodetype t, const char *s, struct node *np)
321 {
322 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
323 	if (np->t != T_EVENT)
324 		outfl(O_ERR, np->file, np->line,
325 		    "%s %s property must be an engine name "
326 		    "(i.e. serd.x or serd.x@a/b)",
327 		    ptree_nodetype2str(t), s);
328 
329 	return (1);
330 }
331 
332 static int
333 check_count(enum nodetype t, const char *s, struct node *np)
334 {
335 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
336 	if (np->t != T_EVENT)
337 		outfl(O_ERR, np->file, np->line,
338 		    "%s %s property must be an engine name "
339 		    "(i.e. stat.x or stat.x@a/b)",
340 		    ptree_nodetype2str(t), s);
341 
342 	/* XXX confirm engine has been declared */
343 	return (1);
344 }
345 
346 static int
347 check_timeval(enum nodetype t, const char *s, struct node *np)
348 {
349 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
350 	if (np->t != T_TIMEVAL)
351 		outfl(O_ERR, np->file, np->line,
352 		    "%s %s property must be a number with time units",
353 		    ptree_nodetype2str(t), s);
354 	return (1);
355 }
356 
357 static int
358 check_id(enum nodetype t, const char *s, struct node *np)
359 {
360 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
361 	if (np->t != T_NAME || np->u.name.next || np->u.name.child)
362 		outfl(O_ERR, np->file, np->line,
363 		    "%s %s property must be simple name",
364 		    ptree_nodetype2str(t), s);
365 	return (1);
366 }
367 
368 static int
369 check_serd_method(enum nodetype t, const char *s, struct node *np)
370 {
371 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
372 	if (np->t != T_NAME || np->u.name.next || np->u.name.child ||
373 	    (np->u.name.s != L_volatile &&
374 	    np->u.name.s != L_persistent))
375 		outfl(O_ERR, np->file, np->line,
376 		    "%s %s property must be \"volatile\" or \"persistent\"",
377 		    ptree_nodetype2str(t), s);
378 	return (1);
379 }
380 
381 static int
382 check_serd_id(enum nodetype t, const char *s, struct node *np)
383 {
384 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
385 	if (np->t != T_GLOBID)
386 		outfl(O_ERR, np->file, np->line,
387 		    "%s %s property must be a global ID",
388 		    ptree_nodetype2str(t), s);
389 	return (1);
390 }
391 
392 void
393 check_stmt_required_properties(struct node *stmtnp)
394 {
395 	struct lut *lutp = stmtnp->u.stmt.lutp;
396 	struct node *np = stmtnp->u.stmt.np;
397 	int i;
398 
399 	for (i = 0; Allowednames[i].t; i++)
400 		if (stmtnp->t == Allowednames[i].t &&
401 		    Allowednames[i].required &&
402 		    tree_s2np_lut_lookup(lutp, Allowednames[i].name) == NULL)
403 			outfl(Allowednames[i].outflags,
404 			    np->file, np->line,
405 			    "%s statement missing property: %s",
406 			    ptree_nodetype2str(stmtnp->t),
407 			    Allowednames[i].name);
408 }
409 
410 void
411 check_stmt_allowed_properties(enum nodetype t,
412     struct node *nvpairnp, struct lut *lutp)
413 {
414 	int i;
415 	const char *s = nvpairnp->u.expr.left->u.name.s;
416 	struct node *np;
417 
418 	for (i = 0; Allowednames[i].t; i++)
419 		if (t == Allowednames[i].t && Allowednames[i].name == NULL) {
420 			/* NULL name means just call checker */
421 			(*Allowednames[i].checker)(t, s,
422 			    nvpairnp->u.expr.right);
423 			return;
424 		} else if (t == Allowednames[i].t && s == Allowednames[i].name)
425 			break;
426 	if (Allowednames[i].name == NULL)
427 		outfl(O_ERR, nvpairnp->file, nvpairnp->line,
428 		    "illegal property name in %s declaration: %s",
429 		    ptree_nodetype2str(t), s);
430 	else if ((np = tree_s2np_lut_lookup(lutp, s)) != NULL) {
431 		/*
432 		 * redeclaring prop is allowed if value is the same
433 		 */
434 		if (np->t != nvpairnp->u.expr.right->t)
435 			outfl(O_ERR, nvpairnp->file, nvpairnp->line,
436 			    "property redeclared (with differnt type) "
437 			    "in %s declaration: %s",
438 			    ptree_nodetype2str(t), s);
439 		switch (np->t) {
440 			case T_NUM:
441 			case T_TIMEVAL:
442 				if (np->u.ull == nvpairnp->u.expr.right->u.ull)
443 					return;
444 				break;
445 
446 			case T_NAME:
447 				if (tree_namecmp(np,
448 				    nvpairnp->u.expr.right) == 0)
449 					return;
450 				break;
451 
452 			case T_EVENT:
453 				if (tree_eventcmp(np,
454 				    nvpairnp->u.expr.right) == 0)
455 					return;
456 				break;
457 
458 			default:
459 				outfl(O_ERR, nvpairnp->file, nvpairnp->line,
460 				    "value for property \"%s\" is an "
461 				    "invalid type: %s",
462 				    nvpairnp->u.expr.left->u.name.s,
463 				    ptree_nodetype2str(np->t));
464 				return;
465 		}
466 		outfl(O_ERR, nvpairnp->file, nvpairnp->line,
467 		    "property redeclared in %s declaration: %s",
468 		    ptree_nodetype2str(t), s);
469 	} else
470 		(*Allowednames[i].checker)(t, s, nvpairnp->u.expr.right);
471 }
472 
473 void
474 check_propnames(enum nodetype t, struct node *np, int from, int to)
475 {
476 	struct node *dnp;
477 	struct lut *lutp;
478 
479 	ASSERT(np != NULL);
480 	ASSERTinfo(np->t == T_EVENT || np->t == T_LIST || np->t == T_ARROW,
481 	    ptree_nodetype2str(np->t));
482 
483 	if (np->t == T_EVENT) {
484 		switch (np->u.event.ename->u.name.t) {
485 		case N_UNSPEC:
486 			outfl(O_ERR, np->file, np->line,
487 			    "name in %s statement must begin with "
488 			    "type (example: \"error.\")",
489 			    ptree_nodetype2str(t));
490 			return;
491 		case N_FAULT:
492 			lutp = Faults;
493 			if (to) {
494 				outfl(O_ERR, np->file, np->line,
495 				    "%s has fault on right side of \"->\"",
496 				    ptree_nodetype2str(t));
497 				return;
498 			}
499 			if (!from) {
500 				outfl(O_DIE, np->file, np->line,
501 				    "internal error: %s has fault without "
502 				    "from flag",
503 				    ptree_nodetype2str(t));
504 			}
505 			break;
506 		case N_UPSET:
507 			lutp = Upsets;
508 			if (to) {
509 				outfl(O_ERR, np->file, np->line,
510 				    "%s has upset on right side of \"->\"",
511 				    ptree_nodetype2str(t));
512 				return;
513 			}
514 			if (!from)
515 				outfl(O_DIE, np->file, np->line,
516 				    "internal error: %s has upset without "
517 				    "from flag",
518 				    ptree_nodetype2str(t));
519 			break;
520 		case N_DEFECT:
521 			lutp = Defects;
522 			if (to) {
523 				outfl(O_ERR, np->file, np->line,
524 				    "%s has defect on right side of \"->\"",
525 				    ptree_nodetype2str(t));
526 				return;
527 			}
528 			if (!from) {
529 				outfl(O_DIE, np->file, np->line,
530 				    "internal error: %s has defect without "
531 				    "from flag",
532 				    ptree_nodetype2str(t));
533 			}
534 			break;
535 		case N_ERROR:
536 			lutp = Errors;
537 			if (!from && !to)
538 				outfl(O_DIE, np->file, np->line,
539 				    "%s has error without from or to flags",
540 				    ptree_nodetype2str(t));
541 			break;
542 		case N_EREPORT:
543 			lutp = Ereports;
544 			if (from) {
545 				outfl(O_ERR, np->file, np->line,
546 				    "%s has report on left side of \"->\"",
547 				    ptree_nodetype2str(t));
548 				return;
549 			}
550 			if (!to)
551 				outfl(O_DIE, np->file, np->line,
552 				    "internal error: %s has report without "
553 				    "to flag",
554 				    ptree_nodetype2str(t));
555 			break;
556 		default:
557 			outfl(O_DIE, np->file, np->line,
558 			    "internal error: check_propnames: "
559 			    "unexpected type: %d", np->u.name.t);
560 		}
561 
562 		if ((dnp = tree_event2np_lut_lookup(lutp, np)) == NULL) {
563 			outfl(O_ERR, np->file, np->line,
564 			    "%s statement contains undeclared event",
565 			    ptree_nodetype2str(t));
566 		} else
567 			dnp->u.stmt.flags |= STMT_REF;
568 		np->u.event.declp = dnp;
569 	} else if (np->t == T_LIST) {
570 		check_propnames(t, np->u.expr.left, from, to);
571 		check_propnames(t, np->u.expr.right, from, to);
572 	} else if (np->t == T_ARROW) {
573 		check_propnames(t, np->u.arrow.lhs, 1, to);
574 		check_propnames(t, np->u.arrow.rhs, from, 1);
575 	}
576 }
577 
578 static struct lut *
579 record_iterators(struct node *np, struct lut *ex)
580 {
581 	if (np == NULL)
582 		return (ex);
583 
584 	switch (np->t) {
585 	case T_ARROW:
586 		ex = record_iterators(np->u.arrow.lhs, ex);
587 		ex = record_iterators(np->u.arrow.rhs, ex);
588 		break;
589 
590 	case T_LIST:
591 		ex = record_iterators(np->u.expr.left, ex);
592 		ex = record_iterators(np->u.expr.right, ex);
593 		break;
594 
595 	case T_EVENT:
596 		ex = record_iterators(np->u.event.epname, ex);
597 		break;
598 
599 	case T_NAME:
600 		if (np->u.name.child && np->u.name.child->t == T_NAME)
601 			ex = lut_add(ex, (void *) np->u.name.child->u.name.s,
602 			    (void *) np, NULL);
603 		ex = record_iterators(np->u.name.next, ex);
604 		break;
605 
606 	default:
607 		outfl(O_DIE, np->file, np->line,
608 		    "record_iterators: internal error: unexpected type: %s",
609 		    ptree_nodetype2str(np->t));
610 	}
611 
612 	return (ex);
613 }
614 
615 void
616 check_exprscope(struct node *np, struct lut *ex)
617 {
618 	if (np == NULL)
619 		return;
620 
621 	switch (np->t) {
622 	case T_EVENT:
623 		check_exprscope(np->u.event.eexprlist, ex);
624 		break;
625 
626 	case T_ARROW:
627 		check_exprscope(np->u.arrow.lhs, ex);
628 		check_exprscope(np->u.arrow.rhs, ex);
629 		break;
630 
631 	case T_NAME:
632 		if (np->u.name.child && np->u.name.child->t == T_NAME) {
633 			if (lut_lookup(ex,
634 			    (void *) np->u.name.child->u.name.s, NULL) == NULL)
635 				outfl(O_ERR, np->file, np->line,
636 				    "constraint contains undefined"
637 				    " iterator: %s",
638 				    np->u.name.child->u.name.s);
639 		}
640 		check_exprscope(np->u.name.next, ex);
641 		break;
642 
643 	case T_QUOTE:
644 	case T_GLOBID:
645 		break;
646 
647 	case T_ASSIGN:
648 	case T_NE:
649 	case T_EQ:
650 	case T_LIST:
651 	case T_AND:
652 	case T_OR:
653 	case T_NOT:
654 	case T_ADD:
655 	case T_SUB:
656 	case T_MUL:
657 	case T_DIV:
658 	case T_MOD:
659 	case T_LT:
660 	case T_LE:
661 	case T_GT:
662 	case T_GE:
663 	case T_BITAND:
664 	case T_BITOR:
665 	case T_BITXOR:
666 	case T_BITNOT:
667 	case T_LSHIFT:
668 	case T_RSHIFT:
669 	case T_CONDIF:
670 	case T_CONDELSE:
671 		check_exprscope(np->u.expr.left, ex);
672 		check_exprscope(np->u.expr.right, ex);
673 		break;
674 
675 	case T_FUNC:
676 		check_exprscope(np->u.func.arglist, ex);
677 		break;
678 
679 	case T_NUM:
680 	case T_TIMEVAL:
681 		break;
682 
683 	default:
684 		outfl(O_DIE, np->file, np->line,
685 		    "check_exprscope: internal error: unexpected type: %s",
686 		    ptree_nodetype2str(np->t));
687 	}
688 }
689 
690 /*
691  * check_propscope -- check constraints for out of scope variable refs
692  */
693 void
694 check_propscope(struct node *np)
695 {
696 	struct lut *ex;
697 
698 	ex = record_iterators(np, NULL);
699 	check_exprscope(np, ex);
700 	lut_free(ex, NULL, NULL);
701 }
702 
703 /*
704  * check_upset_engine -- validate the engine property in an upset statement
705  *
706  * we do this after the full parse tree has been constructed rather than while
707  * building the parse tree because it is inconvenient for the user if we
708  * require SERD engines to be declared before used in an upset "engine"
709  * property.
710  */
711 
712 /*ARGSUSED*/
713 void
714 check_upset_engine(struct node *lhs, struct node *rhs, void *arg)
715 {
716 	enum nodetype t = (enum nodetype)arg;
717 	struct node *engnp;
718 	struct node *declp;
719 
720 	ASSERTeq(rhs->t, t, ptree_nodetype2str);
721 
722 	if ((engnp = tree_s2np_lut_lookup(rhs->u.stmt.lutp, L_engine)) == NULL)
723 		return;
724 
725 	ASSERT(engnp->t == T_EVENT);
726 
727 	if ((declp = tree_event2np_lut_lookup(SERDs, engnp)) == NULL) {
728 		outfl(O_ERR, engnp->file, engnp->line,
729 		    "%s %s property contains undeclared name",
730 		    ptree_nodetype2str(t), L_engine);
731 		return;
732 	}
733 	engnp->u.event.declp = declp;
734 }
735 
736 /*
737  * check_refcount -- see if declared names are used
738  *
739  * this is run after the entire parse tree is constructed, so a refcount
740  * of zero means the name has been declared but otherwise not used.
741  */
742 
743 void
744 check_refcount(struct node *lhs, struct node *rhs, void *arg)
745 {
746 	enum nodetype t = (enum nodetype)arg;
747 
748 	ASSERTeq(rhs->t, t, ptree_nodetype2str);
749 
750 	if (rhs->u.stmt.flags & STMT_REF)
751 		return;
752 
753 	outfl(O_WARN|O_NONL, rhs->file, rhs->line,
754 	    "%s name declared but not used: ", ptree_nodetype2str(t));
755 	ptree_name(O_WARN|O_NONL, lhs);
756 	out(O_WARN, NULL);
757 }
758 
759 /*
760  * set check_cycle_warninglevel only for val >= 0
761  */
762 int
763 check_cycle_level(long long val)
764 {
765 	static int check_cycle_warninglevel = -1;
766 
767 	if (val == 0)
768 		check_cycle_warninglevel = 0;
769 	else if (val > 0)
770 		check_cycle_warninglevel = 1;
771 
772 	return (check_cycle_warninglevel);
773 }
774 
775 /*
776  * check_cycle -- see props from an error have cycles
777  *
778  * this is run after the entire parse tree is constructed, for
779  * each error that has been declared.
780  */
781 
782 /*ARGSUSED*/
783 void
784 check_cycle(struct node *lhs, struct node *rhs, void *arg)
785 {
786 	struct node *np;
787 
788 	ASSERTeq(rhs->t, T_ERROR, ptree_nodetype2str);
789 
790 	if (rhs->u.stmt.flags & STMT_CYCLE)
791 		return;		/* already reported this cycle */
792 
793 	if (rhs->u.stmt.flags & STMT_CYMARK) {
794 #ifdef ESC
795 		int warninglevel;
796 
797 		warninglevel = check_cycle_level(-1);
798 		if (warninglevel <= 0) {
799 			int olevel = O_ERR;
800 
801 			if (warninglevel == 0)
802 				olevel = O_WARN;
803 
804 			out(olevel|O_NONL, "cycle in propagation tree: ");
805 			ptree_name(olevel|O_NONL, rhs->u.stmt.np);
806 			out(olevel, NULL);
807 		}
808 #endif /* ESC */
809 
810 		rhs->u.stmt.flags |= STMT_CYCLE;
811 	}
812 
813 	rhs->u.stmt.flags |= STMT_CYMARK;
814 
815 	/* for each propagation */
816 	for (np = Props; np; np = np->u.stmt.next)
817 		check_cycle_lhs(rhs, np->u.stmt.np);
818 
819 	rhs->u.stmt.flags &= ~STMT_CYMARK;
820 }
821 
822 /*
823  * check_cycle_lhs -- find the lhs of an arrow for cycle checking
824  */
825 
826 static void
827 check_cycle_lhs(struct node *stmtnp, struct node *arrow)
828 {
829 	struct node *trylhs;
830 	struct node *tryrhs;
831 
832 	/* handle cascaded arrows */
833 	switch (arrow->u.arrow.lhs->t) {
834 	case T_ARROW:
835 		/* first recurse left */
836 		check_cycle_lhs(stmtnp, arrow->u.arrow.lhs);
837 
838 		/*
839 		 * return if there's a list of events internal to
840 		 * cascaded props (which is not allowed)
841 		 */
842 		if (arrow->u.arrow.lhs->u.arrow.rhs->t != T_EVENT)
843 			return;
844 
845 		/* then try this arrow (thing cascaded *to*) */
846 		trylhs = arrow->u.arrow.lhs->u.arrow.rhs;
847 		tryrhs = arrow->u.arrow.rhs;
848 		break;
849 
850 	case T_EVENT:
851 	case T_LIST:
852 		trylhs = arrow->u.arrow.lhs;
853 		tryrhs = arrow->u.arrow.rhs;
854 		break;
855 
856 	default:
857 		out(O_DIE, "lhs: unexpected type: %s",
858 		    ptree_nodetype2str(arrow->u.arrow.lhs->t));
859 		/*NOTREACHED*/
860 	}
861 
862 	check_cycle_lhs_try(stmtnp, trylhs, tryrhs);
863 }
864 
865 /*
866  * check_cycle_lhs_try -- try matching an event name on lhs of an arrow
867  */
868 
869 static void
870 check_cycle_lhs_try(struct node *stmtnp, struct node *lhs, struct node *rhs)
871 {
872 	if (lhs->t == T_LIST) {
873 		check_cycle_lhs_try(stmtnp, lhs->u.expr.left, rhs);
874 		check_cycle_lhs_try(stmtnp, lhs->u.expr.right, rhs);
875 		return;
876 	}
877 
878 	ASSERT(lhs->t == T_EVENT);
879 
880 	if (tree_eventcmp(stmtnp->u.stmt.np, lhs) != 0)
881 		return;		/* no match */
882 
883 	check_cycle_rhs(rhs);
884 }
885 
886 /*
887  * check_cycle_rhs -- foreach error on rhs, see if we cycle to a marked error
888  */
889 
890 static void
891 check_cycle_rhs(struct node *rhs)
892 {
893 	struct node *dnp;
894 
895 	if (rhs->t == T_LIST) {
896 		check_cycle_rhs(rhs->u.expr.left);
897 		check_cycle_rhs(rhs->u.expr.right);
898 		return;
899 	}
900 
901 	ASSERT(rhs->t == T_EVENT);
902 
903 	if (rhs->u.event.ename->u.name.t != N_ERROR)
904 		return;
905 
906 	if ((dnp = tree_event2np_lut_lookup(Errors, rhs)) == NULL) {
907 		outfl(O_ERR|O_NONL,
908 		    rhs->file, rhs->line,
909 		    "unexpected undeclared event during cycle check");
910 		ptree_name(O_ERR|O_NONL, rhs);
911 		out(O_ERR, NULL);
912 		return;
913 	}
914 	check_cycle(NULL, dnp, 0);
915 }
916 
917 /*
918  * Force iterators to be simple names, expressions, or numbers
919  */
920 void
921 check_name_iterator(struct node *np)
922 {
923 	if (np->u.name.child->t != T_NUM &&
924 	    np->u.name.child->t != T_NAME &&
925 	    np->u.name.child->t != T_CONDIF &&
926 	    np->u.name.child->t != T_SUB &&
927 	    np->u.name.child->t != T_ADD &&
928 	    np->u.name.child->t != T_MUL &&
929 	    np->u.name.child->t != T_DIV &&
930 	    np->u.name.child->t != T_MOD &&
931 	    np->u.name.child->t != T_LSHIFT &&
932 	    np->u.name.child->t != T_RSHIFT) {
933 		outfl(O_ERR|O_NONL, np->file, np->line,
934 		"invalid iterator: ");
935 		ptree_name_iter(O_ERR|O_NONL, np);
936 		out(O_ERR, NULL);
937 	}
938 }
939 
940 /*
941  * Iterators on a declaration may only be implicit
942  */
943 void
944 check_type_iterator(struct node *np)
945 {
946 	while (np != NULL) {
947 		if (np->t == T_EVENT) {
948 			np = np->u.event.epname;
949 		} else if (np->t == T_NAME) {
950 			if (np->u.name.child != NULL &&
951 			    np->u.name.child->t != T_NUM) {
952 				outfl(O_ERR|O_NONL, np->file, np->line,
953 				    "explicit iterators disallowed "
954 				    "in declarations: ");
955 				ptree_name_iter(O_ERR|O_NONL, np);
956 				out(O_ERR, NULL);
957 			}
958 			np = np->u.name.next;
959 		} else {
960 			break;
961 		}
962 	}
963 }
964 
965 void
966 check_func(struct node *np)
967 {
968 	struct node *arglist = np->u.func.arglist;
969 
970 	ASSERTinfo(np->t == T_FUNC, ptree_nodetype2str(np->t));
971 
972 	if (np->u.func.s == L_within) {
973 		switch (arglist->t) {
974 		case T_NUM:
975 			if (arglist->u.ull != 0ULL) {
976 				outfl(O_ERR, arglist->file, arglist->line,
977 				    "parameter of within must be 0"
978 				    ", \"infinity\" or a time value.");
979 			}
980 			break;
981 
982 		case T_NAME:
983 			if (arglist->u.name.s != L_infinity) {
984 				outfl(O_ERR, arglist->file, arglist->line,
985 				    "parameter of within must be 0"
986 				    ", \"infinity\" or a time value.");
987 			}
988 			break;
989 
990 		case T_LIST:
991 			/*
992 			 * if two parameters, the left or min must be
993 			 * either T_NUM or T_TIMEVAL
994 			 */
995 			if (arglist->u.expr.left->t != T_NUM &&
996 			    arglist->u.expr.left->t != T_TIMEVAL) {
997 				outfl(O_ERR, arglist->file, arglist->line,
998 				    "first parameter of within must be"
999 				    " either a time value or zero.");
1000 			}
1001 
1002 			/*
1003 			 * if two parameters, the right or max must
1004 			 * be either T_NUM, T_NAME or T_TIMEVAL
1005 			 */
1006 			if (arglist->u.expr.right->t != T_NUM &&
1007 			    arglist->u.expr.right->t != T_TIMEVAL &&
1008 			    arglist->u.expr.right->t != T_NAME) {
1009 				outfl(O_ERR, arglist->file, arglist->line,
1010 				    "second parameter of within must "
1011 				    "be 0, \"infinity\" or time value.");
1012 			}
1013 
1014 			/*
1015 			 * if right or left is a T_NUM it must
1016 			 * be zero
1017 			 */
1018 			if ((arglist->u.expr.left->t == T_NUM) &&
1019 			    (arglist->u.expr.left->u.ull != 0ULL)) {
1020 				outfl(O_ERR, arglist->file, arglist->line,
1021 				    "within parameter must be "
1022 				    "0 or a time value.");
1023 			}
1024 			if ((arglist->u.expr.right->t == T_NUM) &&
1025 			    (arglist->u.expr.right->u.ull != 0ULL)) {
1026 				outfl(O_ERR, arglist->file, arglist->line,
1027 				    "within parameter must be "
1028 				    "0 or a time value.");
1029 			}
1030 
1031 			/* if right is a T_NAME it must be "infinity" */
1032 			if ((arglist->u.expr.right->t == T_NAME) &&
1033 			    (arglist->u.expr.right->u.name.s != L_infinity)) {
1034 				outfl(O_ERR, arglist->file, arglist->line,
1035 				    "\"infinity\" is the only "
1036 				    "valid name for within parameter.");
1037 			}
1038 
1039 			/*
1040 			 * the first parameter [min] must not be greater
1041 			 * than the second parameter [max].
1042 			 */
1043 			if (arglist->u.expr.left->u.ull >
1044 			    arglist->u.expr.right->u.ull) {
1045 				outfl(O_ERR, arglist->file, arglist->line,
1046 				    "the first value (min) of"
1047 				    " within must be less than"
1048 				    " the second (max) value");
1049 			}
1050 			break;
1051 
1052 		case T_TIMEVAL:
1053 			break; /* no restrictions on T_TIMEVAL */
1054 
1055 		default:
1056 			outfl(O_ERR, arglist->file, arglist->line,
1057 			    "parameter of within must be 0"
1058 			    ", \"infinity\" or a time value.");
1059 		}
1060 	} else if (np->u.func.s == L_call) {
1061 		if (arglist->t != T_QUOTE &&
1062 		    arglist->t != T_LIST &&
1063 		    arglist->t != T_GLOBID &&
1064 		    arglist->t != T_CONDIF &&
1065 		    arglist->t != T_LIST &&
1066 		    arglist->t != T_FUNC)
1067 			outfl(O_ERR, arglist->file, arglist->line,
1068 			    "invalid first argument to call()");
1069 	} else if (np->u.func.s == L_fru) {
1070 		if (arglist->t != T_NAME)
1071 			outfl(O_ERR, arglist->file, arglist->line,
1072 			    "argument to fru() must be a path");
1073 	} else if (np->u.func.s == L_asru) {
1074 		if (arglist->t != T_NAME)
1075 			outfl(O_ERR, arglist->file, arglist->line,
1076 			    "argument to asru() must be a path");
1077 	} else if (np->u.func.s == L_is_connected ||
1078 	    np->u.func.s == L_is_under) {
1079 		if (arglist->t == T_LIST &&
1080 		    (arglist->u.expr.left->t == T_NAME ||
1081 		    (arglist->u.expr.left->t == T_FUNC &&
1082 		    (arglist->u.expr.left->u.func.s == L_fru ||
1083 		    arglist->u.expr.left->u.func.s == L_asru))) &&
1084 		    (arglist->u.expr.right->t == T_NAME ||
1085 		    (arglist->u.expr.right->t == T_FUNC &&
1086 		    (arglist->u.expr.right->u.func.s == L_fru ||
1087 		    arglist->u.expr.right->u.func.s == L_asru)))) {
1088 			if (arglist->u.expr.left->t == T_FUNC)
1089 				check_func(arglist->u.expr.left);
1090 			if (arglist->u.expr.right->t == T_FUNC)
1091 				check_func(arglist->u.expr.right);
1092 		} else {
1093 			outfl(O_ERR, arglist->file, arglist->line,
1094 			    "%s() must have paths or calls to "
1095 			    "fru() and/or asru() as arguments",
1096 			    np->u.func.s);
1097 		}
1098 	} else if (np->u.func.s == L_is_on) {
1099 		if (arglist->t == T_NAME ||
1100 		    (arglist->t == T_FUNC &&
1101 		    (arglist->u.func.s == L_fru ||
1102 		    arglist->u.func.s == L_asru))) {
1103 			if (arglist->t == T_FUNC)
1104 				check_func(arglist);
1105 		} else {
1106 			outfl(O_ERR, arglist->file, arglist->line,
1107 			    "argument to is_on() must be a path or a call to "
1108 			    "fru() or asru()");
1109 		}
1110 	} else if (np->u.func.s == L_is_present) {
1111 		if (arglist->t == T_NAME ||
1112 		    (arglist->t == T_FUNC &&
1113 		    (arglist->u.func.s == L_fru ||
1114 		    arglist->u.func.s == L_asru))) {
1115 			if (arglist->t == T_FUNC)
1116 				check_func(arglist);
1117 		} else {
1118 			outfl(O_ERR, arglist->file, arglist->line,
1119 			    "argument to is_present() must be a path or a call "
1120 			    "to fru() or asru()");
1121 		}
1122 	} else if (np->u.func.s == L_has_fault) {
1123 		if (arglist->t == T_LIST &&
1124 		    (arglist->u.expr.left->t == T_NAME ||
1125 		    (arglist->u.expr.left->t == T_FUNC &&
1126 		    (arglist->u.expr.left->u.func.s == L_fru ||
1127 		    arglist->u.expr.left->u.func.s == L_asru))) &&
1128 		    arglist->u.expr.right->t == T_QUOTE) {
1129 			if (arglist->u.expr.left->t == T_FUNC)
1130 				check_func(arglist->u.expr.left);
1131 		} else {
1132 			outfl(O_ERR, arglist->file, arglist->line,
1133 			    "%s() must have path or call to "
1134 			    "fru() and/or asru() as first argument; "
1135 			    "second argument must be a string", np->u.func.s);
1136 		}
1137 	} else if (np->u.func.s == L_is_type) {
1138 		if (arglist->t == T_NAME ||
1139 		    (arglist->t == T_FUNC &&
1140 		    (arglist->u.func.s == L_fru ||
1141 		    arglist->u.func.s == L_asru))) {
1142 			if (arglist->t == T_FUNC)
1143 				check_func(arglist);
1144 		} else {
1145 			outfl(O_ERR, arglist->file, arglist->line,
1146 			    "argument to is_type() must be a path or a call to "
1147 			    "fru() or asru()");
1148 		}
1149 	} else if (np->u.func.s == L_confcall) {
1150 		if (arglist->t != T_QUOTE &&
1151 		    (arglist->t != T_LIST ||
1152 		    arglist->u.expr.left->t != T_QUOTE))
1153 			outfl(O_ERR, arglist->file, arglist->line,
1154 			    "confcall(): first argument must be a string "
1155 			    "(the name of the operation)");
1156 	} else if (np->u.func.s == L_confprop ||
1157 	    np->u.func.s == L_confprop_defined) {
1158 		if (arglist->t == T_LIST &&
1159 		    (arglist->u.expr.left->t == T_NAME ||
1160 		    (arglist->u.expr.left->t == T_FUNC &&
1161 		    (arglist->u.expr.left->u.func.s == L_fru ||
1162 		    arglist->u.expr.left->u.func.s == L_asru))) &&
1163 		    arglist->u.expr.right->t == T_QUOTE) {
1164 			if (arglist->u.expr.left->t == T_FUNC)
1165 				check_func(arglist->u.expr.left);
1166 		} else {
1167 			outfl(O_ERR, arglist->file, arglist->line,
1168 			    "%s(): first argument must be a path or a call to "
1169 			    "fru() or asru(); "
1170 			    "second argument must be a string", np->u.func.s);
1171 		}
1172 	} else if (np->u.func.s == L_count) {
1173 		if (arglist->t != T_EVENT) {
1174 			outfl(O_ERR, arglist->file, arglist->line,
1175 			    "count(): argument must be an engine name");
1176 		}
1177 	} else if (np->u.func.s == L_defined) {
1178 		if (arglist->t != T_GLOBID)
1179 			outfl(O_ERR, arglist->file, arglist->line,
1180 			    "argument to defined() must be a global");
1181 	} else if (np->u.func.s == L_payloadprop) {
1182 		if (arglist->t != T_QUOTE)
1183 			outfl(O_ERR, arglist->file, arglist->line,
1184 			    "argument to payloadprop() must be a string");
1185 	} else if (np->u.func.s == L_payloadprop_contains) {
1186 		if (arglist->t != T_LIST ||
1187 		    arglist->u.expr.left->t != T_QUOTE ||
1188 		    arglist->u.expr.right == NULL)
1189 			outfl(O_ERR, arglist->file, arglist->line,
1190 			    "args to payloadprop_contains(): must be a quoted "
1191 			    "string (property name) and an expression "
1192 			    "(to match)");
1193 	} else if (np->u.func.s == L_payloadprop_defined) {
1194 		if (arglist->t != T_QUOTE)
1195 			outfl(O_ERR, arglist->file, arglist->line,
1196 			    "arg to payloadprop_defined(): must be a quoted "
1197 			    "string");
1198 	} else if (np->u.func.s == L_setpayloadprop) {
1199 		if (arglist->t == T_LIST &&
1200 		    arglist->u.expr.left->t == T_QUOTE) {
1201 			if (arglist->u.expr.right->t == T_FUNC)
1202 				check_func(arglist->u.expr.right);
1203 		} else {
1204 			outfl(O_ERR, arglist->file, arglist->line,
1205 			    "setpayloadprop(): "
1206 			    "first arg must be a string, "
1207 			    "second arg a value");
1208 		}
1209 	} else if (np->u.func.s == L_setserdn || np->u.func.s == L_setserdt ||
1210 	    np->u.func.s == L_setserdsuffix || np->u.func.s ==
1211 	    L_setserdincrement) {
1212 		if (arglist->t == T_FUNC)
1213 			check_func(arglist);
1214 	} else if (np->u.func.s == L_envprop) {
1215 		if (arglist->t != T_QUOTE)
1216 			outfl(O_ERR, arglist->file, arglist->line,
1217 			    "argument to envprop() must be a string");
1218 	} else
1219 		outfl(O_WARN, np->file, np->line,
1220 		    "possible platform-specific function: %s",
1221 		    np->u.func.s);
1222 }
1223 
1224 void
1225 check_expr(struct node *np)
1226 {
1227 	ASSERT(np != NULL);
1228 
1229 	switch (np->t) {
1230 	case T_ASSIGN:
1231 		ASSERT(np->u.expr.left != NULL);
1232 		if (np->u.expr.left->t != T_GLOBID)
1233 			outfl(O_ERR, np->file, np->line,
1234 			    "assignment only allowed to globals (e.g. $a)");
1235 		break;
1236 	}
1237 }
1238 
1239 void
1240 check_event(struct node *np)
1241 {
1242 	ASSERT(np != NULL);
1243 	ASSERTinfo(np->t == T_EVENT, ptree_nodetype2str(np->t));
1244 
1245 	if (np->u.event.epname == NULL) {
1246 		outfl(O_ERR|O_NONL, np->file, np->line,
1247 		    "pathless events not allowed: ");
1248 		ptree_name(O_ERR|O_NONL, np->u.event.ename);
1249 		out(O_ERR, NULL);
1250 	}
1251 }
1252 
1253 /*
1254  * check for properties that are required on declarations. This
1255  * should be done after all declarations since they can be
1256  * redeclared with a different set of properties.
1257  */
1258 /*ARGSUSED*/
1259 void
1260 check_required_props(struct node *lhs, struct node *rhs, void *arg)
1261 {
1262 	ASSERTeq(rhs->t, (enum nodetype)arg, ptree_nodetype2str);
1263 
1264 	check_stmt_required_properties(rhs);
1265 }
1266 
1267 /*
1268  * check that cascading prop statements do not contain lists internally.
1269  * the first and last event lists in the cascading prop may be single
1270  * events or lists of events.
1271  */
1272 /*ARGSUSED*/
1273 void
1274 check_proplists(enum nodetype t, struct node *np)
1275 {
1276 	ASSERT(np->t == T_ARROW);
1277 	/*
1278 	 * not checking the right hand side of the top level prop
1279 	 * since it is the last part of the propagation and can be
1280 	 * an event or list of events
1281 	 */
1282 	check_proplists_lhs(t, np->u.arrow.lhs);
1283 }
1284 
1285 /*ARGSUSED*/
1286 static void
1287 check_proplists_lhs(enum nodetype t, struct node *lhs)
1288 {
1289 	if (lhs->t == T_ARROW) {
1290 		if (lhs->u.arrow.rhs->t == T_LIST) {
1291 			outfl(O_ERR, lhs->file, lhs->line,
1292 			    "lists are not allowed internally on cascading %s",
1293 			    (t == T_PROP) ? "propagations" : "masks");
1294 		}
1295 		check_proplists_lhs(t, lhs->u.arrow.lhs);
1296 	}
1297 }
1298