From 13dbe5418c1e61bb3c433efa7c2dc412e08b356d Mon Sep 17 00:00:00 2001 From: t3sserakt Date: Wed, 30 Jun 2021 13:04:40 +0200 Subject: - starting testbed with netjail --- src/include/gnunet_testbed_ng_service.h | 39 ++++ src/include/gnunet_testing_ng_lib.h | 2 +- src/testbed/Makefile.am | 12 + src/testbed/netjail_core.sh | 107 +++++++++ src/testbed/netjail_exec.sh | 19 ++ src/testbed/netjail_start.sh | 52 +++++ src/testbed/netjail_stop.sh | 26 +++ src/testbed/test_testbed_api_cmd_netjail.c | 94 ++++++++ src/testbed/testbed_api_cmd_netjail_start.c | 193 +++++++++++++++ .../testbed_api_cmd_netjail_start_testbed.c | 258 +++++++++++++++++++++ src/testbed/testbed_api_cmd_netjail_stop.c | 197 ++++++++++++++++ src/testing/testing_api_loop.c | 54 +++-- src/util/child_management.c | 42 ++-- 13 files changed, 1062 insertions(+), 33 deletions(-) create mode 100755 src/testbed/netjail_core.sh create mode 100755 src/testbed/netjail_exec.sh create mode 100755 src/testbed/netjail_start.sh create mode 100755 src/testbed/netjail_stop.sh create mode 100644 src/testbed/test_testbed_api_cmd_netjail.c create mode 100644 src/testbed/testbed_api_cmd_netjail_start.c create mode 100644 src/testbed/testbed_api_cmd_netjail_start_testbed.c create mode 100644 src/testbed/testbed_api_cmd_netjail_stop.c diff --git a/src/include/gnunet_testbed_ng_service.h b/src/include/gnunet_testbed_ng_service.h index 370617e68..b19a6e958 100644 --- a/src/include/gnunet_testbed_ng_service.h +++ b/src/include/gnunet_testbed_ng_service.h @@ -213,4 +213,43 @@ GNUNET_TESTBED_shutdown_peer (struct PeerCmdState *ps); void GNUNET_TESTBED_shutdown_service (struct TngState *ss); +/** + * Create command. + * + * @param label name for command. + * @param binaryname to start. + * @return command. + */ +struct GNUNET_TESTING_Command +GNUNET_TESTBED_cmd_netjail_start (const char *label, + char *local_m, + char *global_n); + +/** + * Create command. + * + * @param label name for command. + * @param binaryname to exec. + * @return command. + */ +struct GNUNET_TESTING_Command +GNUNET_TESTBED_cmd_netjail_start_testbed (const char *label, + char *const binary_argv[], + char *local_m, + char *global_n, + GNUNET_MessageTokenizerCallback cb, + GNUNET_HELPER_ExceptionCallback exp_cb); + +/** + * Create command. + * + * @param label name for command. + * @param binaryname to stop. + * @return command. + */ +struct GNUNET_TESTING_Command +GNUNET_TESTBED_cmd_netjail_stop (const char *label, + char *local_m, + char *global_n); + #endif diff --git a/src/include/gnunet_testing_ng_lib.h b/src/include/gnunet_testing_ng_lib.h index 55c8a7246..dd83bbe82 100644 --- a/src/include/gnunet_testing_ng_lib.h +++ b/src/include/gnunet_testing_ng_lib.h @@ -101,7 +101,7 @@ struct GNUNET_TESTING_Command * @param cont function to call upon completion, can be NULL * @param cont_cls closure for @a cont */ - bool + int (*finish)(void *cls, GNUNET_SCHEDULER_TaskCallback cont, void *cont_cls); diff --git a/src/testbed/Makefile.am b/src/testbed/Makefile.am index 5b81f7295..486c4a4f8 100644 --- a/src/testbed/Makefile.am +++ b/src/testbed/Makefile.am @@ -92,6 +92,9 @@ lib_LTLIBRARIES = \ libgnunettestbed.la libgnunettestbed_la_SOURCES = \ + testbed_api_cmd_netjail_start.c \ + testbed_api_cmd_netjail_start_testbed.c \ + testbed_api_cmd_netjail_stop.c \ testbed_api.c testbed_api.h testbed.h \ testbed_api_hosts.c testbed_api_hosts.h testbed_helper.h \ testbed_api_cmd_controller.c \ @@ -131,6 +134,7 @@ generate_underlay_topology_LDADD = $(XLIB) \ $(LTLIBINTL) -lsqlite3 check_PROGRAMS = \ + test_testbed_api_cmd_netjail \ test_testbed_api_hosts \ test_gnunet_helper_testbed \ test_testbed_api_controllerlink \ @@ -165,6 +169,7 @@ check_PROGRAMS = \ if ENABLE_TEST_RUN AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; TESTS = \ + test_testbed_api_cmd_netjail \ test_testbed_api \ test_testbed_api_sd \ test_testbed_api_operations \ @@ -195,6 +200,13 @@ if ENABLE_TEST_RUN $(underlay_testcases) endif +test_testbed_api_cmd_netjail_SOURCES = \ + test_testbed_api_cmd_netjail.c +test_testbed_api_cmd_netjail_LDADD = \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + test_testbed_api_SOURCES = \ test_testbed_api.c test_testbed_api_LDADD = \ diff --git a/src/testbed/netjail_core.sh b/src/testbed/netjail_core.sh new file mode 100755 index 000000000..d25948bc7 --- /dev/null +++ b/src/testbed/netjail_core.sh @@ -0,0 +1,107 @@ +#!/bin/sh +# + +JAILOR=${SUDO_USER:?must run in sudo} + +# running with `sudo` is required to be +# able running the actual commands as the +# original user. + +export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + +netjail_check() { + NODE_COUNT=$1 + + FD_COUNT=$(($(ls /proc/self/fd | wc -w) - 4)) + + # quit if `$FD_COUNT < ($LOCAL_M * $GLOBAL_N * 2)`: + # the script also requires `sudo -C ($FD_COUNT + 4)` + # so you need 'Defaults closefrom_override' in the + # sudoers file. + + if [ $FD_COUNT -lt $(($NODE_COUNT * 2)) ]; then + echo "File descriptors do not match requirements!" >&2 + exit 1 + fi +} + +netjail_print_name() { + printf "%s%02x%02x" $1 $2 ${3:-0} +} + +netjail_bridge() { + BRIDGE=$1 + + ip link add $BRIDGE type bridge + ip link set dev $BRIDGE up +} + +netjail_bridge_clear() { + BRIDGE=$1 + + ip link delete $BRIDGE +} + +netjail_node() { + NODE=$1 + + ip netns add $NODE +} + +netjail_node_clear() { + NODE=$1 + + ip netns delete $NODE +} + +netjail_node_link_bridge() { + NODE=$1 + BRIDGE=$2 + ADDRESS=$3 + MASK=$4 + + LINK_IF="$NODE-$BRIDGE-0" + LINK_BR="$NODE-$BRIDGE-1" + + ip link add $LINK_IF type veth peer name $LINK_BR + ip link set $LINK_IF netns $NODE + ip link set $LINK_BR master $BRIDGE + + ip -n $NODE addr add "$ADDRESS/$MASK" dev $LINK_IF + ip -n $NODE link set $LINK_IF up + ip -n $NODE link set up dev lo + + ip link set $LINK_BR up +} + +netjail_node_add_nat() { + NODE=$1 + ADDRESS=$2 + MASK=$3 + + ip netns exec $NODE iptables -t nat -A POSTROUTING -s "$ADDRESS/$MASK" -j MASQUERADE +} + +netjail_node_add_default() { + NODE=$1 + ADDRESS=$2 + + ip -n $NODE route add default via $ADDRESS +} + +netjail_node_exec() { + NODE=$1 + FD_IN=$2 + FD_OUT=$3 + shift 3 + + unshare -fp --kill-child -- ip netns exec $NODE sudo -u $JAILOR -- $@ 1>& $FD_OUT 0<& $FD_IN +} + +netjail_node_exec_without_fds() { + local NODE=$1 + shift 3 + + unshare -fp --kill-child -- ip netns exec $NODE sudo -u $JAILOR -- $@ +} + diff --git a/src/testbed/netjail_exec.sh b/src/testbed/netjail_exec.sh new file mode 100755 index 000000000..274a8b037 --- /dev/null +++ b/src/testbed/netjail_exec.sh @@ -0,0 +1,19 @@ +#!/bin/sh +. "./netjail_core.sh" + +set -eu +set -x + +export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + +LOCAL_M=$1 +M=$2 +N=$3 + +NODE=$(netjail_print_name "N" $N $M) +INDEX=$(($LOCAL_M * ($N - 1) + $M - 1)) + +FD_X=$(($INDEX * 2 + 3 + 0)) +FD_Y=$(($INDEX * 2 + 3 + 1)) + +netjail_node_exec_without_fds $NODE $@ diff --git a/src/testbed/netjail_start.sh b/src/testbed/netjail_start.sh new file mode 100755 index 000000000..a2abca2d7 --- /dev/null +++ b/src/testbed/netjail_start.sh @@ -0,0 +1,52 @@ +#!/bin/sh +. "./netjail_core.sh" + +set -eu +set -x + +export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + +LOCAL_M=$1 +GLOBAL_N=$2 + +# TODO: stunserver? ..and globally known peer? + +shift 2 + +LOCAL_GROUP="192.168.15" +GLOBAL_GROUP="92.68.150" + +NETWORK_NET=$(netjail_print_name "n" $GLOBAL_N $LOCAL_M) + +netjail_bridge $NETWORK_NET + +for N in $(seq $GLOBAL_N); do + ROUTER=$(netjail_print_name "R" $N) + + netjail_node $ROUTER + netjail_node_link_bridge $ROUTER $NETWORK_NET "$GLOBAL_GROUP.$N" 24 + + ROUTER_NET=$(netjail_print_name "r" $N) + + netjail_bridge $ROUTER_NET + + for M in $(seq $LOCAL_M); do + NODE=$(netjail_print_name "N" $N $M) + + netjail_node $NODE + netjail_node_link_bridge $NODE $ROUTER_NET "$LOCAL_GROUP.$M" 24 + done + + ROUTER_ADDR="$LOCAL_GROUP.$(($LOCAL_M+1))" + + netjail_node_link_bridge $ROUTER $ROUTER_NET $ROUTER_ADDR 24 + netjail_node_add_nat $ROUTER $ROUTER_ADDR 24 + + for M in $(seq $LOCAL_M); do + NODE=$(netjail_print_name "N" $N $M) + + netjail_node_add_default $NODE $ROUTER_ADDR + done +done + + diff --git a/src/testbed/netjail_stop.sh b/src/testbed/netjail_stop.sh new file mode 100755 index 000000000..4763cb107 --- /dev/null +++ b/src/testbed/netjail_stop.sh @@ -0,0 +1,26 @@ +#!/bin/sh +. "./netjail_core.sh" + +set -eu +set -x + +export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + +LOCAL_M=$1 +GLOBAL_N=$2 +NETWORK_NET=$(netjail_print_name "n" $GLOBAL_N $LOCAL_M) + +shift 2 + +for N in $(seq $GLOBAL_N); do + for M in $(seq $LOCAL_M); do + netjail_node_clear $(netjail_print_name "N" $N $M) + done + + netjail_bridge_clear $(netjail_print_name "r" $N) + netjail_node_clear $(netjail_print_name "R" $N) +done + +netjail_bridge_clear $NETWORK_NET + +echo "Done" diff --git a/src/testbed/test_testbed_api_cmd_netjail.c b/src/testbed/test_testbed_api_cmd_netjail.c new file mode 100644 index 000000000..d29ebdcb4 --- /dev/null +++ b/src/testbed/test_testbed_api_cmd_netjail.c @@ -0,0 +1,94 @@ +/* + This file is part of GNUnet + Copyright (C) 2021 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file testing/test_testing_api_cmd_netjail.c + * @brief Test case executing a script in a network name space. + * @author t3sserakt + */ +#include "platform.h" +#include "gnunet_testing_ng_lib.h" +#include "gnunet_testbed_ng_service.h" +#include "gnunet_util_lib.h" + +#define HELPER_TESTBED_BINARY "../testbed/gnunet-helper-testbed" + +static int +tokenizer_cb (void *cls, const struct GNUNET_MessageHeader *message) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Called tokenizer.\n"); + return GNUNET_OK; +} + + +static void +exp_cb (void *cls) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Called exp_cb.\n"); +} + + +/** + * Main function to run the test cases. + * + * @param cls not used. + * + */ +static void +run (void *cls) +{ + char *const binary_argv[3] = {HELPER_TESTBED_BINARY, NULL}; + + struct GNUNET_TESTING_Command commands[] = { + GNUNET_TESTBED_cmd_netjail_start ("netjail-start-1", + "1", + "2"), + GNUNET_TESTBED_cmd_netjail_start_testbed ("netjail-exec-1", + binary_argv, + "1", + "2", + &tokenizer_cb, + &exp_cb), + GNUNET_TESTBED_cmd_netjail_stop ("netjail-stop-1", + "1", + "2"), + GNUNET_TESTING_cmd_end () + }; + + GNUNET_TESTING_run (NULL, + commands, + GNUNET_TIME_UNIT_FOREVER_REL); +} + + +int +main (int argc, + char *const *argv) +{ + int rv = 0; + + GNUNET_log_setup ("test-netjail", + "DEBUG", + NULL); + GNUNET_SCHEDULER_run (&run, + NULL); + + return rv; +} diff --git a/src/testbed/testbed_api_cmd_netjail_start.c b/src/testbed/testbed_api_cmd_netjail_start.c new file mode 100644 index 000000000..320537a61 --- /dev/null +++ b/src/testbed/testbed_api_cmd_netjail_start.c @@ -0,0 +1,193 @@ +/* + This file is part of GNUnet + Copyright (C) 2021 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file testing/testing_api_cmd_hello_world.c + * @brief Command to start the netjail script. + * @author t3sserakt + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testing_ng_lib.h" +#include "gnunet_testbed_ng_service.h" + +#define NETJAIL_START_SCRIPT "./netjail_start.sh" + +struct NetJailState +{ + struct GNUNET_ChildWaitHandle *cwh; + + char *local_m; + + char *global_n; + + /** + * The process id of the start script. + */ + struct GNUNET_OS_Process *start_proc; + + unsigned int finished; +}; + + +/** +* +* +* @param cls closure +* @param cmd current CMD being cleaned up. +*/ +static void +netjail_start_cleanup (void *cls, + const struct GNUNET_TESTING_Command *cmd) +{ + struct NetJailState *ns = cls; + + if (NULL != ns->cwh) + { + GNUNET_wait_child_cancel (ns->cwh); + ns->cwh = NULL; + } + if (NULL != ns->start_proc) + { + GNUNET_assert (0 == + GNUNET_OS_process_kill (ns->start_proc, + SIGKILL)); + GNUNET_assert (GNUNET_OK == + GNUNET_OS_process_wait (ns->start_proc)); + GNUNET_OS_process_destroy (ns->start_proc); + ns->start_proc = NULL; + } +} + + +/** +* +* +* @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 int +netjail_start_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + return GNUNET_OK; +} + +static void +child_completed_callback (void *cls, + enum GNUNET_OS_ProcessStatusType type, + long unsigned int exit_code) +{ + struct NetJailState *ns = cls; + + if (0 == exit_code) + { + ns->finished = GNUNET_YES; + } + else + { + ns->finished = GNUNET_SYSERR; + } + GNUNET_OS_process_destroy (ns->start_proc); + ns->start_proc = NULL; +} + + + +/** +* Run the "hello world" CMD. +* +* @param cls closure. +* @param cmd CMD being run. +* @param is interpreter state. +*/ +static void +netjail_start_run (void *cls, + const struct GNUNET_TESTING_Command *cmd, + struct GNUNET_TESTING_Interpreter *is) +{ + struct NetJailState *ns = cls; + char *const script_argv[] = {NETJAIL_START_SCRIPT, + ns->local_m, + ns->global_n, + NULL}; + + ns->start_proc = GNUNET_OS_start_process_vap (GNUNET_OS_INHERIT_STD_ERR, + NULL, + NULL, + NULL, + NETJAIL_START_SCRIPT, + script_argv); + + ns->cwh = GNUNET_wait_child (ns->start_proc, + &child_completed_callback, + ns); + GNUNET_break (NULL != ns->cwh); +} + +static int +netjail_start_finish (void *cls, + GNUNET_SCHEDULER_TaskCallback cont, + void *cont_cls) +{ + struct NetJailState *ns = cls; + + if (ns->finished) + { + cont (cont_cls); + } + return ns->finished; +} + +/** + * Create command. + * + * @param label name for command. + * @param binaryname to start. + * @return command. + */ +struct GNUNET_TESTING_Command +GNUNET_TESTBED_cmd_netjail_start (const char *label, + char *local_m, + char *global_n) +{ + struct NetJailState *ns; + + ns = GNUNET_new (struct NetJailState); + ns->local_m = local_m; + ns->global_n = global_n; + ns->finished = GNUNET_NO; + + struct GNUNET_TESTING_Command cmd = { + .cls = ns, + .label = label, + .run = &netjail_start_run, + .finish = &netjail_start_finish, + .cleanup = &netjail_start_cleanup, + .traits = &netjail_start_traits + }; + + return cmd; +} diff --git a/src/testbed/testbed_api_cmd_netjail_start_testbed.c b/src/testbed/testbed_api_cmd_netjail_start_testbed.c new file mode 100644 index 000000000..da51350a1 --- /dev/null +++ b/src/testbed/testbed_api_cmd_netjail_start_testbed.c @@ -0,0 +1,258 @@ +/* + This file is part of GNUnet + Copyright (C) 2021 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file testing/testing_api_cmd_hello_world.c + * @brief Command to start the netjail peers. + * @author t3sserakt + */ +#include "platform.h" +#include "gnunet_testing_ng_lib.h" +#include "gnunet_testbed_ng_service.h" +#include "testbed_api.h" + +#define NETJAIL_EXEC_SCRIPT "./netjail_exec.sh" + +struct NetJailState +{ + + /** + * The process handle + */ + struct GNUNET_HELPER_Handle *helper; + + GNUNET_MessageTokenizerCallback cb; + + GNUNET_HELPER_ExceptionCallback exp_cb; + + char *binary_name; + + char *local_m; + + char *global_n; + + char **binary_argv; + + /** + * The send handle for the helper + */ + struct GNUNET_HELPER_SendHandle *shandle; + + /** + * The message corresponding to send handle + */ + struct GNUNET_MessageHeader *msg; +}; + + +/** +* +* +* @param cls closure +* @param cmd current CMD being cleaned up. +*/ +static void +netjail_exec_cleanup (void *cls, + const struct GNUNET_TESTING_Command *cmd) +{ + struct NetJailState *ns = cls; + + GNUNET_free (ns->binary_name); +} + + +/** +* +* +* @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 int +netjail_exec_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + return GNUNET_OK; +} + + +// TODO This would be a useful macro. +/** + * Function to join NULL terminated list of arguments + * + * @param argv1 the NULL terminated list of arguments. Cannot be NULL. + * @param argv2 the NULL terminated list of arguments. Cannot be NULL. + * @return the joined NULL terminated arguments + */ +static char ** +join_argv (const char *const *argv1, const char *const *argv2) +{ + char **argvj; + char *argv; + unsigned int carg = 0; + unsigned int cnt; + + carg = 0; + argvj = NULL; + for (cnt = 0; NULL != argv1[cnt]; cnt++) + { + argv = GNUNET_strdup (argv1[cnt]); + GNUNET_array_append (argvj, carg, argv); + } + for (cnt = 0; NULL != argv2[cnt]; cnt++) + { + argv = GNUNET_strdup (argv2[cnt]); + GNUNET_array_append (argvj, carg, argv); + } + GNUNET_array_append (argvj, carg, NULL); + return argvj; +} + + +/** + * Continuation function from GNUNET_HELPER_send() + * + * @param cls closure + * @param result GNUNET_OK on success, + * GNUNET_NO if helper process died + * GNUNET_SYSERR during GNUNET_HELPER_stop + */ +static void +clear_msg (void *cls, int result) +{ + struct NetJailState *ns = cls; + + GNUNET_assert (NULL != ns->shandle); + ns->shandle = NULL; + GNUNET_free (ns->msg); + ns->msg = NULL; +} + + +/** +* Run the "hello world" CMD. +* +* @param cls closure. +* @param cmd CMD being run. +* @param is interpreter state. +*/ +static void +netjail_exec_run (void *cls, + const struct GNUNET_TESTING_Command *cmd, + struct GNUNET_TESTING_Interpreter *is) +{ + struct NetJailState *ns = cls; + char **helper_argv; + struct GNUNET_TESTBED_HelperInit *msg; + struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create (); + char *const script_argv[] = {NETJAIL_EXEC_SCRIPT, + ns->local_m, + "1", + "1", + NULL}; + GNUNET_MessageTokenizerCallback cb = ns->cb; + GNUNET_HELPER_ExceptionCallback exp_cb = ns->exp_cb; + + if ((GNUNET_YES != GNUNET_DISK_file_test ("test_testbed_api.conf")) || + (GNUNET_SYSERR == GNUNET_CONFIGURATION_load (cfg, + "test_testbed_api.conf"))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ( + "Unreadable or malformed configuration file `%s', exit ...\n"), + "test_testbed_api.conf"); + } + + helper_argv = join_argv ((const char **) script_argv, + (const char **) ns->binary_argv); + + ns->helper = GNUNET_HELPER_start (GNUNET_YES, + NETJAIL_EXEC_SCRIPT, + helper_argv, + cb, + exp_cb, + ns); + + msg = GNUNET_TESTBED_create_helper_init_msg_ ("127.0.0.1", NULL, cfg); + ns->msg = &msg->header; + ns->shandle = GNUNET_HELPER_send (ns->helper, &msg->header, GNUNET_NO, + &clear_msg, ns); + if (NULL == ns->shandle) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Send handle is NULL!\n"); + GNUNET_free (msg); + } +} + + +/** + * Create command. + * + * @param label name for command. + * @param binaryname to exec. + * @return command. + */ +struct GNUNET_TESTING_Command +GNUNET_TESTBED_cmd_netjail_start_testbed (const char *label, + char *const binary_argv[], + char *local_m, + char *global_n, + GNUNET_MessageTokenizerCallback cb, + GNUNET_HELPER_ExceptionCallback exp_cb) +{ + struct NetJailState *ns; + unsigned int append_cnt; + char **argvj; + char *argv; + unsigned int carg = 0; + + ns = GNUNET_new (struct NetJailState); + argvj = NULL; + for (append_cnt = 0; NULL != binary_argv[append_cnt]; append_cnt++) + { + argv = GNUNET_strdup (binary_argv[append_cnt]); + GNUNET_array_append (argvj, + carg, + argv); + } + GNUNET_array_append (argvj, carg, NULL); + + ns->binary_argv = argvj; + + ns->local_m = local_m; + ns->global_n = global_n; + ns->cb = cb; + ns->exp_cb = exp_cb; + + struct GNUNET_TESTING_Command cmd = { + .cls = ns, + .label = label, + .run = &netjail_exec_run, + .cleanup = &netjail_exec_cleanup, + .traits = &netjail_exec_traits + }; + + return cmd; +} diff --git a/src/testbed/testbed_api_cmd_netjail_stop.c b/src/testbed/testbed_api_cmd_netjail_stop.c new file mode 100644 index 000000000..1e6586f94 --- /dev/null +++ b/src/testbed/testbed_api_cmd_netjail_stop.c @@ -0,0 +1,197 @@ +/* + This file is part of GNUnet + Copyright (C) 2021 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file testing/testing_api_cmd_hello_world.c + * @brief Command to stop the netjail script. + * @author t3sserakt + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testing_ng_lib.h" +#include "gnunet_testbed_ng_service.h" + + +#define NETJAIL_STOP_SCRIPT "./netjail_stop.sh" + +struct GNUNET_ChildWaitHandle *cwh; + +struct NetJailState +{ + char *local_m; + + char *global_n; + + /** + * The process id of the start script. + */ + struct GNUNET_OS_Process *stop_proc; + + unsigned int finished; +}; + + +/** +* +* +* @param cls closure +* @param cmd current CMD being cleaned up. +*/ +static void +netjail_stop_cleanup (void *cls, + const struct GNUNET_TESTING_Command *cmd) +{ + struct NetJailState *ns = cls; + + if (NULL != cwh) + { + GNUNET_wait_child_cancel (cwh); + cwh = NULL; + } + if (NULL != ns->stop_proc) + { + GNUNET_assert (0 == + GNUNET_OS_process_kill (ns->stop_proc, + SIGKILL)); + GNUNET_assert (GNUNET_OK == + GNUNET_OS_process_wait (ns->stop_proc)); + GNUNET_OS_process_destroy (ns->stop_proc); + ns->stop_proc = NULL; + } +} + + +/** +* +* +* @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 int +netjail_stop_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + return GNUNET_OK; +} + + +static void +child_completed_callback (void *cls, + enum GNUNET_OS_ProcessStatusType type, + long unsigned int exit_code) +{ + struct NetJailState *ns = cls; + + cwh = NULL; + if (0 == exit_code) + { + ns->finished = GNUNET_YES; + } + else + { + ns->finished = GNUNET_SYSERR; + } + GNUNET_OS_process_destroy (ns->stop_proc); + ns->stop_proc = NULL; +} + + +/** +* Run the "hello world" CMD. +* +* @param cls closure. +* @param cmd CMD being run. +* @param is interpreter state. +*/ +static void +netjail_stop_run (void *cls, + const struct GNUNET_TESTING_Command *cmd, + struct GNUNET_TESTING_Interpreter *is) +{ + struct NetJailState *ns = cls; + char *const script_argv[] = {NETJAIL_STOP_SCRIPT, + ns->local_m, + ns->global_n, + NULL}; + + ns->stop_proc = GNUNET_OS_start_process_vap (GNUNET_OS_INHERIT_STD_ERR, + NULL, + NULL, + NULL, + NETJAIL_STOP_SCRIPT, + script_argv); + + cwh = GNUNET_wait_child (ns->stop_proc, + &child_completed_callback, + ns); + GNUNET_break (NULL != cwh); + +} + + +static int +netjail_stop_finish (void *cls, + GNUNET_SCHEDULER_TaskCallback cont, + void *cont_cls) +{ + struct NetJailState *ns = cls; + + if (ns->finished) + { + cont (cont_cls); + } + return ns->finished; +} + + +/** + * Create command. + * + * @param label name for command. + * @param binaryname to stop. + * @return command. + */ +struct GNUNET_TESTING_Command +GNUNET_TESTBED_cmd_netjail_stop (const char *label, + char *local_m, + char *global_n) +{ + struct NetJailState *ns; + + ns = GNUNET_new (struct NetJailState); + ns->local_m = local_m; + ns->global_n = global_n; + + struct GNUNET_TESTING_Command cmd = { + .cls = ns, + .label = label, + .run = &netjail_stop_run, + .finish = &netjail_stop_finish, + .cleanup = &netjail_stop_cleanup, + .traits = &netjail_stop_traits + }; + + return cmd; +} diff --git a/src/testing/testing_api_loop.c b/src/testing/testing_api_loop.c index 1b50b9ff5..ccee76898 100644 --- a/src/testing/testing_api_loop.c +++ b/src/testing/testing_api_loop.c @@ -30,6 +30,9 @@ #include "gnunet_testing_ng_lib.h" #include "testing.h" +#define CHECK_FINISHED_PERIOD \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1) + struct GNUNET_TESTING_Interpreter *is; /** @@ -188,14 +191,20 @@ run_finish_task_next (void *cls) struct FinishTaskClosure *ftc = cls; const struct GNUNET_TESTING_Command *cmd = ftc->cmd; struct GNUNET_TESTING_Interpreter *is = ftc->is; + unsigned int finished = cmd->finish (cmd->cls, &interpreter_next, is); - if (cmd->finish (cmd->cls, &interpreter_next, is)) + if (GNUNET_YES == finished) { - is->finish_task = GNUNET_SCHEDULER_add_now (&run_finish_task_next, ftc); + is->finish_task = NULL; + } + else if (GNUNET_NO == finished) + { + is->finish_task = GNUNET_SCHEDULER_add_delayed (CHECK_FINISHED_PERIOD, + &run_finish_task_next, ftc); } else { - is->finish_task = NULL; + GNUNET_TESTING_interpreter_fail (is); } } @@ -210,6 +219,7 @@ run_finish_task_sync (void *cls) struct FinishTaskClosure *ftc; struct SyncState *sync_state = sync_cmd->cls; struct GNUNET_SCHEDULER_Task *finish_task = sync_state->finish_task; + unsigned int finished = cmd->finish (cmd->cls, &interpreter_next, is); GNUNET_assert (NULL != finish_task); ftc = GNUNET_new (struct FinishTaskClosure); @@ -222,17 +232,21 @@ run_finish_task_sync (void *cls) GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "The command with label %s did not finish its asynchronous task in time.\n", cmd->label); - is->result = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); + GNUNET_TESTING_interpreter_fail (is); } - if (cmd->finish (cmd->cls, run_finish_task_next, ftc)) + if (GNUNET_YES == finished) + { + finish_task = NULL; + } + else if (GNUNET_NO == finished) { - finish_task = GNUNET_SCHEDULER_add_now (&run_finish_task_sync, stc); + finish_task = GNUNET_SCHEDULER_add_delayed (CHECK_FINISHED_PERIOD, + &run_finish_task_sync, stc); } else { - finish_task = NULL; + GNUNET_TESTING_interpreter_fail (is); } } @@ -252,8 +266,10 @@ start_finish_on_ref (void *cls, stc->sync_cmd = cmd; stc->is = is; sync_state->start_finish_time = GNUNET_TIME_absolute_get (); - sync_state->finish_task = GNUNET_SCHEDULER_add_now (&run_finish_task_sync, - stc); + sync_state->finish_task = GNUNET_SCHEDULER_add_delayed ( + CHECK_FINISHED_PERIOD, + &run_finish_task_sync, + stc); } @@ -281,7 +297,7 @@ GNUNET_TESTING_cmd_finish (const char *finish_label, const struct GNUNET_TESTING_Command -GNUNET_TESTING_cmd_make_asynchronous (const struct GNUNET_TESTING_Command cmd) +GNUNET_TESTING_cmd_make_unblocking (const struct GNUNET_TESTING_Command cmd) { GNUNET_assert (NULL != cmd.finish); @@ -375,10 +391,12 @@ interpreter_run (void *cls) GNUNET_SCHEDULER_shutdown (); return; } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Running command `%s'\n", - cmd->label); + else if (NULL != cmd) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Running command `%s'\n", + cmd->label); + } cmd->start_time = cmd->last_req_time = GNUNET_TIME_absolute_get (); @@ -388,10 +406,14 @@ interpreter_run (void *cls) is); if ((NULL != cmd->finish) && (GNUNET_NO == cmd->asynchronous_finish)) { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Next task will not be called directly!\n"); ftc = GNUNET_new (struct FinishTaskClosure); ftc->cmd = cmd; ftc->is = is; - cmd->finish_task = GNUNET_SCHEDULER_add_now (run_finish_task_next, ftc); + cmd->finish_task = GNUNET_SCHEDULER_add_delayed (CHECK_FINISHED_PERIOD, + &run_finish_task_next, + ftc); } else { diff --git a/src/util/child_management.c b/src/util/child_management.c index 7edc33dc1..7775bfc3d 100644 --- a/src/util/child_management.c +++ b/src/util/child_management.c @@ -86,6 +86,10 @@ maint_child_death (void *cls) (void) cls; sig_task = NULL; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received SIGCHLD.\n"); + /* drain pipe */ pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ); @@ -150,17 +154,37 @@ sighandler_child_death (void) } -void __attribute__ ((constructor)) +// void __attribute__ ((constructor)) +static void child_management_start () { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Trying to start child management.\n"); if (NULL != sigpipe) return; /* already initialized */ sigpipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE); GNUNET_assert (sigpipe != NULL); shc_chld = GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Child management started.\n"); } +/** + * Clean up. + */ +// void __attribute__ ((destructor)) +static void +child_management_done () +{ + GNUNET_assert (NULL == sig_task); + GNUNET_SIGNAL_handler_uninstall (shc_chld); + shc_chld = NULL; + GNUNET_DISK_pipe_close (sigpipe); + sigpipe = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Child management stopped.\n"); +} struct GNUNET_ChildWaitHandle * GNUNET_wait_child (struct GNUNET_OS_Process *proc, @@ -189,7 +213,6 @@ GNUNET_wait_child (struct GNUNET_OS_Process *proc, return cwh; } - void GNUNET_wait_child_cancel (struct GNUNET_ChildWaitHandle *cwh) { @@ -198,22 +221,9 @@ GNUNET_wait_child_cancel (struct GNUNET_ChildWaitHandle *cwh) cwh); if (NULL == cwh_head) { + child_management_done (); GNUNET_SCHEDULER_cancel (sig_task); sig_task = NULL; } GNUNET_free (cwh); } - - -/** - * Clean up. - */ -void __attribute__ ((destructor)) -GNUNET_CM_done () -{ - GNUNET_assert (NULL == sig_task); - GNUNET_SIGNAL_handler_uninstall (shc_chld); - shc_chld = NULL; - GNUNET_DISK_pipe_close (sigpipe); - sigpipe = NULL; -} -- cgit v1.2.3