From a2dde02b64a8ee75c9243632eb45a6ceb9b62dd5 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 7 Jun 2023 23:11:05 +0200 Subject: major libtalertesting API refactoring, including no longer having taler-specific logic in the test engine core --- src/testing/testing_api_loop.c | 866 ++++++++++++++++++++++++----------------- 1 file changed, 502 insertions(+), 364 deletions(-) (limited to 'src/testing/testing_api_loop.c') diff --git a/src/testing/testing_api_loop.c b/src/testing/testing_api_loop.c index 271b6e768..ea6b805b0 100644 --- a/src/testing/testing_api_loop.c +++ b/src/testing/testing_api_loop.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2018 Taler Systems SA + Copyright (C) 2018-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -26,15 +26,62 @@ #include "platform.h" #include "taler_json_lib.h" #include +#include "taler_extensions.h" #include "taler_signatures.h" #include "taler_testing_lib.h" -#include "taler_fakebank_lib.h" -/** - * Pipe used to communicate child death via signal. - * Must be global, as used in signal handler! - */ -static struct GNUNET_DISK_PipeHandle *sigpipe; + +struct TALER_TESTING_Interpreter +{ + + /** + * Commands the interpreter will run. + */ + struct TALER_TESTING_Command *commands; + + /** + * Interpreter task (if one is scheduled). + */ + struct GNUNET_SCHEDULER_Task *task; + + /** + * Handle for the child management. + */ + struct GNUNET_ChildWaitHandle *cwh; + + /** + * Main execution context for the main loop. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Context for running the CURL event loop. + */ + struct GNUNET_CURL_RescheduleContext *rc; + + /** + * Hash map mapping variable names to commands. + */ + struct GNUNET_CONTAINER_MultiHashMap *vars; + + /** + * Task run on timeout. + */ + struct GNUNET_SCHEDULER_Task *timeout_task; + + /** + * Instruction pointer. Tells #interpreter_run() which instruction to run + * next. Need (signed) int because it gets -1 when rewinding the + * interpreter to the first CMD. + */ + int ip; + + /** + * Result of the testcases, #GNUNET_OK on success + */ + enum GNUNET_GenericReturnValue result; + +}; const struct TALER_TESTING_Command * @@ -90,13 +137,29 @@ TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *is, "Command not found: %s\n", label); return NULL; +} + +const struct TALER_TESTING_Command * +TALER_TESTING_interpreter_get_command (struct TALER_TESTING_Interpreter *is, + const char *name) +{ + const struct TALER_TESTING_Command *cmd; + struct GNUNET_HashCode h_name; + + GNUNET_CRYPTO_hash (name, + strlen (name), + &h_name); + cmd = GNUNET_CONTAINER_multihashmap_get (is->vars, + &h_name); + if (NULL == cmd) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Command not found by name: %s\n", + name); + return cmd; } -/** - * Obtain main execution context for the main loop. - */ struct GNUNET_CURL_Context * TALER_TESTING_interpreter_get_context (struct TALER_TESTING_Interpreter *is) { @@ -104,38 +167,18 @@ TALER_TESTING_interpreter_get_context (struct TALER_TESTING_Interpreter *is) } -struct TALER_FAKEBANK_Handle * -TALER_TESTING_interpreter_get_fakebank (struct TALER_TESTING_Interpreter *is) +void +TALER_TESTING_touch_cmd (struct TALER_TESTING_Interpreter *is) { - return is->fakebank; + is->commands[is->ip].last_req_time + = GNUNET_TIME_absolute_get (); } void -TALER_TESTING_run_with_fakebank (struct TALER_TESTING_Interpreter *is, - struct TALER_TESTING_Command *commands, - const char *bank_url) +TALER_TESTING_inc_tries (struct TALER_TESTING_Interpreter *is) { - char *currency; - - if (GNUNET_OK != - TALER_config_get_currency (is->cfg, - ¤cy)) - { - is->result = GNUNET_SYSERR; - return; - } - is->fakebank = TALER_TESTING_run_fakebank (bank_url, - currency); - GNUNET_free (currency); - if (NULL == is->fakebank) - { - GNUNET_break (0); - is->result = GNUNET_SYSERR; - return; - } - TALER_TESTING_run (is, - commands); + is->commands[is->ip].num_tries++; } @@ -159,7 +202,12 @@ TALER_TESTING_interpreter_next (struct TALER_TESTING_Interpreter *is) return; /* ignore, we already failed! */ if (TALER_TESTING_cmd_is_batch (cmd)) { - TALER_TESTING_cmd_batch_next (is); + if (TALER_TESTING_cmd_batch_next (is, + cmd->cls)) + { + cmd->finish_time = GNUNET_TIME_absolute_get (); + is->ip++; /* batch is done */ + } } else { @@ -202,19 +250,9 @@ TALER_TESTING_interpreter_fail (struct TALER_TESTING_Interpreter *is) } -struct TALER_TESTING_Command -TALER_TESTING_cmd_end (void) -{ - static struct TALER_TESTING_Command cmd; - cmd.label = NULL; - - return cmd; -} - - const char * -TALER_TESTING_interpreter_get_current_label (struct - TALER_TESTING_Interpreter *is) +TALER_TESTING_interpreter_get_current_label ( + struct TALER_TESTING_Interpreter *is) { struct TALER_TESTING_Command *cmd = &is->commands[is->ip]; @@ -246,6 +284,19 @@ interpreter_run (void *cls) = cmd->last_req_time = GNUNET_TIME_absolute_get (); cmd->num_tries = 1; + if (NULL != cmd->name) + { + struct GNUNET_HashCode h_name; + + GNUNET_CRYPTO_hash (cmd->name, + strlen (cmd->name), + &h_name); + (void) GNUNET_CONTAINER_multihashmap_put ( + is->vars, + &h_name, + cmd, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); + } cmd->run (cmd->cls, cmd, is); @@ -277,22 +328,11 @@ do_shutdown (void *cls) if (NULL != cmd->cleanup) cmd->cleanup (cmd->cls, cmd); - if (NULL != is->exchange) - { - TALER_LOG_DEBUG ("Disconnecting the exchange\n"); - TALER_EXCHANGE_disconnect (is->exchange); - is->exchange = NULL; - } if (NULL != is->task) { GNUNET_SCHEDULER_cancel (is->task); is->task = NULL; } - if (NULL != is->fakebank) - { - TALER_FAKEBANK_stop (is->fakebank); - is->fakebank = NULL; - } if (NULL != is->ctx) { GNUNET_CURL_fini (is->ctx); @@ -303,15 +343,20 @@ do_shutdown (void *cls) GNUNET_CURL_gnunet_rc_destroy (is->rc); is->rc = NULL; } + if (NULL != is->vars) + { + GNUNET_CONTAINER_multihashmap_destroy (is->vars); + is->vars = NULL; + } if (NULL != is->timeout_task) { GNUNET_SCHEDULER_cancel (is->timeout_task); is->timeout_task = NULL; } - if (NULL != is->child_death_task) + if (NULL != is->cwh) { - GNUNET_SCHEDULER_cancel (is->child_death_task); - is->child_death_task = NULL; + GNUNET_wait_child_cancel (is->cwh); + is->cwh = NULL; } GNUNET_free (is->commands); } @@ -339,30 +384,24 @@ do_timeout (void *cls) * process died). * * @param cls the `struct TALER_TESTING_Interpreter *` + * @param type type of the process + * @param exit_code status code of the process */ static void -maint_child_death (void *cls) +maint_child_death (void *cls, + enum GNUNET_OS_ProcessStatusType type, + long unsigned int code) { struct TALER_TESTING_Interpreter *is = cls; struct TALER_TESTING_Command *cmd = &is->commands[is->ip]; - const struct GNUNET_DISK_FileHandle *pr; struct GNUNET_OS_Process **processp; - char c[16]; - enum GNUNET_OS_ProcessStatusType type; - unsigned long code; + is->cwh = NULL; while (TALER_TESTING_cmd_is_batch (cmd)) cmd = TALER_TESTING_cmd_batch_get_current (cmd); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got SIGCHLD for `%s'.\n", cmd->label); - is->child_death_task = NULL; - pr = GNUNET_DISK_pipe_handle (sigpipe, - GNUNET_DISK_PIPE_END_READ); - GNUNET_break (0 < - GNUNET_DISK_file_read (pr, - &c, - sizeof (c))); if (GNUNET_OK != TALER_TESTING_get_trait_process (cmd, &processp)) @@ -371,13 +410,8 @@ maint_child_death (void *cls) TALER_TESTING_interpreter_fail (is); return; } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got the dead child process handle, waiting for termination ...\n"); - GNUNET_assert (GNUNET_OK == - GNUNET_OS_process_wait_status (*processp, - &type, - &code)); GNUNET_OS_process_destroy (*processp); *processp = NULL; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -411,7 +445,6 @@ maint_child_death (void *cls) TALER_TESTING_interpreter_fail (is); return; } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Dead child, go on with next command.\n"); TALER_TESTING_interpreter_next (is); @@ -421,16 +454,24 @@ maint_child_death (void *cls) void TALER_TESTING_wait_for_sigchld (struct TALER_TESTING_Interpreter *is) { - const struct GNUNET_DISK_FileHandle *pr; + struct GNUNET_OS_Process **processp; + struct TALER_TESTING_Command *cmd = &is->commands[is->ip]; - GNUNET_assert (NULL == is->child_death_task); - pr = GNUNET_DISK_pipe_handle (sigpipe, - GNUNET_DISK_PIPE_END_READ); - is->child_death_task - = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - pr, - &maint_child_death, - is); + while (TALER_TESTING_cmd_is_batch (cmd)) + cmd = TALER_TESTING_cmd_batch_get_current (cmd); + if (GNUNET_OK != + TALER_TESTING_get_trait_process (cmd, + &processp)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (NULL == is->cwh); + is->cwh + = GNUNET_wait_child (*processp, + &maint_child_death, + is); } @@ -507,349 +548,446 @@ struct MainContext /** - * Signal handler called for SIGCHLD. Triggers the - * respective handler by writing to the trigger pipe. + * Initialize scheduler loop and curl context for the testcase, + * and responsible to run the "run" method. + * + * @param cls closure, typically the "run" method, the + * interpreter state and a closure for "run". */ static void -sighandler_child_death (void) +main_wrapper (void *cls) { - static char c; - int old_errno = errno; /* back-up errno */ + struct MainContext *main_ctx = cls; - GNUNET_break (1 == GNUNET_DISK_file_write - (GNUNET_DISK_pipe_handle (sigpipe, - GNUNET_DISK_PIPE_END_WRITE), - &c, sizeof (c))); - errno = old_errno; /* restore errno */ + main_ctx->main_cb (main_ctx->main_cb_cls, + main_ctx->is); } +enum GNUNET_GenericReturnValue +TALER_TESTING_loop (TALER_TESTING_Main main_cb, + void *main_cb_cls) +{ + struct TALER_TESTING_Interpreter is; + struct MainContext main_ctx = { + .main_cb = main_cb, + .main_cb_cls = main_cb_cls, + /* needed to init the curl ctx */ + .is = &is, + }; + + memset (&is, + 0, + sizeof (is)); + is.ctx = GNUNET_CURL_init ( + &GNUNET_CURL_gnunet_scheduler_reschedule, + &is.rc); + GNUNET_CURL_enable_async_scope_header (is.ctx, + "Taler-Correlation-Id"); + GNUNET_assert (NULL != is.ctx); + is.rc = GNUNET_CURL_gnunet_rc_create (is.ctx); + is.vars = GNUNET_CONTAINER_multihashmap_create (1024, + false); + /* Blocking */ + GNUNET_SCHEDULER_run (&main_wrapper, + &main_ctx); + return is.result; +} + + +int +TALER_TESTING_main (char *const *argv, + const char *loglevel, + const char *cfg_file, + const char *exchange_account_section, + enum TALER_TESTING_BankSystem bs, + struct TALER_TESTING_Credentials *cred, + TALER_TESTING_Main main_cb, + void *main_cb_cls) +{ + enum GNUNET_GenericReturnValue ret; + + unsetenv ("XDG_DATA_HOME"); + unsetenv ("XDG_CONFIG_HOME"); + GNUNET_log_setup (argv[0], + loglevel, + NULL); + if (GNUNET_OK != + TALER_TESTING_get_credentials (cfg_file, + exchange_account_section, + bs, + cred)) + { + GNUNET_break (0); + return 77; + } + if (GNUNET_OK != + TALER_TESTING_cleanup_files_cfg (NULL, + cred->cfg)) + { + GNUNET_break (0); + return 77; + } + if (GNUNET_OK != + TALER_extensions_init (cred->cfg)) + { + GNUNET_break (0); + return 77; + } + ret = TALER_TESTING_loop (main_cb, + main_cb_cls); + /* TODO: should we free 'cred' resources here? */ + return (GNUNET_OK == ret) ? 0 : 1; +} + + +/* ************** iterate over commands ********* */ + + void -TALER_TESTING_cert_cb (void *cls, - const struct TALER_EXCHANGE_KeysResponse *kr) +TALER_TESTING_iterate (struct TALER_TESTING_Interpreter *is, + bool asc, + TALER_TESTING_CommandIterator cb, + void *cb_cls) { - const struct TALER_EXCHANGE_HttpResponse *hr = &kr->hr; - struct MainContext *main_ctx = cls; - struct TALER_TESTING_Interpreter *is = main_ctx->is; + unsigned int start; + unsigned int end; + int inc; - switch (hr->http_status) + if (asc) { - case MHD_HTTP_OK: - /* dealt with below */ - break; - default: - if (GNUNET_YES == is->working) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Got NULL response for /keys during execution (%u/%d)!\n", - hr->http_status, - (int) hr->ec); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Got failure response for /keys during startup (%u/%d), retrying!\n", - hr->http_status, - (int) hr->ec); - TALER_EXCHANGE_disconnect (is->exchange); - GNUNET_assert ( - NULL != (is->exchange - = TALER_EXCHANGE_connect (is->ctx, - main_ctx->exchange_url, - &TALER_TESTING_cert_cb, - main_ctx, - TALER_EXCHANGE_OPTION_END))); - return; + inc = 1; + start = 0; + end = is->ip; + } + else + { + inc = -1; + start = is->ip; + end = 0; + } + for (unsigned int off = start; off != end + inc; off += inc) + { + const struct TALER_TESTING_Command *cmd = &is->commands[off]; + + cb (cb_cls, + cmd); } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got %d DK from /keys in generation %u\n", - kr->details.ok.keys->num_denom_keys, - is->key_generation + 1); - is->key_generation++; - is->keys = kr->details.ok.keys; - - /* /keys has been called for some reason and - * the interpreter is already running. */ - if (GNUNET_YES == is->working) - return; - is->working = GNUNET_YES; - /* Trigger the next command. */ - TALER_LOG_DEBUG ("Cert_cb, scheduling CMD (ip: %d)\n", - is->ip); - GNUNET_SCHEDULER_add_now (&interpreter_run, - is); +} + + +/* ************** special commands ********* */ + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_end (void) +{ + static struct TALER_TESTING_Command cmd; + cmd.label = NULL; + + return cmd; +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_set_var (const char *name, + struct TALER_TESTING_Command cmd) +{ + cmd.name = name; + return cmd; } /** - * Initialize scheduler loop and curl context for the testcase, - * and responsible to run the "run" method. + * State for a "rewind" CMD. + */ +struct RewindIpState +{ + /** + * Instruction pointer to set into the interpreter. + */ + const char *target_label; + + /** + * How many times this set should take place. However, this value lives at + * the calling process, and this CMD is only in charge of checking and + * decremeting it. + */ + unsigned int counter; +}; + + +/** + * Seek for the @a target command in @a batch (and rewind to it + * if successful). * - * @param cls closure, typically the "run" method, the - * interpreter state and a closure for "run". + * @param is the interpreter state (for failures) + * @param cmd batch to search for @a target + * @param target command to search for + * @return #GNUNET_OK on success, #GNUNET_NO if target was not found, + * #GNUNET_SYSERR if target is in the future and we failed */ -static void -main_wrapper_exchange_agnostic (void *cls) +static enum GNUNET_GenericReturnValue +seek_batch (struct TALER_TESTING_Interpreter *is, + const struct TALER_TESTING_Command *cmd, + const struct TALER_TESTING_Command *target) { - struct MainContext *main_ctx = cls; + unsigned int new_ip; + struct TALER_TESTING_Command **batch; + struct TALER_TESTING_Command *current; + struct TALER_TESTING_Command *icmd; + struct TALER_TESTING_Command *match; - main_ctx->main_cb (main_ctx->main_cb_cls, - main_ctx->is); + current = TALER_TESTING_cmd_batch_get_current (cmd); + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_batch_cmds (cmd, + &batch)); + match = NULL; + for (new_ip = 0; + NULL != (icmd = &(*batch)[new_ip]); + new_ip++) + { + if (current == target) + current = NULL; + if (icmd == target) + { + match = icmd; + break; + } + if (TALER_TESTING_cmd_is_batch (icmd)) + { + int ret = seek_batch (is, + icmd, + target); + if (GNUNET_SYSERR == ret) + return GNUNET_SYSERR; /* failure! */ + if (GNUNET_OK == ret) + { + match = icmd; + break; + } + } + } + if (NULL == current) + { + /* refuse to jump forward */ + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return GNUNET_SYSERR; + } + if (NULL == match) + return GNUNET_NO; /* not found */ + TALER_TESTING_cmd_batch_set_current (cmd, + new_ip); + return GNUNET_OK; } /** - * Function run when the test is aborted before we launch the actual - * interpreter. Cleans up our state. + * Run the "rewind" CMD. * - * @param cls the main context + * @param cls closure. + * @param cmd command being executed now. + * @param is the interpreter state. */ static void -do_abort (void *cls) +rewind_ip_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) { - struct MainContext *main_ctx = cls; - struct TALER_TESTING_Interpreter *is = main_ctx->is; + struct RewindIpState *ris = cls; + const struct TALER_TESTING_Command *target; + unsigned int new_ip; - is->timeout_task = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Executing abort prior to interpreter launch\n"); - if (NULL != is->exchange) + (void) cmd; + if (0 == ris->counter) { - TALER_EXCHANGE_disconnect (is->exchange); - is->exchange = NULL; + TALER_TESTING_interpreter_next (is); + return; } - if (NULL != is->fakebank) + target + = TALER_TESTING_interpreter_lookup_command (is, + ris->target_label); + if (NULL == target) { - TALER_FAKEBANK_stop (is->fakebank); - is->fakebank = NULL; + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; } - if (NULL != is->ctx) + ris->counter--; + for (new_ip = 0; + NULL != is->commands[new_ip].label; + new_ip++) { - GNUNET_CURL_fini (is->ctx); - is->ctx = NULL; + const struct TALER_TESTING_Command *cmd = &is->commands[new_ip]; + + if (cmd == target) + break; + if (TALER_TESTING_cmd_is_batch (cmd)) + { + int ret = seek_batch (is, + cmd, + target); + if (GNUNET_SYSERR == ret) + return; /* failure! */ + if (GNUNET_OK == ret) + break; + } } - if (NULL != is->rc) + if (new_ip > (unsigned int) is->ip) { - GNUNET_CURL_gnunet_rc_destroy (is->rc); - is->rc = NULL; + /* refuse to jump forward */ + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; } + is->ip = new_ip - 1; /* -1 because the next function will advance by one */ + TALER_TESTING_interpreter_next (is); } -/** - * Initialize scheduler loop and curl context for the testcase, - * and responsible to run the "run" method. - * - * @param cls a `struct MainContext *` - */ -static void -main_wrapper_exchange_connect (void *cls) +struct TALER_TESTING_Command +TALER_TESTING_cmd_rewind_ip (const char *label, + const char *target_label, + unsigned int counter) { - struct MainContext *main_ctx = cls; - struct TALER_TESTING_Interpreter *is = main_ctx->is; - char *exchange_url; + struct RewindIpState *ris; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (is->cfg, - "exchange", - "BASE_URL", - &exchange_url)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "BASE_URL"); - return; + ris = GNUNET_new (struct RewindIpState); + ris->target_label = target_label; + ris->counter = counter; + { + struct TALER_TESTING_Command cmd = { + .cls = ris, + .label = label, + .run = &rewind_ip_run + }; + + return cmd; } - main_ctx->exchange_url = exchange_url; - is->timeout_task = GNUNET_SCHEDULER_add_shutdown (&do_abort, - main_ctx); - is->working = GNUNET_YES; - GNUNET_assert (NULL == is->exchange); - GNUNET_break ( - NULL != (is->exchange = - TALER_EXCHANGE_connect (is->ctx, - exchange_url, - &TALER_TESTING_cert_cb, - main_ctx, - TALER_EXCHANGE_OPTION_END))); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Starting main test loop\n"); - main_ctx->main_cb (main_ctx->main_cb_cls, - is); } /** - * Load the exchange and auditor key material into @a is. + * State for a "authchange" CMD. + */ +struct AuthchangeState +{ + + /** + * What is the new authorization token to send? + */ + const char *auth_token; + + /** + * Old context, clean up on termination. + */ + struct GNUNET_CURL_Context *old_ctx; +}; + + +/** + * Run the command. * - * @param[in,out] is state to initialize + * @param cls closure. + * @param cmd the command to execute. + * @param is the interpreter state. */ -static enum GNUNET_GenericReturnValue -load_keys (struct TALER_TESTING_Interpreter *is) +static void +authchange_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) { - char *fn; + struct AuthchangeState *ss = cls; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (is->cfg, - "exchange-offline", - "MASTER_PRIV_FILE", - &fn)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange-offline", - "MASTER_PRIV_FILE"); - return GNUNET_SYSERR; - } - if (GNUNET_SYSERR == - GNUNET_DISK_directory_create_for_file (fn)) + (void) cmd; + ss->old_ctx = is->ctx; + if (NULL != is->rc) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not setup directory for master private key file `%s'\n", - fn); - GNUNET_free (fn); - return GNUNET_SYSERR; + GNUNET_CURL_gnunet_rc_destroy (is->rc); + is->rc = NULL; } - if (GNUNET_SYSERR == - GNUNET_CRYPTO_eddsa_key_from_file (fn, - GNUNET_YES, - &is->master_priv.eddsa_priv)) + is->ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, + &is->rc); + GNUNET_CURL_enable_async_scope_header (is->ctx, + "Taler-Correlation-Id"); + GNUNET_assert (NULL != is->ctx); + is->rc = GNUNET_CURL_gnunet_rc_create (is->ctx); + if (NULL != ss->auth_token) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not load master private key from `%s'\n", - fn); - GNUNET_free (fn); - return GNUNET_SYSERR; - } - GNUNET_free (fn); - GNUNET_CRYPTO_eddsa_key_get_public (&is->master_priv.eddsa_priv, - &is->master_pub.eddsa_pub); + char *authorization; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (is->cfg, - "auditor", - "AUDITOR_PRIV_FILE", - &fn)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "auditor", - "AUDITOR_PRIV_FILE"); - return GNUNET_SYSERR; - } - if (GNUNET_SYSERR == - GNUNET_DISK_directory_create_for_file (fn)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not setup directory for auditor private key file `%s'\n", - fn); - GNUNET_free (fn); - return GNUNET_SYSERR; - } - if (GNUNET_SYSERR == - GNUNET_CRYPTO_eddsa_key_from_file (fn, - GNUNET_YES, - &is->auditor_priv.eddsa_priv)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not load auditor private key from `%s'\n", - fn); - GNUNET_free (fn); - return GNUNET_SYSERR; + GNUNET_asprintf (&authorization, + "%s: %s", + MHD_HTTP_HEADER_AUTHORIZATION, + ss->auth_token); + GNUNET_assert (GNUNET_OK == + GNUNET_CURL_append_header (is->ctx, + authorization)); + GNUNET_free (authorization); } - GNUNET_free (fn); - GNUNET_CRYPTO_eddsa_key_get_public (&is->auditor_priv.eddsa_priv, - &is->auditor_pub.eddsa_pub); - return GNUNET_OK; + TALER_TESTING_interpreter_next (is); } /** - * Load the exchange and auditor URLs from the configuration into @a is. + * Call GNUNET_CURL_fini(). Done as a separate task to + * ensure that all of the command's cleanups have been + * executed first. See #7151. * - * @param[in,out] is state to initialize + * @param cls a `struct GNUNET_CURL_Context *` to clean up. */ -static enum GNUNET_GenericReturnValue -load_urls (struct TALER_TESTING_Interpreter *is) +static void +deferred_cleanup_cb (void *cls) { - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (is->cfg, - "auditor", - "BASE_URL", - &is->auditor_url)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "auditor", - "BASE_URL"); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (is->cfg, - "exchange", - "BASE_URL", - &is->exchange_url)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "BASE_URL"); - GNUNET_free (is->auditor_url); - return GNUNET_SYSERR; + struct GNUNET_CURL_Context *ctx = cls; + + GNUNET_CURL_fini (ctx); +} + + +/** + * Cleanup the state from a "authchange" CMD. + * + * @param cls closure. + * @param cmd the command which is being cleaned up. + */ +static void +authchange_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct AuthchangeState *ss = cls; + + (void) cmd; + if (NULL != ss->old_ctx) + { + (void) GNUNET_SCHEDULER_add_now (&deferred_cleanup_cb, + ss->old_ctx); + ss->old_ctx = NULL; } - return GNUNET_OK; + GNUNET_free (ss); } -enum GNUNET_GenericReturnValue -TALER_TESTING_setup (TALER_TESTING_Main main_cb, - void *main_cb_cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_OS_Process *exchanged, - int exchange_connect) -{ - struct TALER_TESTING_Interpreter is = { - .cfg = cfg, - .exchanged = exchanged - }; - struct MainContext main_ctx = { - .main_cb = main_cb, - .main_cb_cls = main_cb_cls, - /* needed to init the curl ctx */ - .is = &is, - }; - struct GNUNET_SIGNAL_Context *shc_chld; +struct TALER_TESTING_Command +TALER_TESTING_cmd_set_authorization (const char *label, + const char *auth_token) +{ + struct AuthchangeState *ss; - if (GNUNET_OK != - load_keys (&is)) - return GNUNET_SYSERR; - if (GNUNET_OK != - load_urls (&is)) - return GNUNET_SYSERR; - sigpipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE); - GNUNET_assert (NULL != sigpipe); - shc_chld = GNUNET_SIGNAL_handler_install ( - SIGCHLD, - &sighandler_child_death); - is.ctx = GNUNET_CURL_init ( - &GNUNET_CURL_gnunet_scheduler_reschedule, - &is.rc); - GNUNET_CURL_enable_async_scope_header (is.ctx, - "Taler-Correlation-Id"); - GNUNET_assert (NULL != is.ctx); - is.rc = GNUNET_CURL_gnunet_rc_create (is.ctx); + ss = GNUNET_new (struct AuthchangeState); + ss->auth_token = auth_token; - /* Blocking */ - if (GNUNET_YES == exchange_connect) - GNUNET_SCHEDULER_run (&main_wrapper_exchange_connect, - &main_ctx); - else - GNUNET_SCHEDULER_run (&main_wrapper_exchange_agnostic, - &main_ctx); - if (NULL != is.final_cleanup_cb) - is.final_cleanup_cb (is.final_cleanup_cb_cls); - GNUNET_free (main_ctx.exchange_url); - GNUNET_SIGNAL_handler_uninstall (shc_chld); - GNUNET_DISK_pipe_close (sigpipe); - sigpipe = NULL; - GNUNET_free (is.auditor_url); - GNUNET_free (is.exchange_url); - return is.result; + { + struct TALER_TESTING_Command cmd = { + .cls = ss, + .label = label, + .run = &authchange_run, + .cleanup = &authchange_cleanup + }; + + return cmd; + } } -- cgit v1.2.3