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", 1, 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, "action", 0, check_action, O_ERR },
77 	{ T_FAULT, "count", 0, check_count, O_ERR },
78 	{ T_UPSET, "engine", 0, check_engine, O_ERR },
79 	{ T_DEFECT, "FRU", 0, check_fru_asru, O_ERR },
80 	{ T_DEFECT, "ASRU", 0, check_fru_asru, O_ERR },
81 	{ T_EREPORT, "poller", 0, check_id, O_ERR },
82 	{ T_EREPORT, "delivery", 0, check_timeval, O_ERR },
83 	{ T_EREPORT, "discard_if_config_unknown", 0, check_num, 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, NULL) == NULL)
631 				outfl(O_ERR, np->file, np->line,
632 				    "constraint contains undefined"
633 				    " iterator: %s",
634 				    np->u.name.child->u.name.s);
635 		}
636 		check_exprscope(np->u.name.next, ex);
637 		break;
638 
639 	case T_QUOTE:
640 	case T_GLOBID:
641 		break;
642 
643 	case T_ASSIGN:
644 	case T_NE:
645 	case T_EQ:
646 	case T_LIST:
647 	case T_AND:
648 	case T_OR:
649 	case T_NOT:
650 	case T_ADD:
651 	case T_SUB:
652 	case T_MUL:
653 	case T_DIV:
654 	case T_MOD:
655 	case T_LT:
656 	case T_LE:
657 	case T_GT:
658 	case T_GE:
659 	case T_BITAND:
660 	case T_BITOR:
661 	case T_BITXOR:
662 	case T_BITNOT:
663 	case T_LSHIFT:
664 	case T_RSHIFT:
665 	case T_CONDIF:
666 	case T_CONDELSE:
667 		check_exprscope(np->u.expr.left, ex);
668 		check_exprscope(np->u.expr.right, ex);
669 		break;
670 
671 	case T_FUNC:
672 		check_exprscope(np->u.func.arglist, ex);
673 		break;
674 
675 	case T_NUM:
676 	case T_TIMEVAL:
677 		break;
678 
679 	default:
680 		outfl(O_DIE, np->file, np->line,
681 		    "check_exprscope: internal error: unexpected type: %s",
682 		    ptree_nodetype2str(np->t));
683 	}
684 }
685 
686 /*
687  * check_propscope -- check constraints for out of scope variable refs
688  */
689 void
690 check_propscope(struct node *np)
691 {
692 	struct lut *ex;
693 
694 	ex = record_iterators(np, NULL);
695 	check_exprscope(np, ex);
696 	lut_free(ex, NULL, NULL);
697 }
698 
699 /*
700  * check_upset_engine -- validate the engine property in an upset statement
701  *
702  * we do this after the full parse tree has been constructed rather than while
703  * building the parse tree because it is inconvenient for the user if we
704  * require SERD engines to be declared before used in an upset "engine"
705  * property.
706  */
707 
708 /*ARGSUSED*/
709 void
710 check_upset_engine(struct node *lhs, struct node *rhs, void *arg)
711 {
712 	enum nodetype t = (enum nodetype)arg;
713 	struct node *engnp;
714 	struct node *declp;
715 
716 	ASSERTeq(rhs->t, t, ptree_nodetype2str);
717 
718 	if ((engnp = tree_s2np_lut_lookup(rhs->u.stmt.lutp, L_engine)) == NULL)
719 		return;
720 
721 	ASSERT(engnp->t == T_EVENT);
722 
723 	if ((declp = tree_event2np_lut_lookup(SERDs, engnp)) == NULL) {
724 		outfl(O_ERR, engnp->file, engnp->line,
725 		    "%s %s property contains undeclared name",
726 		    ptree_nodetype2str(t), L_engine);
727 		return;
728 	}
729 	engnp->u.event.declp = declp;
730 }
731 
732 /*
733  * check_refcount -- see if declared names are used
734  *
735  * this is run after the entire parse tree is constructed, so a refcount
736  * of zero means the name has been declared but otherwise not used.
737  */
738 
739 void
740 check_refcount(struct node *lhs, struct node *rhs, void *arg)
741 {
742 	enum nodetype t = (enum nodetype)arg;
743 
744 	ASSERTeq(rhs->t, t, ptree_nodetype2str);
745 
746 	if (rhs->u.stmt.flags & STMT_REF)
747 		return;
748 
749 	outfl(O_WARN|O_NONL, rhs->file, rhs->line,
750 	    "%s name declared but not used: ", ptree_nodetype2str(t));
751 	ptree_name(O_WARN|O_NONL, lhs);
752 	out(O_WARN, NULL);
753 }
754 
755 /*
756  * set check_cycle_warninglevel only for val >= 0
757  */
758 int
759 check_cycle_level(long long val)
760 {
761 	static int check_cycle_warninglevel = -1;
762 
763 	if (val == 0)
764 		check_cycle_warninglevel = 0;
765 	else if (val > 0)
766 		check_cycle_warninglevel = 1;
767 
768 	return (check_cycle_warninglevel);
769 }
770 
771 /*
772  * check_cycle -- see props from an error have cycles
773  *
774  * this is run after the entire parse tree is constructed, for
775  * each error that has been declared.
776  */
777 
778 /*ARGSUSED*/
779 void
780 check_cycle(struct node *lhs, struct node *rhs, void *arg)
781 {
782 	struct node *np;
783 
784 	ASSERTeq(rhs->t, T_ERROR, ptree_nodetype2str);
785 
786 	if (rhs->u.stmt.flags & STMT_CYCLE)
787 		return;		/* already reported this cycle */
788 
789 	if (rhs->u.stmt.flags & STMT_CYMARK) {
790 #ifdef ESC
791 		int warninglevel;
792 
793 		warninglevel = check_cycle_level(-1);
794 		if (warninglevel <= 0) {
795 			int olevel = O_ERR;
796 
797 			if (warninglevel == 0)
798 				olevel = O_WARN;
799 
800 			out(olevel|O_NONL, "cycle in propagation tree: ");
801 			ptree_name(olevel|O_NONL, rhs->u.stmt.np);
802 			out(olevel, NULL);
803 		}
804 #endif /* ESC */
805 
806 		rhs->u.stmt.flags |= STMT_CYCLE;
807 	}
808 
809 	rhs->u.stmt.flags |= STMT_CYMARK;
810 
811 	/* for each propagation */
812 	for (np = Props; np; np = np->u.stmt.next)
813 		check_cycle_lhs(rhs, np->u.stmt.np);
814 
815 	rhs->u.stmt.flags &= ~STMT_CYMARK;
816 }
817 
818 /*
819  * check_cycle_lhs -- find the lhs of an arrow for cycle checking
820  */
821 
822 static void
823 check_cycle_lhs(struct node *stmtnp, struct node *arrow)
824 {
825 	struct node *trylhs;
826 	struct node *tryrhs;
827 
828 	/* handle cascaded arrows */
829 	switch (arrow->u.arrow.lhs->t) {
830 	case T_ARROW:
831 		/* first recurse left */
832 		check_cycle_lhs(stmtnp, arrow->u.arrow.lhs);
833 
834 		/*
835 		 * return if there's a list of events internal to
836 		 * cascaded props (which is not allowed)
837 		 */
838 		if (arrow->u.arrow.lhs->u.arrow.rhs->t != T_EVENT)
839 			return;
840 
841 		/* then try this arrow (thing cascaded *to*) */
842 		trylhs = arrow->u.arrow.lhs->u.arrow.rhs;
843 		tryrhs = arrow->u.arrow.rhs;
844 		break;
845 
846 	case T_EVENT:
847 	case T_LIST:
848 		trylhs = arrow->u.arrow.lhs;
849 		tryrhs = arrow->u.arrow.rhs;
850 		break;
851 
852 	default:
853 		out(O_DIE, "lhs: unexpected type: %s",
854 		    ptree_nodetype2str(arrow->u.arrow.lhs->t));
855 		/*NOTREACHED*/
856 	}
857 
858 	check_cycle_lhs_try(stmtnp, trylhs, tryrhs);
859 }
860 
861 /*
862  * check_cycle_lhs_try -- try matching an event name on lhs of an arrow
863  */
864 
865 static void
866 check_cycle_lhs_try(struct node *stmtnp, struct node *lhs, struct node *rhs)
867 {
868 	if (lhs->t == T_LIST) {
869 		check_cycle_lhs_try(stmtnp, lhs->u.expr.left, rhs);
870 		check_cycle_lhs_try(stmtnp, lhs->u.expr.right, rhs);
871 		return;
872 	}
873 
874 	ASSERT(lhs->t == T_EVENT);
875 
876 	if (tree_eventcmp(stmtnp->u.stmt.np, lhs) != 0)
877 		return;		/* no match */
878 
879 	check_cycle_rhs(rhs);
880 }
881 
882 /*
883  * check_cycle_rhs -- foreach error on rhs, see if we cycle to a marked error
884  */
885 
886 static void
887 check_cycle_rhs(struct node *rhs)
888 {
889 	struct node *dnp;
890 
891 	if (rhs->t == T_LIST) {
892 		check_cycle_rhs(rhs->u.expr.left);
893 		check_cycle_rhs(rhs->u.expr.right);
894 		return;
895 	}
896 
897 	ASSERT(rhs->t == T_EVENT);
898 
899 	if (rhs->u.event.ename->u.name.t != N_ERROR)
900 		return;
901 
902 	if ((dnp = tree_event2np_lut_lookup(Errors, rhs)) == NULL) {
903 		outfl(O_ERR|O_NONL,
904 		    rhs->file, rhs->line,
905 		    "unexpected undeclared event during cycle check");
906 		ptree_name(O_ERR|O_NONL, rhs);
907 		out(O_ERR, NULL);
908 		return;
909 	}
910 	check_cycle(NULL, dnp, 0);
911 }
912 
913 /*
914  * Force iterators to be simple names, expressions, or numbers
915  */
916 void
917 check_name_iterator(struct node *np)
918 {
919 	if (np->u.name.child->t != T_NUM &&
920 	    np->u.name.child->t != T_NAME &&
921 	    np->u.name.child->t != T_CONDIF &&
922 	    np->u.name.child->t != T_SUB &&
923 	    np->u.name.child->t != T_ADD &&
924 	    np->u.name.child->t != T_MUL &&
925 	    np->u.name.child->t != T_DIV &&
926 	    np->u.name.child->t != T_MOD &&
927 	    np->u.name.child->t != T_LSHIFT &&
928 	    np->u.name.child->t != T_RSHIFT) {
929 		outfl(O_ERR|O_NONL, np->file, np->line,
930 		"invalid iterator: ");
931 		ptree_name_iter(O_ERR|O_NONL, np);
932 		out(O_ERR, NULL);
933 	}
934 }
935 
936 /*
937  * Iterators on a declaration may only be implicit
938  */
939 void
940 check_type_iterator(struct node *np)
941 {
942 	while (np != NULL) {
943 		if (np->t == T_EVENT) {
944 			np = np->u.event.epname;
945 		} else if (np->t == T_NAME) {
946 			if (np->u.name.child != NULL &&
947 			    np->u.name.child->t != T_NUM) {
948 				outfl(O_ERR|O_NONL, np->file, np->line,
949 				    "explicit iterators disallowed "
950 				    "in declarations: ");
951 				ptree_name_iter(O_ERR|O_NONL, np);
952 				out(O_ERR, NULL);
953 			}
954 			np = np->u.name.next;
955 		} else {
956 			break;
957 		}
958 	}
959 }
960 
961 void
962 check_func(struct node *np)
963 {
964 	struct node *arglist = np->u.func.arglist;
965 
966 	ASSERTinfo(np->t == T_FUNC, ptree_nodetype2str(np->t));
967 
968 	if (np->u.func.s == L_within) {
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 			}
976 			break;
977 
978 		case T_NAME:
979 			if (arglist->u.name.s != L_infinity) {
980 				outfl(O_ERR, arglist->file, arglist->line,
981 				    "parameter of within must be 0"
982 				    ", \"infinity\" or a time value.");
983 			}
984 			break;
985 
986 		case T_LIST:
987 			/*
988 			 * if two parameters, the left or min must be
989 			 * either T_NUM or T_TIMEVAL
990 			 */
991 			if (arglist->u.expr.left->t != T_NUM &&
992 			    arglist->u.expr.left->t != T_TIMEVAL) {
993 				outfl(O_ERR, arglist->file, arglist->line,
994 				    "first parameter of within must be"
995 				    " either a time value or zero.");
996 			}
997 
998 			/*
999 			 * if two parameters, the right or max must
1000 			 * be either T_NUM, T_NAME or T_TIMEVAL
1001 			 */
1002 			if (arglist->u.expr.right->t != T_NUM &&
1003 			    arglist->u.expr.right->t != T_TIMEVAL &&
1004 			    arglist->u.expr.right->t != T_NAME) {
1005 				outfl(O_ERR, arglist->file, arglist->line,
1006 				    "second parameter of within must "
1007 				    "be 0, \"infinity\" or time value.");
1008 			}
1009 
1010 			/*
1011 			 * if right or left is a T_NUM it must
1012 			 * be zero
1013 			 */
1014 			if ((arglist->u.expr.left->t == T_NUM) &&
1015 			    (arglist->u.expr.left->u.ull != 0ULL)) {
1016 				outfl(O_ERR, arglist->file, arglist->line,
1017 				    "within parameter must be "
1018 				    "0 or a time value.");
1019 			}
1020 			if ((arglist->u.expr.right->t == T_NUM) &&
1021 			    (arglist->u.expr.right->u.ull != 0ULL)) {
1022 				outfl(O_ERR, arglist->file, arglist->line,
1023 				    "within parameter must be "
1024 				    "0 or a time value.");
1025 			}
1026 
1027 			/* if right is a T_NAME it must be "infinity" */
1028 			if ((arglist->u.expr.right->t == T_NAME) &&
1029 			    (arglist->u.expr.right->u.name.s != L_infinity)) {
1030 				outfl(O_ERR, arglist->file, arglist->line,
1031 				    "\"infinity\" is the only "
1032 				    "valid name for within parameter.");
1033 			}
1034 
1035 			/*
1036 			 * the first parameter [min] must not be greater
1037 			 * than the second parameter [max].
1038 			 */
1039 			if (arglist->u.expr.left->u.ull >
1040 			    arglist->u.expr.right->u.ull) {
1041 				outfl(O_ERR, arglist->file, arglist->line,
1042 				    "the first value (min) of"
1043 				    " within must be less than"
1044 				    " the second (max) value");
1045 			}
1046 			break;
1047 
1048 		case T_TIMEVAL:
1049 			break; /* no restrictions on T_TIMEVAL */
1050 
1051 		default:
1052 			outfl(O_ERR, arglist->file, arglist->line,
1053 			    "parameter of within must be 0"
1054 			    ", \"infinity\" or a time value.");
1055 		}
1056 	} else if (np->u.func.s == L_call) {
1057 		if (arglist->t != T_QUOTE &&
1058 		    arglist->t != T_LIST &&
1059 		    arglist->t != T_GLOBID &&
1060 		    arglist->t != T_CONDIF &&
1061 		    arglist->t != T_LIST &&
1062 		    arglist->t != T_FUNC)
1063 			outfl(O_ERR, arglist->file, arglist->line,
1064 			    "invalid first argument to call()");
1065 	} else if (np->u.func.s == L_fru) {
1066 		if (arglist->t != T_NAME)
1067 			outfl(O_ERR, arglist->file, arglist->line,
1068 			    "argument to fru() must be a path");
1069 	} else if (np->u.func.s == L_asru) {
1070 		if (arglist->t != T_NAME)
1071 			outfl(O_ERR, arglist->file, 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 (arglist->t == T_LIST &&
1076 		    (arglist->u.expr.left->t == T_NAME ||
1077 		    (arglist->u.expr.left->t == T_FUNC &&
1078 		    (arglist->u.expr.left->u.func.s == L_fru ||
1079 		    arglist->u.expr.left->u.func.s == L_asru))) &&
1080 		    (arglist->u.expr.right->t == T_NAME ||
1081 		    (arglist->u.expr.right->t == T_FUNC &&
1082 		    (arglist->u.expr.right->u.func.s == L_fru ||
1083 		    arglist->u.expr.right->u.func.s == L_asru)))) {
1084 			if (arglist->u.expr.left->t == T_FUNC)
1085 				check_func(arglist->u.expr.left);
1086 			if (arglist->u.expr.right->t == T_FUNC)
1087 				check_func(arglist->u.expr.right);
1088 		} else {
1089 			outfl(O_ERR, arglist->file, arglist->line,
1090 			    "%s() must have paths or calls to "
1091 			    "fru() and/or asru() as arguments",
1092 			    np->u.func.s);
1093 		}
1094 	} else if (np->u.func.s == L_is_on) {
1095 		if (arglist->t == T_FUNC &&
1096 		    (arglist->u.func.s == L_fru ||
1097 		    arglist->u.func.s == L_asru)) {
1098 			check_func(arglist);
1099 		} else {
1100 			outfl(O_ERR, arglist->file, arglist->line,
1101 			    "argument to is_on() must be a call to "
1102 			    "fru() or asru()");
1103 		}
1104 	} else if (np->u.func.s == L_is_present) {
1105 		if (arglist->t == T_FUNC &&
1106 		    (arglist->u.func.s == L_fru ||
1107 		    arglist->u.func.s == L_asru)) {
1108 			check_func(arglist);
1109 		} else {
1110 			outfl(O_ERR, arglist->file, arglist->line,
1111 			    "argument to is_present() must be a call to "
1112 			    "fru() or asru()");
1113 		}
1114 	} else if (np->u.func.s == L_is_type) {
1115 		if (arglist->t == T_FUNC &&
1116 		    (arglist->u.func.s == L_fru ||
1117 		    arglist->u.func.s == L_asru)) {
1118 			check_func(arglist);
1119 		} else {
1120 			outfl(O_ERR, arglist->file, arglist->line,
1121 			    "argument to is_type() must be a call to "
1122 			    "fru() or asru()");
1123 		}
1124 	} else if (np->u.func.s == L_confcall) {
1125 		if (arglist->t != T_QUOTE &&
1126 		    (arglist->t != T_LIST ||
1127 		    arglist->u.expr.left->t != T_QUOTE))
1128 			outfl(O_ERR, arglist->file, arglist->line,
1129 			    "confcall(): first argument must be a string "
1130 			    "(the name of the operation)");
1131 	} else if (np->u.func.s == L_confprop ||
1132 	    np->u.func.s == L_confprop_defined) {
1133 		if (arglist->t == T_LIST &&
1134 		    (arglist->u.expr.left->t == T_FUNC &&
1135 		    (arglist->u.expr.left->u.func.s == L_fru ||
1136 		    arglist->u.expr.left->u.func.s == L_asru)) &&
1137 		    arglist->u.expr.right->t == T_QUOTE) {
1138 			check_func(arglist->u.expr.left);
1139 		} else {
1140 			outfl(O_ERR, arglist->file, arglist->line,
1141 			    "%s(): first argument must be a call to "
1142 			    "fru() or asru(); "
1143 			    "second argument must be a string", np->u.func.s);
1144 		}
1145 	} else if (np->u.func.s == L_count) {
1146 		if (arglist->t != T_EVENT) {
1147 			outfl(O_ERR, arglist->file, arglist->line,
1148 			    "count(): argument must be an engine name");
1149 		}
1150 	} else if (np->u.func.s == L_defined) {
1151 		if (arglist->t != T_GLOBID)
1152 			outfl(O_ERR, arglist->file, arglist->line,
1153 			    "argument to defined() must be a global");
1154 	} else if (np->u.func.s == L_payloadprop) {
1155 		if (arglist->t != T_QUOTE)
1156 			outfl(O_ERR, arglist->file, arglist->line,
1157 			    "argument to payloadprop() must be a string");
1158 	} else if (np->u.func.s == L_payloadprop_contains) {
1159 		if (arglist->t != T_LIST ||
1160 		    arglist->u.expr.left->t != T_QUOTE ||
1161 		    arglist->u.expr.right == NULL)
1162 			outfl(O_ERR, arglist->file, arglist->line,
1163 			    "args to payloadprop_contains(): must be a quoted "
1164 			    "string (property name) and an expression "
1165 			    "(to match)");
1166 	} else if (np->u.func.s == L_payloadprop_defined) {
1167 		if (arglist->t != T_QUOTE)
1168 			outfl(O_ERR, arglist->file, arglist->line,
1169 			    "arg to payloadprop_defined(): must be a quoted "
1170 			    "string");
1171 	} else if (np->u.func.s == L_setpayloadprop) {
1172 		if (arglist->t == T_LIST &&
1173 		    arglist->u.expr.left->t == T_QUOTE) {
1174 			if (arglist->u.expr.right->t == T_FUNC)
1175 				check_func(arglist->u.expr.right);
1176 		} else {
1177 			outfl(O_ERR, arglist->file, arglist->line,
1178 			    "setpayloadprop(): "
1179 			    "first arg must be a string, "
1180 			    "second arg a value");
1181 		}
1182 	} else if (np->u.func.s == L_envprop) {
1183 		if (arglist->t != T_QUOTE)
1184 			outfl(O_ERR, arglist->file, arglist->line,
1185 			    "argument to envprop() must be a string");
1186 	} else
1187 		outfl(O_WARN, np->file, np->line,
1188 		    "possible platform-specific function: %s",
1189 		    np->u.func.s);
1190 }
1191 
1192 void
1193 check_expr(struct node *np)
1194 {
1195 	ASSERT(np != NULL);
1196 
1197 	switch (np->t) {
1198 	case T_ASSIGN:
1199 		ASSERT(np->u.expr.left != NULL);
1200 		if (np->u.expr.left->t != T_GLOBID)
1201 			outfl(O_ERR, np->file, np->line,
1202 			    "assignment only allowed to globals (e.g. $a)");
1203 		break;
1204 	}
1205 }
1206 
1207 void
1208 check_event(struct node *np)
1209 {
1210 	ASSERT(np != NULL);
1211 	ASSERTinfo(np->t == T_EVENT, ptree_nodetype2str(np->t));
1212 
1213 	if (np->u.event.epname == NULL) {
1214 		outfl(O_ERR|O_NONL, np->file, np->line,
1215 		    "pathless events not allowed: ");
1216 		ptree_name(O_ERR|O_NONL, np->u.event.ename);
1217 		out(O_ERR, NULL);
1218 	}
1219 }
1220 
1221 /*
1222  * check for properties that are required on declarations. This
1223  * should be done after all declarations since they can be
1224  * redeclared with a different set of properties.
1225  */
1226 /*ARGSUSED*/
1227 void
1228 check_required_props(struct node *lhs, struct node *rhs, void *arg)
1229 {
1230 	ASSERTeq(rhs->t, (enum nodetype)arg, ptree_nodetype2str);
1231 
1232 	check_stmt_required_properties(rhs);
1233 }
1234 
1235 /*
1236  * check that cascading prop statements do not contain lists internally.
1237  * the first and last event lists in the cascading prop may be single
1238  * events or lists of events.
1239  */
1240 /*ARGSUSED*/
1241 void
1242 check_proplists(enum nodetype t, struct node *np)
1243 {
1244 	ASSERT(np->t == T_ARROW);
1245 	/*
1246 	 * not checking the right hand side of the top level prop
1247 	 * since it is the last part of the propagation and can be
1248 	 * an event or list of events
1249 	 */
1250 	check_proplists_lhs(t, np->u.arrow.lhs);
1251 }
1252 
1253 /*ARGSUSED*/
1254 static void
1255 check_proplists_lhs(enum nodetype t, struct node *lhs)
1256 {
1257 	if (lhs->t == T_ARROW) {
1258 		if (lhs->u.arrow.rhs->t == T_LIST) {
1259 			outfl(O_ERR, lhs->file, lhs->line,
1260 			    "lists are not allowed internally on cascading %s",
1261 			    (t == T_PROP) ? "propagations" : "masks");
1262 		}
1263 		check_proplists_lhs(t, lhs->u.arrow.lhs);
1264 	}
1265 }
1266