testing_api_loop.c (25929B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2018-2023 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it 6 under the terms of the GNU General Public License as published 7 by the Free Software Foundation; either version 3, or (at your 8 option) any later version. 9 10 TALER is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public 16 License along with TALER; see the file COPYING. If not, see 17 <http://www.gnu.org/licenses/> 18 */ 19 20 /** 21 * @file testing/testing_api_loop.c 22 * @brief main interpreter loop for testcases 23 * @author Christian Grothoff 24 * @author Marcello Stanisci 25 */ 26 #include "taler/taler_json_lib.h" 27 #include <gnunet/gnunet_curl_lib.h> 28 #include "taler/taler_util.h" 29 #include "taler/taler_testing_lib.h" 30 31 32 /** 33 * Age restriction mask for the exchange under test. 34 * Set during TALER_TESTING_run_with_fakebank by reading the [exchange] config. 35 */ 36 struct TALER_AgeMask TALER_testing_age_restriction_mask; 37 38 39 /** 40 * The interpreter and its state 41 */ 42 struct TALER_TESTING_Interpreter 43 { 44 45 /** 46 * Commands the interpreter will run. 47 */ 48 struct TALER_TESTING_Command *commands; 49 50 /** 51 * Interpreter task (if one is scheduled). 52 */ 53 struct GNUNET_SCHEDULER_Task *task; 54 55 /** 56 * Handle for the child management. 57 */ 58 struct GNUNET_ChildWaitHandle *cwh; 59 60 /** 61 * Main execution context for the main loop. 62 */ 63 struct GNUNET_CURL_Context *ctx; 64 65 /** 66 * Context for running the CURL event loop. 67 */ 68 struct GNUNET_CURL_RescheduleContext *rc; 69 70 /** 71 * Hash map mapping variable names to commands. 72 */ 73 struct GNUNET_CONTAINER_MultiHashMap *vars; 74 75 /** 76 * Task run on timeout. 77 */ 78 struct GNUNET_SCHEDULER_Task *timeout_task; 79 80 /** 81 * Instruction pointer. Tells #interpreter_run() which instruction to run 82 * next. Need (signed) int because it gets -1 when rewinding the 83 * interpreter to the first CMD. 84 */ 85 int ip; 86 87 /** 88 * Result of the testcases, #GNUNET_OK on success 89 */ 90 enum GNUNET_GenericReturnValue result; 91 92 }; 93 94 95 const struct TALER_TESTING_Command * 96 TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *is, 97 const char *label) 98 { 99 if (NULL == label) 100 { 101 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 102 "Attempt to lookup command for empty label\n"); 103 return NULL; 104 } 105 /* Search backwards as we most likely reference recent commands */ 106 for (int i = is->ip; i >= 0; i--) 107 { 108 const struct TALER_TESTING_Command *cmd = &is->commands[i]; 109 110 /* Give precedence to top-level commands. */ 111 if ( (NULL != cmd->label) && 112 (0 == strcmp (cmd->label, 113 label)) ) 114 return cmd; 115 116 if (TALER_TESTING_cmd_is_batch (cmd)) 117 { 118 struct TALER_TESTING_Command *batch; 119 struct TALER_TESTING_Command *current; 120 struct TALER_TESTING_Command *icmd; 121 const struct TALER_TESTING_Command *match; 122 123 current = TALER_TESTING_cmd_batch_get_current (cmd); 124 GNUNET_assert (GNUNET_OK == 125 TALER_TESTING_get_trait_batch_cmds (cmd, 126 &batch)); 127 /* We must do the loop forward, but we can find the last match */ 128 match = NULL; 129 for (unsigned int j = 0; 130 NULL != (icmd = &batch[j])->label; 131 j++) 132 { 133 if (current == icmd) 134 break; /* do not go past current command */ 135 if ( (NULL != icmd->label) && 136 (0 == strcmp (icmd->label, 137 label)) ) 138 match = icmd; 139 } 140 if (NULL != match) 141 return match; 142 } 143 } 144 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 145 "Command not found: %s\n", 146 label); 147 return NULL; 148 } 149 150 151 const struct TALER_TESTING_Command * 152 TALER_TESTING_interpreter_get_command (struct TALER_TESTING_Interpreter *is, 153 const char *name) 154 { 155 const struct TALER_TESTING_Command *cmd; 156 struct GNUNET_HashCode h_name; 157 158 GNUNET_CRYPTO_hash (name, 159 strlen (name), 160 &h_name); 161 cmd = GNUNET_CONTAINER_multihashmap_get (is->vars, 162 &h_name); 163 if (NULL == cmd) 164 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 165 "Command not found by name: %s\n", 166 name); 167 return cmd; 168 } 169 170 171 struct GNUNET_CURL_Context * 172 TALER_TESTING_interpreter_get_context (struct TALER_TESTING_Interpreter *is) 173 { 174 return is->ctx; 175 } 176 177 178 void 179 TALER_TESTING_touch_cmd (struct TALER_TESTING_Interpreter *is) 180 { 181 is->commands[is->ip].last_req_time 182 = GNUNET_TIME_absolute_get (); 183 } 184 185 186 void 187 TALER_TESTING_inc_tries (struct TALER_TESTING_Interpreter *is) 188 { 189 is->commands[is->ip].num_tries++; 190 } 191 192 193 /** 194 * Run the main interpreter loop that performs exchange operations. 195 * 196 * @param cls contains the `struct InterpreterState` 197 */ 198 static void 199 interpreter_run (void *cls); 200 201 202 void 203 TALER_TESTING_interpreter_next (struct TALER_TESTING_Interpreter *is) 204 { 205 static unsigned long long ipc; 206 static struct GNUNET_TIME_Absolute last_report; 207 struct TALER_TESTING_Command *cmd = &is->commands[is->ip]; 208 209 if (GNUNET_SYSERR == is->result) 210 return; /* ignore, we already failed! */ 211 if (TALER_TESTING_cmd_is_batch (cmd)) 212 { 213 if (TALER_TESTING_cmd_batch_next (is, 214 cmd->cls)) 215 { 216 /* batch is done */ 217 cmd->finish_time = GNUNET_TIME_absolute_get (); 218 is->ip++; 219 } 220 } 221 else 222 { 223 cmd->finish_time = GNUNET_TIME_absolute_get (); 224 is->ip++; 225 } 226 if (0 == (ipc % 1000)) 227 { 228 if (0 != ipc) 229 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 230 "Interpreter executed 1000 instructions in %s\n", 231 GNUNET_STRINGS_relative_time_to_string ( 232 GNUNET_TIME_absolute_get_duration (last_report), 233 true)); 234 last_report = GNUNET_TIME_absolute_get (); 235 } 236 ipc++; 237 is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, 238 is); 239 } 240 241 242 void 243 TALER_TESTING_interpreter_fail (struct TALER_TESTING_Interpreter *is) 244 { 245 struct TALER_TESTING_Command *cmd = &is->commands[is->ip]; 246 247 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 248 "Failed at command `%s'\n", 249 cmd->label); 250 while (TALER_TESTING_cmd_is_batch (cmd)) 251 { 252 cmd = TALER_TESTING_cmd_batch_get_current (cmd); 253 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 254 "Batch is at command `%s'\n", 255 cmd->label); 256 } 257 is->result = GNUNET_SYSERR; 258 GNUNET_SCHEDULER_shutdown (); 259 } 260 261 262 const char * 263 TALER_TESTING_interpreter_get_current_label ( 264 struct TALER_TESTING_Interpreter *is) 265 { 266 struct TALER_TESTING_Command *cmd = &is->commands[is->ip]; 267 268 return cmd->label; 269 } 270 271 272 void 273 TALER_TESTING_update_variables_ ( 274 struct TALER_TESTING_Interpreter *is, 275 struct TALER_TESTING_Command *cmd) 276 { 277 struct GNUNET_HashCode h_name; 278 279 if (NULL == cmd->name) 280 return; 281 GNUNET_CRYPTO_hash (cmd->name, 282 strlen (cmd->name), 283 &h_name); 284 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 285 "Storing command %s under variable `%s'\n", 286 cmd->label, 287 cmd->name); 288 (void) GNUNET_CONTAINER_multihashmap_put ( 289 is->vars, 290 &h_name, 291 cmd, 292 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); 293 } 294 295 296 static void 297 interpreter_run (void *cls) 298 { 299 struct TALER_TESTING_Interpreter *is = cls; 300 struct TALER_TESTING_Command *cmd = &is->commands[is->ip]; 301 302 is->task = NULL; 303 if (NULL == cmd->label) 304 { 305 306 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 307 "Running command END\n"); 308 is->result = GNUNET_OK; 309 GNUNET_SCHEDULER_shutdown (); 310 return; 311 } 312 313 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 314 "Running command `%s'\n", 315 cmd->label); 316 cmd->last_req_time 317 = GNUNET_TIME_absolute_get (); 318 if (0 == cmd->num_tries) 319 cmd->start_time = cmd->last_req_time; 320 cmd->num_tries++; 321 TALER_TESTING_update_variables_ (is, 322 cmd); 323 cmd->run (cmd->cls, 324 cmd, 325 is); 326 } 327 328 329 /** 330 * Function run when the test terminates (good or bad). 331 * Cleans up our state. 332 * 333 * @param cls the interpreter state. 334 */ 335 static void 336 do_shutdown (void *cls) 337 { 338 struct TALER_TESTING_Interpreter *is = cls; 339 struct TALER_TESTING_Command *cmd; 340 const char *label; 341 342 label = is->commands[is->ip].label; 343 if (NULL == label) 344 label = "END"; 345 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 346 "Executing shutdown at `%s'\n", 347 label); 348 for (unsigned int j = 0; 349 NULL != (cmd = &is->commands[j])->label; 350 j++) 351 if (NULL != cmd->cleanup) 352 cmd->cleanup (cmd->cls, 353 cmd); 354 if (NULL != is->task) 355 { 356 GNUNET_SCHEDULER_cancel (is->task); 357 is->task = NULL; 358 } 359 if (NULL != is->ctx) 360 { 361 GNUNET_CURL_fini (is->ctx); 362 is->ctx = NULL; 363 } 364 if (NULL != is->rc) 365 { 366 GNUNET_CURL_gnunet_rc_destroy (is->rc); 367 is->rc = NULL; 368 } 369 if (NULL != is->vars) 370 { 371 GNUNET_CONTAINER_multihashmap_destroy (is->vars); 372 is->vars = NULL; 373 } 374 if (NULL != is->timeout_task) 375 { 376 GNUNET_SCHEDULER_cancel (is->timeout_task); 377 is->timeout_task = NULL; 378 } 379 if (NULL != is->cwh) 380 { 381 GNUNET_wait_child_cancel (is->cwh); 382 is->cwh = NULL; 383 } 384 GNUNET_free (is->commands); 385 } 386 387 388 /** 389 * Function run when the test terminates (good or bad) with timeout. 390 * 391 * @param cls the `struct TALER_TESTING_Interpreter *` 392 */ 393 static void 394 do_timeout (void *cls) 395 { 396 struct TALER_TESTING_Interpreter *is = cls; 397 398 is->timeout_task = NULL; 399 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 400 "Terminating test due to timeout\n"); 401 GNUNET_SCHEDULER_shutdown (); 402 } 403 404 405 /** 406 * Task triggered whenever we receive a SIGCHLD (child 407 * process died). 408 * 409 * @param cls the `struct TALER_TESTING_Interpreter *` 410 * @param type type of the process 411 * @param code status code of the process 412 */ 413 static void 414 maint_child_death (void *cls, 415 enum GNUNET_OS_ProcessStatusType type, 416 long unsigned int code) 417 { 418 struct TALER_TESTING_Interpreter *is = cls; 419 struct TALER_TESTING_Command *cmd = &is->commands[is->ip]; 420 struct GNUNET_Process **processp; 421 422 is->cwh = NULL; 423 while (TALER_TESTING_cmd_is_batch (cmd)) 424 cmd = TALER_TESTING_cmd_batch_get_current (cmd); 425 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 426 "Got SIGCHLD for `%s'.\n", 427 cmd->label); 428 if (GNUNET_OK != 429 TALER_TESTING_get_trait_process (cmd, 430 &processp)) 431 { 432 GNUNET_break (0); 433 TALER_TESTING_interpreter_fail (is); 434 return; 435 } 436 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 437 "Got the dead child process handle, waiting for termination ...\n"); 438 GNUNET_process_destroy (*processp); 439 *processp = NULL; 440 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 441 "... definitively terminated\n"); 442 switch (type) 443 { 444 case GNUNET_OS_PROCESS_UNKNOWN: 445 GNUNET_break (0); 446 TALER_TESTING_interpreter_fail (is); 447 return; 448 case GNUNET_OS_PROCESS_RUNNING: 449 GNUNET_break (0); 450 TALER_TESTING_interpreter_fail (is); 451 return; 452 case GNUNET_OS_PROCESS_STOPPED: 453 GNUNET_break (0); 454 TALER_TESTING_interpreter_fail (is); 455 return; 456 case GNUNET_OS_PROCESS_EXITED: 457 if (0 != code) 458 { 459 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 460 "Process exited with unexpected status %u\n", 461 (unsigned int) code); 462 TALER_TESTING_interpreter_fail (is); 463 return; 464 } 465 break; 466 case GNUNET_OS_PROCESS_SIGNALED: 467 GNUNET_break (0); 468 TALER_TESTING_interpreter_fail (is); 469 return; 470 } 471 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 472 "Dead child, go on with next command.\n"); 473 TALER_TESTING_interpreter_next (is); 474 } 475 476 477 void 478 TALER_TESTING_wait_for_sigchld (struct TALER_TESTING_Interpreter *is) 479 { 480 struct GNUNET_Process **processp; 481 struct TALER_TESTING_Command *cmd = &is->commands[is->ip]; 482 483 while (TALER_TESTING_cmd_is_batch (cmd)) 484 cmd = TALER_TESTING_cmd_batch_get_current (cmd); 485 if (GNUNET_OK != 486 TALER_TESTING_get_trait_process (cmd, 487 &processp)) 488 { 489 GNUNET_break (0); 490 TALER_TESTING_interpreter_fail (is); 491 return; 492 } 493 GNUNET_assert (NULL == is->cwh); 494 is->cwh 495 = GNUNET_wait_child (*processp, 496 &maint_child_death, 497 is); 498 } 499 500 501 void 502 TALER_TESTING_run2 (struct TALER_TESTING_Interpreter *is, 503 struct TALER_TESTING_Command *commands, 504 struct GNUNET_TIME_Relative timeout) 505 { 506 unsigned int i; 507 508 if (NULL != is->timeout_task) 509 { 510 GNUNET_SCHEDULER_cancel (is->timeout_task); 511 is->timeout_task = NULL; 512 } 513 /* get the number of commands */ 514 for (i = 0; NULL != commands[i].label; i++) 515 ; 516 is->commands = GNUNET_malloc_large ( (i + 1) 517 * sizeof (struct TALER_TESTING_Command)); 518 GNUNET_assert (NULL != is->commands); 519 GNUNET_memcpy (is->commands, 520 commands, 521 sizeof (struct TALER_TESTING_Command) * i); 522 is->timeout_task = GNUNET_SCHEDULER_add_delayed ( 523 timeout, 524 &do_timeout, 525 is); 526 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 527 is); 528 is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, 529 is); 530 } 531 532 533 #include "valgrind.h" 534 535 void 536 TALER_TESTING_run (struct TALER_TESTING_Interpreter *is, 537 struct TALER_TESTING_Command *commands) 538 { 539 TALER_TESTING_run2 (is, 540 commands, 541 0 == RUNNING_ON_VALGRIND 542 ? GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 543 5) 544 : GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 545 50)); 546 } 547 548 549 /** 550 * Information used by the wrapper around the main 551 * "run" method. 552 */ 553 struct MainContext 554 { 555 /** 556 * Main "run" method. 557 */ 558 TALER_TESTING_Main main_cb; 559 560 /** 561 * Closure for @e main_cb. 562 */ 563 void *main_cb_cls; 564 565 /** 566 * Interpreter state. 567 */ 568 struct TALER_TESTING_Interpreter *is; 569 570 /** 571 * URL of the exchange. 572 */ 573 char *exchange_url; 574 575 }; 576 577 578 /** 579 * Initialize scheduler loop and curl context for the testcase, 580 * and responsible to run the "run" method. 581 * 582 * @param cls closure, typically the "run" method, the 583 * interpreter state and a closure for "run". 584 */ 585 static void 586 main_wrapper (void *cls) 587 { 588 struct MainContext *main_ctx = cls; 589 590 main_ctx->main_cb (main_ctx->main_cb_cls, 591 main_ctx->is); 592 } 593 594 595 enum GNUNET_GenericReturnValue 596 TALER_TESTING_loop (TALER_TESTING_Main main_cb, 597 void *main_cb_cls) 598 { 599 struct TALER_TESTING_Interpreter is; 600 struct MainContext main_ctx = { 601 .main_cb = main_cb, 602 .main_cb_cls = main_cb_cls, 603 /* needed to init the curl ctx */ 604 .is = &is, 605 }; 606 607 memset (&is, 608 0, 609 sizeof (is)); 610 is.ctx = GNUNET_CURL_init ( 611 &GNUNET_CURL_gnunet_scheduler_reschedule, 612 &is.rc); 613 GNUNET_CURL_enable_async_scope_header (is.ctx, 614 "Taler-Correlation-Id"); 615 GNUNET_assert (NULL != is.ctx); 616 is.rc = GNUNET_CURL_gnunet_rc_create (is.ctx); 617 is.vars = GNUNET_CONTAINER_multihashmap_create (1024, 618 false); 619 /* Blocking */ 620 GNUNET_SCHEDULER_run (&main_wrapper, 621 &main_ctx); 622 return is.result; 623 } 624 625 626 int 627 TALER_TESTING_main (char *const *argv, 628 const char *loglevel, 629 const char *cfg_file, 630 const char *exchange_account_section, 631 enum TALER_TESTING_BankSystem bs, 632 struct TALER_TESTING_Credentials *cred, 633 TALER_TESTING_Main main_cb, 634 void *main_cb_cls) 635 { 636 enum GNUNET_GenericReturnValue ret; 637 638 unsetenv ("XDG_DATA_HOME"); 639 unsetenv ("XDG_CONFIG_HOME"); 640 GNUNET_log_setup (argv[0], 641 loglevel, 642 NULL); 643 if (GNUNET_OK != 644 TALER_TESTING_get_credentials (cfg_file, 645 exchange_account_section, 646 bs, 647 cred)) 648 { 649 GNUNET_break (0); 650 return 77; 651 } 652 if (GNUNET_OK != 653 TALER_TESTING_cleanup_files_cfg (NULL, 654 cred->cfg)) 655 { 656 GNUNET_break (0); 657 return 77; 658 } 659 /* Initialize age restriction mask from [exchange] config */ 660 memset (&TALER_testing_age_restriction_mask, 661 0, 662 sizeof (TALER_testing_age_restriction_mask)); 663 if (GNUNET_YES == 664 GNUNET_CONFIGURATION_get_value_yesno (cred->cfg, 665 "exchange", 666 "AGE_RESTRICTION_ENABLED")) 667 { 668 char *groups = NULL; 669 670 if (GNUNET_OK != 671 GNUNET_CONFIGURATION_get_value_string (cred->cfg, 672 "exchange", 673 "AGE_GROUPS", 674 &groups)) 675 groups = GNUNET_strdup ("8:10:12:14:16:18:21"); 676 if (GNUNET_OK != 677 TALER_parse_age_group_string (groups, 678 &TALER_testing_age_restriction_mask)) 679 { 680 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 681 "Invalid AGE_GROUPS in [exchange]: %s\n", 682 groups); 683 GNUNET_free (groups); 684 GNUNET_break (0); 685 return 77; 686 } 687 GNUNET_free (groups); 688 } 689 ret = TALER_TESTING_loop (main_cb, 690 main_cb_cls); 691 /* FIXME: should we free 'cred' resources here? */ 692 return (GNUNET_OK == ret) ? 0 : 1; 693 } 694 695 696 /* ************** iterate over commands ********* */ 697 698 699 void 700 TALER_TESTING_iterate (struct TALER_TESTING_Interpreter *is, 701 bool asc, 702 TALER_TESTING_CommandIterator cb, 703 void *cb_cls) 704 { 705 unsigned int start; 706 unsigned int end; 707 int inc; 708 709 if (asc) 710 { 711 inc = 1; 712 start = 0; 713 end = is->ip; 714 } 715 else 716 { 717 inc = -1; 718 start = is->ip; 719 end = 0; 720 } 721 for (unsigned int off = start; off != end + inc; off += inc) 722 { 723 const struct TALER_TESTING_Command *cmd = &is->commands[off]; 724 725 cb (cb_cls, 726 cmd); 727 } 728 } 729 730 731 /* ************** special commands ********* */ 732 733 734 struct TALER_TESTING_Command 735 TALER_TESTING_cmd_end (void) 736 { 737 static struct TALER_TESTING_Command cmd; 738 cmd.label = NULL; 739 740 return cmd; 741 } 742 743 744 struct TALER_TESTING_Command 745 TALER_TESTING_cmd_set_var (const char *name, 746 struct TALER_TESTING_Command cmd) 747 { 748 cmd.name = name; 749 return cmd; 750 } 751 752 753 /** 754 * State for a "rewind" CMD. 755 */ 756 struct RewindIpState 757 { 758 /** 759 * Instruction pointer to set into the interpreter. 760 */ 761 const char *target_label; 762 763 /** 764 * How many times this set should take place. However, this value lives at 765 * the calling process, and this CMD is only in charge of checking and 766 * decremeting it. 767 */ 768 unsigned int counter; 769 }; 770 771 772 /** 773 * Seek for the @a target command in @a batch (and rewind to it 774 * if successful). 775 * 776 * @param is the interpreter state (for failures) 777 * @param cmd batch to search for @a target 778 * @param target command to search for 779 * @return #GNUNET_OK on success, #GNUNET_NO if target was not found, 780 * #GNUNET_SYSERR if target is in the future and we failed 781 */ 782 static enum GNUNET_GenericReturnValue 783 seek_batch (struct TALER_TESTING_Interpreter *is, 784 const struct TALER_TESTING_Command *cmd, 785 const struct TALER_TESTING_Command *target) 786 { 787 unsigned int new_ip; 788 struct TALER_TESTING_Command *batch; 789 struct TALER_TESTING_Command *current; 790 struct TALER_TESTING_Command *icmd; 791 struct TALER_TESTING_Command *match; 792 793 current = TALER_TESTING_cmd_batch_get_current (cmd); 794 GNUNET_assert (GNUNET_OK == 795 TALER_TESTING_get_trait_batch_cmds (cmd, 796 &batch)); 797 match = NULL; 798 for (new_ip = 0; 799 NULL != (icmd = &batch[new_ip])->label; 800 new_ip++) 801 { 802 if (current == target) 803 current = NULL; 804 if (icmd == target) 805 { 806 match = icmd; 807 break; 808 } 809 if (TALER_TESTING_cmd_is_batch (icmd)) 810 { 811 enum GNUNET_GenericReturnValue ret; 812 813 ret = seek_batch (is, 814 icmd, 815 target); 816 if (GNUNET_SYSERR == ret) 817 return GNUNET_SYSERR; /* failure! */ 818 if (GNUNET_OK == ret) 819 { 820 match = icmd; 821 break; 822 } 823 } 824 } 825 if (NULL == current) 826 { 827 /* refuse to jump forward */ 828 GNUNET_break (0); 829 TALER_TESTING_interpreter_fail (is); 830 return GNUNET_SYSERR; 831 } 832 if (NULL == match) 833 return GNUNET_NO; /* not found */ 834 TALER_TESTING_cmd_batch_set_current (cmd, 835 new_ip); 836 return GNUNET_OK; 837 } 838 839 840 /** 841 * Run the "rewind" CMD. 842 * 843 * @param cls closure. 844 * @param cmd command being executed now. 845 * @param is the interpreter state. 846 */ 847 static void 848 rewind_ip_run (void *cls, 849 const struct TALER_TESTING_Command *cmd, 850 struct TALER_TESTING_Interpreter *is) 851 { 852 struct RewindIpState *ris = cls; 853 const struct TALER_TESTING_Command *target; 854 unsigned int new_ip; 855 856 (void) cmd; 857 if (0 == ris->counter) 858 { 859 TALER_TESTING_interpreter_next (is); 860 return; 861 } 862 target 863 = TALER_TESTING_interpreter_lookup_command (is, 864 ris->target_label); 865 if (NULL == target) 866 { 867 GNUNET_break (0); 868 TALER_TESTING_interpreter_fail (is); 869 return; 870 } 871 ris->counter--; 872 for (new_ip = 0; 873 NULL != is->commands[new_ip].label; 874 new_ip++) 875 { 876 const struct TALER_TESTING_Command *ipcmd 877 = &is->commands[new_ip]; 878 879 if (ipcmd == target) 880 break; 881 if (TALER_TESTING_cmd_is_batch (ipcmd)) 882 { 883 int ret = seek_batch (is, 884 ipcmd, 885 target); 886 if (GNUNET_SYSERR == ret) 887 return; /* failure! */ 888 if (GNUNET_OK == ret) 889 break; 890 } 891 } 892 if (new_ip > (unsigned int) is->ip) 893 { 894 /* refuse to jump forward */ 895 GNUNET_break (0); 896 TALER_TESTING_interpreter_fail (is); 897 return; 898 } 899 is->ip = new_ip - 1; /* -1 because the next function will advance by one */ 900 TALER_TESTING_interpreter_next (is); 901 } 902 903 904 struct TALER_TESTING_Command 905 TALER_TESTING_cmd_rewind_ip (const char *label, 906 const char *target_label, 907 unsigned int counter) 908 { 909 struct RewindIpState *ris; 910 911 ris = GNUNET_new (struct RewindIpState); 912 ris->target_label = target_label; 913 ris->counter = counter; 914 { 915 struct TALER_TESTING_Command cmd = { 916 .cls = ris, 917 .label = label, 918 .run = &rewind_ip_run 919 }; 920 921 return cmd; 922 } 923 } 924 925 926 /** 927 * State for a "authchange" CMD. 928 */ 929 struct AuthchangeState 930 { 931 932 /** 933 * What is the new authorization token to send? 934 */ 935 const char *auth_token; 936 937 /** 938 * Old context, clean up on termination. 939 */ 940 struct GNUNET_CURL_Context *old_ctx; 941 }; 942 943 944 /** 945 * Run the command. 946 * 947 * @param cls closure. 948 * @param cmd the command to execute. 949 * @param is the interpreter state. 950 */ 951 static void 952 authchange_run (void *cls, 953 const struct TALER_TESTING_Command *cmd, 954 struct TALER_TESTING_Interpreter *is) 955 { 956 struct AuthchangeState *ss = cls; 957 958 (void) cmd; 959 ss->old_ctx = is->ctx; 960 if (NULL != is->rc) 961 { 962 GNUNET_CURL_gnunet_rc_destroy (is->rc); 963 is->rc = NULL; 964 } 965 is->ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 966 &is->rc); 967 GNUNET_CURL_enable_async_scope_header (is->ctx, 968 "Taler-Correlation-Id"); 969 GNUNET_assert (NULL != is->ctx); 970 is->rc = GNUNET_CURL_gnunet_rc_create (is->ctx); 971 if (NULL != ss->auth_token) 972 { 973 char *authorization; 974 975 GNUNET_asprintf (&authorization, 976 "%s: %s", 977 MHD_HTTP_HEADER_AUTHORIZATION, 978 ss->auth_token); 979 GNUNET_assert (GNUNET_OK == 980 GNUNET_CURL_append_header (is->ctx, 981 authorization)); 982 GNUNET_free (authorization); 983 } 984 TALER_TESTING_interpreter_next (is); 985 } 986 987 988 /** 989 * Call GNUNET_CURL_fini(). Done as a separate task to 990 * ensure that all of the command's cleanups have been 991 * executed first. See #7151. 992 * 993 * @param cls a `struct GNUNET_CURL_Context *` to clean up. 994 */ 995 static void 996 deferred_cleanup_cb (void *cls) 997 { 998 struct GNUNET_CURL_Context *ctx = cls; 999 1000 GNUNET_CURL_fini (ctx); 1001 } 1002 1003 1004 /** 1005 * Cleanup the state from a "authchange" CMD. 1006 * 1007 * @param cls closure. 1008 * @param cmd the command which is being cleaned up. 1009 */ 1010 static void 1011 authchange_cleanup (void *cls, 1012 const struct TALER_TESTING_Command *cmd) 1013 { 1014 struct AuthchangeState *ss = cls; 1015 1016 (void) cmd; 1017 if (NULL != ss->old_ctx) 1018 { 1019 (void) GNUNET_SCHEDULER_add_now (&deferred_cleanup_cb, 1020 ss->old_ctx); 1021 ss->old_ctx = NULL; 1022 } 1023 GNUNET_free (ss); 1024 } 1025 1026 1027 struct TALER_TESTING_Command 1028 TALER_TESTING_cmd_set_authorization (const char *label, 1029 const char *auth_token) 1030 { 1031 struct AuthchangeState *ss; 1032 1033 ss = GNUNET_new (struct AuthchangeState); 1034 ss->auth_token = auth_token; 1035 1036 { 1037 struct TALER_TESTING_Command cmd = { 1038 .cls = ss, 1039 .label = label, 1040 .run = &authchange_run, 1041 .cleanup = &authchange_cleanup 1042 }; 1043 1044 return cmd; 1045 } 1046 } 1047 1048 1049 /* end of testing_api_loop.c */