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