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