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