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