xref: /illumos-gate/usr/src/cmd/fm/eversholt/common/check.c (revision 4fceebdf03eeac0d7c58a4f70cc19b00a8c40a73)
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 2006 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_SERD, "N", 1, check_num, O_ERR },
84 	{ T_SERD, "T", 1, check_timeval, O_ERR },
85 	{ T_SERD, "method", 1, check_serd_method, O_ERR },
86 	{ T_SERD, "trip", 1, check_reportlist, O_ERR },
87 	{ T_SERD, "FRU", 0, check_fru_asru, O_ERR },
88 	{ T_SERD, "id", 0, check_serd_id, O_ERR },
89 	{ T_ERROR, "ASRU", 0, check_fru_asru, O_ERR },
90 	{ T_CONFIG, NULL, 0, check_quote, O_ERR },
91 	{ 0, NULL, 0 },
92 };
93 
94 void
95 check_init(void)
96 {
97 	int i;
98 
99 	for (i = 0; Allowednames[i].t; i++)
100 		if (Allowednames[i].name != NULL)
101 			Allowednames[i].name = stable(Allowednames[i].name);
102 }
103 
104 void
105 check_fini(void)
106 {
107 }
108 
109 /*ARGSUSED*/
110 void
111 check_report_combination(struct node *np)
112 {
113 	/* nothing to check for here.  poller is only prop and it is optional */
114 }
115 
116 /*
117  * check_path_iterators -- verify all iterators are explicit
118  */
119 static void
120 check_path_iterators(struct node *np)
121 {
122 	if (np == NULL)
123 		return;
124 
125 	switch (np->t) {
126 		case T_ARROW:
127 			check_path_iterators(np->u.arrow.lhs);
128 			check_path_iterators(np->u.arrow.rhs);
129 			break;
130 
131 		case T_LIST:
132 			check_path_iterators(np->u.expr.left);
133 			check_path_iterators(np->u.expr.right);
134 			break;
135 
136 		case T_EVENT:
137 			check_path_iterators(np->u.event.epname);
138 			break;
139 
140 		case T_NAME:
141 			if (np->u.name.child == NULL)
142 				outfl(O_DIE, np->file, np->line,
143 				    "internal error: check_path_iterators: "
144 				    "unexpected implicit iterator: %s",
145 				    np->u.name.s);
146 			check_path_iterators(np->u.name.next);
147 			break;
148 
149 		default:
150 			outfl(O_DIE, np->file, np->line,
151 			    "internal error: check_path_iterators: "
152 			    "unexpected type: %s",
153 			    ptree_nodetype2str(np->t));
154 	}
155 }
156 
157 void
158 check_arrow(struct node *np)
159 {
160 	ASSERTinfo(np->t == T_ARROW, ptree_nodetype2str(np->t));
161 
162 	if (np->u.arrow.lhs->t != T_ARROW &&
163 	    np->u.arrow.lhs->t != T_LIST &&
164 	    np->u.arrow.lhs->t != T_EVENT) {
165 		outfl(O_ERR,
166 		    np->u.arrow.lhs->file, np->u.arrow.lhs->line,
167 		    "%s not allowed on left-hand side of arrow",
168 		    ptree_nodetype2str(np->u.arrow.lhs->t));
169 	}
170 
171 	if (!check_nork(np->u.arrow.nnp) ||
172 		!check_nork(np->u.arrow.knp))
173 			outfl(O_ERR, np->file, np->line,
174 			    "counts associated with propagation arrows "
175 			    "must be integers");
176 
177 	check_path_iterators(np);
178 }
179 
180 /*
181  * make sure the nork values are valid.
182  * Nork values must be "A" for all(T_NAME),
183  * a number(T_NUM), or a simple
184  * expression(T_SUB, T_ADD, T_MUL, T_DIV)
185  */
186 static int
187 check_nork(struct node *np)
188 {
189 	int rval = 0;
190 
191 	/* NULL means no nork value which is allowed */
192 	if (np == NULL) {
193 		rval = 1;
194 	}
195 	else
196 	{
197 		/* if the nork is a name it must be A for "All" */
198 		if (np->t == T_NAME)
199 			if (*np->u.name.s == 'A')
200 				return (1);
201 
202 		/*  T_NUM allowed */
203 		if (np->t == T_NUM)
204 			rval = 1;
205 
206 		/*  simple expressions allowed */
207 		if (np->t == T_SUB ||
208 			np->t == T_ADD ||
209 			np->t == T_MUL ||
210 			np->t == T_DIV)
211 			rval = 1;
212 	}
213 
214 	return (rval);
215 }
216 
217 static int
218 check_reportlist(enum nodetype t, const char *s, struct node *np)
219 {
220 	if (np == NULL)
221 		return (1);
222 	else if (np->t == T_EVENT) {
223 		if (np->u.event.ename->u.name.t != N_EREPORT) {
224 			outfl(O_ERR, np->file, np->line,
225 			    "%s %s property must begin with \"ereport.\"",
226 			    ptree_nodetype2str(t), s);
227 		} else if (tree_event2np_lut_lookup(Ereports, np) == NULL) {
228 			outfl(O_ERR, np->file, np->line,
229 			    "%s %s property contains undeclared name",
230 			    ptree_nodetype2str(t), s);
231 		}
232 		check_type_iterator(np);
233 	} else if (np->t == T_LIST) {
234 		(void) check_reportlist(t, s, np->u.expr.left);
235 		(void) check_reportlist(t, s, np->u.expr.right);
236 	}
237 	return (1);
238 }
239 
240 static int
241 check_num(enum nodetype t, const char *s, struct node *np)
242 {
243 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
244 	if (np->t != T_NUM)
245 		outfl(O_ERR, np->file, np->line,
246 		    "%s %s property must be a single number",
247 		    ptree_nodetype2str(t), s);
248 	return (1);
249 }
250 
251 /*ARGSUSED1*/
252 static int
253 check_quote(enum nodetype t, const char *s, struct node *np)
254 {
255 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
256 	if (np->t != T_QUOTE)
257 		outfl(O_ERR, np->file, np->line,
258 		    "%s properties must be quoted strings",
259 		    ptree_nodetype2str(t));
260 	return (1);
261 }
262 
263 static int
264 check_action(enum nodetype t, const char *s, struct node *np)
265 {
266 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
267 
268 	if (np->t != T_FUNC)
269 		outfl(O_ERR, np->file, np->line,
270 		    "%s %s property must be a function or list of functions",
271 		    ptree_nodetype2str(t), s);
272 	return (1);
273 }
274 
275 static int
276 check_num_func(enum nodetype t, const char *s, struct node *np)
277 {
278 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
279 	if (np->t != T_NUM && np->t != T_FUNC)
280 		outfl(O_ERR, np->file, np->line,
281 		    "%s %s property must be a number or function",
282 		    ptree_nodetype2str(t), s);
283 	return (1);
284 }
285 
286 static int
287 check_fru_asru(enum nodetype t, const char *s, struct node *np)
288 {
289 	ASSERT(s != NULL);
290 
291 	/* make sure it is a node type T_NAME? */
292 	if (np->t == T_NAME) {
293 	    if (s == L_ASRU) {
294 		if (tree_name2np_lut_lookup_name(ASRUs, np) == NULL)
295 			outfl(O_ERR, np->file, np->line,
296 			    "ASRU property contains undeclared asru");
297 	    } else if (s == L_FRU) {
298 		if (tree_name2np_lut_lookup_name(FRUs, np) == NULL)
299 			outfl(O_ERR, np->file, np->line,
300 			    "FRU property contains undeclared fru");
301 	    } else {
302 		    outfl(O_ERR, np->file, np->line,
303 			"illegal property name in %s declaration: %s",
304 			ptree_nodetype2str(t), s);
305 	    }
306 	    check_type_iterator(np);
307 	} else
308 		outfl(O_ERR, np->file, np->line,
309 		    "illegal type used for %s property: %s",
310 		    s, ptree_nodetype2str(np->t));
311 	return (1);
312 }
313 
314 static int
315 check_engine(enum nodetype t, const char *s, struct node *np)
316 {
317 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
318 	if (np->t != T_EVENT)
319 		outfl(O_ERR, np->file, np->line,
320 		    "%s %s property must be an engine name "
321 		    "(i.e. serd.x or serd.x@a/b)",
322 		    ptree_nodetype2str(t), s);
323 
324 	return (1);
325 }
326 
327 static int
328 check_count(enum nodetype t, const char *s, struct node *np)
329 {
330 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
331 	if (np->t != T_EVENT)
332 		outfl(O_ERR, np->file, np->line,
333 		    "%s %s property must be an engine name "
334 		    "(i.e. stat.x or stat.x@a/b)",
335 		    ptree_nodetype2str(t), s);
336 
337 	/* XXX confirm engine has been declared */
338 	return (1);
339 }
340 
341 static int
342 check_timeval(enum nodetype t, const char *s, struct node *np)
343 {
344 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
345 	if (np->t != T_TIMEVAL)
346 		outfl(O_ERR, np->file, np->line,
347 		    "%s %s property must be a number with time units",
348 		    ptree_nodetype2str(t), s);
349 	return (1);
350 }
351 
352 static int
353 check_id(enum nodetype t, const char *s, struct node *np)
354 {
355 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
356 	if (np->t != T_NAME || np->u.name.next || np->u.name.child)
357 		outfl(O_ERR, np->file, np->line,
358 		    "%s %s property must be simple name",
359 		    ptree_nodetype2str(t), s);
360 	return (1);
361 }
362 
363 static int
364 check_serd_method(enum nodetype t, const char *s, struct node *np)
365 {
366 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
367 	if (np->t != T_NAME || np->u.name.next || np->u.name.child ||
368 	    (np->u.name.s != L_volatile &&
369 	    np->u.name.s != L_persistent))
370 		outfl(O_ERR, np->file, np->line,
371 		    "%s %s property must be \"volatile\" or \"persistent\"",
372 		    ptree_nodetype2str(t), s);
373 	return (1);
374 }
375 
376 static int
377 check_serd_id(enum nodetype t, const char *s, struct node *np)
378 {
379 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
380 	if (np->t != T_GLOBID)
381 		outfl(O_ERR, np->file, np->line,
382 		    "%s %s property must be a global ID",
383 		    ptree_nodetype2str(t), s);
384 	return (1);
385 }
386 
387 void
388 check_stmt_required_properties(struct node *stmtnp)
389 {
390 	struct lut *lutp = stmtnp->u.stmt.lutp;
391 	struct node *np = stmtnp->u.stmt.np;
392 	int i;
393 
394 	for (i = 0; Allowednames[i].t; i++)
395 		if (stmtnp->t == Allowednames[i].t &&
396 		    Allowednames[i].required &&
397 		    tree_s2np_lut_lookup(lutp, Allowednames[i].name) == NULL)
398 			outfl(Allowednames[i].outflags,
399 			    np->file, np->line,
400 			    "%s statement missing property: %s",
401 			    ptree_nodetype2str(stmtnp->t),
402 			    Allowednames[i].name);
403 }
404 
405 void
406 check_stmt_allowed_properties(enum nodetype t,
407     struct node *nvpairnp, struct lut *lutp)
408 {
409 	int i;
410 	const char *s = nvpairnp->u.expr.left->u.name.s;
411 	struct node *np;
412 
413 	for (i = 0; Allowednames[i].t; i++)
414 		if (t == Allowednames[i].t && Allowednames[i].name == NULL) {
415 			/* NULL name means just call checker */
416 			(*Allowednames[i].checker)(t, s,
417 			    nvpairnp->u.expr.right);
418 			return;
419 		} else if (t == Allowednames[i].t && s == Allowednames[i].name)
420 			break;
421 	if (Allowednames[i].name == NULL)
422 		outfl(O_ERR, nvpairnp->file, nvpairnp->line,
423 		    "illegal property name in %s declaration: %s",
424 		    ptree_nodetype2str(t), s);
425 	else if ((np = tree_s2np_lut_lookup(lutp, s)) != NULL) {
426 		/*
427 		 * redeclaring prop is allowed if value is the same
428 		 */
429 		if (np->t != nvpairnp->u.expr.right->t)
430 			outfl(O_ERR, nvpairnp->file, nvpairnp->line,
431 			    "property redeclared (with differnt type) "
432 			    "in %s declaration: %s",
433 			    ptree_nodetype2str(t), s);
434 		switch (np->t) {
435 			case T_NUM:
436 			case T_TIMEVAL:
437 				if (np->u.ull == nvpairnp->u.expr.right->u.ull)
438 					return;
439 				break;
440 
441 			case T_NAME:
442 				if (tree_namecmp(np,
443 				    nvpairnp->u.expr.right) == 0)
444 					return;
445 				break;
446 
447 			case T_EVENT:
448 				if (tree_eventcmp(np,
449 				    nvpairnp->u.expr.right) == 0)
450 					return;
451 				break;
452 
453 			default:
454 				outfl(O_ERR, nvpairnp->file, nvpairnp->line,
455 				    "value for property \"%s\" is an "
456 				    "invalid type: %s",
457 				    nvpairnp->u.expr.left->u.name.s,
458 				    ptree_nodetype2str(np->t));
459 				return;
460 		}
461 		outfl(O_ERR, nvpairnp->file, nvpairnp->line,
462 		    "property redeclared in %s declaration: %s",
463 		    ptree_nodetype2str(t), s);
464 	} else
465 		(*Allowednames[i].checker)(t, s, nvpairnp->u.expr.right);
466 }
467 
468 void
469 check_propnames(enum nodetype t, struct node *np, int from, int to)
470 {
471 	struct node *dnp;
472 	struct lut *lutp;
473 
474 	ASSERT(np != NULL);
475 	ASSERTinfo(np->t == T_EVENT || np->t == T_LIST || np->t == T_ARROW,
476 				ptree_nodetype2str(np->t));
477 
478 	if (np->t == T_EVENT) {
479 		switch (np->u.event.ename->u.name.t) {
480 		case N_UNSPEC:
481 			outfl(O_ERR, np->file, np->line,
482 			    "name in %s statement must begin with "
483 			    "type (example: \"error.\")",
484 			    ptree_nodetype2str(t));
485 			return;
486 		case N_FAULT:
487 			lutp = Faults;
488 			if (to) {
489 				outfl(O_ERR, np->file, np->line,
490 				    "%s has fault on right side of \"->\"",
491 				    ptree_nodetype2str(t));
492 				return;
493 			}
494 			if (!from) {
495 				outfl(O_DIE, np->file, np->line,
496 				    "internal error: %s has fault without "
497 				    "from flag",
498 				    ptree_nodetype2str(t));
499 			}
500 			break;
501 		case N_UPSET:
502 			lutp = Upsets;
503 			if (to) {
504 				outfl(O_ERR, np->file, np->line,
505 				    "%s has upset on right side of \"->\"",
506 				    ptree_nodetype2str(t));
507 				return;
508 			}
509 			if (!from)
510 				outfl(O_DIE, np->file, np->line,
511 				    "internal error: %s has upset without "
512 				    "from flag",
513 				    ptree_nodetype2str(t));
514 			break;
515 		case N_DEFECT:
516 			lutp = Defects;
517 			if (to) {
518 				outfl(O_ERR, np->file, np->line,
519 				    "%s has defect on right side of \"->\"",
520 				    ptree_nodetype2str(t));
521 				return;
522 			}
523 			if (!from) {
524 				outfl(O_DIE, np->file, np->line,
525 				    "internal error: %s has defect without "
526 				    "from flag",
527 				    ptree_nodetype2str(t));
528 			}
529 			break;
530 		case N_ERROR:
531 			lutp = Errors;
532 			if (!from && !to)
533 				outfl(O_DIE, np->file, np->line,
534 				    "%s has error without from or to flags",
535 				    ptree_nodetype2str(t));
536 			break;
537 		case N_EREPORT:
538 			lutp = Ereports;
539 			if (from) {
540 				outfl(O_ERR, np->file, np->line,
541 				    "%s has report on left side of \"->\"",
542 				    ptree_nodetype2str(t));
543 				return;
544 			}
545 			if (!to)
546 				outfl(O_DIE, np->file, np->line,
547 				    "internal error: %s has report without "
548 				    "to flag",
549 				    ptree_nodetype2str(t));
550 			break;
551 		default:
552 			outfl(O_DIE, np->file, np->line,
553 			    "internal error: check_propnames: "
554 			    "unexpected type: %d", np->u.name.t);
555 		}
556 
557 		if ((dnp = tree_event2np_lut_lookup(lutp, np)) == NULL) {
558 			outfl(O_ERR, np->file, np->line,
559 			    "%s statement contains undeclared event",
560 			    ptree_nodetype2str(t));
561 		} else
562 			dnp->u.stmt.flags |= STMT_REF;
563 		np->u.event.declp = dnp;
564 	} else if (np->t == T_LIST) {
565 		check_propnames(t, np->u.expr.left, from, to);
566 		check_propnames(t, np->u.expr.right, from, to);
567 	} else if (np->t == T_ARROW) {
568 		check_propnames(t, np->u.arrow.lhs, 1, to);
569 		check_propnames(t, np->u.arrow.rhs, from, 1);
570 	}
571 }
572 
573 static struct lut *
574 record_iterators(struct node *np, struct lut *ex)
575 {
576 	if (np == NULL)
577 		return (ex);
578 
579 	switch (np->t) {
580 	case T_ARROW:
581 		ex = record_iterators(np->u.arrow.lhs, ex);
582 		ex = record_iterators(np->u.arrow.rhs, ex);
583 		break;
584 
585 	case T_LIST:
586 		ex = record_iterators(np->u.expr.left, ex);
587 		ex = record_iterators(np->u.expr.right, ex);
588 		break;
589 
590 	case T_EVENT:
591 		ex = record_iterators(np->u.event.epname, ex);
592 		break;
593 
594 	case T_NAME:
595 		if (np->u.name.child && np->u.name.child->t == T_NAME)
596 			ex = lut_add(ex, (void *) np->u.name.child->u.name.s,
597 			    (void *) np, NULL);
598 		ex = record_iterators(np->u.name.next, ex);
599 		break;
600 
601 	default:
602 		outfl(O_DIE, np->file, np->line,
603 		    "record_iterators: internal error: unexpected type: %s",
604 		    ptree_nodetype2str(np->t));
605 	}
606 
607 	return (ex);
608 }
609 
610 void
611 check_exprscope(struct node *np, struct lut *ex)
612 {
613 	if (np == NULL)
614 		return;
615 
616 	switch (np->t) {
617 	case T_EVENT:
618 		check_exprscope(np->u.event.eexprlist, ex);
619 		break;
620 
621 	case T_ARROW:
622 		check_exprscope(np->u.arrow.lhs, ex);
623 		check_exprscope(np->u.arrow.rhs, ex);
624 		break;
625 
626 	case T_NAME:
627 		if (np->u.name.child && np->u.name.child->t == T_NAME) {
628 			if (lut_lookup(ex,
629 					(void *) np->u.name.child->u.name.s,
630 					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 	ASSERTinfo(np->t == T_FUNC, ptree_nodetype2str(np->t));
965 
966 	if (np->u.func.s == L_within) {
967 		struct node *arglist = np->u.func.arglist;
968 		switch (arglist->t) {
969 			case T_NUM:
970 				if (arglist->u.ull != 0ULL)
971 				    outfl(O_ERR, arglist->file, arglist->line,
972 					"parameter of within must be 0"
973 					", \"infinity\" or a time value.");
974 				break;
975 			case T_NAME:
976 				if (arglist->u.name.s != L_infinity)
977 				    outfl(O_ERR, arglist->file, arglist->line,
978 					"parameter of within must be 0"
979 					", \"infinity\" or a time value.");
980 				break;
981 			case T_LIST:
982 				/*
983 				 * if two parameters, the left or min must be
984 				 * either T_NUM or T_TIMEVAL
985 				 */
986 				if (arglist->u.expr.left->t != T_NUM &&
987 				    arglist->u.expr.left->t != T_TIMEVAL)
988 					outfl(O_ERR,
989 					    arglist->file, arglist->line,
990 					    "first parameter of within must be"
991 					    " either a time value or zero.");
992 
993 				/*
994 				 * if two parameters, the right or max must
995 				 * be either T_NUM, T_NAME or T_TIMEVAL
996 				 */
997 				if (arglist->u.expr.right->t != T_NUM &&
998 				    arglist->u.expr.right->t != T_TIMEVAL &&
999 				    arglist->u.expr.right->t != T_NAME)
1000 					outfl(O_ERR,
1001 					    arglist->file, arglist->line,
1002 					    "second parameter of within must "
1003 					    "be 0, \"infinity\" "
1004 					    "or time value.");
1005 
1006 				/*
1007 				 * if right or left is a T_NUM it must
1008 				 * be zero
1009 				 */
1010 				if (arglist->u.expr.left->t == T_NUM)
1011 					if (arglist->u.expr.left->u.ull != 0ULL)
1012 					    outfl(O_ERR,
1013 						arglist->file, arglist->line,
1014 						"within parameter must be 0 or"
1015 						" a time value.");
1016 				if (arglist->u.expr.right->t == T_NUM)
1017 					if (arglist->u.expr.right->u.ull
1018 					    != 0ULL)
1019 					    outfl(O_ERR,
1020 						arglist->file, arglist->line,
1021 						"within parameter must be 0 or"
1022 						" a time value.");
1023 
1024 				/* if right is a T_NAME it must be "infinity" */
1025 				if (arglist->u.expr.right->t == T_NAME)
1026 					if (arglist->u.expr.right->u.name.s
1027 					    != L_infinity)
1028 					    outfl(O_ERR,
1029 						arglist->file, arglist->line,
1030 						"\"infinity\" is the only valid"
1031 						" name for within parameter.");
1032 
1033 				/*
1034 				 * the first parameter [min] must not be greater
1035 				 * than the second parameter [max].
1036 				 */
1037 				if (arglist->u.expr.left->u.ull >
1038 				    arglist->u.expr.right->u.ull)
1039 					outfl(O_ERR,
1040 					    arglist->file, arglist->line,
1041 					    "the first value (min) of"
1042 					    " within must be less than"
1043 					    " the second (max) value");
1044 				break;
1045 			case T_TIMEVAL:
1046 				break; /* no restrictions on T_TIMEVAL */
1047 			default:
1048 				outfl(O_ERR, arglist->file, arglist->line,
1049 					"parameter of within must be 0"
1050 					", \"infinity\" or a time value.");
1051 		}
1052 	} else if (np->u.func.s == L_call) {
1053 		if (np->u.func.arglist->t != T_QUOTE &&
1054 		    np->u.func.arglist->t != T_LIST &&
1055 		    np->u.func.arglist->t != T_GLOBID &&
1056 		    np->u.func.arglist->t != T_CONDIF &&
1057 		    np->u.func.arglist->t != T_LIST &&
1058 		    np->u.func.arglist->t != T_FUNC)
1059 			outfl(O_ERR, np->u.func.arglist->file,
1060 				np->u.func.arglist->line,
1061 				"invalid first argument to call()");
1062 	} else if (np->u.func.s == L_fru) {
1063 		if (np->u.func.arglist->t != T_NAME)
1064 			outfl(O_ERR, np->u.func.arglist->file,
1065 				np->u.func.arglist->line,
1066 				"argument to fru() must be a path");
1067 	} else if (np->u.func.s == L_asru) {
1068 		if (np->u.func.arglist->t != T_NAME)
1069 			outfl(O_ERR, np->u.func.arglist->file,
1070 				np->u.func.arglist->line,
1071 				"argument to asru() must be a path");
1072 	} else if (np->u.func.s == L_is_connected ||
1073 	    np->u.func.s == L_is_under) {
1074 		if (np->u.func.arglist->t == T_LIST &&
1075 		    (np->u.func.arglist->u.expr.left->t == T_NAME ||
1076 		    (np->u.func.arglist->u.expr.left->t == T_FUNC &&
1077 		    (np->u.func.arglist->u.expr.left->u.func.s == L_fru ||
1078 		    np->u.func.arglist->u.expr.left->u.func.s == L_asru))) &&
1079 		    (np->u.func.arglist->u.expr.right->t == T_NAME ||
1080 		    (np->u.func.arglist->u.expr.right->t == T_FUNC &&
1081 		    (np->u.func.arglist->u.expr.right->u.func.s == L_fru ||
1082 		    np->u.func.arglist->u.expr.right->u.func.s == L_asru)))) {
1083 			if (np->u.func.arglist->u.expr.left->t == T_FUNC)
1084 				check_func(np->u.func.arglist->u.expr.left);
1085 			if (np->u.func.arglist->u.expr.right->t == T_FUNC)
1086 				check_func(np->u.func.arglist->u.expr.right);
1087 		} else {
1088 			outfl(O_ERR, np->u.func.arglist->file,
1089 			    np->u.func.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 (np->u.func.arglist->t == T_FUNC &&
1096 		    (np->u.func.arglist->u.func.s == L_fru ||
1097 		    np->u.func.arglist->u.func.s == L_asru)) {
1098 			check_func(np->u.func.arglist);
1099 		} else {
1100 			outfl(O_ERR, np->u.func.arglist->file,
1101 			    np->u.func.arglist->line,
1102 			    "argument to is_on() must be a call to "
1103 			    "fru() or asru()");
1104 		}
1105 	} else if (np->u.func.s == L_is_present) {
1106 		if (np->u.func.arglist->t == T_FUNC &&
1107 		    (np->u.func.arglist->u.func.s == L_fru ||
1108 		    np->u.func.arglist->u.func.s == L_asru)) {
1109 			check_func(np->u.func.arglist);
1110 		} else {
1111 			outfl(O_ERR, np->u.func.arglist->file,
1112 			    np->u.func.arglist->line,
1113 			    "argument to is_present() must be a call to "
1114 			    "fru() or asru()");
1115 		}
1116 	} else if (np->u.func.s == L_is_type) {
1117 		if (np->u.func.arglist->t == T_FUNC &&
1118 		    (np->u.func.arglist->u.func.s == L_fru ||
1119 		    np->u.func.arglist->u.func.s == L_asru)) {
1120 			check_func(np->u.func.arglist);
1121 		} else {
1122 			outfl(O_ERR, np->u.func.arglist->file,
1123 			    np->u.func.arglist->line,
1124 			    "argument to is_type() must be a call to "
1125 			    "fru() or asru()");
1126 		}
1127 	} else if (np->u.func.s == L_confcall) {
1128 		if (np->u.func.arglist->t != T_QUOTE &&
1129 		    (np->u.func.arglist->t != T_LIST ||
1130 		    np->u.func.arglist->u.expr.left->t != T_QUOTE))
1131 			outfl(O_ERR, np->u.func.arglist->file,
1132 			    np->u.func.arglist->line,
1133 			    "confcall(): first argument must be a string "
1134 			    "(the name of the operation)");
1135 	} else if (np->u.func.s == L_confprop ||
1136 	    np->u.func.s == L_confprop_defined) {
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 			    "%s(): first argument must be a call to "
1147 			    "fru() or asru(); "
1148 			    "second argument must be a string", np->u.func.s);
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