testing_api_loop.c (25891B)
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]); 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 int ret = seek_batch (is, 812 icmd, 813 target); 814 if (GNUNET_SYSERR == ret) 815 return GNUNET_SYSERR; /* failure! */ 816 if (GNUNET_OK == ret) 817 { 818 match = icmd; 819 break; 820 } 821 } 822 } 823 if (NULL == current) 824 { 825 /* refuse to jump forward */ 826 GNUNET_break (0); 827 TALER_TESTING_interpreter_fail (is); 828 return GNUNET_SYSERR; 829 } 830 if (NULL == match) 831 return GNUNET_NO; /* not found */ 832 TALER_TESTING_cmd_batch_set_current (cmd, 833 new_ip); 834 return GNUNET_OK; 835 } 836 837 838 /** 839 * Run the "rewind" CMD. 840 * 841 * @param cls closure. 842 * @param cmd command being executed now. 843 * @param is the interpreter state. 844 */ 845 static void 846 rewind_ip_run (void *cls, 847 const struct TALER_TESTING_Command *cmd, 848 struct TALER_TESTING_Interpreter *is) 849 { 850 struct RewindIpState *ris = cls; 851 const struct TALER_TESTING_Command *target; 852 unsigned int new_ip; 853 854 (void) cmd; 855 if (0 == ris->counter) 856 { 857 TALER_TESTING_interpreter_next (is); 858 return; 859 } 860 target 861 = TALER_TESTING_interpreter_lookup_command (is, 862 ris->target_label); 863 if (NULL == target) 864 { 865 GNUNET_break (0); 866 TALER_TESTING_interpreter_fail (is); 867 return; 868 } 869 ris->counter--; 870 for (new_ip = 0; 871 NULL != is->commands[new_ip].label; 872 new_ip++) 873 { 874 const struct TALER_TESTING_Command *ipcmd 875 = &is->commands[new_ip]; 876 877 if (ipcmd == target) 878 break; 879 if (TALER_TESTING_cmd_is_batch (ipcmd)) 880 { 881 int ret = seek_batch (is, 882 ipcmd, 883 target); 884 if (GNUNET_SYSERR == ret) 885 return; /* failure! */ 886 if (GNUNET_OK == ret) 887 break; 888 } 889 } 890 if (new_ip > (unsigned int) is->ip) 891 { 892 /* refuse to jump forward */ 893 GNUNET_break (0); 894 TALER_TESTING_interpreter_fail (is); 895 return; 896 } 897 is->ip = new_ip - 1; /* -1 because the next function will advance by one */ 898 TALER_TESTING_interpreter_next (is); 899 } 900 901 902 struct TALER_TESTING_Command 903 TALER_TESTING_cmd_rewind_ip (const char *label, 904 const char *target_label, 905 unsigned int counter) 906 { 907 struct RewindIpState *ris; 908 909 ris = GNUNET_new (struct RewindIpState); 910 ris->target_label = target_label; 911 ris->counter = counter; 912 { 913 struct TALER_TESTING_Command cmd = { 914 .cls = ris, 915 .label = label, 916 .run = &rewind_ip_run 917 }; 918 919 return cmd; 920 } 921 } 922 923 924 /** 925 * State for a "authchange" CMD. 926 */ 927 struct AuthchangeState 928 { 929 930 /** 931 * What is the new authorization token to send? 932 */ 933 const char *auth_token; 934 935 /** 936 * Old context, clean up on termination. 937 */ 938 struct GNUNET_CURL_Context *old_ctx; 939 }; 940 941 942 /** 943 * Run the command. 944 * 945 * @param cls closure. 946 * @param cmd the command to execute. 947 * @param is the interpreter state. 948 */ 949 static void 950 authchange_run (void *cls, 951 const struct TALER_TESTING_Command *cmd, 952 struct TALER_TESTING_Interpreter *is) 953 { 954 struct AuthchangeState *ss = cls; 955 956 (void) cmd; 957 ss->old_ctx = is->ctx; 958 if (NULL != is->rc) 959 { 960 GNUNET_CURL_gnunet_rc_destroy (is->rc); 961 is->rc = NULL; 962 } 963 is->ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 964 &is->rc); 965 GNUNET_CURL_enable_async_scope_header (is->ctx, 966 "Taler-Correlation-Id"); 967 GNUNET_assert (NULL != is->ctx); 968 is->rc = GNUNET_CURL_gnunet_rc_create (is->ctx); 969 if (NULL != ss->auth_token) 970 { 971 char *authorization; 972 973 GNUNET_asprintf (&authorization, 974 "%s: %s", 975 MHD_HTTP_HEADER_AUTHORIZATION, 976 ss->auth_token); 977 GNUNET_assert (GNUNET_OK == 978 GNUNET_CURL_append_header (is->ctx, 979 authorization)); 980 GNUNET_free (authorization); 981 } 982 TALER_TESTING_interpreter_next (is); 983 } 984 985 986 /** 987 * Call GNUNET_CURL_fini(). Done as a separate task to 988 * ensure that all of the command's cleanups have been 989 * executed first. See #7151. 990 * 991 * @param cls a `struct GNUNET_CURL_Context *` to clean up. 992 */ 993 static void 994 deferred_cleanup_cb (void *cls) 995 { 996 struct GNUNET_CURL_Context *ctx = cls; 997 998 GNUNET_CURL_fini (ctx); 999 } 1000 1001 1002 /** 1003 * Cleanup the state from a "authchange" CMD. 1004 * 1005 * @param cls closure. 1006 * @param cmd the command which is being cleaned up. 1007 */ 1008 static void 1009 authchange_cleanup (void *cls, 1010 const struct TALER_TESTING_Command *cmd) 1011 { 1012 struct AuthchangeState *ss = cls; 1013 1014 (void) cmd; 1015 if (NULL != ss->old_ctx) 1016 { 1017 (void) GNUNET_SCHEDULER_add_now (&deferred_cleanup_cb, 1018 ss->old_ctx); 1019 ss->old_ctx = NULL; 1020 } 1021 GNUNET_free (ss); 1022 } 1023 1024 1025 struct TALER_TESTING_Command 1026 TALER_TESTING_cmd_set_authorization (const char *label, 1027 const char *auth_token) 1028 { 1029 struct AuthchangeState *ss; 1030 1031 ss = GNUNET_new (struct AuthchangeState); 1032 ss->auth_token = auth_token; 1033 1034 { 1035 struct TALER_TESTING_Command cmd = { 1036 .cls = ss, 1037 .label = label, 1038 .run = &authchange_run, 1039 .cleanup = &authchange_cleanup 1040 }; 1041 1042 return cmd; 1043 } 1044 } 1045 1046 1047 /* end of testing_api_loop.c */