1 /* 2 * testcode/replay.c - store and use a replay of events for the DNS resolver. 3 * 4 * Copyright (c) 2007, NLnet Labs. All rights reserved. 5 * 6 * This software is open source. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * Redistributions of source code must retain the above copyright notice, 13 * this list of conditions and the following disclaimer. 14 * 15 * Redistributions in binary form must reproduce the above copyright notice, 16 * this list of conditions and the following disclaimer in the documentation 17 * and/or other materials provided with the distribution. 18 * 19 * Neither the name of the NLNET LABS nor the names of its contributors may 20 * be used to endorse or promote products derived from this software without 21 * specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 29 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 */ 35 36 /** 37 * \file 38 * Store and use a replay of events for the DNS resolver. 39 * Used to test known scenarios to get known outcomes. 40 */ 41 42 #include "config.h" 43 /* for strtod prototype */ 44 #include <math.h> 45 #include <ctype.h> 46 #include <time.h> 47 #include "util/log.h" 48 #include "util/net_help.h" 49 #include "util/config_file.h" 50 #include "testcode/replay.h" 51 #include "testcode/testpkts.h" 52 #include "testcode/fake_event.h" 53 #include "sldns/str2wire.h" 54 #include "util/timeval_func.h" 55 56 /** max length of lines in file */ 57 #define MAX_LINE_LEN 10240 58 59 /** 60 * Expand a macro 61 * @param store: value storage 62 * @param runtime: replay runtime for other stuff. 63 * @param text: the macro text, after the ${, Updated to after the } when 64 * done (successfully). 65 * @return expanded text, malloced. NULL on failure. 66 */ 67 static char* macro_expand(rbtree_type* store, 68 struct replay_runtime* runtime, char** text); 69 70 /** parse keyword in string. 71 * @param line: if found, the line is advanced to after the keyword. 72 * @param keyword: string. 73 * @return: true if found, false if not. 74 */ 75 static int 76 parse_keyword(char** line, const char* keyword) 77 { 78 size_t len = (size_t)strlen(keyword); 79 if(strncmp(*line, keyword, len) == 0) { 80 *line += len; 81 return 1; 82 } 83 return 0; 84 } 85 86 /** delete moment */ 87 static void 88 replay_moment_delete(struct replay_moment* mom) 89 { 90 if(!mom) 91 return; 92 if(mom->match) { 93 delete_entry(mom->match); 94 } 95 free(mom->autotrust_id); 96 free(mom->string); 97 free(mom->variable); 98 config_delstrlist(mom->file_content); 99 free(mom); 100 } 101 102 /** delete range */ 103 static void 104 replay_range_delete(struct replay_range* rng) 105 { 106 if(!rng) 107 return; 108 delete_entry(rng->match); 109 free(rng); 110 } 111 112 void 113 strip_end_white(char* p) 114 { 115 size_t i; 116 for(i = strlen(p); i > 0; i--) { 117 if(isspace((unsigned char)p[i-1])) 118 p[i-1] = 0; 119 else return; 120 } 121 } 122 123 /** 124 * Read a range from file. 125 * @param remain: Rest of line (after RANGE keyword). 126 * @param in: file to read from. 127 * @param name: name to print in errors. 128 * @param pstate: read state structure with 129 * with lineno : incremented as lines are read. 130 * ttl, origin, prev for readentry. 131 * @param line: line buffer. 132 * @return: range object to add to list, or NULL on error. 133 */ 134 static struct replay_range* 135 replay_range_read(char* remain, FILE* in, const char* name, 136 struct sldns_file_parse_state* pstate, char* line) 137 { 138 struct replay_range* rng = (struct replay_range*)malloc( 139 sizeof(struct replay_range)); 140 off_t pos; 141 char *parse; 142 struct entry* entry, *last = NULL; 143 if(!rng) 144 return NULL; 145 memset(rng, 0, sizeof(*rng)); 146 /* read time range */ 147 if(sscanf(remain, " %d %d", &rng->start_step, &rng->end_step)!=2) { 148 log_err("Could not read time range: %s", line); 149 free(rng); 150 return NULL; 151 } 152 /* read entries */ 153 pos = ftello(in); 154 while(fgets(line, MAX_LINE_LEN-1, in)) { 155 pstate->lineno++; 156 parse = line; 157 while(isspace((unsigned char)*parse)) 158 parse++; 159 if(!*parse || *parse == ';') { 160 pos = ftello(in); 161 continue; 162 } 163 if(parse_keyword(&parse, "ADDRESS")) { 164 while(isspace((unsigned char)*parse)) 165 parse++; 166 strip_end_white(parse); 167 if(!extstrtoaddr(parse, &rng->addr, &rng->addrlen, 168 UNBOUND_DNS_PORT)) { 169 log_err("Line %d: could not read ADDRESS: %s", 170 pstate->lineno, parse); 171 free(rng); 172 return NULL; 173 } 174 pos = ftello(in); 175 continue; 176 } 177 if(parse_keyword(&parse, "RANGE_END")) { 178 return rng; 179 } 180 /* set position before line; read entry */ 181 pstate->lineno--; 182 fseeko(in, pos, SEEK_SET); 183 entry = read_entry(in, name, pstate, 1); 184 if(!entry) 185 fatal_exit("%d: bad entry", pstate->lineno); 186 entry->next = NULL; 187 if(last) 188 last->next = entry; 189 else rng->match = entry; 190 last = entry; 191 192 pos = ftello(in); 193 } 194 replay_range_delete(rng); 195 return NULL; 196 } 197 198 /** Read FILE match content */ 199 static void 200 read_file_content(FILE* in, int* lineno, struct replay_moment* mom) 201 { 202 char line[MAX_LINE_LEN]; 203 char* remain = line; 204 struct config_strlist** last = &mom->file_content; 205 line[MAX_LINE_LEN-1]=0; 206 if(!fgets(line, MAX_LINE_LEN-1, in)) 207 fatal_exit("FILE_BEGIN expected at line %d", *lineno); 208 if(!parse_keyword(&remain, "FILE_BEGIN")) 209 fatal_exit("FILE_BEGIN expected at line %d", *lineno); 210 while(fgets(line, MAX_LINE_LEN-1, in)) { 211 (*lineno)++; 212 if(strncmp(line, "FILE_END", 8) == 0) { 213 return; 214 } 215 strip_end_white(line); 216 if(!cfg_strlist_insert(last, strdup(line))) 217 fatal_exit("malloc failure"); 218 last = &( (*last)->next ); 219 } 220 fatal_exit("no FILE_END in input file"); 221 } 222 223 /** read assign step info */ 224 static void 225 read_assign_step(char* remain, struct replay_moment* mom) 226 { 227 char buf[1024]; 228 char eq; 229 int skip; 230 buf[sizeof(buf)-1]=0; 231 if(sscanf(remain, " %1023s %c %n", buf, &eq, &skip) != 2) 232 fatal_exit("cannot parse assign: %s", remain); 233 mom->variable = strdup(buf); 234 if(eq != '=') 235 fatal_exit("no '=' in assign: %s", remain); 236 remain += skip; 237 strip_end_white(remain); 238 mom->string = strdup(remain); 239 if(!mom->variable || !mom->string) 240 fatal_exit("out of memory"); 241 } 242 243 /** 244 * Read a replay moment 'STEP' from file. 245 * @param remain: Rest of line (after STEP keyword). 246 * @param in: file to read from. 247 * @param name: name to print in errors. 248 * @param pstate: with lineno, ttl, origin, prev for parse state. 249 * lineno is incremented. 250 * @return: range object to add to list, or NULL on error. 251 */ 252 static struct replay_moment* 253 replay_moment_read(char* remain, FILE* in, const char* name, 254 struct sldns_file_parse_state* pstate) 255 { 256 struct replay_moment* mom = (struct replay_moment*)malloc( 257 sizeof(struct replay_moment)); 258 int skip = 0; 259 int readentry = 0; 260 if(!mom) 261 return NULL; 262 memset(mom, 0, sizeof(*mom)); 263 if(sscanf(remain, " %d%n", &mom->time_step, &skip) != 1) { 264 log_err("%d: cannot read number: %s", pstate->lineno, remain); 265 free(mom); 266 return NULL; 267 } 268 remain += skip; 269 while(isspace((unsigned char)*remain)) 270 remain++; 271 if(parse_keyword(&remain, "NOTHING")) { 272 mom->evt_type = repevt_nothing; 273 } else if(parse_keyword(&remain, "QUERY")) { 274 mom->evt_type = repevt_front_query; 275 readentry = 1; 276 if(!extstrtoaddr("127.0.0.1", &mom->addr, &mom->addrlen, 277 UNBOUND_DNS_PORT)) 278 fatal_exit("internal error"); 279 } else if(parse_keyword(&remain, "CHECK_ANSWER")) { 280 mom->evt_type = repevt_front_reply; 281 readentry = 1; 282 } else if(parse_keyword(&remain, "CHECK_OUT_QUERY")) { 283 mom->evt_type = repevt_back_query; 284 readentry = 1; 285 } else if(parse_keyword(&remain, "REPLY")) { 286 mom->evt_type = repevt_back_reply; 287 readentry = 1; 288 } else if(parse_keyword(&remain, "TIMEOUT")) { 289 mom->evt_type = repevt_timeout; 290 } else if(parse_keyword(&remain, "TIME_PASSES")) { 291 mom->evt_type = repevt_time_passes; 292 while(isspace((unsigned char)*remain)) 293 remain++; 294 if(parse_keyword(&remain, "EVAL")) { 295 while(isspace((unsigned char)*remain)) 296 remain++; 297 mom->string = strdup(remain); 298 if(!mom->string) fatal_exit("out of memory"); 299 if(strlen(mom->string)>0) 300 mom->string[strlen(mom->string)-1]=0; 301 remain += strlen(mom->string); 302 } 303 } else if(parse_keyword(&remain, "CHECK_AUTOTRUST")) { 304 mom->evt_type = repevt_autotrust_check; 305 while(isspace((unsigned char)*remain)) 306 remain++; 307 strip_end_white(remain); 308 mom->autotrust_id = strdup(remain); 309 if(!mom->autotrust_id) fatal_exit("out of memory"); 310 read_file_content(in, &pstate->lineno, mom); 311 } else if(parse_keyword(&remain, "CHECK_TEMPFILE")) { 312 mom->evt_type = repevt_tempfile_check; 313 while(isspace((unsigned char)*remain)) 314 remain++; 315 strip_end_white(remain); 316 mom->autotrust_id = strdup(remain); 317 if(!mom->autotrust_id) fatal_exit("out of memory"); 318 read_file_content(in, &pstate->lineno, mom); 319 } else if(parse_keyword(&remain, "ERROR")) { 320 mom->evt_type = repevt_error; 321 } else if(parse_keyword(&remain, "TRAFFIC")) { 322 mom->evt_type = repevt_traffic; 323 } else if(parse_keyword(&remain, "ASSIGN")) { 324 mom->evt_type = repevt_assign; 325 read_assign_step(remain, mom); 326 } else if(parse_keyword(&remain, "INFRA_RTT")) { 327 char *s, *m; 328 mom->evt_type = repevt_infra_rtt; 329 while(isspace((unsigned char)*remain)) 330 remain++; 331 s = remain; 332 remain = strchr(s, ' '); 333 if(!remain) fatal_exit("expected three args for INFRA_RTT"); 334 remain[0] = 0; 335 remain++; 336 while(isspace((unsigned char)*remain)) 337 remain++; 338 m = strchr(remain, ' '); 339 if(!m) fatal_exit("expected three args for INFRA_RTT"); 340 m[0] = 0; 341 m++; 342 while(isspace((unsigned char)*m)) 343 m++; 344 if(!extstrtoaddr(s, &mom->addr, &mom->addrlen, UNBOUND_DNS_PORT)) 345 fatal_exit("bad infra_rtt address %s", s); 346 strip_end_white(m); 347 mom->variable = strdup(remain); 348 mom->string = strdup(m); 349 if(!mom->string) fatal_exit("out of memory"); 350 if(!mom->variable) fatal_exit("out of memory"); 351 } else if(parse_keyword(&remain, "FLUSH_MESSAGE")) { 352 mom->evt_type = repevt_flush_message; 353 while(isspace((unsigned char)*remain)) 354 remain++; 355 strip_end_white(remain); 356 mom->string = strdup(remain); 357 if(!mom->string) fatal_exit("out of memory"); 358 } else if(parse_keyword(&remain, "EXPIRE_MESSAGE")) { 359 mom->evt_type = repevt_expire_message; 360 while(isspace((unsigned char)*remain)) 361 remain++; 362 strip_end_white(remain); 363 mom->string = strdup(remain); 364 if(!mom->string) fatal_exit("out of memory"); 365 } else { 366 log_err("%d: unknown event type %s", pstate->lineno, remain); 367 free(mom); 368 return NULL; 369 } 370 while(isspace((unsigned char)*remain)) 371 remain++; 372 if(parse_keyword(&remain, "ADDRESS")) { 373 while(isspace((unsigned char)*remain)) 374 remain++; 375 strip_end_white(remain); 376 if(!extstrtoaddr(remain, &mom->addr, &mom->addrlen, 377 UNBOUND_DNS_PORT)) { 378 log_err("line %d: could not parse ADDRESS: %s", 379 pstate->lineno, remain); 380 free(mom); 381 return NULL; 382 } 383 } 384 if(parse_keyword(&remain, "ELAPSE")) { 385 double sec; 386 errno = 0; 387 sec = strtod(remain, &remain); 388 if(sec == 0. && errno != 0) { 389 log_err("line %d: could not parse ELAPSE: %s (%s)", 390 pstate->lineno, remain, strerror(errno)); 391 free(mom); 392 return NULL; 393 } 394 #ifndef S_SPLINT_S 395 mom->elapse.tv_sec = (int)sec; 396 mom->elapse.tv_usec = (int)((sec - (double)mom->elapse.tv_sec) 397 *1000000. + 0.5); 398 #endif 399 } 400 401 if(readentry) { 402 mom->match = read_entry(in, name, pstate, 1); 403 if(!mom->match) { 404 free(mom); 405 return NULL; 406 } 407 } 408 409 return mom; 410 } 411 412 /** makes scenario with title on rest of line */ 413 static struct replay_scenario* 414 make_scenario(char* line) 415 { 416 struct replay_scenario* scen; 417 while(isspace((unsigned char)*line)) 418 line++; 419 if(!*line) { 420 log_err("scenario: no title given"); 421 return NULL; 422 } 423 scen = (struct replay_scenario*)malloc(sizeof(struct replay_scenario)); 424 if(!scen) 425 return NULL; 426 memset(scen, 0, sizeof(*scen)); 427 scen->title = strdup(line); 428 if(!scen->title) { 429 free(scen); 430 return NULL; 431 } 432 return scen; 433 } 434 435 struct replay_scenario* 436 replay_scenario_read(FILE* in, const char* name, int* lineno) 437 { 438 char line[MAX_LINE_LEN]; 439 char *parse; 440 struct replay_scenario* scen = NULL; 441 struct sldns_file_parse_state pstate; 442 line[MAX_LINE_LEN-1]=0; 443 memset(&pstate, 0, sizeof(pstate)); 444 pstate.default_ttl = 3600; 445 pstate.lineno = *lineno; 446 447 while(fgets(line, MAX_LINE_LEN-1, in)) { 448 parse=line; 449 pstate.lineno++; 450 (*lineno)++; 451 while(isspace((unsigned char)*parse)) 452 parse++; 453 if(!*parse) 454 continue; /* empty line */ 455 if(parse_keyword(&parse, ";")) 456 continue; /* comment */ 457 if(parse_keyword(&parse, "SCENARIO_BEGIN")) { 458 if(scen) 459 fatal_exit("%d: double SCENARIO_BEGIN", *lineno); 460 scen = make_scenario(parse); 461 if(!scen) 462 fatal_exit("%d: could not make scen", *lineno); 463 continue; 464 } 465 if(!scen) 466 fatal_exit("%d: expected SCENARIO", *lineno); 467 if(parse_keyword(&parse, "RANGE_BEGIN")) { 468 struct replay_range* newr = replay_range_read(parse, 469 in, name, &pstate, line); 470 if(!newr) 471 fatal_exit("%d: bad range", pstate.lineno); 472 *lineno = pstate.lineno; 473 newr->next_range = scen->range_list; 474 scen->range_list = newr; 475 } else if(parse_keyword(&parse, "STEP")) { 476 struct replay_moment* mom = replay_moment_read(parse, 477 in, name, &pstate); 478 if(!mom) 479 fatal_exit("%d: bad moment", pstate.lineno); 480 *lineno = pstate.lineno; 481 if(scen->mom_last && 482 scen->mom_last->time_step >= mom->time_step) 483 fatal_exit("%d: time goes backwards", *lineno); 484 if(scen->mom_last) 485 scen->mom_last->mom_next = mom; 486 else scen->mom_first = mom; 487 scen->mom_last = mom; 488 } else if(parse_keyword(&parse, "SCENARIO_END")) { 489 struct replay_moment *p = scen->mom_first; 490 int num = 0; 491 while(p) { 492 num++; 493 p = p->mom_next; 494 } 495 log_info("Scenario has %d steps", num); 496 return scen; 497 } 498 } 499 log_err("scenario read failed at line %d (no SCENARIO_END?)", *lineno); 500 replay_scenario_delete(scen); 501 return NULL; 502 } 503 504 void 505 replay_scenario_delete(struct replay_scenario* scen) 506 { 507 struct replay_moment* mom, *momn; 508 struct replay_range* rng, *rngn; 509 if(!scen) 510 return; 511 free(scen->title); 512 mom = scen->mom_first; 513 while(mom) { 514 momn = mom->mom_next; 515 replay_moment_delete(mom); 516 mom = momn; 517 } 518 rng = scen->range_list; 519 while(rng) { 520 rngn = rng->next_range; 521 replay_range_delete(rng); 522 rng = rngn; 523 } 524 free(scen); 525 } 526 527 /** fetch oldest timer in list that is enabled */ 528 static struct fake_timer* 529 first_timer(struct replay_runtime* runtime) 530 { 531 struct fake_timer* p, *res = NULL; 532 for(p=runtime->timer_list; p; p=p->next) { 533 if(!p->enabled) 534 continue; 535 if(!res) 536 res = p; 537 else if(timeval_smaller(&p->tv, &res->tv)) 538 res = p; 539 } 540 return res; 541 } 542 543 struct fake_timer* 544 replay_get_oldest_timer(struct replay_runtime* runtime) 545 { 546 struct fake_timer* t = first_timer(runtime); 547 if(t && timeval_smaller(&t->tv, &runtime->now_tv)) 548 return t; 549 return NULL; 550 } 551 552 int 553 replay_var_compare(const void* a, const void* b) 554 { 555 struct replay_var* x = (struct replay_var*)a; 556 struct replay_var* y = (struct replay_var*)b; 557 return strcmp(x->name, y->name); 558 } 559 560 rbtree_type* 561 macro_store_create(void) 562 { 563 return rbtree_create(&replay_var_compare); 564 } 565 566 /** helper function to delete macro values */ 567 static void 568 del_macro(rbnode_type* x, void* ATTR_UNUSED(arg)) 569 { 570 struct replay_var* v = (struct replay_var*)x; 571 free(v->name); 572 free(v->value); 573 free(v); 574 } 575 576 void 577 macro_store_delete(rbtree_type* store) 578 { 579 if(!store) 580 return; 581 traverse_postorder(store, del_macro, NULL); 582 free(store); 583 } 584 585 /** return length of macro */ 586 static size_t 587 macro_length(char* text) 588 { 589 /* we are after ${, looking for } */ 590 int depth = 0; 591 size_t len = 0; 592 while(*text) { 593 len++; 594 if(*text == '}') { 595 if(depth == 0) 596 break; 597 depth--; 598 } else if(text[0] == '$' && text[1] == '{') { 599 depth++; 600 } 601 text++; 602 } 603 return len; 604 } 605 606 /** insert new stuff at start of buffer */ 607 static int 608 do_buf_insert(char* buf, size_t remain, char* after, char* inserted) 609 { 610 char* save = strdup(after); 611 size_t len; 612 if(!save) return 0; 613 if(strlen(inserted) > remain) { 614 free(save); 615 return 0; 616 } 617 len = strlcpy(buf, inserted, remain); 618 buf += len; 619 remain -= len; 620 (void)strlcpy(buf, save, remain); 621 free(save); 622 return 1; 623 } 624 625 /** do macro recursion */ 626 static char* 627 do_macro_recursion(rbtree_type* store, struct replay_runtime* runtime, 628 char* at, size_t remain) 629 { 630 char* after = at+2; 631 char* expand = macro_expand(store, runtime, &after); 632 if(!expand) 633 return NULL; /* expansion failed */ 634 if(!do_buf_insert(at, remain, after, expand)) { 635 free(expand); 636 return NULL; 637 } 638 free(expand); 639 return at; /* and parse over the expanded text to see if again */ 640 } 641 642 /** get var from store */ 643 static struct replay_var* 644 macro_getvar(rbtree_type* store, char* name) 645 { 646 struct replay_var k; 647 k.node.key = &k; 648 k.name = name; 649 return (struct replay_var*)rbtree_search(store, &k); 650 } 651 652 /** do macro variable */ 653 static char* 654 do_macro_variable(rbtree_type* store, char* buf, size_t remain) 655 { 656 struct replay_var* v; 657 char* at = buf+1; 658 char* name = at; 659 char sv; 660 if(at[0]==0) 661 return NULL; /* no variable name after $ */ 662 while(*at && (isalnum((unsigned char)*at) || *at=='_')) { 663 at++; 664 } 665 /* terminator, we are working in macro_expand() buffer */ 666 sv = *at; 667 *at = 0; 668 v = macro_getvar(store, name); 669 *at = sv; 670 671 if(!v) { 672 log_err("variable is not defined: $%s", name); 673 return NULL; /* variable undefined is error for now */ 674 } 675 676 /* insert the variable contents */ 677 if(!do_buf_insert(buf, remain, at, v->value)) 678 return NULL; 679 return buf; /* and expand the variable contents */ 680 } 681 682 /** do ctime macro on argument */ 683 static char* 684 do_macro_ctime(char* arg) 685 { 686 char buf[32]; 687 time_t tt = (time_t)atoi(arg); 688 if(tt == 0 && strcmp(arg, "0") != 0) { 689 log_err("macro ctime: expected number, not: %s", arg); 690 return NULL; 691 } 692 ctime_r(&tt, buf); 693 #ifdef USE_WINSOCK 694 if(strlen(buf) > 10 && buf[7]==' ' && buf[8]=='0') 695 buf[8]=' '; /* fix error in windows ctime */ 696 #endif 697 strip_end_white(buf); 698 return strdup(buf); 699 } 700 701 /** perform arithmetic operator */ 702 static double 703 perform_arith(double x, char op, double y, double* res) 704 { 705 switch(op) { 706 case '+': 707 *res = x+y; 708 break; 709 case '-': 710 *res = x-y; 711 break; 712 case '/': 713 *res = x/y; 714 break; 715 case '*': 716 *res = x*y; 717 break; 718 default: 719 *res = 0; 720 return 0; 721 } 722 723 return 1; 724 } 725 726 /** do macro arithmetic on two numbers and operand */ 727 static char* 728 do_macro_arith(char* orig, size_t remain, char** arithstart) 729 { 730 double x, y, result; 731 char operator; 732 int skip; 733 char buf[32]; 734 char* at; 735 /* not yet done? we want number operand number expanded first. */ 736 if(!*arithstart) { 737 /* remember start pos of expr, skip the first number */ 738 at = orig; 739 *arithstart = at; 740 while(*at && (isdigit((unsigned char)*at) || *at == '.')) 741 at++; 742 return at; 743 } 744 /* move back to start */ 745 remain += (size_t)(orig - *arithstart); 746 at = *arithstart; 747 748 /* parse operands */ 749 if(sscanf(at, " %lf %c %lf%n", &x, &operator, &y, &skip) != 3) { 750 *arithstart = NULL; 751 return do_macro_arith(orig, remain, arithstart); 752 } 753 if(isdigit((unsigned char)operator)) { 754 *arithstart = orig; 755 return at+skip; /* do nothing, but setup for later number */ 756 } 757 758 /* calculate result */ 759 if(!perform_arith(x, operator, y, &result)) { 760 log_err("unknown operator: %s", at); 761 return NULL; 762 } 763 764 /* put result back in buffer */ 765 snprintf(buf, sizeof(buf), "%.12g", result); 766 if(!do_buf_insert(at, remain, at+skip, buf)) 767 return NULL; 768 769 /* the result can be part of another expression, restart that */ 770 *arithstart = NULL; 771 return at; 772 } 773 774 /** Do range macro on expanded buffer */ 775 static char* 776 do_macro_range(char* buf) 777 { 778 double x, y, z; 779 if(sscanf(buf, " %lf %lf %lf", &x, &y, &z) != 3) { 780 log_err("range func requires 3 args: %s", buf); 781 return NULL; 782 } 783 if(x <= y && y <= z) { 784 char res[1024]; 785 snprintf(res, sizeof(res), "%.24g", y); 786 return strdup(res); 787 } 788 fatal_exit("value %.24g not in range [%.24g, %.24g]", y, x, z); 789 return NULL; 790 } 791 792 static char* 793 macro_expand(rbtree_type* store, struct replay_runtime* runtime, char** text) 794 { 795 char buf[10240]; 796 char* at = *text; 797 size_t len = macro_length(at); 798 int dofunc = 0; 799 char* arithstart = NULL; 800 if(len >= sizeof(buf)) 801 return NULL; /* too long */ 802 buf[0] = 0; 803 (void)strlcpy(buf, at, len+1-1); /* do not copy last '}' character */ 804 at = buf; 805 806 /* check for functions */ 807 if(strcmp(buf, "time") == 0) { 808 if(runtime) 809 snprintf(buf, sizeof(buf), ARG_LL "d", (long long)runtime->now_secs); 810 else 811 snprintf(buf, sizeof(buf), ARG_LL "d", (long long)0); 812 *text += len; 813 return strdup(buf); 814 } else if(strcmp(buf, "timeout") == 0) { 815 time_t res = 0; 816 if(runtime) { 817 struct fake_timer* t = first_timer(runtime); 818 if(t && (time_t)t->tv.tv_sec >= runtime->now_secs) 819 res = (time_t)t->tv.tv_sec - runtime->now_secs; 820 } 821 snprintf(buf, sizeof(buf), ARG_LL "d", (long long)res); 822 *text += len; 823 return strdup(buf); 824 } else if(strncmp(buf, "ctime ", 6) == 0 || 825 strncmp(buf, "ctime\t", 6) == 0) { 826 at += 6; 827 dofunc = 1; 828 } else if(strncmp(buf, "range ", 6) == 0 || 829 strncmp(buf, "range\t", 6) == 0) { 830 at += 6; 831 dofunc = 1; 832 } 833 834 /* actual macro text expansion */ 835 while(*at) { 836 size_t remain = sizeof(buf)-strlen(buf); 837 if(strncmp(at, "${", 2) == 0) { 838 at = do_macro_recursion(store, runtime, at, remain); 839 } else if(*at == '$') { 840 at = do_macro_variable(store, at, remain); 841 } else if(isdigit((unsigned char)*at)) { 842 at = do_macro_arith(at, remain, &arithstart); 843 } else { 844 /* copy until whitespace or operator */ 845 if(*at && (isalnum((unsigned char)*at) || *at=='_')) { 846 at++; 847 while(*at && (isalnum((unsigned char)*at) || *at=='_')) 848 at++; 849 } else at++; 850 } 851 if(!at) return NULL; /* failure */ 852 } 853 *text += len; 854 if(dofunc) { 855 /* post process functions, buf has the argument(s) */ 856 if(strncmp(buf, "ctime", 5) == 0) { 857 return do_macro_ctime(buf+6); 858 } else if(strncmp(buf, "range", 5) == 0) { 859 return do_macro_range(buf+6); 860 } 861 } 862 return strdup(buf); 863 } 864 865 char* 866 macro_process(rbtree_type* store, struct replay_runtime* runtime, char* text) 867 { 868 char buf[10240]; 869 char* next, *expand; 870 char* at = text; 871 if(!strstr(text, "${")) 872 return strdup(text); /* no macros */ 873 buf[0] = 0; 874 buf[sizeof(buf)-1]=0; 875 while( (next=strstr(at, "${")) ) { 876 /* copy text before next macro */ 877 if((size_t)(next-at) >= sizeof(buf)-strlen(buf)) 878 return NULL; /* string too long */ 879 (void)strlcpy(buf+strlen(buf), at, (size_t)(next-at+1)); 880 /* process the macro itself */ 881 next += 2; 882 expand = macro_expand(store, runtime, &next); 883 if(!expand) return NULL; /* expansion failed */ 884 (void)strlcpy(buf+strlen(buf), expand, sizeof(buf)-strlen(buf)); 885 free(expand); 886 at = next; 887 } 888 /* copy remainder fixed text */ 889 (void)strlcpy(buf+strlen(buf), at, sizeof(buf)-strlen(buf)); 890 return strdup(buf); 891 } 892 893 char* 894 macro_lookup(rbtree_type* store, char* name) 895 { 896 struct replay_var* x = macro_getvar(store, name); 897 if(!x) return strdup(""); 898 return strdup(x->value); 899 } 900 901 void macro_print_debug(rbtree_type* store) 902 { 903 struct replay_var* x; 904 RBTREE_FOR(x, struct replay_var*, store) { 905 log_info("%s = %s", x->name, x->value); 906 } 907 } 908 909 int 910 macro_assign(rbtree_type* store, char* name, char* value) 911 { 912 struct replay_var* x = macro_getvar(store, name); 913 if(x) { 914 free(x->value); 915 } else { 916 x = (struct replay_var*)malloc(sizeof(*x)); 917 if(!x) return 0; 918 x->node.key = x; 919 x->name = strdup(name); 920 if(!x->name) { 921 free(x); 922 return 0; 923 } 924 (void)rbtree_insert(store, &x->node); 925 } 926 x->value = strdup(value); 927 return x->value != NULL; 928 } 929 930 /* testbound assert function for selftest. counts the number of tests */ 931 #define tb_assert(x) \ 932 do { if(!(x)) fatal_exit("%s:%d: %s: assertion %s failed", \ 933 __FILE__, __LINE__, __func__, #x); \ 934 num_asserts++; \ 935 } while(0); 936 937 void testbound_selftest(void) 938 { 939 /* test the macro store */ 940 rbtree_type* store = macro_store_create(); 941 char* v; 942 int r; 943 int num_asserts = 0; 944 tb_assert(store); 945 946 v = macro_lookup(store, "bla"); 947 tb_assert(strcmp(v, "") == 0); 948 free(v); 949 950 v = macro_lookup(store, "vlerk"); 951 tb_assert(strcmp(v, "") == 0); 952 free(v); 953 954 r = macro_assign(store, "bla", "waarde1"); 955 tb_assert(r); 956 957 v = macro_lookup(store, "vlerk"); 958 tb_assert(strcmp(v, "") == 0); 959 free(v); 960 961 v = macro_lookup(store, "bla"); 962 tb_assert(strcmp(v, "waarde1") == 0); 963 free(v); 964 965 r = macro_assign(store, "vlerk", "kanteel"); 966 tb_assert(r); 967 968 v = macro_lookup(store, "bla"); 969 tb_assert(strcmp(v, "waarde1") == 0); 970 free(v); 971 972 v = macro_lookup(store, "vlerk"); 973 tb_assert(strcmp(v, "kanteel") == 0); 974 free(v); 975 976 r = macro_assign(store, "bla", "ww"); 977 tb_assert(r); 978 979 v = macro_lookup(store, "bla"); 980 tb_assert(strcmp(v, "ww") == 0); 981 free(v); 982 983 tb_assert( macro_length("}") == 1); 984 tb_assert( macro_length("blabla}") == 7); 985 tb_assert( macro_length("bla${zoink}bla}") == 7+8); 986 tb_assert( macro_length("bla${zoink}${bla}bla}") == 7+8+6); 987 988 v = macro_process(store, NULL, ""); 989 tb_assert( v && strcmp(v, "") == 0); 990 free(v); 991 992 v = macro_process(store, NULL, "${}"); 993 tb_assert( v && strcmp(v, "") == 0); 994 free(v); 995 996 v = macro_process(store, NULL, "blabla ${} dinges"); 997 tb_assert( v && strcmp(v, "blabla dinges") == 0); 998 free(v); 999 1000 v = macro_process(store, NULL, "1${$bla}2${$bla}3"); 1001 tb_assert( v && strcmp(v, "1ww2ww3") == 0); 1002 free(v); 1003 1004 v = macro_process(store, NULL, "it is ${ctime 123456}"); 1005 tb_assert( v && strcmp(v, "it is Fri Jan 2 10:17:36 1970") == 0); 1006 free(v); 1007 1008 r = macro_assign(store, "t1", "123456"); 1009 tb_assert(r); 1010 v = macro_process(store, NULL, "it is ${ctime ${$t1}}"); 1011 tb_assert( v && strcmp(v, "it is Fri Jan 2 10:17:36 1970") == 0); 1012 free(v); 1013 1014 v = macro_process(store, NULL, "it is ${ctime $t1}"); 1015 tb_assert( v && strcmp(v, "it is Fri Jan 2 10:17:36 1970") == 0); 1016 free(v); 1017 1018 r = macro_assign(store, "x", "1"); 1019 tb_assert(r); 1020 r = macro_assign(store, "y", "2"); 1021 tb_assert(r); 1022 v = macro_process(store, NULL, "${$x + $x}"); 1023 tb_assert( v && strcmp(v, "2") == 0); 1024 free(v); 1025 v = macro_process(store, NULL, "${$x - $x}"); 1026 tb_assert( v && strcmp(v, "0") == 0); 1027 free(v); 1028 v = macro_process(store, NULL, "${$y * $y}"); 1029 tb_assert( v && strcmp(v, "4") == 0); 1030 free(v); 1031 v = macro_process(store, NULL, "${32 / $y + $x + $y}"); 1032 tb_assert( v && strcmp(v, "19") == 0); 1033 free(v); 1034 1035 v = macro_process(store, NULL, "${32 / ${$y+$y} + ${${100*3}/3}}"); 1036 tb_assert( v && strcmp(v, "108") == 0); 1037 free(v); 1038 1039 v = macro_process(store, NULL, "${1 2 33 2 1}"); 1040 tb_assert( v && strcmp(v, "1 2 33 2 1") == 0); 1041 free(v); 1042 1043 v = macro_process(store, NULL, "${123 3 + 5}"); 1044 tb_assert( v && strcmp(v, "123 8") == 0); 1045 free(v); 1046 1047 v = macro_process(store, NULL, "${123 glug 3 + 5}"); 1048 tb_assert( v && strcmp(v, "123 glug 8") == 0); 1049 free(v); 1050 1051 macro_store_delete(store); 1052 printf("selftest successful (%d checks).\n", num_asserts); 1053 } 1054