diff --git a/src/bank-lib/test_bank.sh b/src/bank-lib/test_bank.sh index ab76d2bad..7d72497ea 100755 --- a/src/bank-lib/test_bank.sh +++ b/src/bank-lib/test_bank.sh @@ -1,4 +1,5 @@ #!/bin/bash +# This file is in the public domain. set -eu diff --git a/src/benchmark/taler-benchmark-setup.sh b/src/benchmark/taler-benchmark-setup.sh index 57f2e0cde..70a7654bf 100755 --- a/src/benchmark/taler-benchmark-setup.sh +++ b/src/benchmark/taler-benchmark-setup.sh @@ -57,16 +57,19 @@ START_AUDITOR=0 START_BACKUP=0 START_EXCHANGE=0 START_FAKEBANK=0 +START_AGGREGATOR=0 START_MERCHANT=0 START_NEXUS=0 START_SANDBOX=0 +START_TRANSFER=0 +START_WIREWATCH=0 USE_VALGRIND="" CONF_ORIG="~/.config/taler.conf" LOGLEVEL="DEBUG" DEFAULT_SLEEP="0.2" # Parse command-line options -while getopts ':abc:efhl:mnsv' OPTION; do +while getopts ':abc:efghl:mnstvw' OPTION; do case "$OPTION" in a) START_AUDITOR="1" @@ -95,9 +98,14 @@ while getopts ':abc:efhl:mnsv' OPTION; do echo ' -m -- start merchant' echo ' -n -- start nexus' echo ' -s -- start sandbox' + echo ' -t -- start transfer' echo ' -v -- use valgrind' + echo ' -w -- start wirewatch' exit 0 ;; + g) + START_AGGREGATOR="1" + ;; l) LOGLEVEL="$OPTARG" ;; @@ -110,10 +118,16 @@ while getopts ':abc:efhl:mnsv' OPTION; do s) START_SANDBOX="1" ;; + t) + START_TRANSFER="1" + ;; v) USE_VALGRIND="valgrind --leak-check=yes" DEFAULT_SLEEP="2" ;; + w) + START_WIREWATCH="1" + ;; ?) exit_fail "Unrecognized command line option" ;; @@ -364,7 +378,6 @@ fi if [ "1" = "$START_EXCHANGE" ] then echo -n "Starting exchange ..." - EXCHANGE_PORT=$(taler-config -c "$CONF" -s EXCHANGE -o PORT) EXCHANGE_URL="http://localhost:${EXCHANGE_PORT}/" MASTER_PRIV_FILE=$(taler-config -f -c "${CONF}" -s "EXCHANGE-OFFLINE" -o "MASTER_PRIV_FILE") @@ -384,10 +397,28 @@ then $USE_VALGRIND taler-exchange-secmod-cs -c "$CONF" -L "$LOGLEVEL" 2> taler-exchange-secmod-cs.log & $USE_VALGRIND taler-exchange-httpd -c "$CONF" -L "$LOGLEVEL" 2> taler-exchange-httpd.log & EXCHANGE_HTTPD_PID=$! + echo " DONE" +fi + +if [ "1" = "$START_WIREWATCH" ] +then + echo -n "Starting wirewatch ..." $USE_VALGRIND taler-exchange-wirewatch -c "$CONF" 2> taler-exchange-wirewatch.log & WIREWATCH_PID=$! + echo " DONE" +fi + +if [ "1" = "$START_AGGREGATOR" ] +then + echo -n "Starting aggregator ..." $USE_VALGRIND taler-exchange-aggregator -c "$CONF" 2> taler-exchange-aggregator.log & AGGREGATOR_PID=$! + echo " DONE" +fi + +if [ "1" = "$START_TRANSFER" ] +then + echo -n "Starting transfer ..." $USE_VALGRIND taler-exchange-transfer -c "$CONF" 2> taler-exchange-transfer.log & TRANSFER_PID=$! echo " DONE" diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h index 664db6ccd..23a4c2526 100644 --- a/src/include/taler_testing_lib.h +++ b/src/include/taler_testing_lib.h @@ -986,6 +986,21 @@ TALER_TESTING_history_entry_cmp ( /* ************** Specific interpreter commands ************ */ +/** + * Launch GNU Taler setup. + * + * @param label command label. + * @param config_file configuration file to use + * @param ... NULL-terminated (const char *) arguments to pass to taler-benchmark-setup.sh + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_system_start ( + const char *label, + const char *config_file, + ...); + + /** * Command to modify authorization header used in the CURL context. * This will destroy the existing CURL context and create a fresh diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am index 86cb029c7..8aa3ac1b9 100644 --- a/src/testing/Makefile.am +++ b/src/testing/Makefile.am @@ -107,6 +107,7 @@ libtalertesting_la_SOURCES = \ testing_api_cmd_signal.c \ testing_api_cmd_sleep.c \ testing_api_cmd_stat.c \ + testing_api_cmd_system_start.c \ testing_api_cmd_take_aml_decision.c \ testing_api_cmd_transfer_get.c \ testing_api_cmd_wait.c \ diff --git a/src/testing/testing_api_cmd_system_start.c b/src/testing/testing_api_cmd_system_start.c new file mode 100644 index 000000000..491f03c56 --- /dev/null +++ b/src/testing/testing_api_cmd_system_start.c @@ -0,0 +1,387 @@ +/* + This file is part of TALER + Copyright (C) 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 + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, + see +*/ +/** + * @file testing/testing_api_cmd_system_start.c + * @brief run taler-benchmark-setup.sh command + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "taler_signatures.h" +#include "taler_testing_lib.h" + + +/** + * State for a "system" CMD. + */ +struct SystemState +{ + + /** + * System process. + */ + struct GNUNET_OS_Process *system_proc; + + /** + * Input pipe to @e system_proc, used to keep the + * process alive until we are done. + */ + struct GNUNET_DISK_PipeHandle *pipe_in; + + /** + * Output pipe to @e system_proc, used to find out + * when the services are ready. + */ + struct GNUNET_DISK_PipeHandle *pipe_out; + + /** + * Task reading from @e pipe_in. + */ + struct GNUNET_SCHEDULER_Task *reader; + + /** + * Waiting for child to die. + */ + struct GNUNET_ChildWaitHandle *cwh; + + /** + * Our interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * NULL-terminated array of command-line arguments. + */ + char **args; + + /** + * Current input buffer, 0-terminated. Contains the last 15 bytes of input + * so we can search them again for the "<>" tag. + */ + char ibuf[16]; + + /** + * Did we find the ready tag? + */ + bool ready; + + /** + * Is the child process still running? + */ + bool active; +}; + + +/** + * Defines a GNUNET_ChildCompletedCallback which is sent back + * upon death or completion of a child process. + * + * @param cls our `struct SystemState *` + * @param type type of the process + * @param exit_code status code of the process + */ +static void +setup_terminated (void *cls, + enum GNUNET_OS_ProcessStatusType type, + long unsigned int exit_code) +{ + struct SystemState *as = cls; + + as->cwh = NULL; + as->active = false; + if (NULL != as->reader) + { + GNUNET_SCHEDULER_cancel (as->reader); + as->reader = NULL; + } + if (! as->ready) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Launching Taler system failed: %d/%llu\n", + (int) type, + (unsigned long long) exit_code); + TALER_TESTING_interpreter_fail (as->is); + return; + } +} + + +/** + * Start helper to read from stdout of child. + * + * @param as our system state + */ +static void +start_reader (struct SystemState *as); + + +static void +read_stdout (void *cls) +{ + struct SystemState *as = cls; + const struct GNUNET_DISK_FileHandle *fh; + char buf[1024 * 10]; + ssize_t ret; + size_t off = 0; + + as->reader = NULL; + strcpy (buf, + as->ibuf); + off = strlen (buf); + fh = GNUNET_DISK_pipe_handle (as->pipe_out, + GNUNET_DISK_PIPE_END_READ); + ret = GNUNET_DISK_file_read (fh, + &buf[off], + sizeof (buf) - off); + if (-1 == ret) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "read"); + TALER_TESTING_interpreter_fail (as->is); + return; + } + if (0 == ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Child closed stdout\n"); + return; + } + start_reader (as); + off += ret; + if (as->ready) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Taler system UP\n"); + TALER_TESTING_interpreter_next (as->is); + return; /* done */ + } + if (NULL != + memmem (buf, + off, + "\n<>\n", + strlen ("\n<>\n"))) + { + as->ready = true; + return; + } + + { + size_t mcpy; + + mcpy = GNUNET_MIN (off, + sizeof (as->ibuf) - 1); + memcpy (as->ibuf, + &buf[off - mcpy], + mcpy); + as->ibuf[mcpy] = '\0'; + } +} + + +static void +start_reader (struct SystemState *as) +{ + const struct GNUNET_DISK_FileHandle *fh; + + GNUNET_assert (NULL == as->reader); + fh = GNUNET_DISK_pipe_handle (as->pipe_out, + GNUNET_DISK_PIPE_END_READ); + as->reader = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, + fh, + &read_stdout, + as); +} + + +/** + * Run the command. Use the `taler-exchange-system' program. + * + * @param cls closure. + * @param cmd command being run. + * @param is interpreter state. + */ +static void +system_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct SystemState *as = cls; + + (void) cmd; + as->is = is; + as->pipe_in = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE); + GNUNET_assert (NULL != as->pipe_in); + as->pipe_out = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE); + GNUNET_assert (NULL != as->pipe_out); + as->system_proc + = GNUNET_OS_start_process_vap ( + GNUNET_OS_INHERIT_STD_ERR, + as->pipe_in, as->pipe_out, NULL, + "taler-benchmark-setup.sh", + as->args); + if (NULL == as->system_proc) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + as->active = true; + start_reader (as); + as->cwh = GNUNET_wait_child (as->system_proc, + &setup_terminated, + as); +} + + +/** + * Free the state of a "system" CMD, and possibly kill its + * process if it did not terminate correctly. + * + * @param cls closure. + * @param cmd the command being freed. + */ +static void +system_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct SystemState *as = cls; + + (void) cmd; + if (NULL != as->cwh) + { + GNUNET_wait_child_cancel (as->cwh); + as->cwh = NULL; + } + if (NULL != as->reader) + { + GNUNET_SCHEDULER_cancel (as->reader); + as->reader = NULL; + } + if (NULL != as->pipe_in) + { + GNUNET_break (GNUNET_OK == + GNUNET_DISK_pipe_close (as->pipe_in)); + as->pipe_in = NULL; + } + if (NULL != as->pipe_out) + { + GNUNET_break (GNUNET_OK == + GNUNET_DISK_pipe_close (as->pipe_out)); + as->pipe_out = NULL; + } + if (NULL != as->system_proc) + { + if (as->active) + { + GNUNET_break (0 == + GNUNET_OS_process_kill (as->system_proc, + SIGTERM)); + GNUNET_OS_process_wait (as->system_proc); + } + GNUNET_OS_process_destroy (as->system_proc); + as->system_proc = NULL; + } + + for (unsigned int i = 0; NULL != as->args[i]; i++) + GNUNET_free (as->args[i]); + GNUNET_free (as->args); + GNUNET_free (as); +} + + +/** + * Offer "system" CMD internal data to other commands. + * + * @param cls closure. + * @param[out] ret result. + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +system_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct SystemState *as = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_process (&as->system_proc), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_system_start ( + const char *label, + const char *config_file, + ...) +{ + struct SystemState *as; + va_list ap; + const char *arg; + unsigned int cnt; + + as = GNUNET_new (struct SystemState); + cnt = 4; /* 0-2 reserved, +1 for NULL termination */ + va_start (ap, + config_file); + while (NULL != (arg = va_arg (ap, + const char *))) + { + cnt++; + } + va_end (ap); + as->args = GNUNET_new_array (cnt, + char *); + as->args[0] = GNUNET_strdup ("taler-benchmark-setup"); + as->args[1] = GNUNET_strdup ("-c"); + as->args[2] = GNUNET_strdup (config_file); + cnt = 3; + va_start (ap, + config_file); + while (NULL != (arg = va_arg (ap, + const char *))) + { + as->args[cnt++] = GNUNET_strdup (arg); + } + va_end (ap); + + { + struct TALER_TESTING_Command cmd = { + .cls = as, + .label = label, + .run = &system_run, + .cleanup = &system_cleanup, + .traits = &system_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_system_start.c */