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