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