testing_api_loop.c (26221B)
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; 208 209 if (GNUNET_SYSERR == is->result) 210 return; /* ignore, we already failed! */ 211 if (0 > is->ip) 212 { 213 /* We were rewound to before the first command; there is no 214 command to finish, just (re)start at the beginning. */ 215 is->ip = 0; 216 is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, 217 is); 218 return; 219 } 220 cmd = &is->commands[is->ip]; 221 if (TALER_TESTING_cmd_is_batch (cmd)) 222 { 223 if (TALER_TESTING_cmd_batch_next (is, 224 cmd->cls)) 225 { 226 /* batch is done */ 227 cmd->finish_time = GNUNET_TIME_absolute_get (); 228 is->ip++; 229 } 230 } 231 else 232 { 233 cmd->finish_time = GNUNET_TIME_absolute_get (); 234 is->ip++; 235 } 236 if (0 == (ipc % 1000)) 237 { 238 if (0 != ipc) 239 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 240 "Interpreter executed 1000 instructions in %s\n", 241 GNUNET_STRINGS_relative_time_to_string ( 242 GNUNET_TIME_absolute_get_duration (last_report), 243 true)); 244 last_report = GNUNET_TIME_absolute_get (); 245 } 246 ipc++; 247 is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, 248 is); 249 } 250 251 252 void 253 TALER_TESTING_interpreter_fail (struct TALER_TESTING_Interpreter *is) 254 { 255 struct TALER_TESTING_Command *cmd = &is->commands[is->ip]; 256 257 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 258 "Failed at command `%s'\n", 259 cmd->label); 260 while (TALER_TESTING_cmd_is_batch (cmd)) 261 { 262 cmd = TALER_TESTING_cmd_batch_get_current (cmd); 263 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 264 "Batch is at command `%s'\n", 265 cmd->label); 266 } 267 is->result = GNUNET_SYSERR; 268 GNUNET_SCHEDULER_shutdown (); 269 } 270 271 272 const char * 273 TALER_TESTING_interpreter_get_current_label ( 274 struct TALER_TESTING_Interpreter *is) 275 { 276 struct TALER_TESTING_Command *cmd = &is->commands[is->ip]; 277 278 return cmd->label; 279 } 280 281 282 void 283 TALER_TESTING_update_variables_ ( 284 struct TALER_TESTING_Interpreter *is, 285 struct TALER_TESTING_Command *cmd) 286 { 287 struct GNUNET_HashCode h_name; 288 289 if (NULL == cmd->name) 290 return; 291 GNUNET_CRYPTO_hash (cmd->name, 292 strlen (cmd->name), 293 &h_name); 294 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 295 "Storing command %s under variable `%s'\n", 296 cmd->label, 297 cmd->name); 298 (void) GNUNET_CONTAINER_multihashmap_put ( 299 is->vars, 300 &h_name, 301 cmd, 302 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); 303 } 304 305 306 static void 307 interpreter_run (void *cls) 308 { 309 struct TALER_TESTING_Interpreter *is = cls; 310 struct TALER_TESTING_Command *cmd = &is->commands[is->ip]; 311 312 is->task = NULL; 313 if (NULL == cmd->label) 314 { 315 316 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 317 "Running command END\n"); 318 is->result = GNUNET_OK; 319 GNUNET_SCHEDULER_shutdown (); 320 return; 321 } 322 323 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 324 "Running command `%s'\n", 325 cmd->label); 326 cmd->last_req_time 327 = GNUNET_TIME_absolute_get (); 328 if (0 == cmd->num_tries) 329 cmd->start_time = cmd->last_req_time; 330 cmd->num_tries++; 331 TALER_TESTING_update_variables_ (is, 332 cmd); 333 cmd->run (cmd->cls, 334 cmd, 335 is); 336 } 337 338 339 /** 340 * Function run when the test terminates (good or bad). 341 * Cleans up our state. 342 * 343 * @param cls the interpreter state. 344 */ 345 static void 346 do_shutdown (void *cls) 347 { 348 struct TALER_TESTING_Interpreter *is = cls; 349 struct TALER_TESTING_Command *cmd; 350 const char *label; 351 352 label = is->commands[is->ip].label; 353 if (NULL == label) 354 label = "END"; 355 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 356 "Executing shutdown at `%s'\n", 357 label); 358 for (unsigned int j = 0; 359 NULL != (cmd = &is->commands[j])->label; 360 j++) 361 if (NULL != cmd->cleanup) 362 cmd->cleanup (cmd->cls, 363 cmd); 364 if (NULL != is->task) 365 { 366 GNUNET_SCHEDULER_cancel (is->task); 367 is->task = NULL; 368 } 369 if (NULL != is->ctx) 370 { 371 GNUNET_CURL_fini (is->ctx); 372 is->ctx = NULL; 373 } 374 if (NULL != is->rc) 375 { 376 GNUNET_CURL_gnunet_rc_destroy (is->rc); 377 is->rc = NULL; 378 } 379 if (NULL != is->vars) 380 { 381 GNUNET_CONTAINER_multihashmap_destroy (is->vars); 382 is->vars = NULL; 383 } 384 if (NULL != is->timeout_task) 385 { 386 GNUNET_SCHEDULER_cancel (is->timeout_task); 387 is->timeout_task = NULL; 388 } 389 if (NULL != is->cwh) 390 { 391 GNUNET_wait_child_cancel (is->cwh); 392 is->cwh = NULL; 393 } 394 GNUNET_free (is->commands); 395 } 396 397 398 /** 399 * Function run when the test terminates (good or bad) with timeout. 400 * 401 * @param cls the `struct TALER_TESTING_Interpreter *` 402 */ 403 static void 404 do_timeout (void *cls) 405 { 406 struct TALER_TESTING_Interpreter *is = cls; 407 408 is->timeout_task = NULL; 409 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 410 "Terminating test due to timeout\n"); 411 GNUNET_SCHEDULER_shutdown (); 412 } 413 414 415 /** 416 * Task triggered whenever we receive a SIGCHLD (child 417 * process died). 418 * 419 * @param cls the `struct TALER_TESTING_Interpreter *` 420 * @param type type of the process 421 * @param code status code of the process 422 */ 423 static void 424 maint_child_death (void *cls, 425 enum GNUNET_OS_ProcessStatusType type, 426 long unsigned int code) 427 { 428 struct TALER_TESTING_Interpreter *is = cls; 429 struct TALER_TESTING_Command *cmd = &is->commands[is->ip]; 430 struct GNUNET_Process **processp; 431 432 is->cwh = NULL; 433 while (TALER_TESTING_cmd_is_batch (cmd)) 434 cmd = TALER_TESTING_cmd_batch_get_current (cmd); 435 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 436 "Got SIGCHLD for `%s'.\n", 437 cmd->label); 438 if (GNUNET_OK != 439 TALER_TESTING_get_trait_process (cmd, 440 &processp)) 441 { 442 GNUNET_break (0); 443 TALER_TESTING_interpreter_fail (is); 444 return; 445 } 446 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 447 "Got the dead child process handle, waiting for termination ...\n"); 448 GNUNET_process_destroy (*processp); 449 *processp = NULL; 450 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 451 "... definitively terminated\n"); 452 switch (type) 453 { 454 case GNUNET_OS_PROCESS_UNKNOWN: 455 GNUNET_break (0); 456 TALER_TESTING_interpreter_fail (is); 457 return; 458 case GNUNET_OS_PROCESS_RUNNING: 459 GNUNET_break (0); 460 TALER_TESTING_interpreter_fail (is); 461 return; 462 case GNUNET_OS_PROCESS_STOPPED: 463 GNUNET_break (0); 464 TALER_TESTING_interpreter_fail (is); 465 return; 466 case GNUNET_OS_PROCESS_EXITED: 467 if (0 != code) 468 { 469 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 470 "Process exited with unexpected status %u\n", 471 (unsigned int) code); 472 TALER_TESTING_interpreter_fail (is); 473 return; 474 } 475 break; 476 case GNUNET_OS_PROCESS_SIGNALED: 477 GNUNET_break (0); 478 TALER_TESTING_interpreter_fail (is); 479 return; 480 } 481 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 482 "Dead child, go on with next command.\n"); 483 TALER_TESTING_interpreter_next (is); 484 } 485 486 487 void 488 TALER_TESTING_wait_for_sigchld (struct TALER_TESTING_Interpreter *is) 489 { 490 struct GNUNET_Process **processp; 491 struct TALER_TESTING_Command *cmd = &is->commands[is->ip]; 492 493 while (TALER_TESTING_cmd_is_batch (cmd)) 494 cmd = TALER_TESTING_cmd_batch_get_current (cmd); 495 if (GNUNET_OK != 496 TALER_TESTING_get_trait_process (cmd, 497 &processp)) 498 { 499 GNUNET_break (0); 500 TALER_TESTING_interpreter_fail (is); 501 return; 502 } 503 GNUNET_assert (NULL == is->cwh); 504 is->cwh 505 = GNUNET_wait_child (*processp, 506 &maint_child_death, 507 is); 508 } 509 510 511 void 512 TALER_TESTING_run2 (struct TALER_TESTING_Interpreter *is, 513 struct TALER_TESTING_Command *commands, 514 struct GNUNET_TIME_Relative timeout) 515 { 516 unsigned int i; 517 518 if (NULL != is->timeout_task) 519 { 520 GNUNET_SCHEDULER_cancel (is->timeout_task); 521 is->timeout_task = NULL; 522 } 523 /* get the number of commands */ 524 for (i = 0; NULL != commands[i].label; i++) 525 ; 526 is->commands = GNUNET_malloc_large ( (i + 1) 527 * sizeof (struct TALER_TESTING_Command)); 528 GNUNET_assert (NULL != is->commands); 529 GNUNET_memcpy (is->commands, 530 commands, 531 sizeof (struct TALER_TESTING_Command) * i); 532 is->timeout_task = GNUNET_SCHEDULER_add_delayed ( 533 timeout, 534 &do_timeout, 535 is); 536 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 537 is); 538 is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, 539 is); 540 } 541 542 543 #include "valgrind.h" 544 545 void 546 TALER_TESTING_run (struct TALER_TESTING_Interpreter *is, 547 struct TALER_TESTING_Command *commands) 548 { 549 TALER_TESTING_run2 (is, 550 commands, 551 0 == RUNNING_ON_VALGRIND 552 ? GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 553 5) 554 : GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 555 50)); 556 } 557 558 559 /** 560 * Information used by the wrapper around the main 561 * "run" method. 562 */ 563 struct MainContext 564 { 565 /** 566 * Main "run" method. 567 */ 568 TALER_TESTING_Main main_cb; 569 570 /** 571 * Closure for @e main_cb. 572 */ 573 void *main_cb_cls; 574 575 /** 576 * Interpreter state. 577 */ 578 struct TALER_TESTING_Interpreter *is; 579 580 /** 581 * URL of the exchange. 582 */ 583 char *exchange_url; 584 585 }; 586 587 588 /** 589 * Initialize scheduler loop and curl context for the testcase, 590 * and responsible to run the "run" method. 591 * 592 * @param cls closure, typically the "run" method, the 593 * interpreter state and a closure for "run". 594 */ 595 static void 596 main_wrapper (void *cls) 597 { 598 struct MainContext *main_ctx = cls; 599 600 main_ctx->main_cb (main_ctx->main_cb_cls, 601 main_ctx->is); 602 } 603 604 605 enum GNUNET_GenericReturnValue 606 TALER_TESTING_loop (TALER_TESTING_Main main_cb, 607 void *main_cb_cls) 608 { 609 struct TALER_TESTING_Interpreter is; 610 struct MainContext main_ctx = { 611 .main_cb = main_cb, 612 .main_cb_cls = main_cb_cls, 613 /* needed to init the curl ctx */ 614 .is = &is, 615 }; 616 617 memset (&is, 618 0, 619 sizeof (is)); 620 is.ctx = GNUNET_CURL_init ( 621 &GNUNET_CURL_gnunet_scheduler_reschedule, 622 &is.rc); 623 GNUNET_CURL_enable_async_scope_header (is.ctx, 624 "Taler-Correlation-Id"); 625 GNUNET_assert (NULL != is.ctx); 626 is.rc = GNUNET_CURL_gnunet_rc_create (is.ctx); 627 is.vars = GNUNET_CONTAINER_multihashmap_create (1024, 628 false); 629 /* Blocking */ 630 GNUNET_SCHEDULER_run (&main_wrapper, 631 &main_ctx); 632 return is.result; 633 } 634 635 636 int 637 TALER_TESTING_main (char *const *argv, 638 const char *loglevel, 639 const char *cfg_file, 640 const char *exchange_account_section, 641 enum TALER_TESTING_BankSystem bs, 642 struct TALER_TESTING_Credentials *cred, 643 TALER_TESTING_Main main_cb, 644 void *main_cb_cls) 645 { 646 enum GNUNET_GenericReturnValue ret; 647 648 unsetenv ("XDG_DATA_HOME"); 649 unsetenv ("XDG_CONFIG_HOME"); 650 GNUNET_log_setup (argv[0], 651 loglevel, 652 NULL); 653 if (GNUNET_OK != 654 TALER_TESTING_get_credentials (cfg_file, 655 exchange_account_section, 656 bs, 657 cred)) 658 { 659 GNUNET_break (0); 660 return 77; 661 } 662 if (GNUNET_OK != 663 TALER_TESTING_cleanup_files_cfg (NULL, 664 cred->cfg)) 665 { 666 GNUNET_break (0); 667 return 77; 668 } 669 /* Initialize age restriction mask from [exchange] config */ 670 memset (&TALER_testing_age_restriction_mask, 671 0, 672 sizeof (TALER_testing_age_restriction_mask)); 673 if (GNUNET_YES == 674 GNUNET_CONFIGURATION_get_value_yesno (cred->cfg, 675 "exchange", 676 "AGE_RESTRICTION_ENABLED")) 677 { 678 char *groups = NULL; 679 680 if (GNUNET_OK != 681 GNUNET_CONFIGURATION_get_value_string (cred->cfg, 682 "exchange", 683 "AGE_GROUPS", 684 &groups)) 685 groups = GNUNET_strdup ("8:10:12:14:16:18:21"); 686 if (GNUNET_OK != 687 TALER_parse_age_group_string (groups, 688 &TALER_testing_age_restriction_mask)) 689 { 690 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 691 "Invalid AGE_GROUPS in [exchange]: %s\n", 692 groups); 693 GNUNET_free (groups); 694 GNUNET_break (0); 695 return 77; 696 } 697 GNUNET_free (groups); 698 } 699 ret = TALER_TESTING_loop (main_cb, 700 main_cb_cls); 701 /* FIXME: should we free 'cred' resources here? */ 702 return (GNUNET_OK == ret) ? 0 : 1; 703 } 704 705 706 /* ************** iterate over commands ********* */ 707 708 709 void 710 TALER_TESTING_iterate (struct TALER_TESTING_Interpreter *is, 711 bool asc, 712 TALER_TESTING_CommandIterator cb, 713 void *cb_cls) 714 { 715 unsigned int start; 716 unsigned int end; 717 int inc; 718 719 if (asc) 720 { 721 inc = 1; 722 start = 0; 723 end = is->ip; 724 } 725 else 726 { 727 inc = -1; 728 start = is->ip; 729 end = 0; 730 } 731 for (unsigned int off = start; off != end + inc; off += inc) 732 { 733 const struct TALER_TESTING_Command *cmd = &is->commands[off]; 734 735 cb (cb_cls, 736 cmd); 737 } 738 } 739 740 741 /* ************** special commands ********* */ 742 743 744 struct TALER_TESTING_Command 745 TALER_TESTING_cmd_end (void) 746 { 747 static struct TALER_TESTING_Command cmd; 748 cmd.label = NULL; 749 750 return cmd; 751 } 752 753 754 struct TALER_TESTING_Command 755 TALER_TESTING_cmd_set_var (const char *name, 756 struct TALER_TESTING_Command cmd) 757 { 758 cmd.name = name; 759 return cmd; 760 } 761 762 763 /** 764 * State for a "rewind" CMD. 765 */ 766 struct RewindIpState 767 { 768 /** 769 * Instruction pointer to set into the interpreter. 770 */ 771 const char *target_label; 772 773 /** 774 * How many times this set should take place. However, this value lives at 775 * the calling process, and this CMD is only in charge of checking and 776 * decremeting it. 777 */ 778 unsigned int counter; 779 }; 780 781 782 /** 783 * Seek for the @a target command in @a batch (and rewind to it 784 * if successful). 785 * 786 * @param is the interpreter state (for failures) 787 * @param cmd batch to search for @a target 788 * @param target command to search for 789 * @return #GNUNET_OK on success, #GNUNET_NO if target was not found, 790 * #GNUNET_SYSERR if target is in the future and we failed 791 */ 792 static enum GNUNET_GenericReturnValue 793 seek_batch (struct TALER_TESTING_Interpreter *is, 794 const struct TALER_TESTING_Command *cmd, 795 const struct TALER_TESTING_Command *target) 796 { 797 unsigned int new_ip; 798 struct TALER_TESTING_Command *batch; 799 struct TALER_TESTING_Command *current; 800 struct TALER_TESTING_Command *icmd; 801 struct TALER_TESTING_Command *match; 802 803 current = TALER_TESTING_cmd_batch_get_current (cmd); 804 GNUNET_assert (GNUNET_OK == 805 TALER_TESTING_get_trait_batch_cmds (cmd, 806 &batch)); 807 match = NULL; 808 for (new_ip = 0; 809 NULL != (icmd = &batch[new_ip])->label; 810 new_ip++) 811 { 812 if (current == target) 813 current = NULL; 814 if (icmd == target) 815 { 816 match = icmd; 817 break; 818 } 819 if (TALER_TESTING_cmd_is_batch (icmd)) 820 { 821 enum GNUNET_GenericReturnValue ret; 822 823 ret = seek_batch (is, 824 icmd, 825 target); 826 if (GNUNET_SYSERR == ret) 827 return GNUNET_SYSERR; /* failure! */ 828 if (GNUNET_OK == ret) 829 { 830 match = icmd; 831 break; 832 } 833 } 834 } 835 if (NULL == current) 836 { 837 /* refuse to jump forward */ 838 GNUNET_break (0); 839 TALER_TESTING_interpreter_fail (is); 840 return GNUNET_SYSERR; 841 } 842 if (NULL == match) 843 return GNUNET_NO; /* not found */ 844 TALER_TESTING_cmd_batch_set_current (cmd, 845 new_ip); 846 return GNUNET_OK; 847 } 848 849 850 /** 851 * Run the "rewind" CMD. 852 * 853 * @param cls closure. 854 * @param cmd command being executed now. 855 * @param is the interpreter state. 856 */ 857 static void 858 rewind_ip_run (void *cls, 859 const struct TALER_TESTING_Command *cmd, 860 struct TALER_TESTING_Interpreter *is) 861 { 862 struct RewindIpState *ris = cls; 863 const struct TALER_TESTING_Command *target; 864 unsigned int new_ip; 865 866 (void) cmd; 867 if (0 == ris->counter) 868 { 869 TALER_TESTING_interpreter_next (is); 870 return; 871 } 872 target 873 = TALER_TESTING_interpreter_lookup_command (is, 874 ris->target_label); 875 if (NULL == target) 876 { 877 GNUNET_break (0); 878 TALER_TESTING_interpreter_fail (is); 879 return; 880 } 881 ris->counter--; 882 for (new_ip = 0; 883 NULL != is->commands[new_ip].label; 884 new_ip++) 885 { 886 const struct TALER_TESTING_Command *ipcmd 887 = &is->commands[new_ip]; 888 889 if (ipcmd == target) 890 break; 891 if (TALER_TESTING_cmd_is_batch (ipcmd)) 892 { 893 int ret = seek_batch (is, 894 ipcmd, 895 target); 896 if (GNUNET_SYSERR == ret) 897 return; /* failure! */ 898 if (GNUNET_OK == ret) 899 break; 900 } 901 } 902 if (new_ip > (unsigned int) is->ip) 903 { 904 /* refuse to jump forward */ 905 GNUNET_break (0); 906 TALER_TESTING_interpreter_fail (is); 907 return; 908 } 909 is->ip = new_ip - 1; /* -1 because the next function will advance by one */ 910 TALER_TESTING_interpreter_next (is); 911 } 912 913 914 struct TALER_TESTING_Command 915 TALER_TESTING_cmd_rewind_ip (const char *label, 916 const char *target_label, 917 unsigned int counter) 918 { 919 struct RewindIpState *ris; 920 921 ris = GNUNET_new (struct RewindIpState); 922 ris->target_label = target_label; 923 ris->counter = counter; 924 { 925 struct TALER_TESTING_Command cmd = { 926 .cls = ris, 927 .label = label, 928 .run = &rewind_ip_run 929 }; 930 931 return cmd; 932 } 933 } 934 935 936 /** 937 * State for a "authchange" CMD. 938 */ 939 struct AuthchangeState 940 { 941 942 /** 943 * What is the new authorization token to send? 944 */ 945 const char *auth_token; 946 947 /** 948 * Old context, clean up on termination. 949 */ 950 struct GNUNET_CURL_Context *old_ctx; 951 }; 952 953 954 /** 955 * Run the command. 956 * 957 * @param cls closure. 958 * @param cmd the command to execute. 959 * @param is the interpreter state. 960 */ 961 static void 962 authchange_run (void *cls, 963 const struct TALER_TESTING_Command *cmd, 964 struct TALER_TESTING_Interpreter *is) 965 { 966 struct AuthchangeState *ss = cls; 967 968 (void) cmd; 969 ss->old_ctx = is->ctx; 970 if (NULL != is->rc) 971 { 972 GNUNET_CURL_gnunet_rc_destroy (is->rc); 973 is->rc = NULL; 974 } 975 is->ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 976 &is->rc); 977 GNUNET_CURL_enable_async_scope_header (is->ctx, 978 "Taler-Correlation-Id"); 979 GNUNET_assert (NULL != is->ctx); 980 is->rc = GNUNET_CURL_gnunet_rc_create (is->ctx); 981 if (NULL != ss->auth_token) 982 { 983 char *authorization; 984 985 GNUNET_asprintf (&authorization, 986 "%s: %s", 987 MHD_HTTP_HEADER_AUTHORIZATION, 988 ss->auth_token); 989 GNUNET_assert (GNUNET_OK == 990 GNUNET_CURL_append_header (is->ctx, 991 authorization)); 992 GNUNET_free (authorization); 993 } 994 TALER_TESTING_interpreter_next (is); 995 } 996 997 998 /** 999 * Call GNUNET_CURL_fini(). Done as a separate task to 1000 * ensure that all of the command's cleanups have been 1001 * executed first. See #7151. 1002 * 1003 * @param cls a `struct GNUNET_CURL_Context *` to clean up. 1004 */ 1005 static void 1006 deferred_cleanup_cb (void *cls) 1007 { 1008 struct GNUNET_CURL_Context *ctx = cls; 1009 1010 GNUNET_CURL_fini (ctx); 1011 } 1012 1013 1014 /** 1015 * Cleanup the state from a "authchange" CMD. 1016 * 1017 * @param cls closure. 1018 * @param cmd the command which is being cleaned up. 1019 */ 1020 static void 1021 authchange_cleanup (void *cls, 1022 const struct TALER_TESTING_Command *cmd) 1023 { 1024 struct AuthchangeState *ss = cls; 1025 1026 (void) cmd; 1027 if (NULL != ss->old_ctx) 1028 { 1029 (void) GNUNET_SCHEDULER_add_now (&deferred_cleanup_cb, 1030 ss->old_ctx); 1031 ss->old_ctx = NULL; 1032 } 1033 GNUNET_free (ss); 1034 } 1035 1036 1037 struct TALER_TESTING_Command 1038 TALER_TESTING_cmd_set_authorization (const char *label, 1039 const char *auth_token) 1040 { 1041 struct AuthchangeState *ss; 1042 1043 ss = GNUNET_new (struct AuthchangeState); 1044 ss->auth_token = auth_token; 1045 1046 { 1047 struct TALER_TESTING_Command cmd = { 1048 .cls = ss, 1049 .label = label, 1050 .run = &authchange_run, 1051 .cleanup = &authchange_cleanup 1052 }; 1053 1054 return cmd; 1055 } 1056 } 1057 1058 1059 /* end of testing_api_loop.c */