summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/include/gnunet_testbed_ng_service.h39
-rw-r--r--src/include/gnunet_testing_ng_lib.h2
-rw-r--r--src/testbed/Makefile.am12
-rwxr-xr-xsrc/testbed/netjail_core.sh107
-rwxr-xr-xsrc/testbed/netjail_exec.sh19
-rwxr-xr-xsrc/testbed/netjail_start.sh52
-rwxr-xr-xsrc/testbed/netjail_stop.sh26
-rw-r--r--src/testbed/test_testbed_api_cmd_netjail.c94
-rw-r--r--src/testbed/testbed_api_cmd_netjail_start.c193
-rw-r--r--src/testbed/testbed_api_cmd_netjail_start_testbed.c258
-rw-r--r--src/testbed/testbed_api_cmd_netjail_stop.c197
-rw-r--r--src/testing/testing_api_loop.c54
-rw-r--r--src/util/child_management.c42
13 files changed, 1062 insertions, 33 deletions
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 <http://www.gnu.org/licenses/>.
+
+ 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 <http://www.gnu.org/licenses/>.
+
+ 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 <http://www.gnu.org/licenses/>.
+
+ 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 <http://www.gnu.org/licenses/>.
+
+ 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;
-}