xref: /illumos-gate/usr/src/cmd/fm/eversholt/common/check.c (revision b35c6776bcf599e80d0bcf7e248313c3e5b4847a)
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 	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,
972 					    arglist->file, arglist->line,
973 					    "parameter of within must be 0"
974 					    ", \"infinity\" or a time value.");
975 				break;
976 			case T_NAME:
977 				if (arglist->u.name.s != L_infinity)
978 					outfl(O_ERR,
979 					    arglist->file, arglist->line,
980 					    "parameter of within must be 0"
981 					    ", \"infinity\" or a time value.");
982 				break;
983 			case T_LIST:
984 				/*
985 				 * if two parameters, the left or min must be
986 				 * either T_NUM or T_TIMEVAL
987 				 */
988 				if (arglist->u.expr.left->t != T_NUM &&
989 				    arglist->u.expr.left->t != T_TIMEVAL)
990 					outfl(O_ERR,
991 					    arglist->file, arglist->line,
992 					    "first parameter of within must be"
993 					    " either a time value or zero.");
994 
995 			/*
996 			 * if two parameters, the right or max must
997 			 * be either T_NUM, T_NAME or T_TIMEVAL
998 			 */
999 			if (arglist->u.expr.right->t != T_NUM &&
1000 			    arglist->u.expr.right->t != T_TIMEVAL &&
1001 			    arglist->u.expr.right->t != T_NAME)
1002 				outfl(O_ERR,
1003 				    arglist->file, arglist->line,
1004 				    "second parameter of within must "
1005 				    "be 0, \"infinity\" "
1006 				    "or time value.");
1007 
1008 				/*
1009 				 * if right or left is a T_NUM it must
1010 				 * be zero
1011 				 */
1012 				if (arglist->u.expr.left->t == T_NUM)
1013 					if (arglist->u.expr.left->u.ull != 0ULL)
1014 						outfl(O_ERR, arglist->file,
1015 						    arglist->line,
1016 						    "within parameter must be "
1017 						    "0 or a time value.");
1018 				if (arglist->u.expr.right->t == T_NUM)
1019 					if (arglist->u.expr.right->u.ull
1020 					    != 0ULL)
1021 						outfl(O_ERR,
1022 						    arglist->file,
1023 						    arglist->line,
1024 						    "within parameter must be "
1025 						    "0 or a time value.");
1026 
1027 				/* if right is a T_NAME it must be "infinity" */
1028 				if (arglist->u.expr.right->t == T_NAME)
1029 					if (arglist->u.expr.right->u.name.s
1030 					    != L_infinity)
1031 						outfl(O_ERR,
1032 						    arglist->file,
1033 						    arglist->line,
1034 						    "\"infinity\" is the only "
1035 						    "valid name for within "
1036 						    "parameter.");
1037 
1038 			/*
1039 			 * the first parameter [min] must not be greater
1040 			 * than the second parameter [max].
1041 			 */
1042 			if (arglist->u.expr.left->u.ull >
1043 			    arglist->u.expr.right->u.ull)
1044 				outfl(O_ERR, arglist->file, arglist->line,
1045 				    "parameter of within must be 0"
1046 				    ", \"infinity\" or a time value.");
1047 		}
1048 	} else if (np->u.func.s == L_call) {
1049 		if (np->u.func.arglist->t != T_QUOTE &&
1050 		    np->u.func.arglist->t != T_LIST &&
1051 		    np->u.func.arglist->t != T_GLOBID &&
1052 		    np->u.func.arglist->t != T_CONDIF &&
1053 		    np->u.func.arglist->t != T_LIST &&
1054 		    np->u.func.arglist->t != T_FUNC)
1055 			outfl(O_ERR, np->u.func.arglist->file,
1056 			    np->u.func.arglist->line,
1057 			    "invalid first argument to call()");
1058 	} else if (np->u.func.s == L_fru) {
1059 		if (np->u.func.arglist->t != T_NAME)
1060 			outfl(O_ERR, np->u.func.arglist->file,
1061 			    np->u.func.arglist->line,
1062 			    "argument to fru() must be a path");
1063 	} else if (np->u.func.s == L_asru) {
1064 		if (np->u.func.arglist->t != T_NAME)
1065 			outfl(O_ERR, np->u.func.arglist->file,
1066 			    np->u.func.arglist->line,
1067 			    "argument to asru() must be a path");
1068 	} else if (np->u.func.s == L_is_connected ||
1069 	    np->u.func.s == L_is_under) {
1070 		if (np->u.func.arglist->t == T_LIST &&
1071 		    (np->u.func.arglist->u.expr.left->t == T_NAME ||
1072 		    (np->u.func.arglist->u.expr.left->t == T_FUNC &&
1073 		    (np->u.func.arglist->u.expr.left->u.func.s == L_fru ||
1074 		    np->u.func.arglist->u.expr.left->u.func.s == L_asru))) &&
1075 		    (np->u.func.arglist->u.expr.right->t == T_NAME ||
1076 		    (np->u.func.arglist->u.expr.right->t == T_FUNC &&
1077 		    (np->u.func.arglist->u.expr.right->u.func.s == L_fru ||
1078 		    np->u.func.arglist->u.expr.right->u.func.s == L_asru)))) {
1079 			if (np->u.func.arglist->u.expr.left->t == T_FUNC)
1080 				check_func(np->u.func.arglist->u.expr.left);
1081 			if (np->u.func.arglist->u.expr.right->t == T_FUNC)
1082 				check_func(np->u.func.arglist->u.expr.right);
1083 		} else {
1084 			outfl(O_ERR, np->u.func.arglist->file,
1085 			    np->u.func.arglist->line,
1086 			    "%s() must have paths or calls to "
1087 			    "fru() and/or asru() as arguments",
1088 			    np->u.func.s);
1089 		}
1090 	} else if (np->u.func.s == L_is_on) {
1091 		if (np->u.func.arglist->t == T_FUNC &&
1092 		    (np->u.func.arglist->u.func.s == L_fru ||
1093 		    np->u.func.arglist->u.func.s == L_asru)) {
1094 			check_func(np->u.func.arglist);
1095 		} else {
1096 			outfl(O_ERR, np->u.func.arglist->file,
1097 			    np->u.func.arglist->line,
1098 			    "argument to is_on() must be a call to "
1099 			    "fru() or asru()");
1100 		}
1101 	} else if (np->u.func.s == L_is_present) {
1102 		if (np->u.func.arglist->t == T_FUNC &&
1103 		    (np->u.func.arglist->u.func.s == L_fru ||
1104 		    np->u.func.arglist->u.func.s == L_asru)) {
1105 			check_func(np->u.func.arglist);
1106 		} else {
1107 			outfl(O_ERR, np->u.func.arglist->file,
1108 			    np->u.func.arglist->line,
1109 			    "argument to is_present() must be a call to "
1110 			    "fru() or asru()");
1111 		}
1112 	} else if (np->u.func.s == L_is_type) {
1113 		if (np->u.func.arglist->t == T_FUNC &&
1114 		    (np->u.func.arglist->u.func.s == L_fru ||
1115 		    np->u.func.arglist->u.func.s == L_asru)) {
1116 			check_func(np->u.func.arglist);
1117 		} else {
1118 			outfl(O_ERR, np->u.func.arglist->file,
1119 			    np->u.func.arglist->line,
1120 			    "argument to is_type() must be a call to "
1121 			    "fru() or asru()");
1122 		}
1123 	} else if (np->u.func.s == L_confcall) {
1124 		if (np->u.func.arglist->t != T_QUOTE &&
1125 		    (np->u.func.arglist->t != T_LIST ||
1126 		    np->u.func.arglist->u.expr.left->t != T_QUOTE))
1127 			outfl(O_ERR, np->u.func.arglist->file,
1128 			    np->u.func.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 (np->u.func.arglist->t == T_LIST &&
1134 		    (np->u.func.arglist->u.expr.left->t == T_FUNC &&
1135 		    (np->u.func.arglist->u.expr.left->u.func.s == L_fru ||
1136 		    np->u.func.arglist->u.expr.left->u.func.s == L_asru)) &&
1137 		    np->u.func.arglist->u.expr.right->t == T_QUOTE) {
1138 			check_func(np->u.func.arglist->u.expr.left);
1139 		} else {
1140 			outfl(O_ERR, np->u.func.arglist->file,
1141 			    np->u.func.arglist->line,
1142 			    "%s(): first argument must be a call to "
1143 			    "fru() or asru(); "
1144 			    "second argument must be a string", np->u.func.s);
1145 		}
1146 	} else if (np->u.func.s == L_count) {
1147 		if (np->u.func.arglist->t != T_EVENT) {
1148 			outfl(O_ERR, np->u.func.arglist->file,
1149 			    np->u.func.arglist->line,
1150 			    "count(): argument must be an engine name");
1151 		}
1152 	} else if (np->u.func.s == L_defined) {
1153 		if (np->u.func.arglist->t != T_GLOBID)
1154 			outfl(O_ERR, np->u.func.arglist->file,
1155 			    np->u.func.arglist->line,
1156 			    "argument to defined() must be a global");
1157 	} else if (np->u.func.s == L_payloadprop) {
1158 		if (np->u.func.arglist->t != T_QUOTE)
1159 			outfl(O_ERR, np->u.func.arglist->file,
1160 			    np->u.func.arglist->line,
1161 			    "argument to payloadprop() must be a string");
1162 	} else if (np->u.func.s == L_payloadprop_contains) {
1163 		if (np->u.func.arglist->t != T_LIST ||
1164 		    np->u.func.arglist->u.expr.left->t != T_QUOTE ||
1165 		    np->u.func.arglist->u.expr.right == NULL)
1166 			outfl(O_ERR, np->u.func.arglist->file,
1167 			    np->u.func.arglist->line,
1168 			    "args to payloadprop_contains(): must be a quoted "
1169 			    "string (property name) and an expression "
1170 			    "(to match)");
1171 	} else if (np->u.func.s == L_payloadprop_defined) {
1172 		if (np->u.func.arglist->t != T_QUOTE)
1173 			outfl(O_ERR, np->u.func.arglist->file,
1174 			    np->u.func.arglist->line,
1175 			    "arg to payloadprop_defined(): must be a quoted "
1176 			    "string");
1177 	} else if (np->u.func.s == L_setpayloadprop) {
1178 		if (np->u.func.arglist->t == T_LIST &&
1179 		    np->u.func.arglist->u.expr.left->t == T_QUOTE) {
1180 			if (np->u.func.arglist->u.expr.right->t == T_FUNC)
1181 				check_func(np->u.func.arglist->u.expr.right);
1182 		} else {
1183 			outfl(O_ERR, np->u.func.arglist->file,
1184 			    np->u.func.arglist->line,
1185 			    "setpayloadprop(): "
1186 			    "first arg must be a string, "
1187 			    "second arg a value");
1188 		}
1189 	} else if (np->u.func.s == L_envprop) {
1190 		if (np->u.func.arglist->t != T_QUOTE)
1191 			outfl(O_ERR, np->u.func.arglist->file,
1192 			    np->u.func.arglist->line,
1193 			    "argument to envprop() must be a string");
1194 	} else
1195 		outfl(O_WARN, np->file, np->line,
1196 		    "possible platform-specific function: %s",
1197 		    np->u.func.s);
1198 }
1199 
1200 void
1201 check_expr(struct node *np)
1202 {
1203 	ASSERT(np != NULL);
1204 
1205 	switch (np->t) {
1206 	case T_ASSIGN:
1207 		ASSERT(np->u.expr.left != NULL);
1208 		if (np->u.expr.left->t != T_GLOBID)
1209 			outfl(O_ERR, np->file, np->line,
1210 			    "assignment only allowed to globals (e.g. $a)");
1211 		break;
1212 	}
1213 }
1214 
1215 void
1216 check_event(struct node *np)
1217 {
1218 	ASSERT(np != NULL);
1219 	ASSERTinfo(np->t == T_EVENT, ptree_nodetype2str(np->t));
1220 
1221 	if (np->u.event.epname == NULL) {
1222 		outfl(O_ERR|O_NONL, np->file, np->line,
1223 		    "pathless events not allowed: ");
1224 		ptree_name(O_ERR|O_NONL, np->u.event.ename);
1225 		out(O_ERR, NULL);
1226 	}
1227 }
1228 
1229 /*
1230  * check for properties that are required on declarations. This
1231  * should be done after all declarations since they can be
1232  * redeclared with a different set of properties.
1233  */
1234 /*ARGSUSED*/
1235 void
1236 check_required_props(struct node *lhs, struct node *rhs, void *arg)
1237 {
1238 	ASSERTeq(rhs->t, (enum nodetype)arg, ptree_nodetype2str);
1239 
1240 	check_stmt_required_properties(rhs);
1241 }
1242 
1243 /*
1244  * check that cascading prop statements do not contain lists internally.
1245  * the first and last event lists in the cascading prop may be single
1246  * events or lists of events.
1247  */
1248 /*ARGSUSED*/
1249 void
1250 check_proplists(enum nodetype t, struct node *np)
1251 {
1252 	ASSERT(np->t == T_ARROW);
1253 	/*
1254 	 * not checking the right hand side of the top level prop
1255 	 * since it is the last part of the propagation and can be
1256 	 * an event or list of events
1257 	 */
1258 	check_proplists_lhs(t, np->u.arrow.lhs);
1259 }
1260 
1261 /*ARGSUSED*/
1262 static void
1263 check_proplists_lhs(enum nodetype t, struct node *lhs)
1264 {
1265 	if (lhs->t == T_ARROW) {
1266 		if (lhs->u.arrow.rhs->t == T_LIST) {
1267 			outfl(O_ERR, lhs->file, lhs->line,
1268 			    "lists are not allowed internally on cascading %s",
1269 			    (t == T_PROP) ? "propagations" : "masks");
1270 		}
1271 		check_proplists_lhs(t, lhs->u.arrow.lhs);
1272 	}
1273 }
1274