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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 * check.c -- routines for checking the prop tree 27 * 28 * this module provides semantic checks on the parse tree. most of 29 * these checks happen during the construction of the parse tree, 30 * when the various tree_X() routines call the various check_X() 31 * routines. in a couple of special cases, a check function will 32 * process the parse tree after it has been fully constructed. these 33 * cases are noted in the comments above the check function. 34 */ 35 36 #pragma ident "%Z%%M% %I% %E% SMI" 37 38 #include <stdio.h> 39 #include "out.h" 40 #include "stable.h" 41 #include "literals.h" 42 #include "lut.h" 43 #include "tree.h" 44 #include "ptree.h" 45 #include "check.h" 46 47 static int check_reportlist(enum nodetype t, const char *s, struct node *np); 48 static int check_num(enum nodetype t, const char *s, struct node *np); 49 static int check_quote(enum nodetype t, const char *s, struct node *np); 50 static int check_action(enum nodetype t, const char *s, struct node *np); 51 static int check_num_func(enum nodetype t, const char *s, struct node *np); 52 static int check_fru_asru(enum nodetype t, const char *s, struct node *np); 53 static int check_engine(enum nodetype t, const char *s, struct node *np); 54 static int check_count(enum nodetype t, const char *s, struct node *np); 55 static int check_timeval(enum nodetype t, const char *s, struct node *np); 56 static int check_id(enum nodetype t, const char *s, struct node *np); 57 static int check_serd_method(enum nodetype t, const char *s, struct node *np); 58 static int check_serd_id(enum nodetype t, const char *s, struct node *np); 59 static int check_nork(struct node *np); 60 static void check_cycle_lhs(struct node *stmtnp, struct node *arrow); 61 static void check_cycle_lhs_try(struct node *stmtnp, struct node *lhs, 62 struct node *rhs); 63 static void check_cycle_rhs(struct node *rhs); 64 static void check_proplists_lhs(enum nodetype t, struct node *lhs); 65 66 static struct { 67 enum nodetype t; 68 const char *name; 69 int required; 70 int (*checker)(enum nodetype t, const char *s, struct node *np); 71 int outflags; 72 } Allowednames[] = { 73 { T_FAULT, "FITrate", 1, check_num_func, O_ERR }, 74 { T_FAULT, "FRU", 0, check_fru_asru, O_ERR }, 75 { T_FAULT, "ASRU", 0, check_fru_asru, O_ERR }, 76 { T_FAULT, "message", 0, check_num_func, O_ERR }, 77 { T_FAULT, "action", 0, check_action, O_ERR }, 78 { T_FAULT, "count", 0, check_count, O_ERR }, 79 { T_UPSET, "engine", 0, check_engine, O_ERR }, 80 { T_DEFECT, "FRU", 0, check_fru_asru, O_ERR }, 81 { T_DEFECT, "ASRU", 0, check_fru_asru, O_ERR }, 82 { T_EREPORT, "poller", 0, check_id, O_ERR }, 83 { T_EREPORT, "delivery", 0, check_timeval, 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, 631 NULL) == NULL) 632 outfl(O_ERR, np->file, np->line, 633 "constraint contains undefined" 634 " iterator: %s", 635 np->u.name.child->u.name.s); 636 } 637 check_exprscope(np->u.name.next, ex); 638 break; 639 640 case T_QUOTE: 641 case T_GLOBID: 642 break; 643 644 case T_ASSIGN: 645 case T_NE: 646 case T_EQ: 647 case T_LIST: 648 case T_AND: 649 case T_OR: 650 case T_NOT: 651 case T_ADD: 652 case T_SUB: 653 case T_MUL: 654 case T_DIV: 655 case T_MOD: 656 case T_LT: 657 case T_LE: 658 case T_GT: 659 case T_GE: 660 case T_BITAND: 661 case T_BITOR: 662 case T_BITXOR: 663 case T_BITNOT: 664 case T_LSHIFT: 665 case T_RSHIFT: 666 case T_CONDIF: 667 case T_CONDELSE: 668 check_exprscope(np->u.expr.left, ex); 669 check_exprscope(np->u.expr.right, ex); 670 break; 671 672 case T_FUNC: 673 check_exprscope(np->u.func.arglist, ex); 674 break; 675 676 case T_NUM: 677 case T_TIMEVAL: 678 break; 679 680 default: 681 outfl(O_DIE, np->file, np->line, 682 "check_exprscope: internal error: unexpected type: %s", 683 ptree_nodetype2str(np->t)); 684 } 685 } 686 687 /* 688 * check_propscope -- check constraints for out of scope variable refs 689 */ 690 void 691 check_propscope(struct node *np) 692 { 693 struct lut *ex; 694 695 ex = record_iterators(np, NULL); 696 check_exprscope(np, ex); 697 lut_free(ex, NULL, NULL); 698 } 699 700 /* 701 * check_upset_engine -- validate the engine property in an upset statement 702 * 703 * we do this after the full parse tree has been constructed rather than while 704 * building the parse tree because it is inconvenient for the user if we 705 * require SERD engines to be declared before used in an upset "engine" 706 * property. 707 */ 708 709 /*ARGSUSED*/ 710 void 711 check_upset_engine(struct node *lhs, struct node *rhs, void *arg) 712 { 713 enum nodetype t = (enum nodetype)arg; 714 struct node *engnp; 715 struct node *declp; 716 717 ASSERTeq(rhs->t, t, ptree_nodetype2str); 718 719 if ((engnp = tree_s2np_lut_lookup(rhs->u.stmt.lutp, L_engine)) == NULL) 720 return; 721 722 ASSERT(engnp->t == T_EVENT); 723 724 if ((declp = tree_event2np_lut_lookup(SERDs, engnp)) == NULL) { 725 outfl(O_ERR, engnp->file, engnp->line, 726 "%s %s property contains undeclared name", 727 ptree_nodetype2str(t), L_engine); 728 return; 729 } 730 engnp->u.event.declp = declp; 731 } 732 733 /* 734 * check_refcount -- see if declared names are used 735 * 736 * this is run after the entire parse tree is constructed, so a refcount 737 * of zero means the name has been declared but otherwise not used. 738 */ 739 740 void 741 check_refcount(struct node *lhs, struct node *rhs, void *arg) 742 { 743 enum nodetype t = (enum nodetype)arg; 744 745 ASSERTeq(rhs->t, t, ptree_nodetype2str); 746 747 if (rhs->u.stmt.flags & STMT_REF) 748 return; 749 750 outfl(O_WARN|O_NONL, rhs->file, rhs->line, 751 "%s name declared but not used: ", ptree_nodetype2str(t)); 752 ptree_name(O_WARN|O_NONL, lhs); 753 out(O_WARN, NULL); 754 } 755 756 /* 757 * set check_cycle_warninglevel only for val >= 0 758 */ 759 int 760 check_cycle_level(long long val) 761 { 762 static int check_cycle_warninglevel = -1; 763 764 if (val == 0) 765 check_cycle_warninglevel = 0; 766 else if (val > 0) 767 check_cycle_warninglevel = 1; 768 769 return (check_cycle_warninglevel); 770 } 771 772 /* 773 * check_cycle -- see props from an error have cycles 774 * 775 * this is run after the entire parse tree is constructed, for 776 * each error that has been declared. 777 */ 778 779 /*ARGSUSED*/ 780 void 781 check_cycle(struct node *lhs, struct node *rhs, void *arg) 782 { 783 struct node *np; 784 785 ASSERTeq(rhs->t, T_ERROR, ptree_nodetype2str); 786 787 if (rhs->u.stmt.flags & STMT_CYCLE) 788 return; /* already reported this cycle */ 789 790 if (rhs->u.stmt.flags & STMT_CYMARK) { 791 #ifdef ESC 792 int warninglevel; 793 794 warninglevel = check_cycle_level(-1); 795 if (warninglevel <= 0) { 796 int olevel = O_ERR; 797 798 if (warninglevel == 0) 799 olevel = O_WARN; 800 801 out(olevel|O_NONL, "cycle in propagation tree: "); 802 ptree_name(olevel|O_NONL, rhs->u.stmt.np); 803 out(olevel, NULL); 804 } 805 #endif /* ESC */ 806 807 rhs->u.stmt.flags |= STMT_CYCLE; 808 } 809 810 rhs->u.stmt.flags |= STMT_CYMARK; 811 812 /* for each propagation */ 813 for (np = Props; np; np = np->u.stmt.next) 814 check_cycle_lhs(rhs, np->u.stmt.np); 815 816 rhs->u.stmt.flags &= ~STMT_CYMARK; 817 } 818 819 /* 820 * check_cycle_lhs -- find the lhs of an arrow for cycle checking 821 */ 822 823 static void 824 check_cycle_lhs(struct node *stmtnp, struct node *arrow) 825 { 826 struct node *trylhs; 827 struct node *tryrhs; 828 829 /* handle cascaded arrows */ 830 switch (arrow->u.arrow.lhs->t) { 831 case T_ARROW: 832 /* first recurse left */ 833 check_cycle_lhs(stmtnp, arrow->u.arrow.lhs); 834 835 /* 836 * return if there's a list of events internal to 837 * cascaded props (which is not allowed) 838 */ 839 if (arrow->u.arrow.lhs->u.arrow.rhs->t != T_EVENT) 840 return; 841 842 /* then try this arrow (thing cascaded *to*) */ 843 trylhs = arrow->u.arrow.lhs->u.arrow.rhs; 844 tryrhs = arrow->u.arrow.rhs; 845 break; 846 847 case T_EVENT: 848 case T_LIST: 849 trylhs = arrow->u.arrow.lhs; 850 tryrhs = arrow->u.arrow.rhs; 851 break; 852 853 default: 854 out(O_DIE, "lhs: unexpected type: %s", 855 ptree_nodetype2str(arrow->u.arrow.lhs->t)); 856 /*NOTREACHED*/ 857 } 858 859 check_cycle_lhs_try(stmtnp, trylhs, tryrhs); 860 } 861 862 /* 863 * check_cycle_lhs_try -- try matching an event name on lhs of an arrow 864 */ 865 866 static void 867 check_cycle_lhs_try(struct node *stmtnp, struct node *lhs, struct node *rhs) 868 { 869 if (lhs->t == T_LIST) { 870 check_cycle_lhs_try(stmtnp, lhs->u.expr.left, rhs); 871 check_cycle_lhs_try(stmtnp, lhs->u.expr.right, rhs); 872 return; 873 } 874 875 ASSERT(lhs->t == T_EVENT); 876 877 if (tree_eventcmp(stmtnp->u.stmt.np, lhs) != 0) 878 return; /* no match */ 879 880 check_cycle_rhs(rhs); 881 } 882 883 /* 884 * check_cycle_rhs -- foreach error on rhs, see if we cycle to a marked error 885 */ 886 887 static void 888 check_cycle_rhs(struct node *rhs) 889 { 890 struct node *dnp; 891 892 if (rhs->t == T_LIST) { 893 check_cycle_rhs(rhs->u.expr.left); 894 check_cycle_rhs(rhs->u.expr.right); 895 return; 896 } 897 898 ASSERT(rhs->t == T_EVENT); 899 900 if (rhs->u.event.ename->u.name.t != N_ERROR) 901 return; 902 903 if ((dnp = tree_event2np_lut_lookup(Errors, rhs)) == NULL) { 904 outfl(O_ERR|O_NONL, 905 rhs->file, rhs->line, 906 "unexpected undeclared event during cycle check"); 907 ptree_name(O_ERR|O_NONL, rhs); 908 out(O_ERR, NULL); 909 return; 910 } 911 check_cycle(NULL, dnp, 0); 912 } 913 914 /* 915 * Force iterators to be simple names, expressions, or numbers 916 */ 917 void 918 check_name_iterator(struct node *np) 919 { 920 if (np->u.name.child->t != T_NUM && 921 np->u.name.child->t != T_NAME && 922 np->u.name.child->t != T_CONDIF && 923 np->u.name.child->t != T_SUB && 924 np->u.name.child->t != T_ADD && 925 np->u.name.child->t != T_MUL && 926 np->u.name.child->t != T_DIV && 927 np->u.name.child->t != T_MOD && 928 np->u.name.child->t != T_LSHIFT && 929 np->u.name.child->t != T_RSHIFT) { 930 outfl(O_ERR|O_NONL, np->file, np->line, 931 "invalid iterator: "); 932 ptree_name_iter(O_ERR|O_NONL, np); 933 out(O_ERR, NULL); 934 } 935 } 936 937 /* 938 * Iterators on a declaration may only be implicit 939 */ 940 void 941 check_type_iterator(struct node *np) 942 { 943 while (np != NULL) { 944 if (np->t == T_EVENT) { 945 np = np->u.event.epname; 946 } else if (np->t == T_NAME) { 947 if (np->u.name.child != NULL && 948 np->u.name.child->t != T_NUM) { 949 outfl(O_ERR|O_NONL, np->file, np->line, 950 "explicit iterators disallowed " 951 "in declarations: "); 952 ptree_name_iter(O_ERR|O_NONL, np); 953 out(O_ERR, NULL); 954 } 955 np = np->u.name.next; 956 } else { 957 break; 958 } 959 } 960 } 961 962 void 963 check_func(struct node *np) 964 { 965 ASSERTinfo(np->t == T_FUNC, ptree_nodetype2str(np->t)); 966 967 if (np->u.func.s == L_within) { 968 struct node *arglist = np->u.func.arglist; 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 break; 976 case T_NAME: 977 if (arglist->u.name.s != L_infinity) 978 outfl(O_ERR, arglist->file, arglist->line, 979 "parameter of within must be 0" 980 ", \"infinity\" or a time value."); 981 break; 982 case T_LIST: 983 /* 984 * if two parameters, the left or min must be 985 * either T_NUM or T_TIMEVAL 986 */ 987 if (arglist->u.expr.left->t != T_NUM && 988 arglist->u.expr.left->t != T_TIMEVAL) 989 outfl(O_ERR, 990 arglist->file, arglist->line, 991 "first parameter of within must be" 992 " either a time value or zero."); 993 994 /* 995 * if two parameters, the right or max must 996 * be either T_NUM, T_NAME or T_TIMEVAL 997 */ 998 if (arglist->u.expr.right->t != T_NUM && 999 arglist->u.expr.right->t != T_TIMEVAL && 1000 arglist->u.expr.right->t != T_NAME) 1001 outfl(O_ERR, 1002 arglist->file, arglist->line, 1003 "second parameter of within must " 1004 "be 0, \"infinity\" " 1005 "or time value."); 1006 1007 /* 1008 * if right or left is a T_NUM it must 1009 * be zero 1010 */ 1011 if (arglist->u.expr.left->t == T_NUM) 1012 if (arglist->u.expr.left->u.ull != 0ULL) 1013 outfl(O_ERR, 1014 arglist->file, arglist->line, 1015 "within parameter must be 0 or" 1016 " a time value."); 1017 if (arglist->u.expr.right->t == T_NUM) 1018 if (arglist->u.expr.right->u.ull 1019 != 0ULL) 1020 outfl(O_ERR, 1021 arglist->file, arglist->line, 1022 "within parameter must be 0 or" 1023 " a time value."); 1024 1025 /* if right is a T_NAME it must be "infinity" */ 1026 if (arglist->u.expr.right->t == T_NAME) 1027 if (arglist->u.expr.right->u.name.s 1028 != L_infinity) 1029 outfl(O_ERR, 1030 arglist->file, arglist->line, 1031 "\"infinity\" is the only valid" 1032 " name for within parameter."); 1033 1034 /* 1035 * the first parameter [min] must not be greater 1036 * than the second parameter [max]. 1037 */ 1038 if (arglist->u.expr.left->u.ull > 1039 arglist->u.expr.right->u.ull) 1040 outfl(O_ERR, 1041 arglist->file, arglist->line, 1042 "the first value (min) of" 1043 " within must be less than" 1044 " the second (max) value"); 1045 break; 1046 case T_TIMEVAL: 1047 break; /* no restrictions on T_TIMEVAL */ 1048 default: 1049 outfl(O_ERR, arglist->file, arglist->line, 1050 "parameter of within must be 0" 1051 ", \"infinity\" or a time value."); 1052 } 1053 } else if (np->u.func.s == L_call) { 1054 if (np->u.func.arglist->t != T_QUOTE && 1055 np->u.func.arglist->t != T_LIST && 1056 np->u.func.arglist->t != T_GLOBID && 1057 np->u.func.arglist->t != T_CONDIF && 1058 np->u.func.arglist->t != T_LIST && 1059 np->u.func.arglist->t != T_FUNC) 1060 outfl(O_ERR, np->u.func.arglist->file, 1061 np->u.func.arglist->line, 1062 "invalid first argument to call()"); 1063 } else if (np->u.func.s == L_fru) { 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 fru() must be a path"); 1068 } else if (np->u.func.s == L_asru) { 1069 if (np->u.func.arglist->t != T_NAME) 1070 outfl(O_ERR, np->u.func.arglist->file, 1071 np->u.func.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 (np->u.func.arglist->t == T_LIST && 1076 (np->u.func.arglist->u.expr.left->t == T_NAME || 1077 (np->u.func.arglist->u.expr.left->t == T_FUNC && 1078 (np->u.func.arglist->u.expr.left->u.func.s == L_fru || 1079 np->u.func.arglist->u.expr.left->u.func.s == L_asru))) && 1080 (np->u.func.arglist->u.expr.right->t == T_NAME || 1081 (np->u.func.arglist->u.expr.right->t == T_FUNC && 1082 (np->u.func.arglist->u.expr.right->u.func.s == L_fru || 1083 np->u.func.arglist->u.expr.right->u.func.s == L_asru)))) { 1084 if (np->u.func.arglist->u.expr.left->t == T_FUNC) 1085 check_func(np->u.func.arglist->u.expr.left); 1086 if (np->u.func.arglist->u.expr.right->t == T_FUNC) 1087 check_func(np->u.func.arglist->u.expr.right); 1088 } else { 1089 outfl(O_ERR, np->u.func.arglist->file, 1090 np->u.func.arglist->line, 1091 "%s() must have paths or calls to " 1092 "fru() and/or asru() as arguments", 1093 np->u.func.s); 1094 } 1095 } else if (np->u.func.s == L_is_on) { 1096 if (np->u.func.arglist->t == T_FUNC && 1097 (np->u.func.arglist->u.func.s == L_fru || 1098 np->u.func.arglist->u.func.s == L_asru)) { 1099 check_func(np->u.func.arglist); 1100 } else { 1101 outfl(O_ERR, np->u.func.arglist->file, 1102 np->u.func.arglist->line, 1103 "argument to is_on() must be a call to " 1104 "fru() or asru()"); 1105 } 1106 } else if (np->u.func.s == L_is_present) { 1107 if (np->u.func.arglist->t == T_FUNC && 1108 (np->u.func.arglist->u.func.s == L_fru || 1109 np->u.func.arglist->u.func.s == L_asru)) { 1110 check_func(np->u.func.arglist); 1111 } else { 1112 outfl(O_ERR, np->u.func.arglist->file, 1113 np->u.func.arglist->line, 1114 "argument to is_present() must be a call to " 1115 "fru() or asru()"); 1116 } 1117 } else if (np->u.func.s == L_is_type) { 1118 if (np->u.func.arglist->t == T_FUNC && 1119 (np->u.func.arglist->u.func.s == L_fru || 1120 np->u.func.arglist->u.func.s == L_asru)) { 1121 check_func(np->u.func.arglist); 1122 } else { 1123 outfl(O_ERR, np->u.func.arglist->file, 1124 np->u.func.arglist->line, 1125 "argument to is_type() must be a call to " 1126 "fru() or asru()"); 1127 } 1128 } else if (np->u.func.s == L_confcall) { 1129 if (np->u.func.arglist->t != T_QUOTE && 1130 (np->u.func.arglist->t != T_LIST || 1131 np->u.func.arglist->u.expr.left->t != T_QUOTE)) 1132 outfl(O_ERR, np->u.func.arglist->file, 1133 np->u.func.arglist->line, 1134 "confcall(): first argument must be a string " 1135 "(the name of the operation)"); 1136 } else if (np->u.func.s == L_confprop) { 1137 if (np->u.func.arglist->t == T_LIST && 1138 (np->u.func.arglist->u.expr.left->t == T_FUNC && 1139 (np->u.func.arglist->u.expr.left->u.func.s == L_fru || 1140 np->u.func.arglist->u.expr.left->u.func.s == L_asru)) && 1141 np->u.func.arglist->u.expr.right->t == T_QUOTE) { 1142 check_func(np->u.func.arglist->u.expr.left); 1143 } else { 1144 outfl(O_ERR, np->u.func.arglist->file, 1145 np->u.func.arglist->line, 1146 "confprop(): first argument must be a call to " 1147 "fru() or asru(); " 1148 "second argument must be a string"); 1149 } 1150 } else if (np->u.func.s == L_count) { 1151 if (np->u.func.arglist->t != T_EVENT) { 1152 outfl(O_ERR, np->u.func.arglist->file, 1153 np->u.func.arglist->line, 1154 "count(): argument must be an engine name"); 1155 } 1156 } else if (np->u.func.s == L_defined) { 1157 if (np->u.func.arglist->t != T_GLOBID) 1158 outfl(O_ERR, np->u.func.arglist->file, 1159 np->u.func.arglist->line, 1160 "argument to defined() must be a global"); 1161 } else if (np->u.func.s == L_payloadprop) { 1162 if (np->u.func.arglist->t != T_QUOTE) 1163 outfl(O_ERR, np->u.func.arglist->file, 1164 np->u.func.arglist->line, 1165 "argument to payloadprop() must be a string"); 1166 } else if (np->u.func.s == L_payloadprop_contains) { 1167 if (np->u.func.arglist->t != T_LIST || 1168 np->u.func.arglist->u.expr.left->t != T_QUOTE || 1169 np->u.func.arglist->u.expr.right == NULL) 1170 outfl(O_ERR, np->u.func.arglist->file, 1171 np->u.func.arglist->line, 1172 "args to payloadprop_contains(): must be a quoted " 1173 "string (property name) and an expression " 1174 "(to match)"); 1175 } else if (np->u.func.s == L_payloadprop_defined) { 1176 if (np->u.func.arglist->t != T_QUOTE) 1177 outfl(O_ERR, np->u.func.arglist->file, 1178 np->u.func.arglist->line, 1179 "arg to payloadprop_defined(): must be a quoted " 1180 "string"); 1181 } else if (np->u.func.s == L_setpayloadprop) { 1182 if (np->u.func.arglist->t == T_LIST && 1183 np->u.func.arglist->u.expr.left->t == T_QUOTE) { 1184 if (np->u.func.arglist->u.expr.right->t == T_FUNC) 1185 check_func(np->u.func.arglist->u.expr.right); 1186 } else { 1187 outfl(O_ERR, np->u.func.arglist->file, 1188 np->u.func.arglist->line, 1189 "setpayloadprop(): " 1190 "first arg must be a string, " 1191 "second arg a value"); 1192 } 1193 } else if (np->u.func.s == L_envprop) { 1194 if (np->u.func.arglist->t != T_QUOTE) 1195 outfl(O_ERR, np->u.func.arglist->file, 1196 np->u.func.arglist->line, 1197 "argument to envprop() must be a string"); 1198 } else 1199 outfl(O_WARN, np->file, np->line, 1200 "possible platform-specific function: %s", 1201 np->u.func.s); 1202 } 1203 1204 void 1205 check_expr(struct node *np) 1206 { 1207 ASSERT(np != NULL); 1208 1209 switch (np->t) { 1210 case T_ASSIGN: 1211 ASSERT(np->u.expr.left != NULL); 1212 if (np->u.expr.left->t != T_GLOBID) 1213 outfl(O_ERR, np->file, np->line, 1214 "assignment only allowed to globals (e.g. $a)"); 1215 break; 1216 } 1217 } 1218 1219 void 1220 check_event(struct node *np) 1221 { 1222 ASSERT(np != NULL); 1223 ASSERTinfo(np->t == T_EVENT, ptree_nodetype2str(np->t)); 1224 1225 if (np->u.event.epname == NULL) { 1226 outfl(O_ERR|O_NONL, np->file, np->line, 1227 "pathless events not allowed: "); 1228 ptree_name(O_ERR|O_NONL, np->u.event.ename); 1229 out(O_ERR, NULL); 1230 } 1231 } 1232 1233 /* 1234 * check for properties that are required on declarations. This 1235 * should be done after all declarations since they can be 1236 * redeclared with a different set of properties. 1237 */ 1238 /*ARGSUSED*/ 1239 void 1240 check_required_props(struct node *lhs, struct node *rhs, void *arg) 1241 { 1242 enum nodetype t = (enum nodetype)arg; 1243 1244 ASSERTeq(rhs->t, t, ptree_nodetype2str); 1245 1246 check_stmt_required_properties(rhs); 1247 } 1248 1249 /* 1250 * check that cascading prop statements do not contain lists internally. 1251 * the first and last event lists in the cascading prop may be single 1252 * events or lists of events. 1253 */ 1254 /*ARGSUSED*/ 1255 void 1256 check_proplists(enum nodetype t, struct node *np) 1257 { 1258 ASSERT(np->t == T_ARROW); 1259 /* 1260 * not checking the right hand side of the top level prop 1261 * since it is the last part of the propagation and can be 1262 * an event or list of events 1263 */ 1264 check_proplists_lhs(t, np->u.arrow.lhs); 1265 } 1266 1267 /*ARGSUSED*/ 1268 static void 1269 check_proplists_lhs(enum nodetype t, struct node *lhs) 1270 { 1271 if (lhs->t == T_ARROW) { 1272 if (lhs->u.arrow.rhs->t == T_LIST) { 1273 outfl(O_ERR, lhs->file, lhs->line, 1274 "lists are not allowed internally on" 1275 " cascading %s", 1276 (t == T_PROP) ? "propagations" : "masks"); 1277 } 1278 check_proplists_lhs(t, lhs->u.arrow.lhs); 1279 } 1280 } 1281