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