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