diff options
Diffstat (limited to 'src/testbed')
53 files changed, 21021 insertions, 630 deletions
diff --git a/src/testbed/Makefile.am b/src/testbed/Makefile.am index b7b9360..5977740 100644 --- a/src/testbed/Makefile.am +++ b/src/testbed/Makefile.am @@ -9,31 +9,289 @@ if USE_COVERAGE XLIB = -lgcov endif +if WITH_LL + CC = mpcc + ll_noinst_binaries = \ + ll-master \ + ll-monitor + ll_binaries = \ + gnunet-mpi-test +endif + +libexecdir= $(pkglibdir)/libexec/ + pkgcfgdir= $(pkgdatadir)/config.d/ -dist_pkgcfg_DATA = \ +pkgcfg_DATA = \ testbed.conf +libexec_PROGRAMS = \ + gnunet-service-testbed \ + gnunet-helper-testbed + +bin_PROGRAMS = \ + $(ll_binaries) + +noinst_PROGRAMS = \ + gnunet-testbed-profiler \ + $(ll_noinst_binaries) + +gnunet_service_testbed_SOURCES = \ + gnunet-service-testbed.c \ + gnunet-service-testbed.h \ + gnunet-service-testbed_cache.c \ + gnunet-service-testbed_oc.c +gnunet_service_testbed_LDADD = $(XLIB) \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/testbed/libgnunettestbed.la \ + $(LTLIBINTL) -lz +gnunet_service_testbed_DEPENDENCIES = \ + libgnunettestbed.la + +gnunet_testbed_profiler_SOURCES = \ + gnunet-testbed-profiler.c +gnunet_testbed_profiler_LDADD = $(XLIB) \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +gnunet_helper_testbed_SOURCES = \ + gnunet-helper-testbed.c +gnunet_helper_testbed_LDADD = $(XLIB) \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + libgnunettestbed.la \ + $(LTLIBINTL) -lz +gnunet_helper_testbed_DEPENDENCIES = \ + gnunet-service-testbed.$(OBJEXT) \ + libgnunettestbed.la + +ll_master_SOURCES = \ + ll_master.c +ll_master_LDADD = $(XLIB) \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(LTLIBINTL) -lz -lllapi + +ll_monitor_SOURCES = \ + ll_monitor.c +ll_monitor_LDADD = $(XLIB) \ + $(LTLIBINTL) -lz -lllapi + +gnunet_mpi_test_SOURCES = gnunet_mpi_test.c +gnunet_mpi_test_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + lib_LTLIBRARIES = \ libgnunettestbed.la libgnunettestbed_la_SOURCES = \ - testbed_api.c testbed.h \ - testbed_api_hosts.c testbed_api_hosts.h \ + testbed_api.c testbed_api.h testbed.h \ + testbed_api_hosts.c testbed_api_hosts.h testbed_helper.h \ testbed_api_operations.c testbed_api_operations.h \ testbed_api_peers.c testbed_api_peers.h \ testbed_api_services.c \ + testbed_api_statistics.c \ testbed_api_testbed.c \ testbed_api_test.c \ - testbed_api_topology.c + testbed_api_topology.c testbed_api_topology.h libgnunettestbed_la_LIBADD = $(XLIB) \ $(top_builddir)/src/core/libgnunetcore.la \ $(top_builddir)/src/statistics/libgnunetstatistics.la \ $(top_builddir)/src/transport/libgnunettransport.la \ $(top_builddir)/src/hello/libgnunethello.la \ -lm \ - $(top_builddir)/src/util/libgnunetutil.la + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(LTLIBINTL) libgnunettestbed_la_LDFLAGS = \ $(GN_LIB_LDFLAGS) \ -version-info 0:0:0 +check_PROGRAMS = \ + test_testbed_api_hosts \ + test_testbed_api_controllerlink \ + test_testbed_api_2peers_1controller \ + test_testbed_api_3peers_3controllers \ + test_testbed_api \ + test_testbed_api_operations \ + test_testbed_api_testbed_run \ + test_testbed_api_test \ + test_gnunet_helper_testbed \ + test_testbed_api_topology \ + test_testbed_api_topology_clique \ + test_testbed_api_testbed_run_topologyrandom \ + test_testbed_api_testbed_run_topologyline \ + test_testbed_api_testbed_run_topologyclique \ + test_testbed_api_testbed_run_topologyring \ + test_testbed_api_testbed_run_topologysmallworldring \ + test_testbed_api_testbed_run_topology2dtorus \ + test_testbed_api_testbed_run_topologysmallworld \ + test_testbed_api_testbed_run_topologyfromfile \ + test_testbed_api_testbed_run_topologyscalefree + +if ENABLE_TEST_RUN + TESTS = \ + test_testbed_api \ + test_testbed_api_hosts \ + test_testbed_api_2peers_1controller \ + test_testbed_api_3peers_3controllers \ + test_testbed_api_operations \ + test_gnunet_helper_testbed \ + test_testbed_api_controllerlink \ + test_testbed_api_testbed_run \ + test_testbed_api_test \ + test_testbed_api_topology \ + test_testbed_api_topology_clique \ + test_testbed_api_testbed_run_topologyrandom \ + test_testbed_api_testbed_run_topologyline \ + test_testbed_api_testbed_run_topologyclique \ + test_testbed_api_testbed_run_topologyring \ + test_testbed_api_testbed_run_topologysmallworldring \ + test_testbed_api_testbed_run_topology2dtorus \ + test_testbed_api_testbed_run_topologysmallworld \ + test_testbed_api_testbed_run_topologyfromfile \ + test_testbed_api_testbed_run_topologyscalefree +endif + +test_testbed_api_hosts_SOURCES = \ + test_testbed_api_hosts.c +test_testbed_api_hosts_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_SOURCES = \ + test_testbed_api.c +test_testbed_api_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/dht/libgnunetdht.la \ + libgnunettestbed.la + +test_testbed_api_2peers_1controller_SOURCES = \ + test_testbed_api_2peers_1controller.c +test_testbed_api_2peers_1controller_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + libgnunettestbed.la + +test_testbed_api_3peers_3controllers_SOURCES = \ + test_testbed_api_3peers_3controllers.c +test_testbed_api_3peers_3controllers_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + libgnunettestbed.la + +test_testbed_api_operations_SOURCES = \ + test_testbed_api_operations.c +test_testbed_api_operations_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_controllerlink_SOURCES = \ + test_testbed_api_controllerlink.c +test_testbed_api_controllerlink_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_testbed_run_SOURCES = \ + test_testbed_api_testbed_run.c +test_testbed_api_testbed_run_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_test_SOURCES = \ + test_testbed_api_test.c +test_testbed_api_test_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_topology_SOURCES = \ + test_testbed_api_topology.c +test_testbed_api_topology_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_topology_clique_SOURCES = \ + test_testbed_api_topology_clique.c +test_testbed_api_topology_clique_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_gnunet_helper_testbed_SOURCES = \ + test_gnunet_helper_testbed.c +test_gnunet_helper_testbed_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la \ + -lz + +test_testbed_api_testbed_run_topologyrandom_SOURCES = \ + test_testbed_api_testbed_run.c +test_testbed_api_testbed_run_topologyrandom_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_testbed_run_topologyline_SOURCES = \ + test_testbed_api_testbed_run.c +test_testbed_api_testbed_run_topologyline_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_testbed_run_topologyclique_SOURCES = \ + test_testbed_api_testbed_run.c +test_testbed_api_testbed_run_topologyclique_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_testbed_run_topologyring_SOURCES = \ + test_testbed_api_testbed_run.c +test_testbed_api_testbed_run_topologyring_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_testbed_run_topologysmallworldring_SOURCES = \ + test_testbed_api_testbed_run.c +test_testbed_api_testbed_run_topologysmallworldring_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_testbed_run_topology2dtorus_SOURCES = \ + test_testbed_api_testbed_run.c +test_testbed_api_testbed_run_topology2dtorus_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_testbed_run_topologysmallworld_SOURCES = \ + test_testbed_api_testbed_run.c +test_testbed_api_testbed_run_topologysmallworld_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_testbed_run_topologyfromfile_SOURCES = \ + test_testbed_api_testbed_run.c +test_testbed_api_testbed_run_topologyfromfile_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_testbed_run_topologyscalefree_SOURCES = \ + test_testbed_api_testbed_run.c +test_testbed_api_testbed_run_topologyscalefree_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +EXTRA_DIST = \ + test_testbed_api.conf \ + test_testbed_api_testbed_run_topologyring.conf \ + test_testbed_api_testbed_run_topologyclique.conf \ + test_testbed_api_testbed_run_topologyline.conf \ + test_testbed_api_testbed_run_topologyrandom.conf \ + test_testbed_api_testbed_run_topologysmallworldring.conf \ + test_testbed_api_testbed_run_topology2dtorus.conf \ + test_testbed_api_testbed_run_topologysmallworld.conf \ + test_testbed_api_testbed_run_topologyfromfile.conf \ + test_testbed_api_testbed_run_topologyscalefree.conf \ + overlay_topology.txt \ + sample_hosts.txt \ + sample.job diff --git a/src/testbed/Makefile.in b/src/testbed/Makefile.in index 3b08315..1b3c3e2 100644 --- a/src/testbed/Makefile.in +++ b/src/testbed/Makefile.in @@ -1,9 +1,9 @@ -# Makefile.in generated by automake 1.11.1 from Makefile.am. +# Makefile.in generated by automake 1.11.6 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, -# Inc. +# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -16,7 +16,25 @@ @SET_MAKE@ + VPATH = @srcdir@ +am__make_dryrun = \ + { \ + am__dry=no; \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \ + | grep '^AM OK$$' >/dev/null || am__dry=yes;; \ + *) \ + for am__flg in $$MAKEFLAGS; do \ + case $$am__flg in \ + *=*|--*) ;; \ + *n*) am__dry=yes; break;; \ + esac; \ + done;; \ + esac; \ + test $$am__dry = yes; \ + } pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ @@ -36,27 +54,71 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ +libexec_PROGRAMS = gnunet-service-testbed$(EXEEXT) \ + gnunet-helper-testbed$(EXEEXT) +bin_PROGRAMS = $(am__EXEEXT_1) +noinst_PROGRAMS = gnunet-testbed-profiler$(EXEEXT) $(am__EXEEXT_2) +check_PROGRAMS = test_testbed_api_hosts$(EXEEXT) \ + test_testbed_api_controllerlink$(EXEEXT) \ + test_testbed_api_2peers_1controller$(EXEEXT) \ + test_testbed_api_3peers_3controllers$(EXEEXT) \ + test_testbed_api$(EXEEXT) test_testbed_api_operations$(EXEEXT) \ + test_testbed_api_testbed_run$(EXEEXT) \ + test_testbed_api_test$(EXEEXT) \ + test_gnunet_helper_testbed$(EXEEXT) \ + test_testbed_api_topology$(EXEEXT) \ + test_testbed_api_topology_clique$(EXEEXT) \ + test_testbed_api_testbed_run_topologyrandom$(EXEEXT) \ + test_testbed_api_testbed_run_topologyline$(EXEEXT) \ + test_testbed_api_testbed_run_topologyclique$(EXEEXT) \ + test_testbed_api_testbed_run_topologyring$(EXEEXT) \ + test_testbed_api_testbed_run_topologysmallworldring$(EXEEXT) \ + test_testbed_api_testbed_run_topology2dtorus$(EXEEXT) \ + test_testbed_api_testbed_run_topologysmallworld$(EXEEXT) \ + test_testbed_api_testbed_run_topologyfromfile$(EXEEXT) \ + test_testbed_api_testbed_run_topologyscalefree$(EXEEXT) +@ENABLE_TEST_RUN_TRUE@TESTS = test_testbed_api$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_testbed_api_hosts$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_testbed_api_2peers_1controller$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_testbed_api_3peers_3controllers$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_testbed_api_operations$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_gnunet_helper_testbed$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_testbed_api_controllerlink$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_testbed_api_testbed_run$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_testbed_api_test$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_testbed_api_topology$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_testbed_api_topology_clique$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_testbed_api_testbed_run_topologyrandom$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_testbed_api_testbed_run_topologyline$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_testbed_api_testbed_run_topologyclique$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_testbed_api_testbed_run_topologyring$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_testbed_api_testbed_run_topologysmallworldring$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_testbed_api_testbed_run_topology2dtorus$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_testbed_api_testbed_run_topologysmallworld$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_testbed_api_testbed_run_topologyfromfile$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_testbed_api_testbed_run_topologyscalefree$(EXEEXT) subdir = src/testbed -DIST_COMMON = $(dist_pkgcfg_DATA) $(srcdir)/Makefile.am \ - $(srcdir)/Makefile.in +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(srcdir)/testbed.conf.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/absolute-header.m4 \ $(top_srcdir)/m4/align.m4 $(top_srcdir)/m4/argz.m4 \ - $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \ - $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ - $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libcurl.m4 \ - $(top_srcdir)/m4/libgcrypt.m4 $(top_srcdir)/m4/libtool.m4 \ - $(top_srcdir)/m4/libunistring.m4 $(top_srcdir)/m4/ltdl.m4 \ - $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ - $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ - $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \ + $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/glib-2.0.m4 \ + $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/lib-ld.m4 \ + $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ + $(top_srcdir)/m4/libcurl.m4 $(top_srcdir)/m4/libgcrypt.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libunistring.m4 \ + $(top_srcdir)/m4/ltdl.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/m4/po.m4 \ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/acinclude.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/gnunet_config.h -CONFIG_CLEAN_FILES = +CONFIG_CLEAN_FILES = testbed.conf CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ @@ -79,7 +141,14 @@ am__nobase_list = $(am__nobase_strip_setup); \ am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' -am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgcfgdir)" +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \ + "$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(pkgcfgdir)" LTLIBRARIES = $(lib_LTLIBRARIES) am__DEPENDENCIES_1 = libgnunettestbed_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ @@ -87,19 +156,168 @@ libgnunettestbed_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ $(top_builddir)/src/statistics/libgnunetstatistics.la \ $(top_builddir)/src/transport/libgnunettransport.la \ $(top_builddir)/src/hello/libgnunethello.la \ - $(top_builddir)/src/util/libgnunetutil.la + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(am__DEPENDENCIES_1) am_libgnunettestbed_la_OBJECTS = testbed_api.lo testbed_api_hosts.lo \ testbed_api_operations.lo testbed_api_peers.lo \ - testbed_api_services.lo testbed_api_testbed.lo \ - testbed_api_test.lo testbed_api_topology.lo + testbed_api_services.lo testbed_api_statistics.lo \ + testbed_api_testbed.lo testbed_api_test.lo \ + testbed_api_topology.lo libgnunettestbed_la_OBJECTS = $(am_libgnunettestbed_la_OBJECTS) -AM_V_lt = $(am__v_lt_$(V)) -am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent libgnunettestbed_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libgnunettestbed_la_LDFLAGS) \ $(LDFLAGS) -o $@ +@WITH_LL_TRUE@am__EXEEXT_1 = gnunet-mpi-test$(EXEEXT) +@WITH_LL_TRUE@am__EXEEXT_2 = ll-master$(EXEEXT) ll-monitor$(EXEEXT) +PROGRAMS = $(bin_PROGRAMS) $(libexec_PROGRAMS) $(noinst_PROGRAMS) +am_gnunet_helper_testbed_OBJECTS = gnunet-helper-testbed.$(OBJEXT) +gnunet_helper_testbed_OBJECTS = $(am_gnunet_helper_testbed_OBJECTS) +am_gnunet_mpi_test_OBJECTS = gnunet_mpi_test.$(OBJEXT) +gnunet_mpi_test_OBJECTS = $(am_gnunet_mpi_test_OBJECTS) +gnunet_mpi_test_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la +am_gnunet_service_testbed_OBJECTS = gnunet-service-testbed.$(OBJEXT) \ + gnunet-service-testbed_cache.$(OBJEXT) \ + gnunet-service-testbed_oc.$(OBJEXT) +gnunet_service_testbed_OBJECTS = $(am_gnunet_service_testbed_OBJECTS) +am_gnunet_testbed_profiler_OBJECTS = \ + gnunet-testbed-profiler.$(OBJEXT) +gnunet_testbed_profiler_OBJECTS = \ + $(am_gnunet_testbed_profiler_OBJECTS) +gnunet_testbed_profiler_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(top_builddir)/src/util/libgnunetutil.la libgnunettestbed.la +am_ll_master_OBJECTS = ll_master.$(OBJEXT) +ll_master_OBJECTS = $(am_ll_master_OBJECTS) +ll_master_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(am__DEPENDENCIES_1) +am_ll_monitor_OBJECTS = ll_monitor.$(OBJEXT) +ll_monitor_OBJECTS = $(am_ll_monitor_OBJECTS) +ll_monitor_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am_test_gnunet_helper_testbed_OBJECTS = \ + test_gnunet_helper_testbed.$(OBJEXT) +test_gnunet_helper_testbed_OBJECTS = \ + $(am_test_gnunet_helper_testbed_OBJECTS) +test_gnunet_helper_testbed_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la libgnunettestbed.la +am_test_testbed_api_OBJECTS = test_testbed_api.$(OBJEXT) +test_testbed_api_OBJECTS = $(am_test_testbed_api_OBJECTS) +test_testbed_api_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/dht/libgnunetdht.la libgnunettestbed.la +am_test_testbed_api_2peers_1controller_OBJECTS = \ + test_testbed_api_2peers_1controller.$(OBJEXT) +test_testbed_api_2peers_1controller_OBJECTS = \ + $(am_test_testbed_api_2peers_1controller_OBJECTS) +test_testbed_api_2peers_1controller_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + libgnunettestbed.la +am_test_testbed_api_3peers_3controllers_OBJECTS = \ + test_testbed_api_3peers_3controllers.$(OBJEXT) +test_testbed_api_3peers_3controllers_OBJECTS = \ + $(am_test_testbed_api_3peers_3controllers_OBJECTS) +test_testbed_api_3peers_3controllers_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + libgnunettestbed.la +am_test_testbed_api_controllerlink_OBJECTS = \ + test_testbed_api_controllerlink.$(OBJEXT) +test_testbed_api_controllerlink_OBJECTS = \ + $(am_test_testbed_api_controllerlink_OBJECTS) +test_testbed_api_controllerlink_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la libgnunettestbed.la +am_test_testbed_api_hosts_OBJECTS = test_testbed_api_hosts.$(OBJEXT) +test_testbed_api_hosts_OBJECTS = $(am_test_testbed_api_hosts_OBJECTS) +test_testbed_api_hosts_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la libgnunettestbed.la +am_test_testbed_api_operations_OBJECTS = \ + test_testbed_api_operations.$(OBJEXT) +test_testbed_api_operations_OBJECTS = \ + $(am_test_testbed_api_operations_OBJECTS) +test_testbed_api_operations_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la libgnunettestbed.la +am_test_testbed_api_test_OBJECTS = test_testbed_api_test.$(OBJEXT) +test_testbed_api_test_OBJECTS = $(am_test_testbed_api_test_OBJECTS) +test_testbed_api_test_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la libgnunettestbed.la +am_test_testbed_api_testbed_run_OBJECTS = \ + test_testbed_api_testbed_run.$(OBJEXT) +test_testbed_api_testbed_run_OBJECTS = \ + $(am_test_testbed_api_testbed_run_OBJECTS) +test_testbed_api_testbed_run_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la libgnunettestbed.la +am_test_testbed_api_testbed_run_topology2dtorus_OBJECTS = \ + test_testbed_api_testbed_run.$(OBJEXT) +test_testbed_api_testbed_run_topology2dtorus_OBJECTS = \ + $(am_test_testbed_api_testbed_run_topology2dtorus_OBJECTS) +test_testbed_api_testbed_run_topology2dtorus_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la libgnunettestbed.la +am_test_testbed_api_testbed_run_topologyclique_OBJECTS = \ + test_testbed_api_testbed_run.$(OBJEXT) +test_testbed_api_testbed_run_topologyclique_OBJECTS = \ + $(am_test_testbed_api_testbed_run_topologyclique_OBJECTS) +test_testbed_api_testbed_run_topologyclique_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la libgnunettestbed.la +am_test_testbed_api_testbed_run_topologyfromfile_OBJECTS = \ + test_testbed_api_testbed_run.$(OBJEXT) +test_testbed_api_testbed_run_topologyfromfile_OBJECTS = \ + $(am_test_testbed_api_testbed_run_topologyfromfile_OBJECTS) +test_testbed_api_testbed_run_topologyfromfile_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la libgnunettestbed.la +am_test_testbed_api_testbed_run_topologyline_OBJECTS = \ + test_testbed_api_testbed_run.$(OBJEXT) +test_testbed_api_testbed_run_topologyline_OBJECTS = \ + $(am_test_testbed_api_testbed_run_topologyline_OBJECTS) +test_testbed_api_testbed_run_topologyline_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la libgnunettestbed.la +am_test_testbed_api_testbed_run_topologyrandom_OBJECTS = \ + test_testbed_api_testbed_run.$(OBJEXT) +test_testbed_api_testbed_run_topologyrandom_OBJECTS = \ + $(am_test_testbed_api_testbed_run_topologyrandom_OBJECTS) +test_testbed_api_testbed_run_topologyrandom_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la libgnunettestbed.la +am_test_testbed_api_testbed_run_topologyring_OBJECTS = \ + test_testbed_api_testbed_run.$(OBJEXT) +test_testbed_api_testbed_run_topologyring_OBJECTS = \ + $(am_test_testbed_api_testbed_run_topologyring_OBJECTS) +test_testbed_api_testbed_run_topologyring_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la libgnunettestbed.la +am_test_testbed_api_testbed_run_topologyscalefree_OBJECTS = \ + test_testbed_api_testbed_run.$(OBJEXT) +test_testbed_api_testbed_run_topologyscalefree_OBJECTS = \ + $(am_test_testbed_api_testbed_run_topologyscalefree_OBJECTS) +test_testbed_api_testbed_run_topologyscalefree_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la libgnunettestbed.la +am_test_testbed_api_testbed_run_topologysmallworld_OBJECTS = \ + test_testbed_api_testbed_run.$(OBJEXT) +test_testbed_api_testbed_run_topologysmallworld_OBJECTS = \ + $(am_test_testbed_api_testbed_run_topologysmallworld_OBJECTS) +test_testbed_api_testbed_run_topologysmallworld_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la libgnunettestbed.la +am_test_testbed_api_testbed_run_topologysmallworldring_OBJECTS = \ + test_testbed_api_testbed_run.$(OBJEXT) +test_testbed_api_testbed_run_topologysmallworldring_OBJECTS = $(am_test_testbed_api_testbed_run_topologysmallworldring_OBJECTS) +test_testbed_api_testbed_run_topologysmallworldring_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la libgnunettestbed.la +am_test_testbed_api_topology_OBJECTS = \ + test_testbed_api_topology.$(OBJEXT) +test_testbed_api_topology_OBJECTS = \ + $(am_test_testbed_api_topology_OBJECTS) +test_testbed_api_topology_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la libgnunettestbed.la +am_test_testbed_api_topology_clique_OBJECTS = \ + test_testbed_api_topology_clique.$(OBJEXT) +test_testbed_api_topology_clique_OBJECTS = \ + $(am_test_testbed_api_topology_clique_OBJECTS) +test_testbed_api_topology_clique_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la libgnunettestbed.la DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles @@ -110,27 +328,80 @@ LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) -AM_V_CC = $(am__v_CC_$(V)) -am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY)) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; -AM_V_at = $(am__v_at_$(V)) -am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ -AM_V_CCLD = $(am__v_CCLD_$(V)) -am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY)) +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; -AM_V_GEN = $(am__v_GEN_$(V)) -am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; -SOURCES = $(libgnunettestbed_la_SOURCES) -DIST_SOURCES = $(libgnunettestbed_la_SOURCES) -DATA = $(dist_pkgcfg_DATA) +SOURCES = $(libgnunettestbed_la_SOURCES) \ + $(gnunet_helper_testbed_SOURCES) $(gnunet_mpi_test_SOURCES) \ + $(gnunet_service_testbed_SOURCES) \ + $(gnunet_testbed_profiler_SOURCES) $(ll_master_SOURCES) \ + $(ll_monitor_SOURCES) $(test_gnunet_helper_testbed_SOURCES) \ + $(test_testbed_api_SOURCES) \ + $(test_testbed_api_2peers_1controller_SOURCES) \ + $(test_testbed_api_3peers_3controllers_SOURCES) \ + $(test_testbed_api_controllerlink_SOURCES) \ + $(test_testbed_api_hosts_SOURCES) \ + $(test_testbed_api_operations_SOURCES) \ + $(test_testbed_api_test_SOURCES) \ + $(test_testbed_api_testbed_run_SOURCES) \ + $(test_testbed_api_testbed_run_topology2dtorus_SOURCES) \ + $(test_testbed_api_testbed_run_topologyclique_SOURCES) \ + $(test_testbed_api_testbed_run_topologyfromfile_SOURCES) \ + $(test_testbed_api_testbed_run_topologyline_SOURCES) \ + $(test_testbed_api_testbed_run_topologyrandom_SOURCES) \ + $(test_testbed_api_testbed_run_topologyring_SOURCES) \ + $(test_testbed_api_testbed_run_topologyscalefree_SOURCES) \ + $(test_testbed_api_testbed_run_topologysmallworld_SOURCES) \ + $(test_testbed_api_testbed_run_topologysmallworldring_SOURCES) \ + $(test_testbed_api_topology_SOURCES) \ + $(test_testbed_api_topology_clique_SOURCES) +DIST_SOURCES = $(libgnunettestbed_la_SOURCES) \ + $(gnunet_helper_testbed_SOURCES) $(gnunet_mpi_test_SOURCES) \ + $(gnunet_service_testbed_SOURCES) \ + $(gnunet_testbed_profiler_SOURCES) $(ll_master_SOURCES) \ + $(ll_monitor_SOURCES) $(test_gnunet_helper_testbed_SOURCES) \ + $(test_testbed_api_SOURCES) \ + $(test_testbed_api_2peers_1controller_SOURCES) \ + $(test_testbed_api_3peers_3controllers_SOURCES) \ + $(test_testbed_api_controllerlink_SOURCES) \ + $(test_testbed_api_hosts_SOURCES) \ + $(test_testbed_api_operations_SOURCES) \ + $(test_testbed_api_test_SOURCES) \ + $(test_testbed_api_testbed_run_SOURCES) \ + $(test_testbed_api_testbed_run_topology2dtorus_SOURCES) \ + $(test_testbed_api_testbed_run_topologyclique_SOURCES) \ + $(test_testbed_api_testbed_run_topologyfromfile_SOURCES) \ + $(test_testbed_api_testbed_run_topologyline_SOURCES) \ + $(test_testbed_api_testbed_run_topologyrandom_SOURCES) \ + $(test_testbed_api_testbed_run_topologyring_SOURCES) \ + $(test_testbed_api_testbed_run_topologyscalefree_SOURCES) \ + $(test_testbed_api_testbed_run_topologysmallworld_SOURCES) \ + $(test_testbed_api_testbed_run_topologysmallworldring_SOURCES) \ + $(test_testbed_api_topology_SOURCES) \ + $(test_testbed_api_topology_clique_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +DATA = $(pkgcfg_DATA) ETAGS = etags CTAGS = ctags +am__tty_colors = \ +red=; grn=; lgn=; blu=; std= DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ @@ -143,6 +414,7 @@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ +@WITH_LL_TRUE@CC = mpcc CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ @@ -167,6 +439,10 @@ EXEEXT = @EXEEXT@ EXT_LIBS = @EXT_LIBS@ EXT_LIB_PATH = @EXT_LIB_PATH@ FGREP = @FGREP@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_MKENUMS = @GLIB_MKENUMS@ GMSGFMT = @GMSGFMT@ GMSGFMT_015 = @GMSGFMT_015@ GNUNETDNS_GROUP = @GNUNETDNS_GROUP@ @@ -177,6 +453,7 @@ GN_LIBINTL = @GN_LIBINTL@ GN_LIB_LDFLAGS = @GN_LIB_LDFLAGS@ GN_PLUGIN_LDFLAGS = @GN_PLUGIN_LDFLAGS@ GN_USER_HOME_DIR = @GN_USER_HOME_DIR@ +GOBJECT_QUERY = @GOBJECT_QUERY@ GREP = @GREP@ HAVE_LIBUNISTRING = @HAVE_LIBUNISTRING@ INCLTDL = @INCLTDL@ @@ -199,6 +476,8 @@ LIBCURL_CPPFLAGS = @LIBCURL_CPPFLAGS@ LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBGTOP_CFLAGS = @LIBGTOP_CFLAGS@ +LIBGTOP_LIBS = @LIBGTOP_LIBS@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ LIBLTDL = @LIBLTDL@ @@ -220,6 +499,7 @@ LT_CONFIG_H = @LT_CONFIG_H@ LT_DLLOADERS = @LT_DLLOADERS@ LT_DLPREOPEN = @LT_DLPREOPEN@ MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MONKEYPREFIX = @MONKEYPREFIX@ MSGFMT = @MSGFMT@ @@ -229,6 +509,7 @@ MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@ MYSQL_LDFLAGS = @MYSQL_LDFLAGS@ NM = @NM@ NMEDIT = @NMEDIT@ +NSS_DIR = @NSS_DIR@ OBJC = @OBJC@ OBJCDEPMODE = @OBJCDEPMODE@ OBJCFLAGS = @OBJCFLAGS@ @@ -244,6 +525,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ POSTGRES_CPPFLAGS = @POSTGRES_CPPFLAGS@ POSTGRES_LDFLAGS = @POSTGRES_LDFLAGS@ POSUB = @POSUB@ @@ -275,6 +557,7 @@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ @@ -297,6 +580,7 @@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ +gitcommand = @gitcommand@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ @@ -307,10 +591,9 @@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ -libexecdir = @libexecdir@ +libexecdir = $(pkglibdir)/libexec/ localedir = @localedir@ localstatedir = @localstatedir@ -lt_ECHO = @lt_ECHO@ ltdl_LIBOBJS = @ltdl_LIBOBJS@ ltdl_LTLIBOBJS = @ltdl_LTLIBOBJS@ mandir = @mandir@ @@ -328,6 +611,7 @@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ subdirs = @subdirs@ +svnversioncommand = @svnversioncommand@ sys_symbol_underscore = @sys_symbol_underscore@ sysconfdir = @sysconfdir@ target = @target@ @@ -342,22 +626,85 @@ INCLUDES = -I$(top_srcdir)/src/include @MINGW_TRUE@WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols @USE_COVERAGE_TRUE@AM_CFLAGS = --coverage -O0 @USE_COVERAGE_TRUE@XLIB = -lgcov +@WITH_LL_TRUE@ll_noinst_binaries = \ +@WITH_LL_TRUE@ ll-master \ +@WITH_LL_TRUE@ ll-monitor + +@WITH_LL_TRUE@ll_binaries = \ +@WITH_LL_TRUE@ gnunet-mpi-test + pkgcfgdir = $(pkgdatadir)/config.d/ -dist_pkgcfg_DATA = \ +pkgcfg_DATA = \ testbed.conf +gnunet_service_testbed_SOURCES = \ + gnunet-service-testbed.c \ + gnunet-service-testbed.h \ + gnunet-service-testbed_cache.c \ + gnunet-service-testbed_oc.c + +gnunet_service_testbed_LDADD = $(XLIB) \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/testbed/libgnunettestbed.la \ + $(LTLIBINTL) -lz + +gnunet_service_testbed_DEPENDENCIES = \ + libgnunettestbed.la + +gnunet_testbed_profiler_SOURCES = \ + gnunet-testbed-profiler.c + +gnunet_testbed_profiler_LDADD = $(XLIB) \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +gnunet_helper_testbed_SOURCES = \ + gnunet-helper-testbed.c + +gnunet_helper_testbed_LDADD = $(XLIB) \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + libgnunettestbed.la \ + $(LTLIBINTL) -lz + +gnunet_helper_testbed_DEPENDENCIES = \ + gnunet-service-testbed.$(OBJEXT) \ + libgnunettestbed.la + +ll_master_SOURCES = \ + ll_master.c + +ll_master_LDADD = $(XLIB) \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(LTLIBINTL) -lz -lllapi + +ll_monitor_SOURCES = \ + ll_monitor.c + +ll_monitor_LDADD = $(XLIB) \ + $(LTLIBINTL) -lz -lllapi + +gnunet_mpi_test_SOURCES = gnunet_mpi_test.c +gnunet_mpi_test_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + lib_LTLIBRARIES = \ libgnunettestbed.la libgnunettestbed_la_SOURCES = \ - testbed_api.c testbed.h \ - testbed_api_hosts.c testbed_api_hosts.h \ + testbed_api.c testbed_api.h testbed.h \ + testbed_api_hosts.c testbed_api_hosts.h testbed_helper.h \ testbed_api_operations.c testbed_api_operations.h \ testbed_api_peers.c testbed_api_peers.h \ testbed_api_services.c \ + testbed_api_statistics.c \ testbed_api_testbed.c \ testbed_api_test.c \ - testbed_api_topology.c + testbed_api_topology.c testbed_api_topology.h libgnunettestbed_la_LIBADD = $(XLIB) \ $(top_builddir)/src/core/libgnunetcore.la \ @@ -365,12 +712,174 @@ libgnunettestbed_la_LIBADD = $(XLIB) \ $(top_builddir)/src/transport/libgnunettransport.la \ $(top_builddir)/src/hello/libgnunethello.la \ -lm \ - $(top_builddir)/src/util/libgnunetutil.la + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(LTLIBINTL) libgnunettestbed_la_LDFLAGS = \ $(GN_LIB_LDFLAGS) \ -version-info 0:0:0 +test_testbed_api_hosts_SOURCES = \ + test_testbed_api_hosts.c + +test_testbed_api_hosts_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_SOURCES = \ + test_testbed_api.c + +test_testbed_api_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/dht/libgnunetdht.la \ + libgnunettestbed.la + +test_testbed_api_2peers_1controller_SOURCES = \ + test_testbed_api_2peers_1controller.c + +test_testbed_api_2peers_1controller_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + libgnunettestbed.la + +test_testbed_api_3peers_3controllers_SOURCES = \ + test_testbed_api_3peers_3controllers.c + +test_testbed_api_3peers_3controllers_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + libgnunettestbed.la + +test_testbed_api_operations_SOURCES = \ + test_testbed_api_operations.c + +test_testbed_api_operations_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_controllerlink_SOURCES = \ + test_testbed_api_controllerlink.c + +test_testbed_api_controllerlink_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_testbed_run_SOURCES = \ + test_testbed_api_testbed_run.c + +test_testbed_api_testbed_run_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_test_SOURCES = \ + test_testbed_api_test.c + +test_testbed_api_test_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_topology_SOURCES = \ + test_testbed_api_topology.c + +test_testbed_api_topology_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_topology_clique_SOURCES = \ + test_testbed_api_topology_clique.c + +test_testbed_api_topology_clique_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_gnunet_helper_testbed_SOURCES = \ + test_gnunet_helper_testbed.c + +test_gnunet_helper_testbed_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la \ + -lz + +test_testbed_api_testbed_run_topologyrandom_SOURCES = \ + test_testbed_api_testbed_run.c + +test_testbed_api_testbed_run_topologyrandom_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_testbed_run_topologyline_SOURCES = \ + test_testbed_api_testbed_run.c + +test_testbed_api_testbed_run_topologyline_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_testbed_run_topologyclique_SOURCES = \ + test_testbed_api_testbed_run.c + +test_testbed_api_testbed_run_topologyclique_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_testbed_run_topologyring_SOURCES = \ + test_testbed_api_testbed_run.c + +test_testbed_api_testbed_run_topologyring_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_testbed_run_topologysmallworldring_SOURCES = \ + test_testbed_api_testbed_run.c + +test_testbed_api_testbed_run_topologysmallworldring_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_testbed_run_topology2dtorus_SOURCES = \ + test_testbed_api_testbed_run.c + +test_testbed_api_testbed_run_topology2dtorus_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_testbed_run_topologysmallworld_SOURCES = \ + test_testbed_api_testbed_run.c + +test_testbed_api_testbed_run_topologysmallworld_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_testbed_run_topologyfromfile_SOURCES = \ + test_testbed_api_testbed_run.c + +test_testbed_api_testbed_run_topologyfromfile_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +test_testbed_api_testbed_run_topologyscalefree_SOURCES = \ + test_testbed_api_testbed_run.c + +test_testbed_api_testbed_run_topologyscalefree_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + libgnunettestbed.la + +EXTRA_DIST = \ + test_testbed_api.conf \ + test_testbed_api_testbed_run_topologyring.conf \ + test_testbed_api_testbed_run_topologyclique.conf \ + test_testbed_api_testbed_run_topologyline.conf \ + test_testbed_api_testbed_run_topologyrandom.conf \ + test_testbed_api_testbed_run_topologysmallworldring.conf \ + test_testbed_api_testbed_run_topology2dtorus.conf \ + test_testbed_api_testbed_run_topologysmallworld.conf \ + test_testbed_api_testbed_run_topologyfromfile.conf \ + test_testbed_api_testbed_run_topologyscalefree.conf \ + overlay_topology.txt \ + sample_hosts.txt \ + sample.job + all: all-am .SUFFIXES: @@ -405,9 +914,10 @@ $(top_srcdir)/configure: $(am__configure_deps) $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): +testbed.conf: $(top_builddir)/config.status $(srcdir)/testbed.conf.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) - test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ @@ -415,6 +925,8 @@ install-libLTLIBRARIES: $(lib_LTLIBRARIES) else :; fi; \ done; \ test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } @@ -436,8 +948,196 @@ clean-libLTLIBRARIES: echo "rm -f \"$${dir}/so_locations\""; \ rm -f "$${dir}/so_locations"; \ done -libgnunettestbed.la: $(libgnunettestbed_la_OBJECTS) $(libgnunettestbed_la_DEPENDENCIES) +libgnunettestbed.la: $(libgnunettestbed_la_OBJECTS) $(libgnunettestbed_la_DEPENDENCIES) $(EXTRA_libgnunettestbed_la_DEPENDENCIES) $(AM_V_CCLD)$(libgnunettestbed_la_LINK) -rpath $(libdir) $(libgnunettestbed_la_OBJECTS) $(libgnunettestbed_la_LIBADD) $(LIBS) +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p || test -f $$p1; \ + then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +clean-checkPROGRAMS: + @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +install-libexecPROGRAMS: $(libexec_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p || test -f $$p1; \ + then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-libexecPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(libexecdir)" && rm -f $$files + +clean-libexecPROGRAMS: + @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +gnunet-helper-testbed$(EXEEXT): $(gnunet_helper_testbed_OBJECTS) $(gnunet_helper_testbed_DEPENDENCIES) $(EXTRA_gnunet_helper_testbed_DEPENDENCIES) + @rm -f gnunet-helper-testbed$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(gnunet_helper_testbed_OBJECTS) $(gnunet_helper_testbed_LDADD) $(LIBS) +gnunet-mpi-test$(EXEEXT): $(gnunet_mpi_test_OBJECTS) $(gnunet_mpi_test_DEPENDENCIES) $(EXTRA_gnunet_mpi_test_DEPENDENCIES) + @rm -f gnunet-mpi-test$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(gnunet_mpi_test_OBJECTS) $(gnunet_mpi_test_LDADD) $(LIBS) +gnunet-service-testbed$(EXEEXT): $(gnunet_service_testbed_OBJECTS) $(gnunet_service_testbed_DEPENDENCIES) $(EXTRA_gnunet_service_testbed_DEPENDENCIES) + @rm -f gnunet-service-testbed$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(gnunet_service_testbed_OBJECTS) $(gnunet_service_testbed_LDADD) $(LIBS) +gnunet-testbed-profiler$(EXEEXT): $(gnunet_testbed_profiler_OBJECTS) $(gnunet_testbed_profiler_DEPENDENCIES) $(EXTRA_gnunet_testbed_profiler_DEPENDENCIES) + @rm -f gnunet-testbed-profiler$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(gnunet_testbed_profiler_OBJECTS) $(gnunet_testbed_profiler_LDADD) $(LIBS) +ll-master$(EXEEXT): $(ll_master_OBJECTS) $(ll_master_DEPENDENCIES) $(EXTRA_ll_master_DEPENDENCIES) + @rm -f ll-master$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(ll_master_OBJECTS) $(ll_master_LDADD) $(LIBS) +ll-monitor$(EXEEXT): $(ll_monitor_OBJECTS) $(ll_monitor_DEPENDENCIES) $(EXTRA_ll_monitor_DEPENDENCIES) + @rm -f ll-monitor$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(ll_monitor_OBJECTS) $(ll_monitor_LDADD) $(LIBS) +test_gnunet_helper_testbed$(EXEEXT): $(test_gnunet_helper_testbed_OBJECTS) $(test_gnunet_helper_testbed_DEPENDENCIES) $(EXTRA_test_gnunet_helper_testbed_DEPENDENCIES) + @rm -f test_gnunet_helper_testbed$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_gnunet_helper_testbed_OBJECTS) $(test_gnunet_helper_testbed_LDADD) $(LIBS) +test_testbed_api$(EXEEXT): $(test_testbed_api_OBJECTS) $(test_testbed_api_DEPENDENCIES) $(EXTRA_test_testbed_api_DEPENDENCIES) + @rm -f test_testbed_api$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_testbed_api_OBJECTS) $(test_testbed_api_LDADD) $(LIBS) +test_testbed_api_2peers_1controller$(EXEEXT): $(test_testbed_api_2peers_1controller_OBJECTS) $(test_testbed_api_2peers_1controller_DEPENDENCIES) $(EXTRA_test_testbed_api_2peers_1controller_DEPENDENCIES) + @rm -f test_testbed_api_2peers_1controller$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_testbed_api_2peers_1controller_OBJECTS) $(test_testbed_api_2peers_1controller_LDADD) $(LIBS) +test_testbed_api_3peers_3controllers$(EXEEXT): $(test_testbed_api_3peers_3controllers_OBJECTS) $(test_testbed_api_3peers_3controllers_DEPENDENCIES) $(EXTRA_test_testbed_api_3peers_3controllers_DEPENDENCIES) + @rm -f test_testbed_api_3peers_3controllers$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_testbed_api_3peers_3controllers_OBJECTS) $(test_testbed_api_3peers_3controllers_LDADD) $(LIBS) +test_testbed_api_controllerlink$(EXEEXT): $(test_testbed_api_controllerlink_OBJECTS) $(test_testbed_api_controllerlink_DEPENDENCIES) $(EXTRA_test_testbed_api_controllerlink_DEPENDENCIES) + @rm -f test_testbed_api_controllerlink$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_testbed_api_controllerlink_OBJECTS) $(test_testbed_api_controllerlink_LDADD) $(LIBS) +test_testbed_api_hosts$(EXEEXT): $(test_testbed_api_hosts_OBJECTS) $(test_testbed_api_hosts_DEPENDENCIES) $(EXTRA_test_testbed_api_hosts_DEPENDENCIES) + @rm -f test_testbed_api_hosts$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_testbed_api_hosts_OBJECTS) $(test_testbed_api_hosts_LDADD) $(LIBS) +test_testbed_api_operations$(EXEEXT): $(test_testbed_api_operations_OBJECTS) $(test_testbed_api_operations_DEPENDENCIES) $(EXTRA_test_testbed_api_operations_DEPENDENCIES) + @rm -f test_testbed_api_operations$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_testbed_api_operations_OBJECTS) $(test_testbed_api_operations_LDADD) $(LIBS) +test_testbed_api_test$(EXEEXT): $(test_testbed_api_test_OBJECTS) $(test_testbed_api_test_DEPENDENCIES) $(EXTRA_test_testbed_api_test_DEPENDENCIES) + @rm -f test_testbed_api_test$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_testbed_api_test_OBJECTS) $(test_testbed_api_test_LDADD) $(LIBS) +test_testbed_api_testbed_run$(EXEEXT): $(test_testbed_api_testbed_run_OBJECTS) $(test_testbed_api_testbed_run_DEPENDENCIES) $(EXTRA_test_testbed_api_testbed_run_DEPENDENCIES) + @rm -f test_testbed_api_testbed_run$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_testbed_api_testbed_run_OBJECTS) $(test_testbed_api_testbed_run_LDADD) $(LIBS) +test_testbed_api_testbed_run_topology2dtorus$(EXEEXT): $(test_testbed_api_testbed_run_topology2dtorus_OBJECTS) $(test_testbed_api_testbed_run_topology2dtorus_DEPENDENCIES) $(EXTRA_test_testbed_api_testbed_run_topology2dtorus_DEPENDENCIES) + @rm -f test_testbed_api_testbed_run_topology2dtorus$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_testbed_api_testbed_run_topology2dtorus_OBJECTS) $(test_testbed_api_testbed_run_topology2dtorus_LDADD) $(LIBS) +test_testbed_api_testbed_run_topologyclique$(EXEEXT): $(test_testbed_api_testbed_run_topologyclique_OBJECTS) $(test_testbed_api_testbed_run_topologyclique_DEPENDENCIES) $(EXTRA_test_testbed_api_testbed_run_topologyclique_DEPENDENCIES) + @rm -f test_testbed_api_testbed_run_topologyclique$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_testbed_api_testbed_run_topologyclique_OBJECTS) $(test_testbed_api_testbed_run_topologyclique_LDADD) $(LIBS) +test_testbed_api_testbed_run_topologyfromfile$(EXEEXT): $(test_testbed_api_testbed_run_topologyfromfile_OBJECTS) $(test_testbed_api_testbed_run_topologyfromfile_DEPENDENCIES) $(EXTRA_test_testbed_api_testbed_run_topologyfromfile_DEPENDENCIES) + @rm -f test_testbed_api_testbed_run_topologyfromfile$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_testbed_api_testbed_run_topologyfromfile_OBJECTS) $(test_testbed_api_testbed_run_topologyfromfile_LDADD) $(LIBS) +test_testbed_api_testbed_run_topologyline$(EXEEXT): $(test_testbed_api_testbed_run_topologyline_OBJECTS) $(test_testbed_api_testbed_run_topologyline_DEPENDENCIES) $(EXTRA_test_testbed_api_testbed_run_topologyline_DEPENDENCIES) + @rm -f test_testbed_api_testbed_run_topologyline$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_testbed_api_testbed_run_topologyline_OBJECTS) $(test_testbed_api_testbed_run_topologyline_LDADD) $(LIBS) +test_testbed_api_testbed_run_topologyrandom$(EXEEXT): $(test_testbed_api_testbed_run_topologyrandom_OBJECTS) $(test_testbed_api_testbed_run_topologyrandom_DEPENDENCIES) $(EXTRA_test_testbed_api_testbed_run_topologyrandom_DEPENDENCIES) + @rm -f test_testbed_api_testbed_run_topologyrandom$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_testbed_api_testbed_run_topologyrandom_OBJECTS) $(test_testbed_api_testbed_run_topologyrandom_LDADD) $(LIBS) +test_testbed_api_testbed_run_topologyring$(EXEEXT): $(test_testbed_api_testbed_run_topologyring_OBJECTS) $(test_testbed_api_testbed_run_topologyring_DEPENDENCIES) $(EXTRA_test_testbed_api_testbed_run_topologyring_DEPENDENCIES) + @rm -f test_testbed_api_testbed_run_topologyring$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_testbed_api_testbed_run_topologyring_OBJECTS) $(test_testbed_api_testbed_run_topologyring_LDADD) $(LIBS) +test_testbed_api_testbed_run_topologyscalefree$(EXEEXT): $(test_testbed_api_testbed_run_topologyscalefree_OBJECTS) $(test_testbed_api_testbed_run_topologyscalefree_DEPENDENCIES) $(EXTRA_test_testbed_api_testbed_run_topologyscalefree_DEPENDENCIES) + @rm -f test_testbed_api_testbed_run_topologyscalefree$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_testbed_api_testbed_run_topologyscalefree_OBJECTS) $(test_testbed_api_testbed_run_topologyscalefree_LDADD) $(LIBS) +test_testbed_api_testbed_run_topologysmallworld$(EXEEXT): $(test_testbed_api_testbed_run_topologysmallworld_OBJECTS) $(test_testbed_api_testbed_run_topologysmallworld_DEPENDENCIES) $(EXTRA_test_testbed_api_testbed_run_topologysmallworld_DEPENDENCIES) + @rm -f test_testbed_api_testbed_run_topologysmallworld$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_testbed_api_testbed_run_topologysmallworld_OBJECTS) $(test_testbed_api_testbed_run_topologysmallworld_LDADD) $(LIBS) +test_testbed_api_testbed_run_topologysmallworldring$(EXEEXT): $(test_testbed_api_testbed_run_topologysmallworldring_OBJECTS) $(test_testbed_api_testbed_run_topologysmallworldring_DEPENDENCIES) $(EXTRA_test_testbed_api_testbed_run_topologysmallworldring_DEPENDENCIES) + @rm -f test_testbed_api_testbed_run_topologysmallworldring$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_testbed_api_testbed_run_topologysmallworldring_OBJECTS) $(test_testbed_api_testbed_run_topologysmallworldring_LDADD) $(LIBS) +test_testbed_api_topology$(EXEEXT): $(test_testbed_api_topology_OBJECTS) $(test_testbed_api_topology_DEPENDENCIES) $(EXTRA_test_testbed_api_topology_DEPENDENCIES) + @rm -f test_testbed_api_topology$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_testbed_api_topology_OBJECTS) $(test_testbed_api_topology_LDADD) $(LIBS) +test_testbed_api_topology_clique$(EXEEXT): $(test_testbed_api_topology_clique_OBJECTS) $(test_testbed_api_topology_clique_DEPENDENCIES) $(EXTRA_test_testbed_api_topology_clique_DEPENDENCIES) + @rm -f test_testbed_api_topology_clique$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_testbed_api_topology_clique_OBJECTS) $(test_testbed_api_topology_clique_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -445,11 +1145,31 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-helper-testbed.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-service-testbed.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-service-testbed_cache.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-service-testbed_oc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-testbed-profiler.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet_mpi_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ll_master.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ll_monitor.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_gnunet_helper_testbed.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_testbed_api.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_testbed_api_2peers_1controller.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_testbed_api_3peers_3controllers.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_testbed_api_controllerlink.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_testbed_api_hosts.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_testbed_api_operations.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_testbed_api_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_testbed_api_testbed_run.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_testbed_api_topology.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_testbed_api_topology_clique.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testbed_api.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testbed_api_hosts.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testbed_api_operations.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testbed_api_peers.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testbed_api_services.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testbed_api_statistics.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testbed_api_test.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testbed_api_testbed.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testbed_api_topology.Plo@am__quote@ @@ -457,36 +1177,36 @@ distclean-compile: .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po -@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(COMPILE) -c $< +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po -@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo -@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs -install-dist_pkgcfgDATA: $(dist_pkgcfg_DATA) +install-pkgcfgDATA: $(pkgcfg_DATA) @$(NORMAL_INSTALL) - test -z "$(pkgcfgdir)" || $(MKDIR_P) "$(DESTDIR)$(pkgcfgdir)" - @list='$(dist_pkgcfg_DATA)'; test -n "$(pkgcfgdir)" || list=; \ + @list='$(pkgcfg_DATA)'; test -n "$(pkgcfgdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgcfgdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgcfgdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -496,13 +1216,11 @@ install-dist_pkgcfgDATA: $(dist_pkgcfg_DATA) $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgcfgdir)" || exit $$?; \ done -uninstall-dist_pkgcfgDATA: +uninstall-pkgcfgDATA: @$(NORMAL_UNINSTALL) - @list='$(dist_pkgcfg_DATA)'; test -n "$(pkgcfgdir)" || list=; \ + @list='$(pkgcfg_DATA)'; test -n "$(pkgcfgdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ - test -n "$$files" || exit 0; \ - echo " ( cd '$(DESTDIR)$(pkgcfgdir)' && rm -f" $$files ")"; \ - cd "$(DESTDIR)$(pkgcfgdir)" && rm -f $$files + dir='$(DESTDIR)$(pkgcfgdir)'; $(am__uninstall_files_from_dir) ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ @@ -556,6 +1274,99 @@ GTAGS: distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + $(am__tty_colors); \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + col=$$red; res=XPASS; \ + ;; \ + *) \ + col=$$grn; res=PASS; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xfail=`expr $$xfail + 1`; \ + col=$$lgn; res=XFAIL; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + col=$$red; res=FAIL; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + col=$$blu; res=SKIP; \ + fi; \ + echo "$${col}$$res$${std}: $$tst"; \ + done; \ + if test "$$all" -eq 1; then \ + tests="test"; \ + All=""; \ + else \ + tests="tests"; \ + All="All "; \ + fi; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="$$All$$all $$tests passed"; \ + else \ + if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ + banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all $$tests failed"; \ + else \ + if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ + banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + if test "$$skip" -eq 1; then \ + skipped="($$skip test was not run)"; \ + else \ + skipped="($$skip tests were not run)"; \ + fi; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + if test "$$failed" -eq 0; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + fi; \ + echo "$${col}$$dashes$${std}"; \ + echo "$${col}$$banner$${std}"; \ + test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ + test -z "$$report" || echo "$${col}$$report$${std}"; \ + echo "$${col}$$dashes$${std}"; \ + test "$$failed" -eq 0; \ + else :; fi + distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ @@ -587,10 +1398,14 @@ distdir: $(DISTFILES) fi; \ done check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS check: check-am -all-am: Makefile $(LTLIBRARIES) $(DATA) +all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(DATA) +install-binPROGRAMS: install-libLTLIBRARIES + installdirs: - for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgcfgdir)"; do \ + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(pkgcfgdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am @@ -603,10 +1418,15 @@ install-am: all-am installcheck: installcheck-am install-strip: - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - `test -z '$(STRIP)' || \ - echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi mostlyclean-generic: clean-generic: @@ -620,8 +1440,9 @@ maintainer-clean-generic: @echo "it deletes files that may require special tools to rebuild." clean: clean-am -clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ - mostlyclean-am +clean-am: clean-binPROGRAMS clean-checkPROGRAMS clean-generic \ + clean-libLTLIBRARIES clean-libexecPROGRAMS clean-libtool \ + clean-noinstPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) @@ -641,13 +1462,14 @@ info: info-am info-am: -install-data-am: install-dist_pkgcfgDATA +install-data-am: install-pkgcfgDATA install-dvi: install-dvi-am install-dvi-am: -install-exec-am: install-libLTLIBRARIES +install-exec-am: install-binPROGRAMS install-libLTLIBRARIES \ + install-libexecPROGRAMS install-html: install-html-am @@ -687,25 +1509,28 @@ ps: ps-am ps-am: -uninstall-am: uninstall-dist_pkgcfgDATA uninstall-libLTLIBRARIES - -.MAKE: install-am install-strip - -.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ - clean-libLTLIBRARIES clean-libtool ctags distclean \ - distclean-compile distclean-generic distclean-libtool \ - distclean-tags distdir dvi dvi-am html html-am info info-am \ - install install-am install-data install-data-am \ - install-dist_pkgcfgDATA install-dvi install-dvi-am \ - install-exec install-exec-am install-html install-html-am \ - install-info install-info-am install-libLTLIBRARIES \ - install-man install-pdf install-pdf-am install-ps \ +uninstall-am: uninstall-binPROGRAMS uninstall-libLTLIBRARIES \ + uninstall-libexecPROGRAMS uninstall-pkgcfgDATA + +.MAKE: check-am install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-TESTS check-am clean \ + clean-binPROGRAMS clean-checkPROGRAMS clean-generic \ + clean-libLTLIBRARIES clean-libexecPROGRAMS clean-libtool \ + clean-noinstPROGRAMS ctags distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-binPROGRAMS install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am \ + install-libLTLIBRARIES install-libexecPROGRAMS install-man \ + install-pdf install-pdf-am install-pkgcfgDATA install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags uninstall \ - uninstall-am uninstall-dist_pkgcfgDATA \ - uninstall-libLTLIBRARIES + uninstall-am uninstall-binPROGRAMS uninstall-libLTLIBRARIES \ + uninstall-libexecPROGRAMS uninstall-pkgcfgDATA # Tell versions [3.59,3.63) of GNU make to not export all variables. diff --git a/src/testbed/gnunet-helper-testbed.c b/src/testbed/gnunet-helper-testbed.c new file mode 100644 index 0000000..7d0bf87 --- /dev/null +++ b/src/testbed/gnunet-helper-testbed.c @@ -0,0 +1,489 @@ +/* + This file is part of GNUnet + (C) 2012 Christian Grothoff (and other contributing authors) + + GNUnet 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. + + 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file testbed/gnunet-helper-testbed.c + * @brief Helper binary that is started from a remote controller to start + * gnunet-service-testbed. This binary also receives configuration + * from the remove controller which is put in a temporary location + * with ports and paths fixed so that gnunet-service-testbed runs + * without any hurdles. This binary also kills the testbed service + * should the connection from the remote controller is dropped + * @author Sree Harsha Totakura <sreeharsha@totakura.in> + */ + + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testing_lib.h" +#include "gnunet_testbed_service.h" +#include "testbed_helper.h" +#include "testbed_api.h" +#include <zlib.h> + +/** + * Generic logging shortcut + */ +#define LOG(kind, ...) \ + GNUNET_log (kind, __VA_ARGS__) + +/** + * Debug logging shorthand + */ +#define LOG_DEBUG(...) \ + LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__) + + +/** + * We need pipe control only on WINDOWS + */ +#if WINDOWS +#define PIPE_CONTROL GNUNET_YES +#else +#define PIPE_CONTROL GNUNET_NO +#endif + + +/** + * Context for a single write on a chunk of memory + */ +struct WriteContext +{ + /** + * The data to write + */ + void *data; + + /** + * The length of the data + */ + size_t length; + + /** + * The current position from where the write operation should begin + */ + size_t pos; +}; + + +/** + * Handle to the testing system + */ +static struct GNUNET_TESTING_System *test_system; + +/** + * Our message stream tokenizer + */ +struct GNUNET_SERVER_MessageStreamTokenizer *tokenizer; + +/** + * Disk handle from stdin + */ +static struct GNUNET_DISK_FileHandle *stdin_fd; + +/** + * Disk handle for stdout + */ +static struct GNUNET_DISK_FileHandle *stdout_fd; + +/** + * The process handle to the testbed service + */ +static struct GNUNET_OS_Process *testbed; + +/** + * Task identifier for the read task + */ +static GNUNET_SCHEDULER_TaskIdentifier read_task_id; + +/** + * Task identifier for the write task + */ +static GNUNET_SCHEDULER_TaskIdentifier write_task_id; + +/** + * Are we done reading messages from stdin? + */ +static int done_reading; + +/** + * Result to return in case we fail + */ +static int status; + + +/** + * Are we shutting down + */ +static int in_shutdown; + + +/** + * Task to shutting down nicely + * + * @param cls NULL + * @param tc the task context + */ +static void +shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + LOG_DEBUG ("Shutting down\n"); + in_shutdown = GNUNET_YES; + if (GNUNET_SCHEDULER_NO_TASK != read_task_id) + { + GNUNET_SCHEDULER_cancel (read_task_id); + read_task_id = GNUNET_SCHEDULER_NO_TASK; + } + if (GNUNET_SCHEDULER_NO_TASK != write_task_id) + { + GNUNET_SCHEDULER_cancel (write_task_id); + write_task_id = GNUNET_SCHEDULER_NO_TASK; + } + if (NULL != stdin_fd) + (void) GNUNET_DISK_file_close (stdin_fd); + if (NULL != stdout_fd) + (void) GNUNET_DISK_file_close (stdout_fd); + GNUNET_SERVER_mst_destroy (tokenizer); + tokenizer = NULL; + if (NULL != testbed) + { + LOG_DEBUG ("Killing testbed\n"); + GNUNET_break (0 == GNUNET_OS_process_kill (testbed, SIGTERM)); + GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (testbed)); + GNUNET_OS_process_destroy (testbed); + testbed = NULL; + } + if (NULL != test_system) + { + GNUNET_TESTING_system_destroy (test_system, GNUNET_YES); + test_system = NULL; + } +} + + +/** + * Task to write to the standard out + * + * @param cls the WriteContext + * @param tc the TaskContext + */ +static void +write_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct WriteContext *wc = cls; + ssize_t bytes_wrote; + + GNUNET_assert (NULL != wc); + write_task_id = GNUNET_SCHEDULER_NO_TASK; + if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason)) + { + GNUNET_free (wc->data); + GNUNET_free (wc); + return; + } + bytes_wrote = + GNUNET_DISK_file_write (stdout_fd, wc->data + wc->pos, + wc->length - wc->pos); + if (GNUNET_SYSERR == bytes_wrote) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Cannot reply back configuration\n"); + GNUNET_free (wc->data); + GNUNET_free (wc); + return; + } + wc->pos += bytes_wrote; + if (wc->pos == wc->length) + { + GNUNET_free (wc->data); + GNUNET_free (wc); + return; + } + write_task_id = + GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL, stdout_fd, + &write_task, wc); +} + + +/** + * Functions with this signature are called whenever a + * complete message is received by the tokenizer. + * + * Do not call GNUNET_SERVER_mst_destroy in callback + * + * @param cls closure + * @param client identification of the client + * @param message the actual message + * + * @return GNUNET_OK on success, GNUNET_SYSERR to stop further processing + */ +static int +tokenizer_cb (void *cls, void *client, + const struct GNUNET_MessageHeader *message) +{ + const struct GNUNET_TESTBED_HelperInit *msg; + struct GNUNET_TESTBED_HelperReply *reply; + struct GNUNET_CONFIGURATION_Handle *cfg; + struct WriteContext *wc; + char *binary; + char *trusted_ip; + char *hostname; + char *config; + char *xconfig; + size_t config_size; + uLongf ul_config_size; + size_t xconfig_size; + uint16_t trusted_ip_size; + uint16_t hostname_size; + uint16_t msize; + + msize = ntohs (message->size); + if ((sizeof (struct GNUNET_TESTBED_HelperInit) >= msize) || + (GNUNET_MESSAGE_TYPE_TESTBED_HELPER_INIT != ntohs (message->type))) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Received unexpected message -- exiting\n"); + goto error; + } + msg = (const struct GNUNET_TESTBED_HelperInit *) message; + trusted_ip_size = ntohs (msg->trusted_ip_size); + trusted_ip = (char *) &msg[1]; + if ('\0' != trusted_ip[trusted_ip_size]) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Trusted IP cannot be empty -- exiting\n"); + goto error; + } + hostname_size = ntohs (msg->hostname_size); + if ((sizeof (struct GNUNET_TESTBED_HelperInit) + trusted_ip_size + 1 + + hostname_size) >= msize) + { + GNUNET_break (0); + LOG (GNUNET_ERROR_TYPE_WARNING, "Received unexpected message -- exiting\n"); + goto error; + } + ul_config_size = (uLongf) ntohs (msg->config_size); + config = GNUNET_malloc (ul_config_size); + xconfig_size = + ntohs (message->size) - (trusted_ip_size + 1 + + sizeof (struct GNUNET_TESTBED_HelperInit)); + if (Z_OK != + uncompress ((Bytef *) config, &ul_config_size, + (const Bytef *) (trusted_ip + trusted_ip_size + 1 + + hostname_size), (uLongf) xconfig_size)) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + "Error while uncompressing config -- exiting\n"); + GNUNET_free (config); + goto error; + } + cfg = GNUNET_CONFIGURATION_create (); + if (GNUNET_OK != + GNUNET_CONFIGURATION_deserialize (cfg, config, ul_config_size, GNUNET_NO)) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + "Unable to deserialize config -- exiting\n"); + GNUNET_free (config); + goto error; + } + GNUNET_free (config); + hostname = NULL; + if (0 != hostname_size) + { + hostname = GNUNET_malloc (hostname_size + 1); + (void) strncpy (hostname, ((char *) &msg[1]) + trusted_ip_size + 1, + hostname_size); + hostname[hostname_size] = '\0'; + } + test_system = + GNUNET_TESTING_system_create ("testbed-helper", trusted_ip, hostname); + GNUNET_free_non_null (hostname); + hostname = NULL; + GNUNET_assert (NULL != test_system); + GNUNET_assert (GNUNET_OK == + GNUNET_TESTING_configuration_create (test_system, cfg)); + GNUNET_assert (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", + "DEFAULTCONFIG", + &config)); + if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, config)) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + "Unable to write config file: %s -- exiting\n", config); + GNUNET_CONFIGURATION_destroy (cfg); + GNUNET_free (config); + goto error; + } + LOG_DEBUG ("Staring testbed with config: %s\n", config); + binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-testbed"); + testbed = + GNUNET_OS_start_process (PIPE_CONTROL, + GNUNET_OS_INHERIT_STD_ERR /*verbose? */ , NULL, + NULL, binary, "gnunet-service-testbed", "-c", + config, NULL); + GNUNET_free (binary); + GNUNET_free (config); + if (NULL == testbed) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + "Error starting gnunet-service-testbed -- exiting\n"); + GNUNET_CONFIGURATION_destroy (cfg); + goto error; + } + done_reading = GNUNET_YES; + config = GNUNET_CONFIGURATION_serialize (cfg, &config_size); + GNUNET_CONFIGURATION_destroy (cfg); + cfg = NULL; + xconfig_size = + GNUNET_TESTBED_compress_config_ (config, config_size, &xconfig); + GNUNET_free (config); + wc = GNUNET_malloc (sizeof (struct WriteContext)); + wc->length = xconfig_size + sizeof (struct GNUNET_TESTBED_HelperReply); + reply = GNUNET_realloc (xconfig, wc->length); + memmove (&reply[1], reply, xconfig_size); + reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_HELPER_REPLY); + reply->header.size = htons ((uint16_t) wc->length); + reply->config_size = htons ((uint16_t) config_size); + wc->data = reply; + write_task_id = + GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL, stdout_fd, + &write_task, wc); + return GNUNET_OK; + +error: + status = GNUNET_SYSERR; + GNUNET_SCHEDULER_shutdown (); + return GNUNET_SYSERR; +} + + +/** + * Task to read from stdin + * + * @param cls NULL + * @param tc the task context + */ +static void +read_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE]; + ssize_t sread; + + read_task_id = GNUNET_SCHEDULER_NO_TASK; + if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason)) + return; + sread = GNUNET_DISK_file_read (stdin_fd, buf, sizeof (buf)); + if ((GNUNET_SYSERR == sread) || (0 == sread)) + { + GNUNET_SCHEDULER_shutdown (); + return; + } + if (GNUNET_YES == done_reading) + { + /* didn't expect any more data! */ + GNUNET_SCHEDULER_shutdown (); + return; + } + LOG_DEBUG ("Read %u bytes\n", sread); + if (GNUNET_OK != + GNUNET_SERVER_mst_receive (tokenizer, NULL, buf, sread, GNUNET_NO, + GNUNET_NO)) + { + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + read_task_id = /* No timeout while reading */ + GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, stdin_fd, + &read_task, NULL); +} + + +/** + * Main function that will be run. + * + * @param cls closure + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param cfg configuration + */ +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + LOG_DEBUG ("Starting testbed helper...\n"); + tokenizer = GNUNET_SERVER_mst_create (&tokenizer_cb, NULL); + stdin_fd = GNUNET_DISK_get_handle_from_native (stdin); + stdout_fd = GNUNET_DISK_get_handle_from_native (stdout); + read_task_id = + GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, stdin_fd, + &read_task, NULL); + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task, + NULL); +} + + +/** + * Signal handler called for SIGCHLD. + */ +static void +sighandler_child_death () +{ + if ((NULL != testbed) && (GNUNET_NO == in_shutdown)) + { + LOG_DEBUG ("Child died\n"); + GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (testbed)); + GNUNET_OS_process_destroy (testbed); + testbed = NULL; + GNUNET_SCHEDULER_shutdown (); /* We are done too! */ + } +} + + +/** + * Main function + * + * @param argc the number of command line arguments + * @param argv command line arg array + * @return return code + */ +int +main (int argc, char **argv) +{ + struct GNUNET_SIGNAL_Context *shc_chld; + + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + int ret; + + status = GNUNET_OK; + in_shutdown = GNUNET_NO; + shc_chld = + GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death); + ret = + GNUNET_PROGRAM_run (argc, argv, "gnunet-helper-testbed", + "Helper for starting gnunet-service-testbed", options, + &run, NULL); + GNUNET_SIGNAL_handler_uninstall (shc_chld); + shc_chld = NULL; + if (GNUNET_OK != ret) + return 1; + return (GNUNET_OK == status) ? 0 : 1; +} + +/* end of gnunet-helper-testbed.c */ diff --git a/src/testbed/gnunet-service-testbed.c b/src/testbed/gnunet-service-testbed.c new file mode 100644 index 0000000..f1dc3fa --- /dev/null +++ b/src/testbed/gnunet-service-testbed.c @@ -0,0 +1,2227 @@ +/* + This file is part of GNUnet. + (C) 2012 Christian Grothoff (and other contributing authors) + + GNUnet 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 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file testbed/gnunet-service-testbed.c + * @brief implementation of the TESTBED service + * @author Sree Harsha Totakura + */ + +#include "gnunet-service-testbed.h" + +#include <zlib.h> + + +/***********/ +/* Globals */ +/***********/ + +/** + * Our configuration + */ +struct GNUNET_CONFIGURATION_Handle *our_config; + +/** + * The master context; generated with the first INIT message + */ +struct Context *GST_context; + +/** + * A list of directly linked neighbours + */ +struct Slave **GST_slave_list; + +/** + * A list of peers we know about + */ +struct Peer **GST_peer_list; + +/** + * Array of hosts + */ +struct GNUNET_TESTBED_Host **GST_host_list; + +/** + * DLL head for forwarded operation contexts + */ +struct ForwardedOperationContext *fopcq_head; + +/** + * DLL tail for forwarded operation contexts + */ +struct ForwardedOperationContext *fopcq_tail; + +/** + * Operation queue for open file descriptors + */ +struct OperationQueue *GST_opq_openfds; + +/** + * The size of the host list + */ +unsigned int GST_host_list_size; + +/** + * The size of directly linked neighbours list + */ +unsigned int GST_slave_list_size; + +/** + * The size of the peer list + */ +unsigned int GST_peer_list_size; + + +/***********************************/ +/* Local definitions and variables */ +/***********************************/ + +/** + * The message queue for sending messages to clients + */ +struct MessageQueue +{ + /** + * The message to be sent + */ + struct GNUNET_MessageHeader *msg; + + /** + * The client to send the message to + */ + struct GNUNET_SERVER_Client *client; + + /** + * next pointer for DLL + */ + struct MessageQueue *next; + + /** + * prev pointer for DLL + */ + struct MessageQueue *prev; +}; + +/** + * Our hostname; we give this to all the peers we start + */ +static char *hostname; + +/** + * Current Transmit Handle; NULL if no notify transmit exists currently + */ +static struct GNUNET_SERVER_TransmitHandle *transmit_handle; + +/** + * The head for the LCF queue + */ +static struct LCFContextQueue *lcfq_head; + +/** + * The tail for the LCF queue + */ +static struct LCFContextQueue *lcfq_tail; + +/** + * The message queue head + */ +static struct MessageQueue *mq_head; + +/** + * The message queue tail + */ +static struct MessageQueue *mq_tail; + +/** + * The hashmap of shared services + */ +static struct GNUNET_CONTAINER_MultiHashMap *ss_map; + +/** + * A list of routes + */ +static struct Route **route_list; + +/** + * The event mask for the events we listen from sub-controllers + */ +static uint64_t event_mask; + +/** + * The size of the route list + */ +static unsigned int route_list_size; + +/** + * The lcf_task handle + */ +static GNUNET_SCHEDULER_TaskIdentifier lcf_proc_task_id; + +/** + * The shutdown task handle + */ +static GNUNET_SCHEDULER_TaskIdentifier shutdown_task_id; + + +/** + * Function called to notify a client about the connection begin ready to queue + * more data. "buf" will be NULL and "size" zero if the connection was closed + * for writing in the meantime. + * + * @param cls NULL + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * @return number of bytes written to buf + */ +static size_t +transmit_ready_notify (void *cls, size_t size, void *buf) +{ + struct MessageQueue *mq_entry; + + transmit_handle = NULL; + mq_entry = mq_head; + GNUNET_assert (NULL != mq_entry); + if (0 == size) + return 0; + GNUNET_assert (ntohs (mq_entry->msg->size) <= size); + size = ntohs (mq_entry->msg->size); + memcpy (buf, mq_entry->msg, size); + GNUNET_free (mq_entry->msg); + GNUNET_SERVER_client_drop (mq_entry->client); + GNUNET_CONTAINER_DLL_remove (mq_head, mq_tail, mq_entry); + GNUNET_free (mq_entry); + mq_entry = mq_head; + if (NULL != mq_entry) + transmit_handle = + GNUNET_SERVER_notify_transmit_ready (mq_entry->client, + ntohs (mq_entry->msg->size), + GNUNET_TIME_UNIT_FOREVER_REL, + &transmit_ready_notify, NULL); + return size; +} + + +/** + * Queues a message in send queue for sending to the service + * + * @param client the client to whom the queued message has to be sent + * @param msg the message to queue + */ +void +GST_queue_message (struct GNUNET_SERVER_Client *client, + struct GNUNET_MessageHeader *msg) +{ + struct MessageQueue *mq_entry; + uint16_t type; + uint16_t size; + + type = ntohs (msg->type); + size = ntohs (msg->size); + GNUNET_assert ((GNUNET_MESSAGE_TYPE_TESTBED_INIT <= type) && + (GNUNET_MESSAGE_TYPE_TESTBED_MAX > type)); + mq_entry = GNUNET_malloc (sizeof (struct MessageQueue)); + mq_entry->msg = msg; + mq_entry->client = client; + GNUNET_SERVER_client_keep (client); + LOG_DEBUG ("Queueing message of type %u, size %u for sending\n", type, + ntohs (msg->size)); + GNUNET_CONTAINER_DLL_insert_tail (mq_head, mq_tail, mq_entry); + if (NULL == transmit_handle) + transmit_handle = + GNUNET_SERVER_notify_transmit_ready (client, size, + GNUNET_TIME_UNIT_FOREVER_REL, + &transmit_ready_notify, NULL); +} + + +/** + * Similar to GNUNET_array_grow(); however instead of calling GNUNET_array_grow() + * several times we call it only once. The array is also made to grow in steps + * of LIST_GROW_STEP. + * + * @param ptr the array pointer to grow + * @param size the size of array + * @param accommodate_size the size which the array has to accommdate; after + * this call the array will be big enough to accommdate sizes upto + * accommodate_size + */ +#define array_grow_large_enough(ptr, size, accommodate_size) \ + do \ + { \ + unsigned int growth_size; \ + GNUNET_assert (size <= accommodate_size); \ + growth_size = size; \ + while (growth_size <= accommodate_size) \ + growth_size += LIST_GROW_STEP; \ + GNUNET_array_grow (ptr, size, growth_size); \ + GNUNET_assert (size > accommodate_size); \ + } while (0) + + +/** + * Function to add a host to the current list of known hosts + * + * @param host the host to add + * @return GNUNET_OK on success; GNUNET_SYSERR on failure due to host-id + * already in use + */ +static int +host_list_add (struct GNUNET_TESTBED_Host *host) +{ + uint32_t host_id; + + host_id = GNUNET_TESTBED_host_get_id_ (host); + if (GST_host_list_size <= host_id) + array_grow_large_enough (GST_host_list, GST_host_list_size, host_id); + if (NULL != GST_host_list[host_id]) + { + LOG_DEBUG ("A host with id: %u already exists\n", host_id); + return GNUNET_SYSERR; + } + GST_host_list[host_id] = host; + return GNUNET_OK; +} + + +/** + * Adds a route to the route list + * + * @param route the route to add + */ +static void +route_list_add (struct Route *route) +{ + if (route->dest >= route_list_size) + array_grow_large_enough (route_list, route_list_size, route->dest); + GNUNET_assert (NULL == route_list[route->dest]); + route_list[route->dest] = route; +} + + +/** + * Adds a slave to the slave array + * + * @param slave the slave controller to add + */ +static void +slave_list_add (struct Slave *slave) +{ + if (slave->host_id >= GST_slave_list_size) + array_grow_large_enough (GST_slave_list, GST_slave_list_size, + slave->host_id); + GNUNET_assert (NULL == GST_slave_list[slave->host_id]); + GST_slave_list[slave->host_id] = slave; +} + + +/** + * Adds a peer to the peer array + * + * @param peer the peer to add + */ +static void +peer_list_add (struct Peer *peer) +{ + if (peer->id >= GST_peer_list_size) + array_grow_large_enough (GST_peer_list, GST_peer_list_size, peer->id); + GNUNET_assert (NULL == GST_peer_list[peer->id]); + GST_peer_list[peer->id] = peer; +} + + +/** + * Removes a the give peer from the peer array + * + * @param peer the peer to be removed + */ +static void +peer_list_remove (struct Peer *peer) +{ + unsigned int orig_size; + uint32_t id; + + GST_peer_list[peer->id] = NULL; + orig_size = GST_peer_list_size; + while (GST_peer_list_size >= LIST_GROW_STEP) + { + for (id = GST_peer_list_size - 1; + (id >= GST_peer_list_size - LIST_GROW_STEP) && (id != UINT32_MAX); + id--) + if (NULL != GST_peer_list[id]) + break; + if (id != ((GST_peer_list_size - LIST_GROW_STEP) - 1)) + break; + GST_peer_list_size -= LIST_GROW_STEP; + } + if (orig_size == GST_peer_list_size) + return; + GST_peer_list = + GNUNET_realloc (GST_peer_list, + sizeof (struct Peer *) * GST_peer_list_size); +} + + +/** + * Finds the route with directly connected host as destination through which + * the destination host can be reached + * + * @param host_id the id of the destination host + * @return the route with directly connected destination host; NULL if no route + * is found + */ +struct Route * +GST_find_dest_route (uint32_t host_id) +{ + struct Route *route; + + if (route_list_size <= host_id) + return NULL; + while (NULL != (route = route_list[host_id])) + { + if (route->thru == GST_context->host_id) + break; + host_id = route->thru; + } + return route; +} + + +/** + * Routes message to a host given its host_id + * + * @param host_id the id of the destination host + * @param msg the message to be routed + */ +static void +route_message (uint32_t host_id, const struct GNUNET_MessageHeader *msg) +{ + GNUNET_break (0); +} + + +/** + * Send operation failure message to client + * + * @param client the client to which the failure message has to be sent to + * @param operation_id the id of the failed operation + * @param emsg the error message; can be NULL + */ +void +GST_send_operation_fail_msg (struct GNUNET_SERVER_Client *client, + uint64_t operation_id, const char *emsg) +{ + struct GNUNET_TESTBED_OperationFailureEventMessage *msg; + uint16_t msize; + uint16_t emsg_len; + + msize = sizeof (struct GNUNET_TESTBED_OperationFailureEventMessage); + emsg_len = (NULL == emsg) ? 0 : strlen (emsg) + 1; + msize += emsg_len; + msg = GNUNET_malloc (msize); + msg->header.size = htons (msize); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_OPERATION_FAIL_EVENT); + msg->event_type = htonl (GNUNET_TESTBED_ET_OPERATION_FINISHED); + msg->operation_id = GNUNET_htonll (operation_id); + if (0 != emsg_len) + memcpy (&msg[1], emsg, emsg_len); + GST_queue_message (client, &msg->header); +} + + +/** + * Function to send generic operation success message to given client + * + * @param client the client to send the message to + * @param operation_id the id of the operation which was successful + */ +static void +send_operation_success_msg (struct GNUNET_SERVER_Client *client, + uint64_t operation_id) +{ + struct GNUNET_TESTBED_GenericOperationSuccessEventMessage *msg; + uint16_t msize; + + msize = sizeof (struct GNUNET_TESTBED_GenericOperationSuccessEventMessage); + msg = GNUNET_malloc (msize); + msg->header.size = htons (msize); + msg->header.type = + htons (GNUNET_MESSAGE_TYPE_TESTBED_GENERIC_OPERATION_SUCCESS); + msg->operation_id = GNUNET_htonll (operation_id); + msg->event_type = htonl (GNUNET_TESTBED_ET_OPERATION_FINISHED); + GST_queue_message (client, &msg->header); +} + + +/** + * Callback which will be called to after a host registration succeeded or failed + * + * @param cls the handle to the slave at which the registration is completed + * @param emsg the error message; NULL if host registration is successful + */ +static void +hr_completion (void *cls, const char *emsg); + + +/** + * Attempts to register the next host in the host registration queue + * + * @param slave the slave controller whose host registration queue is checked + * for host registrations + */ +static void +register_next_host (struct Slave *slave) +{ + struct HostRegistration *hr; + + hr = slave->hr_dll_head; + GNUNET_assert (NULL != hr); + GNUNET_assert (NULL == slave->rhandle); + LOG (GNUNET_ERROR_TYPE_DEBUG, "Registering host %u at %u\n", + GNUNET_TESTBED_host_get_id_ (hr->host), + GNUNET_TESTBED_host_get_id_ (GST_host_list[slave->host_id])); + slave->rhandle = + GNUNET_TESTBED_register_host (slave->controller, hr->host, hr_completion, + slave); +} + + +/** + * Callback which will be called to after a host registration succeeded or failed + * + * @param cls the handle to the slave at which the registration is completed + * @param emsg the error message; NULL if host registration is successful + */ +static void +hr_completion (void *cls, const char *emsg) +{ + struct Slave *slave = cls; + struct HostRegistration *hr; + + slave->rhandle = NULL; + hr = slave->hr_dll_head; + GNUNET_assert (NULL != hr); + LOG (GNUNET_ERROR_TYPE_DEBUG, "Registering host %u at %u successful\n", + GNUNET_TESTBED_host_get_id_ (hr->host), + GNUNET_TESTBED_host_get_id_ (GST_host_list[slave->host_id])); + GNUNET_CONTAINER_DLL_remove (slave->hr_dll_head, slave->hr_dll_tail, hr); + if (NULL != hr->cb) + hr->cb (hr->cb_cls, emsg); + GNUNET_free (hr); + if (NULL != slave->hr_dll_head) + register_next_host (slave); +} + + +/** + * Adds a host registration's request to a slave's registration queue + * + * @param slave the slave controller at which the given host has to be + * registered + * @param cb the host registration completion callback + * @param cb_cls the closure for the host registration completion callback + * @param host the host which has to be registered + */ +void +GST_queue_host_registration (struct Slave *slave, + GNUNET_TESTBED_HostRegistrationCompletion cb, + void *cb_cls, struct GNUNET_TESTBED_Host *host) +{ + struct HostRegistration *hr; + int call_register; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Queueing host registration for host %u at %u\n", + GNUNET_TESTBED_host_get_id_ (host), + GNUNET_TESTBED_host_get_id_ (GST_host_list[slave->host_id])); + hr = GNUNET_malloc (sizeof (struct HostRegistration)); + hr->cb = cb; + hr->cb_cls = cb_cls; + hr->host = host; + call_register = (NULL == slave->hr_dll_head) ? GNUNET_YES : GNUNET_NO; + GNUNET_CONTAINER_DLL_insert_tail (slave->hr_dll_head, slave->hr_dll_tail, hr); + if (GNUNET_YES == call_register) + register_next_host (slave); +} + + +/** + * The Link Controller forwarding task + * + * @param cls the LCFContext + * @param tc the Task context from scheduler + */ +static void +lcf_proc_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + + +/** + * Completion callback for host registrations while forwarding Link Controller messages + * + * @param cls the LCFContext + * @param emsg the error message; NULL if host registration is successful + */ +static void +lcf_proc_cc (void *cls, const char *emsg) +{ + struct LCFContext *lcf = cls; + + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == lcf_proc_task_id); + switch (lcf->state) + { + case INIT: + if (NULL != emsg) + goto registration_error; + lcf->state = DELEGATED_HOST_REGISTERED; + lcf_proc_task_id = GNUNET_SCHEDULER_add_now (&lcf_proc_task, lcf); + break; + case DELEGATED_HOST_REGISTERED: + if (NULL != emsg) + goto registration_error; + lcf->state = SLAVE_HOST_REGISTERED; + lcf_proc_task_id = GNUNET_SCHEDULER_add_now (&lcf_proc_task, lcf); + break; + default: + GNUNET_assert (0); /* Shouldn't reach here */ + } + return; + +registration_error: + LOG (GNUNET_ERROR_TYPE_WARNING, "Host registration failed with message: %s\n", + emsg); + lcf->state = FINISHED; + lcf_proc_task_id = GNUNET_SCHEDULER_add_now (&lcf_proc_task, lcf); +} + + +/** + * Callback to relay the reply msg of a forwarded operation back to the client + * + * @param cls ForwardedOperationContext + * @param msg the message to relay + */ +void +GST_forwarded_operation_reply_relay (void *cls, + const struct GNUNET_MessageHeader *msg) +{ + struct ForwardedOperationContext *fopc = cls; + struct GNUNET_MessageHeader *dup_msg; + uint16_t msize; + + msize = ntohs (msg->size); + LOG_DEBUG ("Relaying message with type: %u, size: %u\n", ntohs (msg->type), + msize); + dup_msg = GNUNET_copy_message (msg); + GST_queue_message (fopc->client, dup_msg); + GNUNET_SERVER_client_drop (fopc->client); + GNUNET_SCHEDULER_cancel (fopc->timeout_task); + GNUNET_CONTAINER_DLL_remove (fopcq_head, fopcq_tail, fopc); + GNUNET_free (fopc); +} + + +/** + * Task to free resources when forwarded operation has been timedout + * + * @param cls the ForwardedOperationContext + * @param tc the task context from scheduler + */ +void +GST_forwarded_operation_timeout (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct ForwardedOperationContext *fopc = cls; + + GNUNET_TESTBED_forward_operation_msg_cancel_ (fopc->opc); + LOG (GNUNET_ERROR_TYPE_WARNING, "A forwarded operation has timed out\n"); + GST_send_operation_fail_msg (fopc->client, fopc->operation_id, "Timeout"); + GNUNET_SERVER_client_drop (fopc->client); + GNUNET_CONTAINER_DLL_remove (fopcq_head, fopcq_tail, fopc); + GNUNET_free (fopc); +} + + +/** + * The Link Controller forwarding task + * + * @param cls the LCFContext + * @param tc the Task context from scheduler + */ +static void +lcf_proc_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + + +/** + * Callback to be called when forwarded link controllers operation is + * successfull. We have to relay the reply msg back to the client + * + * @param cls the LCFContext + * @param msg the message to relay + */ +static void +lcf_forwarded_operation_reply_relay (void *cls, + const struct GNUNET_MessageHeader *msg) +{ + struct LCFContext *lcf = cls; + + GNUNET_assert (NULL != lcf->fopc); + GST_forwarded_operation_reply_relay (lcf->fopc, msg); + lcf->fopc = NULL; + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == lcf_proc_task_id); + lcf_proc_task_id = GNUNET_SCHEDULER_add_now (&lcf_proc_task, lcf); +} + + +/** + * Task to free resources when forwarded link controllers has been timedout + * + * @param cls the LCFContext + * @param tc the task context from scheduler + */ +static void +lcf_forwarded_operation_timeout (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct LCFContext *lcf = cls; + + GNUNET_assert (NULL != lcf->fopc); + lcf->fopc->timeout_task = GNUNET_SCHEDULER_NO_TASK; + GST_forwarded_operation_timeout (lcf->fopc, tc); + lcf->fopc = NULL; + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == lcf_proc_task_id); + lcf_proc_task_id = GNUNET_SCHEDULER_add_now (&lcf_proc_task, lcf); +} + + +/** + * The Link Controller forwarding task + * + * @param cls the LCFContext + * @param tc the Task context from scheduler + */ +static void +lcf_proc_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct LCFContext *lcf = cls; + struct LCFContextQueue *lcfq; + + lcf_proc_task_id = GNUNET_SCHEDULER_NO_TASK; + switch (lcf->state) + { + case INIT: + if (GNUNET_NO == + GNUNET_TESTBED_is_host_registered_ (GST_host_list + [lcf->delegated_host_id], + lcf->gateway->controller)) + { + GST_queue_host_registration (lcf->gateway, lcf_proc_cc, lcf, + GST_host_list[lcf->delegated_host_id]); + } + else + { + lcf->state = DELEGATED_HOST_REGISTERED; + lcf_proc_task_id = GNUNET_SCHEDULER_add_now (&lcf_proc_task, lcf); + } + break; + case DELEGATED_HOST_REGISTERED: + if (GNUNET_NO == + GNUNET_TESTBED_is_host_registered_ (GST_host_list[lcf->slave_host_id], + lcf->gateway->controller)) + { + GST_queue_host_registration (lcf->gateway, lcf_proc_cc, lcf, + GST_host_list[lcf->slave_host_id]); + } + else + { + lcf->state = SLAVE_HOST_REGISTERED; + lcf_proc_task_id = GNUNET_SCHEDULER_add_now (&lcf_proc_task, lcf); + } + break; + case SLAVE_HOST_REGISTERED: + lcf->fopc = GNUNET_malloc (sizeof (struct ForwardedOperationContext)); + lcf->fopc->client = lcf->client; + lcf->fopc->operation_id = lcf->operation_id; + lcf->fopc->type = OP_LINK_CONTROLLERS; + lcf->fopc->opc = + GNUNET_TESTBED_forward_operation_msg_ (lcf->gateway->controller, + lcf->operation_id, + &lcf->msg->header, + &lcf_forwarded_operation_reply_relay, + lcf); + lcf->fopc->timeout_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &lcf_forwarded_operation_timeout, + lcf); + GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, lcf->fopc); + lcf->state = FINISHED; + break; + case FINISHED: + lcfq = lcfq_head; + GNUNET_assert (lcfq->lcf == lcf); + GNUNET_free (lcf->msg); + GNUNET_free (lcf); + GNUNET_CONTAINER_DLL_remove (lcfq_head, lcfq_tail, lcfq); + GNUNET_free (lcfq); + if (NULL != lcfq_head) + lcf_proc_task_id = + GNUNET_SCHEDULER_add_now (&lcf_proc_task, lcfq_head->lcf); + } +} + + +/** + * Callback for event from slave controllers + * + * @param cls struct Slave * + * @param event information about the event + */ +static void +slave_event_callback (void *cls, + const struct GNUNET_TESTBED_EventInformation *event) +{ + struct RegisteredHostContext *rhc; + struct GNUNET_CONFIGURATION_Handle *cfg; + struct GNUNET_TESTBED_Operation *old_op; + + /* We currently only get here when working on RegisteredHostContexts */ + GNUNET_assert (GNUNET_TESTBED_ET_OPERATION_FINISHED == event->type); + rhc = event->details.operation_finished.op_cls; + GNUNET_assert (rhc->sub_op == event->details.operation_finished.operation); + switch (rhc->state) + { + case RHC_GET_CFG: + cfg = event->details.operation_finished.generic; + old_op = rhc->sub_op; + rhc->state = RHC_LINK; + rhc->sub_op = + GNUNET_TESTBED_controller_link (rhc, rhc->gateway->controller, + rhc->reg_host, rhc->host, cfg, + GNUNET_NO); + GNUNET_TESTBED_operation_done (old_op); + break; + case RHC_LINK: + LOG_DEBUG ("OL: Linking controllers successfull\n"); + GNUNET_TESTBED_operation_done (rhc->sub_op); + rhc->sub_op = NULL; + rhc->state = RHC_OL_CONNECT; + GST_process_next_focc (rhc); + break; + default: + GNUNET_assert (0); + } +} + + +/** + * Callback to signal successfull startup of the controller process + * + * @param cls the handle to the slave whose status is to be found here + * @param cfg the configuration with which the controller has been started; + * NULL if status is not GNUNET_OK + * @param status GNUNET_OK if the startup is successfull; GNUNET_SYSERR if not, + * GNUNET_TESTBED_controller_stop() shouldn't be called in this case + */ +static void +slave_status_callback (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg, + int status) +{ + struct Slave *slave = cls; + struct LinkControllersContext *lcc; + + lcc = slave->lcc; + if (GNUNET_SYSERR == status) + { + slave->controller_proc = NULL; + GST_slave_list[slave->host_id] = NULL; + if (NULL != slave->cfg) + GNUNET_CONFIGURATION_destroy (slave->cfg); + GNUNET_free (slave); + slave = NULL; + LOG (GNUNET_ERROR_TYPE_WARNING, "Unexpected slave shutdown\n"); + GNUNET_SCHEDULER_shutdown (); /* We too shutdown */ + goto clean_lcc; + } + slave->controller = + GNUNET_TESTBED_controller_connect (cfg, GST_host_list[slave->host_id], + event_mask, &slave_event_callback, + slave); + if (NULL != slave->controller) + { + send_operation_success_msg (lcc->client, lcc->operation_id); + slave->cfg = GNUNET_CONFIGURATION_dup (cfg); + } + else + { + GST_send_operation_fail_msg (lcc->client, lcc->operation_id, + "Could not connect to delegated controller"); + GNUNET_TESTBED_controller_stop (slave->controller_proc); + GST_slave_list[slave->host_id] = NULL; + GNUNET_free (slave); + slave = NULL; + } + +clean_lcc: + if (NULL != lcc) + { + if (NULL != lcc->client) + { + GNUNET_SERVER_receive_done (lcc->client, GNUNET_OK); + GNUNET_SERVER_client_drop (lcc->client); + lcc->client = NULL; + } + GNUNET_free (lcc); + } + if (NULL != slave) + slave->lcc = NULL; +} + + +/** + * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_INIT messages + * + * @param cls NULL + * @param client identification of the client + * @param message the actual message + */ +static void +handle_init (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct GNUNET_TESTBED_InitMessage *msg; + struct GNUNET_TESTBED_Host *host; + const char *controller_hostname; + uint16_t msize; + + if (NULL != GST_context) + { + LOG_DEBUG ("We are being connected to laterally\n"); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + msg = (const struct GNUNET_TESTBED_InitMessage *) message; + msize = ntohs (message->size); + if (msize <= sizeof (struct GNUNET_TESTBED_InitMessage)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + msize -= sizeof (struct GNUNET_TESTBED_InitMessage); + controller_hostname = (const char *) &msg[1]; + if ('\0' != controller_hostname[msize - 1]) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GST_context = GNUNET_malloc (sizeof (struct Context)); + GNUNET_SERVER_client_keep (client); + GST_context->client = client; + GST_context->host_id = ntohl (msg->host_id); + GST_context->master_ip = GNUNET_strdup (controller_hostname); + LOG_DEBUG ("Our IP: %s\n", GST_context->master_ip); + GST_context->system = + GNUNET_TESTING_system_create ("testbed", GST_context->master_ip, + hostname); + host = + GNUNET_TESTBED_host_create_with_id (GST_context->host_id, + GST_context->master_ip, NULL, 0); + host_list_add (host); + LOG_DEBUG ("Created master context with host ID: %u\n", GST_context->host_id); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST messages + * + * @param cls NULL + * @param client identification of the client + * @param message the actual message + */ +static void +handle_add_host (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_TESTBED_Host *host; + const struct GNUNET_TESTBED_AddHostMessage *msg; + struct GNUNET_TESTBED_HostConfirmedMessage *reply; + char *username; + char *hostname; + char *emsg; + uint32_t host_id; + uint16_t username_length; + uint16_t hostname_length; + uint16_t reply_size; + uint16_t msize; + + msg = (const struct GNUNET_TESTBED_AddHostMessage *) message; + msize = ntohs (msg->header.size); + username = (char *) &msg[1]; + username_length = ntohs (msg->user_name_length); + if (0 != username_length) + username_length++; + /* msg must contain hostname */ + GNUNET_assert (msize > + (sizeof (struct GNUNET_TESTBED_AddHostMessage) + + username_length + 1)); + if (0 != username_length) + GNUNET_assert ('\0' == username[username_length - 1]); + hostname = username + username_length; + hostname_length = + msize - (sizeof (struct GNUNET_TESTBED_AddHostMessage) + username_length); + GNUNET_assert ('\0' == hostname[hostname_length - 1]); + GNUNET_assert (strlen (hostname) == hostname_length - 1); + host_id = ntohl (msg->host_id); + LOG_DEBUG ("Received ADDHOST %u message\n", host_id); + LOG_DEBUG ("-------host id: %u\n", host_id); + LOG_DEBUG ("-------hostname: %s\n", hostname); + if (0 != username_length) + LOG_DEBUG ("-------username: %s\n", username); + else + { + LOG_DEBUG ("-------username: NULL\n"); + username = NULL; + } + LOG_DEBUG ("-------ssh port: %u\n", ntohs (msg->ssh_port)); + host = + GNUNET_TESTBED_host_create_with_id (host_id, hostname, username, + ntohs (msg->ssh_port)); + GNUNET_assert (NULL != host); + reply_size = sizeof (struct GNUNET_TESTBED_HostConfirmedMessage); + if (GNUNET_OK != host_list_add (host)) + { + /* We are unable to add a host */ + emsg = "A host exists with given host-id"; + LOG_DEBUG ("%s: %u", emsg, host_id); + GNUNET_TESTBED_host_destroy (host); + reply_size += strlen (emsg) + 1; + reply = GNUNET_malloc (reply_size); + memcpy (&reply[1], emsg, strlen (emsg) + 1); + } + else + { + LOG_DEBUG ("Added host %u at %u\n", host_id, GST_context->host_id); + reply = GNUNET_malloc (reply_size); + } + reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_ADD_HOST_SUCCESS); + reply->header.size = htons (reply_size); + reply->host_id = htonl (host_id); + GST_queue_message (client, &reply->header); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Iterator over hash map entries. + * + * @param cls closure + * @param key current key code + * @param value value in the hash map + * @return GNUNET_YES if we should continue to + * iterate, + * GNUNET_NO if not. + */ +int +ss_exists_iterator (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct SharedService *queried_ss = cls; + struct SharedService *ss = value; + + if (0 == strcmp (ss->name, queried_ss->name)) + return GNUNET_NO; + else + return GNUNET_YES; +} + + +/** + * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST messages + * + * @param cls NULL + * @param client identification of the client + * @param message the actual message + */ +static void +handle_configure_shared_service (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct GNUNET_TESTBED_ConfigureSharedServiceMessage *msg; + struct SharedService *ss; + char *service_name; + struct GNUNET_HashCode hash; + uint16_t msg_size; + uint16_t service_name_size; + + msg = (const struct GNUNET_TESTBED_ConfigureSharedServiceMessage *) message; + msg_size = ntohs (message->size); + if (msg_size <= sizeof (struct GNUNET_TESTBED_ConfigureSharedServiceMessage)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + service_name_size = + msg_size - sizeof (struct GNUNET_TESTBED_ConfigureSharedServiceMessage); + service_name = (char *) &msg[1]; + if ('\0' != service_name[service_name_size - 1]) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + LOG_DEBUG ("Received service sharing request for %s, with %d peers\n", + service_name, ntohl (msg->num_peers)); + if (ntohl (msg->host_id) != GST_context->host_id) + { + route_message (ntohl (msg->host_id), message); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + GNUNET_SERVER_receive_done (client, GNUNET_OK); + ss = GNUNET_malloc (sizeof (struct SharedService)); + ss->name = strdup (service_name); + ss->num_shared = ntohl (msg->num_peers); + GNUNET_CRYPTO_hash (ss->name, service_name_size, &hash); + if (GNUNET_SYSERR == + GNUNET_CONTAINER_multihashmap_get_multiple (ss_map, &hash, + &ss_exists_iterator, ss)) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + "Service %s already configured as a shared service. " + "Ignoring service sharing request \n", ss->name); + GNUNET_free (ss->name); + GNUNET_free (ss); + return; + } + GNUNET_CONTAINER_multihashmap_put (ss_map, &hash, ss, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); +} + + +/** + * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_LCONTROLLERS message + * + * @param cls NULL + * @param client identification of the client + * @param message the actual message + */ +static void +handle_link_controllers (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct GNUNET_TESTBED_ControllerLinkMessage *msg; + struct GNUNET_CONFIGURATION_Handle *cfg; + struct LCFContextQueue *lcfq; + struct Route *route; + struct Route *new_route; + char *config; + uLongf dest_size; + size_t config_size; + uint32_t delegated_host_id; + uint32_t slave_host_id; + uint16_t msize; + + if (NULL == GST_context) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + msize = ntohs (message->size); + if (sizeof (struct GNUNET_TESTBED_ControllerLinkMessage) >= msize) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + msg = (const struct GNUNET_TESTBED_ControllerLinkMessage *) message; + delegated_host_id = ntohl (msg->delegated_host_id); + if (delegated_host_id == GST_context->host_id) + { + GNUNET_break (0); + LOG (GNUNET_ERROR_TYPE_WARNING, "Trying to link ourselves\n"); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + if ((delegated_host_id >= GST_host_list_size) || + (NULL == GST_host_list[delegated_host_id])) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + "Delegated host %u not registered with us\n", delegated_host_id); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + slave_host_id = ntohl (msg->slave_host_id); + if ((slave_host_id >= GST_host_list_size) || + (NULL == GST_host_list[slave_host_id])) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Slave host %u not registered with us\n", + slave_host_id); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + if (slave_host_id == delegated_host_id) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Slave and delegated host are same\n"); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + if (slave_host_id == GST_context->host_id) /* Link from us */ + { + struct Slave *slave; + struct LinkControllersContext *lcc; + + msize -= sizeof (struct GNUNET_TESTBED_ControllerLinkMessage); + config_size = ntohs (msg->config_size); + if ((delegated_host_id < GST_slave_list_size) && (NULL != GST_slave_list[delegated_host_id])) /* We have already added */ + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Host %u already connected\n", + delegated_host_id); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + config = GNUNET_malloc (config_size); + dest_size = (uLongf) config_size; + if (Z_OK != + uncompress ((Bytef *) config, &dest_size, (const Bytef *) &msg[1], + (uLong) msize)) + { + GNUNET_break (0); /* Compression error */ + GNUNET_free (config); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + if (config_size != dest_size) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Uncompressed config size mismatch\n"); + GNUNET_free (config); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + cfg = GNUNET_CONFIGURATION_create (); /* Free here or in lcfcontext */ + if (GNUNET_OK != + GNUNET_CONFIGURATION_deserialize (cfg, config, config_size, GNUNET_NO)) + { + GNUNET_break (0); /* Configuration parsing error */ + GNUNET_free (config); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_free (config); + if ((delegated_host_id < GST_slave_list_size) && + (NULL != GST_slave_list[delegated_host_id])) + { + GNUNET_break (0); /* Configuration parsing error */ + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + slave = GNUNET_malloc (sizeof (struct Slave)); + slave->host_id = delegated_host_id; + slave->reghost_map = GNUNET_CONTAINER_multihashmap_create (100, GNUNET_NO); + slave_list_add (slave); + if (1 != msg->is_subordinate) + { + slave->controller = + GNUNET_TESTBED_controller_connect (cfg, GST_host_list[slave->host_id], + event_mask, &slave_event_callback, + slave); + slave->cfg = cfg; + if (NULL != slave->controller) + send_operation_success_msg (client, GNUNET_ntohll (msg->operation_id)); + else + GST_send_operation_fail_msg (client, GNUNET_ntohll (msg->operation_id), + "Could not connect to delegated controller"); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + lcc = GNUNET_malloc (sizeof (struct LinkControllersContext)); + lcc->operation_id = GNUNET_ntohll (msg->operation_id); + GNUNET_SERVER_client_keep (client); + lcc->client = client; + slave->lcc = lcc; + slave->controller_proc = + GNUNET_TESTBED_controller_start (GST_context->master_ip, + GST_host_list[slave->host_id], cfg, + &slave_status_callback, slave); + GNUNET_CONFIGURATION_destroy (cfg); + new_route = GNUNET_malloc (sizeof (struct Route)); + new_route->dest = delegated_host_id; + new_route->thru = GST_context->host_id; + route_list_add (new_route); + return; + } + + /* Route the request */ + if (slave_host_id >= route_list_size) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "No route towards slave host"); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + lcfq = GNUNET_malloc (sizeof (struct LCFContextQueue)); + lcfq->lcf = GNUNET_malloc (sizeof (struct LCFContext)); + lcfq->lcf->delegated_host_id = delegated_host_id; + lcfq->lcf->slave_host_id = slave_host_id; + route = GST_find_dest_route (slave_host_id); + GNUNET_assert (NULL != route); /* because we add routes carefully */ + GNUNET_assert (route->dest < GST_slave_list_size); + GNUNET_assert (NULL != GST_slave_list[route->dest]); + lcfq->lcf->state = INIT; + lcfq->lcf->operation_id = GNUNET_ntohll (msg->operation_id); + lcfq->lcf->gateway = GST_slave_list[route->dest]; + lcfq->lcf->msg = GNUNET_malloc (msize); + (void) memcpy (lcfq->lcf->msg, msg, msize); + GNUNET_SERVER_client_keep (client); + lcfq->lcf->client = client; + if (NULL == lcfq_head) + { + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == lcf_proc_task_id); + GNUNET_CONTAINER_DLL_insert_tail (lcfq_head, lcfq_tail, lcfq); + lcf_proc_task_id = GNUNET_SCHEDULER_add_now (&lcf_proc_task, lcfq->lcf); + } + else + GNUNET_CONTAINER_DLL_insert_tail (lcfq_head, lcfq_tail, lcfq); + /* FIXME: Adding a new route should happen after the controllers are linked + * successfully */ + if (1 != msg->is_subordinate) + { + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + if ((delegated_host_id < route_list_size) && + (NULL != route_list[delegated_host_id])) + { + GNUNET_break_op (0); /* Are you trying to link delegated host twice + * with is subordinate flag set to GNUNET_YES? */ + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + new_route = GNUNET_malloc (sizeof (struct Route)); + new_route->dest = delegated_host_id; + new_route->thru = route->dest; + route_list_add (new_route); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * The task to be executed if the forwarded peer create operation has been + * timed out + * + * @param cls the FowardedOperationContext + * @param tc the TaskContext from the scheduler + */ +static void +peer_create_forward_timeout (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct ForwardedOperationContext *fopc = cls; + + GNUNET_free (fopc->cls); + GST_forwarded_operation_timeout (fopc, tc); +} + + +/** + * Callback to be called when forwarded peer create operation is successfull. We + * have to relay the reply msg back to the client + * + * @param cls ForwardedOperationContext + * @param msg the peer create success message + */ +static void +peer_create_success_cb (void *cls, const struct GNUNET_MessageHeader *msg) +{ + struct ForwardedOperationContext *fopc = cls; + struct Peer *remote_peer; + + if (ntohs (msg->type) == GNUNET_MESSAGE_TYPE_TESTBED_CREATE_PEER_SUCCESS) + { + GNUNET_assert (NULL != fopc->cls); + remote_peer = fopc->cls; + peer_list_add (remote_peer); + } + GST_forwarded_operation_reply_relay (fopc, msg); +} + + +/** + * Function to destroy a peer + * + * @param peer the peer structure to destroy + */ +void +GST_destroy_peer (struct Peer *peer) +{ + GNUNET_break (0 == peer->reference_cnt); + if (GNUNET_YES == peer->is_remote) + { + peer_list_remove (peer); + GNUNET_free (peer); + return; + } + if (GNUNET_YES == peer->details.local.is_running) + { + GNUNET_TESTING_peer_stop (peer->details.local.peer); + peer->details.local.is_running = GNUNET_NO; + } + GNUNET_TESTING_peer_destroy (peer->details.local.peer); + GNUNET_CONFIGURATION_destroy (peer->details.local.cfg); + peer_list_remove (peer); + GNUNET_free (peer); +} + + +/** + * Callback to be called when forwarded peer destroy operation is successfull. We + * have to relay the reply msg back to the client + * + * @param cls ForwardedOperationContext + * @param msg the peer create success message + */ +static void +peer_destroy_success_cb (void *cls, const struct GNUNET_MessageHeader *msg) +{ + struct ForwardedOperationContext *fopc = cls; + struct Peer *remote_peer; + + if (GNUNET_MESSAGE_TYPE_TESTBED_GENERIC_OPERATION_SUCCESS == + ntohs (msg->type)) + { + remote_peer = fopc->cls; + GNUNET_assert (NULL != remote_peer); + remote_peer->destroy_flag = GNUNET_YES; + if (0 == remote_peer->reference_cnt) + GST_destroy_peer (remote_peer); + } + GST_forwarded_operation_reply_relay (fopc, msg); +} + + + +/** + * Handler for GNUNET_MESSAGE_TYPE_TESTBED_CREATEPEER messages + * + * @param cls NULL + * @param client identification of the client + * @param message the actual message + */ +static void +handle_peer_create (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct GNUNET_TESTBED_PeerCreateMessage *msg; + struct GNUNET_TESTBED_PeerCreateSuccessEventMessage *reply; + struct GNUNET_CONFIGURATION_Handle *cfg; + struct ForwardedOperationContext *fo_ctxt; + struct Route *route; + struct Peer *peer; + char *config; + size_t dest_size; + int ret; + uint32_t config_size; + uint32_t host_id; + uint32_t peer_id; + uint16_t msize; + + + msize = ntohs (message->size); + if (msize <= sizeof (struct GNUNET_TESTBED_PeerCreateMessage)) + { + GNUNET_break (0); /* We need configuration */ + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + msg = (const struct GNUNET_TESTBED_PeerCreateMessage *) message; + host_id = ntohl (msg->host_id); + peer_id = ntohl (msg->peer_id); + if (UINT32_MAX == peer_id) + { + GST_send_operation_fail_msg (client, GNUNET_ntohll (msg->operation_id), + "Cannot create peer with given ID"); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + if (host_id == GST_context->host_id) + { + char *emsg; + + /* We are responsible for this peer */ + msize -= sizeof (struct GNUNET_TESTBED_PeerCreateMessage); + config_size = ntohl (msg->config_size); + config = GNUNET_malloc (config_size); + dest_size = config_size; + if (Z_OK != + (ret = + uncompress ((Bytef *) config, (uLongf *) & dest_size, + (const Bytef *) &msg[1], (uLong) msize))) + { + GNUNET_break (0); /* uncompression error */ + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + if (config_size != dest_size) + { + GNUNET_break (0); /* Uncompressed config size mismatch */ + GNUNET_free (config); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + cfg = GNUNET_CONFIGURATION_create (); + if (GNUNET_OK != + GNUNET_CONFIGURATION_deserialize (cfg, config, config_size, GNUNET_NO)) + { + GNUNET_break (0); /* Configuration parsing error */ + GNUNET_free (config); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_free (config); + GNUNET_CONFIGURATION_set_value_number (cfg, "TESTBED", "PEERID", + (unsigned long long) peer_id); + peer = GNUNET_malloc (sizeof (struct Peer)); + peer->is_remote = GNUNET_NO; + peer->details.local.cfg = cfg; + peer->id = peer_id; + LOG_DEBUG ("Creating peer with id: %u\n", (unsigned int) peer->id); + peer->details.local.peer = + GNUNET_TESTING_peer_configure (GST_context->system, + peer->details.local.cfg, peer->id, + NULL /* Peer id */ , + &emsg); + if (NULL == peer->details.local.peer) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Configuring peer failed: %s\n", emsg); + GNUNET_free (emsg); + GNUNET_free (peer); + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + peer->details.local.is_running = GNUNET_NO; + peer_list_add (peer); + reply = + GNUNET_malloc (sizeof + (struct GNUNET_TESTBED_PeerCreateSuccessEventMessage)); + reply->header.size = + htons (sizeof (struct GNUNET_TESTBED_PeerCreateSuccessEventMessage)); + reply->header.type = + htons (GNUNET_MESSAGE_TYPE_TESTBED_CREATE_PEER_SUCCESS); + reply->peer_id = msg->peer_id; + reply->operation_id = msg->operation_id; + GST_queue_message (client, &reply->header); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + + /* Forward peer create request */ + route = GST_find_dest_route (host_id); + if (NULL == route) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + + peer = GNUNET_malloc (sizeof (struct Peer)); + peer->is_remote = GNUNET_YES; + peer->id = peer_id; + peer->details.remote.slave = GST_slave_list[route->dest]; + peer->details.remote.remote_host_id = host_id; + fo_ctxt = GNUNET_malloc (sizeof (struct ForwardedOperationContext)); + GNUNET_SERVER_client_keep (client); + fo_ctxt->client = client; + fo_ctxt->operation_id = GNUNET_ntohll (msg->operation_id); + fo_ctxt->cls = peer; //GST_slave_list[route->dest]->controller; + fo_ctxt->type = OP_PEER_CREATE; + fo_ctxt->opc = + GNUNET_TESTBED_forward_operation_msg_ (GST_slave_list + [route->dest]->controller, + fo_ctxt->operation_id, + &msg->header, + peer_create_success_cb, fo_ctxt); + fo_ctxt->timeout_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &peer_create_forward_timeout, + fo_ctxt); + GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fo_ctxt); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_DESTROYPEER messages + * + * @param cls NULL + * @param client identification of the client + * @param message the actual message + */ +static void +handle_peer_destroy (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct GNUNET_TESTBED_PeerDestroyMessage *msg; + struct ForwardedOperationContext *fopc; + struct Peer *peer; + uint32_t peer_id; + + msg = (const struct GNUNET_TESTBED_PeerDestroyMessage *) message; + peer_id = ntohl (msg->peer_id); + LOG_DEBUG ("Received peer destory on peer: %u and operation id: %ul\n", + peer_id, GNUNET_ntohll (msg->operation_id)); + if ((GST_peer_list_size <= peer_id) || (NULL == GST_peer_list[peer_id])) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Asked to destroy a non existent peer with id: %u\n", peer_id); + GST_send_operation_fail_msg (client, GNUNET_ntohll (msg->operation_id), + "Peer doesn't exist"); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + peer = GST_peer_list[peer_id]; + if (GNUNET_YES == peer->is_remote) + { + /* Forward the destory message to sub controller */ + fopc = GNUNET_malloc (sizeof (struct ForwardedOperationContext)); + GNUNET_SERVER_client_keep (client); + fopc->client = client; + fopc->cls = peer; + fopc->type = OP_PEER_DESTROY; + fopc->operation_id = GNUNET_ntohll (msg->operation_id); + fopc->opc = + GNUNET_TESTBED_forward_operation_msg_ (peer->details.remote. + slave->controller, + fopc->operation_id, &msg->header, + &peer_destroy_success_cb, fopc); + fopc->timeout_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &GST_forwarded_operation_timeout, + fopc); + GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fopc); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + peer->destroy_flag = GNUNET_YES; + if (0 == peer->reference_cnt) + GST_destroy_peer (peer); + else + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Delaying peer destroy as peer is currently in use\n"); + send_operation_success_msg (client, GNUNET_ntohll (msg->operation_id)); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_DESTROYPEER messages + * + * @param cls NULL + * @param client identification of the client + * @param message the actual message + */ +static void +handle_peer_start (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct GNUNET_TESTBED_PeerStartMessage *msg; + struct GNUNET_TESTBED_PeerEventMessage *reply; + struct ForwardedOperationContext *fopc; + struct Peer *peer; + uint32_t peer_id; + + msg = (const struct GNUNET_TESTBED_PeerStartMessage *) message; + peer_id = ntohl (msg->peer_id); + if ((peer_id >= GST_peer_list_size) || (NULL == GST_peer_list[peer_id])) + { + GNUNET_break (0); + LOG (GNUNET_ERROR_TYPE_ERROR, + "Asked to start a non existent peer with id: %u\n", peer_id); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + peer = GST_peer_list[peer_id]; + if (GNUNET_YES == peer->is_remote) + { + fopc = GNUNET_malloc (sizeof (struct ForwardedOperationContext)); + GNUNET_SERVER_client_keep (client); + fopc->client = client; + fopc->operation_id = GNUNET_ntohll (msg->operation_id); + fopc->type = OP_PEER_START; + fopc->opc = + GNUNET_TESTBED_forward_operation_msg_ (peer->details.remote. + slave->controller, + fopc->operation_id, &msg->header, + &GST_forwarded_operation_reply_relay, + fopc); + fopc->timeout_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &GST_forwarded_operation_timeout, + fopc); + GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fopc); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + if (GNUNET_OK != GNUNET_TESTING_peer_start (peer->details.local.peer)) + { + GST_send_operation_fail_msg (client, GNUNET_ntohll (msg->operation_id), + "Failed to start"); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + peer->details.local.is_running = GNUNET_YES; + reply = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_PeerEventMessage)); + reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_PEER_EVENT); + reply->header.size = htons (sizeof (struct GNUNET_TESTBED_PeerEventMessage)); + reply->event_type = htonl (GNUNET_TESTBED_ET_PEER_START); + reply->host_id = htonl (GST_context->host_id); + reply->peer_id = msg->peer_id; + reply->operation_id = msg->operation_id; + GST_queue_message (client, &reply->header); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_DESTROYPEER messages + * + * @param cls NULL + * @param client identification of the client + * @param message the actual message + */ +static void +handle_peer_stop (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct GNUNET_TESTBED_PeerStopMessage *msg; + struct GNUNET_TESTBED_PeerEventMessage *reply; + struct ForwardedOperationContext *fopc; + struct Peer *peer; + uint32_t peer_id; + + msg = (const struct GNUNET_TESTBED_PeerStopMessage *) message; + peer_id = ntohl (msg->peer_id); + LOG (GNUNET_ERROR_TYPE_DEBUG, "Received PEER_STOP for peer %u\n", peer_id); + if ((peer_id >= GST_peer_list_size) || (NULL == GST_peer_list[peer_id])) + { + GST_send_operation_fail_msg (client, GNUNET_ntohll (msg->operation_id), + "Peer not found"); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + peer = GST_peer_list[peer_id]; + if (GNUNET_YES == peer->is_remote) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "Forwarding PEER_STOP for peer %u\n", + peer_id); + fopc = GNUNET_malloc (sizeof (struct ForwardedOperationContext)); + GNUNET_SERVER_client_keep (client); + fopc->client = client; + fopc->operation_id = GNUNET_ntohll (msg->operation_id); + fopc->type = OP_PEER_STOP; + fopc->opc = + GNUNET_TESTBED_forward_operation_msg_ (peer->details.remote. + slave->controller, + fopc->operation_id, &msg->header, + &GST_forwarded_operation_reply_relay, + fopc); + fopc->timeout_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &GST_forwarded_operation_timeout, + fopc); + GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fopc); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + if (GNUNET_OK != GNUNET_TESTING_peer_stop (peer->details.local.peer)) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Stopping peer %u failed\n", peer_id); + GST_send_operation_fail_msg (client, GNUNET_ntohll (msg->operation_id), + "Peer not running"); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, "Peer %u successfully stopped\n", peer_id); + peer->details.local.is_running = GNUNET_NO; + reply = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_PeerEventMessage)); + reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_PEER_EVENT); + reply->header.size = htons (sizeof (struct GNUNET_TESTBED_PeerEventMessage)); + reply->event_type = htonl (GNUNET_TESTBED_ET_PEER_STOP); + reply->host_id = htonl (GST_context->host_id); + reply->peer_id = msg->peer_id; + reply->operation_id = msg->operation_id; + GST_queue_message (client, &reply->header); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Handler for GNUNET_MESSAGE_TYPE_TESTBED_GETPEERCONFIG messages + * + * @param cls NULL + * @param client identification of the client + * @param message the actual message + */ +static void +handle_peer_get_config (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct GNUNET_TESTBED_PeerGetConfigurationMessage *msg; + struct GNUNET_TESTBED_PeerConfigurationInformationMessage *reply; + struct Peer *peer; + char *config; + char *xconfig; + size_t c_size; + size_t xc_size; + uint32_t peer_id; + uint16_t msize; + + msg = (const struct GNUNET_TESTBED_PeerGetConfigurationMessage *) message; + peer_id = ntohl (msg->peer_id); + if ((peer_id >= GST_peer_list_size) || (NULL == GST_peer_list[peer_id])) + { + GST_send_operation_fail_msg (client, GNUNET_ntohll (msg->operation_id), + "Peer not found"); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + peer = GST_peer_list[peer_id]; + if (GNUNET_YES == peer->is_remote) + { + struct ForwardedOperationContext *fopc; + + LOG_DEBUG ("Forwarding PEER_GET_CONFIG for peer: %u\n", peer_id); + fopc = GNUNET_malloc (sizeof (struct ForwardedOperationContext)); + GNUNET_SERVER_client_keep (client); + fopc->client = client; + fopc->operation_id = GNUNET_ntohll (msg->operation_id); + fopc->type = OP_PEER_INFO; + fopc->opc = + GNUNET_TESTBED_forward_operation_msg_ (peer->details.remote. + slave->controller, + fopc->operation_id, &msg->header, + &GST_forwarded_operation_reply_relay, + fopc); + fopc->timeout_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &GST_forwarded_operation_timeout, + fopc); + GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fopc); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + LOG_DEBUG ("Received PEER_GET_CONFIG for peer: %u\n", peer_id); + config = + GNUNET_CONFIGURATION_serialize (GST_peer_list[peer_id]->details.local.cfg, + &c_size); + xc_size = GNUNET_TESTBED_compress_config_ (config, c_size, &xconfig); + GNUNET_free (config); + msize = + xc_size + + sizeof (struct GNUNET_TESTBED_PeerConfigurationInformationMessage); + reply = GNUNET_realloc (xconfig, msize); + (void) memmove (&reply[1], reply, xc_size); + reply->header.size = htons (msize); + reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_PEER_CONFIGURATION); + reply->peer_id = msg->peer_id; + reply->operation_id = msg->operation_id; + GNUNET_TESTING_peer_get_identity (GST_peer_list[peer_id]->details.local.peer, + &reply->peer_identity); + reply->config_size = htons ((uint16_t) c_size); + GST_queue_message (client, &reply->header); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Handler for GNUNET_MESSAGE_TYPE_TESTBED_GETSLAVECONFIG messages + * + * @param cls NULL + * @param client identification of the client + * @param message the actual message + */ +static void +handle_slave_get_config (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_TESTBED_SlaveGetConfigurationMessage *msg; + struct Slave *slave; + struct GNUNET_TESTBED_SlaveConfiguration *reply; + char *config; + char *xconfig; + size_t config_size; + size_t xconfig_size; + size_t reply_size; + uint64_t op_id; + uint32_t slave_id; + + msg = (struct GNUNET_TESTBED_SlaveGetConfigurationMessage *) message; + slave_id = ntohl (msg->slave_id); + op_id = GNUNET_ntohll (msg->operation_id); + if ((GST_slave_list_size <= slave_id) || (NULL == GST_slave_list[slave_id])) + { + /* FIXME: Add forwardings for this type of message here.. */ + GST_send_operation_fail_msg (client, op_id, "Slave not found"); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + slave = GST_slave_list[slave_id]; + if (NULL == slave->cfg) + { + GST_send_operation_fail_msg (client, op_id, + "Configuration not found (slave not started by me)"); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + config = GNUNET_CONFIGURATION_serialize (slave->cfg, &config_size); + xconfig_size = + GNUNET_TESTBED_compress_config_ (config, config_size, &xconfig); + GNUNET_free (config); + reply_size = xconfig_size + sizeof (struct GNUNET_TESTBED_SlaveConfiguration); + GNUNET_break (reply_size <= UINT16_MAX); + GNUNET_break (config_size <= UINT16_MAX); + reply = GNUNET_realloc (xconfig, reply_size); + (void) memmove (&reply[1], reply, xconfig_size); + reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_SLAVE_CONFIGURATION); + reply->header.size = htons ((uint16_t) reply_size); + reply->slave_id = msg->slave_id; + reply->operation_id = msg->operation_id; + reply->config_size = htons ((uint16_t) config_size); + GST_queue_message (client, &reply->header); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Iterator over hash map entries. + * + * @param cls closure + * @param key current key code + * @param value value in the hash map + * @return GNUNET_YES if we should continue to + * iterate, + * GNUNET_NO if not. + */ +static int +ss_map_free_iterator (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct SharedService *ss = value; + + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (ss_map, key, value)); + GNUNET_free (ss->name); + GNUNET_free (ss); + return GNUNET_YES; +} + + +/** + * Iterator for freeing hash map entries in a slave's reghost_map + * + * @param cls handle to the slave + * @param key current key code + * @param value value in the hash map + * @return GNUNET_YES if we should continue to + * iterate, + * GNUNET_NO if not. + */ +static int +reghost_free_iterator (void *cls, const struct GNUNET_HashCode *key, + void *value) +{ + struct Slave *slave = cls; + struct RegisteredHostContext *rhc = value; + struct ForwardedOverlayConnectContext *focc; + + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (slave->reghost_map, key, + value)); + while (NULL != (focc = rhc->focc_dll_head)) + { + GNUNET_CONTAINER_DLL_remove (rhc->focc_dll_head, rhc->focc_dll_tail, focc); + GST_cleanup_focc (focc); + } + if (NULL != rhc->sub_op) + GNUNET_TESTBED_operation_done (rhc->sub_op); + if (NULL != rhc->client) + GNUNET_SERVER_client_drop (rhc->client); + GNUNET_free (value); + return GNUNET_YES; +} + + +/** + * Task to clean up and shutdown nicely + * + * @param cls NULL + * @param tc the TaskContext from scheduler + */ +static void +shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct LCFContextQueue *lcfq; + struct ForwardedOperationContext *fopc; + struct MessageQueue *mq_entry; + uint32_t id; + + shutdown_task_id = GNUNET_SCHEDULER_NO_TASK; + LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down testbed service\n"); + (void) GNUNET_CONTAINER_multihashmap_iterate (ss_map, &ss_map_free_iterator, + NULL); + GNUNET_CONTAINER_multihashmap_destroy (ss_map); + /* cleanup any remaining forwarded operations */ + while (NULL != (fopc = fopcq_head)) + { + GNUNET_CONTAINER_DLL_remove (fopcq_head, fopcq_tail, fopc); + GNUNET_TESTBED_forward_operation_msg_cancel_ (fopc->opc); + if (GNUNET_SCHEDULER_NO_TASK != fopc->timeout_task) + GNUNET_SCHEDULER_cancel (fopc->timeout_task); + GNUNET_SERVER_client_drop (fopc->client); + switch (fopc->type) + { + case OP_PEER_CREATE: + GNUNET_free (fopc->cls); + break; + case OP_PEER_START: + case OP_PEER_STOP: + case OP_PEER_DESTROY: + case OP_PEER_INFO: + case OP_OVERLAY_CONNECT: + case OP_LINK_CONTROLLERS: + case OP_GET_SLAVE_CONFIG: + break; + case OP_FORWARDED: + GNUNET_assert (0); + }; + GNUNET_free (fopc); + } + if (NULL != lcfq_head) + { + if (GNUNET_SCHEDULER_NO_TASK != lcf_proc_task_id) + { + GNUNET_SCHEDULER_cancel (lcf_proc_task_id); + lcf_proc_task_id = GNUNET_SCHEDULER_NO_TASK; + } + } + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == lcf_proc_task_id); + for (lcfq = lcfq_head; NULL != lcfq; lcfq = lcfq_head) + { + GNUNET_free (lcfq->lcf->msg); + GNUNET_free (lcfq->lcf); + GNUNET_CONTAINER_DLL_remove (lcfq_head, lcfq_tail, lcfq); + GNUNET_free (lcfq); + } + GST_free_occq (); + GST_free_roccq (); + /* Clear peer list */ + for (id = 0; id < GST_peer_list_size; id++) + if (NULL != GST_peer_list[id]) + { + /* If destroy flag is set it means that this peer should have been + * destroyed by a context which we destroy before */ + GNUNET_break (GNUNET_NO == GST_peer_list[id]->destroy_flag); + /* counter should be zero as we free all contexts before */ + GNUNET_break (0 == GST_peer_list[id]->reference_cnt); + if ((GNUNET_NO == GST_peer_list[id]->is_remote) && + (GNUNET_YES == GST_peer_list[id]->details.local.is_running)) + GNUNET_TESTING_peer_kill (GST_peer_list[id]->details.local.peer); + } + for (id = 0; id < GST_peer_list_size; id++) + if (NULL != GST_peer_list[id]) + { + if (GNUNET_NO == GST_peer_list[id]->is_remote) + { + if (GNUNET_YES == GST_peer_list[id]->details.local.is_running) + GNUNET_TESTING_peer_wait (GST_peer_list[id]->details.local.peer); + GNUNET_TESTING_peer_destroy (GST_peer_list[id]->details.local.peer); + GNUNET_CONFIGURATION_destroy (GST_peer_list[id]->details.local.cfg); + } + GNUNET_free (GST_peer_list[id]); + } + GNUNET_free_non_null (GST_peer_list); + /* Clear host list */ + for (id = 0; id < GST_host_list_size; id++) + if (NULL != GST_host_list[id]) + GNUNET_TESTBED_host_destroy (GST_host_list[id]); + GNUNET_free_non_null (GST_host_list); + /* Clear route list */ + for (id = 0; id < route_list_size; id++) + if (NULL != route_list[id]) + GNUNET_free (route_list[id]); + GNUNET_free_non_null (route_list); + /* Clear GST_slave_list */ + for (id = 0; id < GST_slave_list_size; id++) + if (NULL != GST_slave_list[id]) + { + struct HostRegistration *hr_entry; + + while (NULL != (hr_entry = GST_slave_list[id]->hr_dll_head)) + { + GNUNET_CONTAINER_DLL_remove (GST_slave_list[id]->hr_dll_head, + GST_slave_list[id]->hr_dll_tail, hr_entry); + GNUNET_free (hr_entry); + } + if (NULL != GST_slave_list[id]->rhandle) + GNUNET_TESTBED_cancel_registration (GST_slave_list[id]->rhandle); + (void) + GNUNET_CONTAINER_multihashmap_iterate (GST_slave_list + [id]->reghost_map, + reghost_free_iterator, + GST_slave_list[id]); + GNUNET_CONTAINER_multihashmap_destroy (GST_slave_list[id]->reghost_map); + if (NULL != GST_slave_list[id]->cfg) + GNUNET_CONFIGURATION_destroy (GST_slave_list[id]->cfg); + if (NULL != GST_slave_list[id]->controller) + GNUNET_TESTBED_controller_disconnect (GST_slave_list[id]->controller); + if (NULL != GST_slave_list[id]->controller_proc) + GNUNET_TESTBED_controller_stop (GST_slave_list[id]->controller_proc); + GNUNET_free (GST_slave_list[id]); + } + GNUNET_free_non_null (GST_slave_list); + if (NULL != GST_context) + { + GNUNET_free_non_null (GST_context->master_ip); + if (NULL != GST_context->system) + GNUNET_TESTING_system_destroy (GST_context->system, GNUNET_YES); + GNUNET_SERVER_client_drop (GST_context->client); + GNUNET_free (GST_context); + GST_context = NULL; + } + if (NULL != transmit_handle) + GNUNET_SERVER_notify_transmit_ready_cancel (transmit_handle); + while (NULL != (mq_entry = mq_head)) + { + GNUNET_free (mq_entry->msg); + GNUNET_SERVER_client_drop (mq_entry->client); + GNUNET_CONTAINER_DLL_remove (mq_head, mq_tail, mq_entry); + GNUNET_free (mq_entry); + } + GNUNET_free_non_null (hostname); + GNUNET_CONFIGURATION_destroy (our_config); + /* Free hello cache */ + GST_cache_clear (); + GNUNET_TESTBED_operation_queue_destroy_ (GST_opq_openfds); + GST_opq_openfds = NULL; +} + + +/** + * Callback for client disconnect + * + * @param cls NULL + * @param client the client which has disconnected + */ +static void +client_disconnect_cb (void *cls, struct GNUNET_SERVER_Client *client) +{ + if (NULL == GST_context) + return; + if (client == GST_context->client) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "Master client disconnected\n"); + /* should not be needed as we're terminated by failure to read + * from stdin, but if stdin fails for some reason, this shouldn't + * hurt for now --- might need to revise this later if we ever + * decide that master connections might be temporarily down + * for some reason */ + //GNUNET_SCHEDULER_shutdown (); + } +} + + +/** + * Testbed setup + * + * @param cls closure + * @param server the initialized server + * @param cfg configuration to use + */ +static void +testbed_run (void *cls, struct GNUNET_SERVER_Handle *server, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + static const struct GNUNET_SERVER_MessageHandler message_handlers[] = { + {&handle_init, NULL, GNUNET_MESSAGE_TYPE_TESTBED_INIT, 0}, + {&handle_add_host, NULL, GNUNET_MESSAGE_TYPE_TESTBED_ADD_HOST, 0}, + {&handle_configure_shared_service, NULL, + GNUNET_MESSAGE_TYPE_TESTBED_SHARE_SERVICE, 0}, + {&handle_link_controllers, NULL, + GNUNET_MESSAGE_TYPE_TESTBED_LINK_CONTROLLERS, 0}, + {&handle_peer_create, NULL, GNUNET_MESSAGE_TYPE_TESTBED_CREATE_PEER, 0}, + {&handle_peer_destroy, NULL, GNUNET_MESSAGE_TYPE_TESTBED_DESTROY_PEER, + sizeof (struct GNUNET_TESTBED_PeerDestroyMessage)}, + {&handle_peer_start, NULL, GNUNET_MESSAGE_TYPE_TESTBED_START_PEER, + sizeof (struct GNUNET_TESTBED_PeerStartMessage)}, + {&handle_peer_stop, NULL, GNUNET_MESSAGE_TYPE_TESTBED_STOP_PEER, + sizeof (struct GNUNET_TESTBED_PeerStopMessage)}, + {&handle_peer_get_config, NULL, + GNUNET_MESSAGE_TYPE_TESTBED_GET_PEER_CONFIGURATION, + sizeof (struct GNUNET_TESTBED_PeerGetConfigurationMessage)}, + {&GST_handle_overlay_connect, NULL, + GNUNET_MESSAGE_TYPE_TESTBED_OVERLAY_CONNECT, + sizeof (struct GNUNET_TESTBED_OverlayConnectMessage)}, + {&GST_handle_remote_overlay_connect, NULL, + GNUNET_MESSAGE_TYPE_TESTBED_REMOTE_OVERLAY_CONNECT, 0}, + {handle_slave_get_config, NULL, + GNUNET_MESSAGE_TYPE_TESTBED_GET_SLAVE_CONFIGURATION, + sizeof (struct GNUNET_TESTBED_SlaveGetConfigurationMessage)}, + {NULL} + }; + char *logfile; + unsigned long long num; + + if (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_filename (cfg, "TESTBED", "LOG_FILE", + &logfile)) + { + GNUNET_break (GNUNET_OK == GNUNET_log_setup ("testbed", "DEBUG", logfile)); + GNUNET_free (logfile); + } + GNUNET_assert (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_number (cfg, "TESTBED", + "CACHE_SIZE", &num)); + GST_cache_init ((unsigned int) num); + GNUNET_assert (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_number (cfg, "TESTBED", + "MAX_OPEN_FDS", &num)); + GST_opq_openfds = GNUNET_TESTBED_operation_queue_create_ ((unsigned int) num); + GNUNET_assert (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (cfg, "testbed", + "HOSTNAME", &hostname)); + our_config = GNUNET_CONFIGURATION_dup (cfg); + GNUNET_SERVER_add_handlers (server, message_handlers); + GNUNET_SERVER_disconnect_notify (server, &client_disconnect_cb, NULL); + ss_map = GNUNET_CONTAINER_multihashmap_create (5, GNUNET_NO); + shutdown_task_id = + GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_UNIT_FOREVER_REL, + GNUNET_SCHEDULER_PRIORITY_IDLE, + &shutdown_task, NULL); + LOG_DEBUG ("Testbed startup complete\n"); + event_mask = 1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED; +} + + +/** + * The starting point of execution + */ +int +main (int argc, char *const *argv) +{ + //sleep (15); /* Debugging */ + return (GNUNET_OK == + GNUNET_SERVICE_run (argc, argv, "testbed", GNUNET_SERVICE_OPTION_NONE, + &testbed_run, NULL)) ? 0 : 1; +} + +/* end of gnunet-service-testbed.c */ diff --git a/src/testbed/gnunet-service-testbed.h b/src/testbed/gnunet-service-testbed.h new file mode 100644 index 0000000..ebf3c66 --- /dev/null +++ b/src/testbed/gnunet-service-testbed.h @@ -0,0 +1,911 @@ +/* + This file is part of GNUnet. + (C) 2012 Christian Grothoff (and other contributing authors) + + GNUnet 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 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file testbed/gnunet-service-testbed.h + * @brief data structures shared amongst components of TESTBED service + * @author Sree Harsha Totakura + */ + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testbed_service.h" +#include "gnunet_transport_service.h" +#include "gnunet_core_service.h" + +#include "testbed.h" +#include "testbed_api.h" +#include "testbed_api_operations.h" +#include "testbed_api_hosts.h" +#include "gnunet_testing_lib.h" + + +/** + * Generic logging + */ +#define LOG(kind,...) \ + GNUNET_log (kind, __VA_ARGS__) + +/** + * Debug logging + */ +#define LOG_DEBUG(...) \ + LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__) + +/** + * By how much should the arrays lists grow + */ +#define LIST_GROW_STEP 10 + +/** + * Default timeout for operations which may take some time + */ +#define TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 15) + + +/** + * A routing entry + */ +struct Route +{ + /** + * destination host + */ + uint32_t dest; + + /** + * The destination host is reachable thru + */ + uint32_t thru; +}; + + +/** + * Context information for operations forwarded to subcontrollers + */ +struct ForwardedOperationContext +{ + /** + * The next pointer for DLL + */ + struct ForwardedOperationContext *next; + + /** + * The prev pointer for DLL + */ + struct ForwardedOperationContext *prev; + + /** + * The generated operation context + */ + struct OperationContext *opc; + + /** + * The client to which we have to reply + */ + struct GNUNET_SERVER_Client *client; + + /** + * Closure pointer + */ + void *cls; + + /** + * Task ID for the timeout task + */ + GNUNET_SCHEDULER_TaskIdentifier timeout_task; + + /** + * The id of the operation that has been forwarded + */ + uint64_t operation_id; + + /** + * The type of the operation which is forwarded + */ + enum OperationType type; + +}; + + +/** + * A DLL of host registrations to be made + */ +struct HostRegistration +{ + /** + * next registration in the DLL + */ + struct HostRegistration *next; + + /** + * previous registration in the DLL + */ + struct HostRegistration *prev; + + /** + * The callback to call after this registration's status is available + */ + GNUNET_TESTBED_HostRegistrationCompletion cb; + + /** + * The closure for the above callback + */ + void *cb_cls; + + /** + * The host that has to be registered + */ + struct GNUNET_TESTBED_Host *host; +}; + + +/** + * Context information used while linking controllers + */ +struct LinkControllersContext +{ + /** + * The client which initiated the link controller operation + */ + struct GNUNET_SERVER_Client *client; + + /** + * The ID of the operation + */ + uint64_t operation_id; + +}; + + +/** + * Structure representing a connected(directly-linked) controller + */ +struct Slave +{ + /** + * The controller process handle if we had started the controller + */ + struct GNUNET_TESTBED_ControllerProc *controller_proc; + + /** + * The controller handle + */ + struct GNUNET_TESTBED_Controller *controller; + + /** + * The configuration of the slave. Cannot be NULL + */ + struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * handle to lcc which is associated with this slave startup. Should be set to + * NULL when the slave has successfully started up + */ + struct LinkControllersContext *lcc; + + /** + * Head of the host registration DLL + */ + struct HostRegistration *hr_dll_head; + + /** + * Tail of the host registration DLL + */ + struct HostRegistration *hr_dll_tail; + + /** + * The current host registration handle + */ + struct GNUNET_TESTBED_HostRegistrationHandle *rhandle; + + /** + * Hashmap to hold Registered host contexts + */ + struct GNUNET_CONTAINER_MultiHashMap *reghost_map; + + /** + * The id of the host this controller is running on + */ + uint32_t host_id; + +}; + + +/** + * A peer + */ + +struct Peer +{ + + union + { + struct + { + /** + * The peer handle from testing API + */ + struct GNUNET_TESTING_Peer *peer; + + /** + * The modified (by GNUNET_TESTING_peer_configure) configuration this + * peer is configured with + */ + struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Is the peer running + */ + int is_running; + + } local; + + struct + { + /** + * The slave this peer is started through + */ + struct Slave *slave; + + /** + * The id of the remote host this peer is running on + */ + uint32_t remote_host_id; + + } remote; + + } details; + + /** + * Is this peer locally created? + */ + int is_remote; + + /** + * Our local reference id for this peer + */ + uint32_t id; + + /** + * References to peers are using in forwarded overlay contexts and remote + * overlay connect contexts. A peer can only be destroyed after all such + * contexts are destroyed. For this, we maintain a reference counter. When we + * use a peer in any such context, we increment this counter. We decrement it + * when we are destroying these contexts + */ + uint32_t reference_cnt; + + /** + * While destroying a peer, due to the fact that there could be references to + * this peer, we delay the peer destroy to a further time. We do this by using + * this flag to destroy the peer while destroying a context in which this peer + * has been used. When the flag is set to 1 and reference_cnt = 0 we destroy + * the peer + */ + uint32_t destroy_flag; + +}; + + +/** + * The main context information associated with the client which started us + */ +struct Context +{ + /** + * The client handle associated with this context + */ + struct GNUNET_SERVER_Client *client; + + /** + * The network address of the master controller + */ + char *master_ip; + + /** + * The TESTING system handle for starting peers locally + */ + struct GNUNET_TESTING_System *system; + + /** + * Our host id according to this context + */ + uint32_t host_id; +}; + + +/** + * The structure for identifying a shared service + */ +struct SharedService +{ + /** + * The name of the shared service + */ + char *name; + + /** + * Number of shared peers per instance of the shared service + */ + uint32_t num_shared; + + /** + * Number of peers currently sharing the service + */ + uint32_t num_sharing; +}; + + +/** + * Context information to used during operations which forward the overlay + * connect message + */ +struct ForwardedOverlayConnectContext +{ + /** + * next ForwardedOverlayConnectContext in the DLL + */ + struct ForwardedOverlayConnectContext *next; + + /** + * previous ForwardedOverlayConnectContext in the DLL + */ + struct ForwardedOverlayConnectContext *prev; + + /** + * A copy of the original overlay connect message + */ + struct GNUNET_MessageHeader *orig_msg; + + /** + * The id of the operation which created this context information + */ + uint64_t operation_id; + + /** + * the id of peer 1 + */ + uint32_t peer1; + + /** + * The id of peer 2 + */ + uint32_t peer2; + + /** + * Id of the host where peer2 is running + */ + uint32_t peer2_host_id; +}; + + +/** + * This context information will be created for each host that is registered at + * slave controllers during overlay connects. + */ +struct RegisteredHostContext +{ + /** + * The host which is being registered + */ + struct GNUNET_TESTBED_Host *reg_host; + + /** + * The host of the controller which has to connect to the above rhost + */ + struct GNUNET_TESTBED_Host *host; + + /** + * The gateway to which this operation is forwarded to + */ + struct Slave *gateway; + + /** + * The gateway through which peer2's controller can be reached + */ + struct Slave *gateway2; + + /** + * Handle for sub-operations + */ + struct GNUNET_TESTBED_Operation *sub_op; + + /** + * The client which initiated the link controller operation + */ + struct GNUNET_SERVER_Client *client; + + /** + * Head of the ForwardedOverlayConnectContext DLL + */ + struct ForwardedOverlayConnectContext *focc_dll_head; + + /** + * Tail of the ForwardedOverlayConnectContext DLL + */ + struct ForwardedOverlayConnectContext *focc_dll_tail; + + /** + * Enumeration of states for this context + */ + enum RHCState + { + + /** + * The initial state + */ + RHC_INIT = 0, + + /** + * State where we attempt to get peer2's controller configuration + */ + RHC_GET_CFG, + + /** + * State where we attempt to link the controller of peer 1 to the controller + * of peer2 + */ + RHC_LINK, + + /** + * State where we attempt to do the overlay connection again + */ + RHC_OL_CONNECT + } state; + +}; + + +/** + * States of LCFContext + */ +enum LCFContextState +{ + /** + * The Context has been initialized; Nothing has been done on it + */ + INIT, + + /** + * Delegated host has been registered at the forwarding controller + */ + DELEGATED_HOST_REGISTERED, + + /** + * The slave host has been registred at the forwarding controller + */ + SLAVE_HOST_REGISTERED, + + /** + * The context has been finished (may have error) + */ + FINISHED +}; + + +/** + * Link controllers request forwarding context + */ +struct LCFContext +{ + /** + * The gateway which will pass the link message to delegated host + */ + struct Slave *gateway; + + /** + * The controller link message that has to be forwarded to + */ + struct GNUNET_TESTBED_ControllerLinkMessage *msg; + + /** + * The client which has asked to perform this operation + */ + struct GNUNET_SERVER_Client *client; + + /** + * Handle for operations which are forwarded while linking controllers + */ + struct ForwardedOperationContext *fopc; + + /** + * The id of the operation which created this context + */ + uint64_t operation_id; + + /** + * The state of this context + */ + enum LCFContextState state; + + /** + * The delegated host + */ + uint32_t delegated_host_id; + + /** + * The slave host + */ + uint32_t slave_host_id; + +}; + + +/** + * Structure of a queue entry in LCFContext request queue + */ +struct LCFContextQueue +{ + /** + * The LCFContext + */ + struct LCFContext *lcf; + + /** + * Head prt for DLL + */ + struct LCFContextQueue *next; + + /** + * Tail ptr for DLL + */ + struct LCFContextQueue *prev; +}; + +/** + * Our configuration + */ +struct GNUNET_CONFIGURATION_Handle *our_config; + +/** + * The master context; generated with the first INIT message + */ +extern struct Context *GST_context; + +/** + * DLL head for forwarded operation contexts + */ +extern struct ForwardedOperationContext *fopcq_head; + +/** + * DLL tail for forwarded operation contexts + */ +extern struct ForwardedOperationContext *fopcq_tail; + +/** + * A list of peers we know about + */ +extern struct Peer **GST_peer_list; + +/** + * Array of hosts + */ +extern struct GNUNET_TESTBED_Host **GST_host_list; + +/** + * A list of directly linked neighbours + */ +extern struct Slave **GST_slave_list; + +/** + * Operation queue for open file descriptors + */ +extern struct OperationQueue *GST_opq_openfds; + +/** + * The size of the peer list + */ +extern unsigned int GST_peer_list_size; + +/** + * The size of the host list + */ +extern unsigned int GST_host_list_size; + +/** + * The size of directly linked neighbours list + */ +extern unsigned int GST_slave_list_size; + + +/** + * Queues a message in send queue for sending to the service + * + * @param client the client to whom the queued message has to be sent + * @param msg the message to queue + */ +void +GST_queue_message (struct GNUNET_SERVER_Client *client, + struct GNUNET_MessageHeader *msg); + + +/** + * Function to destroy a peer + * + * @param peer the peer structure to destroy + */ +void +GST_destroy_peer (struct Peer *peer); + + +/** + * Finds the route with directly connected host as destination through which + * the destination host can be reached + * + * @param host_id the id of the destination host + * @return the route with directly connected destination host; NULL if no route + * is found + */ +struct Route * +GST_find_dest_route (uint32_t host_id); + + +/** + * Handler for GNUNET_MESSAGE_TYPE_TESTBED_OLCONNECT messages + * + * @param cls NULL + * @param client identification of the client + * @param message the actual message + */ +void +GST_handle_overlay_connect (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message); + + +/** + * Adds a host registration's request to a slave's registration queue + * + * @param slave the slave controller at which the given host has to be + * registered + * @param cb the host registration completion callback + * @param cb_cls the closure for the host registration completion callback + * @param host the host which has to be registered + */ +void +GST_queue_host_registration (struct Slave *slave, + GNUNET_TESTBED_HostRegistrationCompletion cb, + void *cb_cls, struct GNUNET_TESTBED_Host *host); + + +/** + * Callback to relay the reply msg of a forwarded operation back to the client + * + * @param cls ForwardedOperationContext + * @param msg the message to relay + */ +void +GST_forwarded_operation_reply_relay (void *cls, + const struct GNUNET_MessageHeader *msg); + + +/** + * Task to free resources when forwarded operation has been timedout + * + * @param cls the ForwardedOperationContext + * @param tc the task context from scheduler + */ +void +GST_forwarded_operation_timeout (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc); + + +/** + * Send operation failure message to client + * + * @param client the client to which the failure message has to be sent to + * @param operation_id the id of the failed operation + * @param emsg the error message; can be NULL + */ +void +GST_send_operation_fail_msg (struct GNUNET_SERVER_Client *client, + uint64_t operation_id, const char *emsg); + + +/** + * Handler for GNUNET_MESSAGE_TYPE_TESTBED_REQUESTCONNECT messages + * + * @param cls NULL + * @param client identification of the client + * @param message the actual message + */ +void +GST_handle_remote_overlay_connect (void *cls, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message); + + +/** + * Processes a forwarded overlay connect context in the queue of the given RegisteredHostContext + * + * @param rhc the RegisteredHostContext + */ +void +GST_process_next_focc (struct RegisteredHostContext *rhc); + + +/** + * Cleans up ForwardedOverlayConnectContext + * + * @param focc the ForwardedOverlayConnectContext to cleanup + */ +void +GST_cleanup_focc (struct ForwardedOverlayConnectContext *focc); + + +/** + * Clears all pending overlay connect contexts in queue + */ +void +GST_free_occq (); + + +/** + * Clears all pending remote overlay connect contexts in queue + */ +void +GST_free_roccq (); + + +/** + * Initializes the cache + * + * @param size the size of the cache + */ +void +GST_cache_init (unsigned int size); + + +/** + * Clear cache + */ +void +GST_cache_clear (); + + +/** + * Looks up in the hello cache and returns the HELLO of the given peer + * + * @param peer_id the index of the peer whose HELLO has to be looked up + * @return the HELLO message; NULL if not found + */ +const struct GNUNET_MessageHeader * +GST_cache_lookup_hello (const unsigned int peer_id); + + +/** + * Caches the HELLO of the given peer. Updates the HELLO if it was already + * cached before + * + * @param id the peer identity of the peer whose HELLO has to be cached + * @param hello the HELLO message + */ +void +GST_cache_add_hello (const unsigned int peer_id, + const struct GNUNET_MessageHeader *hello); + + +/** + * Functions of this type are called when the needed handle is available for + * usage. These functions are to be registered with either of the functions + * GST_cache_get_handle_transport() or GST_cache_get_handle_core(). The + * corresponding handles will be set and if they are not, then it signals an + * error while opening the handles. + * + * @param cls the closure passed to GST_cache_get_handle_transport() or + * GST_cache_get_handle_core() + * @param ch the handle to CORE. Can be NULL if it is not requested + * @param th the handle to TRANSPORT. Can be NULL if it is not requested + * @param peer_id the identity of the peer. Will be NULL if ch is NULL. In other + * cases, its value being NULL means that CORE connection has failed. + */ +typedef void (*GST_cache_handle_ready_cb) (void *cls, + struct GNUNET_CORE_Handle * ch, + struct GNUNET_TRANSPORT_Handle * th, + const struct GNUNET_PeerIdentity * + peer_id); + + +/** + * Callback to notify when the target peer given to + * GST_cache_get_handle_transport() is connected. Note that this callback may + * not be called if the target peer is already connected. Use + * GNUNET_TRANSPORT_check_neighbour_connected() to check if the target peer is + * already connected or not. This callback will be called only once or never (in + * case the target cannot be connected). + * + * @param cls the closure given to GST_cache_get_handle_done() for this callback + * @param target the peer identity of the target peer. The pointer should be + * valid until GST_cache_get_handle_done() is called. + */ +typedef void (*GST_cache_peer_connect_notify) (void *cls, + const struct GNUNET_PeerIdentity + * target); + + +/** + * Get a transport handle with the given configuration. If the handle is already + * cached before, it will be retured in the given callback; the peer_id is used to lookup in the + * cache. If not a new operation is started to open the transport handle and + * will be given in the callback when it is available. + * + * @param peer_id the index of the peer + * @param cfg the configuration with which the transport handle has to be + * created if it was not present in the cache + * @param cb the callback to notify when the transport handle is available + * @param cb_cls the closure for the above callback + * @param target the peer identify of the peer whose connection to our TRANSPORT + * subsystem will be notified through the connect_notify_cb. Can be NULL + * @param connect_notify_cb the callback to call when the given target peer is + * connected. This callback will only be called once or never again (in + * case the target peer cannot be connected). Can be NULL + * @param connect_notify_cb_cls the closure for the above callback + * @return the handle which can be used cancel or mark that the handle is no + * longer being used + */ +struct GSTCacheGetHandle * +GST_cache_get_handle_transport (unsigned int peer_id, + const struct GNUNET_CONFIGURATION_Handle *cfg, + GST_cache_handle_ready_cb cb, void *cb_cls, + const struct GNUNET_PeerIdentity *target, + GST_cache_peer_connect_notify connect_notify_cb, + void *connect_notify_cb_cls); + + +/** + * Get a CORE handle with the given configuration. If the handle is already + * cached before, it will be retured in the given callback; the peer_id is used + * to lookup in the cache. If the handle is not cached before, a new operation + * is started to open the CORE handle and will be given in the callback when it + * is available along with the peer identity + * + * @param peer_id the index of the peer + * @param cfg the configuration with which the transport handle has to be + * created if it was not present in the cache + * @param cb the callback to notify when the transport handle is available + * @param cb_cls the closure for the above callback + * @param target the peer identify of the peer whose connection to our CORE + * subsystem will be notified through the connect_notify_cb. Can be NULL + * @param connect_notify_cb the callback to call when the given target peer is + * connected. This callback will only be called once or never again (in + * case the target peer cannot be connected). Can be NULL + * @param connect_notify_cb_cls the closure for the above callback + * @return the handle which can be used cancel or mark that the handle is no + * longer being used + */ +struct GSTCacheGetHandle * +GST_cache_get_handle_core (unsigned int peer_id, + const struct GNUNET_CONFIGURATION_Handle *cfg, + GST_cache_handle_ready_cb cb, void *cb_cls, + const struct GNUNET_PeerIdentity *target, + GST_cache_peer_connect_notify connect_notify_cb, + void *connect_notify_cb_cls); + + +/** + * Mark the GetCacheHandle as being done if a handle has been provided already + * or as being cancelled if the callback for the handle hasn't been called. + * + * @param cgh the CacheGetHandle handle + */ +void +GST_cache_get_handle_done (struct GSTCacheGetHandle *cgh); + +/* End of gnunet-service-testbed.h */ diff --git a/src/testbed/gnunet-service-testbed_cache.c b/src/testbed/gnunet-service-testbed_cache.c new file mode 100644 index 0000000..112868e --- /dev/null +++ b/src/testbed/gnunet-service-testbed_cache.c @@ -0,0 +1,1048 @@ +/* + This file is part of GNUnet. + (C) 2012 Christian Grothoff (and other contributing authors) + + GNUnet 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 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file testbed/gnunet-service-testbed_cache.h + * @brief testbed cache implementation + * @author Sree Harsha Totakura + */ +#include "gnunet-service-testbed.h" + +/** + * Redefine LOG with a changed log component string + */ +#ifdef LOG +#undef LOG +#endif +#define LOG(kind,...) \ + GNUNET_log_from (kind, "testbed-cache", __VA_ARGS__) + + +/** + * Time to expire a cache entry + */ +#define CACHE_EXPIRY \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15) + + +/** + * Type of cache-get requests + */ +enum CacheGetType +{ + /** + * Get transport handle + */ + CGT_TRANSPORT_HANDLE = 1, + + /** + * Get core handle + */ + CGT_CORE_HANDLE +}; + + +/** + * The cache-get request handle + */ +struct GSTCacheGetHandle; + + +/** + * This context structure is used to maintain a queue of notifications to check + * which of them are to be notified when a peer is connected. + */ +struct ConnectNotifyContext +{ + /** + * The next ptr for the DLL + */ + struct ConnectNotifyContext *next; + + /** + * The prev ptr for the DLL + */ + struct ConnectNotifyContext *prev; + + /** + * The peer identity of the target peer. When this target peer is connected, + * call the notify callback + */ + const struct GNUNET_PeerIdentity *target; + + /** + * The notify callback to be called when the target peer is connected + */ + GST_cache_peer_connect_notify cb; + + /** + * The closure for the notify callback + */ + void *cb_cls; + + /** + * The GSTCacheGetHandle reposible for creating this context + */ + struct GSTCacheGetHandle *cgh; + +}; + + +/** + * The cache-get request handle + */ +struct GSTCacheGetHandle +{ + /** + * The next ptr for the DLL. Used in struct CacheEntry + */ + struct GSTCacheGetHandle *next; + + /** + * The prev ptr for the DLL. Used in struct CacheEntry + */ + struct GSTCacheGetHandle *prev; + + /** + * The cache entry object this handle corresponds to + */ + struct CacheEntry *entry; + + /** + * The cache callback to call when a handle is available + */ + GST_cache_handle_ready_cb cb; + + /** + * The closure for the above callback + */ + void *cb_cls; + + /** + * The peer connect notify context created for this handle; can be NULL + */ + struct ConnectNotifyContext *nctxt; + + /** + * The type of this cache-get request + */ + enum CacheGetType type; + + /** + * Did we call the cache callback already? + */ + int notify_called; +}; + +/** + * Cache entry + */ +struct CacheEntry +{ + /** + * DLL next ptr for least recently used cache entries + */ + struct CacheEntry *next; + + /** + * DLL prev ptr for least recently used cache entries + */ + struct CacheEntry *prev; + + /** + * The transport handle to the peer corresponding to this entry; can be NULL + */ + struct GNUNET_TRANSPORT_Handle *transport_handle_; + + /** + * The operation handle for transport handle + */ + struct GNUNET_TESTBED_Operation *transport_op_; + + /** + * The core handle to the peer corresponding to this entry; can be NULL + */ + struct GNUNET_CORE_Handle *core_handle; + + /** + * The operation handle for core handle + */ + struct GNUNET_TESTBED_Operation *core_op; + + /** + * The peer identity of this peer. Will be set upon opening a connection to + * the peers CORE service. Will be NULL until then and after the CORE + * connection is closed + */ + struct GNUNET_PeerIdentity *peer_identity; + + /** + * The configuration of the peer. Should be not NULL as long as the core_handle + * or transport_handle are valid + */ + struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * The key for this entry + */ + struct GNUNET_HashCode key; + + /** + * The HELLO message + */ + struct GNUNET_MessageHeader *hello; + + /** + * the head of the CacheGetHandle queue + */ + struct GSTCacheGetHandle *cgh_qhead; + + /** + * the tail of the CacheGetHandle queue + */ + struct GSTCacheGetHandle *cgh_qtail; + + /** + * DLL head for the queue of notifications contexts to check which of them are to + * be notified when a peer is connected. + */ + struct ConnectNotifyContext *nctxt_qhead; + + /** + * DLL tail for the queue of notifications contexts to check which of them are to + * be notified when a peer is connected. + */ + struct ConnectNotifyContext *nctxt_qtail; + + /** + * The task that calls the cache callback + */ + GNUNET_SCHEDULER_TaskIdentifier notify_task; + + /** + * The task to expire this cache entry, free any handlers it has opened and + * mark their corresponding operations as done. + */ + GNUNET_SCHEDULER_TaskIdentifier expire_task; + + /** + * Number of operations this cache entry is being used + */ + unsigned int demand; + + /** + * The id of the peer this entry corresponds to + */ + unsigned int peer_id; + + /** + * Is this entry in LRU cache queue? + */ + unsigned int in_lru; +}; + + +/** + * Hashmap to maintain cache + */ +static struct GNUNET_CONTAINER_MultiHashMap *cache; + +/** + * DLL head for least recently used cache entries; least recently used + * cache items are at the head. The cache enties are added to this queue when + * their demand becomes zero. They are removed from the queue when they are + * needed by any operation. + */ +static struct CacheEntry *lru_cache_head; + +/** + * DLL tail for least recently used cache entries; recently used cache + * items are at the tail.The cache enties are added to this queue when + * their demand becomes zero. They are removed from the queue when they are + * needed by any operation. + */ +static struct CacheEntry *lru_cache_tail; + +/** + * the size of the LRU queue + */ +static unsigned int lru_cache_size; + +/** + * the threshold size for the LRU queue + */ +static unsigned int lru_cache_threshold_size; + +/** + * The total number of elements in cache + */ +static unsigned int cache_size; + + +/** + * Looks up in the cache and returns the entry + * + * @param id the peer identity of the peer whose corresponding entry has to be looked up + * @return the HELLO message; NULL if not found + */ +static struct CacheEntry * +cache_lookup (const struct GNUNET_HashCode *key) +{ + struct CacheEntry *entry; + + if (NULL == cache) + return NULL; + entry = GNUNET_CONTAINER_multihashmap_get (cache, key); + return entry; +} + + +/** + * Function to disconnect the core and transport handles; free the existing + * configuration; and remove from the LRU cache list. The entry is left to be in + * the hash table so that the HELLO can still be found later + * + * @param entry the cache entry + */ +static void +close_handles (struct CacheEntry *entry) +{ + struct ConnectNotifyContext *ctxt; + + GNUNET_assert (0 == entry->demand); + if (GNUNET_YES == entry->in_lru) + { + GNUNET_assert (0 < lru_cache_size); + if (GNUNET_SCHEDULER_NO_TASK != entry->expire_task) + { + GNUNET_SCHEDULER_cancel (entry->expire_task); + entry->expire_task = GNUNET_SCHEDULER_NO_TASK; + } + GNUNET_CONTAINER_DLL_remove (lru_cache_head, lru_cache_tail, entry); + lru_cache_size--; + entry->in_lru = GNUNET_NO; + } + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == entry->expire_task); + while (NULL != (ctxt = entry->nctxt_qhead)) + { + GNUNET_CONTAINER_DLL_remove (entry->nctxt_qhead, entry->nctxt_qtail, ctxt); + GNUNET_free (ctxt); + } + LOG_DEBUG ("Cleaning up handles from an entry in cache\n"); + if (NULL != entry->transport_handle_) + { + GNUNET_assert (NULL != entry->transport_op_); + GNUNET_TESTBED_operation_done (entry->transport_op_); + entry->transport_op_ = NULL; + } + if (NULL != entry->core_handle) + { + GNUNET_assert (NULL != entry->core_op); + GNUNET_TESTBED_operation_done (entry->core_op); + entry->core_op = NULL; + } + if (NULL != entry->cfg) + { + GNUNET_CONFIGURATION_destroy (entry->cfg); + entry->cfg = NULL; + } +} + + +/** + * The task to expire this cache entry, free any handlers it has opened and + * mark their corresponding operations as done. + * + * @param cls the CacheEntry + * @param tc the scheduler task context + */ +static void +expire_cache_entry (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct CacheEntry *entry = cls; + + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != entry->expire_task); + entry->expire_task = GNUNET_SCHEDULER_NO_TASK; + close_handles (entry); +} + + +/** + * Creates a new cache entry and then puts it into the cache's hashtable. + * + * @param key the hash code to use for inserting the newly created entry + * @param peer_id the index of the peer to tag the newly created entry + * @return the newly created entry + */ +static struct CacheEntry * +add_entry (const struct GNUNET_HashCode *key, unsigned int peer_id) +{ + struct CacheEntry *entry; + + entry = GNUNET_malloc (sizeof (struct CacheEntry)); + entry->peer_id = peer_id; + memcpy (&entry->key, key, sizeof (struct GNUNET_HashCode)); + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put (cache, &entry->key, entry, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)); + cache_size++; + return entry; +} + + +/** + * Function to find a suitable GSTCacheGetHandle which is waiting for one of the + * handles in given entry to be available. + * + * @param entry the cache entry whose GSTCacheGetHandle list has to be searched + * @param head the starting list element in the GSTCacheGetHandle where the + * search has to be begin + * @return a suitable GSTCacheGetHandle whose handle ready notify callback + * hasn't been called yet. NULL if no such suitable GSTCacheGetHandle + * is found + */ +static struct GSTCacheGetHandle * +search_suitable_cgh (const struct CacheEntry *entry, + const struct GSTCacheGetHandle *head) +{ + const struct GSTCacheGetHandle *cgh; + + for (cgh = head; NULL != cgh; cgh = cgh->next) + { + if (GNUNET_YES == cgh->notify_called) + return NULL; + switch (cgh->type) + { + case CGT_TRANSPORT_HANDLE: + if (NULL == entry->transport_handle_) + continue; + break; + case CGT_CORE_HANDLE: + if (NULL == entry->core_handle) + continue; + break; + } + break; + } + return (struct GSTCacheGetHandle *) cgh; +} + + +/** + * Task to call the handle ready notify callback of a queued GSTCacheGetHandle + * of an entry when one or all of its handles are available. + * + * @param cls the cache entry + * @param tc the task context from scheduler + */ +static void +call_cgh_cb (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct CacheEntry *entry = cls; + struct GSTCacheGetHandle *cgh; + const struct GSTCacheGetHandle *cgh2; + + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != entry->notify_task); + entry->notify_task = GNUNET_SCHEDULER_NO_TASK; + cgh = search_suitable_cgh (entry, entry->cgh_qhead); + GNUNET_assert (NULL != cgh); + cgh2 = NULL; + if (NULL != cgh->next) + cgh2 = search_suitable_cgh (entry, cgh->next); + GNUNET_CONTAINER_DLL_remove (entry->cgh_qhead, entry->cgh_qtail, cgh); + cgh->notify_called = GNUNET_YES; + GNUNET_CONTAINER_DLL_insert_tail (entry->cgh_qhead, entry->cgh_qtail, cgh); + if (NULL != cgh2) + entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry); + if (NULL != cgh->nctxt) + { /* Register the peer connect notify callback */ + GNUNET_CONTAINER_DLL_insert_tail (entry->nctxt_qhead, entry->nctxt_qtail, + cgh->nctxt); + } + LOG_DEBUG ("Calling notify for handle type %u\n", cgh->type); + cgh->cb (cgh->cb_cls, entry->core_handle, entry->transport_handle_, + entry->peer_identity); +} + + +/** + * Function called from peer connect notify callbacks from CORE and TRANSPORT + * connections. This function calls the pendning peer connect notify callbacks + * which are queued in an entry. + * + * @param cls the cache entry + * @param peer the peer that connected + * @param type the type of the handle this notification corresponds to + */ +static void +peer_connect_notify_cb (void *cls, const struct GNUNET_PeerIdentity *peer, + const enum CacheGetType type) +{ + struct CacheEntry *entry = cls; + struct ConnectNotifyContext *ctxt; + struct ConnectNotifyContext *ctxt2; + GST_cache_peer_connect_notify cb; + void *cb_cls; + + + for (ctxt = entry->nctxt_qhead; NULL != ctxt;) + { + GNUNET_assert (NULL != ctxt->cgh); + if (type != ctxt->cgh->type) + { + ctxt = ctxt->next; + continue; + } + if (0 != memcmp (ctxt->target, peer, sizeof (struct GNUNET_PeerIdentity))) + { + ctxt = ctxt->next; + continue; + } + cb = ctxt->cb; + cb_cls = ctxt->cb_cls; + ctxt->cgh->nctxt = NULL; + ctxt2 = ctxt->next; + GNUNET_CONTAINER_DLL_remove (entry->nctxt_qhead, entry->nctxt_qtail, ctxt); + GNUNET_free (ctxt); + ctxt = ctxt2; + cb (cb_cls, peer); + } + if (NULL == ctxt) + return; + +} + + +/** + * Function called to notify transport users that another + * peer connected to us. + * + * @param cls closure + * @param peer the peer that connected + * @param ats performance data + * @param ats_count number of entries in ats (excluding 0-termination) + */ +static void +transport_peer_connect_notify_cb (void *cls, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_ATS_Information *ats, + uint32_t ats_count) +{ + peer_connect_notify_cb (cls, peer, CGT_TRANSPORT_HANDLE); +} + + +/** + * Function called when resources for opening a connection to TRANSPORT are + * available. + * + * @param cls the cache entry + */ +static void +opstart_get_handle_transport (void *cls) +{ + struct CacheEntry *entry = cls; + + GNUNET_assert (NULL != entry); + LOG_DEBUG ("Opening a transport connection to peer %u\n", entry->peer_id); + entry->transport_handle_ = + GNUNET_TRANSPORT_connect (entry->cfg, NULL, entry, NULL, + &transport_peer_connect_notify_cb, NULL); + if (NULL == entry->transport_handle_) + { + GNUNET_break (0); + return; + } + if (0 == entry->demand) + return; + if (GNUNET_SCHEDULER_NO_TASK != entry->notify_task) + return; + if (NULL != search_suitable_cgh (entry, entry->cgh_qhead)) + entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry); +} + + +/** + * Function called when the operation responsible for opening a TRANSPORT + * connection is marked as done. + * + * @param cls the cache entry + */ +static void +oprelease_get_handle_transport (void *cls) +{ + struct CacheEntry *entry = cls; + + if (NULL == entry->transport_handle_) + return; + GNUNET_TRANSPORT_disconnect (entry->transport_handle_); + entry->transport_handle_ = NULL; +} + + +/** + * Function called after GNUNET_CORE_connect has succeeded (or failed + * for good). Note that the private key of the peer is intentionally + * not exposed here; if you need it, your process should try to read + * the private key file directly (which should work if you are + * authorized...). Implementations of this function must not call + * GNUNET_CORE_disconnect (other than by scheduling a new task to + * do this later). + * + * @param cls closure + * @param server handle to the server, NULL if we failed + * @param my_identity ID of this peer, NULL if we failed + */ +static void +core_startup_cb (void *cls, struct GNUNET_CORE_Handle *server, + const struct GNUNET_PeerIdentity *my_identity) +{ + struct CacheEntry *entry = cls; + + if (NULL == my_identity) + { + GNUNET_break (0); + return; + } + GNUNET_assert (NULL == entry->peer_identity); + entry->core_handle = server; + entry->peer_identity = GNUNET_malloc (sizeof (struct GNUNET_PeerIdentity)); + memcpy (entry->peer_identity, my_identity, + sizeof (struct GNUNET_PeerIdentity)); + if (0 == entry->demand) + return; + if (GNUNET_SCHEDULER_NO_TASK != entry->notify_task) + return; + if (NULL != search_suitable_cgh (entry, entry->cgh_qhead)) + entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry); +} + + +/** + * Method called whenever a given peer connects at CORE level + * + * @param cls closure + * @param peer peer identity this notification is about + * @param atsi performance data for the connection + * @param atsi_count number of records in 'atsi' + */ +static void +core_peer_connect_cb (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + peer_connect_notify_cb (cls, peer, CGT_CORE_HANDLE); +} + + +/** + * Function called when resources for opening a connection to CORE are + * available. + * + * @param cls the cache entry + */ +static void +opstart_get_handle_core (void *cls) +{ + struct CacheEntry *entry = cls; + + const struct GNUNET_CORE_MessageHandler no_handlers[] = { + {NULL, 0, 0} + }; + + GNUNET_assert (NULL != entry); + LOG_DEBUG ("Opening a CORE connection to peer %u\n", entry->peer_id); + /* void?: We also get the handle when the connection to CORE is successful */ + (void) GNUNET_CORE_connect (entry->cfg, entry, /* closure */ + &core_startup_cb, /* core startup notify */ + &core_peer_connect_cb, /* peer connect notify */ + NULL, /* peer disconnect notify */ + NULL, /* inbound notify */ + GNUNET_NO, /* inbound header only? */ + NULL, /* outbound notify */ + GNUNET_NO, /* outbound header only? */ + no_handlers); +} + + +/** + * Function called when the operation responsible for opening a TRANSPORT + * connection is marked as done. + * + * @param cls the cache entry + */ +static void +oprelease_get_handle_core (void *cls) +{ + struct CacheEntry *entry = cls; + + if (NULL == entry->core_handle) + return; + GNUNET_CORE_disconnect (entry->core_handle); + entry->core_handle = NULL; + GNUNET_free_non_null (entry->peer_identity); + entry->peer_identity = NULL; +} + + +/** + * Function to get a handle with given configuration. The type of the handle is + * implicitly provided in the GSTCacheGetHandle. If the handle is already cached + * before, it will be retured in the given callback; the peer_id is used to + * lookup in the cache; if not, a new operation is started to open the transport + * handle and will be given in the callback when it is available. + * + * @param cls the cache entry + */ +static struct GSTCacheGetHandle * +cache_get_handle (unsigned int peer_id, struct GSTCacheGetHandle *cgh, + const struct GNUNET_CONFIGURATION_Handle *cfg, + const struct GNUNET_PeerIdentity *target, + GST_cache_peer_connect_notify connect_notify_cb, + void *connect_notify_cb_cls) +{ + struct GNUNET_HashCode key; + void *handle; + struct CacheEntry *entry; + struct ConnectNotifyContext *ctxt; + struct GNUNET_TESTBED_Operation *op; + + GNUNET_assert (0 != cgh->type); + GNUNET_CRYPTO_hash (&peer_id, sizeof (peer_id), &key); + handle = NULL; + entry = cache_lookup (&key); + if (NULL != entry) + { + if (GNUNET_YES == entry->in_lru) + { + GNUNET_assert (0 == entry->demand); + GNUNET_assert (0 < lru_cache_size); + if (GNUNET_SCHEDULER_NO_TASK != entry->expire_task) + { + GNUNET_SCHEDULER_cancel (entry->expire_task); + entry->expire_task = GNUNET_SCHEDULER_NO_TASK; + } + GNUNET_CONTAINER_DLL_remove (lru_cache_head, lru_cache_tail, entry); + lru_cache_size--; + entry->in_lru = GNUNET_NO; + } + switch (cgh->type) + { + case CGT_TRANSPORT_HANDLE: + handle = entry->transport_handle_; + if (NULL != handle) + LOG_DEBUG ("Found TRANSPORT handle in cache for peer %u\n", + entry->peer_id); + break; + case CGT_CORE_HANDLE: + handle = entry->core_handle; + if (NULL != handle) + LOG_DEBUG ("Found CORE handle in cache for peer %u\n", entry->peer_id); + break; + } + } + if (NULL == entry) + entry = add_entry (&key, peer_id); + if (NULL == entry->cfg) + entry->cfg = GNUNET_CONFIGURATION_dup (cfg); + entry->demand++; + cgh->entry = entry; + GNUNET_CONTAINER_DLL_insert (entry->cgh_qhead, entry->cgh_qtail, cgh); + if ((NULL != target) && (NULL != connect_notify_cb)) + { + ctxt = GNUNET_malloc (sizeof (struct ConnectNotifyContext)); + ctxt->target = target; + ctxt->cb = connect_notify_cb; + ctxt->cb_cls = connect_notify_cb_cls; + GNUNET_assert (NULL == cgh->nctxt); + cgh->nctxt = ctxt; + ctxt->cgh = cgh; + } + if (NULL != handle) + { + if (GNUNET_SCHEDULER_NO_TASK == entry->notify_task) + entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry); + return cgh; + } + switch (cgh->type) + { + case CGT_TRANSPORT_HANDLE: + if (NULL != entry->transport_op_) + return cgh; + op = GNUNET_TESTBED_operation_create_ (entry, &opstart_get_handle_transport, + &oprelease_get_handle_transport); + entry->transport_op_ = op; + break; + case CGT_CORE_HANDLE: + if (NULL != entry->core_op) + return cgh; + op = GNUNET_TESTBED_operation_create_ (entry, &opstart_get_handle_core, + &oprelease_get_handle_core); + entry->core_op = op; + break; + } + GNUNET_TESTBED_operation_queue_insert_ (GST_opq_openfds, op); + GNUNET_TESTBED_operation_begin_wait_ (op); + return cgh; +} + + +/** + * Iterator over hash map entries. + * + * @param cls closure + * @param key current key code + * @param value value in the hash map + * @return GNUNET_YES if we should continue to + * iterate, + * GNUNET_NO if not. + */ +static int +cache_clear_iterator (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct CacheEntry *entry = value; + static unsigned int ncleared; + + GNUNET_assert (NULL != entry); + GNUNET_break (0 == entry->demand); + LOG_DEBUG ("Clearing entry %u of %u\n", ++ncleared, cache_size); + GNUNET_CONTAINER_multihashmap_remove (cache, key, value); + if (0 == entry->demand) + close_handles (entry); + GNUNET_free_non_null (entry->hello); + GNUNET_break (GNUNET_SCHEDULER_NO_TASK == entry->expire_task); + GNUNET_break (NULL == entry->transport_handle_); + GNUNET_break (NULL == entry->transport_op_); + GNUNET_break (NULL == entry->core_handle); + GNUNET_break (NULL == entry->core_op); + GNUNET_break (NULL == entry->cfg); + GNUNET_assert (NULL == entry->cgh_qhead); + GNUNET_assert (NULL == entry->cgh_qtail); + GNUNET_assert (NULL == entry->nctxt_qhead); + GNUNET_assert (NULL == entry->nctxt_qtail); + GNUNET_free (entry); + return GNUNET_YES; +} + + +/** + * Clear cache + */ +void +GST_cache_clear () +{ + GNUNET_CONTAINER_multihashmap_iterate (cache, &cache_clear_iterator, NULL); + GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap_size (cache)); + GNUNET_CONTAINER_multihashmap_destroy (cache); +} + + +/** + * Initializes the cache + * + * @param size the size of the cache + */ +void +GST_cache_init (unsigned int size) +{ + if (0 == size) + return; + lru_cache_threshold_size = size; + if (size > 1) + size = size / 2; + cache = GNUNET_CONTAINER_multihashmap_create (size, GNUNET_YES); +} + + +/** + * Mark the GetCacheHandle as being done if a handle has been provided already + * or as being cancelled if the callback for the handle hasn't been called. + * + * @param cgh the CacheGetHandle handle + */ +void +GST_cache_get_handle_done (struct GSTCacheGetHandle *cgh) +{ + struct CacheEntry *entry; + + entry = cgh->entry; + GNUNET_assert (NULL != entry); + GNUNET_assert (0 < entry->demand); + entry->demand--; + if (GNUNET_SCHEDULER_NO_TASK != entry->notify_task) + { + GNUNET_SCHEDULER_cancel (entry->notify_task); + entry->notify_task = GNUNET_SCHEDULER_NO_TASK; + } + GNUNET_CONTAINER_DLL_remove (entry->cgh_qhead, entry->cgh_qtail, cgh); + if (NULL != cgh->nctxt) + { + GNUNET_assert (cgh == cgh->nctxt->cgh); + if (GNUNET_YES == cgh->notify_called) + GNUNET_CONTAINER_DLL_remove (entry->nctxt_qhead, entry->nctxt_qtail, + cgh->nctxt); + GNUNET_free (cgh->nctxt); + } + GNUNET_free (cgh); + if (0 == entry->demand) + { + entry->expire_task = + GNUNET_SCHEDULER_add_delayed (CACHE_EXPIRY, &expire_cache_entry, entry); + GNUNET_CONTAINER_DLL_insert_tail (lru_cache_head, lru_cache_tail, entry); + lru_cache_size++; + entry->in_lru = GNUNET_YES; + if (lru_cache_size > lru_cache_threshold_size) + close_handles (lru_cache_head); + } + else + { + struct GSTCacheGetHandle *cgh2; + + if (NULL != (cgh2 = search_suitable_cgh (entry, entry->cgh_qhead))) + entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry); + } +} + + +/** + * Get a transport handle with the given configuration. If the handle is + * already cached before, it will be retured in the given callback; the peer_id + * is used to lookup in the cache; if not, a new operation is started to open the + * transport handle and will be given in the callback when it is available. + * + * @param peer_id the index of the peer + * @param cfg the configuration with which the transport handle has to be + * created if it was not present in the cache + * @param cb the callback to notify when the transport handle is available + * @param cb_cls the closure for the above callback + * @param target the peer identify of the peer whose connection to our TRANSPORT + * subsystem will be notified through the connect_notify_cb. Can be NULL + * @param connect_notify_cb the callback to call when the given target peer is + * connected. This callback will only be called once or never again (in + * case the target peer cannot be connected). Can be NULL + * @param connect_notify_cb_cls the closure for the above callback + * @return the handle which can be used cancel or mark that the handle is no + * longer being used + */ +struct GSTCacheGetHandle * +GST_cache_get_handle_transport (unsigned int peer_id, + const struct GNUNET_CONFIGURATION_Handle *cfg, + GST_cache_handle_ready_cb cb, void *cb_cls, + const struct GNUNET_PeerIdentity *target, + GST_cache_peer_connect_notify connect_notify_cb, + void *connect_notify_cb_cls) +{ + struct GSTCacheGetHandle *cgh; + + cgh = GNUNET_malloc (sizeof (struct GSTCacheGetHandle)); + cgh->cb = cb; + cgh->cb_cls = cb_cls; + cgh->type = CGT_TRANSPORT_HANDLE; + return cache_get_handle (peer_id, cgh, cfg, target, connect_notify_cb, + connect_notify_cb_cls); +} + + +/** + * Get a CORE handle with the given configuration. If the handle is already + * cached before, it will be retured in the given callback; the peer_id is used + * to lookup in the cache. If the handle is not cached before, a new operation + * is started to open the CORE handle and will be given in the callback when it + * is available along with the peer identity + * + * @param peer_id the index of the peer + * @param cfg the configuration with which the transport handle has to be + * created if it was not present in the cache + * @param cb the callback to notify when the transport handle is available + * @param cb_cls the closure for the above callback + * @param target the peer identify of the peer whose connection to our CORE + * subsystem will be notified through the connect_notify_cb. Can be NULL + * @param connect_notify_cb the callback to call when the given target peer is + * connected. This callback will only be called once or never again (in + * case the target peer cannot be connected). Can be NULL + * @param connect_notify_cb_cls the closure for the above callback + * @return the handle which can be used cancel or mark that the handle is no + * longer being used + */ +struct GSTCacheGetHandle * +GST_cache_get_handle_core (unsigned int peer_id, + const struct GNUNET_CONFIGURATION_Handle *cfg, + GST_cache_handle_ready_cb cb, void *cb_cls, + const struct GNUNET_PeerIdentity *target, + GST_cache_peer_connect_notify connect_notify_cb, + void *connect_notify_cb_cls) +{ + struct GSTCacheGetHandle *cgh; + + cgh = GNUNET_malloc (sizeof (struct GSTCacheGetHandle)); + cgh->cb = cb; + cgh->cb_cls = cb_cls; + cgh->type = CGT_CORE_HANDLE; + return cache_get_handle (peer_id, cgh, cfg, target, connect_notify_cb, + connect_notify_cb_cls); +} + + +/** + * Looks up in the hello cache and returns the HELLO of the given peer + * + * @param peer_id the index of the peer whose HELLO has to be looked up + * @return the HELLO message; NULL if not found + */ +const struct GNUNET_MessageHeader * +GST_cache_lookup_hello (const unsigned int peer_id) +{ + struct CacheEntry *entry; + struct GNUNET_HashCode key; + + LOG_DEBUG ("Looking up HELLO for peer %u\n", peer_id); + GNUNET_CRYPTO_hash (&peer_id, sizeof (peer_id), &key); + entry = cache_lookup (&key); + if (NULL == entry) + return NULL; + if (NULL != entry->hello) + LOG_DEBUG ("HELLO found for peer %u\n", peer_id); + return entry->hello; +} + + +/** + * Caches the HELLO of the given peer. Updates the HELLO if it was already + * cached before + * + * @param id the peer identity of the peer whose HELLO has to be cached + * @param hello the HELLO message + */ +void +GST_cache_add_hello (const unsigned int peer_id, + const struct GNUNET_MessageHeader *hello) +{ + struct CacheEntry *entry; + struct GNUNET_HashCode key; + + GNUNET_CRYPTO_hash (&peer_id, sizeof (peer_id), &key); + entry = GNUNET_CONTAINER_multihashmap_get (cache, &key); + if (NULL == entry) + entry = add_entry (&key, peer_id); + GNUNET_free_non_null (entry->hello); + entry->hello = GNUNET_copy_message (hello); +} + +/* end of gnunet-service-testbed_hc.c */ diff --git a/src/testbed/gnunet-service-testbed_oc.c b/src/testbed/gnunet-service-testbed_oc.c new file mode 100644 index 0000000..9b571e3 --- /dev/null +++ b/src/testbed/gnunet-service-testbed_oc.c @@ -0,0 +1,1595 @@ +/* + This file is part of GNUnet. + (C) 2012 Christian Grothoff (and other contributing authors) + + GNUnet 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 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file testbed/gnunet-service-testbed_oc.c + * @brief code for handling overlay connect operations + * @author Sree Harsha Totakura + */ + +#include "gnunet-service-testbed.h" + +/** + * Redefine LOG with a changed log component string + */ +#ifdef LOG +#undef LOG +#endif +#define LOG(kind,...) \ + GNUNET_log_from (kind, "testbed-OC", __VA_ARGS__) + + +/** + * Context information for requesting TRANSPORT to connect to a peer + */ +struct TryConnectContext +{ + /** + * The identity of the peer to which the transport has to attempt a connection + */ + struct GNUNET_PeerIdentity *pid; + + /** + * The transport handle obtained from cache. Do NOT close/disconnect. + */ + struct GNUNET_TRANSPORT_Handle *th_; + + /** + * The GetCacheHandle for the p1th transport handle + */ + struct GSTCacheGetHandle *cgh_th; + + /** + * the try connect handle + */ + struct GNUNET_TRANSPORT_TryConnectHandle *tch; + + /** + * The task handle + */ + GNUNET_SCHEDULER_TaskIdentifier task; + + /** + * The id of the operation which is resposible for this context + */ + uint64_t op_id; + + /** + * The number of times we attempted to connect + */ + unsigned int retries; + +}; + + +/** + * Context information for connecting 2 peers in overlay. + */ +struct OverlayConnectContext +{ + /** + * The next pointer for maintaining a DLL of all OverlayConnectContexts + */ + struct OverlayConnectContext *next; + + /** + * The prev pointer for maintaining a DLL of all OverlayConnectContexts + */ + struct OverlayConnectContext *prev; + + /** + * The client which has requested for overlay connection. This is used to send + * either a success of failure message + */ + struct GNUNET_SERVER_Client *client; + + /** + * the first peer which is to expect an overlay connection from the second peer. + */ + struct Peer *peer; + + /** + * Transport handle of the first peer obtained from cache to get its HELLO. Do + * NOT close/disconnect. + */ + struct GNUNET_TRANSPORT_Handle *p1th_; + + /** + * The CacheGetHandle for the p1th transport handle + */ + struct GSTCacheGetHandle *cgh_p1th; + + /** + * The GetCacheHandle for registering callback to notify CORE level peer + * connects and to get our identity. + */ + struct GSTCacheGetHandle *cgh_ch; + + /** + * HELLO of the first peer. This should be sent to the second peer. + */ + struct GNUNET_MessageHeader *hello; + + /** + * Get GetHelloHandle to acquire a HELLO of the first peer + */ + struct GNUNET_TRANSPORT_GetHelloHandle *ghh; + + /** + * The handle for offering the HELLO of the first peer to the second + * peer. This is only used if the second peer is a local peer. + */ + struct GNUNET_TRANSPORT_OfferHelloHandle *ohh; + + /** + * The error message we send if this overlay connect operation has timed out + */ + char *emsg; + + /** + * Operation context for the suboperation we start to get the identity of the + * second peer if it is a remote peer + */ + struct OperationContext *opc; + + /** + * Controller of peer 2; NULL if the peer is a local peer + */ + struct GNUNET_TESTBED_Controller *peer2_controller; + + /** + * The transport TryConnectContext. This will be NULL if the second peer is a + * remote peer + */ + struct TryConnectContext tcc; + + /** + * The peer identity of the first peer + */ + struct GNUNET_PeerIdentity peer_identity; + + /** + * The peer identity of the other peer + */ + struct GNUNET_PeerIdentity other_peer_identity; + + /** + * The id of the operation responsible for creating this context + */ + uint64_t op_id; + + /** + * The id of the task for sending HELLO of peer 2 to peer 1 and ask peer 1 to + * connect to peer 2 + */ + GNUNET_SCHEDULER_TaskIdentifier send_hello_task; + + /** + * The id of the overlay connect timeout task + */ + GNUNET_SCHEDULER_TaskIdentifier timeout_task; + + /** + * The id of the cleanup task + */ + GNUNET_SCHEDULER_TaskIdentifier cleanup_task; + + /** + * The id of peer A + */ + uint32_t peer_id; + + /** + * The id of peer B + */ + uint32_t other_peer_id; + +}; + + +/** + * Context information for remote overlay connect operations. Remote overlay + * connections are used when peers A and B reside on different hosts. In these + * operations the host controller for peer B is asked by the host controller of + * peer A to make peer B connect to peer A by sending the controller of peer B + * the HELLO of peer A. + */ +struct RemoteOverlayConnectCtx +{ + /** + * the next pointer for DLL + */ + struct RemoteOverlayConnectCtx *next; + + /** + * the prev pointer for DLL + */ + struct RemoteOverlayConnectCtx *prev; + + /** + * The peer handle of peer B + */ + struct Peer *peer; + + /** + * Peer A's HELLO + */ + struct GNUNET_MessageHeader *hello; + + /** + * The handle for offering HELLO + */ + struct GNUNET_TRANSPORT_OfferHelloHandle *ohh; + + /** + * The transport try connect context + */ + struct TryConnectContext tcc; + + /** + * The peer identity of peer A + */ + struct GNUNET_PeerIdentity a_id; + + /** + * Task for offering HELLO of A to B and doing try_connect + */ + GNUNET_SCHEDULER_TaskIdentifier attempt_connect_task_id; + + /** + * Task to timeout RequestOverlayConnect + */ + GNUNET_SCHEDULER_TaskIdentifier timeout_rocc_task_id; + + /** + * The id of the operation responsible for creating this context + */ + uint64_t op_id; +}; + + +/** + * DLL head for OverlayConnectContext DLL - to be used to clean up during shutdown + */ +static struct OverlayConnectContext *occq_head; + +/** + * DLL tail for OverlayConnectContext DLL + */ +static struct OverlayConnectContext *occq_tail; + +/** + * DLL head for RequectOverlayConnectContext DLL - to be used to clean up during + * shutdown + */ +static struct RemoteOverlayConnectCtx *roccq_head; + +/** + * DLL tail for RequectOverlayConnectContext DLL + */ +static struct RemoteOverlayConnectCtx *roccq_tail; + + +/** + * Cleans up ForwardedOverlayConnectContext + * + * @param focc the ForwardedOverlayConnectContext to cleanup + */ +void +GST_cleanup_focc (struct ForwardedOverlayConnectContext *focc) +{ + GNUNET_free_non_null (focc->orig_msg); + GNUNET_free (focc); +} + + +/** + * Timeout task for cancelling a forwarded overlay connect connect + * + * @param cls the ForwardedOverlayConnectContext + * @param tc the task context from the scheduler + */ +static void +forwarded_overlay_connect_timeout (void *cls, + const struct GNUNET_SCHEDULER_TaskContext + *tc) +{ + struct ForwardedOperationContext *fopc = cls; + struct RegisteredHostContext *rhc; + struct ForwardedOverlayConnectContext *focc; + + rhc = fopc->cls; + focc = rhc->focc_dll_head; + GNUNET_CONTAINER_DLL_remove (rhc->focc_dll_head, rhc->focc_dll_tail, focc); + GST_cleanup_focc (focc); + LOG_DEBUG ("Overlay linking between peers %u and %u failed\n", focc->peer1, + focc->peer2); + GST_forwarded_operation_timeout (cls, tc); + if (NULL != rhc->focc_dll_head) + GST_process_next_focc (rhc); +} + + +/** + * Callback to be called when forwarded overlay connection operation has a reply + * from the sub-controller successfull. We have to relay the reply msg back to + * the client + * + * @param cls ForwardedOperationContext + * @param msg the peer create success message + */ +static void +forwarded_overlay_connect_listener (void *cls, + const struct GNUNET_MessageHeader *msg) +{ + struct ForwardedOperationContext *fopc = cls; + struct RegisteredHostContext *rhc; + struct ForwardedOverlayConnectContext *focc; + + rhc = fopc->cls; + GST_forwarded_operation_reply_relay (cls, msg); + focc = rhc->focc_dll_head; + GNUNET_CONTAINER_DLL_remove (rhc->focc_dll_head, rhc->focc_dll_tail, focc); + GST_cleanup_focc (focc); + if (NULL != rhc->focc_dll_head) + GST_process_next_focc (rhc); +} + + +/** + * Processes a forwarded overlay connect context in the queue of the given RegisteredHostContext + * + * @param rhc the RegisteredHostContext + */ +void +GST_process_next_focc (struct RegisteredHostContext *rhc) +{ + struct ForwardedOperationContext *fopc; + struct ForwardedOverlayConnectContext *focc; + + focc = rhc->focc_dll_head; + GNUNET_assert (NULL != focc); + GNUNET_assert (RHC_OL_CONNECT == rhc->state); + fopc = GNUNET_malloc (sizeof (struct ForwardedOperationContext)); + GNUNET_SERVER_client_keep (rhc->client); + fopc->client = rhc->client; + fopc->operation_id = focc->operation_id; + fopc->cls = rhc; + fopc->type = OP_OVERLAY_CONNECT; + fopc->opc = + GNUNET_TESTBED_forward_operation_msg_ (rhc->gateway->controller, + focc->operation_id, focc->orig_msg, + &forwarded_overlay_connect_listener, + fopc); + GNUNET_free (focc->orig_msg); + focc->orig_msg = NULL; + fopc->timeout_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &forwarded_overlay_connect_timeout, + fopc); + GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fopc); +} + + +/** + * Cleanup overlay connect context structure + * + * @param occ the overlay connect context + */ +static void +cleanup_occ (struct OverlayConnectContext *occ) +{ + LOG_DEBUG ("0x%llx: Cleaning up occ\n", occ->op_id); + GNUNET_free_non_null (occ->emsg); + GNUNET_free_non_null (occ->hello); + GNUNET_SERVER_client_drop (occ->client); + if (NULL != occ->opc) + GNUNET_TESTBED_forward_operation_msg_cancel_ (occ->opc); + if (GNUNET_SCHEDULER_NO_TASK != occ->send_hello_task) + GNUNET_SCHEDULER_cancel (occ->send_hello_task); + if (GNUNET_SCHEDULER_NO_TASK != occ->cleanup_task) + GNUNET_SCHEDULER_cancel (occ->cleanup_task); + if (GNUNET_SCHEDULER_NO_TASK != occ->timeout_task) + GNUNET_SCHEDULER_cancel (occ->timeout_task); + if (NULL != occ->cgh_ch) + { + GST_cache_get_handle_done (occ->cgh_ch); + occ->peer->reference_cnt--; + } + if (NULL != occ->ghh) + GNUNET_TRANSPORT_get_hello_cancel (occ->ghh); + if (NULL != occ->ohh) + GNUNET_TRANSPORT_offer_hello_cancel (occ->ohh); + if (GNUNET_SCHEDULER_NO_TASK != occ->tcc.task) + GNUNET_SCHEDULER_cancel (occ->tcc.task); + if (NULL != occ->tcc.tch) + GNUNET_TRANSPORT_try_connect_cancel (occ->tcc.tch); + if (NULL != occ->cgh_p1th) + { + GST_cache_get_handle_done (occ->cgh_p1th); + occ->peer->reference_cnt--; + } + if (NULL != occ->tcc.cgh_th) + { + GST_cache_get_handle_done (occ->tcc.cgh_th); + GST_peer_list[occ->other_peer_id]->reference_cnt--; + } + if ((GNUNET_YES == occ->peer->destroy_flag) && + (0 == occ->peer->reference_cnt)) + GST_destroy_peer (occ->peer); + if ((NULL == occ->peer2_controller) && + (GNUNET_YES == GST_peer_list[occ->other_peer_id]->destroy_flag) && + (0 == GST_peer_list[occ->other_peer_id]->reference_cnt)) + GST_destroy_peer (GST_peer_list[occ->other_peer_id]); + GNUNET_CONTAINER_DLL_remove (occq_head, occq_tail, occ); + GNUNET_free (occ); +} + + +/** + * Task for cleaing up overlay connect context structure + * + * @param cls the overlay connect context + * @param tc the task context + */ +static void +do_cleanup_occ (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct OverlayConnectContext *occ = cls; + + occ->cleanup_task = GNUNET_SCHEDULER_NO_TASK; + cleanup_occ (occ); +} + + +/** + * Task which will be run when overlay connect request has been timed out + * + * @param cls the OverlayConnectContext + * @param tc the TaskContext + */ +static void +timeout_overlay_connect (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct OverlayConnectContext *occ = cls; + + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != occ->timeout_task); + occ->timeout_task = GNUNET_SCHEDULER_NO_TASK; + LOG (GNUNET_ERROR_TYPE_WARNING, + "0x%llx: Timeout while connecting peers %u and %u\n", occ->op_id, + occ->peer_id, occ->other_peer_id); + GST_send_operation_fail_msg (occ->client, occ->op_id, occ->emsg); + cleanup_occ (occ); +} + + +static void +send_overlay_connect_success_msg (struct OverlayConnectContext *occ) +{ + struct GNUNET_TESTBED_ConnectionEventMessage *msg; + + LOG_DEBUG ("0x%llx: Peers connected - Sending overlay connect success\n", + occ->op_id); + msg = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_ConnectionEventMessage)); + msg->header.size = + htons (sizeof (struct GNUNET_TESTBED_ConnectionEventMessage)); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_PEER_CONNECT_EVENT); + msg->event_type = htonl (GNUNET_TESTBED_ET_CONNECT); + msg->peer1 = htonl (occ->peer_id); + msg->peer2 = htonl (occ->other_peer_id); + msg->operation_id = GNUNET_htonll (occ->op_id); + GST_queue_message (occ->client, &msg->header); +} + + +/** + * Function called to notify transport users that another + * peer connected to us. + * + * @param cls closure + * @param new_peer the peer that connected + */ +static void +overlay_connect_notify (void *cls, const struct GNUNET_PeerIdentity *new_peer) +{ + struct OverlayConnectContext *occ = cls; + char *new_peer_str; + char *other_peer_str; + + //LOG_DEBUG ("Overlay connect notify\n"); + if (0 == + memcmp (new_peer, &occ->peer_identity, + sizeof (struct GNUNET_PeerIdentity))) + return; + new_peer_str = GNUNET_strdup (GNUNET_i2s (new_peer)); + other_peer_str = GNUNET_strdup (GNUNET_i2s (&occ->other_peer_identity)); + if (0 != + memcmp (new_peer, &occ->other_peer_identity, + sizeof (struct GNUNET_PeerIdentity))) + { + /* LOG_DEBUG ("Unexpected peer %4s connected when expecting peer %4s\n", */ + /* new_peer_str, other_peer_str); */ + GNUNET_free (new_peer_str); + GNUNET_free (other_peer_str); + return; + } + GNUNET_free (new_peer_str); + LOG_DEBUG ("0x%llx: Peer %4s connected to peer %4s\n", occ->op_id, + other_peer_str, GNUNET_i2s (&occ->peer_identity)); + GNUNET_free (other_peer_str); + if (GNUNET_SCHEDULER_NO_TASK != occ->send_hello_task) + { + GNUNET_SCHEDULER_cancel (occ->send_hello_task); + occ->send_hello_task = GNUNET_SCHEDULER_NO_TASK; + } + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != occ->timeout_task); + GNUNET_SCHEDULER_cancel (occ->timeout_task); + occ->timeout_task = GNUNET_SCHEDULER_NO_TASK; + if (GNUNET_SCHEDULER_NO_TASK != occ->tcc.task) + { + GNUNET_SCHEDULER_cancel (occ->tcc.task); + occ->tcc.task = GNUNET_SCHEDULER_NO_TASK; + } + GNUNET_free_non_null (occ->emsg); + occ->emsg = NULL; + send_overlay_connect_success_msg (occ); + occ->cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup_occ, occ); + //cleanup_occ (occ); +} + + +/** + * Task to ask transport of a peer to connect to another peer + * + * @param cls the TryConnectContext + * @param tc the scheduler task context + */ +static void +try_connect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + + +/** + * Callback to be called with result of the try connect request. + * + * @param cls the overlay connect context + * @param result GNUNET_OK if message was transmitted to transport service + * GNUNET_SYSERR if message was not transmitted to transport service + */ +static void +try_connect_cb (void *cls, const int result) +{ + struct TryConnectContext *tcc = cls; + + tcc->tch = NULL; + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == tcc->task); + tcc->task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MILLISECONDS, + 500 + pow (2, ++tcc->retries)), + &try_connect_task, tcc); +} + + +/** + * Task to ask transport of a peer to connect to another peer + * + * @param cls the TryConnectContext + * @param tc the scheduler task context + */ +static void +try_connect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct TryConnectContext *tcc = cls; + + tcc->task = GNUNET_SCHEDULER_NO_TASK; + if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason)) + return; + GNUNET_assert (NULL == tcc->tch); + GNUNET_assert (NULL != tcc->pid); + GNUNET_assert (NULL != tcc->th_); + GNUNET_assert (NULL != tcc->cgh_th); + LOG_DEBUG ("0x%llx: Trail %u to connect to peer %s\n", tcc->op_id, + tcc->retries, GNUNET_i2s (tcc->pid)); + tcc->tch = + GNUNET_TRANSPORT_try_connect (tcc->th_, tcc->pid, &try_connect_cb, tcc); +} + + +/** + * Task to offer HELLO of peer 1 to peer 2 and try to make peer 2 to connect to + * peer 1. + * + * @param cls the OverlayConnectContext + * @param tc the TaskContext from scheduler + */ +static void +send_hello (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + + +/** + * Task that is run when hello has been sent + * + * @param cls the overlay connect context + * @param tc the scheduler task context; if tc->reason = + * GNUNET_SCHEDULER_REASON_TIMEOUT then sending HELLO failed; if + * GNUNET_SCHEDULER_REASON_READ_READY is succeeded + */ +static void +occ_hello_sent_cb (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct OverlayConnectContext *occ = cls; + + occ->ohh = NULL; + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == occ->send_hello_task); + if (GNUNET_SCHEDULER_REASON_TIMEOUT == tc->reason) + { + GNUNET_free_non_null (occ->emsg); + GNUNET_asprintf (&occ->emsg, + "0x%llx: Timeout while offering HELLO to other peer", + occ->op_id); + occ->send_hello_task = GNUNET_SCHEDULER_add_now (&send_hello, occ); + return; + } + if (GNUNET_SCHEDULER_REASON_READ_READY != tc->reason) + return; + GNUNET_free_non_null (occ->emsg); + GNUNET_asprintf (&occ->emsg, "0x%llx: Timeout while try connect", occ->op_id); + occ->tcc.pid = &occ->peer_identity; + occ->tcc.op_id = occ->op_id; + occ->tcc.task = GNUNET_SCHEDULER_add_now (&try_connect_task, &occ->tcc); +} + + +/** + * Task to offer HELLO of peer 1 to peer 2 and try to make peer 2 to connect to + * peer 1. + * + * @param cls the OverlayConnectContext + * @param tc the TaskContext from scheduler + */ +static void +send_hello (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct OverlayConnectContext *occ = cls; + char *other_peer_str; + + occ->send_hello_task = GNUNET_SCHEDULER_NO_TASK; + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + return; + GNUNET_assert (NULL != occ->hello); + other_peer_str = GNUNET_strdup (GNUNET_i2s (&occ->other_peer_identity)); + if (NULL != occ->peer2_controller) + { + struct GNUNET_TESTBED_RemoteOverlayConnectMessage *msg; + uint16_t msize; + uint16_t hello_size; + + LOG_DEBUG ("0x%llx: Offering HELLO of %s (size: %u) to %s via Remote " + "Overlay Request\n", occ->op_id, + GNUNET_i2s (&occ->peer_identity), ntohs (occ->hello->size), + other_peer_str); + hello_size = ntohs (occ->hello->size); + msize = + sizeof (struct GNUNET_TESTBED_RemoteOverlayConnectMessage) + hello_size; + msg = GNUNET_malloc (msize); + msg->header.type = + htons (GNUNET_MESSAGE_TYPE_TESTBED_REMOTE_OVERLAY_CONNECT); + msg->header.size = htons (msize); + msg->peer = htonl (occ->other_peer_id); + msg->operation_id = GNUNET_htonll (occ->op_id); + (void) memcpy (&msg->peer_identity, &occ->peer_identity, + sizeof (struct GNUNET_PeerIdentity)); + memcpy (msg->hello, occ->hello, hello_size); + GNUNET_TESTBED_queue_message_ (occ->peer2_controller, &msg->header); + } + else + { + LOG_DEBUG ("0x%llx: Offering HELLO of %s to %s\n", occ->op_id, + GNUNET_i2s (&occ->peer_identity), other_peer_str); + occ->ohh = + GNUNET_TRANSPORT_offer_hello (occ->tcc.th_, occ->hello, + occ_hello_sent_cb, occ); + if (NULL == occ->ohh) + { + GNUNET_break (0); + occ->send_hello_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MILLISECONDS, + 100 + + GNUNET_CRYPTO_random_u32 + (GNUNET_CRYPTO_QUALITY_WEAK, 500)), + &send_hello, occ); + } + } + GNUNET_free (other_peer_str); +} + + +/** + * Callback from cache with needed handles set + * + * @param cls the closure passed to GST_cache_get_handle_transport() + * @param ch the handle to CORE. Can be NULL if it is not requested + * @param th the handle to TRANSPORT. Can be NULL if it is not requested + * @param ignore_ peer identity which is ignored in this callback + */ +static void +p2_transport_connect_cache_callback (void *cls, struct GNUNET_CORE_Handle *ch, + struct GNUNET_TRANSPORT_Handle *th, + const struct GNUNET_PeerIdentity *ignore_) +{ + struct OverlayConnectContext *occ = cls; + + if (NULL == th) + { + GNUNET_asprintf (&occ->emsg, "0x%llx: Cannot connect to TRANSPORT of %s", + occ->op_id, GNUNET_i2s (&occ->other_peer_identity)); + GNUNET_SCHEDULER_cancel (occ->timeout_task); + occ->timeout_task = + GNUNET_SCHEDULER_add_now (&timeout_overlay_connect, occ); + return; + } + occ->tcc.th_ = th; + GNUNET_asprintf (&occ->emsg, "0x%llx: Timeout while offering HELLO to %s", + occ->op_id, GNUNET_i2s (&occ->other_peer_identity)); + occ->send_hello_task = GNUNET_SCHEDULER_add_now (&send_hello, occ); +} + + +/** + * Connects to the transport of the other peer if it is a local peer and + * schedules the send hello task + * + * @param occ the overlay connect context + */ +static void +p2_transport_connect (struct OverlayConnectContext *occ) +{ + GNUNET_assert (NULL == occ->emsg); + GNUNET_assert (NULL != occ->hello); + GNUNET_assert (NULL == occ->ghh); + GNUNET_assert (NULL == occ->p1th_); + GNUNET_assert (NULL == occ->cgh_p1th); + if (NULL == occ->peer2_controller) + { + GST_peer_list[occ->other_peer_id]->reference_cnt++; + occ->tcc.cgh_th = + GST_cache_get_handle_transport (occ->other_peer_id, + GST_peer_list[occ->other_peer_id]-> + details.local.cfg, + &p2_transport_connect_cache_callback, + occ, NULL, NULL, NULL); + return; + } + GNUNET_asprintf (&occ->emsg, "0x%llx: Timeout while offering HELLO to %s", + occ->op_id, GNUNET_i2s (&occ->other_peer_identity)); + occ->send_hello_task = GNUNET_SCHEDULER_add_now (&send_hello, occ); +} + + +/** + * Test for checking whether HELLO message is empty + * + * @param cls empty flag to set + * @param address the HELLO + * @param expiration expiration of the HELLO + * @return + */ +static int +test_address (void *cls, const struct GNUNET_HELLO_Address *address, + struct GNUNET_TIME_Absolute expiration) +{ + int *empty = cls; + + *empty = GNUNET_NO; + return GNUNET_OK; +} + + +/** + * Function called whenever there is an update to the HELLO of peers in the + * OverlayConnectClosure. If we have a valid HELLO, we connect to the peer 2's + * transport and offer peer 1's HELLO and ask peer 2 to connect to peer 1 + * + * @param cls closure + * @param hello our updated HELLO + */ +static void +hello_update_cb (void *cls, const struct GNUNET_MessageHeader *hello) +{ + struct OverlayConnectContext *occ = cls; + int empty; + uint16_t msize; + + msize = ntohs (hello->size); + empty = GNUNET_YES; + (void) GNUNET_HELLO_iterate_addresses ((const struct GNUNET_HELLO_Message *) + hello, GNUNET_NO, &test_address, + &empty); + if (GNUNET_YES == empty) + { + LOG_DEBUG ("0x%llx: HELLO of %s is empty\n", occ->op_id, + GNUNET_i2s (&occ->peer_identity)); + return; + } + LOG_DEBUG ("0x%llx: Received HELLO of %s\n", occ->op_id, + GNUNET_i2s (&occ->peer_identity)); + occ->hello = GNUNET_malloc (msize); + GST_cache_add_hello (occ->peer_id, hello); + memcpy (occ->hello, hello, msize); + GNUNET_TRANSPORT_get_hello_cancel (occ->ghh); + occ->ghh = NULL; + GST_cache_get_handle_done (occ->cgh_p1th); + occ->peer->reference_cnt--; + occ->cgh_p1th = NULL; + occ->p1th_ = NULL; + GNUNET_free_non_null (occ->emsg); + occ->emsg = NULL; + p2_transport_connect (occ); +} + + +/** + * Callback from cache with needed handles set + * + * @param cls the closure passed to GST_cache_get_handle_transport() + * @param ch the handle to CORE. Can be NULL if it is not requested + * @param th the handle to TRANSPORT. Can be NULL if it is not requested + * @param ignore_ peer identity which is ignored in this callback + */ +static void +p1_transport_connect_cache_callback (void *cls, struct GNUNET_CORE_Handle *ch, + struct GNUNET_TRANSPORT_Handle *th, + const struct GNUNET_PeerIdentity *ignore_) +{ + struct OverlayConnectContext *occ = cls; + + GNUNET_free_non_null (occ->emsg); + occ->emsg = NULL; + if (NULL == th) + { + GNUNET_asprintf (&occ->emsg, "0x%llx: Cannot connect to TRANSPORT of %s", + occ->op_id, GNUNET_i2s (&occ->peer_identity)); + GNUNET_SCHEDULER_cancel (occ->timeout_task); + occ->timeout_task = + GNUNET_SCHEDULER_add_now (&timeout_overlay_connect, occ); + return; + } + GNUNET_assert (NULL == occ->p1th_); + GNUNET_assert (NULL != occ->cgh_p1th); + occ->p1th_ = th; + GNUNET_asprintf (&occ->emsg, + "0x%llx: Timeout while acquiring HELLO of peer %4s", + occ->op_id, GNUNET_i2s (&occ->peer_identity)); + occ->ghh = GNUNET_TRANSPORT_get_hello (occ->p1th_, &hello_update_cb, occ); +} + + +/** + * Callback from cache with needed handles set + * + * @param cls the closure passed to GST_cache_get_handle_transport() + * @param ch the handle to CORE. Can be NULL if it is not requested + * @param th the handle to TRANSPORT. Can be NULL if it is not requested + * @param my_identity the identity of our peer + */ +static void +occ_cache_get_handle_core_cb (void *cls, struct GNUNET_CORE_Handle *ch, + struct GNUNET_TRANSPORT_Handle *th, + const struct GNUNET_PeerIdentity *my_identity) +{ + struct OverlayConnectContext *occ = cls; + const struct GNUNET_MessageHeader *hello; + + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != occ->timeout_task); + GNUNET_free_non_null (occ->emsg); + if ((NULL == ch) || (NULL == my_identity)) + { + (void) GNUNET_asprintf (&occ->emsg, + "0x%llx: Failed to connect to CORE of peer with" + "id: %u", occ->op_id, occ->peer_id); + GNUNET_SCHEDULER_cancel (occ->timeout_task); + occ->timeout_task = + GNUNET_SCHEDULER_add_now (&timeout_overlay_connect, occ); + return; + } + //occ->ch_ = ch; + occ->emsg = NULL; + if (GNUNET_YES == + GNUNET_CORE_is_peer_connected_sync (ch, &occ->other_peer_identity)) + { + LOG_DEBUG ("0x%llx: Target peer already connected\n", occ->op_id); + GNUNET_SCHEDULER_cancel (occ->timeout_task); + occ->timeout_task = GNUNET_SCHEDULER_NO_TASK; + send_overlay_connect_success_msg (occ); + occ->cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup_occ, occ); + return; + } + memcpy (&occ->peer_identity, my_identity, + sizeof (struct GNUNET_PeerIdentity)); + LOG_DEBUG ("0x%llx: Acquiring HELLO of peer %s\n", occ->op_id, + GNUNET_i2s (&occ->peer_identity)); + /* Lookup for HELLO in hello cache */ + if (NULL != (hello = GST_cache_lookup_hello (occ->peer_id))) + { + LOG_DEBUG ("0x%llx: HELLO of peer %s found in cache\n", occ->op_id, + GNUNET_i2s (&occ->peer_identity)); + occ->hello = GNUNET_copy_message (hello); + p2_transport_connect (occ); + return; + } + GNUNET_asprintf (&occ->emsg, + "0x%llx: Timeout while acquiring TRANSPORT of %s from cache", + occ->op_id, GNUNET_i2s (&occ->peer_identity)); + occ->peer->reference_cnt++; + occ->cgh_p1th = + GST_cache_get_handle_transport (occ->peer_id, + occ->peer->details.local.cfg, + p1_transport_connect_cache_callback, occ, + NULL, NULL, NULL); + return; +} + + +/** + * Callback to be called when forwarded get peer config operation as part of + * overlay connect is successfull. Connection to Peer 1's core is made and is + * checked for new connection from peer 2 + * + * @param cls ForwardedOperationContext + * @param msg the peer create success message + */ +static void +overlay_connect_get_config (void *cls, const struct GNUNET_MessageHeader *msg) +{ + struct OverlayConnectContext *occ = cls; + const struct GNUNET_TESTBED_PeerConfigurationInformationMessage *cmsg; + + occ->opc = NULL; + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != occ->timeout_task); + if (GNUNET_MESSAGE_TYPE_TESTBED_PEER_CONFIGURATION != ntohs (msg->type)) + { + GNUNET_SCHEDULER_cancel (occ->timeout_task); + occ->timeout_task = + GNUNET_SCHEDULER_add_now (&timeout_overlay_connect, occ); + } + cmsg = + (const struct GNUNET_TESTBED_PeerConfigurationInformationMessage *) msg; + memcpy (&occ->other_peer_identity, &cmsg->peer_identity, + sizeof (struct GNUNET_PeerIdentity)); + GNUNET_free_non_null (occ->emsg); + GNUNET_asprintf (&occ->emsg, + "0x%llx: Timeout while connecting to CORE of peer with " + "id: %u", occ->op_id, occ->peer_id); + occ->peer->reference_cnt++; + occ->cgh_ch = + GST_cache_get_handle_core (occ->peer_id, occ->peer->details.local.cfg, + occ_cache_get_handle_core_cb, occ, + &occ->other_peer_identity, + &overlay_connect_notify, occ); + return; +} + + +/** + * Callback which will be called to after a host registration succeeded or failed + * + * @param cls the RegisteredHostContext + * @param emsg the error message; NULL if host registration is successful + */ +static void +registeredhost_registration_completion (void *cls, const char *emsg) +{ + struct RegisteredHostContext *rhc = cls; + struct GNUNET_CONFIGURATION_Handle *cfg; + uint32_t peer2_host_id; + + /* if (NULL != rhc->focc_dll_head) */ + /* TESTBED_process_next_focc (rhc); */ + peer2_host_id = GNUNET_TESTBED_host_get_id_ (rhc->reg_host); + GNUNET_assert (RHC_INIT == rhc->state); + GNUNET_assert (NULL == rhc->sub_op); + if ((NULL == rhc->gateway2) || ((peer2_host_id < GST_slave_list_size) /* Check if we have the needed config */ + && (NULL != GST_slave_list[peer2_host_id]))) + { + rhc->state = RHC_LINK; + cfg = + (NULL == + rhc->gateway2) ? our_config : GST_slave_list[peer2_host_id]->cfg; + rhc->sub_op = + GNUNET_TESTBED_controller_link (rhc, rhc->gateway->controller, + rhc->reg_host, rhc->host, cfg, + GNUNET_NO); + return; + } + rhc->state = RHC_GET_CFG; + rhc->sub_op = + GNUNET_TESTBED_get_slave_config (rhc, rhc->gateway2->controller, + rhc->reg_host); +} + + +/** + * Iterator to match a registered host context + * + * @param cls pointer 2 pointer of RegisteredHostContext + * @param key current key code + * @param value value in the hash map + * @return GNUNET_YES if we should continue to + * iterate, + * GNUNET_NO if not. + */ +static int +reghost_match_iterator (void *cls, const struct GNUNET_HashCode *key, + void *value) +{ + struct RegisteredHostContext **rh = cls; + struct RegisteredHostContext *rh_val = value; + + if ((rh_val->host == (*rh)->host) && (rh_val->reg_host == (*rh)->reg_host)) + { + GNUNET_free (*rh); + *rh = rh_val; + return GNUNET_NO; + } + return GNUNET_YES; +} + + +/** + * Function to generate the hashcode corresponding to a RegisteredHostContext + * + * @param reg_host the host which is being registered in RegisteredHostContext + * @param host the host of the controller which has to connect to the above rhost + * @return the hashcode + */ +static struct GNUNET_HashCode +hash_hosts (struct GNUNET_TESTBED_Host *reg_host, + struct GNUNET_TESTBED_Host *host) +{ + struct GNUNET_HashCode hash; + uint32_t host_ids[2]; + + host_ids[0] = GNUNET_TESTBED_host_get_id_ (reg_host); + host_ids[1] = GNUNET_TESTBED_host_get_id_ (host); + GNUNET_CRYPTO_hash (host_ids, sizeof (host_ids), &hash); + return hash; +} + + +/** + * Handler for GNUNET_MESSAGE_TYPE_TESTBED_OLCONNECT messages + * + * @param cls NULL + * @param client identification of the client + * @param message the actual message + */ +void +GST_handle_overlay_connect (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct GNUNET_TESTBED_OverlayConnectMessage *msg; + struct Peer *peer; + struct OverlayConnectContext *occ; + struct GNUNET_TESTBED_Controller *peer2_controller; + uint64_t operation_id; + uint32_t p1; + uint32_t p2; + uint32_t peer2_host_id; + + if (sizeof (struct GNUNET_TESTBED_OverlayConnectMessage) != + ntohs (message->size)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + msg = (const struct GNUNET_TESTBED_OverlayConnectMessage *) message; + p1 = ntohl (msg->peer1); + p2 = ntohl (msg->peer2); + if ((p1 >= GST_peer_list_size) || (NULL == GST_peer_list[p1])) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + peer = GST_peer_list[p1]; + peer2_host_id = ntohl (msg->peer2_host_id); + operation_id = GNUNET_ntohll (msg->operation_id); + LOG_DEBUG + ("Received overlay connect for peers %u and %u with op id: 0x%llx\n", p1, + p2, operation_id); + if (GNUNET_YES == peer->is_remote) + { + struct ForwardedOperationContext *fopc; + struct Route *route_to_peer2_host; + struct Route *route_to_peer1_host; + + LOG_DEBUG ("0x%llx: Forwarding overlay connect\n", operation_id); + route_to_peer2_host = NULL; + route_to_peer1_host = NULL; + route_to_peer2_host = GST_find_dest_route (peer2_host_id); + if ((NULL != route_to_peer2_host) || + (peer2_host_id == GST_context->host_id)) + { + /* Peer 2 either below us OR with us */ + route_to_peer1_host = + GST_find_dest_route (GST_peer_list[p1]->details. + remote.remote_host_id); + /* Because we get this message only if we know where peer 1 is */ + GNUNET_assert (NULL != route_to_peer1_host); + if ((peer2_host_id == GST_context->host_id) || + (route_to_peer2_host->dest != route_to_peer1_host->dest)) + { + /* Peer2 is either with us OR peer1 and peer2 can be reached through + * different gateways */ + struct GNUNET_HashCode hash; + struct RegisteredHostContext *rhc; + int skip_focc; + + rhc = GNUNET_malloc (sizeof (struct RegisteredHostContext)); + if (NULL != route_to_peer2_host) + rhc->reg_host = GST_host_list[route_to_peer2_host->dest]; + else + rhc->reg_host = GST_host_list[GST_context->host_id]; + rhc->host = GST_host_list[route_to_peer1_host->dest]; + GNUNET_assert (NULL != rhc->reg_host); + GNUNET_assert (NULL != rhc->host); + rhc->gateway = peer->details.remote.slave; + rhc->gateway2 = + (NULL == + route_to_peer2_host) ? NULL : + GST_slave_list[route_to_peer2_host->dest]; + rhc->state = RHC_INIT; + GNUNET_SERVER_client_keep (client); + rhc->client = client; + hash = hash_hosts (rhc->reg_host, rhc->host); + skip_focc = GNUNET_NO; + if ((GNUNET_NO == + GNUNET_CONTAINER_multihashmap_contains (peer->details. + remote.slave->reghost_map, + &hash)) || + (GNUNET_SYSERR != + GNUNET_CONTAINER_multihashmap_get_multiple (peer->details.remote. + slave->reghost_map, + &hash, + reghost_match_iterator, + &rhc))) + { + /* create and add a new registerd host context */ + /* add the focc to its queue */ + GNUNET_CONTAINER_multihashmap_put (peer->details.remote. + slave->reghost_map, &hash, rhc, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + GNUNET_assert (NULL != GST_host_list[peer2_host_id]); + GST_queue_host_registration (peer->details.remote.slave, + registeredhost_registration_completion, + rhc, GST_host_list[peer2_host_id]); + } + else + { + /* rhc is now set to the existing one from the hash map by + * reghost_match_iterator() */ + /* if queue is empty then ignore creating focc and proceed with + * normal forwarding */ + if (RHC_OL_CONNECT == rhc->state) + skip_focc = GNUNET_YES; + } + if (GNUNET_NO == skip_focc) + { + struct ForwardedOverlayConnectContext *focc; + + focc = GNUNET_malloc (sizeof (struct ForwardedOverlayConnectContext)); + focc->peer1 = p1; + focc->peer2 = p2; + focc->peer2_host_id = peer2_host_id; + focc->orig_msg = GNUNET_copy_message (message); + focc->operation_id = operation_id; + GNUNET_CONTAINER_DLL_insert_tail (rhc->focc_dll_head, + rhc->focc_dll_tail, focc); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + } + } + fopc = GNUNET_malloc (sizeof (struct ForwardedOperationContext)); + GNUNET_SERVER_client_keep (client); + fopc->client = client; + fopc->operation_id = operation_id; + fopc->type = OP_OVERLAY_CONNECT; + fopc->opc = + GNUNET_TESTBED_forward_operation_msg_ (peer->details.remote. + slave->controller, operation_id, + message, + &GST_forwarded_operation_reply_relay, + fopc); + fopc->timeout_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &GST_forwarded_operation_timeout, + fopc); + GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fopc); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + + peer2_controller = NULL; + if ((p2 >= GST_peer_list_size) || (NULL == GST_peer_list[p2])) + { + if ((peer2_host_id >= GST_slave_list_size) || + (NULL == GST_slave_list[peer2_host_id])) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + "0x%llx: Configuration of peer2's controller missing for connecting peers" + "%u and %u\n", operation_id, p1, p2); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + peer2_controller = GST_slave_list[peer2_host_id]->controller; + if (NULL == peer2_controller) + { + GNUNET_break (0); /* What's going on? */ + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + } + else + { + if (GNUNET_YES == GST_peer_list[p2]->is_remote) + peer2_controller = GST_peer_list[p2]->details.remote.slave->controller; + } + occ = GNUNET_malloc (sizeof (struct OverlayConnectContext)); + GNUNET_CONTAINER_DLL_insert_tail (occq_head, occq_tail, occ); + GNUNET_SERVER_client_keep (client); + occ->client = client; + occ->peer_id = p1; + occ->other_peer_id = p2; + occ->peer = GST_peer_list[p1]; + occ->op_id = GNUNET_ntohll (msg->operation_id); + occ->peer2_controller = peer2_controller; + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == occ->timeout_task); + occ->timeout_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &timeout_overlay_connect, occ); + /* Get the identity of the second peer */ + if (NULL != occ->peer2_controller) + { + struct GNUNET_TESTBED_PeerGetConfigurationMessage cmsg; + + cmsg.header.size = + htons (sizeof (struct GNUNET_TESTBED_PeerGetConfigurationMessage)); + cmsg.header.type = + htons (GNUNET_MESSAGE_TYPE_TESTBED_GET_PEER_CONFIGURATION); + cmsg.peer_id = msg->peer2; + cmsg.operation_id = msg->operation_id; + occ->opc = + GNUNET_TESTBED_forward_operation_msg_ (occ->peer2_controller, + occ->op_id, &cmsg.header, + &overlay_connect_get_config, + occ); + GNUNET_asprintf (&occ->emsg, + "0x%llx: Timeout while getting peer identity of peer " + "with id: %u", occ->op_id, occ->other_peer_id); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + GNUNET_TESTING_peer_get_identity (GST_peer_list[occ->other_peer_id]-> + details.local.peer, + &occ->other_peer_identity); + GNUNET_asprintf (&occ->emsg, + "0x%llx: Timeout while connecting to CORE of peer with " + "id: %u", occ->op_id, occ->peer_id); + occ->peer->reference_cnt++; + occ->cgh_ch = + GST_cache_get_handle_core (occ->peer_id, occ->peer->details.local.cfg, + occ_cache_get_handle_core_cb, occ, + &occ->other_peer_identity, + &overlay_connect_notify, occ); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Function to cleanup RemoteOverlayConnectCtx and any associated tasks + * with it + * + * @param rocc the RemoteOverlayConnectCtx + */ +static void +cleanup_rocc (struct RemoteOverlayConnectCtx *rocc) +{ + LOG_DEBUG ("0x%llx: Cleaning up rocc\n", rocc->op_id); + if (GNUNET_SCHEDULER_NO_TASK != rocc->attempt_connect_task_id) + GNUNET_SCHEDULER_cancel (rocc->attempt_connect_task_id); + if (GNUNET_SCHEDULER_NO_TASK != rocc->timeout_rocc_task_id) + GNUNET_SCHEDULER_cancel (rocc->timeout_rocc_task_id); + if (NULL != rocc->ohh) + GNUNET_TRANSPORT_offer_hello_cancel (rocc->ohh); + if (NULL != rocc->tcc.tch) + GNUNET_TRANSPORT_try_connect_cancel (rocc->tcc.tch); + if (GNUNET_SCHEDULER_NO_TASK != rocc->tcc.task) + GNUNET_SCHEDULER_cancel (rocc->tcc.task); + //GNUNET_TRANSPORT_disconnect (rocc->tcc.th_); + GST_cache_get_handle_done (rocc->tcc.cgh_th); + rocc->peer->reference_cnt--; + if ((GNUNET_YES == rocc->peer->destroy_flag) && + (0 == rocc->peer->reference_cnt)) + GST_destroy_peer (rocc->peer); + GNUNET_free_non_null (rocc->hello); + GNUNET_CONTAINER_DLL_remove (roccq_head, roccq_tail, rocc); + GNUNET_free (rocc); +} + + +/** + * Task to timeout rocc and cleanit up + * + * @param cls the RemoteOverlayConnectCtx + * @param tc the TaskContext from scheduler + */ +static void +timeout_rocc_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct RemoteOverlayConnectCtx *rocc = cls; + + GNUNET_assert (rocc->timeout_rocc_task_id != GNUNET_SCHEDULER_NO_TASK); + rocc->timeout_rocc_task_id = GNUNET_SCHEDULER_NO_TASK; + LOG_DEBUG ("0x%llx: rocc timed out\n", rocc->op_id); + cleanup_rocc (rocc); +} + + +/** + * Function called to notify transport users that another + * peer connected to us. + * + * @param cls closure + * @param new_peer the peer that connected + * @param ats performance data + * @param ats_count number of entries in ats (excluding 0-termination) + */ +static void +cache_transport_peer_connect_notify (void *cls, + const struct GNUNET_PeerIdentity *new_peer) +{ + struct RemoteOverlayConnectCtx *rocc = cls; + + LOG_DEBUG ("0x%llx: Request Overlay connect notify\n", rocc->op_id); + GNUNET_assert (0 == + memcmp (new_peer, &rocc->a_id, + sizeof (struct GNUNET_PeerIdentity))); + LOG_DEBUG ("0x%llx: Peer %4s connected\n", rocc->op_id, + GNUNET_i2s (&rocc->a_id)); + cleanup_rocc (rocc); +} + + +/** + * Task to offer the HELLO message to the peer and ask it to connect to the peer + * whose identity is in RemoteOverlayConnectCtx + * + * @param cls the RemoteOverlayConnectCtx + * @param tc the TaskContext from scheduler + */ +static void +attempt_connect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + + +/** + * Task that is run when hello has been sent + * + * @param cls the overlay connect context + * @param tc the scheduler task context; if tc->reason = + * GNUNET_SCHEDULER_REASON_TIMEOUT then sending HELLO failed; if + * GNUNET_SCHEDULER_REASON_READ_READY is succeeded + */ +static void +rocc_hello_sent_cb (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct RemoteOverlayConnectCtx *rocc = cls; + + rocc->ohh = NULL; + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == rocc->attempt_connect_task_id); + LOG_DEBUG ("0x%llx: HELLO of peer %4s sent to local peer with id: %u\n", + rocc->op_id, GNUNET_i2s (&rocc->a_id), rocc->peer->id); + if (GNUNET_SCHEDULER_REASON_TIMEOUT == tc->reason) + { + GNUNET_break (0); + rocc->attempt_connect_task_id = + GNUNET_SCHEDULER_add_now (&attempt_connect_task, rocc); + return; + } + if (GNUNET_SCHEDULER_REASON_READ_READY != tc->reason) + return; + rocc->tcc.task = GNUNET_SCHEDULER_add_now (&try_connect_task, &rocc->tcc); +} + + +/** + * Task to offer the HELLO message to the peer and ask it to connect to the peer + * whose identity is in RemoteOverlayConnectCtx + * + * @param cls the RemoteOverlayConnectCtx + * @param tc the TaskContext from scheduler + */ +static void +attempt_connect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct RemoteOverlayConnectCtx *rocc = cls; + + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != rocc->attempt_connect_task_id); + rocc->attempt_connect_task_id = GNUNET_SCHEDULER_NO_TASK; + LOG_DEBUG ("0x%llx: Offering HELLO of peer %4s to local peer with id: %u\n", + rocc->op_id, GNUNET_i2s (&rocc->a_id), rocc->peer->id); + rocc->ohh = + GNUNET_TRANSPORT_offer_hello (rocc->tcc.th_, rocc->hello, + rocc_hello_sent_cb, rocc); + if (NULL == rocc->ohh) + rocc->attempt_connect_task_id = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MILLISECONDS, + 100 + + GNUNET_CRYPTO_random_u32 + (GNUNET_CRYPTO_QUALITY_WEAK, 500)), + &attempt_connect_task, rocc); +} + + +/** + * Callback from cache with needed handles set + * + * @param cls the closure passed to GST_cache_get_handle_transport() + * @param ch the handle to CORE. Can be NULL if it is not requested + * @param th the handle to TRANSPORT. Can be NULL if it is not requested + * @param ignore_ peer identity which is ignored in this callback + */ +static void +rocc_cache_get_handle_transport_cb (void *cls, struct GNUNET_CORE_Handle *ch, + struct GNUNET_TRANSPORT_Handle *th, + const struct GNUNET_PeerIdentity *ignore_) +{ + struct RemoteOverlayConnectCtx *rocc = cls; + + if (NULL == th) + { + rocc->timeout_rocc_task_id = + GNUNET_SCHEDULER_add_now (&timeout_rocc_task, rocc); + return; + } + rocc->tcc.th_ = th; + rocc->tcc.pid = &rocc->a_id; + if (GNUNET_YES == + GNUNET_TRANSPORT_check_neighbour_connected (rocc->tcc.th_, rocc->tcc.pid)) + { + LOG_DEBUG ("0x%llx: Target peer %4s already connected to local peer: %u\n", + rocc->op_id, GNUNET_i2s (&rocc->a_id), rocc->peer->id); + cleanup_rocc (rocc); + return; + } + rocc->attempt_connect_task_id = + GNUNET_SCHEDULER_add_now (&attempt_connect_task, rocc); +} + + +/** + * Handler for GNUNET_MESSAGE_TYPE_TESTBED_REQUESTCONNECT messages + * + * @param cls NULL + * @param client identification of the client + * @param message the actual message + */ +void +GST_handle_remote_overlay_connect (void *cls, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct GNUNET_TESTBED_RemoteOverlayConnectMessage *msg; + struct RemoteOverlayConnectCtx *rocc; + struct Peer *peer; + uint32_t peer_id; + uint16_t msize; + uint16_t hsize; + + msize = ntohs (message->size); + if (sizeof (struct GNUNET_TESTBED_RemoteOverlayConnectMessage) >= msize) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + msg = (const struct GNUNET_TESTBED_RemoteOverlayConnectMessage *) message; + if ((NULL == msg->hello) || + (GNUNET_MESSAGE_TYPE_HELLO != ntohs (msg->hello->type))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + hsize = ntohs (msg->hello->size); + if ((sizeof (struct GNUNET_TESTBED_RemoteOverlayConnectMessage) + hsize) != + msize) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + peer_id = ntohl (msg->peer); + if ((peer_id >= GST_peer_list_size) || + (NULL == (peer = GST_peer_list[peer_id]))) + { + GNUNET_break_op (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + if (GNUNET_YES == peer->is_remote) + { + struct GNUNET_MessageHeader *msg2; + + msg2 = GNUNET_copy_message (message); + GNUNET_TESTBED_queue_message_ (peer->details.remote.slave->controller, + msg2); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + rocc = GNUNET_malloc (sizeof (struct RemoteOverlayConnectCtx)); + rocc->op_id = GNUNET_ntohll (msg->operation_id); + GNUNET_CONTAINER_DLL_insert_tail (roccq_head, roccq_tail, rocc); + memcpy (&rocc->a_id, &msg->peer_identity, + sizeof (struct GNUNET_PeerIdentity)); + LOG_DEBUG ("Received request for overlay connection with op_id: 0x%llx " + "from local peer %u to peer %4s with hello size: %u\n", + rocc->op_id, peer_id, GNUNET_i2s (&rocc->a_id), hsize); + rocc->peer = peer; + rocc->peer->reference_cnt++; + rocc->hello = GNUNET_malloc (hsize); + memcpy (rocc->hello, msg->hello, hsize); + rocc->tcc.cgh_th = + GST_cache_get_handle_transport (peer_id, rocc->peer->details.local.cfg, + &rocc_cache_get_handle_transport_cb, rocc, + &rocc->a_id, + &cache_transport_peer_connect_notify, + rocc); + rocc->timeout_rocc_task_id = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &timeout_rocc_task, rocc); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Clears all pending overlay connect contexts in queue + */ +void +GST_free_occq () +{ + struct OverlayConnectContext *occ; + + while (NULL != (occ = occq_head)) + cleanup_occ (occ); +} + + +/** + * Clears all pending remote overlay connect contexts in queue + */ +void +GST_free_roccq () +{ + struct RemoteOverlayConnectCtx *rocc; + + while (NULL != (rocc = roccq_head)) + cleanup_rocc (rocc); +} diff --git a/src/testbed/gnunet-testbed-profiler.c b/src/testbed/gnunet-testbed-profiler.c new file mode 100644 index 0000000..ebb82df --- /dev/null +++ b/src/testbed/gnunet-testbed-profiler.c @@ -0,0 +1,283 @@ +/* + This file is part of GNUnet. + (C) 2011, 2012 Christian Grothoff (and other contributing authors) + + GNUnet 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. + + 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file testbed/gnunet-testbed-profiler.c + * @brief Profiling driver for the testbed. + * @author Sree Harsha Totakura <sreeharsha@totakura.in> + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_util_lib.h" +#include "gnunet_testbed_service.h" +#include "testbed_api_hosts.h" + +/** + * Generic loggins shorthand + */ +#define LOG(kind,...) \ + GNUNET_log (kind, __VA_ARGS__) + + +/** + * Handle to global configuration + */ +struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Peer linking - topology operation + */ +struct GNUNET_TESTBED_Operation *topology_op; + +/** + * Abort task identifier + */ +static GNUNET_SCHEDULER_TaskIdentifier abort_task; + +/** + * Shutdown task identifier + */ +static GNUNET_SCHEDULER_TaskIdentifier shutdown_task; + +/** + * Global event mask for all testbed events + */ +uint64_t event_mask; + +/** + * Number of peers to be started by the profiler + */ +static unsigned int num_peers; + +/** + * Number of timeout failures to tolerate + */ +static unsigned int num_cont_fails; + +/** + * Continuous failures during overlay connect operations + */ +static unsigned int cont_fails; + +/** + * Links which are successfully established + */ +static unsigned int established_links; + +/** + * Links which are not successfully established + */ +static unsigned int failed_links; + +/** + * Global testing status + */ +static int result; + + +/** + * Shutdown nicely + * + * @param cls NULL + * @param tc the task context + */ +static void +do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + shutdown_task = GNUNET_SCHEDULER_NO_TASK; + if (GNUNET_SCHEDULER_NO_TASK != abort_task) + GNUNET_SCHEDULER_cancel (abort_task); + if (NULL != cfg) + GNUNET_CONFIGURATION_destroy (cfg); + GNUNET_SCHEDULER_shutdown (); /* Stop scheduler to shutdown testbed run */ +} + + +/** + * abort task to run on test timed out + * + * @param cls NULL + * @param tc the task context + */ +static void +do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + LOG (GNUNET_ERROR_TYPE_WARNING, "Aborting\n"); + abort_task = GNUNET_SCHEDULER_NO_TASK; + result = GNUNET_SYSERR; + if (GNUNET_SCHEDULER_NO_TASK != shutdown_task) + GNUNET_SCHEDULER_cancel (shutdown_task); + shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); +} + + +/** + * Function to print summary about how many overlay links we have made and how + * many failed + */ +static void +print_overlay_links_summary () +{ + static int printed_already; + + if (GNUNET_YES == printed_already) + return; + printed_already = GNUNET_YES; + printf ("%u links succeeded\n", established_links); + printf ("%u links failed due to timeouts\n", failed_links); +} + + +/** + * Controller event callback + * + * @param cls NULL + * @param event the controller event + */ +static void +controller_event_cb (void *cls, + const struct GNUNET_TESTBED_EventInformation *event) +{ + switch (event->type) + { + case GNUNET_TESTBED_ET_OPERATION_FINISHED: + /* Control reaches here when a peer linking operation fails */ + if (NULL != event->details.operation_finished.emsg) + { + printf ("F"); + fflush (stdout); + failed_links++; + if (++cont_fails > num_cont_fails) + { + printf ("\nAborting due to very high failure rate\n"); + print_overlay_links_summary (); + GNUNET_SCHEDULER_cancel (abort_task); + abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL); + return; + } + } + break; + case GNUNET_TESTBED_ET_CONNECT: + { + if (0 != cont_fails) + cont_fails--; + if (0 == established_links) + printf ("Establishing links. Please wait\n"); + printf ("."); + fflush (stdout); + established_links++; + } + break; + default: + GNUNET_break (0); + } +} + + +/** + * Signature of a main function for a testcase. + * + * @param cls closure + * @param num_peers number of peers in 'peers' + * @param peers handle to peers run in the testbed + */ +static void +test_run (void *cls, unsigned int num_peers, struct GNUNET_TESTBED_Peer **peers) +{ + result = GNUNET_OK; + fprintf (stdout, "\n"); + print_overlay_links_summary (); + fprintf (stdout, "Testbed running, waiting for keystroke to shut down\n"); + fflush (stdout); + (void) getc (stdin); + fprintf (stdout, "Shutting down. Please wait\n"); + fflush (stdout); + shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); +} + + +/** + * Main function that will be run by the scheduler. + * + * @param cls closure + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param config configuration + */ +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *config) +{ + if (NULL == args[0]) + { + fprintf (stderr, _("No hosts-file specified on command line\n")); + return; + } + if (0 == num_peers) + { + result = GNUNET_OK; + return; + } + cfg = GNUNET_CONFIGURATION_dup (config); + event_mask = 0; + event_mask |= (1LL << GNUNET_TESTBED_ET_CONNECT); + event_mask |= (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED); + GNUNET_TESTBED_run (args[0], cfg, num_peers, event_mask, controller_event_cb, + NULL, &test_run, NULL); + abort_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_abort, + NULL); +} + + +/** + * Main function. + * + * @return 0 on success + */ +int +main (int argc, char *const *argv) +{ + static const struct GNUNET_GETOPT_CommandLineOption options[] = { + {'p', "num-peers", "COUNT", + gettext_noop ("create COUNT number of peers"), + GNUNET_YES, &GNUNET_GETOPT_set_uint, &num_peers}, + {'e', "num-errors", "COUNT", + gettext_noop ("tolerate COUNT number of continious timeout failures"), + GNUNET_YES, &GNUNET_GETOPT_set_uint, &num_cont_fails}, + GNUNET_GETOPT_OPTION_END + }; + int ret; + + if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) + return 2; + result = GNUNET_SYSERR; + ret = + GNUNET_PROGRAM_run (argc, argv, + "gnunet-testbed-profiler [OPTIONS] hosts-file", + _("Profiler for testbed"), options, &run, NULL); + GNUNET_free ((void *) argv); + if (GNUNET_OK != ret) + return ret; + if (GNUNET_OK != result) + return 1; + return 0; +} diff --git a/src/testbed/gnunet_mpi_test.c b/src/testbed/gnunet_mpi_test.c new file mode 100644 index 0000000..fded9e2 --- /dev/null +++ b/src/testbed/gnunet_mpi_test.c @@ -0,0 +1,107 @@ +#include "platform.h" +#include "gnunet_util_lib.h" +#include <mpi.h> + +/** + * Generic logging shorthand + */ +#define LOG(kind,...) \ + GNUNET_log_from (kind, "gnunet-mpi-test", __VA_ARGS__) + +int +main (int argc, char *argv[]) +{ + char *msg; + char *filename; + char **argv2; + struct GNUNET_OS_Process *proc; + unsigned long code; + pid_t pid; + enum GNUNET_OS_ProcessStatusType proc_status; + int ntasks; + int rank; + int msg_size; + int ret; + unsigned int cnt; + + ret = GNUNET_SYSERR; + if (argc < 2) + { + printf ("Need arguments: gnunet-mpi-test <cmd> <cmd_args>"); + return 1; + } + if (MPI_SUCCESS != MPI_Init (&argc, &argv)) + { + GNUNET_break (0); + return 1; + } + if (MPI_SUCCESS != MPI_Comm_size (MPI_COMM_WORLD, &ntasks)) + { + GNUNET_break (0); + goto finalize; + } + if (MPI_SUCCESS != MPI_Comm_rank (MPI_COMM_WORLD, &rank)) + { + GNUNET_break (0); + goto finalize; + } + pid = getpid (); + (void) GNUNET_asprintf (&filename, "%d-%d.mpiout", (int) pid, rank); + msg_size = GNUNET_asprintf (&msg, "My rank is: %d\n", rank); + printf ("%s", msg); + if (msg_size == + GNUNET_DISK_fn_write (filename, msg, msg_size, + GNUNET_DISK_PERM_USER_READ | + GNUNET_DISK_PERM_GROUP_READ | + GNUNET_DISK_PERM_USER_WRITE | + GNUNET_DISK_PERM_GROUP_WRITE)) + ret = GNUNET_OK; + GNUNET_free (filename); + GNUNET_free (msg); + if (GNUNET_OK != ret) + { + GNUNET_break (0); + goto finalize; + } + + ret = GNUNET_SYSERR; + argv2 = GNUNET_malloc (sizeof (char *) * (argc)); + for (cnt = 1; cnt < argc; cnt++) + argv2[cnt - 1] = argv[cnt]; + proc = + GNUNET_OS_start_process_vap (GNUNET_NO, GNUNET_OS_INHERIT_STD_ALL, NULL, + NULL, argv2[0], argv2); + if (NULL == proc) + { + printf ("Cannot exec\n"); + GNUNET_free (argv2); + goto finalize; + } + do + { + (void) sleep (1); + ret = GNUNET_OS_process_status (proc, &proc_status, &code); + } + while (GNUNET_NO == ret); + GNUNET_free (argv2); + GNUNET_assert (GNUNET_NO != ret); + if (GNUNET_OK == ret) + { + if (0 != code) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Child terminated abnormally\n"); + ret = GNUNET_SYSERR; + GNUNET_break (0); + goto finalize; + } + } + else + GNUNET_break (0); + +finalize: + (void) MPI_Finalize (); + if (GNUNET_OK == ret) + return 0; + printf ("Something went wrong\n"); + return 1; +} diff --git a/src/testbed/ll_master.c b/src/testbed/ll_master.c new file mode 100644 index 0000000..98cef42 --- /dev/null +++ b/src/testbed/ll_master.c @@ -0,0 +1,92 @@ +/* + This file is part of GNUnet + (C) 2012 Christian Grothoff (and other contributing authors) + + GNUnet 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. + + 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file testbed/ll_master.c + * @brief The load level master. Creates child processes through LoadLeveler + * @author Sree Harsha Totakura <sreeharsha@totakura.in> + */ + +#include "platform.h" +#include "gnunet_util_lib.h" +#include <llapi.h> + +/** + * LL job information + */ +static struct LL_job job_info; + +/** + * Exit status + */ +static int status; + +/** + * Main function that will be run. + * + * @param cls closure + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param cfg configuration + */ +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + int ret; + + if (NULL == args[0]) + { + fprintf (stderr, _("Job command file not given. Exiting\n")); + return; + } + ret = llsubmit (args[0], NULL, //char *monitor_program, + NULL, //char *monitor_arg, + &job_info, LL_JOB_VERSION); + if (0 != ret) + return; + status = GNUNET_OK; +} + + +/** + * Main function + * + * @param argc the number of command line arguments + * @param argv command line arg array + * @return return code + */ +int +main (int argc, char **argv) +{ + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + int ret; + + status = GNUNET_SYSERR; + ret = + GNUNET_PROGRAM_run (argc, argv, "ll-master", + "LoadLeveler master process for starting child processes", + options, &run, NULL); + if (GNUNET_OK != ret) + return 1; + return (GNUNET_OK == status) ? 0 : 1; +} diff --git a/src/testbed/ll_monitor.c b/src/testbed/ll_monitor.c new file mode 100644 index 0000000..3a43b49 --- /dev/null +++ b/src/testbed/ll_monitor.c @@ -0,0 +1,76 @@ +/* + This file is part of GNUnet + (C) 2012 Christian Grothoff (and other contributing authors) + + GNUnet 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. + + 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file testbed/ll_monitor.c + * @brief The load level monitor process. This is called whenever a job event + * happens. This file is called with the following syntax: + * "monitor_program job_id user_arg state exit_status" + * @author Sree Harsha Totakura <sreeharsha@totakura.in> + */ + +#include "platform.h" +#include "gnunet_common.h" +#include <llapi.h> + + +/** + * Main function + * + * @param argc the number of command line arguments + * @param argv command line arg array + * @return return code + */ +int +main (int argc, char **argv) +{ + char *job_id; + char *user_arg; + char *state; + char *exit_status; + char *outfile; + FILE *out; + + if (5 != argc) + { + fprintf (stderr, "Invalid number of arguments\n"); + return 1; + } + job_id = argv[1]; + user_arg = argv[2]; + state = argv[3]; + exit_status = argv[4]; + PRINTF ("Job id: %s\n", job_id); + PRINTF ("\t User arg: %s \n", user_arg); + PRINTF ("\t Job state: %s \n", state); + PRINTF ("\t Exit status: %s \n", exit_status); + + if (-1 == asprintf (&outfile, "job-%s.status", job_id)) + return 1; + out = fopen (outfile, "a"); + if (NULL == out) + return 1; + fprintf (out, "Job id: %s\n", job_id); + fprintf (out, "\t User arg: %s \n", user_arg); + fprintf (out, "\t Job state: %s \n", state); + fprintf (out, "\t Exit status: %s \n", exit_status); + fclose (out); + return 0; +} diff --git a/src/testbed/overlay_topology.txt b/src/testbed/overlay_topology.txt new file mode 100644 index 0000000..420dbb6 --- /dev/null +++ b/src/testbed/overlay_topology.txt @@ -0,0 +1,5 @@ + +1:2|3 +3:4| 0| 1 +2: 3|1|0 +0: 2 diff --git a/src/testbed/sample.job b/src/testbed/sample.job new file mode 100755 index 0000000..da3ee47 --- /dev/null +++ b/src/testbed/sample.job @@ -0,0 +1,16 @@ +#!/bin/bash +# This job command file is called job.cmd +#@ job_type = parallel +#@ class = general +#@ node = 1 +#@ output = job$(jobid).out +#@ error = job$(jobid).err +#@ total_tasks=16 +#@ wall_clock_limit = 0:0:1 +#@ network.MPI = sn_all,not_shared,us +##@ ... other LoadLeveler keywords (see below) +#@ notification = always +#@ notify_user = totakura@in.tum.de +#@ queue + +#@ executable = /bin/bash diff --git a/src/testbed/sample_hosts.txt b/src/testbed/sample_hosts.txt new file mode 100644 index 0000000..5b66169 --- /dev/null +++ b/src/testbed/sample_hosts.txt @@ -0,0 +1,15 @@ +totakura@192.168.0.1:22 +totakura@192.168.0.2:22 +totakura@192.168.0.3:22 +totakura@192.168.0.4:22 +totakura@192.168.0.5:22 +totakura@192.168.0.6:22 +totakura@192.168.0.7:22 +totakura@192.168.0.8:22 +totakura@192.168.0.9:22 +totakura@192.168.0.10:22 +totakura@192.168.0.11:22 +totakura@192.168.0.12:22 +totakura@192.168.0.13:22 +totakura@192.168.0.14:22 +totakura@192.168.0.15:22 diff --git a/src/testbed/test_gnunet_helper_testbed.c b/src/testbed/test_gnunet_helper_testbed.c new file mode 100644 index 0000000..db889c1 --- /dev/null +++ b/src/testbed/test_gnunet_helper_testbed.c @@ -0,0 +1,249 @@ +/* + This file is part of GNUnet + (C) 2012 Christian Grothoff (and other contributing authors) + + GNUnet 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. + + 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file testbed/test_gnunet_helper_testbed.c + * @brief Testcase for testing gnunet-helper-testbed.c + * @author Sree Harsha Totakura <sreeharsha@totakura.in> + */ + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testbed_service.h" +#include <zlib.h> + +#include "testbed_api.h" +#include "testbed_helper.h" +#include "testbed_api_hosts.h" + +/** + * Generic logging shortcut + */ +#define LOG(kind,...) \ + GNUNET_log (kind, __VA_ARGS__) + + +/** + * Handle to the helper process + */ +static struct GNUNET_HELPER_Handle *helper; + +/** + * Message to helper + */ +static struct GNUNET_TESTBED_HelperInit *msg; + +/** + * Message send handle + */ +static struct GNUNET_HELPER_SendHandle *shandle; + +/** + * Abort task identifier + */ +static GNUNET_SCHEDULER_TaskIdentifier abort_task; + +/** + * Shutdown task identifier + */ +static GNUNET_SCHEDULER_TaskIdentifier shutdown_task; + +/** + * Configuratin handler + */ +static struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Global testing status + */ +static int result; + + +/** + * Shutdown nicely + * + * @param cls NULL + * @param tc the task context + */ +static void +do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if (GNUNET_SCHEDULER_NO_TASK != abort_task) + GNUNET_SCHEDULER_cancel (abort_task); + GNUNET_HELPER_stop (helper); + GNUNET_free_non_null (msg); + if (NULL != cfg) + GNUNET_CONFIGURATION_destroy (cfg); +} + + +/** + * abort task to run on test timed out + * + * @param cls NULL + * @param tc the task context + */ +static void +do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + abort_task = GNUNET_SCHEDULER_NO_TASK; + LOG (GNUNET_ERROR_TYPE_WARNING, "Test timedout -- Aborting\n"); + result = GNUNET_SYSERR; + if (NULL != shandle) + GNUNET_HELPER_send_cancel (shandle); + if (GNUNET_SCHEDULER_NO_TASK == shutdown_task) + shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); +} + + +/** + * Continuation function. + * + * @param cls closure + * @param result GNUNET_OK on success, + * GNUNET_NO if helper process died + * GNUNET_SYSERR during GNUNET_HELPER_stop + */ +static void +cont_cb (void *cls, int result) +{ + shandle = NULL; + LOG (GNUNET_ERROR_TYPE_DEBUG, "Message sent\n"); + GNUNET_assert (GNUNET_OK == result); +} + + +/** + * Functions with this signature are called whenever a + * complete message is received by the tokenizer. + * + * Do not call GNUNET_SERVER_mst_destroy in callback + * + * @param cls closure + * @param client identification of the client + * @param message the actual message + * + * @return GNUNET_OK on success, GNUNET_SYSERR to stop further processing + */ +static int +mst_cb (void *cls, void *client, const struct GNUNET_MessageHeader *message) +{ + const struct GNUNET_TESTBED_HelperReply *msg; + char *config; + uLongf config_size; + uLongf xconfig_size; + + msg = (const struct GNUNET_TESTBED_HelperReply *) message; + config_size = 0; + xconfig_size = 0; + GNUNET_assert (sizeof (struct GNUNET_TESTBED_HelperReply) < + ntohs (msg->header.size)); + GNUNET_assert (GNUNET_MESSAGE_TYPE_TESTBED_HELPER_REPLY == + ntohs (msg->header.type)); + config_size = (uLongf) ntohs (msg->config_size); + xconfig_size = + (uLongf) (ntohs (msg->header.size) - + sizeof (struct GNUNET_TESTBED_HelperReply)); + config = GNUNET_malloc (config_size); + GNUNET_assert (Z_OK == + uncompress ((Bytef *) config, &config_size, + (const Bytef *) &msg[1], xconfig_size)); + GNUNET_free (config); + if (GNUNET_SCHEDULER_NO_TASK == shutdown_task) + shutdown_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 1), + &do_shutdown, NULL); + return GNUNET_OK; +} + + +/** + * Callback that will be called when the helper process dies. This is not called + * when the helper process is stoped using GNUNET_HELPER_stop() + * + * @param cls the closure from GNUNET_HELPER_start() + */ +static void +exp_cb (void *cls) +{ + helper = NULL; + result = GNUNET_SYSERR; +} + + +/** + * Main function that will be run. + * + * @param cls closure + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param cfg configuration + */ +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg2) +{ + static char *const binary_argv[] = { + "gnunet-helper-testbed", + NULL + }; + const char *controller_name = "127.0.0.1"; + + helper = + GNUNET_HELPER_start (GNUNET_YES, "gnunet-helper-testbed", binary_argv, + &mst_cb, &exp_cb, NULL); + GNUNET_assert (NULL != helper); + cfg = GNUNET_CONFIGURATION_dup (cfg2); + msg = GNUNET_TESTBED_create_helper_init_msg_ (controller_name, NULL, cfg); + shandle = + GNUNET_HELPER_send (helper, &msg->header, GNUNET_NO, &cont_cb, NULL); + GNUNET_assert (NULL != shandle); + abort_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MINUTES, 1), &do_abort, + NULL); +} + + +/** + * Main function + * + * @param argc the number of command line arguments + * @param argv command line arg array + * @return return code + */ +int +main (int argc, char **argv) +{ + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + + result = GNUNET_OK; + if (GNUNET_OK != + GNUNET_PROGRAM_run (argc, argv, "test_gnunet_helper_testbed", + "Testcase for testing gnunet-helper-testbed.c", + options, &run, NULL)) + return 1; + return (GNUNET_OK == result) ? 0 : 1; +} + +/* end of test_gnunet_helper_testbed.c */ diff --git a/src/testbed/test_testbed_api.c b/src/testbed/test_testbed_api.c new file mode 100644 index 0000000..c5c4486 --- /dev/null +++ b/src/testbed/test_testbed_api.c @@ -0,0 +1,464 @@ +/* + This file is part of GNUnet + (C) 2008--2012 Christian Grothoff (and other contributing authors) + + GNUnet 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. + + 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file testbed/test_testbed_api.c + * @brief testcases for the testbed api + * @author Sree Harsha Totakura + */ + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_dht_service.h" +#include "gnunet_testing_lib.h" +#include "gnunet_testbed_service.h" + +/** + * Generic logging shortcut + */ +#define LOG(kind,...) \ + GNUNET_log (kind, __VA_ARGS__) + +/** + * Relative time seconds shorthand + */ +#define TIME_REL_SECS(sec) \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, sec) + +/** + * Our localhost + */ +static struct GNUNET_TESTBED_Host *host; + +/** + * The controller process + */ +static struct GNUNET_TESTBED_ControllerProc *cp; + +/** + * The controller handle + */ +static struct GNUNET_TESTBED_Controller *controller; + +/** + * A neighbouring host + */ +static struct GNUNET_TESTBED_Host *neighbour; + +/** + * Handle for neighbour registration + */ +static struct GNUNET_TESTBED_HostRegistrationHandle *reg_handle; + +/** + * Handle for a peer + */ +static struct GNUNET_TESTBED_Peer *peer; + +/** + * Handle to configuration + */ +static struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Handle to operation + */ +static struct GNUNET_TESTBED_Operation *operation; + +/** + * Handle to peer's DHT service + */ +static struct GNUNET_DHT_Handle *dht_handle; + +/** + * Abort task identifier + */ +static GNUNET_SCHEDULER_TaskIdentifier abort_task; + +/** + * The testing result + */ +static int result; + + +/** + * Enumeration of sub testcases + */ +enum Test +{ + /** + * Test cases which are not covered by the below ones + */ + OTHER, + + /** + * Test where we get a peer config from controller + */ + PEER_GETCONFIG, + + /** + * Test where we connect to a service running on the peer + */ + PEER_SERVICE_CONNECT, + + /** + * Test where we get a peer's identity from controller + */ + PEER_DESTROY, +}; + +/** + * Testing status + */ +static enum Test sub_test; + +/** + * Shutdown nicely + * + * @param cls NULL + * @param tc the task context + */ +static void +do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down...\n"); + if (GNUNET_SCHEDULER_NO_TASK != abort_task) + GNUNET_SCHEDULER_cancel (abort_task); + if (NULL != reg_handle) + GNUNET_TESTBED_cancel_registration (reg_handle); + GNUNET_TESTBED_controller_disconnect (controller); + GNUNET_CONFIGURATION_destroy (cfg); + if (NULL != cp) + GNUNET_TESTBED_controller_stop (cp); + GNUNET_TESTBED_host_destroy (neighbour); + GNUNET_TESTBED_host_destroy (host); +} + + +/** + * abort task to run on test timed out + * + * @param cls NULL + * @param tc the task context + */ +static void +do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + LOG (GNUNET_ERROR_TYPE_WARNING, "Test timedout -- Aborting\n"); + abort_task = GNUNET_SCHEDULER_NO_TASK; + do_shutdown (cls, tc); +} + + +/** + * Adapter function called to establish a connection to + * a service. + * + * @param cls closure + * @param cfg configuration of the peer to connect to; will be available until + * GNUNET_TESTBED_operation_done() is called on the operation returned + * from GNUNET_TESTBED_service_connect() + * @return service handle to return in 'op_result', NULL on error + */ +static void * +dht_connect_adapter (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + GNUNET_assert (NULL == cls); + GNUNET_assert (OTHER == sub_test); + sub_test = PEER_SERVICE_CONNECT; + dht_handle = GNUNET_DHT_connect (cfg, 10); + return dht_handle; +} + + +/** + * Adapter function called to destroy a connection to + * a service. + * + * @param cls closure + * @param op_result service handle returned from the connect adapter + */ +static void +dht_disconnect_adapter (void *cls, void *op_result) +{ + GNUNET_assert (NULL != op_result); + GNUNET_assert (op_result == dht_handle); + GNUNET_DHT_disconnect (dht_handle); + dht_handle = NULL; + GNUNET_assert (PEER_SERVICE_CONNECT == sub_test); + GNUNET_assert (NULL != operation); + operation = GNUNET_TESTBED_peer_stop (peer, NULL, NULL); + GNUNET_assert (NULL != operation); +} + + +/** + * Callback to be called when a service connect operation is completed + * + * @param cls the callback closure from functions generating an operation + * @param op the operation that has been finished + * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter() + * @param emsg error message in case the operation has failed; will be NULL if + * operation has executed successfully. + */ +static void +service_connect_comp_cb (void *cls, struct GNUNET_TESTBED_Operation *op, + void *ca_result, const char *emsg) +{ + switch (sub_test) + { + case PEER_SERVICE_CONNECT: + GNUNET_assert (operation == op); + GNUNET_assert (NULL == emsg); + GNUNET_assert (NULL == cls); + GNUNET_assert (ca_result == dht_handle); + GNUNET_TESTBED_operation_done (operation); /* This results in call to + * disconnect adapter */ + break; + default: + GNUNET_assert (0); + } +} + + + +/** + * Callback to be called when the requested peer information is available + * + * @param cb_cls the closure from GNUNET_TETSBED_peer_get_information() + * @param op the operation this callback corresponds to + * @param pinfo the result; will be NULL if the operation has failed + * @param emsg error message if the operation has failed; will be NULL if the + * operation is successfull + */ +static void +peerinfo_cb (void *cb_cls, struct GNUNET_TESTBED_Operation *op, + const struct GNUNET_TESTBED_PeerInformation *pinfo, + const char *emsg) +{ + switch (sub_test) + { + case PEER_GETCONFIG: + GNUNET_assert (NULL != pinfo); + GNUNET_assert (NULL == emsg); + GNUNET_assert (NULL == cb_cls); + GNUNET_assert (operation == op); + GNUNET_assert (GNUNET_TESTBED_PIT_CONFIGURATION == pinfo->pit); + GNUNET_assert (NULL != pinfo->result.cfg); + sub_test = PEER_DESTROY; + GNUNET_TESTBED_operation_done (operation); + operation = GNUNET_TESTBED_peer_destroy (peer); + break; + default: + GNUNET_assert (0); + } +} + + +/** + * Signature of the event handler function called by the + * respective event controller. + * + * @param cls closure + * @param event information about the event + */ +static void +controller_cb (void *cls, const struct GNUNET_TESTBED_EventInformation *event) +{ + switch (event->type) + { + case GNUNET_TESTBED_ET_OPERATION_FINISHED: + switch (sub_test) + { + case PEER_DESTROY: + GNUNET_assert (event->details.operation_finished.operation == operation); + GNUNET_assert (NULL == event->details.operation_finished.op_cls); + GNUNET_assert (NULL == event->details.operation_finished.emsg); + GNUNET_assert (NULL == event->details.operation_finished.generic); + GNUNET_TESTBED_operation_done (operation); + GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); + break; + case PEER_SERVICE_CONNECT: + GNUNET_assert (event->details.operation_finished.operation == operation); + GNUNET_assert (NULL == event->details.operation_finished.op_cls); + GNUNET_assert (NULL == event->details.operation_finished.emsg); + GNUNET_assert (NULL != dht_handle); + GNUNET_assert (event->details.operation_finished.generic == dht_handle); + break; + default: + GNUNET_assert (0); + break; + } + break; + case GNUNET_TESTBED_ET_PEER_START: + GNUNET_assert (event->details.peer_start.host == host); + GNUNET_assert (event->details.peer_start.peer == peer); + GNUNET_assert (OTHER == sub_test); + GNUNET_TESTBED_operation_done (operation); + operation = + GNUNET_TESTBED_service_connect (NULL, peer, "dht", + &service_connect_comp_cb, NULL, + &dht_connect_adapter, + &dht_disconnect_adapter, NULL); + GNUNET_assert (NULL != operation); + break; + case GNUNET_TESTBED_ET_PEER_STOP: + GNUNET_assert (event->details.peer_stop.peer == peer); + GNUNET_assert (PEER_SERVICE_CONNECT == sub_test); + result = GNUNET_YES; + sub_test = PEER_GETCONFIG; + GNUNET_TESTBED_operation_done (operation); + operation = + GNUNET_TESTBED_peer_get_information (peer, + GNUNET_TESTBED_PIT_CONFIGURATION, + &peerinfo_cb, NULL); + break; + default: + GNUNET_assert (0); /* We should never reach this state */ + } +} + + +/** + * Functions of this signature are called when a peer has been successfully + * created + * + * @param cls the closure from GNUNET_TESTBED_peer_create() + * @param peer the handle for the created peer; NULL on any error during + * creation + * @param emsg NULL if peer is not NULL; else MAY contain the error description + */ +static void +peer_create_cb (void *cls, struct GNUNET_TESTBED_Peer *peer, const char *emsg) +{ + struct GNUNET_TESTBED_Peer **peer_ptr; + + peer_ptr = cls; + GNUNET_assert (NULL != peer); + GNUNET_assert (NULL != peer_ptr); + *peer_ptr = peer; + GNUNET_TESTBED_operation_done (operation); + operation = GNUNET_TESTBED_peer_start (NULL, peer, NULL, NULL); + GNUNET_assert (NULL != operation); +} + + +/** + * Callback which will be called to after a host registration succeeded or failed + * + * @param cls the host which has been registered + * @param emsg the error message; NULL if host registration is successful + */ +static void +registration_comp (void *cls, const char *emsg) +{ + GNUNET_assert (cls == neighbour); + reg_handle = NULL; + operation = + GNUNET_TESTBED_peer_create (controller, host, cfg, &peer_create_cb, + &peer); + GNUNET_assert (NULL != operation); +} + + +/** + * Callback to signal successfull startup of the controller process + * + * @param cls the closure from GNUNET_TESTBED_controller_start() + * @param cfg the configuration with which the controller has been started; + * NULL if status is not GNUNET_OK + * @param status GNUNET_OK if the startup is successfull; GNUNET_SYSERR if not, + * GNUNET_TESTBED_controller_stop() shouldn't be called in this case + */ +static void +status_cb (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg, int status) +{ + uint64_t event_mask; + + GNUNET_assert (GNUNET_OK == status); + event_mask = 0; + event_mask |= (1L << GNUNET_TESTBED_ET_PEER_START); + event_mask |= (1L << GNUNET_TESTBED_ET_PEER_STOP); + event_mask |= (1L << GNUNET_TESTBED_ET_CONNECT); + event_mask |= (1L << GNUNET_TESTBED_ET_OPERATION_FINISHED); + controller = + GNUNET_TESTBED_controller_connect (cfg, host, event_mask, &controller_cb, + NULL); + GNUNET_assert (NULL != controller); + neighbour = GNUNET_TESTBED_host_create ("localhost", NULL, 0); + GNUNET_assert (NULL != neighbour); + reg_handle = + GNUNET_TESTBED_register_host (controller, neighbour, ®istration_comp, + neighbour); + GNUNET_assert (NULL != reg_handle); +} + + + +/** + * Main run function. + * + * @param cls NULL + * @param args arguments passed to GNUNET_PROGRAM_run + * @param cfgfile the path to configuration file + * @param cfg the configuration file handle + */ +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *config) +{ + host = GNUNET_TESTBED_host_create (NULL, NULL, 0); + GNUNET_assert (NULL != host); + cfg = GNUNET_CONFIGURATION_dup (config); + cp = GNUNET_TESTBED_controller_start ("127.0.0.1", host, cfg, status_cb, + NULL); + abort_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MINUTES, 5), &do_abort, + NULL); +} + + +/** + * Main function + */ +int +main (int argc, char **argv) +{ + int ret; + + char *const argv2[] = { "test_testbed_api", + "-c", "test_testbed_api.conf", + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + + result = GNUNET_SYSERR; + ret = + GNUNET_PROGRAM_run ((sizeof (argv2) / sizeof (char *)) - 1, argv2, + "test_testbed_api", "nohelp", options, &run, NULL); + if ((GNUNET_OK != ret) || (GNUNET_OK != result)) + return 1; + return 0; +} + +/* end of test_testbed_api.c */ diff --git a/src/testbed/test_testbed_api.conf b/src/testbed/test_testbed_api.conf new file mode 100644 index 0000000..52e0c9b --- /dev/null +++ b/src/testbed/test_testbed_api.conf @@ -0,0 +1,91 @@ +[testbed] +AUTOSTART = NO +PORT = 12113 +ACCEPT_FROM = 127.0.0.1; +HOSTNAME = localhost +NEIGHBOUR_LIMIT = 100 +TOPOLOGY = RANDOM +#PREFIX = xterm -geometry 100x85 -T peer1 -e libtool --mode=execute gdb --args + +[fs] +AUTOSTART = NO + +[resolver] +AUTOSTART = NO + +[mesh] +AUTOSTART = NO + +[dht] +AUTOSTART = NO + +[block] +plugins = dht test + +[dhtcache] +QUOTA = 1 MB +DATABASE = sqlite + +[transport] +PLUGINS = tcp +ACCEPT_FROM6 = ::1; +ACCEPT_FROM = 127.0.0.1; +NEIGHBOUR_LIMIT = 50 +PORT = 12365 + +[ats] +WAN_QUOTA_OUT = 3932160 +WAN_QUOTA_IN = 3932160 + +[core] +PORT = 12092 +AUTOSTART = YES + +[arm] +DEFAULTSERVICES = core transport +PORT = 12366 + +[transport-tcp] +TIMEOUT = 300 s +PORT = 12368 + +[TESTING] +NUM_PEERS = 5 +WEAKRANDOM = YES +HOSTKEYSFILE = ../../contrib/testing_hostkeys.dat +MAX_CONCURRENT_SSH = 10 +USE_PROGRESSBARS = YES +PEERGROUP_TIMEOUT = 2400 s + +[gnunetd] +HOSTKEY = $SERVICEHOME/.hostkey + +[PATHS] +SERVICEHOME = /tmp/test-testbed/ + +[dns] +AUTOSTART = NO + +[nse] +AUTOSTART = NO + +[vpn] +AUTOSTART = NO + +[nat] +RETURN_LOCAL_ADDRESSES = YES + +[gns-helper-service-w32] +AUTOSTART = NO + +[consensus] +AUTOSTART = NO + +[gns] +AUTOSTART = NO + +[statistics] +AUTOSTART = NO + +[peerinfo] +NO_IO = YES diff --git a/src/testbed/test_testbed_api_2peers_1controller.c b/src/testbed/test_testbed_api_2peers_1controller.c new file mode 100644 index 0000000..d5616c2 --- /dev/null +++ b/src/testbed/test_testbed_api_2peers_1controller.c @@ -0,0 +1,532 @@ +/* + This file is part of GNUnet + (C) 2008--2012 Christian Grothoff (and other contributing authors) + + GNUnet 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. + + 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file testbed/test_testbed_api_2peers_1controller.c + * @brief testcases for the testbed api: 2 peers are configured, started and + * connected together. The 2 peer reside on a single controller. + * @author Sree Harsha Totakura + */ + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testing_lib.h" +#include "gnunet_testbed_service.h" + + +/** + * Generic logging shortcut + */ +#define LOG(kind,...) \ + GNUNET_log (kind, __VA_ARGS__) + +/** + * Relative time seconds shorthand + */ +#define TIME_REL_SECS(sec) \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, sec) + +/** + * Peer context + */ +struct PeerContext +{ + /** + * The peer handle + */ + struct GNUNET_TESTBED_Peer *peer; + + /** + * Operations involving this peer + */ + struct GNUNET_TESTBED_Operation *operation; + + /** + * set to GNUNET_YES when peer is started + */ + int is_running; +}; + +/** + * Our localhost + */ +static struct GNUNET_TESTBED_Host *host; + +/** + * The controller process + */ +static struct GNUNET_TESTBED_ControllerProc *cp; + +/** + * The controller handle + */ +static struct GNUNET_TESTBED_Controller *controller; + +/** + * A neighbouring host + */ +static struct GNUNET_TESTBED_Host *neighbour; + +/** + * Handle for neighbour registration + */ +static struct GNUNET_TESTBED_HostRegistrationHandle *reg_handle; + +/** + * peer 1 + */ +static struct PeerContext peer1; + +/** + * peer2 + */ +static struct PeerContext peer2; + +/** + * Handle to configuration + */ +static struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Handle to operations involving both peers + */ +static struct GNUNET_TESTBED_Operation *common_operation; + +/** + * Abort task identifier + */ +static GNUNET_SCHEDULER_TaskIdentifier abort_task; + +/** + * Delayed connect job identifier + */ +static GNUNET_SCHEDULER_TaskIdentifier delayed_connect_task; + +/** + * Different stages in testing + */ +enum Stage +{ + + /** + * Initial stage + */ + INIT, + + /** + * peers are created + */ + PEERS_CREATED, + + /** + * peers are started + */ + PEERS_STARTED, + + /** + * peers are connected + */ + PEERS_CONNECTED, + + /** + * Peers are connected once again (this should not fail as they are already connected) + */ + PEERS_CONNECTED_2, + + /** + * peers are stopped + */ + PEERS_STOPPED, + + /** + * Final success stage + */ + SUCCESS +}; + +/** + * The testing result + */ +static enum Stage result; + +/** + * shortcut to exit during failure + */ +#define FAIL_TEST(cond) do { \ + if (!(cond)) { \ + GNUNET_break(0); \ + if (GNUNET_SCHEDULER_NO_TASK != abort_task) \ + GNUNET_SCHEDULER_cancel (abort_task); \ + abort_task = GNUNET_SCHEDULER_NO_TASK; \ + GNUNET_SCHEDULER_add_now (do_shutdown, NULL); \ + return; \ + } \ + } while (0) + +/** + * Shutdown nicely + * + * @param cls NULL + * @param tc the task context + */ +static void +do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if (GNUNET_SCHEDULER_NO_TASK != abort_task) + GNUNET_SCHEDULER_cancel (abort_task); + if (GNUNET_SCHEDULER_NO_TASK != delayed_connect_task) + GNUNET_SCHEDULER_cancel (delayed_connect_task); + if (NULL != reg_handle) + GNUNET_TESTBED_cancel_registration (reg_handle); + GNUNET_TESTBED_controller_disconnect (controller); + GNUNET_CONFIGURATION_destroy (cfg); + if (NULL != cp) + GNUNET_TESTBED_controller_stop (cp); + GNUNET_TESTBED_host_destroy (neighbour); + GNUNET_TESTBED_host_destroy (host); +} + + +/** + * abort task to run on test timed out + * + * @param cls NULL + * @param tc the task context + */ +static void +do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + LOG (GNUNET_ERROR_TYPE_WARNING, "Test timedout -- Aborting\n"); + abort_task = GNUNET_SCHEDULER_NO_TASK; + do_shutdown (cls, tc); +} + + +/** + * Callback to be called when an operation is completed + * + * @param cls the callback closure from functions generating an operation + * @param op the operation that has been finished + * @param emsg error message in case the operation has failed; will be NULL if + * operation has executed successfully. + */ +static void +op_comp_cb (void *cls, struct GNUNET_TESTBED_Operation *op, const char *emsg); + + +/** + * task for delaying a connect + * + * @param cls NULL + * @param tc the task context + */ +static void +do_delayed_connect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + delayed_connect_task = GNUNET_SCHEDULER_NO_TASK; + FAIL_TEST (NULL == common_operation); + common_operation = + GNUNET_TESTBED_overlay_connect (NULL, &op_comp_cb, NULL, peer1.peer, + peer2.peer); +} + + +/** + * Callback to be called when an operation is completed + * + * @param cls the callback closure from functions generating an operation + * @param op the operation that has been finished + * @param emsg error message in case the operation has failed; will be NULL if + * operation has executed successfully. + */ +static void +op_comp_cb (void *cls, struct GNUNET_TESTBED_Operation *op, const char *emsg) +{ + FAIL_TEST (common_operation == op); + switch (result) + { + case PEERS_STARTED: + FAIL_TEST (NULL == peer1.operation); + FAIL_TEST (NULL == peer2.operation); + FAIL_TEST (NULL != common_operation); + break; + case PEERS_CONNECTED: + FAIL_TEST (NULL == peer1.operation); + FAIL_TEST (NULL == peer2.operation); + FAIL_TEST (NULL != common_operation); + break; + default: + FAIL_TEST (0); + } +} + + +/** + * Signature of the event handler function called by the + * respective event controller. + * + * @param cls closure + * @param event information about the event + */ +static void +controller_cb (void *cls, const struct GNUNET_TESTBED_EventInformation *event) +{ + switch (event->type) + { + case GNUNET_TESTBED_ET_OPERATION_FINISHED: /* Will be reached when we destroy peers */ + FAIL_TEST (PEERS_STOPPED == result); + FAIL_TEST (NULL == event->details.operation_finished.op_cls); + FAIL_TEST (NULL == event->details.operation_finished.emsg); + FAIL_TEST (NULL == event->details.operation_finished.generic); + if (event->details.operation_finished.operation == peer1.operation) + { + GNUNET_TESTBED_operation_done (peer1.operation); + peer1.operation = NULL; + peer1.peer = NULL; + } + else if (event->details.operation_finished.operation == peer2.operation) + { + GNUNET_TESTBED_operation_done (peer2.operation); + peer2.operation = NULL; + peer2.peer = NULL; + } + else + FAIL_TEST (0); + if ((NULL == peer1.peer) && (NULL == peer2.peer)) + { + result = SUCCESS; + GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); + } + break; + case GNUNET_TESTBED_ET_PEER_START: + FAIL_TEST (INIT == result); + FAIL_TEST (event->details.peer_start.host == host); + if (event->details.peer_start.peer == peer1.peer) + { + peer1.is_running = GNUNET_YES; + GNUNET_TESTBED_operation_done (peer1.operation); + peer1.operation = NULL; + } + else if (event->details.peer_start.peer == peer2.peer) + { + peer2.is_running = GNUNET_YES; + GNUNET_TESTBED_operation_done (peer2.operation); + peer2.operation = NULL; + } + else + FAIL_TEST (0); + if ((GNUNET_YES == peer1.is_running) && (GNUNET_YES == peer2.is_running)) + { + result = PEERS_STARTED; + common_operation = + GNUNET_TESTBED_overlay_connect (NULL, &op_comp_cb, NULL, peer1.peer, + peer2.peer); + } + break; + case GNUNET_TESTBED_ET_PEER_STOP: + FAIL_TEST (PEERS_CONNECTED_2 == result); + if (event->details.peer_stop.peer == peer1.peer) + { + peer1.is_running = GNUNET_NO; + GNUNET_TESTBED_operation_done (peer1.operation); + peer1.operation = GNUNET_TESTBED_peer_destroy (peer1.peer); + } + else if (event->details.peer_stop.peer == peer2.peer) + { + peer2.is_running = GNUNET_NO; + GNUNET_TESTBED_operation_done (peer2.operation); + peer2.operation = GNUNET_TESTBED_peer_destroy (peer2.peer); + } + else + FAIL_TEST (0); + if ((GNUNET_NO == peer1.is_running) && (GNUNET_NO == peer2.is_running)) + result = PEERS_STOPPED; + break; + case GNUNET_TESTBED_ET_CONNECT: + switch (result) + { + case PEERS_STARTED: + FAIL_TEST (NULL == peer1.operation); + FAIL_TEST (NULL == peer2.operation); + FAIL_TEST (NULL != common_operation); + FAIL_TEST ((event->details.peer_connect.peer1 == peer1.peer) && + (event->details.peer_connect.peer2 == peer2.peer)); + GNUNET_TESTBED_operation_done (common_operation); + common_operation = NULL; + result = PEERS_CONNECTED; + LOG (GNUNET_ERROR_TYPE_DEBUG, "Peers connected\n"); + delayed_connect_task = + GNUNET_SCHEDULER_add_delayed (TIME_REL_SECS (3), &do_delayed_connect, + NULL); + break; + case PEERS_CONNECTED: + FAIL_TEST (NULL == peer1.operation); + FAIL_TEST (NULL == peer2.operation); + FAIL_TEST (NULL != common_operation); + GNUNET_TESTBED_operation_done (common_operation); + common_operation = NULL; + result = PEERS_CONNECTED_2; + LOG (GNUNET_ERROR_TYPE_DEBUG, "Peers connected again\n"); + peer1.operation = GNUNET_TESTBED_peer_stop (peer1.peer, NULL, NULL); + peer2.operation = GNUNET_TESTBED_peer_stop (peer2.peer, NULL, NULL); + break; + default: + FAIL_TEST (0); + } + break; + default: + FAIL_TEST (0); + }; +} + + +/** + * Functions of this signature are called when a peer has been successfully + * created + * + * @param cls the closure from GNUNET_TESTBED_peer_create() + * @param peer the handle for the created peer; NULL on any error during + * creation + * @param emsg NULL if peer is not NULL; else MAY contain the error description + */ +static void +peer_create_cb (void *cls, struct GNUNET_TESTBED_Peer *peer, const char *emsg) +{ + struct PeerContext *pc = cls; + + FAIL_TEST (NULL != pc->operation); + FAIL_TEST (NULL != peer); + FAIL_TEST (NULL == pc->peer); + pc->peer = peer; + GNUNET_TESTBED_operation_done (pc->operation); + pc->operation = GNUNET_TESTBED_peer_start (NULL, pc->peer, NULL, NULL); +} + + +/** + * Callback which will be called to after a host registration succeeded or failed + * + * @param cls the host which has been registered + * @param emsg the error message; NULL if host registration is successful + */ +static void +registration_comp (void *cls, const char *emsg) +{ + FAIL_TEST (cls == neighbour); + reg_handle = NULL; + peer1.operation = + GNUNET_TESTBED_peer_create (controller, host, cfg, &peer_create_cb, + &peer1); + peer2.operation = + GNUNET_TESTBED_peer_create (controller, host, cfg, &peer_create_cb, + &peer2); + FAIL_TEST (NULL != peer1.operation); + FAIL_TEST (NULL != peer2.operation); +} + + +/** + * Callback to signal successfull startup of the controller process + * + * @param cls the closure from GNUNET_TESTBED_controller_start() + * @param cfg the configuration with which the controller has been started; + * NULL if status is not GNUNET_OK + * @param status GNUNET_OK if the startup is successfull; GNUNET_SYSERR if not, + * GNUNET_TESTBED_controller_stop() shouldn't be called in this case + */ +static void +status_cb (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg, int status) +{ + uint64_t event_mask; + + if (GNUNET_OK != status) + { + cp = NULL; + FAIL_TEST (0); + } + event_mask = 0; + event_mask |= (1L << GNUNET_TESTBED_ET_PEER_START); + event_mask |= (1L << GNUNET_TESTBED_ET_PEER_STOP); + event_mask |= (1L << GNUNET_TESTBED_ET_CONNECT); + event_mask |= (1L << GNUNET_TESTBED_ET_OPERATION_FINISHED); + controller = + GNUNET_TESTBED_controller_connect (cfg, host, event_mask, &controller_cb, + NULL); + FAIL_TEST (NULL != controller); + neighbour = GNUNET_TESTBED_host_create ("localhost", NULL, 0); + FAIL_TEST (NULL != neighbour); + reg_handle = + GNUNET_TESTBED_register_host (controller, neighbour, ®istration_comp, + neighbour); + FAIL_TEST (NULL != reg_handle); +} + + + +/** + * Main run function. + * + * @param cls NULL + * @param args arguments passed to GNUNET_PROGRAM_run + * @param cfgfile the path to configuration file + * @param cfg the configuration file handle + */ +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *config) +{ + host = GNUNET_TESTBED_host_create (NULL, NULL, 0); + FAIL_TEST (NULL != host); + cfg = GNUNET_CONFIGURATION_dup (config); + cp = GNUNET_TESTBED_controller_start ("127.0.0.1", host, cfg, status_cb, + NULL); + abort_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MINUTES, 3), &do_abort, + NULL); +} + + +/** + * Main function + */ +int +main (int argc, char **argv) +{ + int ret; + + char *const argv2[] = { "test_testbed_api_2peers_1controller", + "-c", "test_testbed_api.conf", + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + result = INIT; + ret = + GNUNET_PROGRAM_run ((sizeof (argv2) / sizeof (char *)) - 1, argv2, + "test_testbed_api_2peers_1controller", "nohelp", + options, &run, NULL); + if ((GNUNET_OK != ret) || (SUCCESS != result)) + return 1; + return 0; +} + +/* end of test_testbed_api_2peers_1controller.c */ diff --git a/src/testbed/test_testbed_api_3peers_3controllers.c b/src/testbed/test_testbed_api_3peers_3controllers.c new file mode 100644 index 0000000..47ad810 --- /dev/null +++ b/src/testbed/test_testbed_api_3peers_3controllers.c @@ -0,0 +1,946 @@ +/* + This file is part of GNUnet + (C) 2008--2012 Christian Grothoff (and other contributing authors) + + GNUnet 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. + + 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file testbed/test_testbed_api_3peers_3controllers.c + * @brief testcases for the testbed api: 3 peers are configured, started and + * connected together. Each peer resides on its own controller. + * @author Sree Harsha Totakura + */ + + +/** + * The testing architecture is: + * A + * / \ + * / \ + * B === C + * A is the master controller and B, C are slave controllers. B links to C + * laterally. + * Peers are mapped to controllers in the following relations: + * Peer Controller + * 1 A + * 2 B + * 3 C + * + */ + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testing_lib.h" +#include "gnunet_testbed_service.h" + + +/** + * Generic logging shortcut + */ +#define LOG(kind,...) \ + GNUNET_log (kind, __VA_ARGS__) + +/** + * Relative time seconds shorthand + */ +#define TIME_REL_SECS(sec) \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, sec) + + +/** + * Peer context + */ +struct PeerContext +{ + /** + * The peer handle + */ + struct GNUNET_TESTBED_Peer *peer; + + /** + * Operations involving this peer + */ + struct GNUNET_TESTBED_Operation *operation; + + /** + * set to GNUNET_YES when peer is started + */ + int is_running; +}; + +/** + * Our localhost + */ +static struct GNUNET_TESTBED_Host *host; + +/** + * The controller process of one controller + */ +static struct GNUNET_TESTBED_ControllerProc *cp1; + +/** + * A neighbouring host + */ +static struct GNUNET_TESTBED_Host *neighbour1; + +/** + * Another neighbouring host + */ +static struct GNUNET_TESTBED_Host *neighbour2; + +/** + * Handle for neighbour registration + */ +static struct GNUNET_TESTBED_HostRegistrationHandle *reg_handle; + +/** + * The controller handle of one controller + */ +static struct GNUNET_TESTBED_Controller *controller1; + +/** + * peer 1 + */ +static struct PeerContext peer1; + +/** + * peer2 + */ +static struct PeerContext peer2; + +/** + * peer3 + */ +static struct PeerContext peer3; + +/** + * Handle to starting configuration + */ +static struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Handle to slave controller C's configuration, used to establish lateral link from + * master controller + */ +static struct GNUNET_CONFIGURATION_Handle *cfg2; + +/** + * Handle to operations involving both peers + */ +static struct GNUNET_TESTBED_Operation *common_operation; + +/** + * The handle for whether a host is habitable or not + */ +struct GNUNET_TESTBED_HostHabitableCheckHandle *hc_handle; + +/** + * Abort task identifier + */ +static GNUNET_SCHEDULER_TaskIdentifier abort_task; + +/** + * Delayed connect job identifier + */ +static GNUNET_SCHEDULER_TaskIdentifier delayed_connect_task; + +/** + * Different stages in testing + */ +enum Stage +{ + + /** + * Initial stage + */ + INIT, + + /** + * Controller 1 has started + */ + CONTROLLER1_UP, + + /** + * peer1 is created + */ + PEER1_CREATED, + + /** + * peer1 is started + */ + PEER1_STARTED, + + /** + * Controller 2 has started + */ + CONTROLLER2_UP, + + /** + * peer2 is created + */ + PEER2_CREATED, + + /** + * peer2 is started + */ + PEER2_STARTED, + + /** + * Controller 3 has started + */ + CONTROLLER3_UP, + + /** + * Peer3 is created + */ + PEER3_CREATED, + + /** + * Peer3 started + */ + PEER3_STARTED, + + /** + * peer1 and peer2 are connected + */ + PEERS_1_2_CONNECTED, + + /** + * peer2 and peer3 are connected + */ + PEERS_2_3_CONNECTED, + + /** + * Peers are connected once again (this should not fail as they are already connected) + */ + PEERS_CONNECTED_2, + + /** + * peers are stopped + */ + PEERS_STOPPED, + + /** + * Final success stage + */ + SUCCESS, + + /** + * Optional stage for marking test to be skipped + */ + SKIP +}; + +/** + * The testing result + */ +static enum Stage result; + +/** + * Shutdown nicely + * + * @param cls NULL + * @param tc the task context + */ +static void +do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if (GNUNET_SCHEDULER_NO_TASK != abort_task) + GNUNET_SCHEDULER_cancel (abort_task); + if (NULL != hc_handle) + GNUNET_TESTBED_is_host_habitable_cancel (hc_handle); + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == delayed_connect_task); + if (NULL != common_operation) + GNUNET_TESTBED_operation_done (common_operation); + if (NULL != reg_handle) + GNUNET_TESTBED_cancel_registration (reg_handle); + if (NULL != controller1) + GNUNET_TESTBED_controller_disconnect (controller1); + GNUNET_CONFIGURATION_destroy (cfg); + if (NULL != cfg2) + GNUNET_CONFIGURATION_destroy (cfg2); + if (NULL != cp1) + GNUNET_TESTBED_controller_stop (cp1); + if (NULL != host) + GNUNET_TESTBED_host_destroy (host); + if (NULL != neighbour1) + GNUNET_TESTBED_host_destroy (neighbour1); + if (NULL != neighbour2) + GNUNET_TESTBED_host_destroy (neighbour2); +} + + +/** + * abort task to run on test timed out + * + * @param cls NULL + * @param tc the task context + */ +static void +do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + LOG (GNUNET_ERROR_TYPE_WARNING, "Test timedout -- Aborting\n"); + abort_task = GNUNET_SCHEDULER_NO_TASK; + if (GNUNET_SCHEDULER_NO_TASK != delayed_connect_task) + { + GNUNET_SCHEDULER_cancel (delayed_connect_task); + delayed_connect_task = GNUNET_SCHEDULER_NO_TASK; + } + do_shutdown (cls, tc); +} + +static void +abort_test () +{ + if (GNUNET_SCHEDULER_NO_TASK != abort_task) + GNUNET_SCHEDULER_cancel (abort_task); + abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL); +} + + +/** + * Callback to be called when an operation is completed + * + * @param cls the callback closure from functions generating an operation + * @param op the operation that has been finished + * @param emsg error message in case the operation has failed; will be NULL if + * operation has executed successfully. + */ +static void +op_comp_cb (void *cls, struct GNUNET_TESTBED_Operation *op, const char *emsg); + + +/** + * task for delaying a connect + * + * @param cls NULL + * @param tc the task context + */ +static void +do_delayed_connect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + delayed_connect_task = GNUNET_SCHEDULER_NO_TASK; + if (NULL != common_operation) + { + GNUNET_break (0); + abort_test (); + return; + } + common_operation = + GNUNET_TESTBED_overlay_connect (NULL, &op_comp_cb, NULL, peer1.peer, + peer2.peer); +} + + +/** + * Callback to be called when an operation is completed + * + * @param cls the callback closure from functions generating an operation + * @param op the operation that has been finished + * @param emsg error message in case the operation has failed; will be NULL if + * operation has executed successfully. + */ +static void +op_comp_cb (void *cls, struct GNUNET_TESTBED_Operation *op, const char *emsg) +{ + if (common_operation != op) + { + GNUNET_break (0); + abort_test (); + return; + } + + switch (result) + { + case PEER3_STARTED: + case PEERS_2_3_CONNECTED: + case PEERS_1_2_CONNECTED: + break; + default: + GNUNET_break (0); + abort_test (); + return; + } + if ((NULL != peer1.operation) || (NULL != peer2.operation) || + (NULL != peer3.operation)) + { + GNUNET_break (0); + abort_test (); + return; + } +} + + +/** + * Functions of this signature are called when a peer has been successfully + * created + * + * @param cls NULL + * @param peer the handle for the created peer; NULL on any error during + * creation + * @param emsg NULL if peer is not NULL; else MAY contain the error description + */ +static void +peer_create_cb (void *cls, struct GNUNET_TESTBED_Peer *peer, const char *emsg) +{ + switch (result) + { + case CONTROLLER1_UP: + if ((NULL == peer1.operation) || (NULL == peer) || (NULL != peer1.peer)) + { + GNUNET_break (0); + abort_test (); + return; + } + peer1.peer = peer; + GNUNET_TESTBED_operation_done (peer1.operation); + result = PEER1_CREATED; + peer1.operation = GNUNET_TESTBED_peer_start (NULL, peer, NULL, NULL); + break; + case CONTROLLER2_UP: + if ((NULL == peer2.operation) || (NULL == peer) || (NULL != peer2.peer)) + { + GNUNET_break (0); + abort_test (); + return; + } + peer2.peer = peer; + GNUNET_TESTBED_operation_done (peer2.operation); + result = PEER2_CREATED; + peer2.operation = GNUNET_TESTBED_peer_start (NULL, peer, NULL, NULL); + break; + case CONTROLLER3_UP: + if ((NULL == peer3.operation) || (NULL == peer) || (NULL != peer3.peer)) + { + GNUNET_break (0); + abort_test (); + return; + } + peer3.peer = peer; + GNUNET_TESTBED_operation_done (peer3.operation); + result = PEER3_CREATED; + peer3.operation = GNUNET_TESTBED_peer_start (NULL, peer, NULL, NULL); + break; + default: + GNUNET_break (0); + abort_test (); + return; + } +} + + +/** + * Signature of the event handler function called by the + * respective event controller. + * + * @param cls closure + * @param event information about the event + */ +static void +controller_cb (void *cls, const struct GNUNET_TESTBED_EventInformation *event) +{ + switch (event->type) + { + case GNUNET_TESTBED_ET_OPERATION_FINISHED: + if ((NULL != event->details.operation_finished.op_cls) || + (NULL != event->details.operation_finished.emsg)) + { + GNUNET_break (0); + abort_test (); + return; + } + switch (result) + { + case PEERS_STOPPED: + if (NULL != event->details.operation_finished.generic) + { + GNUNET_break (0); + abort_test (); + return; + } + if (event->details.operation_finished.operation == peer1.operation) + { + GNUNET_TESTBED_operation_done (peer1.operation); + peer1.operation = NULL; + peer1.peer = NULL; + } + else if (event->details.operation_finished.operation == peer2.operation) + { + GNUNET_TESTBED_operation_done (peer2.operation); + peer2.operation = NULL; + peer2.peer = NULL; + } + else if (event->details.operation_finished.operation == peer3.operation) + { + GNUNET_TESTBED_operation_done (peer3.operation); + peer3.operation = NULL; + peer3.peer = NULL; + } + else + { + GNUNET_break (0); + abort_test (); + return; + } + if ((NULL == peer1.peer) && (NULL == peer2.peer) && (NULL == peer3.peer)) + { + result = SUCCESS; + GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); + } + break; + case PEER1_STARTED: + if ((NULL != event->details.operation_finished.generic) || + (NULL == common_operation)) + { + GNUNET_break (0); + abort_test (); + return; + } + GNUNET_TESTBED_operation_done (common_operation); + common_operation = NULL; + result = CONTROLLER2_UP; + peer2.operation = + GNUNET_TESTBED_peer_create (controller1, neighbour1, cfg, + &peer_create_cb, NULL); + if (NULL == peer2.operation) + { + GNUNET_break (0); + abort_test (); + return; + } + break; + case PEER2_STARTED: + if ((NULL != event->details.operation_finished.generic) || + (NULL == common_operation)) + { + GNUNET_break (0); + abort_test (); + return; + } + GNUNET_TESTBED_operation_done (common_operation); + common_operation = NULL; + result = CONTROLLER3_UP; + peer3.operation = + GNUNET_TESTBED_peer_create (controller1, neighbour2, cfg, + &peer_create_cb, NULL); + if (NULL == peer3.operation) + { + GNUNET_break (0); + abort_test (); + return; + } + break; + default: + GNUNET_break (0); + abort_test (); + return; + } + break; + case GNUNET_TESTBED_ET_PEER_START: + switch (result) + { + case PEER1_CREATED: + if (event->details.peer_start.host != host) + { + GNUNET_break (0); + abort_test (); + return; + } + peer1.is_running = GNUNET_YES; + GNUNET_TESTBED_operation_done (peer1.operation); + peer1.operation = NULL; + result = PEER1_STARTED; + common_operation = + GNUNET_TESTBED_controller_link (NULL, controller1, neighbour1, NULL, + cfg, GNUNET_YES); + break; + case PEER2_CREATED: + if (event->details.peer_start.host != neighbour1) + { + GNUNET_break (0); + abort_test (); + return; + } + peer2.is_running = GNUNET_YES; + GNUNET_TESTBED_operation_done (peer2.operation); + peer2.operation = NULL; + result = PEER2_STARTED; + if (NULL != common_operation) + { + GNUNET_break (0); + abort_test (); + return; + } + common_operation = + GNUNET_TESTBED_controller_link (NULL, controller1, neighbour2, NULL, + cfg, GNUNET_YES); + if (NULL == common_operation) + { + GNUNET_break (0); + abort_test (); + return; + } + break; + case PEER3_CREATED: + if (event->details.peer_start.host != neighbour2) + { + GNUNET_break (0); + abort_test (); + return; + } + peer3.is_running = GNUNET_YES; + GNUNET_TESTBED_operation_done (peer3.operation); + peer3.operation = NULL; + result = PEER3_STARTED; + common_operation = + GNUNET_TESTBED_overlay_connect (NULL, &op_comp_cb, NULL, peer2.peer, + peer1.peer); + break; + default: + GNUNET_break (0); + abort_test (); + return; + } + break; + case GNUNET_TESTBED_ET_PEER_STOP: + if (PEERS_CONNECTED_2 != result) + { + GNUNET_break (0); + abort_test (); + return; + } + if (event->details.peer_stop.peer == peer1.peer) + { + peer1.is_running = GNUNET_NO; + GNUNET_TESTBED_operation_done (peer1.operation); + } + else if (event->details.peer_stop.peer == peer2.peer) + { + peer2.is_running = GNUNET_NO; + GNUNET_TESTBED_operation_done (peer2.operation); + } + else if (event->details.peer_stop.peer == peer3.peer) + { + peer3.is_running = GNUNET_NO; + GNUNET_TESTBED_operation_done (peer3.operation); + } + else + { + GNUNET_break (0); + abort_test (); + return; + } + if ((GNUNET_NO == peer1.is_running) && (GNUNET_NO == peer2.is_running) && + (GNUNET_NO == peer3.is_running)) + { + result = PEERS_STOPPED; + peer1.operation = GNUNET_TESTBED_peer_destroy (peer1.peer); + peer2.operation = GNUNET_TESTBED_peer_destroy (peer2.peer); + peer3.operation = GNUNET_TESTBED_peer_destroy (peer3.peer); + } + break; + case GNUNET_TESTBED_ET_CONNECT: + if ((NULL != peer1.operation) || (NULL != peer2.operation) || + (NULL != peer3.operation) || (NULL == common_operation)) + { + GNUNET_break (0); + abort_test (); + return; + } + switch (result) + { + case PEER3_STARTED: + if ((event->details.peer_connect.peer1 != peer2.peer) || + (event->details.peer_connect.peer2 != peer1.peer)) + { + GNUNET_break (0); + abort_test (); + return; + } + GNUNET_TESTBED_operation_done (common_operation); + common_operation = NULL; + result = PEERS_1_2_CONNECTED; + LOG (GNUNET_ERROR_TYPE_DEBUG, "Peers connected\n"); + common_operation = + GNUNET_TESTBED_overlay_connect (NULL, &op_comp_cb, NULL, peer2.peer, + peer3.peer); + break; + case PEERS_1_2_CONNECTED: + if ((event->details.peer_connect.peer1 != peer2.peer) || + (event->details.peer_connect.peer2 != peer3.peer)) + { + GNUNET_break (0); + abort_test (); + return; + } + GNUNET_TESTBED_operation_done (common_operation); + common_operation = NULL; + result = PEERS_2_3_CONNECTED; + delayed_connect_task = + GNUNET_SCHEDULER_add_delayed (TIME_REL_SECS (3), &do_delayed_connect, + NULL); + break; + case PEERS_2_3_CONNECTED: + if ((event->details.peer_connect.peer1 != peer1.peer) || + (event->details.peer_connect.peer2 != peer2.peer)) + { + GNUNET_break (0); + abort_test (); + return; + } + GNUNET_TESTBED_operation_done (common_operation); + common_operation = NULL; + result = PEERS_CONNECTED_2; + LOG (GNUNET_ERROR_TYPE_DEBUG, "Peers connected again\n"); + peer1.operation = GNUNET_TESTBED_peer_stop (peer1.peer, NULL, NULL); + peer2.operation = GNUNET_TESTBED_peer_stop (peer2.peer, NULL, NULL); + peer3.operation = GNUNET_TESTBED_peer_stop (peer3.peer, NULL, NULL); + break; + default: + GNUNET_break (0); + abort_test (); + return; + } + break; + default: + GNUNET_break (0); + abort_test (); + return; + } +} + + +/** + * Callback which will be called to after a host registration succeeded or failed + * + * @param cls the host which has been registered + * @param emsg the error message; NULL if host registration is successful + */ +static void +registration_comp (void *cls, const char *emsg) +{ + reg_handle = NULL; + if (cls == neighbour1) + { + neighbour2 = GNUNET_TESTBED_host_create ("127.0.0.1", NULL, 0); + if (NULL == neighbour2) + { + GNUNET_break (0); + abort_test (); + return; + } + reg_handle = + GNUNET_TESTBED_register_host (controller1, neighbour2, + ®istration_comp, neighbour2); + if (NULL == reg_handle) + { + GNUNET_break (0); + abort_test (); + return; + } + return; + } + if (cls != neighbour2) + { + GNUNET_break (0); + abort_test (); + return; + } + peer1.operation = + GNUNET_TESTBED_peer_create (controller1, host, cfg, &peer_create_cb, + &peer1); + if (NULL == peer1.operation) + { + GNUNET_break (0); + abort_test (); + return; + } +} + + +/** + * Callback to signal successfull startup of the controller process + * + * @param cls the closure from GNUNET_TESTBED_controller_start() + * @param cfg the configuration with which the controller has been started; + * NULL if status is not GNUNET_OK + * @param status GNUNET_OK if the startup is successfull; GNUNET_SYSERR if not, + * GNUNET_TESTBED_controller_stop() shouldn't be called in this case + */ +static void +status_cb (void *cls, const struct GNUNET_CONFIGURATION_Handle *config, + int status) +{ + uint64_t event_mask; + + if (GNUNET_OK != status) + { + GNUNET_break (0); + cp1 = NULL; + abort_test (); + return; + } + event_mask = 0; + event_mask |= (1L << GNUNET_TESTBED_ET_PEER_START); + event_mask |= (1L << GNUNET_TESTBED_ET_PEER_STOP); + event_mask |= (1L << GNUNET_TESTBED_ET_CONNECT); + event_mask |= (1L << GNUNET_TESTBED_ET_OPERATION_FINISHED); + switch (result) + { + case INIT: + controller1 = + GNUNET_TESTBED_controller_connect (config, host, event_mask, + &controller_cb, NULL); + if (NULL == controller1) + { + GNUNET_break (0); + abort_test (); + return; + } + result = CONTROLLER1_UP; + neighbour1 = GNUNET_TESTBED_host_create ("127.0.0.1", NULL, 0); + if (NULL == neighbour1) + { + GNUNET_break (0); + abort_test (); + return; + } + reg_handle = + GNUNET_TESTBED_register_host (controller1, neighbour1, + ®istration_comp, neighbour1); + if (NULL == reg_handle) + { + GNUNET_break (0); + abort_test (); + return; + } + break; + default: + GNUNET_break (0); + abort_test (); + return; + } +} + + +/** + * Callbacks of this type are called by GNUNET_TESTBED_is_host_habitable to + * inform whether the given host is habitable or not. The Handle returned by + * GNUNET_TESTBED_is_host_habitable() is invalid after this callback is called + * + * @param cls NULL + * @param host the host whose status is being reported; will be NULL if the host + * given to GNUNET_TESTBED_is_host_habitable() is NULL + * @param status GNUNET_YES if it is habitable; GNUNET_NO if not + */ +static void +host_habitable_cb (void *cls, const struct GNUNET_TESTBED_Host *_host, + int status) +{ + hc_handle = NULL; + if (GNUNET_NO == status) + { + (void) PRINTF ("%s", + "Unable to run the test as this system is not configured " + "to use password less SSH logins to localhost.\n" + "Skipping test\n"); + GNUNET_SCHEDULER_cancel (abort_task); + abort_task = GNUNET_SCHEDULER_NO_TASK; + (void) GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); + result = SKIP; + return; + } + cp1 = + GNUNET_TESTBED_controller_start ("127.0.0.1", host, cfg, status_cb, NULL); +} + + +/** + * Main run function. + * + * @param cls NULL + * @param args arguments passed to GNUNET_PROGRAM_run + * @param cfgfile the path to configuration file + * @param cfg the configuration file handle + */ +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *config) +{ + host = GNUNET_TESTBED_host_create (NULL, NULL, 0); + if (NULL == host) + { + GNUNET_break (0); + abort_test (); + return; + } + if (NULL == + (hc_handle = + GNUNET_TESTBED_is_host_habitable (host, config, &host_habitable_cb, + NULL))) + { + GNUNET_TESTBED_host_destroy (host); + host = NULL; + (void) PRINTF ("%s", + "Unable to run the test as this system is not configured " + "to use password less SSH logins to localhost.\n" + "Skipping test\n"); + result = SKIP; + return; + } + cfg = GNUNET_CONFIGURATION_dup (config); + abort_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MINUTES, 3), &do_abort, + NULL); +} + + +/** + * Main function + */ +int +main (int argc, char **argv) +{ + char *const argv2[] = { "test_testbed_api_3peers_3controllers", + "-c", "test_testbed_api.conf", + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + int ret; + + result = INIT; + ret = + GNUNET_PROGRAM_run ((sizeof (argv2) / sizeof (char *)) - 1, argv2, + "test_testbed_api_3peers_3controllers", "nohelp", + options, &run, NULL); + if (GNUNET_OK != ret) + return 1; + switch (result) + { + case SUCCESS: + return 0; + case SKIP: + return 77; /* Mark test as skipped */ + default: + return 1; + } +} + +/* end of test_testbed_api_3peers_3controllers.c */ diff --git a/src/testbed/test_testbed_api_controllerlink.c b/src/testbed/test_testbed_api_controllerlink.c new file mode 100644 index 0000000..eadc6c9 --- /dev/null +++ b/src/testbed/test_testbed_api_controllerlink.c @@ -0,0 +1,749 @@ +/* + This file is part of GNUnet + (C) 2008--2012 Christian Grothoff (and other contributing authors) + + GNUnet 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. + + 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file testbed/test_testbed_api_controllerlink.c + * @brief testcase for testing controller to subcontroller linking + * @author Sree Harsha Totakura <sreeharsha@totakura.in> + */ + + +/** + * The controller architecture we try to achieve in this test case: + * + * Master Controller + * // \\ + * // \\ + * Slave Controller 1---------Slave Controller 3 + * || + * || + * Slave Controller 2 + */ + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testing_lib.h" +#include "gnunet_testbed_service.h" + +/** + * Generic logging shortcut + */ +#define LOG(kind,...) \ + GNUNET_log (kind, __VA_ARGS__) + +/** + * Debug logging shorthand + */ +#define LOG_DEBUG(...) \ + LOG(GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__) + +/** + * Different stages in testing + */ +enum Stage +{ + + /** + * Initial stage + */ + INIT, + + /** + * Master controller has started + */ + MASTER_STARTED, + + /** + * A peer has been created on master + */ + MASTER_PEER_CREATE_SUCCESS, + + /** + * Peer on master controller has been started successfully. + */ + MASTER_PEER_START_SUCCESS, + + /** + * The first slave has been registered at master controller + */ + SLAVE1_REGISTERED, + + /** + * The second slave has been registered at the master controller + */ + SLAVE2_REGISTERED, + + /** + * Link from master to slave 1 has been successfully created + */ + SLAVE1_LINK_SUCCESS, + + /** + * Peer create on slave 1 successful + */ + SLAVE1_PEER_CREATE_SUCCESS, + + /** + * Peer startup on slave 1 successful + */ + SLAVE1_PEER_START_SUCCESS, + + /** + * Link from slave 1 to slave 2 has been successfully created. + */ + SLAVE2_LINK_SUCCESS, + + /** + * Peer create on slave 2 successful + */ + SLAVE2_PEER_CREATE_SUCCESS, + + /** + * Peer on slave 1 successfully stopped + */ + SLAVE1_PEER_STOP_SUCCESS, + + /** + * Peer startup on slave 2 successful + */ + SLAVE2_PEER_START_SUCCESS, + + /** + * Try to connect peers on master and slave 2. + */ + MASTER_SLAVE2_PEERS_CONNECTED, + + /** + * Peer on slave 2 successfully stopped + */ + SLAVE2_PEER_STOP_SUCCESS, + + /** + * Peer destroy on slave 1 successful + */ + SLAVE1_PEER_DESTROY_SUCCESS, + + /** + * Peer destory on slave 2 successful + */ + SLAVE2_PEER_DESTROY_SUCCESS, + + /** + * Slave 3 has successfully registered + */ + SLAVE3_REGISTERED, + + /** + * Slave 3 has successfully started + */ + SLAVE3_STARTED, + + /** + * The configuration of slave 3 is acquired + */ + SLAVE3_GET_CONFIG_SUCCESS, + + /** + * Slave 1 has linked to slave 3; + */ + SLAVE3_LINK_SUCCESS, + + /** + * Destory master peer and mark test as success + */ + SUCCESS, + + /** + * Marks test as skipped + */ + SKIP +}; + +/** + * Host for running master controller + */ +static struct GNUNET_TESTBED_Host *host; + +/** + * The master controller process + */ +static struct GNUNET_TESTBED_ControllerProc *cp; + +/** + * Handle to master controller + */ +static struct GNUNET_TESTBED_Controller *mc; + +/** + * Slave host for running slave controller + */ +static struct GNUNET_TESTBED_Host *slave; + +/** + * Another slave host for running another slave controller + */ +static struct GNUNET_TESTBED_Host *slave2; + +/** + * Host for slave 3 + */ +static struct GNUNET_TESTBED_Host *slave3; + +/** + * Slave host registration handle + */ +static struct GNUNET_TESTBED_HostRegistrationHandle *rh; + +/** + * Handle to global configuration + */ +static struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Configuration of slave 3 controller + */ +static struct GNUNET_CONFIGURATION_Handle *cfg3; + +/** + * Abort task + */ +static GNUNET_SCHEDULER_TaskIdentifier abort_task; + +/** + * Operation handle for linking controllers + */ +static struct GNUNET_TESTBED_Operation *op; + +/** + * Handle to peer started at slave 1 + */ +static struct GNUNET_TESTBED_Peer *slave1_peer; + +/** + * Handle to peer started at slave 2 + */ +static struct GNUNET_TESTBED_Peer *slave2_peer; + +/** + * Handle to a peer started at master controller + */ +static struct GNUNET_TESTBED_Peer *master_peer; + +/** + * The handle for whether a host is habitable or not + */ +struct GNUNET_TESTBED_HostHabitableCheckHandle *hc_handle; + +/** + * Event mask + */ +uint64_t event_mask; + +/** + * Global testing status + */ +static enum Stage result; + + +/** + * Shutdown nicely + * + * @param cls NULL + * @param tc the task context + */ +static void +do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if (GNUNET_SCHEDULER_NO_TASK != abort_task) + GNUNET_SCHEDULER_cancel (abort_task); + if (NULL != hc_handle) + GNUNET_TESTBED_is_host_habitable_cancel (hc_handle); + if (NULL != slave3) + GNUNET_TESTBED_host_destroy (slave3); + if (NULL != slave2) + GNUNET_TESTBED_host_destroy (slave2); + if (NULL != slave) + GNUNET_TESTBED_host_destroy (slave); + if (NULL != host) + GNUNET_TESTBED_host_destroy (host); + if (NULL != mc) + GNUNET_TESTBED_controller_disconnect (mc); + if (NULL != cfg) + GNUNET_CONFIGURATION_destroy (cfg); + if (NULL != cfg3) + GNUNET_CONFIGURATION_destroy (cfg3); + if (NULL != cp) + GNUNET_TESTBED_controller_stop (cp); + if (NULL != rh) + GNUNET_TESTBED_cancel_registration (rh); +} + + +/** + * abort task to run on test timed out + * + * @param cls NULL + * @param tc the task context + */ +static void +do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + LOG (GNUNET_ERROR_TYPE_WARNING, "Test timedout -- Aborting\n"); + abort_task = GNUNET_SCHEDULER_NO_TASK; + do_shutdown (cls, tc); +} + + +/** + * Calls abort now + * + * @param + * @return + */ +static void +do_abort_now (void *cls) +{ + if (GNUNET_SCHEDULER_NO_TASK != abort_task) + GNUNET_SCHEDULER_cancel (abort_task); + abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL); +} + + +/** + * Task for inserting delay between tests + * + * @param + * @return + */ +static void +delay_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + switch (result) + { + case SLAVE2_PEER_CREATE_SUCCESS: + op = GNUNET_TESTBED_peer_stop (slave1_peer, NULL, NULL); + GNUNET_assert (NULL != op); + break; + case MASTER_SLAVE2_PEERS_CONNECTED: + op = GNUNET_TESTBED_peer_stop (slave2_peer, NULL, NULL); + GNUNET_assert (NULL != op); + break; + default: + GNUNET_assert (0); + } +} + + +/** + * Functions of this signature are called when a peer has been successfully + * created + * + * @param cls the closure from GNUNET_TESTBED_peer_create() + * @param peer the handle for the created peer; NULL on any error during + * creation + * @param emsg NULL if peer is not NULL; else MAY contain the error description + */ +static void +peer_create_cb (void *cls, struct GNUNET_TESTBED_Peer *peer, const char *emsg) +{ + GNUNET_assert (NULL != peer); + GNUNET_assert (NULL == emsg); + switch (result) + { + case MASTER_STARTED: + result = MASTER_PEER_CREATE_SUCCESS; + master_peer = peer; + GNUNET_TESTBED_operation_done (op); + op = GNUNET_TESTBED_peer_start (NULL, master_peer, NULL, NULL); + break; + case SLAVE1_LINK_SUCCESS: + result = SLAVE1_PEER_CREATE_SUCCESS; + slave1_peer = peer; + GNUNET_TESTBED_operation_done (op); + op = GNUNET_TESTBED_peer_start (NULL, slave1_peer, NULL, NULL); + break; + case SLAVE2_LINK_SUCCESS: + result = SLAVE2_PEER_CREATE_SUCCESS; + slave2_peer = peer; + GNUNET_TESTBED_operation_done (op); + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 1), &delay_task, + NULL); + break; + default: + GNUNET_assert (0); + } + GNUNET_assert (NULL != op); +} + + +/** + * Checks the event if it is an operation finished event and if indicates a + * successfull completion of operation + * + * @param event the event information to check + */ +static void +check_operation_success (const struct GNUNET_TESTBED_EventInformation *event) +{ + GNUNET_assert (NULL != event); + GNUNET_assert (GNUNET_TESTBED_ET_OPERATION_FINISHED == event->type); + GNUNET_assert (event->details.operation_finished.operation == op); + GNUNET_assert (NULL == event->details.operation_finished.op_cls); + GNUNET_assert (NULL == event->details.operation_finished.emsg); + GNUNET_assert (NULL == event->details.operation_finished.generic); +} + + +/** + * Callback which will be called to after a host registration succeeded or failed + * + * @param cls the host which has been registered + * @param emsg the error message; NULL if host registration is successful + */ +static void +registration_cont (void *cls, const char *emsg); + + +/** + * Signature of the event handler function called by the + * respective event controller. + * + * @param cls closure + * @param event information about the event + */ +static void +controller_cb (void *cls, const struct GNUNET_TESTBED_EventInformation *event) +{ + switch (result) + { + case SLAVE2_REGISTERED: + check_operation_success (event); + GNUNET_TESTBED_operation_done (op); + op = NULL; + result = SLAVE1_LINK_SUCCESS; + GNUNET_assert (NULL != slave2); + GNUNET_assert (NULL != slave); + op = GNUNET_TESTBED_peer_create (mc, slave, cfg, peer_create_cb, NULL); + GNUNET_assert (NULL != op); + break; + case SLAVE1_PEER_START_SUCCESS: + check_operation_success (event); + GNUNET_TESTBED_operation_done (op); + result = SLAVE2_LINK_SUCCESS; + op = GNUNET_TESTBED_peer_create (mc, slave2, cfg, peer_create_cb, NULL); + GNUNET_assert (NULL != op); + break; + case MASTER_PEER_CREATE_SUCCESS: + GNUNET_assert (GNUNET_TESTBED_ET_PEER_START == event->type); + GNUNET_assert (event->details.peer_start.host == host); + GNUNET_assert (event->details.peer_start.peer == master_peer); + GNUNET_TESTBED_operation_done (op); + result = MASTER_PEER_START_SUCCESS; + slave = GNUNET_TESTBED_host_create_with_id (1, "127.0.0.1", NULL, 0); + GNUNET_assert (NULL != slave); + rh = GNUNET_TESTBED_register_host (mc, slave, ®istration_cont, NULL); + GNUNET_assert (NULL != rh); + break; + case SLAVE1_PEER_CREATE_SUCCESS: + GNUNET_assert (GNUNET_TESTBED_ET_PEER_START == event->type); + GNUNET_assert (event->details.peer_start.host == slave); + GNUNET_assert (event->details.peer_start.peer == slave1_peer); + GNUNET_TESTBED_operation_done (op); + result = SLAVE1_PEER_START_SUCCESS; + op = GNUNET_TESTBED_controller_link (NULL, mc, slave2, slave, cfg, + GNUNET_YES); + break; + case SLAVE2_PEER_CREATE_SUCCESS: + GNUNET_assert (GNUNET_TESTBED_ET_PEER_STOP == event->type); + GNUNET_assert (event->details.peer_stop.peer == slave1_peer); + GNUNET_TESTBED_operation_done (op); + result = SLAVE1_PEER_STOP_SUCCESS; + op = GNUNET_TESTBED_peer_start (NULL, slave2_peer, NULL, NULL); + GNUNET_assert (NULL != op); + break; + case SLAVE1_PEER_STOP_SUCCESS: + GNUNET_assert (GNUNET_TESTBED_ET_PEER_START == event->type); + GNUNET_assert (event->details.peer_start.host == slave2); + GNUNET_assert (event->details.peer_start.peer == slave2_peer); + GNUNET_TESTBED_operation_done (op); + result = SLAVE2_PEER_START_SUCCESS; + op = GNUNET_TESTBED_overlay_connect (mc, NULL, NULL, master_peer, + slave2_peer); + break; + case SLAVE2_PEER_START_SUCCESS: + GNUNET_assert (NULL != event); + GNUNET_assert (GNUNET_TESTBED_ET_CONNECT == event->type); + GNUNET_assert (event->details.peer_connect.peer1 == master_peer); + GNUNET_assert (event->details.peer_connect.peer2 == slave2_peer); + result = MASTER_SLAVE2_PEERS_CONNECTED; + GNUNET_TESTBED_operation_done (op); + op = NULL; + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 1), &delay_task, + NULL); + break; + case MASTER_SLAVE2_PEERS_CONNECTED: + GNUNET_assert (GNUNET_TESTBED_ET_PEER_STOP == event->type); + GNUNET_assert (event->details.peer_stop.peer == slave2_peer); + GNUNET_TESTBED_operation_done (op); + result = SLAVE2_PEER_STOP_SUCCESS; + op = GNUNET_TESTBED_peer_destroy (slave1_peer); + GNUNET_assert (NULL != op); + break; + case SLAVE2_PEER_STOP_SUCCESS: + check_operation_success (event); + GNUNET_TESTBED_operation_done (op); + result = SLAVE1_PEER_DESTROY_SUCCESS; + op = GNUNET_TESTBED_peer_destroy (slave2_peer); + GNUNET_assert (NULL != op); + break; + case SLAVE1_PEER_DESTROY_SUCCESS: + check_operation_success (event); + GNUNET_TESTBED_operation_done (op); + op = NULL; + result = SLAVE2_PEER_DESTROY_SUCCESS; + slave3 = GNUNET_TESTBED_host_create_with_id (3, "127.0.0.1", NULL, 0); + rh = GNUNET_TESTBED_register_host (mc, slave3, ®istration_cont, NULL); + break; + case SLAVE3_REGISTERED: + check_operation_success (event); + GNUNET_TESTBED_operation_done (op); + op = NULL; + result = SLAVE3_STARTED; + op = GNUNET_TESTBED_get_slave_config (NULL, mc, slave3); + GNUNET_assert (NULL != op); + break; + case SLAVE3_STARTED: + GNUNET_assert (NULL != event); + GNUNET_assert (GNUNET_TESTBED_ET_OPERATION_FINISHED == event->type); + GNUNET_assert (event->details.operation_finished.operation == op); + GNUNET_assert (NULL == event->details.operation_finished.op_cls); + GNUNET_assert (NULL == event->details.operation_finished.emsg); + cfg3 = GNUNET_CONFIGURATION_dup (event->details.operation_finished.generic); + GNUNET_TESTBED_operation_done (op); + result = SLAVE3_GET_CONFIG_SUCCESS; + op = GNUNET_TESTBED_controller_link (NULL, mc, slave3, slave, cfg3, + GNUNET_NO); + break; + case SLAVE3_GET_CONFIG_SUCCESS: + result = SLAVE3_LINK_SUCCESS; + GNUNET_TESTBED_operation_done (op); + op = GNUNET_TESTBED_peer_destroy (master_peer); + break; + case SLAVE3_LINK_SUCCESS: + check_operation_success (event); + result = SUCCESS; + GNUNET_TESTBED_operation_done (op); + op = NULL; + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 3), &do_shutdown, + NULL); + break; + default: + GNUNET_assert (0); + } +} + + +/** + * Callback which will be called to after a host registration succeeded or failed + * + * @param cls the host which has been registered + * @param emsg the error message; NULL if host registration is successful + */ +static void +registration_cont (void *cls, const char *emsg) +{ + rh = NULL; + switch (result) + { + case MASTER_PEER_START_SUCCESS: + GNUNET_assert (NULL == emsg); + GNUNET_assert (NULL != mc); + result = SLAVE1_REGISTERED; + slave2 = GNUNET_TESTBED_host_create_with_id (2, "127.0.0.1", NULL, 0); + GNUNET_assert (NULL != slave2); + rh = GNUNET_TESTBED_register_host (mc, slave2, ®istration_cont, NULL); + GNUNET_assert (NULL != rh); + break; + case SLAVE1_REGISTERED: + GNUNET_assert (NULL == emsg); + GNUNET_assert (NULL != mc); + result = SLAVE2_REGISTERED; + GNUNET_assert (NULL != cfg); + op = GNUNET_TESTBED_controller_link (NULL, mc, slave, NULL, cfg, + GNUNET_YES); + GNUNET_assert (NULL != op); + break; + case SLAVE2_PEER_DESTROY_SUCCESS: + GNUNET_assert (NULL == emsg); + GNUNET_assert (NULL != mc); + GNUNET_assert (NULL == op); + result = SLAVE3_REGISTERED; + op = GNUNET_TESTBED_controller_link (NULL, mc, slave3, NULL, cfg, + GNUNET_YES); + GNUNET_assert (NULL != op); + break; + default: + GNUNET_break (0); + do_abort_now (NULL); + } +} + +/** + * Callback to signal successfull startup of the controller process + * + * @param cls the closure from GNUNET_TESTBED_controller_start() + * @param cfg the configuration with which the controller has been started; + * NULL if status is not GNUNET_OK + * @param status GNUNET_OK if the startup is successfull; GNUNET_SYSERR if not, + * GNUNET_TESTBED_controller_stop() shouldn't be called in this case + */ +static void +status_cb (void *cls, const struct GNUNET_CONFIGURATION_Handle *config, + int status) +{ + switch (result) + { + case INIT: + GNUNET_assert (GNUNET_OK == status); + event_mask = 0; + event_mask |= (1L << GNUNET_TESTBED_ET_PEER_START); + event_mask |= (1L << GNUNET_TESTBED_ET_PEER_STOP); + event_mask |= (1L << GNUNET_TESTBED_ET_CONNECT); + event_mask |= (1L << GNUNET_TESTBED_ET_OPERATION_FINISHED); + mc = GNUNET_TESTBED_controller_connect (config, host, event_mask, + &controller_cb, NULL); + GNUNET_assert (NULL != mc); + result = MASTER_STARTED; + op = GNUNET_TESTBED_peer_create (mc, host, cfg, peer_create_cb, NULL); + GNUNET_assert (NULL != op); + break; + default: + GNUNET_break (0); + cp = NULL; + do_abort_now (NULL); + } +} + + +/** + * Callbacks of this type are called by GNUNET_TESTBED_is_host_habitable to + * inform whether the given host is habitable or not. The Handle returned by + * GNUNET_TESTBED_is_host_habitable() is invalid after this callback is called + * + * @param cls NULL + * @param host the host whose status is being reported; will be NULL if the host + * given to GNUNET_TESTBED_is_host_habitable() is NULL + * @param status GNUNET_YES if it is habitable; GNUNET_NO if not + */ +static void +host_habitable_cb (void *cls, const struct GNUNET_TESTBED_Host *_host, + int status) +{ + hc_handle = NULL; + if (GNUNET_NO == status) + { + (void) PRINTF ("%s", + "Unable to run the test as this system is not configured " + "to use password less SSH logins to localhost.\n" + "Skipping test\n"); + GNUNET_SCHEDULER_cancel (abort_task); + abort_task = GNUNET_SCHEDULER_NO_TASK; + (void) GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); + result = SKIP; + return; + } + cp = GNUNET_TESTBED_controller_start ("127.0.0.1", host, cfg, status_cb, + NULL); +} + + +/** + * Main run function. + * + * @param cls NULL + * @param args arguments passed to GNUNET_PROGRAM_run + * @param cfgfile the path to configuration file + * @param cfg the configuration file handle + */ +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *config) +{ + host = GNUNET_TESTBED_host_create (NULL, NULL, 0); + GNUNET_assert (NULL != host); + if (NULL == + (hc_handle = + GNUNET_TESTBED_is_host_habitable (host, config, &host_habitable_cb, + NULL))) + { + GNUNET_TESTBED_host_destroy (host); + host = NULL; + (void) PRINTF ("%s", + "Unable to run the test as this system is not configured " + "to use password less SSH logins to localhost.\n" + "Marking test as successful\n"); + result = SKIP; + return; + } + cfg = GNUNET_CONFIGURATION_dup (config); + abort_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MINUTES, 5), &do_abort, + NULL); +} + + +/** + * Main function + */ +int +main (int argc, char **argv) +{ + char *const argv2[] = { "test_testbed_api_controllerlink", + "-c", "test_testbed_api.conf", + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + int ret; + + result = INIT; + ret = + GNUNET_PROGRAM_run ((sizeof (argv2) / sizeof (char *)) - 1, argv2, + "test_testbed_api_controllerlink", "nohelp", options, + &run, NULL); + if (GNUNET_OK != ret) + return 1; + switch (result) + { + case SUCCESS: + return 0; + case SKIP: + return 77; /* Mark test as skipped */ + default: + return 1; + } +} + +/* end of test_testbed_api_controllerlink.c */ diff --git a/src/testbed/test_testbed_api_hosts.c b/src/testbed/test_testbed_api_hosts.c new file mode 100644 index 0000000..51284ea --- /dev/null +++ b/src/testbed/test_testbed_api_hosts.c @@ -0,0 +1,130 @@ +/* + This file is part of GNUnet + (C) 2008--2012 Christian Grothoff (and other contributing authors) + + GNUnet 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. + + 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file testbed/test_testbed_api_hosts.c + * @brief tests cases for testbed_api_hosts.c + * @author Sree Harsha Totakura + */ + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testbed_service.h" +#include "testbed_api_hosts.h" + + +#define TIME_REL_SECS(sec) \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, sec) + +/** + * Host we are creating and using + */ +static struct GNUNET_TESTBED_Host *host; + +/** + * An array of hosts which are loaded from a file + */ +static struct GNUNET_TESTBED_Host **hosts; + +/** + * Number of hosts in the above list + */ +static unsigned int num_hosts; + +/** + * Global test status + */ +static int status; + +/** + * Shutdown task identifier + */ +GNUNET_SCHEDULER_TaskIdentifier shutdown_id; + +/** + * The shutdown task + * + * @param cls NULL + * @param tc the task context + */ +static void +do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_TESTBED_host_destroy (host); + while (0 != num_hosts) + { + GNUNET_TESTBED_host_destroy (hosts[num_hosts - 1]); + num_hosts--; + } + GNUNET_free (hosts); +} + + +/** + * Main run function. + * + * @param cls NULL + * @param args arguments passed to GNUNET_PROGRAM_run + * @param cfgfile the path to configuration file + * @param cfg the configuration file handle + */ +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + host = GNUNET_TESTBED_host_create ("localhost", NULL, 0); + GNUNET_assert (NULL != host); + GNUNET_assert (0 != GNUNET_TESTBED_host_get_id_ (host)); + GNUNET_TESTBED_host_destroy (host); + host = GNUNET_TESTBED_host_create (NULL, NULL, 0); + GNUNET_assert (NULL != host); + GNUNET_assert (0 == GNUNET_TESTBED_host_get_id_ (host)); + GNUNET_assert (host == GNUNET_TESTBED_host_lookup_by_id_ (0)); + hosts = NULL; + num_hosts = GNUNET_TESTBED_hosts_load_from_file ("sample_hosts.txt", &hosts); + GNUNET_assert (15 == num_hosts); + GNUNET_assert (NULL != hosts); + status = GNUNET_YES; + shutdown_id = + GNUNET_SCHEDULER_add_delayed (TIME_REL_SECS (2), &do_shutdown, NULL); +} + + +int +main (int argc, char **argv) +{ + char *const argv2[] = { "test_testbed_api_hosts", + "-c", "test_testbed_api.conf", + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + + status = GNUNET_SYSERR; + if (GNUNET_OK != + GNUNET_PROGRAM_run ((sizeof (argv2) / sizeof (char *)) - 1, argv2, + "test_testbed_api_hosts", "nohelp", options, &run, + NULL)) + return 1; + return (GNUNET_OK == status) ? 0 : 1; +} + +/* end of test_testbed_api_hosts.c */ diff --git a/src/testbed/test_testbed_api_operations.c b/src/testbed/test_testbed_api_operations.c new file mode 100644 index 0000000..24f23be --- /dev/null +++ b/src/testbed/test_testbed_api_operations.c @@ -0,0 +1,430 @@ +/* + This file is part of GNUnet + (C) 2008--2012 Christian Grothoff (and other contributing authors) + + GNUnet 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. + + 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file testbed/test_testbed_api_operations.c + * @brief tests cases for testbed_api_operations.c + * @author Sree Harsha Totakura + */ + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "testbed_api_operations.h" + +/** + * Generic logging shortcut + */ +#define LOG(kind,...) \ + GNUNET_log (kind, __VA_ARGS__) + +/** + * Queue A. Initially the max active is set to 2 and then reduced to 0 - this + * should block op2 even after op1 has finished. Later the max active is set to + * 2 and this should start op2 + */ +struct OperationQueue *q1; + +/** + * Queue B. Max active set to 2 is not changed throughout the test + */ +struct OperationQueue *q2; + +/** + * This operation should go into both queues and block op2 until it is done + */ +struct GNUNET_TESTBED_Operation *op1; + +/** + * This operation should go into q1 and q2 + */ +struct GNUNET_TESTBED_Operation *op2; + +/** + * This operation should go into both queues and should consume 2 units of + * resources on both queues. Since op2 needs a resource from both queues and is + * queues before this operation, it will be blocked until op2 is released even + * though q1 has + */ +struct GNUNET_TESTBED_Operation *op3; + +/** + * Just like op3, this operation also consumes 2 units of resources on both + * queues. Since this is queued after op3 and both queues are at max active + * 2. This will be blocked until op3 is done. + */ +struct GNUNET_TESTBED_Operation *op4; + +/** + * This operation is started after op4 is released and should consume only 1 + * resource on queue q1. It should be started along with op6 and op7 + */ +struct GNUNET_TESTBED_Operation *op5; + +/** + * This operation is started after op4 is released and should consume only 1 + * resource on q2. It should be started along with op5 and op7 + */ +struct GNUNET_TESTBED_Operation *op6; + +/** + * This operation is started after op4 is released and should consume 1 resource + * on both queues q1 and q1. It should be started along with op5 and op6 + */ +struct GNUNET_TESTBED_Operation *op7; + +/** + * The delay task identifier + */ +GNUNET_SCHEDULER_TaskIdentifier step_task; + + +/** + * Enumeration of test stages + */ +enum Test +{ + /** + * Initial stage + */ + TEST_INIT, + + /** + * op1 has been started + */ + TEST_OP1_STARTED, + + /** + * op1 has been released + */ + TEST_OP1_RELEASED, + + /** + * Temporary pause where no operations should start as we set max active in q1 + * to 0 in stage TEST_OP1_STARTED + */ + TEST_PAUSE, + + /** + * op2 has started + */ + TEST_OP2_STARTED, + + /** + * op2 released + */ + TEST_OP2_RELEASED, + + /** + * op3 has started + */ + TEST_OP3_STARTED, + + /** + * op3 has finished + */ + TEST_OP3_RELEASED, + + /** + * op4 has started + */ + TEST_OP4_STARTED, + + /** + * op4 has released + */ + TEST_OP4_RELEASED, + + /** + * op5, op6, op7 started + */ + TEST_OP5_6_7_STARTED, + + /** + * op5 has released + */ + TEST_OP5_RELEASED, + + /** + * op6 has released + */ + TEST_OP6_RELEASED, + + /** + * op7 has released + */ + TEST_OP7_RELEASED +}; + +/** + * The test result + */ +enum Test result; + + +/** + * Function to call to start an operation once all + * queues the operation is part of declare that the + * operation can be activated. + */ +static void +start_cb (void *cls); + + +/** + * Function to cancel an operation (release all associated resources). This can + * be because of a call to "GNUNET_TESTBED_operation_cancel" (before the + * operation generated an event) or AFTER the operation generated an event due + * to a call to "GNUNET_TESTBED_operation_done". Thus it is not guaranteed that + * a callback to the 'OperationStart' preceeds the call to 'OperationRelease'. + * Implementations of this function are expected to clean up whatever state is + * in 'cls' and release all resources associated with the operation. + */ +static void +release_cb (void *cls); + + +/** + * Task to simulate artificial delay and change the test stage + * + * @param cls NULL + * @param tc the task context + */ +static void +step (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != step_task); + step_task = GNUNET_SCHEDULER_NO_TASK; + switch (result) + { + case TEST_OP1_STARTED: + GNUNET_TESTBED_operation_release_ (op1); + GNUNET_TESTBED_operation_queue_reset_max_active_ (q1, 0); + op3 = GNUNET_TESTBED_operation_create_ (&op3, &start_cb, &release_cb); + GNUNET_TESTBED_operation_queue_insert2_ (q1, op3, 2); + GNUNET_TESTBED_operation_queue_insert2_ (q2, op3, 2); + GNUNET_TESTBED_operation_begin_wait_ (op3); + op4 = GNUNET_TESTBED_operation_create_ (&op4, &start_cb, &release_cb); + GNUNET_TESTBED_operation_queue_insert2_ (q1, op4, 2); + GNUNET_TESTBED_operation_queue_insert2_ (q2, op4, 2); + GNUNET_TESTBED_operation_begin_wait_ (op4); + break; + case TEST_OP1_RELEASED: + result = TEST_PAUSE; + GNUNET_TESTBED_operation_queue_reset_max_active_ (q1, 2); + break; + case TEST_OP2_STARTED: + GNUNET_TESTBED_operation_release_ (op2); + break; + case TEST_OP3_STARTED: + GNUNET_TESTBED_operation_release_ (op3); + break; + case TEST_OP4_STARTED: + GNUNET_TESTBED_operation_release_ (op4); + break; + default: + GNUNET_assert (0); + } +} + + +/** + * Function to call to start an operation once all + * queues the operation is part of declare that the + * operation can be activated. + */ +static void +start_cb (void *cls) +{ + switch (result) + { + case TEST_INIT: + GNUNET_assert (&op1 == cls); + result = TEST_OP1_STARTED; + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == step_task); + step_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &step, NULL); + break; + case TEST_PAUSE: + GNUNET_assert (&op2 == cls); + result = TEST_OP2_STARTED; + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == step_task); + step_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &step, NULL); + break; + case TEST_OP2_RELEASED: + GNUNET_assert (&op3 == cls); + result = TEST_OP3_STARTED; + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == step_task); + step_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &step, NULL); + break; + case TEST_OP3_RELEASED: + GNUNET_assert (&op4 == cls); + result = TEST_OP4_STARTED; + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == step_task); + step_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &step, NULL); + break; + case TEST_OP4_RELEASED: + { + static int nops; + + nops++; + if (nops == 3) + { + result = TEST_OP5_6_7_STARTED; + GNUNET_TESTBED_operation_release_ (op5); + op5 = NULL; + } + } + break; + default: + GNUNET_assert (0); + } +} + + +/** + * Function to cancel an operation (release all associated resources). This can + * be because of a call to "GNUNET_TESTBED_operation_cancel" (before the + * operation generated an event) or AFTER the operation generated an event due + * to a call to "GNUNET_TESTBED_operation_done". Thus it is not guaranteed that + * a callback to the 'OperationStart' preceeds the call to 'OperationRelease'. + * Implementations of this function are expected to clean up whatever state is + * in 'cls' and release all resources associated with the operation. + */ +static void +release_cb (void *cls) +{ + switch (result) + { + case TEST_OP1_STARTED: + GNUNET_assert (&op1 == cls); + result = TEST_OP1_RELEASED; + op1 = NULL; + step_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &step, NULL); + break; + case TEST_OP2_STARTED: + GNUNET_assert (&op2 == cls); + result = TEST_OP2_RELEASED; + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == step_task); + //step_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &step, NULL); + break; + case TEST_OP3_STARTED: + GNUNET_assert (&op3 == cls); + result = TEST_OP3_RELEASED; + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == step_task); + break; + case TEST_OP4_STARTED: + GNUNET_assert (&op4 == cls); + result = TEST_OP4_RELEASED; + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == step_task); + op5 = GNUNET_TESTBED_operation_create_ (&op5, &start_cb, &release_cb); + GNUNET_TESTBED_operation_queue_insert2_ (q1, op5, 1); + GNUNET_TESTBED_operation_begin_wait_ (op5); + op6 = GNUNET_TESTBED_operation_create_ (&op6, &start_cb, &release_cb); + GNUNET_TESTBED_operation_queue_insert2_ (q2, op6, 1); + GNUNET_TESTBED_operation_begin_wait_ (op6); + op7 = GNUNET_TESTBED_operation_create_ (&op7, &start_cb, &release_cb); + GNUNET_TESTBED_operation_queue_insert2_ (q1, op7, 1); + GNUNET_TESTBED_operation_queue_insert2_ (q2, op7, 1); + GNUNET_TESTBED_operation_begin_wait_ (op7); + break; + case TEST_OP5_6_7_STARTED: + result = TEST_OP5_RELEASED; + op5 = NULL; + GNUNET_TESTBED_operation_release_ (op6); + break; + case TEST_OP5_RELEASED: + op6 = NULL; + result = TEST_OP6_RELEASED; + GNUNET_TESTBED_operation_release_ (op7); + break; + case TEST_OP6_RELEASED: + result = TEST_OP7_RELEASED; + op7 = NULL; + GNUNET_TESTBED_operation_queue_destroy_ (q1); + GNUNET_TESTBED_operation_queue_destroy_ (q2); + q1 = NULL; + q2 = NULL; + break; + default: + GNUNET_assert (0); + } +} + + +/** + * Main run function. + * + * @param cls NULL + * @param args arguments passed to GNUNET_PROGRAM_run + * @param cfgfile the path to configuration file + * @param cfg the configuration file handle + */ +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *config) +{ + q1 = GNUNET_TESTBED_operation_queue_create_ (1); + GNUNET_assert (NULL != q1); + q2 = GNUNET_TESTBED_operation_queue_create_ (2); + GNUNET_assert (NULL != q2); + op1 = GNUNET_TESTBED_operation_create_ (&op1, start_cb, release_cb); + GNUNET_assert (NULL != op1); + op2 = GNUNET_TESTBED_operation_create_ (&op2, start_cb, release_cb); + GNUNET_TESTBED_operation_queue_insert_ (q1, op1); + GNUNET_TESTBED_operation_queue_insert_ (q2, op1); + GNUNET_TESTBED_operation_begin_wait_ (op1); + GNUNET_TESTBED_operation_queue_insert_ (q1, op2); + GNUNET_TESTBED_operation_queue_insert_ (q2, op2); + GNUNET_TESTBED_operation_begin_wait_ (op2); + result = TEST_INIT; +} + + +/** + * Main function + */ +int +main (int argc, char **argv) +{ + int ret; + char *const argv2[] = + { "test_testbed_api_operations", "-c", "test_testbed_api.conf", NULL }; + struct GNUNET_GETOPT_CommandLineOption options[] = + { GNUNET_GETOPT_OPTION_END }; + + ret = + GNUNET_PROGRAM_run ((sizeof (argv2) / sizeof (char *)) - 1, argv2, + "test_testbed_api_operations", "nohelp", options, + &run, NULL); + if ((GNUNET_OK != ret) || (TEST_OP7_RELEASED != result)) + return 1; + op1 = NULL; + op2 = NULL; + op3 = NULL; + q1 = NULL; + q2 = NULL; + return 0; +} + +/* end of test_testbed_api_operations.c */ diff --git a/src/testbed/test_testbed_api_test.c b/src/testbed/test_testbed_api_test.c new file mode 100644 index 0000000..705f52f --- /dev/null +++ b/src/testbed/test_testbed_api_test.c @@ -0,0 +1,241 @@ +/* + This file is part of GNUnet + (C) 2008--2012 Christian Grothoff (and other contributing authors) + + GNUnet 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. + + 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file src/testbed/test_testbed_api_test.c + * @brief testing cases for testing high level testbed api helper functions + * @author Sree Harsha Totakura <sreeharsha@totakura.in> + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_testbed_service.h" + + +/** + * Generic logging shortcut + */ +#define LOG(kind,...) \ + GNUNET_log (kind, __VA_ARGS__) + +/** + * Number of peers we want to start + */ +#define NUM_PEERS 25 + +/** + * Array of peers + */ +static struct GNUNET_TESTBED_Peer **peers; + +/** + * Operation handle + */ +static struct GNUNET_TESTBED_Operation *op; + +/** + * Abort task identifier + */ +static GNUNET_SCHEDULER_TaskIdentifier abort_task; + +/** + * shutdown task identifier + */ +static GNUNET_SCHEDULER_TaskIdentifier shutdown_task; + +/** + * Testing result + */ +static int result; + + +/** + * Shutdown nicely + * + * @param cls NULL + * @param tc the task context + */ +static void +do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + shutdown_task = GNUNET_SCHEDULER_NO_TASK; + if (GNUNET_SCHEDULER_NO_TASK != abort_task) + GNUNET_SCHEDULER_cancel (abort_task); + if (NULL != op) + GNUNET_TESTBED_operation_done (op); + GNUNET_SCHEDULER_shutdown (); +} + +/** + * shortcut to exit during failure + */ +#define FAIL_TEST(cond) do { \ + if (!(cond)) { \ + GNUNET_break(0); \ + if (GNUNET_SCHEDULER_NO_TASK != abort_task) \ + GNUNET_SCHEDULER_cancel (abort_task); \ + abort_task = GNUNET_SCHEDULER_NO_TASK; \ + if (GNUNET_SCHEDULER_NO_TASK == shutdown_task) \ + shutdown_task = GNUNET_SCHEDULER_add_now (do_shutdown, NULL); \ + return; \ + } \ + } while (0) + + +/** + * abort task to run on test timed out + * + * @param cls NULL + * @param tc the task context + */ +static void +do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + LOG (GNUNET_ERROR_TYPE_WARNING, "Test timedout -- Aborting\n"); + abort_task = GNUNET_SCHEDULER_NO_TASK; + if (GNUNET_SCHEDULER_NO_TASK != shutdown_task) + GNUNET_SCHEDULER_cancel (shutdown_task); + shutdown_task = GNUNET_SCHEDULER_add_now (do_shutdown, NULL); +} + + +/** + * Callback to be called when the requested peer information is available + * + * @param cb_cls the closure from GNUNET_TETSBED_peer_get_information() + * @param op the operation this callback corresponds to + * @param pinfo the result; will be NULL if the operation has failed + * @param emsg error message if the operation has failed; will be NULL if the + * operation is successfull + */ +static void +peerinfo_cb (void *cb_cls, struct GNUNET_TESTBED_Operation *op_, + const struct GNUNET_TESTBED_PeerInformation *pinfo, + const char *emsg) +{ + FAIL_TEST (op == op_); + FAIL_TEST (NULL == cb_cls); + FAIL_TEST (NULL == emsg); + FAIL_TEST (GNUNET_TESTBED_PIT_IDENTITY == pinfo->pit); + FAIL_TEST (NULL != pinfo->result.id); + GNUNET_TESTBED_operation_done (op); + op = NULL; + result = GNUNET_OK; + shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); +} + + +/** + * Callback to be called when an operation is completed + * + * @param cls the callback closure from functions generating an operation + * @param op the operation that has been finished + * @param emsg error message in case the operation has failed; will be NULL if + * operation has executed successfully. + */ +static void +op_comp_cb (void *cls, struct GNUNET_TESTBED_Operation *op_, const char *emsg) +{ + FAIL_TEST (NULL == cls); + FAIL_TEST (op == op_); + if (NULL != emsg) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "%s\n", emsg); + FAIL_TEST (0); + } + GNUNET_TESTBED_operation_done (op); + op = GNUNET_TESTBED_peer_get_information (peers[0], + GNUNET_TESTBED_PIT_IDENTITY, + &peerinfo_cb, NULL); +} + + +/** + * Controller event callback + * + * @param cls NULL + * @param event the controller event + */ +static void +controller_event_cb (void *cls, + const struct GNUNET_TESTBED_EventInformation *event) +{ + switch (event->type) + { + case GNUNET_TESTBED_ET_CONNECT: + FAIL_TEST (event->details.peer_connect.peer1 == peers[0]); + FAIL_TEST (event->details.peer_connect.peer2 == peers[1]); + break; + default: + FAIL_TEST (0); + } +} + + +/** + * Signature of a main function for a testcase. + * + * @param cls closure + * @param num_peers number of peers in 'peers' + * @param peers handle to peers run in the testbed + */ +static void +test_master (void *cls, unsigned int num_peers, + struct GNUNET_TESTBED_Peer **peers_) +{ + unsigned int peer; + + FAIL_TEST (NULL == cls); + FAIL_TEST (NUM_PEERS == num_peers); + FAIL_TEST (NULL != peers_); + for (peer = 0; peer < num_peers; peer++) + FAIL_TEST (NULL != peers_[peer]); + peers = peers_; + op = GNUNET_TESTBED_overlay_connect (NULL, &op_comp_cb, NULL, peers[0], + peers[1]); + abort_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MINUTES, 3), &do_abort, + NULL); +} + + +/** + * Main function + */ +int +main (int argc, char **argv) +{ + uint64_t event_mask; + + result = GNUNET_SYSERR; + event_mask = 0; + event_mask |= (1LL << GNUNET_TESTBED_ET_CONNECT); + event_mask |= (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED); + (void) GNUNET_TESTBED_test_run ("test_testbed_api_test", + "test_testbed_api.conf", NUM_PEERS, + event_mask, &controller_event_cb, NULL, + &test_master, NULL); + if (GNUNET_OK != result) + return 1; + return 0; +} + +/* end of test_testbed_api_test.c */ diff --git a/src/testbed/test_testbed_api_testbed_run.c b/src/testbed/test_testbed_api_testbed_run.c new file mode 100644 index 0000000..9cad74a --- /dev/null +++ b/src/testbed/test_testbed_api_testbed_run.c @@ -0,0 +1,223 @@ +/* + This file is part of GNUnet + (C) 2008--2012 Christian Grothoff (and other contributing authors) + + GNUnet 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. + + 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file testbed/test_testbed_api_testbed_run.c + * @brief Test cases for testing high-level testbed management + * @author Sree Harsha Totakura <sreeharsha@totakura.in> + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_testbed_service.h" + +/** + * Number of peers we want to start + */ +#define NUM_PEERS 5 + +/** + * The array of peers; we fill this as the peers are given to us by the testbed + */ +static struct GNUNET_TESTBED_Peer *peers[NUM_PEERS]; + +/** + * Operation handle + */ +static struct GNUNET_TESTBED_Operation *op; + +/** + * Abort task identifier + */ +static GNUNET_SCHEDULER_TaskIdentifier abort_task; + +/** + * Current peer id + */ +unsigned int peer_id; + +/** + * Testing result + */ +static int result; + + +/** + * Shutdown nicely + * + * @param cls NULL + * @param tc the task context + */ +static void +do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if (GNUNET_SCHEDULER_NO_TASK != abort_task) + GNUNET_SCHEDULER_cancel (abort_task); + GNUNET_SCHEDULER_shutdown (); /* Stop scheduler to shutdown testbed run */ +} + + +/** + * abort task to run on test timed out + * + * @param cls NULL + * @param tc the task context + */ +static void +do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Test timedout -- Aborting\n"); + abort_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); +} + + +/** + * Signature of a main function for a testcase. + * + * @param cls closure + * @param num_peers number of peers in 'peers' + * @param peers handle to peers run in the testbed + */ +static void +test_master (void *cls, unsigned int num_peers, + struct GNUNET_TESTBED_Peer **peers_) +{ + result = GNUNET_OK; + GNUNET_assert (NULL != peers[0]); + op = GNUNET_TESTBED_peer_stop (peers[0], NULL, NULL); + GNUNET_assert (NULL != op); +} + + +/** + * Controller event callback + * + * @param cls NULL + * @param event the controller event + */ +static void +controller_event_cb (void *cls, + const struct GNUNET_TESTBED_EventInformation *event) +{ + + switch (event->type) + { + case GNUNET_TESTBED_ET_PEER_START: + GNUNET_assert (NULL == peers[peer_id]); + GNUNET_assert (NULL != event->details.peer_start.peer); + peers[peer_id++] = event->details.peer_start.peer; + break; + case GNUNET_TESTBED_ET_PEER_STOP: + GNUNET_assert (NULL != op); + GNUNET_TESTBED_operation_done (op); + GNUNET_assert (peers[0] == event->details.peer_stop.peer); + GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); + break; + default: + GNUNET_assert (0); + } +} + + +/** + * Main run function. + * + * @param cls NULL + * @param args arguments passed to GNUNET_PROGRAM_run + * @param cfgfile the path to configuration file + * @param cfg the configuration file handle + */ +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *config) +{ + uint64_t event_mask; + + event_mask = 0; + event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_START); + event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_STOP); + GNUNET_TESTBED_run (NULL, config, NUM_PEERS, event_mask, &controller_event_cb, + NULL, &test_master, NULL); + abort_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 300), &do_abort, + NULL); +} + + +/** + * Main function + */ +int +main (int argc, char **argv) +{ + char *argv2[] = { + "test_testbed_api_testbed_run", + "-c", NULL, + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + char *testname; + char *config_filename; + int ret; + + if (NULL == (testname = strrchr (argv[0], (int) '_'))) + { + GNUNET_break (0); + return 1; + } + testname++; + testname = GNUNET_strdup (testname); +#ifdef MINGW + { + char *period; + + /* check and remove .exe extension */ + period = strrchr (testname, (int) '.'); + if (NULL != period) + *period = '\0'; + else + GNUNET_break (0); /* Windows with no .exe? */ + } +#endif + if (0 != strcmp ("run", testname)) + { + GNUNET_asprintf (&config_filename, "test_testbed_api_testbed_run_%s.conf", + testname); + } + else + config_filename = GNUNET_strdup ("test_testbed_api.conf"); + GNUNET_free (testname); + argv2[2] = config_filename; + result = GNUNET_SYSERR; + ret = + GNUNET_PROGRAM_run ((sizeof (argv2) / sizeof (char *)) - 1, argv2, + "test_testbed_api_testbed_run", "nohelp", options, + &run, NULL); + GNUNET_free (config_filename); + if ((GNUNET_OK != ret) || (GNUNET_OK != result)) + return 1; + return 0; +} + +/* end of test_testbed_api_testbed_run.c */ diff --git a/src/testbed/test_testbed_api_testbed_run_topology2dtorus.conf b/src/testbed/test_testbed_api_testbed_run_topology2dtorus.conf new file mode 100644 index 0000000..ee0480e --- /dev/null +++ b/src/testbed/test_testbed_api_testbed_run_topology2dtorus.conf @@ -0,0 +1,79 @@ +[testbed] +AUTOSTART = NO +PORT = 12113 +ACCEPT_FROM = 127.0.0.1; +HOSTNAME = localhost +NEIGHBOUR_LIMIT = 100 +OVERLAY_TOPOLOGY = 2D_TORUS +#PREFIX = xterm -geometry 100x85 -T peer1 -e libtool --mode=execute gdb --args + +[fs] +AUTOSTART = NO + +[resolver] +AUTOSTART = NO + +[mesh] +AUTOSTART = NO + +[dht] +AUTOSTART = NO + +[block] +plugins = dht test + +[dhtcache] +QUOTA = 1 MB +DATABASE = sqlite + +[transport] +PLUGINS = tcp +ACCEPT_FROM6 = ::1; +ACCEPT_FROM = 127.0.0.1; +NEIGHBOUR_LIMIT = 50 +PORT = 12365 + +[ats] +WAN_QUOTA_OUT = 3932160 +WAN_QUOTA_IN = 3932160 + +[core] +PORT = 12092 +AUTOSTART = YES + +[arm] +DEFAULTSERVICES = core transport +PORT = 12366 + +[transport-tcp] +TIMEOUT = 300 s +PORT = 12368 + +[TESTING] +NUM_PEERS = 5 +WEAKRANDOM = YES +HOSTKEYSFILE = ../../contrib/testing_hostkeys.dat +MAX_CONCURRENT_SSH = 10 +USE_PROGRESSBARS = YES +PEERGROUP_TIMEOUT = 2400 s + +[gnunetd] +HOSTKEY = $SERVICEHOME/.hostkey + +[PATHS] +SERVICEHOME = /tmp/test-testbed/ + +[dns] +AUTOSTART = NO + +[nse] +AUTOSTART = NO + +[vpn] +AUTOSTART = NO + +[nat] +RETURN_LOCAL_ADDRESSES = YES + +[gns-helper-service-w32] +AUTOSTART = NO diff --git a/src/testbed/test_testbed_api_testbed_run_topologyclique.conf b/src/testbed/test_testbed_api_testbed_run_topologyclique.conf new file mode 100644 index 0000000..4ac9266 --- /dev/null +++ b/src/testbed/test_testbed_api_testbed_run_topologyclique.conf @@ -0,0 +1,79 @@ +[testbed] +AUTOSTART = NO +PORT = 12113 +ACCEPT_FROM = 127.0.0.1; +HOSTNAME = localhost +NEIGHBOUR_LIMIT = 100 +OVERLAY_TOPOLOGY = CLIQUE +#PREFIX = xterm -geometry 100x85 -T peer1 -e libtool --mode=execute gdb --args + +[fs] +AUTOSTART = NO + +[resolver] +AUTOSTART = NO + +[mesh] +AUTOSTART = NO + +[dht] +AUTOSTART = NO + +[block] +plugins = dht test + +[dhtcache] +QUOTA = 1 MB +DATABASE = sqlite + +[transport] +PLUGINS = tcp +ACCEPT_FROM6 = ::1; +ACCEPT_FROM = 127.0.0.1; +NEIGHBOUR_LIMIT = 50 +PORT = 12365 + +[ats] +WAN_QUOTA_OUT = 3932160 +WAN_QUOTA_IN = 3932160 + +[core] +PORT = 12092 +AUTOSTART = YES + +[arm] +DEFAULTSERVICES = core transport +PORT = 12366 + +[transport-tcp] +TIMEOUT = 300 s +PORT = 12368 + +[TESTING] +NUM_PEERS = 5 +WEAKRANDOM = YES +HOSTKEYSFILE = ../../contrib/testing_hostkeys.dat +MAX_CONCURRENT_SSH = 10 +USE_PROGRESSBARS = YES +PEERGROUP_TIMEOUT = 2400 s + +[gnunetd] +HOSTKEY = $SERVICEHOME/.hostkey + +[PATHS] +SERVICEHOME = /tmp/test-testbed/ + +[dns] +AUTOSTART = NO + +[nse] +AUTOSTART = NO + +[vpn] +AUTOSTART = NO + +[nat] +RETURN_LOCAL_ADDRESSES = YES + +[gns-helper-service-w32] +AUTOSTART = NO diff --git a/src/testbed/test_testbed_api_testbed_run_topologyfromfile.conf b/src/testbed/test_testbed_api_testbed_run_topologyfromfile.conf new file mode 100644 index 0000000..b66724f --- /dev/null +++ b/src/testbed/test_testbed_api_testbed_run_topologyfromfile.conf @@ -0,0 +1,80 @@ +[testbed] +AUTOSTART = NO +PORT = 12113 +ACCEPT_FROM = 127.0.0.1; +HOSTNAME = localhost +NEIGHBOUR_LIMIT = 100 +OVERLAY_TOPOLOGY = FROM_FILE +OVERLAY_TOPOLOGY_FILE = overlay_topology.txt +#PREFIX = xterm -geometry 100x85 -T peer1 -e libtool --mode=execute gdb --args + +[fs] +AUTOSTART = NO + +[resolver] +AUTOSTART = NO + +[mesh] +AUTOSTART = NO + +[dht] +AUTOSTART = NO + +[block] +plugins = dht test + +[dhtcache] +QUOTA = 1 MB +DATABASE = sqlite + +[transport] +PLUGINS = tcp +ACCEPT_FROM6 = ::1; +ACCEPT_FROM = 127.0.0.1; +NEIGHBOUR_LIMIT = 50 +PORT = 12365 + +[ats] +WAN_QUOTA_OUT = 3932160 +WAN_QUOTA_IN = 3932160 + +[core] +PORT = 12092 +AUTOSTART = YES + +[arm] +DEFAULTSERVICES = core transport +PORT = 12366 + +[transport-tcp] +TIMEOUT = 300 s +PORT = 12368 + +[TESTING] +NUM_PEERS = 5 +WEAKRANDOM = YES +HOSTKEYSFILE = ../../contrib/testing_hostkeys.dat +MAX_CONCURRENT_SSH = 10 +USE_PROGRESSBARS = YES +PEERGROUP_TIMEOUT = 2400 s + +[gnunetd] +HOSTKEY = $SERVICEHOME/.hostkey + +[PATHS] +SERVICEHOME = /tmp/test-testbed/ + +[dns] +AUTOSTART = NO + +[nse] +AUTOSTART = NO + +[vpn] +AUTOSTART = NO + +[nat] +RETURN_LOCAL_ADDRESSES = YES + +[gns-helper-service-w32] +AUTOSTART = NO diff --git a/src/testbed/test_testbed_api_testbed_run_topologyline.conf b/src/testbed/test_testbed_api_testbed_run_topologyline.conf new file mode 100644 index 0000000..289ccba --- /dev/null +++ b/src/testbed/test_testbed_api_testbed_run_topologyline.conf @@ -0,0 +1,79 @@ +[testbed] +AUTOSTART = NO +PORT = 12113 +ACCEPT_FROM = 127.0.0.1; +HOSTNAME = localhost +NEIGHBOUR_LIMIT = 100 +OVERLAY_TOPOLOGY = LINE +#PREFIX = xterm -geometry 100x85 -T peer1 -e libtool --mode=execute gdb --args + +[fs] +AUTOSTART = NO + +[resolver] +AUTOSTART = NO + +[mesh] +AUTOSTART = NO + +[dht] +AUTOSTART = NO + +[block] +plugins = dht test + +[dhtcache] +QUOTA = 1 MB +DATABASE = sqlite + +[transport] +PLUGINS = tcp +ACCEPT_FROM6 = ::1; +ACCEPT_FROM = 127.0.0.1; +NEIGHBOUR_LIMIT = 50 +PORT = 12365 + +[ats] +WAN_QUOTA_OUT = 3932160 +WAN_QUOTA_IN = 3932160 + +[core] +PORT = 12092 +AUTOSTART = YES + +[arm] +DEFAULTSERVICES = core transport +PORT = 12366 + +[transport-tcp] +TIMEOUT = 300 s +PORT = 12368 + +[TESTING] +NUM_PEERS = 5 +WEAKRANDOM = YES +HOSTKEYSFILE = ../../contrib/testing_hostkeys.dat +MAX_CONCURRENT_SSH = 10 +USE_PROGRESSBARS = YES +PEERGROUP_TIMEOUT = 2400 s + +[gnunetd] +HOSTKEY = $SERVICEHOME/.hostkey + +[PATHS] +SERVICEHOME = /tmp/test-testbed/ + +[dns] +AUTOSTART = NO + +[nse] +AUTOSTART = NO + +[vpn] +AUTOSTART = NO + +[nat] +RETURN_LOCAL_ADDRESSES = YES + +[gns-helper-service-w32] +AUTOSTART = NO diff --git a/src/testbed/test_testbed_api_testbed_run_topologyrandom.conf b/src/testbed/test_testbed_api_testbed_run_topologyrandom.conf new file mode 100644 index 0000000..027602e --- /dev/null +++ b/src/testbed/test_testbed_api_testbed_run_topologyrandom.conf @@ -0,0 +1,79 @@ +[testbed] +AUTOSTART = NO +PORT = 12113 +ACCEPT_FROM = 127.0.0.1; +HOSTNAME = localhost +OVERLAY_TOPOLOGY = RANDOM +OVERLAY_RANDOM_LINKS = 5 +#PREFIX = xterm -geometry 100x85 -T peer1 -e libtool --mode=execute gdb --args + +[fs] +AUTOSTART = NO + +[resolver] +AUTOSTART = NO + +[mesh] +AUTOSTART = NO + +[dht] +AUTOSTART = NO + +[block] +plugins = dht test + +[dhtcache] +QUOTA = 1 MB +DATABASE = sqlite + +[transport] +PLUGINS = tcp +ACCEPT_FROM6 = ::1; +ACCEPT_FROM = 127.0.0.1; +NEIGHBOUR_LIMIT = 50 +PORT = 12365 + +[ats] +WAN_QUOTA_OUT = 3932160 +WAN_QUOTA_IN = 3932160 + +[core] +PORT = 12092 +AUTOSTART = YES + +[arm] +DEFAULTSERVICES = core transport +PORT = 12366 + +[transport-tcp] +TIMEOUT = 300 s +PORT = 12368 + +[TESTING] +NUM_PEERS = 5 +WEAKRANDOM = YES +HOSTKEYSFILE = ../../contrib/testing_hostkeys.dat +MAX_CONCURRENT_SSH = 10 +USE_PROGRESSBARS = YES +PEERGROUP_TIMEOUT = 2400 s + +[gnunetd] +HOSTKEY = $SERVICEHOME/.hostkey + +[PATHS] +SERVICEHOME = /tmp/test-testbed/ + +[dns] +AUTOSTART = NO + +[nse] +AUTOSTART = NO + +[vpn] +AUTOSTART = NO + +[nat] +RETURN_LOCAL_ADDRESSES = YES + +[gns-helper-service-w32] +AUTOSTART = NO diff --git a/src/testbed/test_testbed_api_testbed_run_topologyring.conf b/src/testbed/test_testbed_api_testbed_run_topologyring.conf new file mode 100644 index 0000000..22d934d --- /dev/null +++ b/src/testbed/test_testbed_api_testbed_run_topologyring.conf @@ -0,0 +1,79 @@ +[testbed] +AUTOSTART = NO +PORT = 12113 +ACCEPT_FROM = 127.0.0.1; +HOSTNAME = localhost +NEIGHBOUR_LIMIT = 100 +OVERLAY_TOPOLOGY = RING +#PREFIX = xterm -geometry 100x85 -T peer1 -e libtool --mode=execute gdb --args + +[fs] +AUTOSTART = NO + +[resolver] +AUTOSTART = NO + +[mesh] +AUTOSTART = NO + +[dht] +AUTOSTART = NO + +[block] +plugins = dht test + +[dhtcache] +QUOTA = 1 MB +DATABASE = sqlite + +[transport] +PLUGINS = tcp +ACCEPT_FROM6 = ::1; +ACCEPT_FROM = 127.0.0.1; +NEIGHBOUR_LIMIT = 50 +PORT = 12365 + +[ats] +WAN_QUOTA_OUT = 3932160 +WAN_QUOTA_IN = 3932160 + +[core] +PORT = 12092 +AUTOSTART = YES + +[arm] +DEFAULTSERVICES = core transport +PORT = 12366 + +[transport-tcp] +TIMEOUT = 300 s +PORT = 12368 + +[TESTING] +NUM_PEERS = 5 +WEAKRANDOM = YES +HOSTKEYSFILE = ../../contrib/testing_hostkeys.dat +MAX_CONCURRENT_SSH = 10 +USE_PROGRESSBARS = YES +PEERGROUP_TIMEOUT = 2400 s + +[gnunetd] +HOSTKEY = $SERVICEHOME/.hostkey + +[PATHS] +SERVICEHOME = /tmp/test-testbed/ + +[dns] +AUTOSTART = NO + +[nse] +AUTOSTART = NO + +[vpn] +AUTOSTART = NO + +[nat] +RETURN_LOCAL_ADDRESSES = YES + +[gns-helper-service-w32] +AUTOSTART = NO diff --git a/src/testbed/test_testbed_api_testbed_run_topologyscalefree.conf b/src/testbed/test_testbed_api_testbed_run_topologyscalefree.conf new file mode 100644 index 0000000..22d934d --- /dev/null +++ b/src/testbed/test_testbed_api_testbed_run_topologyscalefree.conf @@ -0,0 +1,79 @@ +[testbed] +AUTOSTART = NO +PORT = 12113 +ACCEPT_FROM = 127.0.0.1; +HOSTNAME = localhost +NEIGHBOUR_LIMIT = 100 +OVERLAY_TOPOLOGY = RING +#PREFIX = xterm -geometry 100x85 -T peer1 -e libtool --mode=execute gdb --args + +[fs] +AUTOSTART = NO + +[resolver] +AUTOSTART = NO + +[mesh] +AUTOSTART = NO + +[dht] +AUTOSTART = NO + +[block] +plugins = dht test + +[dhtcache] +QUOTA = 1 MB +DATABASE = sqlite + +[transport] +PLUGINS = tcp +ACCEPT_FROM6 = ::1; +ACCEPT_FROM = 127.0.0.1; +NEIGHBOUR_LIMIT = 50 +PORT = 12365 + +[ats] +WAN_QUOTA_OUT = 3932160 +WAN_QUOTA_IN = 3932160 + +[core] +PORT = 12092 +AUTOSTART = YES + +[arm] +DEFAULTSERVICES = core transport +PORT = 12366 + +[transport-tcp] +TIMEOUT = 300 s +PORT = 12368 + +[TESTING] +NUM_PEERS = 5 +WEAKRANDOM = YES +HOSTKEYSFILE = ../../contrib/testing_hostkeys.dat +MAX_CONCURRENT_SSH = 10 +USE_PROGRESSBARS = YES +PEERGROUP_TIMEOUT = 2400 s + +[gnunetd] +HOSTKEY = $SERVICEHOME/.hostkey + +[PATHS] +SERVICEHOME = /tmp/test-testbed/ + +[dns] +AUTOSTART = NO + +[nse] +AUTOSTART = NO + +[vpn] +AUTOSTART = NO + +[nat] +RETURN_LOCAL_ADDRESSES = YES + +[gns-helper-service-w32] +AUTOSTART = NO diff --git a/src/testbed/test_testbed_api_testbed_run_topologysmallworld.conf b/src/testbed/test_testbed_api_testbed_run_topologysmallworld.conf new file mode 100644 index 0000000..fabc74f --- /dev/null +++ b/src/testbed/test_testbed_api_testbed_run_topologysmallworld.conf @@ -0,0 +1,83 @@ +[testbed] +AUTOSTART = NO +PORT = 12113 +ACCEPT_FROM = 127.0.0.1; +HOSTNAME = localhost +OVERLAY_TOPOLOGY = SMALL_WORLD +OVERLAY_RANDOM_LINKS = 3 +#PREFIX = xterm -geometry 100x85 -T peer1 -e libtool --mode=execute gdb --args + +[fs] +AUTOSTART = NO + +[resolver] +AUTOSTART = NO + +[mesh] +AUTOSTART = NO + +[dht] +AUTOSTART = NO + +[block] +plugins = dht test + +[dhtcache] +QUOTA = 1 MB +DATABASE = sqlite + +[transport] +PLUGINS = tcp +ACCEPT_FROM6 = ::1; +ACCEPT_FROM = 127.0.0.1; +NEIGHBOUR_LIMIT = 50 +PORT = 12365 + +[ats] +WAN_QUOTA_OUT = 3932160 +WAN_QUOTA_IN = 3932160 + +[core] +PORT = 12092 +AUTOSTART = YES + +[arm] +DEFAULTSERVICES = core transport +PORT = 12366 + +[transport-tcp] +TIMEOUT = 300 s +PORT = 12368 + +[TESTING] +NUM_PEERS = 5 +WEAKRANDOM = YES +HOSTKEYSFILE = ../../contrib/testing_hostkeys.dat +MAX_CONCURRENT_SSH = 10 +USE_PROGRESSBARS = YES +PEERGROUP_TIMEOUT = 2400 s + +[gnunetd] +HOSTKEY = $SERVICEHOME/.hostkey + +[PATHS] +SERVICEHOME = /tmp/test-testbed/ + +[dns] +AUTOSTART = NO + +[nse] +AUTOSTART = NO + +[vpn] +AUTOSTART = NO + +[nat] +RETURN_LOCAL_ADDRESSES = YES + +[gns-helper-service-w32] +AUTOSTART = NO + +[consensus] +AUTOSTART = NO + diff --git a/src/testbed/test_testbed_api_testbed_run_topologysmallworldring.conf b/src/testbed/test_testbed_api_testbed_run_topologysmallworldring.conf new file mode 100644 index 0000000..7b26f4f --- /dev/null +++ b/src/testbed/test_testbed_api_testbed_run_topologysmallworldring.conf @@ -0,0 +1,83 @@ +[testbed] +AUTOSTART = NO +PORT = 12113 +ACCEPT_FROM = 127.0.0.1; +HOSTNAME = localhost +OVERLAY_TOPOLOGY = SMALL_WORLD_RING +OVERLAY_RANDOM_LINKS = 3 +#PREFIX = xterm -geometry 100x85 -T peer1 -e libtool --mode=execute gdb --args + +[fs] +AUTOSTART = NO + +[resolver] +AUTOSTART = NO + +[mesh] +AUTOSTART = NO + +[dht] +AUTOSTART = NO + +[block] +plugins = dht test + +[dhtcache] +QUOTA = 1 MB +DATABASE = sqlite + +[transport] +PLUGINS = tcp +ACCEPT_FROM6 = ::1; +ACCEPT_FROM = 127.0.0.1; +NEIGHBOUR_LIMIT = 50 +PORT = 12365 + +[ats] +WAN_QUOTA_OUT = 3932160 +WAN_QUOTA_IN = 3932160 + +[core] +PORT = 12092 +AUTOSTART = YES + +[arm] +DEFAULTSERVICES = core transport +PORT = 12366 + +[transport-tcp] +TIMEOUT = 300 s +PORT = 12368 + +[TESTING] +NUM_PEERS = 5 +WEAKRANDOM = YES +HOSTKEYSFILE = ../../contrib/testing_hostkeys.dat +MAX_CONCURRENT_SSH = 10 +USE_PROGRESSBARS = YES +PEERGROUP_TIMEOUT = 2400 s + +[gnunetd] +HOSTKEY = $SERVICEHOME/.hostkey + +[PATHS] +SERVICEHOME = /tmp/test-testbed/ + +[dns] +AUTOSTART = NO + +[nse] +AUTOSTART = NO + +[vpn] +AUTOSTART = NO + +[nat] +RETURN_LOCAL_ADDRESSES = YES + +[gns-helper-service-w32] +AUTOSTART = NO + +[consensus] +AUTOSTART = NO + diff --git a/src/testbed/test_testbed_api_topology.c b/src/testbed/test_testbed_api_topology.c new file mode 100644 index 0000000..0098dbe --- /dev/null +++ b/src/testbed/test_testbed_api_topology.c @@ -0,0 +1,172 @@ +/* + This file is part of GNUnet + (C) 2008--2012 Christian Grothoff (and other contributing authors) + + GNUnet 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. + + 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file src/testbed/test_testbed_api_topology.c + * @brief testing cases for testing high level testbed api helper functions + * @author Sree Harsha Totakura <sreeharsha@totakura.in> + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_testbed_service.h" + +/** + * Number of peers we want to start + */ +#define NUM_PEERS 10 + +/** + * Array of peers + */ +static struct GNUNET_TESTBED_Peer **peers; + +/** + * Operation handle + */ +static struct GNUNET_TESTBED_Operation *op; + +/** + * Shutdown task + */ +static GNUNET_SCHEDULER_TaskIdentifier shutdown_task; + +/** + * Testing result + */ +static int result; + +/** + * Counter for counting overlay connections + */ +static unsigned int overlay_connects; + + +/** + * Shutdown nicely + * + * @param cls NULL + * @param tc the task context + */ +static void +do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + shutdown_task = GNUNET_SCHEDULER_NO_TASK; + if (NULL != op) + { + GNUNET_TESTBED_operation_done (op); + op = NULL; + } + GNUNET_SCHEDULER_shutdown (); +} + +/** + * Controller event callback + * + * @param cls NULL + * @param event the controller event + */ +static void +controller_event_cb (void *cls, + const struct GNUNET_TESTBED_EventInformation *event) +{ + switch (event->type) + { + case GNUNET_TESTBED_ET_CONNECT: + overlay_connects++; + if ((NUM_PEERS) == overlay_connects) + { + result = GNUNET_OK; + GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); + } + break; + case GNUNET_TESTBED_ET_OPERATION_FINISHED: + GNUNET_assert (NULL != event->details.operation_finished.emsg); + break; + default: + GNUNET_break (0); + if ((GNUNET_TESTBED_ET_OPERATION_FINISHED == event->type) && + (NULL != event->details.operation_finished.emsg)) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "An operation failed with error: %s\n", + event->details.operation_finished.emsg); + result = GNUNET_SYSERR; + GNUNET_SCHEDULER_cancel (shutdown_task); + shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); + } +} + + +/** + * Signature of a main function for a testcase. + * + * @param cls closure + * @param num_peers number of peers in 'peers' + * @param peers handle to peers run in the testbed + */ +static void +test_master (void *cls, unsigned int num_peers, + struct GNUNET_TESTBED_Peer **peers_) +{ + unsigned int peer; + + GNUNET_assert (NULL == cls); + GNUNET_assert (NUM_PEERS == num_peers); + GNUNET_assert (NULL != peers_); + for (peer = 0; peer < num_peers; peer++) + GNUNET_assert (NULL != peers_[peer]); + peers = peers_; + overlay_connects = 0; + op = GNUNET_TESTBED_overlay_configure_topology (NULL, NUM_PEERS, peers, NULL, + NULL, + NULL, + GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI, + NUM_PEERS, + GNUNET_TESTBED_TOPOLOGY_OPTION_END); + GNUNET_assert (NULL != op); + shutdown_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 300), + do_shutdown, NULL); +} + + +/** + * Main function + */ +int +main (int argc, char **argv) +{ + uint64_t event_mask; + + result = GNUNET_SYSERR; + event_mask = 0; + event_mask |= (1LL << GNUNET_TESTBED_ET_CONNECT); + event_mask |= (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED); + (void) GNUNET_TESTBED_test_run ("test_testbed_api_test", + "test_testbed_api.conf", NUM_PEERS, + event_mask, &controller_event_cb, NULL, + &test_master, NULL); + if (GNUNET_OK != result) + return 1; + return 0; +} + +/* end of test_testbed_api_topology.c */ diff --git a/src/testbed/test_testbed_api_topology_clique.c b/src/testbed/test_testbed_api_topology_clique.c new file mode 100644 index 0000000..3f1ed7a --- /dev/null +++ b/src/testbed/test_testbed_api_topology_clique.c @@ -0,0 +1,168 @@ +/* + This file is part of GNUnet + (C) 2008--2012 Christian Grothoff (and other contributing authors) + + GNUnet 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. + + 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file src/testbed/test_testbed_api_topology.c + * @brief testing cases for testing high level testbed api helper functions + * @author Sree Harsha Totakura <sreeharsha@totakura.in> + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_testbed_service.h" + +/** + * Number of peers we want to start + */ +#define NUM_PEERS 10 + +/** + * Array of peers + */ +static struct GNUNET_TESTBED_Peer **peers; + +/** + * Operation handle + */ +static struct GNUNET_TESTBED_Operation *op; + +/** + * Shutdown task + */ +static GNUNET_SCHEDULER_TaskIdentifier shutdown_task; + +/** + * Testing result + */ +static int result; + +/** + * Counter for counting overlay connections + */ +static unsigned int overlay_connects; + + +/** + * Shutdown nicely + * + * @param cls NULL + * @param tc the task context + */ +static void +do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + shutdown_task = GNUNET_SCHEDULER_NO_TASK; + if (NULL != op) + { + GNUNET_TESTBED_operation_done (op); + op = NULL; + } + GNUNET_SCHEDULER_shutdown (); +} + +/** + * Controller event callback + * + * @param cls NULL + * @param event the controller event + */ +static void +controller_event_cb (void *cls, + const struct GNUNET_TESTBED_EventInformation *event) +{ + switch (event->type) + { + case GNUNET_TESTBED_ET_CONNECT: + overlay_connects++; + if ((NUM_PEERS * (NUM_PEERS - 1)) == overlay_connects) + { + result = GNUNET_OK; + GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); + } + break; + case GNUNET_TESTBED_ET_OPERATION_FINISHED: + GNUNET_assert (NULL != event->details.operation_finished.emsg); + break; + default: + GNUNET_break (0); + result = GNUNET_SYSERR; + GNUNET_SCHEDULER_cancel (shutdown_task); + shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); + } +} + + +/** + * Signature of a main function for a testcase. + * + * @param cls closure + * @param num_peers number of peers in 'peers' + * @param peers handle to peers run in the testbed + */ +static void +test_master (void *cls, unsigned int num_peers, + struct GNUNET_TESTBED_Peer **peers_) +{ + unsigned int peer; + + GNUNET_assert (NULL == cls); + GNUNET_assert (NUM_PEERS == num_peers); + GNUNET_assert (NULL != peers_); + for (peer = 0; peer < num_peers; peer++) + GNUNET_assert (NULL != peers_[peer]); + peers = peers_; + overlay_connects = 0; + op = GNUNET_TESTBED_overlay_configure_topology (NULL, NUM_PEERS, peers, NULL, + NULL, + NULL, + GNUNET_TESTBED_TOPOLOGY_CLIQUE, + /* GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI, */ + /* NUM_PEERS, */ + GNUNET_TESTBED_TOPOLOGY_OPTION_END); + GNUNET_assert (NULL != op); + shutdown_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 300), + do_shutdown, NULL); +} + + +/** + * Main function + */ +int +main (int argc, char **argv) +{ + uint64_t event_mask; + + result = GNUNET_SYSERR; + event_mask = 0; + event_mask |= (1LL << GNUNET_TESTBED_ET_CONNECT); + event_mask |= (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED); + (void) GNUNET_TESTBED_test_run ("test_testbed_api_test", + "test_testbed_api.conf", NUM_PEERS, + event_mask, &controller_event_cb, NULL, + &test_master, NULL); + if (GNUNET_OK != result) + return 1; + return 0; +} + +/* end of test_testbed_api_topology.c */ diff --git a/src/testbed/testbed.conf b/src/testbed/testbed.conf deleted file mode 100644 index e69de29..0000000 --- a/src/testbed/testbed.conf +++ /dev/null diff --git a/src/testbed/testbed.conf.in b/src/testbed/testbed.conf.in new file mode 100644 index 0000000..ec05c76 --- /dev/null +++ b/src/testbed/testbed.conf.in @@ -0,0 +1,18 @@ +[testbed] +AUTOSTART = NO +@UNIXONLY@ PORT = 2101 +HOSTNAME = localhost +HOME = $SERVICEHOME +BINARY = gnunet-service-testbed +# Set this to the path where the testbed helper is installed +# HELPER_BINARY_PATH = @prefix@/lib/gnunet/libexec/gnunet-helper-testbed +ACCEPT_FROM = 127.0.0.1; +ACCEPT_FROM6 = ::1; +UNIXPATH = /tmp/gnunet-service-testbed.sock +UNIX_MATCH_UID = YES +UNIX_MATCH_GID = YES +MAX_PARALLEL_OPERATIONS = 1000 +MAX_PARALLEL_SERVICE_CONNECTIONS = 256 +MAX_PARALLEL_TOPOLOGY_CONFIG_OPERATIONS = 1 +CACHE_SIZE = 30 +MAX_OPEN_FDS = 512 diff --git a/src/testbed/testbed.h b/src/testbed/testbed.h index b7b10ff..180464b 100644 --- a/src/testbed/testbed.h +++ b/src/testbed/testbed.h @@ -24,28 +24,28 @@ * @author Christian Grothoff */ -#ifndef NEW_TESTING_H -#define NEW_TESTING_H +#ifndef TESTBED_H +#define TESTBED_H #include "gnunet_util_lib.h" - +GNUNET_NETWORK_STRUCT_BEGIN /** * Initial message from a client to a testing control service. */ -struct GNUNET_TESTBED_Message + struct GNUNET_TESTBED_InitMessage { /** - * Type is + * Type is GNUNET_MESSAGE_TYPE_TESTBED_INIT */ struct GNUNET_MessageHeader header; /** - * Host ID that the controller is either given - * (if this is the dominating client communicating - * via stdin) or assumed to have (for peer-connections - * between controllers). + * Host ID that the controller is either given (if this is the + * dominating client) or assumed to have (for peer-connections + * between controllers). A controller must check that all + * connections make consistent claims... */ uint32_t host_id GNUNET_PACKED; @@ -55,6 +55,7 @@ struct GNUNET_TESTBED_Message */ uint64_t event_mask GNUNET_PACKED; + /* Followed by 0-terminated hostname of the controller */ }; @@ -65,7 +66,7 @@ struct GNUNET_TESTBED_AddHostMessage { /** - * Type is + * Type is GNUNET_MESSAGE_TYPE_TESTBED_ADD_HOST */ struct GNUNET_MessageHeader header; @@ -96,12 +97,13 @@ struct GNUNET_TESTBED_AddHostMessage /** * Confirmation from the service that adding a host * worked (or failed). + * FIXME: Where is this required? */ struct GNUNET_TESTBED_HostConfirmedMessage { /** - * Type is + * Type is GNUNET_MESSAGE_TYPE_TESTBED_ADD_HOST_SUCCESS */ struct GNUNET_MessageHeader header; @@ -110,9 +112,8 @@ struct GNUNET_TESTBED_HostConfirmedMessage */ uint32_t host_id GNUNET_PACKED; - /* followed by the 0-terminated error message (on failure) - (typical errors include failure to login and - host-id already in use) */ + /* followed by the 0-terminated error message (on failure) + * (typical errors include host-id already in use) */ }; @@ -125,7 +126,7 @@ struct GNUNET_TESTBED_ConfigureSharedServiceMessage { /** - * Type is + * Type is GNUNET_MESSAGE_TYPE_TESTBED_SHARE_SERVICE */ struct GNUNET_MessageHeader header; @@ -154,7 +155,7 @@ struct GNUNET_TESTBED_ControllerLinkMessage { /** - * Type is + * Type is GNUNET_MESSAGE_TYPE_TESTBED_LINK_CONTROLLERS */ struct GNUNET_MessageHeader header; @@ -164,35 +165,50 @@ struct GNUNET_TESTBED_ControllerLinkMessage uint32_t delegated_host_id GNUNET_PACKED; /** + * The id of the operation which created this message + */ + uint64_t operation_id GNUNET_PACKED; + + /** * Which host is responsible for managing the delegation? NBO */ uint32_t slave_host_id GNUNET_PACKED; /** - * Is the receiving controller the master controller for - * the slave host (and thus responsible for starting it?). NBO. + * The size of the uncompressed configuration */ - int32_t is_subordinate GNUNET_PACKED; + uint16_t config_size GNUNET_PACKED; + + /** + * Set to 1 if the receiving controller is the master controller for + * the slave host (and thus responsible for starting it?). 0 if not + */ + uint8_t is_subordinate; /* followed by serialized slave configuration; - gzip'ed configuration file in INI format */ + * gzip'ed configuration file in INI format */ }; /** - * Message sent from client to testing service to + * Message sent from client to testing service to * create (configure, but not start) a peer. */ struct GNUNET_TESTBED_PeerCreateMessage { /** - * Type is + * Type is GNUNET_MESSAGE_TYPE_TESTBED_CREATE_PEER */ struct GNUNET_MessageHeader header; /** + * Unique operation id + */ + uint64_t operation_id GNUNET_PACKED; + + /** * On which host should the peer be started? */ uint32_t host_id GNUNET_PACKED; @@ -202,21 +218,26 @@ struct GNUNET_TESTBED_PeerCreateMessage */ uint32_t peer_id GNUNET_PACKED; + /** + * Size of the uncompressed configuration + */ + uint32_t config_size GNUNET_PACKED; + /* followed by serialized peer configuration; - gzip'ed configuration file in INI format */ - + * gzip'ed configuration file in INI format */ + }; /** - * Message sent from client to testing service to + * Message sent from client to testing service to * reconfigure a (stopped) a peer. */ struct GNUNET_TESTBED_PeerReconfigureMessage { /** - * Type is + * Type is GNUNET_MESSAGE_TYPDE_TESTBED_RECONFIGURE_PEER */ struct GNUNET_MessageHeader header; @@ -231,8 +252,8 @@ struct GNUNET_TESTBED_PeerReconfigureMessage uint64_t operation_id GNUNET_PACKED; /* followed by serialized peer configuration; - gzip'ed configuration file in INI format */ - + * gzip'ed configuration file in INI format */ + }; @@ -244,7 +265,7 @@ struct GNUNET_TESTBED_PeerStartMessage { /** - * Type is + * Type is GNUNET_MESSAGE_TYPE_TESTBED_START_PEER */ struct GNUNET_MessageHeader header; @@ -269,7 +290,7 @@ struct GNUNET_TESTBED_PeerStopMessage { /** - * Type is + * Type is GNUNET_MESSAGE_TYPE_TESTBED_STOP_PEER */ struct GNUNET_MessageHeader header; @@ -294,7 +315,7 @@ struct GNUNET_TESTBED_PeerDestroyMessage { /** - * Type is + * Type is GNUNET_MESSAGE_TYPE_TESTBED_DESTROY_PEER */ struct GNUNET_MessageHeader header; @@ -319,7 +340,7 @@ struct GNUNET_TESTBED_ConfigureUnderlayLinkMessage { /** - * Type is + * Type is GNUNET_MESSAGE_TYPE_TESTBED_CONFIGURE_UNDERLAY_LINK */ struct GNUNET_MessageHeader header; @@ -356,7 +377,7 @@ struct GNUNET_TESTBED_OverlayConnectMessage { /** - * Type is + * Type is GNUNET_MESSAGE_TYPE_TESTBED_OVERLAY_CONNECT */ struct GNUNET_MessageHeader header; @@ -375,6 +396,45 @@ struct GNUNET_TESTBED_OverlayConnectMessage */ uint32_t peer2 GNUNET_PACKED; + /** + * The ID of the host which runs peer2 + */ + uint32_t peer2_host_id GNUNET_PACKED; + +}; + + +/** + * Message sent from host controller of a peer(A) to the host controller of + * another peer(B) to request B to connect to A + */ +struct GNUNET_TESTBED_RemoteOverlayConnectMessage +{ + /** + * Type is GNUNET_MESSAGE_TYPE_TESTBED_REMOTE_OVERLAY_CONNECT + */ + struct GNUNET_MessageHeader header; + + /** + * The Unique ID of B + */ + uint32_t peer GNUNET_PACKED; + + /** + * The Operation ID that is used to identify this operation + */ + uint64_t operation_id GNUNET_PACKED; + + /** + * Identity of A + */ + struct GNUNET_PeerIdentity peer_identity; + + /** + * To be followed by the HELLO message of A + */ + struct GNUNET_MessageHeader hello[0]; + }; @@ -385,7 +445,7 @@ struct GNUNET_TESTBED_PeerEventMessage { /** - * Type is + * Type is GNUNET_MESSAGE_TYPE_TESTBED_PEER_EVENT */ struct GNUNET_MessageHeader header; @@ -394,7 +454,7 @@ struct GNUNET_TESTBED_PeerEventMessage * either GNUNET_TESTBED_ET_PEER_START or GNUNET_TESTBED_ET_PEER_STOP. */ int32_t event_type GNUNET_PACKED; - + /** * Host where the peer is running. */ @@ -420,16 +480,16 @@ struct GNUNET_TESTBED_ConnectionEventMessage { /** - * Type is + * Type is GNUNET_MESSAGE_TYPE_TESTBED_PEER_CONNECT_EVENT */ struct GNUNET_MessageHeader header; /** * 'enum GNUNET_TESTBED_EventType' (in NBO); - * either GNUNET_TESTBED_ET_PEER_CONNECT or GNUNET_TESTBED_ET_PEER_DISCONNECT. + * either GNUNET_TESTBED_ET_CONNECT or GNUNET_TESTBED_ET_DISCONNECT. */ int32_t event_type GNUNET_PACKED; - + /** * First peer. */ @@ -455,7 +515,7 @@ struct GNUNET_TESTBED_OperationFailureEventMessage { /** - * Type is + * Type is GNUNET_MESSAGE_TYPE_TESTBED_OPERATION_FAIL_EVENT */ struct GNUNET_MessageHeader header; @@ -464,7 +524,7 @@ struct GNUNET_TESTBED_OperationFailureEventMessage * GNUNET_TESTBED_ET_OPERATION_FINISHED. */ int32_t event_type GNUNET_PACKED; - + /** * Operation ID of the operation that created this event. */ @@ -482,7 +542,7 @@ struct GNUNET_TESTBED_PeerCreateSuccessEventMessage { /** - * Type is + * Type is GNUNET_MESSAGE_TYPE_TESTBED_CREATE_PEER_SUCCESS */ struct GNUNET_MessageHeader header; @@ -490,19 +550,12 @@ struct GNUNET_TESTBED_PeerCreateSuccessEventMessage * Peer identity of the peer that was created. */ uint32_t peer_id GNUNET_PACKED; - + /** * Operation ID of the operation that created this event. */ uint64_t operation_id GNUNET_PACKED; - /** - * Identity of the peer. - */ - struct GNUNET_PeerIdentity peer_id; - - /* followed by gzip-compressed configuration of the peer */ - }; @@ -515,7 +568,7 @@ struct GNUNET_TESTBED_GenericOperationSuccessEventMessage { /** - * Type is + * Type is GNUNET_MESSAGE_TYPE_TESTBED_GENERIC_OPERATION_SUCCESS */ struct GNUNET_MessageHeader header; @@ -524,7 +577,7 @@ struct GNUNET_TESTBED_GenericOperationSuccessEventMessage * GNUNET_TESTBED_ET_OPERATION_FINISHED. */ int32_t event_type GNUNET_PACKED; - + /** * Operation ID of the operation that created this event. */ @@ -532,4 +585,121 @@ struct GNUNET_TESTBED_GenericOperationSuccessEventMessage }; + +/** + * Message sent from client to testing service to + * obtain the configuration of a peer. + */ +struct GNUNET_TESTBED_PeerGetConfigurationMessage +{ + + /** + * Type is GNUNET_MESSAGE_TYPE_TESTBED_GET_PEER_CONFIGURATION + */ + struct GNUNET_MessageHeader header; + + /** + * Unique ID for the peer. + */ + uint32_t peer_id GNUNET_PACKED; + + /** + * Operation ID that is used to identify this operation. + */ + uint64_t operation_id GNUNET_PACKED; + +}; + + +/** + * Peer configuration and identity reply from controller to a client. + */ +struct GNUNET_TESTBED_PeerConfigurationInformationMessage +{ + + /** + * Type is GNUNET_MESSAGE_TYPE_TESTBED_PEER_CONFIGURATION + */ + struct GNUNET_MessageHeader header; + + /** + * The id of the peer relevant to this information + */ + uint32_t peer_id GNUNET_PACKED; + + /** + * Operation ID of the operation that created this event. + */ + uint64_t operation_id GNUNET_PACKED; + + /** + * Identity of the peer. + */ + struct GNUNET_PeerIdentity peer_identity; + + /** + * The size of configuration when uncompressed + */ + uint16_t config_size GNUNET_PACKED; + + /* followed by gzip-compressed configuration of the peer */ + +}; + + +/** + * Message to request configuration of a slave controller + */ +struct GNUNET_TESTBED_SlaveGetConfigurationMessage +{ + /** + * Type is GNUNET_MESSAGE_TYPE_TESTBED_GET_SLAVE_CONFIGURATION + */ + struct GNUNET_MessageHeader header; + + /** + * The id of the slave host + */ + uint32_t slave_id GNUNET_PACKED; + + /** + * Operation ID + */ + uint64_t operation_id GNUNET_PACKED; + +}; + + +/** + * Reply to GNUNET_MESSAGE_TYPE_TESTBED_GET_SLAVE_CONFIG message + */ +struct GNUNET_TESTBED_SlaveConfiguration +{ + /** + * Type is GNUNET_MESSAGE_TYPE_TESTBED_SLAVE_CONFIGURATION + */ + struct GNUNET_MessageHeader header; + + /** + * The id of the host where the slave is running + */ + uint32_t slave_id GNUNET_PACKED; + + /** + * Operation ID + */ + uint64_t operation_id GNUNET_PACKED; + + /** + * The size of the configuration when uncompressed + */ + uint16_t config_size GNUNET_PACKED; + + /* followed by gzip-compressed configuration of the peer */ + +}; + + +GNUNET_NETWORK_STRUCT_END #endif +/* end of testbed.h */ diff --git a/src/testbed/testbed_api.c b/src/testbed/testbed_api.c index 5168081..054aa3b 100644 --- a/src/testbed/testbed_api.c +++ b/src/testbed/testbed_api.c @@ -24,15 +24,1772 @@ * This library is supposed to make it easier to write * testcases and script large-scale benchmarks. * @author Christian Grothoff + * @author Sree Harsha Totakura */ + + #include "platform.h" #include "gnunet_testbed_service.h" #include "gnunet_core_service.h" #include "gnunet_constants.h" #include "gnunet_transport_service.h" #include "gnunet_hello_lib.h" +#include <zlib.h> + +#include "testbed.h" +#include "testbed_api.h" +#include "testbed_api_hosts.h" +#include "testbed_api_peers.h" +#include "testbed_api_operations.h" + +/** + * Generic logging shorthand + */ +#define LOG(kind, ...) \ + GNUNET_log_from (kind, "testbed-api", __VA_ARGS__); + +/** + * Debug logging + */ +#define LOG_DEBUG(...) \ + LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__); + +/** + * Relative time seconds shorthand + */ +#define TIME_REL_SECS(sec) \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, sec) + + +/** + * Default server message sending retry timeout + */ +#define TIMEOUT_REL TIME_REL_SECS(1) + + +/** + * Handle for controller process + */ +struct GNUNET_TESTBED_ControllerProc +{ + /** + * The process handle + */ + struct GNUNET_HELPER_Handle *helper; + + /** + * The arguments used to start the helper + */ + char **helper_argv; + + /** + * The host where the helper is run + */ + struct GNUNET_TESTBED_Host *host; + + /** + * The controller error callback + */ + GNUNET_TESTBED_ControllerStatusCallback cb; + + /** + * The closure for the above callback + */ + void *cls; + + /** + * The send handle for the helper + */ + struct GNUNET_HELPER_SendHandle *shandle; + + /** + * The message corresponding to send handle + */ + struct GNUNET_MessageHeader *msg; + + /** + * The configuration of the running testbed service + */ + struct GNUNET_CONFIGURATION_Handle *cfg; + +}; + + +/** + * The message queue for sending messages to the controller service + */ +struct MessageQueue +{ + /** + * The message to be sent + */ + struct GNUNET_MessageHeader *msg; + + /** + * next pointer for DLL + */ + struct MessageQueue *next; + + /** + * prev pointer for DLL + */ + struct MessageQueue *prev; +}; + + +/** + * Structure for a controller link + */ +struct ControllerLink +{ + /** + * The next ptr for DLL + */ + struct ControllerLink *next; + + /** + * The prev ptr for DLL + */ + struct ControllerLink *prev; + + /** + * The host which will be referred in the peer start request. This is the + * host where the peer should be started + */ + struct GNUNET_TESTBED_Host *delegated_host; + + /** + * The host which will contacted to delegate the peer start request + */ + struct GNUNET_TESTBED_Host *slave_host; + + /** + * The configuration to be used to connect to slave host + */ + const struct GNUNET_CONFIGURATION_Handle *slave_cfg; + + /** + * GNUNET_YES if the slave should be started (and stopped) by us; GNUNET_NO + * if we are just allowed to use the slave via TCP/IP + */ + int is_subordinate; +}; + + +/** + * handle for host registration + */ +struct GNUNET_TESTBED_HostRegistrationHandle +{ + /** + * The host being registered + */ + struct GNUNET_TESTBED_Host *host; + + /** + * The controller at which this host is being registered + */ + struct GNUNET_TESTBED_Controller *c; + + /** + * The Registartion completion callback + */ + GNUNET_TESTBED_HostRegistrationCompletion cc; + + /** + * The closure for above callback + */ + void *cc_cls; +}; + + +/** + * Context data for forwarded Operation + */ +struct ForwardedOperationData +{ + + /** + * The callback to call when reply is available + */ + GNUNET_CLIENT_MessageHandler cc; + + /** + * The closure for the above callback + */ + void *cc_cls; + +}; + + +/** + * Context data for get slave config operations + */ +struct GetSlaveConfigData +{ + /** + * The id of the slave controller + */ + uint32_t slave_id; + +}; + + +/** + * Context data for controller link operations + */ +struct ControllerLinkData +{ + /** + * The controller link message + */ + struct GNUNET_TESTBED_ControllerLinkMessage *msg; + +}; + + +struct SDEntry +{ + /** + * DLL next pointer + */ + struct SDEntry *next; + + /** + * DLL prev pointer + */ + struct SDEntry *prev; + + /** + * The value to store + */ + unsigned int amount; +}; + + +struct SDHandle +{ + /** + * DLL head for storing entries + */ + struct SDEntry *head; + + /** + * DLL tail for storing entries + */ + struct SDEntry *tail; + + /** + * Squared sum of data values + */ + unsigned long long sqsum; + + /** + * Sum of the data values + */ + unsigned long sum; + + /** + * The average of data amounts + */ + float avg; + + /** + * The variance + */ + double vr; + + /** + * Number of data values; also the length of DLL containing SDEntries + */ + unsigned int cnt; + + /** + * max number of entries we can have in the DLL + */ + unsigned int max_cnt; +}; + + +/** + * This variable is set to the operation that has been last marked as done. It + * is used to verify whether the state associated with an operation is valid + * after the first notify callback is called. Such checks are necessary for + * certain operations where we have 2 notify callbacks. Examples are + * OP_PEER_CREATE, OP_PEER_START/STOP, OP_OVERLAY_CONNECT. + * + * This variable should ONLY be used to compare; it is a dangling pointer!! + */ +static const struct GNUNET_TESTBED_Operation *last_finished_operation; + +/** + * Initialize standard deviation calculation handle + * + * @param max_cnt the maximum number of readings to keep + * @return the initialized handle + */ +static struct SDHandle * +SD_init (unsigned int max_cnt) +{ + struct SDHandle *h; + + GNUNET_assert (1 < max_cnt); + h = GNUNET_malloc (sizeof (struct SDHandle)); + h->max_cnt = max_cnt; + return h; +} + + +/** + * Frees the memory allocated to the SD handle + * + * @param h the SD handle + */ +static void +SD_destroy (struct SDHandle *h) +{ + struct SDEntry *entry; + + while (NULL != (entry = h->head)) + { + GNUNET_CONTAINER_DLL_remove (h->head, h->tail, entry); + GNUNET_free (entry); + } + GNUNET_free (h); +} + + +/** + * Add a reading to SD + * + * @param h the SD handle + * @param amount the reading value + */ +static void +SD_add_data (struct SDHandle *h, unsigned int amount) +{ + struct SDEntry *entry; + double sqavg; + double sqsum_avg; + + entry = NULL; + if (h->cnt == h->max_cnt) + { + entry = h->head; + GNUNET_CONTAINER_DLL_remove (h->head, h->tail, entry); + h->sum -= entry->amount; + h->sqsum -= + ((unsigned long) entry->amount) * ((unsigned long) entry->amount); + h->cnt--; + } + GNUNET_assert (h->cnt < h->max_cnt); + if (NULL == entry) + entry = GNUNET_malloc (sizeof (struct SDEntry)); + entry->amount = amount; + GNUNET_CONTAINER_DLL_insert_tail (h->head, h->tail, entry); + h->sum += amount; + h->cnt++; + h->avg = ((float) h->sum) / ((float) h->cnt); + h->sqsum += ((unsigned long) amount) * ((unsigned long) amount); + sqsum_avg = ((double) h->sqsum) / ((double) h->cnt); + sqavg = ((double) h->avg) * ((double) h->avg); + h->vr = sqsum_avg - sqavg; +} + + +/** + * Returns the factor by which the given amount differs from the standard deviation + * + * @param h the SDhandle + * @param amount the value for which the deviation is returned + + * @return the deviation from the average; GNUNET_SYSERR if the deviation cannot + * be calculated OR 0 if the deviation is less than the average; a + * maximum of 4 is returned for deviations equal to or larger than 4 + */ +static int +SD_deviation_factor (struct SDHandle *h, unsigned int amount) +{ + double diff; + unsigned int n; + + if (h->cnt < 2) + return GNUNET_SYSERR; + if (((float) amount) > h->avg) + diff = ((float) amount) - h->avg; + else + return 0; //diff = h->avg - ((float) amount); + diff *= diff; + for (n = 1; n < 4; n++) + if (diff < (((double) (n * n)) * h->vr)) + break; + return n; +} + + +/** + * Returns the operation context with the given id if found in the Operation + * context queues of the controller + * + * @param c the controller whose queues are searched + * @param id the id which has to be checked + * @return the matching operation context; NULL if no match found + */ +static struct OperationContext * +find_opc (const struct GNUNET_TESTBED_Controller *c, const uint64_t id) +{ + struct OperationContext *opc; + + for (opc = c->ocq_head; NULL != opc; opc = opc->next) + { + if (id == opc->id) + return opc; + } + return NULL; +} + + +/** + * Handler for GNUNET_MESSAGE_TYPE_TESTBED_ADDHOSTCONFIRM message from + * controller (testbed service) + * + * @param c the controller handler + * @param msg message received + * @return GNUNET_YES if we can continue receiving from service; GNUNET_NO if + * not + */ +static int +handle_addhostconfirm (struct GNUNET_TESTBED_Controller *c, + const struct GNUNET_TESTBED_HostConfirmedMessage *msg) +{ + struct GNUNET_TESTBED_HostRegistrationHandle *rh; + char *emsg; + uint16_t msg_size; + + rh = c->rh; + if (NULL == rh) + { + return GNUNET_OK; + } + if (GNUNET_TESTBED_host_get_id_ (rh->host) != ntohl (msg->host_id)) + { + LOG_DEBUG ("Mismatch in host id's %u, %u of host confirm msg\n", + GNUNET_TESTBED_host_get_id_ (rh->host), ntohl (msg->host_id)); + return GNUNET_OK; + } + c->rh = NULL; + msg_size = ntohs (msg->header.size); + if (sizeof (struct GNUNET_TESTBED_HostConfirmedMessage) == msg_size) + { + LOG_DEBUG ("Host %u successfully registered\n", ntohl (msg->host_id)); + GNUNET_TESTBED_mark_host_registered_at_ (rh->host, c); + rh->cc (rh->cc_cls, NULL); + GNUNET_free (rh); + return GNUNET_OK; + } + /* We have an error message */ + emsg = (char *) &msg[1]; + if ('\0' != + emsg[msg_size - sizeof (struct GNUNET_TESTBED_HostConfirmedMessage)]) + { + GNUNET_break (0); + GNUNET_free (rh); + return GNUNET_NO; + } + LOG (GNUNET_ERROR_TYPE_ERROR, _("Adding host %u failed with error: %s\n"), + ntohl (msg->host_id), emsg); + rh->cc (rh->cc_cls, emsg); + GNUNET_free (rh); + return GNUNET_OK; +} + + +/** + * Handler for forwarded operations + * + * @param c the controller handle + * @param opc the opearation context + * @param msg the message + */ +static void +handle_forwarded_operation_msg (struct GNUNET_TESTBED_Controller *c, + struct OperationContext *opc, + const struct GNUNET_MessageHeader *msg) +{ + struct ForwardedOperationData *fo_data; + + fo_data = opc->data; + if (NULL != fo_data->cc) + fo_data->cc (fo_data->cc_cls, msg); + GNUNET_CONTAINER_DLL_remove (c->ocq_head, c->ocq_tail, opc); + GNUNET_free (fo_data); + GNUNET_free (opc); +} + + +/** + * Handler for GNUNET_MESSAGE_TYPE_TESTBED_ADDHOSTCONFIRM message from + * controller (testbed service) + * + * @param c the controller handler + * @param msg message received + * @return GNUNET_YES if we can continue receiving from service; GNUNET_NO if + * not + */ +static int +handle_opsuccess (struct GNUNET_TESTBED_Controller *c, + const struct + GNUNET_TESTBED_GenericOperationSuccessEventMessage *msg) +{ + struct OperationContext *opc; + struct GNUNET_TESTBED_EventInformation event; + uint64_t op_id; + + op_id = GNUNET_ntohll (msg->operation_id); + LOG_DEBUG ("Operation %lu successful\n", op_id); + if (NULL == (opc = find_opc (c, op_id))) + { + LOG_DEBUG ("Operation not found\n"); + return GNUNET_YES; + } + event.type = GNUNET_TESTBED_ET_OPERATION_FINISHED; + event.details.operation_finished.operation = opc->op; + event.details.operation_finished.op_cls = opc->op_cls; + event.details.operation_finished.emsg = NULL; + event.details.operation_finished.generic = NULL; + switch (opc->type) + { + case OP_FORWARDED: + { + handle_forwarded_operation_msg (c, opc, + (const struct GNUNET_MessageHeader *) msg); + return GNUNET_YES; + } + break; + case OP_PEER_DESTROY: + { + struct GNUNET_TESTBED_Peer *peer; + + peer = opc->data; + GNUNET_free (peer); + opc->data = NULL; + //PEERDESTROYDATA + } + break; + case OP_LINK_CONTROLLERS: + { + struct ControllerLinkData *data; + + data = opc->data; + GNUNET_assert (NULL != data); + GNUNET_free (data); + opc->data = NULL; + } + break; + default: + GNUNET_assert (0); + } + GNUNET_CONTAINER_DLL_remove (opc->c->ocq_head, opc->c->ocq_tail, opc); + opc->state = OPC_STATE_FINISHED; + if (0 != (c->event_mask & (1L << GNUNET_TESTBED_ET_OPERATION_FINISHED))) + { + if (NULL != c->cc) + c->cc (c->cc_cls, &event); + } + else + LOG_DEBUG ("Not calling callback\n"); + return GNUNET_YES; +} + + +/** + * Handler for GNUNET_MESSAGE_TYPE_TESTBED_PEERCREATESUCCESS message from + * controller (testbed service) + * + * @param c the controller handle + * @param msg message received + * @return GNUNET_YES if we can continue receiving from service; GNUNET_NO if + * not + */ +static int +handle_peer_create_success (struct GNUNET_TESTBED_Controller *c, + const struct + GNUNET_TESTBED_PeerCreateSuccessEventMessage *msg) +{ + struct OperationContext *opc; + struct PeerCreateData *data; + struct GNUNET_TESTBED_Peer *peer; + GNUNET_TESTBED_PeerCreateCallback cb; + void *cls; + uint64_t op_id; + + GNUNET_assert (sizeof (struct GNUNET_TESTBED_PeerCreateSuccessEventMessage) == + ntohs (msg->header.size)); + op_id = GNUNET_ntohll (msg->operation_id); + if (NULL == (opc = find_opc (c, op_id))) + { + LOG_DEBUG ("Operation context for PeerCreateSuccessEvent not found\n"); + return GNUNET_YES; + } + if (OP_FORWARDED == opc->type) + { + handle_forwarded_operation_msg (c, opc, + (const struct GNUNET_MessageHeader *) msg); + return GNUNET_YES; + } + GNUNET_assert (OP_PEER_CREATE == opc->type); + GNUNET_assert (NULL != opc->data); + data = opc->data; + GNUNET_assert (NULL != data->peer); + peer = data->peer; + GNUNET_assert (peer->unique_id == ntohl (msg->peer_id)); + peer->state = PS_CREATED; + cb = data->cb; + cls = data->cls; + GNUNET_free (opc->data); + GNUNET_CONTAINER_DLL_remove (opc->c->ocq_head, opc->c->ocq_tail, opc); + opc->state = OPC_STATE_FINISHED; + if (NULL != cb) + cb (cls, peer, NULL); + return GNUNET_YES; +} + + +/** + * Handler for GNUNET_MESSAGE_TYPE_TESTBED_PEEREVENT message from + * controller (testbed service) + * + * @param c the controller handler + * @param msg message received + * @return GNUNET_YES if we can continue receiving from service; GNUNET_NO if + * not + */ +static int +handle_peer_event (struct GNUNET_TESTBED_Controller *c, + const struct GNUNET_TESTBED_PeerEventMessage *msg) +{ + struct OperationContext *opc; + struct GNUNET_TESTBED_Peer *peer; + struct PeerEventData *data; + GNUNET_TESTBED_PeerChurnCallback pcc; + void *pcc_cls; + struct GNUNET_TESTBED_EventInformation event; + uint64_t op_id; + + GNUNET_assert (sizeof (struct GNUNET_TESTBED_PeerEventMessage) == + ntohs (msg->header.size)); + op_id = GNUNET_ntohll (msg->operation_id); + if (NULL == (opc = find_opc (c, op_id))) + { + LOG_DEBUG ("Operation not found\n"); + return GNUNET_YES; + } + if (OP_FORWARDED == opc->type) + { + handle_forwarded_operation_msg (c, opc, + (const struct GNUNET_MessageHeader *) msg); + return GNUNET_YES; + } + GNUNET_assert ((OP_PEER_START == opc->type) || (OP_PEER_STOP == opc->type)); + data = opc->data; + GNUNET_assert (NULL != data); + peer = data->peer; + GNUNET_assert (NULL != peer); + event.type = (enum GNUNET_TESTBED_EventType) ntohl (msg->event_type); + switch (event.type) + { + case GNUNET_TESTBED_ET_PEER_START: + peer->state = PS_STARTED; + event.details.peer_start.host = peer->host; + event.details.peer_start.peer = peer; + break; + case GNUNET_TESTBED_ET_PEER_STOP: + peer->state = PS_STOPPED; + event.details.peer_stop.peer = peer; + break; + default: + GNUNET_assert (0); /* We should never reach this state */ + } + pcc = data->pcc; + pcc_cls = data->pcc_cls; + GNUNET_free (data); + GNUNET_CONTAINER_DLL_remove (opc->c->ocq_head, opc->c->ocq_tail, opc); + opc->state = OPC_STATE_FINISHED; + if (0 != + ((GNUNET_TESTBED_ET_PEER_START | GNUNET_TESTBED_ET_PEER_STOP) & + c->event_mask)) + { + if (NULL != c->cc) + c->cc (c->cc_cls, &event); + } + if (NULL != pcc) + pcc (pcc_cls, NULL); + return GNUNET_YES; +} + + +/** + * Handler for GNUNET_MESSAGE_TYPE_TESTBED_PEERCONEVENT message from + * controller (testbed service) + * + * @param c the controller handler + * @param msg message received + * @return GNUNET_YES if we can continue receiving from service; GNUNET_NO if + * not + */ +static int +handle_peer_conevent (struct GNUNET_TESTBED_Controller *c, + const struct GNUNET_TESTBED_ConnectionEventMessage *msg) +{ + struct OperationContext *opc; + struct OverlayConnectData *data; + GNUNET_TESTBED_OperationCompletionCallback cb; + void *cb_cls; + struct GNUNET_TESTBED_EventInformation event; + uint64_t op_id; + + op_id = GNUNET_ntohll (msg->operation_id); + if (NULL == (opc = find_opc (c, op_id))) + { + LOG_DEBUG ("Operation not found\n"); + return GNUNET_YES; + } + if (OP_FORWARDED == opc->type) + { + handle_forwarded_operation_msg (c, opc, + (const struct GNUNET_MessageHeader *) msg); + return GNUNET_YES; + } + GNUNET_assert (OP_OVERLAY_CONNECT == opc->type); + data = opc->data; + GNUNET_assert (NULL != data); + GNUNET_assert ((ntohl (msg->peer1) == data->p1->unique_id) && + (ntohl (msg->peer2) == data->p2->unique_id)); + event.type = (enum GNUNET_TESTBED_EventType) ntohl (msg->event_type); + switch (event.type) + { + case GNUNET_TESTBED_ET_CONNECT: + event.details.peer_connect.peer1 = data->p1; + event.details.peer_connect.peer2 = data->p2; + break; + case GNUNET_TESTBED_ET_DISCONNECT: + GNUNET_assert (0); /* FIXME: implement */ + break; + default: + GNUNET_assert (0); /* Should never reach here */ + break; + } + cb = data->cb; + cb_cls = data->cb_cls; + GNUNET_CONTAINER_DLL_remove (opc->c->ocq_head, opc->c->ocq_tail, opc); + opc->state = OPC_STATE_FINISHED; + if (NULL != cb) + cb (cb_cls, opc->op, NULL); + if (0 != + ((GNUNET_TESTBED_ET_CONNECT | GNUNET_TESTBED_ET_DISCONNECT) & + c->event_mask)) + { + if (NULL != c->cc) + c->cc (c->cc_cls, &event); + } + return GNUNET_YES; +} + + +/** + * Handler for GNUNET_MESSAGE_TYPE_TESTBED_PEERCONFIG message from + * controller (testbed service) + * + * @param c the controller handler + * @param msg message received + * @return GNUNET_YES if we can continue receiving from service; GNUNET_NO if + * not + */ +static int +handle_peer_config (struct GNUNET_TESTBED_Controller *c, + const struct + GNUNET_TESTBED_PeerConfigurationInformationMessage *msg) +{ + struct OperationContext *opc; + struct GNUNET_TESTBED_Peer *peer; + struct PeerInfoData *data; + struct GNUNET_TESTBED_PeerInformation *pinfo; + GNUNET_TESTBED_PeerInfoCallback cb; + void *cb_cls; + uint64_t op_id; + + op_id = GNUNET_ntohll (msg->operation_id); + if (NULL == (opc = find_opc (c, op_id))) + { + LOG_DEBUG ("Operation not found\n"); + return GNUNET_YES; + } + if (OP_FORWARDED == opc->type) + { + handle_forwarded_operation_msg (c, opc, + (const struct GNUNET_MessageHeader *) msg); + return GNUNET_YES; + } + data = opc->data; + GNUNET_assert (NULL != data); + peer = data->peer; + GNUNET_assert (NULL != peer); + GNUNET_assert (ntohl (msg->peer_id) == peer->unique_id); + pinfo = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_PeerInformation)); + pinfo->pit = data->pit; + cb = data->cb; + cb_cls = data->cb_cls; + GNUNET_free (data); + opc->data = NULL; + switch (pinfo->pit) + { + case GNUNET_TESTBED_PIT_IDENTITY: + pinfo->result.id = GNUNET_malloc (sizeof (struct GNUNET_PeerIdentity)); + (void) memcpy (pinfo->result.id, &msg->peer_identity, + sizeof (struct GNUNET_PeerIdentity)); + break; + case GNUNET_TESTBED_PIT_CONFIGURATION: + pinfo->result.cfg = /* Freed in oprelease_peer_getinfo */ + GNUNET_TESTBED_extract_config_ (&msg->header); + break; + case GNUNET_TESTBED_PIT_GENERIC: + GNUNET_assert (0); /* never reach here */ + break; + } + opc->data = pinfo; + GNUNET_CONTAINER_DLL_remove (opc->c->ocq_head, opc->c->ocq_tail, opc); + opc->state = OPC_STATE_FINISHED; + if (NULL != cb) + cb (cb_cls, opc->op, pinfo, NULL); + return GNUNET_YES; +} + + +/** + * Handler for GNUNET_MESSAGE_TYPE_TESTBED_OPERATIONFAILEVENT message from + * controller (testbed service) + * + * @param c the controller handler + * @param msg message received + * @return GNUNET_YES if we can continue receiving from service; GNUNET_NO if + * not + */ +static int +handle_op_fail_event (struct GNUNET_TESTBED_Controller *c, + const struct GNUNET_TESTBED_OperationFailureEventMessage + *msg) +{ + struct OperationContext *opc; + const char *emsg; + uint64_t op_id; + struct GNUNET_TESTBED_EventInformation event; + + op_id = GNUNET_ntohll (msg->operation_id); + if (NULL == (opc = find_opc (c, op_id))) + { + LOG_DEBUG ("Operation not found\n"); + return GNUNET_YES; + } + if (OP_FORWARDED == opc->type) + { + handle_forwarded_operation_msg (c, opc, + (const struct GNUNET_MessageHeader *) msg); + return GNUNET_YES; + } + GNUNET_CONTAINER_DLL_remove (opc->c->ocq_head, opc->c->ocq_tail, opc); + opc->state = OPC_STATE_FINISHED; + emsg = GNUNET_TESTBED_parse_error_string_ (msg); + if (NULL == emsg) + emsg = "Unknown error"; + if (OP_PEER_INFO == opc->type) + { + struct PeerInfoData *data; + + data = opc->data; + if (NULL != data->cb) + data->cb (data->cb_cls, opc->op, NULL, emsg); + GNUNET_free (data); + return GNUNET_YES; /* We do not call controller callback for peer info */ + } + if ((0 != (GNUNET_TESTBED_ET_OPERATION_FINISHED & c->event_mask)) && + (NULL != c->cc)) + { + event.type = GNUNET_TESTBED_ET_OPERATION_FINISHED; + event.details.operation_finished.operation = opc->op; + event.details.operation_finished.op_cls = opc->op_cls; + event.details.operation_finished.emsg = emsg; + event.details.operation_finished.generic = NULL; + c->cc (c->cc_cls, &event); + if (event.details.operation_finished.operation == last_finished_operation) + return GNUNET_YES; + } + switch (opc->type) + { + case OP_PEER_CREATE: + { + struct PeerCreateData *data; + + data = opc->data; + GNUNET_free (data->peer); + if (NULL != data->cb) + data->cb (data->cls, NULL, emsg); + GNUNET_free (data); + } + break; + case OP_PEER_START: + case OP_PEER_STOP: + { + struct PeerEventData *data; + + data = opc->data; + if (NULL != data->pcc) + data->pcc (data->pcc_cls, emsg); + GNUNET_free (data); + } + break; + case OP_PEER_DESTROY: + break; + case OP_PEER_INFO: + GNUNET_assert (0); + case OP_OVERLAY_CONNECT: + { + struct OverlayConnectData *data; + + data = opc->data; + data->failed = GNUNET_YES; + if (NULL != data->cb) + data->cb (data->cb_cls, opc->op, emsg); + } + break; + case OP_FORWARDED: + GNUNET_assert (0); + case OP_LINK_CONTROLLERS: /* No secondary callback */ + break; + default: + GNUNET_break (0); + } + return GNUNET_YES; +} + + +/** + * Function to build GET_SLAVE_CONFIG message + * + * @param op_id the id this message should contain in its operation id field + * @param slave_id the id this message should contain in its slave id field + * @return newly allocated SlaveGetConfigurationMessage + */ +static struct GNUNET_TESTBED_SlaveGetConfigurationMessage * +GNUNET_TESTBED_generate_slavegetconfig_msg_ (uint64_t op_id, uint32_t slave_id) +{ + struct GNUNET_TESTBED_SlaveGetConfigurationMessage *msg; + uint16_t msize; + + msize = sizeof (struct GNUNET_TESTBED_SlaveGetConfigurationMessage); + msg = GNUNET_malloc (msize); + msg->header.size = htons (msize); + msg->header.type = + htons (GNUNET_MESSAGE_TYPE_TESTBED_GET_SLAVE_CONFIGURATION); + msg->operation_id = GNUNET_htonll (op_id); + msg->slave_id = htonl (slave_id); + return msg; +} + + +/** + * Handler for GNUNET_MESSAGE_TYPE_TESTBED_SLAVECONFIG message from controller + * (testbed service) + * + * @param c the controller handler + * @param msg message received + * @return GNUNET_YES if we can continue receiving from service; GNUNET_NO if + * not + */ +static int +handle_slave_config (struct GNUNET_TESTBED_Controller *c, + const struct GNUNET_TESTBED_SlaveConfiguration *msg) +{ + struct OperationContext *opc; + uint64_t op_id; + struct GNUNET_TESTBED_EventInformation event; + + op_id = GNUNET_ntohll (msg->operation_id); + if (NULL == (opc = find_opc (c, op_id))) + { + LOG_DEBUG ("Operation not found\n"); + return GNUNET_YES; + } + if (OP_GET_SLAVE_CONFIG != opc->type) + { + GNUNET_break (0); + return GNUNET_YES; + } + GNUNET_free (opc->data); + opc->data = NULL; + opc->state = OPC_STATE_FINISHED; + GNUNET_CONTAINER_DLL_remove (opc->c->ocq_head, opc->c->ocq_tail, opc); + if ((0 != (GNUNET_TESTBED_ET_OPERATION_FINISHED & c->event_mask)) && + (NULL != c->cc)) + { + opc->data = GNUNET_TESTBED_extract_config_ (&msg->header); + event.type = GNUNET_TESTBED_ET_OPERATION_FINISHED; + event.details.operation_finished.generic = opc->data; + event.details.operation_finished.operation = opc->op; + event.details.operation_finished.op_cls = opc->op_cls; + event.details.operation_finished.emsg = NULL; + c->cc (c->cc_cls, &event); + } + return GNUNET_YES; +} + + +/** + * Handler for messages from controller (testbed service) + * + * @param cls the controller handler + * @param msg message received, NULL on timeout or fatal error + */ +static void +message_handler (void *cls, const struct GNUNET_MessageHeader *msg) +{ + struct GNUNET_TESTBED_Controller *c = cls; + int status; + uint16_t msize; + + c->in_receive = GNUNET_NO; + /* FIXME: Add checks for message integrity */ + if (NULL == msg) + { + LOG_DEBUG ("Receive timed out or connection to service dropped\n"); + return; + } + status = GNUNET_OK; + msize = ntohs (msg->size); + switch (ntohs (msg->type)) + { + case GNUNET_MESSAGE_TYPE_TESTBED_ADD_HOST_SUCCESS: + GNUNET_assert (msize >= + sizeof (struct GNUNET_TESTBED_HostConfirmedMessage)); + status = + handle_addhostconfirm (c, + (const struct GNUNET_TESTBED_HostConfirmedMessage + *) msg); + break; + case GNUNET_MESSAGE_TYPE_TESTBED_GENERIC_OPERATION_SUCCESS: + GNUNET_assert (msize == + sizeof (struct + GNUNET_TESTBED_GenericOperationSuccessEventMessage)); + status = + handle_opsuccess (c, + (const struct + GNUNET_TESTBED_GenericOperationSuccessEventMessage *) + msg); + break; + case GNUNET_MESSAGE_TYPE_TESTBED_CREATE_PEER_SUCCESS: + GNUNET_assert (msize == + sizeof (struct + GNUNET_TESTBED_PeerCreateSuccessEventMessage)); + status = + handle_peer_create_success (c, + (const struct + GNUNET_TESTBED_PeerCreateSuccessEventMessage + *) msg); + break; + case GNUNET_MESSAGE_TYPE_TESTBED_PEER_EVENT: + GNUNET_assert (msize == sizeof (struct GNUNET_TESTBED_PeerEventMessage)); + status = + handle_peer_event (c, + (const struct GNUNET_TESTBED_PeerEventMessage *) + msg); + + break; + case GNUNET_MESSAGE_TYPE_TESTBED_PEER_CONFIGURATION: + GNUNET_assert (msize >= + sizeof (struct + GNUNET_TESTBED_PeerConfigurationInformationMessage)); + status = + handle_peer_config (c, + (const struct + GNUNET_TESTBED_PeerConfigurationInformationMessage + *) msg); + break; + case GNUNET_MESSAGE_TYPE_TESTBED_PEER_CONNECT_EVENT: + GNUNET_assert (msize == + sizeof (struct GNUNET_TESTBED_ConnectionEventMessage)); + status = + handle_peer_conevent (c, + (const struct + GNUNET_TESTBED_ConnectionEventMessage *) msg); + break; + case GNUNET_MESSAGE_TYPE_TESTBED_OPERATION_FAIL_EVENT: + GNUNET_assert (msize >= + sizeof (struct GNUNET_TESTBED_OperationFailureEventMessage)); + status = + handle_op_fail_event (c, + (const struct + GNUNET_TESTBED_OperationFailureEventMessage *) + msg); + break; + case GNUNET_MESSAGE_TYPE_TESTBED_SLAVE_CONFIGURATION: + GNUNET_assert (msize > sizeof (struct GNUNET_TESTBED_SlaveConfiguration)); + status = + handle_slave_config (c, + (const struct GNUNET_TESTBED_SlaveConfiguration *) + msg); + break; + default: + GNUNET_assert (0); + } + if ((GNUNET_OK == status) && (GNUNET_NO == c->in_receive)) + { + c->in_receive = GNUNET_YES; + GNUNET_CLIENT_receive (c->client, &message_handler, c, + GNUNET_TIME_UNIT_FOREVER_REL); + } +} + + +/** + * Function called to notify a client about the connection begin ready to queue + * more data. "buf" will be NULL and "size" zero if the connection was closed + * for writing in the meantime. + * + * @param cls closure + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * @return number of bytes written to buf + */ +static size_t +transmit_ready_notify (void *cls, size_t size, void *buf) +{ + struct GNUNET_TESTBED_Controller *c = cls; + struct MessageQueue *mq_entry; + + c->th = NULL; + mq_entry = c->mq_head; + GNUNET_assert (NULL != mq_entry); + if ((0 == size) && (NULL == buf)) /* Timeout */ + { + LOG_DEBUG ("Message sending timed out -- retrying\n"); + c->th = + GNUNET_CLIENT_notify_transmit_ready (c->client, + ntohs (mq_entry->msg->size), + TIMEOUT_REL, GNUNET_YES, + &transmit_ready_notify, c); + return 0; + } + GNUNET_assert (ntohs (mq_entry->msg->size) <= size); + size = ntohs (mq_entry->msg->size); + memcpy (buf, mq_entry->msg, size); + LOG_DEBUG ("Message of type: %u and size: %u sent\n", + ntohs (mq_entry->msg->type), size); + GNUNET_free (mq_entry->msg); + GNUNET_CONTAINER_DLL_remove (c->mq_head, c->mq_tail, mq_entry); + GNUNET_free (mq_entry); + mq_entry = c->mq_head; + if (NULL != mq_entry) + c->th = + GNUNET_CLIENT_notify_transmit_ready (c->client, + ntohs (mq_entry->msg->size), + TIMEOUT_REL, GNUNET_YES, + &transmit_ready_notify, c); + if (GNUNET_NO == c->in_receive) + { + c->in_receive = GNUNET_YES; + GNUNET_CLIENT_receive (c->client, &message_handler, c, + GNUNET_TIME_UNIT_FOREVER_REL); + } + return size; +} + + +/** + * Queues a message in send queue for sending to the service + * + * @param controller the handle to the controller + * @param msg the message to queue + */ +void +GNUNET_TESTBED_queue_message_ (struct GNUNET_TESTBED_Controller *controller, + struct GNUNET_MessageHeader *msg) +{ + struct MessageQueue *mq_entry; + uint16_t type; + uint16_t size; + + type = ntohs (msg->type); + size = ntohs (msg->size); + GNUNET_assert ((GNUNET_MESSAGE_TYPE_TESTBED_INIT <= type) && + (GNUNET_MESSAGE_TYPE_TESTBED_MAX > type)); + mq_entry = GNUNET_malloc (sizeof (struct MessageQueue)); + mq_entry->msg = msg; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Queueing message of type %u, size %u for sending\n", type, + ntohs (msg->size)); + GNUNET_CONTAINER_DLL_insert_tail (controller->mq_head, controller->mq_tail, + mq_entry); + if (NULL == controller->th) + controller->th = + GNUNET_CLIENT_notify_transmit_ready (controller->client, size, + TIMEOUT_REL, GNUNET_YES, + &transmit_ready_notify, + controller); +} + + +/** + * Sends the given message as an operation. The given callback is called when a + * reply for the operation is available. Call + * GNUNET_TESTBED_forward_operation_msg_cancel_() to cleanup the returned + * operation context if the cc hasn't been called + * + * @param controller the controller to which the message has to be sent + * @param operation_id the operation id of the message + * @param msg the message to send + * @param cc the callback to call when reply is available + * @param cc_cls the closure for the above callback + * @return the operation context which can be used to cancel the forwarded + * operation + */ +struct OperationContext * +GNUNET_TESTBED_forward_operation_msg_ (struct GNUNET_TESTBED_Controller + *controller, uint64_t operation_id, + const struct GNUNET_MessageHeader *msg, + GNUNET_CLIENT_MessageHandler cc, + void *cc_cls) +{ + struct OperationContext *opc; + struct ForwardedOperationData *data; + struct GNUNET_MessageHeader *dup_msg; + uint16_t msize; + + data = GNUNET_malloc (sizeof (struct ForwardedOperationData)); + data->cc = cc; + data->cc_cls = cc_cls; + opc = GNUNET_malloc (sizeof (struct OperationContext)); + opc->c = controller; + opc->type = OP_FORWARDED; + opc->data = data; + opc->id = operation_id; + msize = ntohs (msg->size); + dup_msg = GNUNET_malloc (msize); + (void) memcpy (dup_msg, msg, msize); + GNUNET_TESTBED_queue_message_ (opc->c, dup_msg); + GNUNET_CONTAINER_DLL_insert_tail (controller->ocq_head, controller->ocq_tail, + opc); + return opc; +} + + +/** + * Function to cancel an operation created by simply forwarding an operation + * message. + * + * @param opc the operation context from GNUNET_TESTBED_forward_operation_msg_() + */ +void +GNUNET_TESTBED_forward_operation_msg_cancel_ (struct OperationContext *opc) +{ + GNUNET_CONTAINER_DLL_remove (opc->c->ocq_head, opc->c->ocq_tail, opc); + GNUNET_free (opc->data); + GNUNET_free (opc); +} + + +/** + * Functions with this signature are called whenever a + * complete message is received by the tokenizer. + * + * Do not call GNUNET_SERVER_mst_destroy in callback + * + * @param cls closure + * @param client identification of the client + * @param message the actual message + * + * @return GNUNET_OK on success, GNUNET_SYSERR to stop further processing + */ +static int +helper_mst (void *cls, void *client, const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_TESTBED_ControllerProc *cp = cls; + const struct GNUNET_TESTBED_HelperReply *msg; + const char *hostname; + char *config; + uLongf config_size; + uLongf xconfig_size; + + msg = (const struct GNUNET_TESTBED_HelperReply *) message; + GNUNET_assert (sizeof (struct GNUNET_TESTBED_HelperReply) < + ntohs (msg->header.size)); + GNUNET_assert (GNUNET_MESSAGE_TYPE_TESTBED_HELPER_REPLY == + ntohs (msg->header.type)); + config_size = (uLongf) ntohs (msg->config_size); + xconfig_size = + (uLongf) (ntohs (msg->header.size) - + sizeof (struct GNUNET_TESTBED_HelperReply)); + config = GNUNET_malloc (config_size); + GNUNET_assert (Z_OK == + uncompress ((Bytef *) config, &config_size, + (const Bytef *) &msg[1], xconfig_size)); + GNUNET_assert (NULL == cp->cfg); + cp->cfg = GNUNET_CONFIGURATION_create (); + GNUNET_assert (GNUNET_CONFIGURATION_deserialize + (cp->cfg, config, config_size, GNUNET_NO)); + GNUNET_free (config); + if ((NULL == cp->host) || + (NULL == (hostname = GNUNET_TESTBED_host_get_hostname (cp->host)))) + hostname = "localhost"; + /* Change the hostname so that we can connect to it */ + GNUNET_CONFIGURATION_set_value_string (cp->cfg, "testbed", "hostname", + hostname); + cp->cb (cp->cls, cp->cfg, GNUNET_OK); + return GNUNET_OK; +} + +/** + * 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 GNUNET_TESTBED_ControllerProc *cp = cls; + + GNUNET_assert (NULL != cp->shandle); + cp->shandle = NULL; + GNUNET_free (cp->msg); +} + + +/** + * Callback that will be called when the helper process dies. This is not called + * when the helper process is stoped using GNUNET_HELPER_stop() + * + * @param cls the closure from GNUNET_HELPER_start() + */ +static void +helper_exp_cb (void *cls) +{ + struct GNUNET_TESTBED_ControllerProc *cp = cls; + GNUNET_TESTBED_ControllerStatusCallback cb; + void *cb_cls; + cb = cp->cb; + cb_cls = cp->cls; + cp->helper = NULL; + GNUNET_TESTBED_controller_stop (cp); + if (NULL != cb) + cb (cb_cls, NULL, GNUNET_SYSERR); +} + + +/** + * Function to call to start a link-controllers type operation once all queues + * the operation is part of declare that the operation can be activated. + * + * @param cls the closure from GNUNET_TESTBED_operation_create_() + */ +static void +opstart_link_controllers (void *cls) +{ + struct OperationContext *opc = cls; + struct ControllerLinkData *data; + struct GNUNET_TESTBED_ControllerLinkMessage *msg; + + GNUNET_assert (NULL != opc->data); + data = opc->data; + msg = data->msg; + data->msg = NULL; + opc->state = OPC_STATE_STARTED; + GNUNET_CONTAINER_DLL_insert_tail (opc->c->ocq_head, opc->c->ocq_tail, opc); + GNUNET_TESTBED_queue_message_ (opc->c, &msg->header); +} + + +/** + * Callback which will be called when link-controllers type operation is released + * + * @param cls the closure from GNUNET_TESTBED_operation_create_() + */ +static void +oprelease_link_controllers (void *cls) +{ + struct OperationContext *opc = cls; + struct ControllerLinkData *data; + + data = opc->data; + switch (opc->state) + { + case OPC_STATE_INIT: + GNUNET_free (data->msg); + break; + case OPC_STATE_STARTED: + GNUNET_CONTAINER_DLL_remove (opc->c->ocq_head, opc->c->ocq_tail, opc); + break; + case OPC_STATE_FINISHED: + break; + } + GNUNET_free_non_null (data); + GNUNET_free (opc); +} + + +/** + * Function to be called when get slave config operation is ready + * + * @param cls the OperationContext of type OP_GET_SLAVE_CONFIG + */ +static void +opstart_get_slave_config (void *cls) +{ + struct OperationContext *opc = cls; + struct GetSlaveConfigData *data; + struct GNUNET_TESTBED_SlaveGetConfigurationMessage *msg; + + data = opc->data; + msg = GNUNET_TESTBED_generate_slavegetconfig_msg_ (opc->id, data->slave_id); + GNUNET_CONTAINER_DLL_insert_tail (opc->c->ocq_head, opc->c->ocq_tail, opc); + GNUNET_TESTBED_queue_message_ (opc->c, &msg->header); + opc->state = OPC_STATE_STARTED; +} + + +/** + * Function to be called when get slave config operation is cancelled or finished + * + * @param cls the OperationContext of type OP_GET_SLAVE_CONFIG + */ +static void +oprelease_get_slave_config (void *cls) +{ + struct OperationContext *opc = cls; + + switch (opc->state) + { + case OPC_STATE_INIT: + GNUNET_free (opc->data); + break; + case OPC_STATE_STARTED: + GNUNET_free (opc->data); + GNUNET_CONTAINER_DLL_remove (opc->c->ocq_head, opc->c->ocq_tail, opc); + break; + case OPC_STATE_FINISHED: + if (NULL != opc->data) + GNUNET_CONFIGURATION_destroy (opc->data); + break; + } + GNUNET_free (opc); +} + + +/** + * Initializes the operation queue for parallel overlay connects + * + * @param c the controller handle + * @param npoc the number of parallel overlay connects - the queue size + */ +static void +GNUNET_TESTBED_set_num_parallel_overlay_connects_ (struct + GNUNET_TESTBED_Controller *c, + unsigned int npoc) +{ + fprintf (stderr, "%d", npoc); + GNUNET_free_non_null (c->tslots); + c->tslots_filled = 0; + c->num_parallel_connects = npoc; + c->tslots = GNUNET_malloc (npoc * sizeof (struct TimeSlot)); + GNUNET_TESTBED_operation_queue_reset_max_active_ + (c->opq_parallel_overlay_connect_operations, npoc); +} + + +/** + * Function to copy NULL terminated list of arguments + * + * @param argv the NULL terminated list of arguments. Cannot be NULL. + * @return the copied NULL terminated arguments + */ +static char ** +copy_argv (const char *const *argv) +{ + char **argv_dup; + unsigned int argp; + + GNUNET_assert (NULL != argv); + for (argp = 0; NULL != argv[argp]; argp++) ; + argv_dup = GNUNET_malloc (sizeof (char *) * (argp + 1)); + for (argp = 0; NULL != argv[argp]; argp++) + argv_dup[argp] = strdup (argv[argp]); + return argv_dup; +} + + +/** + * 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; + 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; +} + + +/** + * Frees the given NULL terminated arguments + * + * @param argv the NULL terminated list of arguments + */ +static void +free_argv (char **argv) +{ + unsigned int argp; + + for (argp = 0; NULL != argv[argp]; argp++) + GNUNET_free (argv[argp]); + GNUNET_free (argv); +} + + +/** + * Generates arguments for opening a remote shell. Builds up the arguments + * from the environment variable GNUNET_TESTBED_RSH_CMD. The variable + * should not mention `-p' (port) option and destination address as these will + * be set locally in the function from its parameteres. If the environmental + * variable is not found then it defaults to `ssh -o BatchMode=yes -o + * NoHostAuthenticationForLocalhost=yes' + * + * @param port the destination port number + * @param dst the destination address + * @return NULL terminated list of arguments + */ +static char ** +gen_rsh_args (const char *port, const char *dst) +{ + static const char *default_ssh_args[] = { + "ssh", + "-o", + "BatchMode=yes", + "-o", + "NoHostAuthenticationForLocalhost=yes", + NULL + }; + char **ssh_args; + char *ssh_cmd; + char *ssh_cmd_cp; + char *arg; + unsigned int cnt; + + ssh_args = NULL; + if (NULL != (ssh_cmd = getenv ("GNUNET_TESTBED_RSH_CMD"))) + { + ssh_cmd = GNUNET_strdup (ssh_cmd); + ssh_cmd_cp = ssh_cmd; + for (cnt = 0; NULL != (arg = strtok (ssh_cmd, " ")); ssh_cmd = NULL) + GNUNET_array_append (ssh_args, cnt, GNUNET_strdup (arg)); + GNUNET_free (ssh_cmd_cp); + } + else + { + ssh_args = copy_argv (default_ssh_args); + cnt = (sizeof (default_ssh_args)) / (sizeof (const char *)); + GNUNET_array_grow (ssh_args, cnt, cnt - 1); + } + GNUNET_array_append (ssh_args, cnt, GNUNET_strdup ("-p")); + GNUNET_array_append (ssh_args, cnt, GNUNET_strdup (port)); + GNUNET_array_append (ssh_args, cnt, GNUNET_strdup (dst)); + GNUNET_array_append (ssh_args, cnt, NULL); + return ssh_args; +} + + +/** + * Generates the arguments needed for executing the given binary in a remote + * shell. Builds the arguments from the environmental variable + * GNUNET_TETSBED_RSH_CMD_SUFFIX. If the environmental variable is not found, + * only the given binary name will be present in the returned arguments + * + * @param helper_binary_path the path of the binary to execute + * @return NULL-terminated args + */ +static char ** +gen_rsh_suffix_args (const char *helper_binary_path) +{ + char **rshell_args; + char *rshell_cmd; + char *rshell_cmd_cp; + char *arg; + unsigned int cnt; + + rshell_args = NULL; + cnt = 0; + if (NULL != (rshell_cmd = getenv ("GNUNET_TESTBED_RSH_CMD_SUFFIX"))) + { + rshell_cmd = GNUNET_strdup (rshell_cmd); + rshell_cmd_cp = rshell_cmd; + for (; NULL != (arg = strtok (rshell_cmd, " ")); rshell_cmd = NULL) + GNUNET_array_append (rshell_args, cnt, GNUNET_strdup (arg)); + GNUNET_free (rshell_cmd_cp); + } + GNUNET_array_append (rshell_args, cnt, GNUNET_strdup (helper_binary_path)); + GNUNET_array_append (rshell_args, cnt, NULL); + return rshell_args; +} + + +/** + * Starts a controller process at the given host + * + * @param trusted_ip the ip address of the controller which will be set as TRUSTED + * HOST(all connections form this ip are permitted by the testbed) when + * starting testbed controller at host. This can either be a single ip + * address or a network address in CIDR notation. + * @param host the host where the controller has to be started; NULL for + * localhost + * @param cfg template configuration to use for the remote controller; the + * remote controller will be started with a slightly modified + * configuration (port numbers, unix domain sockets and service home + * values are changed as per TESTING library on the remote host) + * @param cb function called when the controller is successfully started or + * dies unexpectedly; GNUNET_TESTBED_controller_stop shouldn't be + * called if cb is called with GNUNET_SYSERR as status. Will never be + * called in the same task as 'GNUNET_TESTBED_controller_start' + * (synchronous errors will be signalled by returning NULL). This + * parameter cannot be NULL. + * @param cls closure for above callbacks + * @return the controller process handle, NULL on errors + */ +struct GNUNET_TESTBED_ControllerProc * +GNUNET_TESTBED_controller_start (const char *trusted_ip, + struct GNUNET_TESTBED_Host *host, + const struct GNUNET_CONFIGURATION_Handle *cfg, + GNUNET_TESTBED_ControllerStatusCallback cb, + void *cls) +{ + struct GNUNET_TESTBED_ControllerProc *cp; + struct GNUNET_TESTBED_HelperInit *msg; + const char *hostname; + + static char *const binary_argv[] = { + HELPER_TESTBED_BINARY, NULL + }; + + hostname = NULL; + cp = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_ControllerProc)); + if ((NULL == host) || (0 == GNUNET_TESTBED_host_get_id_ (host))) + { + cp->helper = + GNUNET_HELPER_start (GNUNET_YES, HELPER_TESTBED_BINARY, binary_argv, + &helper_mst, &helper_exp_cb, cp); + } + else + { + char *helper_binary_path; + char **ssh_args; + char **rshell_args; + const char *username; + char *port; + char *dst; + + username = GNUNET_TESTBED_host_get_username_ (host); + hostname = GNUNET_TESTBED_host_get_hostname (host); + GNUNET_asprintf (&port, "%u", GNUNET_TESTBED_host_get_ssh_port_ (host)); + if (NULL == username) + GNUNET_asprintf (&dst, "%s", hostname); + else + GNUNET_asprintf (&dst, "%s@%s", username, hostname); + LOG_DEBUG ("Starting SSH to destination %s\n", dst); + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, "testbed", + "HELPER_BINARY_PATH", + &helper_binary_path)) + helper_binary_path = + GNUNET_OS_get_libexec_binary_path (HELPER_TESTBED_BINARY); + ssh_args = gen_rsh_args (port, dst); + rshell_args = gen_rsh_suffix_args (helper_binary_path); + cp->helper_argv = + join_argv ((const char **) ssh_args, (const char **) rshell_args); + free_argv (ssh_args); + free_argv (rshell_args); + GNUNET_free (port); + GNUNET_free (dst); + cp->helper = + GNUNET_HELPER_start (GNUNET_NO, "ssh", cp->helper_argv, &helper_mst, + &helper_exp_cb, cp); + GNUNET_free (helper_binary_path); + } + if (NULL == cp->helper) + { + if (NULL != cp->helper_argv) + free_argv (cp->helper_argv); + GNUNET_free (cp); + return NULL; + } + cp->host = host; + cp->cb = cb; + cp->cls = cls; + msg = GNUNET_TESTBED_create_helper_init_msg_ (trusted_ip, hostname, cfg); + cp->msg = &msg->header; + cp->shandle = + GNUNET_HELPER_send (cp->helper, &msg->header, GNUNET_NO, &clear_msg, cp); + if (NULL == cp->shandle) + { + GNUNET_free (msg); + GNUNET_TESTBED_controller_stop (cp); + return NULL; + } + return cp; +} + + +/** + * Stop the controller process (also will terminate all peers and controllers + * dependent on this controller). This function blocks until the testbed has + * been fully terminated (!). The controller status cb from + * GNUNET_TESTBED_controller_start() will not be called. + * + * @param cproc the controller process handle + */ +void +GNUNET_TESTBED_controller_stop (struct GNUNET_TESTBED_ControllerProc *cproc) +{ + if (NULL != cproc->shandle) + GNUNET_HELPER_send_cancel (cproc->shandle); + if (NULL != cproc->helper) + GNUNET_HELPER_stop (cproc->helper); + if (NULL != cproc->cfg) + GNUNET_CONFIGURATION_destroy (cproc->cfg); + if (NULL != cproc->helper_argv) + free_argv (cproc->helper_argv); + GNUNET_free (cproc); +} /** @@ -40,7 +1797,9 @@ * given host. * * @param cfg configuration to use - * @param host host to run the controller on, NULL for 'localhost' + * @param host host to run the controller on; This should be the same host if + * the controller was previously started with + * GNUNET_TESTBED_controller_start; NULL for localhost * @param event_mask bit mask with set of events to call 'cc' for; * or-ed values of "1LL" shifted by the * respective 'enum GNUNET_TESTBED_EventType' @@ -50,14 +1809,101 @@ * @return handle to the controller */ struct GNUNET_TESTBED_Controller * -GNUNET_TESTBED_controller_start (const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_TESTBED_Host *host, - uint64_t event_mask, - GNUNET_TESTBED_ControllerCallback cc, - void *cc_cls) +GNUNET_TESTBED_controller_connect (const struct GNUNET_CONFIGURATION_Handle + *cfg, struct GNUNET_TESTBED_Host *host, + uint64_t event_mask, + GNUNET_TESTBED_ControllerCallback cc, + void *cc_cls) { - GNUNET_break (0); - return NULL; + struct GNUNET_TESTBED_Controller *controller; + struct GNUNET_TESTBED_InitMessage *msg; + const char *controller_hostname; + unsigned long long max_parallel_operations; + unsigned long long max_parallel_service_connections; + unsigned long long max_parallel_topology_config_operations; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg, "testbed", + "MAX_PARALLEL_OPERATIONS", + &max_parallel_operations)) + { + GNUNET_break (0); + return NULL; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg, "testbed", + "MAX_PARALLEL_SERVICE_CONNECTIONS", + &max_parallel_service_connections)) + { + GNUNET_break (0); + return NULL; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg, "testbed", + "MAX_PARALLEL_TOPOLOGY_CONFIG_OPERATIONS", + &max_parallel_topology_config_operations)) + { + GNUNET_break (0); + return NULL; + } + controller = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Controller)); + controller->cc = cc; + controller->cc_cls = cc_cls; + controller->event_mask = event_mask; + controller->cfg = GNUNET_CONFIGURATION_dup (cfg); + controller->client = GNUNET_CLIENT_connect ("testbed", controller->cfg); + if (NULL == controller->client) + { + GNUNET_TESTBED_controller_disconnect (controller); + return NULL; + } + if (NULL == host) + { + host = GNUNET_TESTBED_host_create_by_id_ (0); + if (NULL == host) /* If the above host create fails */ + { + LOG (GNUNET_ERROR_TYPE_WARNING, + "Treating NULL host as localhost. Multiple references to localhost " + "may break when localhost freed before calling disconnect \n"); + host = GNUNET_TESTBED_host_lookup_by_id_ (0); + } + else + { + controller->aux_host = GNUNET_YES; + } + } + GNUNET_assert (NULL != host); + GNUNET_TESTBED_mark_host_registered_at_ (host, controller); + controller->host = host; + controller->opq_parallel_operations = + GNUNET_TESTBED_operation_queue_create_ ((unsigned int) + max_parallel_operations); + controller->opq_parallel_service_connections = + GNUNET_TESTBED_operation_queue_create_ ((unsigned int) + max_parallel_service_connections); + controller->opq_parallel_topology_config_operations = + GNUNET_TESTBED_operation_queue_create_ ((unsigned int) + max_parallel_topology_config_operations); + controller->opq_parallel_overlay_connect_operations = + GNUNET_TESTBED_operation_queue_create_ (0); + GNUNET_TESTBED_set_num_parallel_overlay_connects_ (controller, 1); + controller->poc_sd = SD_init (10); + controller_hostname = GNUNET_TESTBED_host_get_hostname (host); + if (NULL == controller_hostname) + controller_hostname = "127.0.0.1"; + msg = + GNUNET_malloc (sizeof (struct GNUNET_TESTBED_InitMessage) + + strlen (controller_hostname) + 1); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_INIT); + msg->header.size = + htons (sizeof (struct GNUNET_TESTBED_InitMessage) + + strlen (controller_hostname) + 1); + msg->host_id = htonl (GNUNET_TESTBED_host_get_id_ (host)); + msg->event_mask = GNUNET_htonll (controller->event_mask); + strcpy ((char *) &msg[1], controller_hostname); + GNUNET_TESTBED_queue_message_ (controller, + (struct GNUNET_MessageHeader *) msg); + return controller; } @@ -67,7 +1913,7 @@ GNUNET_TESTBED_controller_start (const struct GNUNET_CONFIGURATION_Handle *cfg, * should not be run for each peer but instead be shared * across N peers on the specified host. This function * must be called before any peers are created at the host. - * + * * @param controller controller to configure * @param service_name name of the service to share * @param num_peers number of peers that should share one instance @@ -75,58 +1921,462 @@ GNUNET_TESTBED_controller_start (const struct GNUNET_CONFIGURATION_Handle *cfg, * use 0 to disable the service */ void -GNUNET_TESTBED_controller_configure_sharing (struct GNUNET_TESTBED_Controller *controller, - const char *service_name, - uint32_t num_peers) +GNUNET_TESTBED_controller_configure_sharing (struct GNUNET_TESTBED_Controller + *controller, + const char *service_name, + uint32_t num_peers) { - GNUNET_break (0); + struct GNUNET_TESTBED_ConfigureSharedServiceMessage *msg; + uint16_t service_name_size; + uint16_t msg_size; + + service_name_size = strlen (service_name) + 1; + msg_size = + sizeof (struct GNUNET_TESTBED_ConfigureSharedServiceMessage) + + service_name_size; + msg = GNUNET_malloc (msg_size); + msg->header.size = htons (msg_size); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_SHARE_SERVICE); + msg->host_id = htonl (GNUNET_TESTBED_host_get_id_ (controller->host)); + msg->num_peers = htonl (num_peers); + memcpy (&msg[1], service_name, service_name_size); + GNUNET_TESTBED_queue_message_ (controller, + (struct GNUNET_MessageHeader *) msg); + GNUNET_break (0); /* This function is not yet implemented on the + * testbed service */ } /** - * Stop the given controller (also will terminate all peers and - * controllers dependent on this controller). This function - * blocks until the testbed has been fully terminated (!). + * disconnects from the controller. * * @param controller handle to controller to stop */ void -GNUNET_TESTBED_controller_stop (struct GNUNET_TESTBED_Controller *controller) +GNUNET_TESTBED_controller_disconnect (struct GNUNET_TESTBED_Controller + *controller) { - GNUNET_break (0); + struct MessageQueue *mq_entry; + + if (NULL != controller->th) + GNUNET_CLIENT_notify_transmit_ready_cancel (controller->th); + /* Clear the message queue */ + while (NULL != (mq_entry = controller->mq_head)) + { + GNUNET_CONTAINER_DLL_remove (controller->mq_head, controller->mq_tail, + mq_entry); + GNUNET_free (mq_entry->msg); + GNUNET_free (mq_entry); + } + if (NULL != controller->client) + GNUNET_CLIENT_disconnect (controller->client); + GNUNET_CONFIGURATION_destroy (controller->cfg); + if (GNUNET_YES == controller->aux_host) + GNUNET_TESTBED_host_destroy (controller->host); + GNUNET_TESTBED_operation_queue_destroy_ (controller->opq_parallel_operations); + GNUNET_TESTBED_operation_queue_destroy_ + (controller->opq_parallel_service_connections); + GNUNET_TESTBED_operation_queue_destroy_ + (controller->opq_parallel_topology_config_operations); + GNUNET_TESTBED_operation_queue_destroy_ + (controller->opq_parallel_overlay_connect_operations); + SD_destroy (controller->poc_sd); + GNUNET_free_non_null (controller->tslots); + GNUNET_free (controller); +} + + +/** + * Register a host with the controller + * + * @param controller the controller handle + * @param host the host to register + * @param cc the completion callback to call to inform the status of + * registration. After calling this callback the registration handle + * will be invalid. Cannot be NULL. + * @param cc_cls the closure for the cc + * @return handle to the host registration which can be used to cancel the + * registration + */ +struct GNUNET_TESTBED_HostRegistrationHandle * +GNUNET_TESTBED_register_host (struct GNUNET_TESTBED_Controller *controller, + struct GNUNET_TESTBED_Host *host, + GNUNET_TESTBED_HostRegistrationCompletion cc, + void *cc_cls) +{ + struct GNUNET_TESTBED_HostRegistrationHandle *rh; + struct GNUNET_TESTBED_AddHostMessage *msg; + const char *username; + const char *hostname; + uint16_t msg_size; + uint16_t user_name_length; + + if (NULL != controller->rh) + return NULL; + hostname = GNUNET_TESTBED_host_get_hostname (host); + if (GNUNET_YES == GNUNET_TESTBED_is_host_registered_ (host, controller)) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Host hostname: %s already registered\n", + (NULL == hostname) ? "localhost" : hostname); + return NULL; + } + rh = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_HostRegistrationHandle)); + rh->host = host; + rh->c = controller; + GNUNET_assert (NULL != cc); + rh->cc = cc; + rh->cc_cls = cc_cls; + controller->rh = rh; + username = GNUNET_TESTBED_host_get_username_ (host); + msg_size = (sizeof (struct GNUNET_TESTBED_AddHostMessage)); + user_name_length = 0; + if (NULL != username) + { + user_name_length = strlen (username) + 1; + msg_size += user_name_length; + } + /* FIXME: what happens when hostname is NULL? localhost */ + GNUNET_assert (NULL != hostname); + msg_size += strlen (hostname) + 1; + msg = GNUNET_malloc (msg_size); + msg->header.size = htons (msg_size); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_ADD_HOST); + msg->host_id = htonl (GNUNET_TESTBED_host_get_id_ (host)); + msg->ssh_port = htons (GNUNET_TESTBED_host_get_ssh_port_ (host)); + if (NULL != username) + { + msg->user_name_length = htons (user_name_length - 1); + memcpy (&msg[1], username, user_name_length); + } + else + msg->user_name_length = htons (user_name_length); + strcpy (((void *) &msg[1]) + user_name_length, hostname); + GNUNET_TESTBED_queue_message_ (controller, + (struct GNUNET_MessageHeader *) msg); + return rh; +} + + +/** + * Cancel the pending registration. Note that if the registration message is + * already sent to the service the cancellation has only the effect that the + * registration completion callback for the registration is never called. + * + * @param handle the registration handle to cancel + */ +void +GNUNET_TESTBED_cancel_registration (struct GNUNET_TESTBED_HostRegistrationHandle + *handle) +{ + if (handle != handle->c->rh) + { + GNUNET_break (0); + return; + } + handle->c->rh = NULL; + GNUNET_free (handle); +} + + +/** + * Same as the GNUNET_TESTBED_controller_link_2, but with ids for delegated host + * and slave host + * + * @param op_cls the operation closure for the event which is generated to + * signal success or failure of this operation + * @param master handle to the master controller who creates the association + * @param delegated_host_id id of the host to which requests should be delegated + * @param slave_host_id id of the host which is used to run the slave controller + * @param sxcfg serialized and compressed configuration + * @param sxcfg_size the size sxcfg + * @param scfg_size the size of uncompressed serialized configuration + * @param is_subordinate GNUNET_YES if the controller at delegated_host should + * be started by the slave controller; GNUNET_NO if the slave + * controller has to connect to the already started delegated + * controller via TCP/IP + * @return the operation handle + */ +struct GNUNET_TESTBED_Operation * +GNUNET_TESTBED_controller_link_2_ (void *op_cls, + struct GNUNET_TESTBED_Controller *master, + uint32_t delegated_host_id, + uint32_t slave_host_id, const char *sxcfg, + size_t sxcfg_size, size_t scfg_size, + int is_subordinate) +{ + struct OperationContext *opc; + struct GNUNET_TESTBED_ControllerLinkMessage *msg; + struct ControllerLinkData *data; + uint16_t msg_size; + + msg_size = sxcfg_size + sizeof (struct GNUNET_TESTBED_ControllerLinkMessage); + msg = GNUNET_malloc (msg_size); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_LINK_CONTROLLERS); + msg->header.size = htons (msg_size); + msg->delegated_host_id = htonl (delegated_host_id); + msg->slave_host_id = htonl (slave_host_id); + msg->config_size = htons ((uint16_t) scfg_size); + msg->is_subordinate = (GNUNET_YES == is_subordinate) ? 1 : 0; + memcpy (&msg[1], sxcfg, sxcfg_size); + data = GNUNET_malloc (sizeof (struct ControllerLinkData)); + data->msg = msg; + opc = GNUNET_malloc (sizeof (struct OperationContext)); + opc->c = master; + opc->data = data; + opc->type = OP_LINK_CONTROLLERS; + opc->id = GNUNET_TESTBED_get_next_op_id (opc->c); + opc->state = OPC_STATE_INIT; + opc->op_cls = op_cls; + msg->operation_id = GNUNET_htonll (opc->id); + opc->op = + GNUNET_TESTBED_operation_create_ (opc, &opstart_link_controllers, + &oprelease_link_controllers); + GNUNET_TESTBED_operation_queue_insert_ (master->opq_parallel_operations, + opc->op); + GNUNET_TESTBED_operation_begin_wait_ (opc->op); + return opc->op; } /** - * Create a link from a 'master' controller to a slave controller. - * Whenever the master controller is asked to start a peer at the - * given 'delegated_host', it will delegate the request to the - * specified slave controller. Note that the slave controller runs at - * the 'slave_host', which may or may not be the same host as the - * 'delegated_host' (for hierarchical delegations). The configuration - * of the slave controller is given and to be used to either create - * the slave controller or to connect to an existing slave controller - * process. 'is_subordinate' specifies if the given slave controller - * should be started and managed by the master controller, or if the - * slave already has a master and this is just a secondary master that - * is also allowed to use the existing slave. + * Same as the GNUNET_TESTBED_controller_link, however expects configuration in + * serialized and compressed * + * @param op_cls the operation closure for the event which is generated to + * signal success or failure of this operation * @param master handle to the master controller who creates the association - * @param delegated_host requests to which host should be delegated - * @param slave_host which host is used to run the slave controller + * @param delegated_host requests to which host should be delegated; cannot be NULL + * @param slave_host which host is used to run the slave controller; use NULL to + * make the master controller connect to the delegated host + * @param sxcfg serialized and compressed configuration + * @param sxcfg_size the size sxcfg + * @param scfg_size the size of uncompressed serialized configuration + * @param is_subordinate GNUNET_YES if the controller at delegated_host should + * be started by the slave controller; GNUNET_NO if the slave + * controller has to connect to the already started delegated + * controller via TCP/IP + * @return the operation handle + */ +struct GNUNET_TESTBED_Operation * +GNUNET_TESTBED_controller_link_2 (void *op_cls, + struct GNUNET_TESTBED_Controller *master, + struct GNUNET_TESTBED_Host *delegated_host, + struct GNUNET_TESTBED_Host *slave_host, + const char *sxcfg, size_t sxcfg_size, + size_t scfg_size, int is_subordinate) +{ + uint32_t delegated_host_id; + uint32_t slave_host_id; + + GNUNET_assert (GNUNET_YES == + GNUNET_TESTBED_is_host_registered_ (delegated_host, master)); + delegated_host_id = GNUNET_TESTBED_host_get_id_ (delegated_host); + slave_host_id = + GNUNET_TESTBED_host_get_id_ ((NULL != + slave_host) ? slave_host : master->host); + if ((NULL != slave_host) && (0 != GNUNET_TESTBED_host_get_id_ (slave_host))) + GNUNET_assert (GNUNET_YES == + GNUNET_TESTBED_is_host_registered_ (slave_host, master)); + + return GNUNET_TESTBED_controller_link_2_ (op_cls, master, delegated_host_id, + slave_host_id, sxcfg, sxcfg_size, + scfg_size, is_subordinate); +} + + +/** + * Compresses given configuration using zlib compress + * + * @param config the serialized configuration + * @param size the size of config + * @param xconfig will be set to the compressed configuration (memory is fresly + * allocated) + * @return the size of the xconfig + */ +size_t +GNUNET_TESTBED_compress_config_ (const char *config, size_t size, + char **xconfig) +{ + size_t xsize; + + xsize = compressBound ((uLong) size); + *xconfig = GNUNET_malloc (xsize); + GNUNET_assert (Z_OK == + compress2 ((Bytef *) * xconfig, (uLongf *) & xsize, + (const Bytef *) config, (uLongf) size, + Z_BEST_SPEED)); + return xsize; +} + + +/** + * Same as the GNUNET_TESTBED_controller_link, but with ids for delegated host + * and slave host + * + * @param op_cls the operation closure for the event which is generated to + * signal success or failure of this operation + * @param master handle to the master controller who creates the association + * @param delegated_host_id id of the host to which requests should be + * delegated; cannot be NULL + * @param slave_host_id id of the host which should connect to controller + * running on delegated host ; use NULL to make the master controller + * connect to the delegated host * @param slave_cfg configuration to use for the slave controller - * @param is_subordinate GNUNET_YES if the slave should be started (and stopped) - * by the master controller; GNUNET_NO if we are just - * allowed to use the slave via TCP/IP + * @param is_subordinate GNUNET_YES if the controller at delegated_host should + * be started by the slave controller; GNUNET_NO if the slave + * controller has to connect to the already started delegated + * controller via TCP/IP + * @return the operation handle */ -void -GNUNET_TESTBED_controller_link (struct GNUNET_TESTBED_Controller *master, - struct GNUNET_TESTBED_Host *delegated_host, - struct GNUNET_TESTBED_Host *slave_host, - const struct GNUNET_CONFIGURATION_Handle *slave_cfg, - int is_subordinate) +struct GNUNET_TESTBED_Operation * +GNUNET_TESTBED_controller_link_ (void *op_cls, + struct GNUNET_TESTBED_Controller *master, + uint32_t delegated_host_id, + uint32_t slave_host_id, + const struct GNUNET_CONFIGURATION_Handle + *slave_cfg, int is_subordinate) { - GNUNET_break (0); + struct GNUNET_TESTBED_Operation *op; + char *config; + char *cconfig; + size_t cc_size; + size_t config_size; + + config = GNUNET_CONFIGURATION_serialize (slave_cfg, &config_size); + cc_size = GNUNET_TESTBED_compress_config_ (config, config_size, &cconfig); + GNUNET_free (config); + /* Configuration doesn't fit in 1 message */ + GNUNET_assert ((UINT16_MAX - + sizeof (struct GNUNET_TESTBED_ControllerLinkMessage)) >= + cc_size); + op = GNUNET_TESTBED_controller_link_2_ (op_cls, master, delegated_host_id, + slave_host_id, (const char *) cconfig, + cc_size, config_size, is_subordinate); + GNUNET_free (cconfig); + return op; +} + + +/** + * Create a link from slave controller to delegated controller. Whenever the + * master controller is asked to start a peer at the delegated controller the + * request will be routed towards slave controller (if a route exists). The + * slave controller will then route it to the delegated controller. The + * configuration of the delegated controller is given and is used to either + * create the delegated controller or to connect to an existing controller. Note + * that while starting the delegated controller the configuration will be + * modified to accommodate available free ports. the 'is_subordinate' specifies + * if the given delegated controller should be started and managed by the slave + * controller, or if the delegated controller already has a master and the slave + * controller connects to it as a non master controller. The success or failure + * of this operation will be signalled through the + * GNUNET_TESTBED_ControllerCallback() with an event of type + * GNUNET_TESTBED_ET_OPERATION_FINISHED + * + * @param op_cls the operation closure for the event which is generated to + * signal success or failure of this operation + * @param master handle to the master controller who creates the association + * @param delegated_host requests to which host should be delegated; cannot be NULL + * @param slave_host which host is used to run the slave controller; use NULL to + * make the master controller connect to the delegated host + * @param slave_cfg configuration to use for the slave controller + * @param is_subordinate GNUNET_YES if the controller at delegated_host should + * be started by the slave controller; GNUNET_NO if the slave + * controller has to connect to the already started delegated + * controller via TCP/IP + * @return the operation handle + */ +struct GNUNET_TESTBED_Operation * +GNUNET_TESTBED_controller_link (void *op_cls, + struct GNUNET_TESTBED_Controller *master, + struct GNUNET_TESTBED_Host *delegated_host, + struct GNUNET_TESTBED_Host *slave_host, + const struct GNUNET_CONFIGURATION_Handle + *slave_cfg, int is_subordinate) +{ + uint32_t slave_host_id; + uint32_t delegated_host_id; + + GNUNET_assert (GNUNET_YES == + GNUNET_TESTBED_is_host_registered_ (delegated_host, master)); + slave_host_id = + GNUNET_TESTBED_host_get_id_ ((NULL != + slave_host) ? slave_host : master->host); + delegated_host_id = GNUNET_TESTBED_host_get_id_ (delegated_host); + if ((NULL != slave_host) && (0 != slave_host_id)) + GNUNET_assert (GNUNET_YES == + GNUNET_TESTBED_is_host_registered_ (slave_host, master)); + return GNUNET_TESTBED_controller_link_ (op_cls, master, delegated_host_id, + slave_host_id, slave_cfg, + is_subordinate); + +} + + +/** + * Like GNUNET_TESTBED_get_slave_config(), however without the host registration + * check. Another difference is that this function takes the id of the slave + * host. + * + * @param op_cls the closure for the operation + * @param master the handle to master controller + * @param slave_host_id id of the host where the slave controller is running to + * the slave_host should remain valid until this operation is cancelled + * or marked as finished + * @return the operation handle; + */ +struct GNUNET_TESTBED_Operation * +GNUNET_TESTBED_get_slave_config_ (void *op_cls, + struct GNUNET_TESTBED_Controller *master, + uint32_t slave_host_id) +{ + struct OperationContext *opc; + struct GetSlaveConfigData *data; + + data = GNUNET_malloc (sizeof (struct GetSlaveConfigData)); + data->slave_id = slave_host_id; + opc = GNUNET_malloc (sizeof (struct OperationContext)); + opc->state = OPC_STATE_INIT; + opc->c = master; + opc->id = GNUNET_TESTBED_get_next_op_id (master); + opc->type = OP_GET_SLAVE_CONFIG; + opc->data = data; + opc->op_cls = op_cls; + opc->op = + GNUNET_TESTBED_operation_create_ (opc, &opstart_get_slave_config, + &oprelease_get_slave_config); + GNUNET_TESTBED_operation_queue_insert_ (master->opq_parallel_operations, + opc->op); + GNUNET_TESTBED_operation_begin_wait_ (opc->op); + return opc->op; +} + + +/** + * Function to acquire the configuration of a running slave controller. The + * completion of the operation is signalled through the controller_cb from + * GNUNET_TESTBED_controller_connect(). If the operation is successful the + * handle to the configuration is available in the generic pointer of + * operation_finished field of struct GNUNET_TESTBED_EventInformation. + * + * @param op_cls the closure for the operation + * @param master the handle to master controller + * @param slave_host the host where the slave controller is running; the handle + * to the slave_host should remain valid until this operation is + * cancelled or marked as finished + * @return the operation handle; NULL if the slave_host is not registered at + * master + */ +struct GNUNET_TESTBED_Operation * +GNUNET_TESTBED_get_slave_config (void *op_cls, + struct GNUNET_TESTBED_Controller *master, + struct GNUNET_TESTBED_Host *slave_host) +{ + if (GNUNET_NO == GNUNET_TESTBED_is_host_registered_ (slave_host, master)) + return NULL; + return GNUNET_TESTBED_get_slave_config_ (op_cls, master, + GNUNET_TESTBED_host_get_id_ + (slave_host)); } @@ -140,11 +2390,375 @@ GNUNET_TESTBED_controller_link (struct GNUNET_TESTBED_Controller *master, * be written to. */ void -GNUNET_TESTBED_overlay_write_topology_to_file (struct GNUNET_TESTBED_Controller *controller, - const char *filename) +GNUNET_TESTBED_overlay_write_topology_to_file (struct GNUNET_TESTBED_Controller + *controller, + const char *filename) { + GNUNET_break (0); } +/** + * Creates a helper initialization message. This function is here because we + * want to use this in testing + * + * @param trusted_ip the ip address of the controller which will be set as TRUSTED + * HOST(all connections form this ip are permitted by the testbed) when + * starting testbed controller at host. This can either be a single ip + * address or a network address in CIDR notation. + * @param hostname the hostname of the destination this message is intended for + * @param cfg the configuration that has to used to start the testbed service + * thru helper + * @return the initialization message + */ +struct GNUNET_TESTBED_HelperInit * +GNUNET_TESTBED_create_helper_init_msg_ (const char *trusted_ip, + const char *hostname, + const struct GNUNET_CONFIGURATION_Handle + *cfg) +{ + struct GNUNET_TESTBED_HelperInit *msg; + char *config; + char *xconfig; + size_t config_size; + size_t xconfig_size; + uint16_t trusted_ip_len; + uint16_t hostname_len; + uint16_t msg_size; + + config = GNUNET_CONFIGURATION_serialize (cfg, &config_size); + GNUNET_assert (NULL != config); + xconfig_size = + GNUNET_TESTBED_compress_config_ (config, config_size, &xconfig); + GNUNET_free (config); + trusted_ip_len = strlen (trusted_ip); + hostname_len = (NULL == hostname) ? 0 : strlen (hostname); + msg_size = + xconfig_size + trusted_ip_len + 1 + + sizeof (struct GNUNET_TESTBED_HelperInit); + msg_size += hostname_len; + msg = GNUNET_realloc (xconfig, msg_size); + (void) memmove (((void *) &msg[1]) + trusted_ip_len + 1 + hostname_len, msg, + xconfig_size); + msg->header.size = htons (msg_size); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_HELPER_INIT); + msg->trusted_ip_size = htons (trusted_ip_len); + msg->hostname_size = htons (hostname_len); + msg->config_size = htons (config_size); + (void) strcpy ((char *) &msg[1], trusted_ip); + if (0 != hostname_len) + (void) strncpy (((char *) &msg[1]) + trusted_ip_len + 1, hostname, + hostname_len); + return msg; +} + + +/** + * Cancel a pending operation. Releases all resources + * of the operation and will ensure that no event + * is generated for the operation. Does NOT guarantee + * that the operation will be fully undone (or that + * nothing ever happened). + * + * @param operation operation to cancel + */ +void +GNUNET_TESTBED_operation_cancel (struct GNUNET_TESTBED_Operation *operation) +{ + GNUNET_TESTBED_operation_done (operation); +} + + +/** + * Signal that the information from an operation has been fully + * processed. This function MUST be called for each event + * of type 'operation_finished' to fully remove the operation + * from the operation queue. After calling this function, the + * 'op_result' becomes invalid (!). + * + * @param operation operation to signal completion for + */ +void +GNUNET_TESTBED_operation_done (struct GNUNET_TESTBED_Operation *operation) +{ + last_finished_operation = operation; + GNUNET_TESTBED_operation_release_ (operation); +} + + +/** + * Generates configuration by uncompressing configuration in given message. The + * given message should be of the following types: + * GNUNET_MESSAGE_TYPE_TESTBED_PEERCONFIG, + * GNUNET_MESSAGE_TYPE_TESTBED_SLAVECONFIG + * + * @param msg the message containing compressed configuration + * @return handle to the parsed configuration + */ +struct GNUNET_CONFIGURATION_Handle * +GNUNET_TESTBED_extract_config_ (const struct GNUNET_MessageHeader *msg) +{ + struct GNUNET_CONFIGURATION_Handle *cfg; + Bytef *data; + const Bytef *xdata; + uLong data_len; + uLong xdata_len; + int ret; + + switch (ntohs (msg->type)) + { + case GNUNET_MESSAGE_TYPE_TESTBED_PEER_CONFIGURATION: + { + const struct GNUNET_TESTBED_PeerConfigurationInformationMessage *imsg; + + imsg = + (const struct GNUNET_TESTBED_PeerConfigurationInformationMessage *) msg; + data_len = (uLong) ntohs (imsg->config_size); + xdata_len = + ntohs (imsg->header.size) - + sizeof (struct GNUNET_TESTBED_PeerConfigurationInformationMessage); + xdata = (const Bytef *) &imsg[1]; + } + break; + case GNUNET_MESSAGE_TYPE_TESTBED_SLAVE_CONFIGURATION: + { + const struct GNUNET_TESTBED_SlaveConfiguration *imsg; + + imsg = (const struct GNUNET_TESTBED_SlaveConfiguration *) msg; + data_len = (uLong) ntohs (imsg->config_size); + xdata_len = + ntohs (imsg->header.size) - + sizeof (struct GNUNET_TESTBED_SlaveConfiguration); + xdata = (const Bytef *) &imsg[1]; + } + break; + default: + GNUNET_assert (0); + } + data = GNUNET_malloc (data_len); + if (Z_OK != (ret = uncompress (data, &data_len, xdata, xdata_len))) + GNUNET_assert (0); + cfg = GNUNET_CONFIGURATION_create (); + GNUNET_assert (GNUNET_OK == + GNUNET_CONFIGURATION_deserialize (cfg, (const char *) data, + (size_t) data_len, + GNUNET_NO)); + GNUNET_free (data); + return cfg; +} + + +/** + * Checks the integrity of the OperationFailureEventMessage and if good returns + * the error message it contains. + * + * @param msg the OperationFailureEventMessage + * @return the error message + */ +const char * +GNUNET_TESTBED_parse_error_string_ (const struct + GNUNET_TESTBED_OperationFailureEventMessage + *msg) +{ + uint16_t msize; + const char *emsg; + + msize = ntohs (msg->header.size); + if (sizeof (struct GNUNET_TESTBED_OperationFailureEventMessage) >= msize) + return NULL; + msize -= sizeof (struct GNUNET_TESTBED_OperationFailureEventMessage); + emsg = (const char *) &msg[1]; + if ('\0' != emsg[msize - 1]) + { + GNUNET_break (0); + return NULL; + } + return emsg; +} + + +/** + * Function to return the operation id for a controller. The operation id is + * created from the controllers host id and its internal operation counter. + * + * @param controller the handle to the controller whose operation id has to be incremented + * @return the incremented operation id. + */ +uint64_t +GNUNET_TESTBED_get_next_op_id (struct GNUNET_TESTBED_Controller * controller) +{ + uint64_t op_id; + + op_id = (uint64_t) GNUNET_TESTBED_host_get_id_ (controller->host); + op_id = op_id << 32; + op_id |= (uint64_t) controller->operation_counter++; + return op_id; +} + + +/** + * Returns a timing slot which will be exclusively locked + * + * @param c the controller handle + * @param key a pointer which is associated to the returned slot; should not be + * NULL. It serves as a key to determine the correct owner of the slot + * @return the time slot index in the array of time slots in the controller + * handle + */ +unsigned int +GNUNET_TESTBED_get_tslot_ (struct GNUNET_TESTBED_Controller *c, void *key) +{ + unsigned int slot; + + GNUNET_assert (NULL != c->tslots); + GNUNET_assert (NULL != key); + for (slot = 0; slot < c->num_parallel_connects; slot++) + if (NULL == c->tslots[slot].key) + { + c->tslots[slot].key = key; + return slot; + } + GNUNET_assert (0); /* We should always find a free tslot */ +} + + +/** + * Decides whether any change in the number of parallel overlay connects is + * necessary to adapt to the load on the system + * + * @param c the controller handle + */ +static void +decide_npoc (struct GNUNET_TESTBED_Controller *c) +{ + struct GNUNET_TIME_Relative avg; + int sd; + unsigned int slot; + unsigned int nvals; + + if (c->tslots_filled != c->num_parallel_connects) + return; + avg = GNUNET_TIME_UNIT_ZERO; + nvals = 0; + for (slot = 0; slot < c->num_parallel_connects; slot++) + { + avg = GNUNET_TIME_relative_add (avg, c->tslots[slot].time); + nvals += c->tslots[slot].nvals; + } + GNUNET_assert (nvals >= c->num_parallel_connects); + avg = GNUNET_TIME_relative_divide (avg, nvals); + GNUNET_assert (GNUNET_TIME_UNIT_FOREVER_REL.rel_value != avg.rel_value); + sd = SD_deviation_factor (c->poc_sd, (unsigned int) avg.rel_value); + if ( (sd <= 5) || + (0 == GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, + c->num_parallel_connects)) ) + SD_add_data (c->poc_sd, (unsigned int) avg.rel_value); + if (GNUNET_SYSERR == sd) + { + GNUNET_TESTBED_set_num_parallel_overlay_connects_ (c, + c->num_parallel_connects); + return; + } + GNUNET_assert (0 <= sd); + if (0 == sd) + { + GNUNET_TESTBED_set_num_parallel_overlay_connects_ (c, + c->num_parallel_connects + * 2); + return; + } + if (1 == sd) + { + GNUNET_TESTBED_set_num_parallel_overlay_connects_ (c, + c->num_parallel_connects + + 1); + return; + } + if (1 == c->num_parallel_connects) + { + GNUNET_TESTBED_set_num_parallel_overlay_connects_ (c, 1); + return; + } + if (2 == sd) + { + GNUNET_TESTBED_set_num_parallel_overlay_connects_ (c, + c->num_parallel_connects + - 1); + return; + } + GNUNET_TESTBED_set_num_parallel_overlay_connects_ (c, + c->num_parallel_connects / + 2); +} + + +/** + * Releases a time slot thus making it available for be used again + * + * @param c the controller handle + * @param index the index of the the time slot + * @param key the key to prove ownership of the timeslot + * @return GNUNET_YES if the time slot is successfully removed; GNUNET_NO if the + * time slot cannot be removed - this could be because of the index + * greater than existing number of time slots or `key' being different + */ +int +GNUNET_TESTBED_release_time_slot_ (struct GNUNET_TESTBED_Controller *c, + unsigned int index, void *key) +{ + struct TimeSlot *slot; + + GNUNET_assert (NULL != key); + if (index >= c->num_parallel_connects) + return GNUNET_NO; + slot = &c->tslots[index]; + if (key != slot->key) + return GNUNET_NO; + slot->key = NULL; + return GNUNET_YES; +} + + +/** + * Function to update a time slot + * + * @param c the controller handle + * @param index the index of the time slot to update + * @param key the key to identify ownership of the slot + * @param time the new time + * @param failed should this reading be treated as coming from a fail event + */ +void +GNUNET_TESTBED_update_time_slot_ (struct GNUNET_TESTBED_Controller *c, + unsigned int index, void *key, + struct GNUNET_TIME_Relative time, int failed) +{ + struct TimeSlot *slot; + + if (GNUNET_YES == failed) + { + if (1 == c->num_parallel_connects) + { + GNUNET_TESTBED_set_num_parallel_overlay_connects_ (c, 1); + return; + } + GNUNET_TESTBED_set_num_parallel_overlay_connects_ (c, + c->num_parallel_connects + - 1); + } + if (GNUNET_NO == GNUNET_TESTBED_release_time_slot_ (c, index, key)) + return; + slot = &c->tslots[index]; + slot->nvals++; + if (GNUNET_TIME_UNIT_ZERO.rel_value == slot->time.rel_value) + { + slot->time = time; + c->tslots_filled++; + decide_npoc (c); + return; + } + slot->time = GNUNET_TIME_relative_add (slot->time, time); +} + /* end of testbed_api.c */ diff --git a/src/testbed/testbed_api.h b/src/testbed/testbed_api.h new file mode 100644 index 0000000..d6c04a6 --- /dev/null +++ b/src/testbed/testbed_api.h @@ -0,0 +1,590 @@ +/* + This file is part of GNUnet + (C) 2012 Christian Grothoff (and other contributing authors) + + GNUnet 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. + + 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file testbed/testbed_api.h + * @brief Interface for functions internally exported from testbed_api.c + * @author Sree Harsha Totakura + */ + +#ifndef TESTBED_API_H +#define TESTBED_API_H + +#include "gnunet_testbed_service.h" +#include "testbed.h" + + +/** + * Testbed Helper binary name + */ +#define HELPER_TESTBED_BINARY "gnunet-helper-testbed" + + +/** + * Enumeration of operations + */ +enum OperationType +{ + /** + * Peer create operation + */ + OP_PEER_CREATE, + + /** + * Peer start operation + */ + OP_PEER_START, + + /** + * Peer stop operation + */ + OP_PEER_STOP, + + /** + * Peer destroy operation + */ + OP_PEER_DESTROY, + + /** + * Get peer information operation + */ + OP_PEER_INFO, + + /** + * Overlay connection operation + */ + OP_OVERLAY_CONNECT, + + /** + * Forwarded operation + */ + OP_FORWARDED, + + /** + * Link controllers operation + */ + OP_LINK_CONTROLLERS, + + /** + * Get slave config operation + */ + OP_GET_SLAVE_CONFIG +}; + + +/** + * The message queue for sending messages to the controller service + */ +struct MessageQueue; + +/** + * Structure for a controller link + */ +struct ControllerLink; + + +/** + * Enumeration of states of OperationContext + */ +enum OperationContextState +{ + /** + * The initial state where the associated operation has just been created + * and is waiting in the operation queues to be started + */ + OPC_STATE_INIT = 0, + + /** + * The operation has been started. It may occupy some resources which are to + * be freed if cancelled. + */ + OPC_STATE_STARTED, + + /** + * The operation has finished. The end results of this operation may occupy + * some resources which are to be freed by operation_done + */ + OPC_STATE_FINISHED +}; + + +/** + * Context information for GNUNET_TESTBED_Operation + */ +struct OperationContext +{ + /** + * next ptr for DLL + */ + struct OperationContext *next; + + /** + * prev ptr for DLL + */ + struct OperationContext *prev; + + /** + * The controller to which this operation context belongs to + */ + struct GNUNET_TESTBED_Controller *c; + + /** + * The operation + */ + struct GNUNET_TESTBED_Operation *op; + + /** + * The operation closure + */ + void *op_cls; + + /** + * Data relevant to the operation + */ + void *data; + + /** + * The id of the opearation + */ + uint64_t id; + + /** + * The type of operation + */ + enum OperationType type; + + /** + * The state of the operation + */ + enum OperationContextState state; +}; + + +/** + * Opaque handle for SD calculations + */ +struct SDHandle; + + +/** + * A slot to record time taken by an overlay connect operation + */ +struct TimeSlot +{ + /** + * A key to identify this timeslot + */ + void *key; + + /** + * Time + */ + struct GNUNET_TIME_Relative time; + + /** + * Number of timing values accumulated + */ + unsigned int nvals; +}; + + +/** + * Handle to interact with a GNUnet testbed controller. Each + * controller has at least one master handle which is created when the + * controller is created; this master handle interacts with the + * controller process, destroying it destroys the controller (by + * closing stdin of the controller process). Additionally, + * controllers can interact with each other (in a P2P fashion); those + * links are established via TCP/IP on the controller's service port. + */ +struct GNUNET_TESTBED_Controller +{ + /** + * The host where the controller is running + */ + struct GNUNET_TESTBED_Host *host; + + /** + * The controller callback + */ + GNUNET_TESTBED_ControllerCallback cc; + + /** + * The closure for controller callback + */ + void *cc_cls; + + /** + * The configuration to use while connecting to controller + */ + struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * The client connection handle to the controller service + */ + struct GNUNET_CLIENT_Connection *client; + + /** + * The head of the message queue + */ + struct MessageQueue *mq_head; + + /** + * The tail of the message queue + */ + struct MessageQueue *mq_tail; + + /** + * The head of the ControllerLink list + */ + struct ControllerLink *cl_head; + + /** + * The tail of the ControllerLink list + */ + struct ControllerLink *cl_tail; + + /** + * The client transmit handle + */ + struct GNUNET_CLIENT_TransmitHandle *th; + + /** + * The host registration handle; NULL if no current registration requests are + * present + */ + struct GNUNET_TESTBED_HostRegistrationHandle *rh; + + /** + * The head of the opeartion context queue + */ + struct OperationContext *ocq_head; + + /** + * The tail of the operation context queue + */ + struct OperationContext *ocq_tail; + + /** + * Operation queue for simultaneous operations + */ + struct OperationQueue *opq_parallel_operations; + + /** + * Operation queue for simultaneous service connections + */ + struct OperationQueue *opq_parallel_service_connections; + + /** + * Operation queue for simultaneous topology configuration operations + */ + struct OperationQueue *opq_parallel_topology_config_operations; + + /** + * Operation queue for simultaneous overlay connect operations + */ + struct OperationQueue *opq_parallel_overlay_connect_operations; + + /** + * An array of timing slots; size should be equal to the current number of parallel + * overlay connects + */ + struct TimeSlot *tslots; + + /** + * Handle for SD calculations amount parallel overlay connect operation finish + * times + */ + struct SDHandle *poc_sd; + + /** + * The controller event mask + */ + uint64_t event_mask; + + /** + * Did we start the receive loop yet? + */ + int in_receive; + + /** + * Did we create the host for this? + */ + int aux_host; + + /** + * The number of parallel overlay connects we do currently + */ + unsigned int num_parallel_connects; + + /** + * Counter to indicate when all the available time slots are filled + */ + unsigned int tslots_filled; + + /** + * The operation id counter. use current value and increment + */ + uint32_t operation_counter; + +}; + + +/** + * Queues a message in send queue for sending to the service + * + * @param controller the handle to the controller + * @param msg the message to queue + */ +void +GNUNET_TESTBED_queue_message_ (struct GNUNET_TESTBED_Controller *controller, + struct GNUNET_MessageHeader *msg); + + +/** + * Compresses given configuration using zlib compress + * + * @param config the serialized configuration + * @param size the size of config + * @param xconfig will be set to the compressed configuration (memory is fresly + * allocated) + * @return the size of the xconfig + */ +size_t +GNUNET_TESTBED_compress_config_ (const char *config, size_t size, + char **xconfig); + + +/** + * Adds an operation to the queue of operations + * + * @param op the operation to add + */ +void +GNUNET_TESTBED_operation_add_ (struct GNUNET_TESTBED_Operation *op); + + +/** + * Creates a helper initialization message. This function is here because we + * want to use this in testing + * + * @param trusted_ip the ip address of the controller which will be set as TRUSTED + * HOST(all connections form this ip are permitted by the testbed) when + * starting testbed controller at host. This can either be a single ip + * address or a network address in CIDR notation. + * @param hostname the hostname of the destination this message is intended for + * @param cfg the configuration that has to used to start the testbed service + * thru helper + * @return the initialization message + */ +struct GNUNET_TESTBED_HelperInit * +GNUNET_TESTBED_create_helper_init_msg_ (const char *cname, const char *hostname, + const struct GNUNET_CONFIGURATION_Handle + *cfg); + + +/** + * Sends the given message as an operation. The given callback is called when a + * reply for the operation is available. Call + * GNUNET_TESTBED_forward_operation_msg_cancel_() to cleanup the returned + * operation context if the cc hasn't been called + * + * @param controller the controller to which the message has to be sent + * @param operation_id the operation id of the message + * @param msg the message to send + * @param cc the callback to call when reply is available + * @param cc_cls the closure for the above callback + * @return the operation context which can be used to cancel the forwarded + * operation + */ +struct OperationContext * +GNUNET_TESTBED_forward_operation_msg_ (struct GNUNET_TESTBED_Controller + *controller, uint64_t operation_id, + const struct GNUNET_MessageHeader *msg, + GNUNET_CLIENT_MessageHandler cc, + void *cc_cls); + +/** + * Function to cancel an operation created by simply forwarding an operation + * message. + * + * @param opc the operation context from GNUNET_TESTBED_forward_operation_msg_() + */ +void +GNUNET_TESTBED_forward_operation_msg_cancel_ (struct OperationContext *opc); + + +/** + * Generates configuration by uncompressing configuration in given message. The + * given message should be of the following types: + * GNUNET_MESSAGE_TYPE_TESTBED_PEERCONFIG, + * GNUNET_MESSAGE_TYPE_TESTBED_SLAVECONFIG + * + * @param msg the message containing compressed configuration + * @return handle to the parsed configuration + */ +struct GNUNET_CONFIGURATION_Handle * +GNUNET_TESTBED_extract_config_ (const struct GNUNET_MessageHeader *msg); + + +/** + * Checks the integrity of the OpeationFailureEventMessage and if good returns + * the error message it contains. + * + * @param msg the OperationFailureEventMessage + * @return the error message + */ +const char * +GNUNET_TESTBED_parse_error_string_ (const struct + GNUNET_TESTBED_OperationFailureEventMessage + *msg); + + +/** + * Function to return the operation id for a controller. The operation id is + * created from the controllers host id and its internal operation counter. + * + * @param controller the handle to the controller whose operation id has to be incremented + * @return the incremented operation id. + */ +uint64_t +GNUNET_TESTBED_get_next_op_id (struct GNUNET_TESTBED_Controller *controller); + + +/** + * Like GNUNET_TESTBED_get_slave_config(), however without the host registration + * check. Another difference is that this function takes the id of the slave + * host. + * + * @param op_cls the closure for the operation + * @param master the handle to master controller + * @param slave_host_id id of the host where the slave controller is running to + * the slave_host should remain valid until this operation is cancelled + * or marked as finished + * @return the operation handle; + */ +struct GNUNET_TESTBED_Operation * +GNUNET_TESTBED_get_slave_config_ (void *op_cls, + struct GNUNET_TESTBED_Controller *master, + uint32_t slave_host_id); + + +/** + * Same as the GNUNET_TESTBED_controller_link_2, but with ids for delegated host + * and slave host + * + * @param op_cls the operation closure for the event which is generated to + * signal success or failure of this operation + * @param master handle to the master controller who creates the association + * @param delegated_host_id id of the host to which requests should be delegated + * @param slave_host_id id of the host which is used to run the slave controller + * @param sxcfg serialized and compressed configuration + * @param sxcfg_size the size sxcfg + * @param scfg_size the size of uncompressed serialized configuration + * @param is_subordinate GNUNET_YES if the controller at delegated_host should + * be started by the slave controller; GNUNET_NO if the slave + * controller has to connect to the already started delegated + * controller via TCP/IP + * @return the operation handle + */ +struct GNUNET_TESTBED_Operation * +GNUNET_TESTBED_controller_link_2_ (void *op_cls, + struct GNUNET_TESTBED_Controller *master, + uint32_t delegated_host_id, + uint32_t slave_host_id, const char *sxcfg, + size_t sxcfg_size, size_t scfg_size, + int is_subordinate); + + +/** + * Same as the GNUNET_TESTBED_controller_link, but with ids for delegated host + * and slave host + * + * @param op_cls the operation closure for the event which is generated to + * signal success or failure of this operation + * @param master handle to the master controller who creates the association + * @param delegated_host_id id of the host to which requests should be + * delegated; cannot be NULL + * @param slave_host_id id of the host which should connect to controller + * running on delegated host ; use NULL to make the master controller + * connect to the delegated host + * @param slave_cfg configuration to use for the slave controller + * @param is_subordinate GNUNET_YES if the controller at delegated_host should + * be started by the slave controller; GNUNET_NO if the slave + * controller has to connect to the already started delegated + * controller via TCP/IP + * @return the operation handle + */ +struct GNUNET_TESTBED_Operation * +GNUNET_TESTBED_controller_link_ (void *op_cls, + struct GNUNET_TESTBED_Controller *master, + uint32_t delegated_host_id, + uint32_t slave_host_id, + const struct GNUNET_CONFIGURATION_Handle + *slave_cfg, int is_subordinate); + + +/** + * Returns a timing slot which will be exclusively locked + * + * @param c the controller handle + * @param key a pointer which is associated to the returned slot; should not be + * NULL. It serves as a key to determine the correct owner of the slot + * @return the time slot index in the array of time slots in the controller + * handle + */ +unsigned int +GNUNET_TESTBED_get_tslot_ (struct GNUNET_TESTBED_Controller *c, void *key); + + +/** + * Function to update a time slot + * + * @param c the controller handle + * @param index the index of the time slot to update + * @param key the key to identify ownership of the slot + * @param time the new time + * @param failed should this reading be treated as coming from a fail event + */ +void +GNUNET_TESTBED_update_time_slot_ (struct GNUNET_TESTBED_Controller *c, + unsigned int index, void *key, + struct GNUNET_TIME_Relative time, int failed); + + +/** + * Releases a time slot thus making it available for be used again + * + * @param c the controller handle + * @param index the index of the the time slot + * @param key the key to prove ownership of the timeslot + * @return GNUNET_YES if the time slot is successfully removed; GNUNET_NO if the + * time slot cannot be removed - this could be because of the index + * greater than existing number of time slots or `key' being different + */ +int +GNUNET_TESTBED_release_time_slot_ (struct GNUNET_TESTBED_Controller *c, + unsigned int index, void *key); + + + + +#endif +/* end of testbed_api.h */ diff --git a/src/testbed/testbed_api_hosts.c b/src/testbed/testbed_api_hosts.c index 1b293b3..293f349 100644 --- a/src/testbed/testbed_api_hosts.c +++ b/src/testbed/testbed_api_hosts.c @@ -26,12 +26,46 @@ * @author Christian Grothoff */ #include "platform.h" +#include "gnunet_util_lib.h" #include "gnunet_testbed_service.h" #include "gnunet_core_service.h" -#include "gnunet_constants.h" #include "gnunet_transport_service.h" -#include "gnunet_hello_lib.h" +#include "testbed_api.h" +#include "testbed_api_hosts.h" + +/** + * Generic logging shorthand + */ +#define LOG(kind, ...) \ + GNUNET_log_from (kind, "testbed-api-hosts", __VA_ARGS__); + +/** + * Number of extra elements we create space for when we grow host list + */ +#define HOST_LIST_GROW_STEP 10 + + +/** + * A list entry for registered controllers list + */ +struct RegisteredController +{ + /** + * The controller at which this host is registered + */ + const struct GNUNET_TESTBED_Controller *controller; + + /** + * The next ptr for DLL + */ + struct RegisteredController *next; + + /** + * The prev ptr for DLL + */ + struct RegisteredController *prev; +}; /** @@ -42,32 +76,73 @@ struct GNUNET_TESTBED_Host { + /** + * The next pointer for DLL + */ + struct GNUNET_TESTBED_Host *next; + + /** + * The prev pointer for DLL + */ + struct GNUNET_TESTBED_Host *prev; + /** + * The hostname of the host; NULL for localhost + */ const char *hostname; + /** + * The username to be used for SSH login + */ const char *username; /** + * The head for the list of controllers where this host is registered + */ + struct RegisteredController *rc_head; + + /** + * The tail for the list of controllers where this host is registered + */ + struct RegisteredController *rc_tail; + + /** * Global ID we use to refer to a host on the network */ - uint32_t unique_id; + uint32_t id; + /** + * The port which is to be used for SSH + */ uint16_t port; + }; /** + * Array of available hosts + */ +static struct GNUNET_TESTBED_Host **host_list; + +/** + * The size of the available hosts list + */ +static unsigned int host_list_size; + + +/** * Lookup a host by ID. - * + * * @param id global host ID assigned to the host; 0 is * reserved to always mean 'localhost' - * @return handle to the host, NULL on error + * @return handle to the host, NULL if host not found */ struct GNUNET_TESTBED_Host * GNUNET_TESTBED_host_lookup_by_id_ (uint32_t id) { - GNUNET_break (0); - return NULL; + if (host_list_size <= id) + return NULL; + return host_list[id]; } @@ -75,7 +150,7 @@ GNUNET_TESTBED_host_lookup_by_id_ (uint32_t id) * Create a host by ID; given this host handle, we could not * run peers at the host, but we can talk about the host * internally. - * + * * @param id global host ID assigned to the host; 0 is * reserved to always mean 'localhost' * @return handle to the host, NULL on error @@ -83,28 +158,66 @@ GNUNET_TESTBED_host_lookup_by_id_ (uint32_t id) struct GNUNET_TESTBED_Host * GNUNET_TESTBED_host_create_by_id_ (uint32_t id) { - return NULL; + return GNUNET_TESTBED_host_create_with_id (id, NULL, NULL, 0); } /** - * Obtain a host's unique global ID. - * + * Obtain the host's unique global ID. + * * @param host handle to the host, NULL means 'localhost' * @return id global host ID assigned to the host (0 is * 'localhost', but then obviously not globally unique) */ uint32_t -GNUNET_TESTBED_host_get_id_ (struct GNUNET_TESTBED_Host *host) +GNUNET_TESTBED_host_get_id_ (const struct GNUNET_TESTBED_Host * host) +{ + return host->id; +} + + +/** + * Obtain the host's hostname. + * + * @param host handle to the host, NULL means 'localhost' + * @return hostname of the host + */ +const char * +GNUNET_TESTBED_host_get_hostname (const struct GNUNET_TESTBED_Host *host) { - GNUNET_break (0); - return 0; + return host->hostname; +} + + +/** + * Obtain the host's username + * + * @param host handle to the host, NULL means 'localhost' + * @return username to login to the host + */ +const char * +GNUNET_TESTBED_host_get_username_ (const struct GNUNET_TESTBED_Host *host) +{ + return host->username; +} + + +/** + * Obtain the host's ssh port + * + * @param host handle to the host, NULL means 'localhost' + * @return username to login to the host + */ +uint16_t +GNUNET_TESTBED_host_get_ssh_port_ (const struct GNUNET_TESTBED_Host * host) +{ + return host->port; } /** * Create a host to run peers and controllers on. - * + * * @param id global host ID assigned to the host; 0 is * reserved to always mean 'localhost' * @param hostname name of the host, use "NULL" for localhost @@ -113,34 +226,52 @@ GNUNET_TESTBED_host_get_id_ (struct GNUNET_TESTBED_Host *host) * @return handle to the host, NULL on error */ struct GNUNET_TESTBED_Host * -GNUNET_TESTBED_host_create_with_id_ (uint32_t id, - const char *hostname, - const char *username, - uint16_t port) +GNUNET_TESTBED_host_create_with_id (uint32_t id, const char *hostname, + const char *username, uint16_t port) { - GNUNET_break (0); - return NULL; + struct GNUNET_TESTBED_Host *host; + unsigned int new_size; + + if ((id < host_list_size) && (NULL != host_list[id])) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Host with id: %u already created\n", id); + return NULL; + } + host = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Host)); + host->hostname = (NULL != hostname) ? GNUNET_strdup (hostname) : NULL; + host->username = (NULL != username) ? GNUNET_strdup (username) : NULL; + host->id = id; + host->port = (0 == port) ? 22 : port; + new_size = host_list_size; + while (id >= new_size) + new_size += HOST_LIST_GROW_STEP; + if (new_size != host_list_size) + GNUNET_array_grow (host_list, host_list_size, new_size); + GNUNET_assert (id < host_list_size); + LOG (GNUNET_ERROR_TYPE_DEBUG, "Adding host with id: %u\n", host->id); + host_list[id] = host; + return host; } /** * Create a host to run peers and controllers on. - * + * * @param hostname name of the host, use "NULL" for localhost * @param username username to use for the login; may be NULL * @param port port number to use for ssh; use 0 to let ssh decide * @return handle to the host, NULL on error */ struct GNUNET_TESTBED_Host * -GNUNET_TESTBED_host_create (const char *hostname, - const char *username, - uint16_t port) +GNUNET_TESTBED_host_create (const char *hostname, const char *username, + uint16_t port) { static uint32_t uid_generator; - return GNUNET_TESTBED_host_create_with_id_ (++uid_generator, - hostname, username, - port); + if (NULL == hostname) + return GNUNET_TESTBED_host_create_with_id (0, hostname, username, port); + return GNUNET_TESTBED_host_create_with_id (++uid_generator, hostname, + username, port); } @@ -148,15 +279,90 @@ GNUNET_TESTBED_host_create (const char *hostname, * Load a set of hosts from a configuration file. * * @param filename file with the host specification - * @param hosts set to the hosts found in the file + * @param hosts set to the hosts found in the file; caller must free this if + * number of hosts returned is greater than 0 * @return number of hosts returned in 'hosts', 0 on error */ unsigned int GNUNET_TESTBED_hosts_load_from_file (const char *filename, - struct GNUNET_TESTBED_Host **hosts) + struct GNUNET_TESTBED_Host ***hosts) { - GNUNET_break (0); - return 0; + //struct GNUNET_TESTBED_Host **host_array; + struct GNUNET_TESTBED_Host *starting_host; + char *data; + char *buf; + char username[256]; + char hostname[256]; + uint64_t fs; + short int port; + int ret; + unsigned int offset; + unsigned int count; + + + GNUNET_assert (NULL != filename); + if (GNUNET_YES != GNUNET_DISK_file_test (filename)) + { + LOG (GNUNET_ERROR_TYPE_WARNING, _("Hosts file %s not found\n"), filename); + return 0; + } + if (GNUNET_OK != + GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES)) + fs = 0; + if (0 == fs) + { + LOG (GNUNET_ERROR_TYPE_WARNING, _("Hosts file %s has no data\n"), filename); + return 0; + } + data = GNUNET_malloc (fs); + if (fs != GNUNET_DISK_fn_read (filename, data, fs)) + { + GNUNET_free (data); + LOG (GNUNET_ERROR_TYPE_WARNING, _("Hosts file %s cannot be read\n"), + filename); + return 0; + } + buf = data; + offset = 0; + starting_host = NULL; + count = 0; + while (offset < (fs - 1)) + { + offset++; + if (((data[offset] == '\n')) && (buf != &data[offset])) + { + data[offset] = '\0'; + ret = + SSCANF (buf, "%255[a-zA-Z0-9_]@%255[a-zA-Z0-9.]:%5hd", username, + hostname, &port); + if (3 == ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Successfully read host %s, port %d and user %s from file\n", + hostname, port, username); + /* We store hosts in a static list; hence we only require the starting + * host pointer in that list to access the newly created list of hosts */ + if (NULL == starting_host) + starting_host = GNUNET_TESTBED_host_create (hostname, username, port); + else + (void) GNUNET_TESTBED_host_create (hostname, username, port); + count++; + } + else + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Error reading line `%s' in hostfile\n", buf); + buf = &data[offset + 1]; + } + else if ((data[offset] == '\n') || (data[offset] == '\0')) + buf = &data[offset + 1]; + } + GNUNET_free (data); + if (NULL == starting_host) + return 0; + *hosts = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Host *) * count); + memcpy (*hosts, &host_list[GNUNET_TESTBED_host_get_id_ (starting_host)], + sizeof (struct GNUNET_TESTBED_Host *) * count); + return count; } @@ -169,32 +375,290 @@ GNUNET_TESTBED_hosts_load_from_file (const char *filename, void GNUNET_TESTBED_host_destroy (struct GNUNET_TESTBED_Host *host) { - GNUNET_break (0); + struct RegisteredController *rc; + uint32_t id; + + GNUNET_assert (host->id < host_list_size); + GNUNET_assert (host_list[host->id] == host); + host_list[host->id] = NULL; + /* clear registered controllers list */ + for (rc = host->rc_head; NULL != rc; rc = host->rc_head) + { + GNUNET_CONTAINER_DLL_remove (host->rc_head, host->rc_tail, rc); + GNUNET_free (rc); + } + GNUNET_free_non_null ((char *) host->username); + GNUNET_free_non_null ((char *) host->hostname); + GNUNET_free (host); + while (host_list_size >= HOST_LIST_GROW_STEP) + { + for (id = host_list_size - 1; id > host_list_size - HOST_LIST_GROW_STEP; + id--) + if (NULL != host_list[id]) + break; + if (id != host_list_size - HOST_LIST_GROW_STEP) + break; + if (NULL != host_list[id]) + break; + host_list_size -= HOST_LIST_GROW_STEP; + } + host_list = + GNUNET_realloc (host_list, + sizeof (struct GNUNET_TESTBED_Host *) * host_list_size); +} + + +/** + * Marks a host as registered with a controller + * + * @param host the host to mark + * @param controller the controller at which this host is registered + */ +void +GNUNET_TESTBED_mark_host_registered_at_ (struct GNUNET_TESTBED_Host *host, + const struct GNUNET_TESTBED_Controller + *const controller) +{ + struct RegisteredController *rc; + + for (rc = host->rc_head; NULL != rc; rc = rc->next) + { + if (controller == rc->controller) /* already registered at controller */ + { + GNUNET_break (0); + return; + } + } + rc = GNUNET_malloc (sizeof (struct RegisteredController)); + rc->controller = controller; + GNUNET_CONTAINER_DLL_insert_tail (host->rc_head, host->rc_tail, rc); +} + + +/** + * Checks whether a host has been registered + * + * @param host the host to check + * @param controller the controller at which host's registration is checked + * @return GNUNET_YES if registered; GNUNET_NO if not + */ +int +GNUNET_TESTBED_is_host_registered_ (const struct GNUNET_TESTBED_Host *host, + const struct GNUNET_TESTBED_Controller + *const controller) +{ + struct RegisteredController *rc; + + for (rc = host->rc_head; NULL != rc; rc = rc->next) + { + if (controller == rc->controller) /* already registered at controller */ + { + return GNUNET_YES; + } + } + return GNUNET_NO; +} + + +/** + * The handle for whether a host is habitable or not + */ +struct GNUNET_TESTBED_HostHabitableCheckHandle +{ + /** + * The host to check + */ + const struct GNUNET_TESTBED_Host *host; + + /* /\** */ + /* * the configuration handle to lookup the path of the testbed helper */ + /* *\/ */ + /* const struct GNUNET_CONFIGURATION_Handle *cfg; */ + + /** + * The callback to call once we have the status + */ + GNUNET_TESTBED_HostHabitableCallback cb; + + /** + * The callback closure + */ + void *cb_cls; + + /** + * The process handle for the SSH process + */ + struct GNUNET_OS_Process *auxp; + + /** + * The SSH destination address string + */ + char *ssh_addr; + + /** + * The destination port string + */ + char *portstr; + + /** + * The path for hte testbed helper binary + */ + char *helper_binary_path; + + /** + * Task id for the habitability check task + */ + GNUNET_SCHEDULER_TaskIdentifier habitability_check_task; + + /** + * How long we wait before checking the process status. Should grow + * exponentially + */ + struct GNUNET_TIME_Relative wait_time; + +}; + + +/** + * Task for checking whether a host is habitable or not + * + * @param cls GNUNET_TESTBED_HostHabitableCheckHandle + * @param tc the scheduler task context + */ +static void +habitability_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_TESTBED_HostHabitableCheckHandle *h = cls; + void *cb_cls; + GNUNET_TESTBED_HostHabitableCallback cb; + const struct GNUNET_TESTBED_Host *host; + unsigned long code; + enum GNUNET_OS_ProcessStatusType type; + int ret; + + h->habitability_check_task = GNUNET_SCHEDULER_NO_TASK; + ret = GNUNET_OS_process_status (h->auxp, &type, &code); + if (GNUNET_SYSERR == ret) + { + GNUNET_break (0); + ret = GNUNET_NO; + goto call_cb; + } + if (GNUNET_NO == ret) + { + h->wait_time = GNUNET_TIME_STD_BACKOFF (h->wait_time); + h->habitability_check_task = + GNUNET_SCHEDULER_add_delayed (h->wait_time, &habitability_check, h); + return; + } + GNUNET_OS_process_destroy (h->auxp); + h->auxp = NULL; + ret = (0 != code) ? GNUNET_NO : GNUNET_YES; + +call_cb: + GNUNET_free (h->ssh_addr); + GNUNET_free (h->portstr); + GNUNET_free (h->helper_binary_path); + if (NULL != h->auxp) + GNUNET_OS_process_destroy (h->auxp); + cb = h->cb; + cb_cls = h->cb_cls; + host = h->host; + GNUNET_free (h); + if (NULL != cb) + cb (cb_cls, host, ret); } /** - * Run a given helper process at the given host. Communication - * with the helper will be via GNUnet messages on stdin/stdout. - * Runs the process via 'ssh' at the specified host, or locally. - * Essentially an SSH-wrapper around the 'gnunet_helper_lib.h' API. - * - * @param host host to use, use "NULL" for localhost - * @param binary_argv binary name and command-line arguments to give to the binary - * @param cb function to call for messages received from the binary - * @param cb_cls closure for cb - * @return handle to terminate the command, NULL on error + * Checks whether a host can be used to start testbed service + * + * @param host the host to check + * @param config the configuration handle to lookup the path of the testbed + * helper + * @param cb the callback to call to inform about habitability of the given host + * @param cb_cls the closure for the callback + * @return NULL upon any error or a handle which can be passed to + * GNUNET_TESTBED_is_host_habitable_cancel() */ -struct GNUNET_HELPER_Handle * -GNUNET_TESTBED_host_run_ (struct GNUNET_TESTBED_Host *host, - char *const binary_argv[], - GNUNET_SERVER_MessageTokenizerCallback cb, void *cb_cls) +struct GNUNET_TESTBED_HostHabitableCheckHandle * +GNUNET_TESTBED_is_host_habitable (const struct GNUNET_TESTBED_Host *host, + const struct GNUNET_CONFIGURATION_Handle + *config, + GNUNET_TESTBED_HostHabitableCallback cb, + void *cb_cls) { - /* FIXME: decide on the SSH command line, prepend it and - run GNUNET_HELPER_start with the modified binary_name and binary_argv! */ - GNUNET_break (0); - return NULL; + struct GNUNET_TESTBED_HostHabitableCheckHandle *h; + char *remote_args[11]; + const char *hostname; + unsigned int argp; + + h = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_HostHabitableCheckHandle)); + h->cb = cb; + h->cb_cls = cb_cls; + h->host = host; + hostname = (NULL == host->hostname) ? "127.0.0.1" : host->hostname; + if (NULL == host->username) + h->ssh_addr = GNUNET_strdup (hostname); + else + GNUNET_asprintf (&h->ssh_addr, "%s@%s", host->username, hostname); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (config, "testbed", + "HELPER_BINARY_PATH", + &h->helper_binary_path)) + h->helper_binary_path = + GNUNET_OS_get_libexec_binary_path (HELPER_TESTBED_BINARY); + argp = 0; + remote_args[argp++] = "ssh"; + GNUNET_asprintf (&h->portstr, "%u", host->port); + remote_args[argp++] = "-p"; + remote_args[argp++] = h->portstr; + remote_args[argp++] = "-o"; + remote_args[argp++] = "BatchMode=yes"; + remote_args[argp++] = "-o"; + remote_args[argp++] = "NoHostAuthenticationForLocalhost=yes"; + remote_args[argp++] = h->ssh_addr; + remote_args[argp++] = "stat"; + remote_args[argp++] = h->helper_binary_path; + remote_args[argp++] = NULL; + GNUNET_assert (argp == 11); + h->auxp = + GNUNET_OS_start_process_vap (GNUNET_NO, GNUNET_OS_INHERIT_STD_ERR, NULL, + NULL, "ssh", remote_args); + if (NULL == h->auxp) + { + GNUNET_break (0); /* Cannot exec SSH? */ + GNUNET_free (h->ssh_addr); + GNUNET_free (h->portstr); + GNUNET_free (h->helper_binary_path); + GNUNET_free (h); + return NULL; + } + h->wait_time = GNUNET_TIME_STD_BACKOFF (h->wait_time); + h->habitability_check_task = + GNUNET_SCHEDULER_add_delayed (h->wait_time, &habitability_check, h); + return h; } +/** + * Function to cancel a request started using GNUNET_TESTBED_is_host_habitable() + * + * @param handle the habitability check handle + */ +void +GNUNET_TESTBED_is_host_habitable_cancel (struct + GNUNET_TESTBED_HostHabitableCheckHandle + *handle) +{ + GNUNET_SCHEDULER_cancel (handle->habitability_check_task); + (void) GNUNET_OS_process_kill (handle->auxp, SIGTERM); + (void) GNUNET_OS_process_wait (handle->auxp); + GNUNET_OS_process_destroy (handle->auxp); + GNUNET_free (handle->ssh_addr); + GNUNET_free (handle->portstr); + GNUNET_free (handle->helper_binary_path); + GNUNET_free (handle); +} + /* end of testbed_api_hosts.c */ diff --git a/src/testbed/testbed_api_hosts.h b/src/testbed/testbed_api_hosts.h index 401d4e0..c0f4290 100644 --- a/src/testbed/testbed_api_hosts.h +++ b/src/testbed/testbed_api_hosts.h @@ -23,16 +23,17 @@ * @brief internal API to access the 'hosts' subsystem * @author Christian Grothoff */ + #ifndef NEW_TESTING_API_HOSTS_H #define NEW_TESTING_API_HOSTS_H #include "gnunet_testbed_service.h" -#include "gnunet_helper_lib.h" +#include "testbed_helper.h" /** * Lookup a host by ID. - * + * * @param id global host ID assigned to the host; 0 is * reserved to always mean 'localhost' * @return handle to the host, NULL on error @@ -45,7 +46,7 @@ GNUNET_TESTBED_host_lookup_by_id_ (uint32_t id); * Create a host by ID; given this host handle, we could not * run peers at the host, but we can talk about the host * internally. - * + * * @param id global host ID assigned to the host; 0 is * reserved to always mean 'localhost' * @return handle to the host, NULL on error @@ -55,51 +56,104 @@ GNUNET_TESTBED_host_create_by_id_ (uint32_t id); /** - * Create a host to run peers and controllers on. This function is used - * if a peer learns about a host via IPC between controllers (and thus - * some higher-level controller has already determined the unique IDs). - * - * @param id global host ID assigned to the host; 0 is - * reserved to always mean 'localhost' - * @param hostname name of the host, use "NULL" for localhost - * @param username username to use for the login; may be NULL - * @param port port number to use for ssh; use 0 to let ssh decide - * @return handle to the host, NULL on error - */ -struct GNUNET_TESTBED_Host * -GNUNET_TESTBED_host_create_with_id_ (uint32_t id, - const char *hostname, - const char *username, - uint16_t port); - - -/** * Obtain a host's unique global ID. - * + * * @param host handle to the host, NULL means 'localhost' * @return id global host ID assigned to the host (0 is * 'localhost', but then obviously not globally unique) */ uint32_t -GNUNET_TESTBED_host_get_id_ (struct GNUNET_TESTBED_Host *host); +GNUNET_TESTBED_host_get_id_ (const struct GNUNET_TESTBED_Host *host); /** - * Run a given helper process at the given host. Communication - * with the helper will be via GNUnet messages on stdin/stdout. - * Runs the process via 'ssh' at the specified host, or locally. - * Essentially an SSH-wrapper around the 'gnunet_helper_lib.h' API. - * - * @param host host to use, use "NULL" for localhost - * @param binary_argv binary name and command-line arguments to give to the binary - * @param cb function to call for messages received from the binary - * @param cb_cls closure for cb - * @return handle to terminate the command, NULL on error + * Obtain the host's username + * + * @param host handle to the host, NULL means 'localhost' + * @return username to login to the host + */ +const char * +GNUNET_TESTBED_host_get_username_ (const struct GNUNET_TESTBED_Host *host); + + +/** + * Obtain the host's ssh port + * + * @param host handle to the host, NULL means 'localhost' + * @return username to login to the host */ -struct GNUNET_HELPER_Handle * -GNUNET_TESTBED_host_run_ (struct GNUNET_TESTBED_Host *host, - char *const binary_argv[], - GNUNET_SERVER_MessageTokenizerCallback cb, void *cb_cls); +uint16_t +GNUNET_TESTBED_host_get_ssh_port_ (const struct GNUNET_TESTBED_Host *host); + + +/** + * Opaque wrapper around GNUNET_HELPER_Handle + */ +struct GNUNET_TESTBED_HelperHandle; + + +/* /\** */ +/* * Run a given helper process at the given host. Communication */ +/* * with the helper will be via GNUnet messages on stdin/stdout. */ +/* * Runs the process via 'ssh' at the specified host, or locally. */ +/* * Essentially an SSH-wrapper around the 'gnunet_helper_lib.h' API. */ +/* * */ +/* * @param controller_ip the ip address of the controller. Will be set as TRUSTED */ +/* * host when starting testbed controller at host */ +/* * @param host host to use, use "NULL" for localhost */ +/* * @param binary_argv binary name and command-line arguments to give to the */ +/* * binary */ +/* * @param cfg template configuration to use for the remote controller; the */ +/* * remote controller will be started with a slightly modified */ +/* * configuration (port numbers, unix domain sockets and service home */ +/* * values are changed as per TESTING library on the remote host) */ +/* * @param cb the callback to run when helper process dies; cannot be NULL */ +/* * @param cb_cls the closure for the above callback */ +/* * @return handle to terminate the command, NULL on error */ +/* *\/ */ +/* struct GNUNET_TESTBED_HelperHandle * */ +/* GNUNET_TESTBED_host_run_ (const char *controller_ip, */ +/* const struct GNUNET_TESTBED_Host *host, */ +/* const struct GNUNET_CONFIGURATION_Handle *cfg, */ +/* GNUNET_HELPER_ExceptionCallback cb, */ +/* void *cb_cls); */ + + + +/* /\** */ +/* * Stops a helper in the HelperHandle using GNUNET_HELPER_stop */ +/* * */ +/* * @param handle the handle returned from GNUNET_TESTBED_host_start_ */ +/* *\/ */ +/* void */ +/* GNUNET_TESTBED_host_stop_ (struct GNUNET_TESTBED_HelperHandle *handle); */ + + +/** + * Marks a host as registered with a controller + * + * @param host the host to mark + * @param controller the controller at which this host is registered + */ +void +GNUNET_TESTBED_mark_host_registered_at_ (struct GNUNET_TESTBED_Host *host, + const struct GNUNET_TESTBED_Controller + *controller); + + +/** + * Checks whether a host has been registered with the given controller + * + * @param host the host to check + * @param controller the controller at which host's registration is checked + * @return GNUNET_YES if registered; GNUNET_NO if not + */ +int +GNUNET_TESTBED_is_host_registered_ (const struct GNUNET_TESTBED_Host *host, + const struct GNUNET_TESTBED_Controller + *controller); + + #endif /* end of testbed_api_hosts.h */ diff --git a/src/testbed/testbed_api_operations.c b/src/testbed/testbed_api_operations.c index c98998b..3e7eb91 100644 --- a/src/testbed/testbed_api_operations.c +++ b/src/testbed/testbed_api_operations.c @@ -28,6 +28,89 @@ /** + * An entry in the operation queue + */ +struct QueueEntry +{ + /** + * The next DLL pointer + */ + struct QueueEntry *next; + + /** + * The prev DLL pointer + */ + struct QueueEntry *prev; + + /** + * The operation this entry holds + */ + struct GNUNET_TESTBED_Operation *op; + + /** + * How many units of resources does the operation need + */ + unsigned int nres; +}; + + +/** + * Queue of operations where we can only support a certain + * number of concurrent operations of a particular type. + */ +struct OperationQueue +{ + /** + * The head of the operation queue + */ + struct QueueEntry *head; + + /** + * The tail of the operation queue + */ + struct QueueEntry *tail; + + /** + * Number of operations that are currently active in this queue. + */ + unsigned int active; + + /** + * Max number of operations which can be active at any time in this queue + */ + unsigned int max_active; + +}; + + +/** + * Operation state + */ +enum OperationState +{ + /** + * The operation is just created and is in initial state + */ + OP_STATE_INIT, + + /** + * The operation is currently waiting for resources + */ + OP_STATE_WAITING, + + /** + * The operation is ready to be started + */ + OP_STATE_READY, + + /** + * The operation has started + */ + OP_STATE_STARTED +}; + + +/** * Opaque handle to an abstract operation to be executed by the testing framework. */ struct GNUNET_TESTBED_Operation @@ -42,33 +125,125 @@ struct GNUNET_TESTBED_Operation * not have been started yet). */ OperationRelease release; - + /** * Closure for callbacks. */ void *cb_cls; - // FIXME! + /** + * Array of operation queues this Operation belongs to. + */ + struct OperationQueue **queues; + + /** + * Array of number resources an operation need from each queue. This numbers + * in this array should correspond to the queues array + */ + unsigned int *nres; + + /** + * The id of the task which calls OperationStart for this operation + */ + GNUNET_SCHEDULER_TaskIdentifier start_task_id; + + /** + * Number of queues in the operation queues array + */ + unsigned int nqueues; + + /** + * The state of the operation + */ + enum OperationState state; }; /** - * Queue of operations where we can only support a certain - * number of concurrent operations of a particular type. + * Task for calling OperationStart on operation + * + * @param cls the Operation + * @param tc the TaskContext from scheduler */ -struct OperationQueue +static void +call_start (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { + struct GNUNET_TESTBED_Operation *op = cls; - /** - * Maximum number of operationst that can be concurrently - * active in this queue. - */ - unsigned int max_active; + op->start_task_id = GNUNET_SCHEDULER_NO_TASK; + op->state = OP_STATE_STARTED; + if (NULL != op->start) + op->start (op->cb_cls); +} - // FIXME! -}; +/** + * Checks for the readiness of an operation and schedules a operation start task + * + * @param op the operation + */ +static void +check_readiness (struct GNUNET_TESTBED_Operation *op) +{ + unsigned int i; + + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == op->start_task_id); + for (i = 0; i < op->nqueues; i++) + { + GNUNET_assert (0 < op->nres[i]); + if ((op->queues[i]->active + op->nres[i]) > op->queues[i]->max_active) + return; + } + for (i = 0; i < op->nqueues; i++) + op->queues[i]->active += op->nres[i]; + op->state = OP_STATE_READY; + op->start_task_id = GNUNET_SCHEDULER_add_now (&call_start, op); +} + + +/** + * Defers a ready to be executed operation back to waiting + * + * @param op the operation to defer + */ +static void +defer (struct GNUNET_TESTBED_Operation *op) +{ + unsigned int i; + + GNUNET_assert (OP_STATE_READY == op->state); + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != op->start_task_id); + GNUNET_SCHEDULER_cancel (op->start_task_id); + op->start_task_id = GNUNET_SCHEDULER_NO_TASK; + for (i = 0; i < op->nqueues; i++) + op->queues[i]->active--; + op->state = OP_STATE_WAITING; +} + + +/** + * Create an 'operation' to be performed. + * + * @param cls closure for the callbacks + * @param start function to call to start the operation + * @param release function to call to close down the operation + * @return handle to the operation + */ +struct GNUNET_TESTBED_Operation * +GNUNET_TESTBED_operation_create_ (void *cls, OperationStart start, + OperationRelease release) +{ + struct GNUNET_TESTBED_Operation *op; + + op = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Operation)); + op->start = start; + op->state = OP_STATE_INIT; + op->release = release; + op->cb_cls = cls; + op->start_task_id = GNUNET_SCHEDULER_NO_TASK; + return op; +} /** @@ -98,97 +273,188 @@ GNUNET_TESTBED_operation_queue_create_ (unsigned int max_active) void GNUNET_TESTBED_operation_queue_destroy_ (struct OperationQueue *queue) { - GNUNET_break (0); + GNUNET_break (NULL == queue->head); + GNUNET_break (NULL == queue->tail); GNUNET_free (queue); } /** - * Add an operation to a queue. An operation can be in multiple - * queues at once. Once all queues permit the operation to become - * active, the operation will be activated. The actual activation - * will occur in a separate task (thus allowing multiple queue - * insertions to be made without having the first one instantly - * trigger the operation if the first queue has sufficient - * resources). + * Function to reset the maximum number of operations in the given queue. If + * max_active is lesser than the number of currently active operations, the + * active operations are not stopped immediately. + * + * @param queue the operation queue which has to be modified + * @param max_active the new maximum number of active operations + */ +void +GNUNET_TESTBED_operation_queue_reset_max_active_ (struct OperationQueue *queue, + unsigned int max_active) +{ + struct QueueEntry *entry; + + queue->max_active = max_active; + /* if (queue->active >= queue->max_active) */ + /* return; */ + + entry = queue->head; + while ((queue->active > queue->max_active) && (NULL != entry)) + { + if (entry->op->state == OP_STATE_READY) + defer (entry->op); + entry = entry->next; + } + + entry = queue->head; + while ((NULL != entry) && (queue->active < queue->max_active)) + { + if (OP_STATE_WAITING == entry->op->state) + check_readiness (entry->op); + entry = entry->next; + } +} + + +/** + * Add an operation to a queue. An operation can be in multiple queues at + * once. Once the operation is inserted into all the queues + * GNUNET_TESTBED_operation_begin_wait_() has to be called to actually start + * waiting for the operation to become active. * * @param queue queue to add the operation to * @param operation operation to add to the queue + * @param nres the number of units of the resources of queue needed by the + * operation. Should be greater than 0. */ void -GNUNET_TESTBED_operation_queue_insert_ (struct OperationQueue *queue, - struct GNUNET_TESTBED_Operation *operation) +GNUNET_TESTBED_operation_queue_insert2_ (struct OperationQueue *queue, + struct GNUNET_TESTBED_Operation + *operation, unsigned int nres) { - GNUNET_break (0); + struct QueueEntry *entry; + unsigned int qsize; + + GNUNET_assert (0 < nres); + entry = GNUNET_malloc (sizeof (struct QueueEntry)); + entry->op = operation; + entry->nres = nres; + GNUNET_CONTAINER_DLL_insert_tail (queue->head, queue->tail, entry); + qsize = operation->nqueues; + GNUNET_array_append (operation->queues, operation->nqueues, queue); + GNUNET_array_append (operation->nres, qsize, nres); + GNUNET_assert (qsize == operation->nqueues); } /** - * Remove an operation from a queue. This can be because the - * oeration was active and has completed (and the resources have - * been released), or because the operation was cancelled and - * thus scheduling the operation is no longer required. + * Add an operation to a queue. An operation can be in multiple queues at + * once. Once the operation is inserted into all the queues + * GNUNET_TESTBED_operation_begin_wait_() has to be called to actually start + * waiting for the operation to become active. The operation is assumed to take + * 1 queue resource. Use GNUNET_TESTBED_operation_queue_insert2_() if it + * requires more than 1 * * @param queue queue to add the operation to * @param operation operation to add to the queue */ void -GNUNET_TESTBED_operation_queue_remove_ (struct OperationQueue *queue, - struct GNUNET_TESTBED_Operation *operation) +GNUNET_TESTBED_operation_queue_insert_ (struct OperationQueue *queue, + struct GNUNET_TESTBED_Operation + *operation) { - GNUNET_break (0); + return GNUNET_TESTBED_operation_queue_insert2_ (queue, operation, 1); } /** - * An operation is 'done' (was cancelled or finished); remove - * it from the queues and release associated resources. + * Marks the given operation as waiting on the queues. Once all queues permit + * the operation to become active, the operation will be activated. The actual + * activation will occur in a separate task (thus allowing multiple queue + * insertions to be made without having the first one instantly trigger the + * operation if the first queue has sufficient resources). * - * @param operation operation that finished + * @param operation the operation to marks as waiting */ -static void -operation_release (struct GNUNET_TESTBED_Operation *operation) +void +GNUNET_TESTBED_operation_begin_wait_ (struct GNUNET_TESTBED_Operation + *operation) { - // call operation->release, remove from queues - GNUNET_break (0); + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == operation->start_task_id); + operation->state = OP_STATE_WAITING; + check_readiness (operation); } /** - * Cancel a pending operation. Releases all resources - * of the operation and will ensure that no event - * is generated for the operation. Does NOT guarantee - * that the operation will be fully undone (or that - * nothing ever happened). - * - * @param operation operation to cancel + * Remove an operation from a queue. This can be because the + * oeration was active and has completed (and the resources have + * been released), or because the operation was cancelled and + * thus scheduling the operation is no longer required. + * + * @param queue queue to add the operation to + * @param operation operation to add to the queue */ void -GNUNET_TESTBED_operation_cancel (struct GNUNET_TESTBED_Operation *operation) +GNUNET_TESTBED_operation_queue_remove_ (struct OperationQueue *queue, + struct GNUNET_TESTBED_Operation + *operation) { - // test that operation had not yet generated an event - GNUNET_break (0); - operation_release (operation); + struct QueueEntry *entry; + struct QueueEntry *entry2; + + for (entry = queue->head; NULL != entry; entry = entry->next) + if (entry->op == operation) + break; + GNUNET_assert (NULL != entry); + GNUNET_assert (0 < entry->nres); + switch (operation->state) + { + case OP_STATE_INIT: + case OP_STATE_WAITING: + break; + case OP_STATE_READY: + case OP_STATE_STARTED: + GNUNET_assert (0 != queue->active); + GNUNET_assert (queue->active >= entry->nres); + queue->active -= entry->nres; + break; + } + entry2 = entry->next; + GNUNET_CONTAINER_DLL_remove (queue->head, queue->tail, entry); + GNUNET_free (entry); + for (; NULL != entry2; entry2 = entry2->next) + if (OP_STATE_WAITING == entry2->op->state) + break; + if (NULL == entry2) + return; + check_readiness (entry2->op); } /** - * Signal that the information from an operation has been fully - * processed. This function MUST be called for each event - * of type 'operation_finished' to fully remove the operation - * from the operation queue. After calling this function, the - * 'op_result' becomes invalid (!). - * - * @param operation operation to signal completion for + * An operation is 'done' (was cancelled or finished); remove + * it from the queues and release associated resources. + * + * @param operation operation that finished */ void -GNUNET_TESTBED_operation_done (struct GNUNET_TESTBED_Operation *operation) +GNUNET_TESTBED_operation_release_ (struct GNUNET_TESTBED_Operation *operation) { - // test that operation was started and had generated an event - GNUNET_break (0); - operation_release (operation); + unsigned int i; + + if (GNUNET_SCHEDULER_NO_TASK != operation->start_task_id) + { + GNUNET_SCHEDULER_cancel (operation->start_task_id); + operation->start_task_id = GNUNET_SCHEDULER_NO_TASK; + } + for (i = 0; i < operation->nqueues; i++) + GNUNET_TESTBED_operation_queue_remove_ (operation->queues[i], operation); + GNUNET_free (operation->queues); + GNUNET_free (operation->nres); + if (NULL != operation->release) + operation->release (operation->cb_cls); + GNUNET_free (operation); } - /* end of testbed_api_operations.c */ diff --git a/src/testbed/testbed_api_operations.h b/src/testbed/testbed_api_operations.h index 4c888d5..61b45e2 100644 --- a/src/testbed/testbed_api_operations.h +++ b/src/testbed/testbed_api_operations.h @@ -59,20 +59,62 @@ GNUNET_TESTBED_operation_queue_destroy_ (struct OperationQueue *queue); /** - * Add an operation to a queue. An operation can be in multiple - * queues at once. Once all queues permit the operation to become - * active, the operation will be activated. The actual activation - * will occur in a separate task (thus allowing multiple queue - * insertions to be made without having the first one instantly - * trigger the operation if the first queue has sufficient - * resources). + * Function to reset the maximum number of operations in the given queue. If + * max_active is lesser than the number of currently active operations, the + * active operations are not stopped immediately. + * + * @param queue the operation queue which has to be modified + * @param max_active the new maximum number of active operations + */ +void +GNUNET_TESTBED_operation_queue_reset_max_active_ (struct OperationQueue *queue, + unsigned int max_active); + + +/** + * Add an operation to a queue. An operation can be in multiple queues at + * once. Once the operation is inserted into all the queues + * GNUNET_TESTBED_operation_begin_wait_() has to be called to actually start + * waiting for the operation to become active. + * + * @param queue queue to add the operation to + * @param operation operation to add to the queue + * @param nres the number of units of the resources of queue needed by the + * operation. Should be greater than 0. + */ +void +GNUNET_TESTBED_operation_queue_insert2_ (struct OperationQueue *queue, + struct GNUNET_TESTBED_Operation + *operation, unsigned int nres); + + +/** + * Add an operation to a queue. An operation can be in multiple queues at + * once. Once the operation is inserted into all the queues + * GNUNET_TESTBED_operation_begin_wait_() has to be called to actually start + * waiting for the operation to become active. * * @param queue queue to add the operation to * @param operation operation to add to the queue */ void GNUNET_TESTBED_operation_queue_insert_ (struct OperationQueue *queue, - struct GNUNET_TESTBED_Operation *operation); + struct GNUNET_TESTBED_Operation + *operation); + + +/** + * Marks the given operation as waiting on the queues. Once all queues permit + * the operation to become active, the operation will be activated. The actual + * activation will occur in a separate task (thus allowing multiple queue + * insertions to be made without having the first one instantly trigger the + * operation if the first queue has sufficient resources). + * + * @param operation the operation to marks as waiting + */ +void +GNUNET_TESTBED_operation_begin_wait_ (struct GNUNET_TESTBED_Operation + *operation); /** @@ -86,7 +128,8 @@ GNUNET_TESTBED_operation_queue_insert_ (struct OperationQueue *queue, */ void GNUNET_TESTBED_operation_queue_remove_ (struct OperationQueue *queue, - struct GNUNET_TESTBED_Operation *operation); + struct GNUNET_TESTBED_Operation + *operation); @@ -94,8 +137,10 @@ GNUNET_TESTBED_operation_queue_remove_ (struct OperationQueue *queue, * Function to call to start an operation once all * queues the operation is part of declare that the * operation can be activated. + * + * @param cls the closure from GNUNET_TESTBED_operation_create_() */ -typedef void (*OperationStart)(void *cls); +typedef void (*OperationStart) (void *cls); /** @@ -107,9 +152,11 @@ typedef void (*OperationStart)(void *cls); * a callback to the 'OperationStart' preceeds the call to * 'OperationRelease'. Implementations of this function are expected * to clean up whatever state is in 'cls' and release all resources - * associated with the operation. + * associated with the operation. + * + * @param cls the closure from GNUNET_TESTBED_operation_create_() */ -typedef void (*OperationRelease)(void *cls); +typedef void (*OperationRelease) (void *cls); /** @@ -118,14 +165,21 @@ typedef void (*OperationRelease)(void *cls); * @param cls closure for the callbacks * @param start function to call to start the operation * @param release function to call to close down the operation - * @param ... FIXME * @return handle to the operation */ struct GNUNET_TESTBED_Operation * -GNUNET_TESTBED_operation_create_ (void *cls, - OperationStart start, - OperationRelease release, - ...); +GNUNET_TESTBED_operation_create_ (void *cls, OperationStart start, + OperationRelease release); + + +/** + * An operation is 'done' (was cancelled or finished); remove + * it from the queues and release associated resources. + * + * @param operation operation that finished + */ +void +GNUNET_TESTBED_operation_release_ (struct GNUNET_TESTBED_Operation *operation); #endif diff --git a/src/testbed/testbed_api_peers.c b/src/testbed/testbed_api_peers.c index 7ee0dd1..908428a 100644 --- a/src/testbed/testbed_api_peers.c +++ b/src/testbed/testbed_api_peers.c @@ -23,118 +23,380 @@ * @brief management of the knowledge about peers in this library * (we know the peer ID, its host, pending operations, etc.) * @author Christian Grothoff + * @author Sree Harsha Totakura */ + #include "platform.h" #include "testbed_api_peers.h" +#include "testbed_api.h" +#include "testbed.h" +#include "testbed_api_hosts.h" +#include "testbed_api_operations.h" + +/** + * Function to call to start a peer_create type operation once all + * queues the operation is part of declare that the + * operation can be activated. + * + * @param cls the closure from GNUNET_TESTBED_operation_create_() + */ +static void +opstart_peer_create (void *cls) +{ + struct OperationContext *opc = cls; + struct PeerCreateData *data; + struct GNUNET_TESTBED_PeerCreateMessage *msg; + char *config; + char *xconfig; + size_t c_size; + size_t xc_size; + uint16_t msize; + + GNUNET_assert (OP_PEER_CREATE == opc->type); + data = opc->data; + GNUNET_assert (NULL != data); + GNUNET_assert (NULL != data->peer); + opc->state = OPC_STATE_STARTED; + config = GNUNET_CONFIGURATION_serialize (data->cfg, &c_size); + xc_size = GNUNET_TESTBED_compress_config_ (config, c_size, &xconfig); + GNUNET_free (config); + msize = xc_size + sizeof (struct GNUNET_TESTBED_PeerCreateMessage); + msg = GNUNET_realloc (xconfig, msize); + memmove (&msg[1], msg, xc_size); + msg->header.size = htons (msize); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_CREATE_PEER); + msg->operation_id = GNUNET_htonll (opc->id); + msg->host_id = htonl (GNUNET_TESTBED_host_get_id_ (data->peer->host)); + msg->peer_id = htonl (data->peer->unique_id); + msg->config_size = htonl (c_size); + GNUNET_CONTAINER_DLL_insert_tail (opc->c->ocq_head, opc->c->ocq_tail, opc); + GNUNET_TESTBED_queue_message_ (opc->c, &msg->header); +} /** - * Details about a peer; kept in a separate struct to avoid bloating - * memory consumption everywhere... + * Callback which will be called when peer_create type operation is released + * + * @param cls the closure from GNUNET_TESTBED_operation_create_() */ -struct PeerDetails +static void +oprelease_peer_create (void *cls) { - /** - * Configuration of the peer; NULL if we are not sure what the peer's correct - * configuration actually is; non-NULL if this peer is controlled by this - * process. - */ - struct GNUNET_CONFIGURATION_Handle *cfg; + struct OperationContext *opc = cls; + + switch (opc->state) + { + case OPC_STATE_STARTED: + GNUNET_CONTAINER_DLL_remove (opc->c->ocq_head, opc->c->ocq_tail, opc); + /* No break we continue flow */ + case OPC_STATE_INIT: + GNUNET_free (((struct PeerCreateData *) opc->data)->peer); + GNUNET_free (opc->data); + break; + case OPC_STATE_FINISHED: + break; + } + GNUNET_free (opc); +} - /** - * If this process has started this peer's ARM process, this is the handle - * to the 'gnunet-service-arm' process of the peer. - */ - struct GNUNET_OS_Process *arm; - - // ... -}; +/** + * Function called when a peer destroy operation is ready + * + * @param cls the closure from GNUNET_TESTBED_operation_create_() + */ +static void +opstart_peer_destroy (void *cls) +{ + struct OperationContext *opc = cls; + struct GNUNET_TESTBED_Peer *peer; + struct GNUNET_TESTBED_PeerDestroyMessage *msg; + + GNUNET_assert (OP_PEER_DESTROY == opc->type); + peer = opc->data; + GNUNET_assert (NULL != peer); + opc->state = OPC_STATE_STARTED; + msg = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_PeerDestroyMessage)); + msg->header.size = htons (sizeof (struct GNUNET_TESTBED_PeerDestroyMessage)); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_DESTROY_PEER); + msg->peer_id = htonl (peer->unique_id); + msg->operation_id = GNUNET_htonll (opc->id); + GNUNET_CONTAINER_DLL_insert_tail (opc->c->ocq_head, opc->c->ocq_tail, opc); + GNUNET_TESTBED_queue_message_ (peer->controller, &msg->header); +} /** - * A peer controlled by the testing framework. A peer runs - * at a particular host. - */ -struct GNUNET_TESTBED_Peer + * Callback which will be called when peer_create type operation is released + * + * @param cls the closure from GNUNET_TESTBED_operation_create_() + */ +static void +oprelease_peer_destroy (void *cls) { - /** - * Our controller context (not necessarily the controller - * that is responsible for starting/running the peer!). - */ - struct GNUNET_TESTBED_Controller *controller; - - /** - * Which host does this peer run on? - */ - struct GNUENT_TESTING_Host *host; - - /** - * Globally unique ID of the peer. - */ - uint32_t unique_id; - - /** - * Internals of the peer for the controlling process; NULL if - * this process is not controlling this peer. - */ - struct PeerDetails *details; - -}; + struct OperationContext *opc = cls; + + if (OPC_STATE_FINISHED != opc->state) + GNUNET_CONTAINER_DLL_remove (opc->c->ocq_head, opc->c->ocq_tail, opc); + GNUNET_free (opc); +} /** - * Lookup a peer by ID. - * - * @param id global peer ID assigned to the peer - * @return handle to the host, NULL on error + * Function called when a peer start operation is ready + * + * @param cls the closure from GNUNET_TESTBED_operation_create_() */ -struct GNUNET_TESTBED_Peer * -GNUNET_TESTBED_peer_lookup_by_id_ (uint32_t id) +static void +opstart_peer_start (void *cls) { - GNUNET_break (0); - return NULL; + struct OperationContext *opc = cls; + struct GNUNET_TESTBED_PeerStartMessage *msg; + struct PeerEventData *data; + struct GNUNET_TESTBED_Peer *peer; + + GNUNET_assert (OP_PEER_START == opc->type); + GNUNET_assert (NULL != opc->data); + data = opc->data; + GNUNET_assert (NULL != data->peer); + peer = data->peer; + GNUNET_assert ((PS_CREATED == peer->state) || (PS_STOPPED == peer->state)); + opc->state = OPC_STATE_STARTED; + msg = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_PeerStartMessage)); + msg->header.size = htons (sizeof (struct GNUNET_TESTBED_PeerStartMessage)); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_START_PEER); + msg->peer_id = htonl (peer->unique_id); + msg->operation_id = GNUNET_htonll (opc->id); + GNUNET_CONTAINER_DLL_insert_tail (opc->c->ocq_head, opc->c->ocq_tail, opc); + GNUNET_TESTBED_queue_message_ (peer->controller, &msg->header); } /** - * Create the given peer at the specified host using the given - * controller. If the given controller is not running on the target - * host, it should find or create a controller at the target host and - * delegate creating the peer. Explicit delegation paths can be setup - * using 'GNUNET_TESTBED_controller_link'. If no explicit delegation - * path exists, a direct link with a subordinate controller is setup - * for the first delegated peer to a particular host; the subordinate - * controller is then destroyed once the last peer that was delegated - * to the remote host is stopped. This function is used in particular - * if some other controller has already assigned a unique ID to the - * peer. + * Callback which will be called when peer start type operation is released * - * Creating the peer only creates the handle to manipulate and further - * configure the peer; use "GNUNET_TESTBED_peer_start" and - * "GNUNET_TESTBED_peer_stop" to actually start/stop the peer's - * processes. + * @param cls the closure from GNUNET_TESTBED_operation_create_() + */ +static void +oprelease_peer_start (void *cls) +{ + struct OperationContext *opc = cls; + + if (OPC_STATE_FINISHED != opc->state) + { + GNUNET_free (opc->data); + GNUNET_CONTAINER_DLL_remove (opc->c->ocq_head, opc->c->ocq_tail, opc); + } + GNUNET_free (opc); +} + + +/** + * Function called when a peer stop operation is ready * - * Note that the given configuration will be adjusted by the - * controller to avoid port/path conflicts with other peers. - * The "final" configuration can be obtained using - * 'GNUNET_TESTBED_peer_get_information'. + * @param cls the closure from GNUNET_TESTBED_operation_create_() + */ +static void +opstart_peer_stop (void *cls) +{ + struct OperationContext *opc = cls; + struct GNUNET_TESTBED_PeerStopMessage *msg; + struct PeerEventData *data; + struct GNUNET_TESTBED_Peer *peer; + + GNUNET_assert (NULL != opc->data); + data = opc->data; + GNUNET_assert (NULL != data->peer); + peer = data->peer; + GNUNET_assert (PS_STARTED == peer->state); + opc->state = OPC_STATE_STARTED; + msg = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_PeerStopMessage)); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_STOP_PEER); + msg->header.size = htons (sizeof (struct GNUNET_TESTBED_PeerStopMessage)); + msg->peer_id = htonl (peer->unique_id); + msg->operation_id = GNUNET_htonll (opc->id); + GNUNET_CONTAINER_DLL_insert_tail (opc->c->ocq_head, opc->c->ocq_tail, opc); + GNUNET_TESTBED_queue_message_ (peer->controller, &msg->header); +} + + +/** + * Callback which will be called when peer stop type operation is released * - * @param unique_id unique ID for this peer - * @param controller controller process to use - * @param host host to run the peer on - * @param cfg configuration to use for the peer - * @return handle to the peer (actual startup will happen asynchronously) + * @param cls the closure from GNUNET_TESTBED_operation_create_() + */ +static void +oprelease_peer_stop (void *cls) +{ + struct OperationContext *opc = cls; + + if (OPC_STATE_FINISHED != opc->state) + { + GNUNET_free (opc->data); + GNUNET_CONTAINER_DLL_remove (opc->c->ocq_head, opc->c->ocq_tail, opc); + } + GNUNET_free (opc); +} + + +/** + * Generate PeerGetConfigurationMessage + * + * @param peer_id the id of the peer whose information we have to get + * @param operation_id the ip of the operation that should be represented in the + * message + * @return the PeerGetConfigurationMessage + */ +struct GNUNET_TESTBED_PeerGetConfigurationMessage * +GNUNET_TESTBED_generate_peergetconfig_msg_ (uint32_t peer_id, + uint64_t operation_id) +{ + struct GNUNET_TESTBED_PeerGetConfigurationMessage *msg; + + msg = + GNUNET_malloc (sizeof + (struct GNUNET_TESTBED_PeerGetConfigurationMessage)); + msg->header.size = + htons (sizeof (struct GNUNET_TESTBED_PeerGetConfigurationMessage)); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_GET_PEER_CONFIGURATION); + msg->peer_id = htonl (peer_id); + msg->operation_id = GNUNET_htonll (operation_id); + return msg; +} + + +/** + * Function called when a peer get information operation is ready + * + * @param cls the closure from GNUNET_TESTBED_operation_create_() + */ +static void +opstart_peer_getinfo (void *cls) +{ + struct OperationContext *opc = cls; + struct PeerInfoData *data; + struct GNUNET_TESTBED_PeerGetConfigurationMessage *msg; + + data = opc->data; + GNUNET_assert (NULL != data); + opc->state = OPC_STATE_STARTED; + msg = + GNUNET_TESTBED_generate_peergetconfig_msg_ (data->peer->unique_id, + opc->id); + GNUNET_CONTAINER_DLL_insert_tail (opc->c->ocq_head, opc->c->ocq_tail, opc); + GNUNET_TESTBED_queue_message_ (opc->c, &msg->header); +} + + +/** + * Callback which will be called when peer stop type operation is released + * + * @param cls the closure from GNUNET_TESTBED_operation_create_() + */ +static void +oprelease_peer_getinfo (void *cls) +{ + struct OperationContext *opc = cls; + struct GNUNET_TESTBED_PeerInformation *data; + + if (OPC_STATE_FINISHED != opc->state) + { + GNUNET_free_non_null (opc->data); + GNUNET_CONTAINER_DLL_remove (opc->c->ocq_head, opc->c->ocq_tail, opc); + } + else + { + data = opc->data; + GNUNET_assert (NULL != data); + switch (data->pit) + { + case GNUNET_TESTBED_PIT_CONFIGURATION: + GNUNET_CONFIGURATION_destroy (data->result.cfg); + break; + case GNUNET_TESTBED_PIT_IDENTITY: + GNUNET_free (data->result.id); + break; + default: + GNUNET_assert (0); /* We should never reach here */ + } + GNUNET_free (data); + } + GNUNET_free (opc); +} + + +/** + * Function called when a overlay connect operation is ready + * + * @param cls the closure from GNUNET_TESTBED_operation_create_() + */ +static void +opstart_overlay_connect (void *cls) +{ + struct OperationContext *opc = cls; + struct GNUNET_TESTBED_OverlayConnectMessage *msg; + struct OverlayConnectData *data; + + opc->state = OPC_STATE_STARTED; + data = opc->data; + GNUNET_assert (NULL != data); + data->tslot_index = GNUNET_TESTBED_get_tslot_ (opc->c, data); + data->tstart = GNUNET_TIME_absolute_get (); + msg = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_OverlayConnectMessage)); + msg->header.size = + htons (sizeof (struct GNUNET_TESTBED_OverlayConnectMessage)); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_OVERLAY_CONNECT); + msg->peer1 = htonl (data->p1->unique_id); + msg->peer2 = htonl (data->p2->unique_id); + msg->operation_id = GNUNET_htonll (opc->id); + msg->peer2_host_id = htonl (GNUNET_TESTBED_host_get_id_ (data->p2->host)); + GNUNET_CONTAINER_DLL_insert_tail (opc->c->ocq_head, opc->c->ocq_tail, opc); + GNUNET_TESTBED_queue_message_ (opc->c, &msg->header); +} + + +/** + * Callback which will be called when overlay connect operation is released + * + * @param cls the closure from GNUNET_TESTBED_operation_create_() + */ +static void +oprelease_overlay_connect (void *cls) +{ + struct OperationContext *opc = cls; + struct GNUNET_TIME_Relative duration; + struct OverlayConnectData *data; + + data = opc->data; + switch (opc->state) + { + case OPC_STATE_INIT: + break; + case OPC_STATE_STARTED: + (void) GNUNET_TESTBED_release_time_slot_ (opc->c, data->tslot_index, data); + GNUNET_CONTAINER_DLL_remove (opc->c->ocq_head, opc->c->ocq_tail, opc); + break; + case OPC_STATE_FINISHED: + duration = GNUNET_TIME_absolute_get_duration (data->tstart); + GNUNET_TESTBED_update_time_slot_ (opc->c, data->tslot_index, data, duration, + data->failed); + } + GNUNET_free (data); + GNUNET_free (opc); +} + + +/** + * Lookup a peer by ID. + * + * @param id global peer ID assigned to the peer + * @return handle to the host, NULL on error */ struct GNUNET_TESTBED_Peer * -GNUNET_TESTBED_peer_create_with_id_ (uint32_t unique_id, - struct GNUNET_TESTBED_Controller *controller, - struct GNUNET_TESTBED_Host *host, - const struct GNUNET_CONFIGURATION_Handle *cfg) +GNUNET_TESTBED_peer_lookup_by_id_ (uint32_t id) { - // FIXME: create locally or delegate... GNUNET_break (0); - return NULL; + return NULL; } @@ -160,70 +422,164 @@ GNUNET_TESTBED_peer_create_with_id_ (uint32_t unique_id, * 'GNUNET_TESTBED_peer_get_information'. * * @param controller controller process to use - * @param host host to run the peer on - * @param cfg configuration to use for the peer - * @return handle to the peer (actual startup will happen asynchronously) + * @param host host to run the peer on; cannot be NULL + * @param cfg Template configuration to use for the peer. Should exist until + * operation is cancelled or GNUNET_TESTBED_operation_done() is called + * @param cb the callback to call when the peer has been created + * @param cls the closure to the above callback + * @return the operation handle */ -struct GNUNET_TESTBED_Peer * +struct GNUNET_TESTBED_Operation * GNUNET_TESTBED_peer_create (struct GNUNET_TESTBED_Controller *controller, - struct GNUNET_TESTBED_Host *host, - const struct GNUNET_CONFIGURATION_Handle *cfg) + struct GNUNET_TESTBED_Host *host, + const struct GNUNET_CONFIGURATION_Handle *cfg, + GNUNET_TESTBED_PeerCreateCallback cb, void *cls) { + + struct GNUNET_TESTBED_Peer *peer; + struct PeerCreateData *data; + struct OperationContext *opc; static uint32_t id_gen; - return GNUNET_TESTBED_peer_create_with_id_ (++id_gen, - controller, - host, - cfg); + peer = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Peer)); + peer->controller = controller; + peer->host = host; + peer->unique_id = id_gen++; + peer->state = PS_INVALID; + data = GNUNET_malloc (sizeof (struct PeerCreateData)); + data->host = host; + data->cfg = cfg; + data->cb = cb; + data->cls = cls; + data->peer = peer; + opc = GNUNET_malloc (sizeof (struct OperationContext)); + opc->c = controller; + opc->data = data; + opc->id = GNUNET_TESTBED_get_next_op_id (controller); + opc->type = OP_PEER_CREATE; + opc->op = + GNUNET_TESTBED_operation_create_ (opc, &opstart_peer_create, + &oprelease_peer_create); + GNUNET_TESTBED_operation_queue_insert_ (controller->opq_parallel_operations, + opc->op); + GNUNET_TESTBED_operation_begin_wait_ (opc->op); + return opc->op; } /** * Start the given peer. * + * @param op_cls the closure for this operation; will be set in + * event->details.operation_finished.op_cls when this operation fails. * @param peer peer to start + * @param pcc function to call upon completion + * @param pcc_cls closure for 'pcc' * @return handle to the operation */ struct GNUNET_TESTBED_Operation * -GNUNET_TESTBED_peer_start (struct GNUNET_TESTBED_Peer *peer) +GNUNET_TESTBED_peer_start (void *op_cls, struct GNUNET_TESTBED_Peer *peer, + GNUNET_TESTBED_PeerChurnCallback pcc, void *pcc_cls) { - // FIXME: start locally or delegate... - GNUNET_break (0); - return NULL; + struct OperationContext *opc; + struct PeerEventData *data; + + data = GNUNET_malloc (sizeof (struct PeerEventData)); + data->peer = peer; + data->pcc = pcc; + data->pcc_cls = pcc_cls; + opc = GNUNET_malloc (sizeof (struct OperationContext)); + opc->c = peer->controller; + opc->data = data; + opc->op_cls = op_cls; + opc->id = GNUNET_TESTBED_get_next_op_id (opc->c); + opc->type = OP_PEER_START; + opc->op = + GNUNET_TESTBED_operation_create_ (opc, &opstart_peer_start, + &oprelease_peer_start); + GNUNET_TESTBED_operation_queue_insert_ (opc->c->opq_parallel_operations, + opc->op); + GNUNET_TESTBED_operation_begin_wait_ (opc->op); + return opc->op; } /** * Stop the given peer. The handle remains valid (use - * "GNUNET_TESTBED_peer_destroy" to fully clean up the + * "GNUNET_TESTBED_peer_destroy" to fully clean up the * state of the peer). * * @param peer peer to stop + * @param pcc function to call upon completion + * @param pcc_cls closure for 'pcc' * @return handle to the operation */ struct GNUNET_TESTBED_Operation * -GNUNET_TESTBED_peer_stop (struct GNUNET_TESTBED_Peer *peer) +GNUNET_TESTBED_peer_stop (struct GNUNET_TESTBED_Peer *peer, + GNUNET_TESTBED_PeerChurnCallback pcc, void *pcc_cls) { - // FIXME: stop locally or delegate... - GNUNET_break (0); - return NULL; + struct OperationContext *opc; + struct PeerEventData *data; + + data = GNUNET_malloc (sizeof (struct PeerEventData)); + data->peer = peer; + data->pcc = pcc; + data->pcc_cls = pcc_cls; + opc = GNUNET_malloc (sizeof (struct OperationContext)); + opc->c = peer->controller; + opc->data = data; + opc->id = GNUNET_TESTBED_get_next_op_id (opc->c); + opc->type = OP_PEER_STOP; + opc->op = + GNUNET_TESTBED_operation_create_ (opc, &opstart_peer_stop, + &oprelease_peer_stop); + GNUNET_TESTBED_operation_queue_insert_ (opc->c->opq_parallel_operations, + opc->op); + GNUNET_TESTBED_operation_begin_wait_ (opc->op); + return opc->op; } /** - * Request information about a peer. + * Request information about a peer. The controller callback will not be called + * with event type GNUNET_TESTBED_ET_OPERATION_FINISHED when result for this + * operation is available. Instead, the GNUNET_TESTBED_PeerInfoCallback() will + * be called. * * @param peer peer to request information about * @param pit desired information + * @param cb the convenience callback to be called when results for this + * operation are available + * @param cb_cls the closure for the above callback * @return handle to the operation */ struct GNUNET_TESTBED_Operation * GNUNET_TESTBED_peer_get_information (struct GNUNET_TESTBED_Peer *peer, - enum GNUNET_TESTBED_PeerInformationType pit) + enum GNUNET_TESTBED_PeerInformationType + pit, GNUNET_TESTBED_PeerInfoCallback cb, + void *cb_cls) { - // FIXME: handle locally or delegate... - GNUNET_break (0); - return NULL; + struct OperationContext *opc; + struct PeerInfoData *data; + + GNUNET_assert (GNUNET_TESTBED_PIT_GENERIC != pit); + data = GNUNET_malloc (sizeof (struct PeerInfoData)); + data->peer = peer; + data->pit = pit; + data->cb = cb; + data->cb_cls = cb_cls; + opc = GNUNET_malloc (sizeof (struct OperationContext)); + opc->c = peer->controller; + opc->data = data; + opc->type = OP_PEER_INFO; + opc->id = GNUNET_TESTBED_get_next_op_id (opc->c); + opc->op = + GNUNET_TESTBED_operation_create_ (opc, &opstart_peer_getinfo, + &oprelease_peer_getinfo); + GNUNET_TESTBED_operation_queue_insert_ (opc->c->opq_parallel_operations, + opc->op); + GNUNET_TESTBED_operation_begin_wait_ (opc->op); + return opc->op; } @@ -239,7 +595,8 @@ GNUNET_TESTBED_peer_get_information (struct GNUNET_TESTBED_Peer *peer, */ struct GNUNET_TESTBED_Operation * GNUNET_TESTBED_peer_update_configuration (struct GNUNET_TESTBED_Peer *peer, - const struct GNUNET_CONFIGURATION_Handle *cfg) + const struct + GNUNET_CONFIGURATION_Handle *cfg) { // FIXME: handle locally or delegate... GNUNET_break (0); @@ -248,8 +605,35 @@ GNUNET_TESTBED_peer_update_configuration (struct GNUNET_TESTBED_Peer *peer, /** + * Destroy the given peer; the peer should have been + * stopped first (if it was started). + * + * @param peer peer to stop + * @return handle to the operation + */ +struct GNUNET_TESTBED_Operation * +GNUNET_TESTBED_peer_destroy (struct GNUNET_TESTBED_Peer *peer) +{ + struct OperationContext *opc; + + opc = GNUNET_malloc (sizeof (struct OperationContext)); + opc->data = peer; + opc->c = peer->controller; + opc->id = GNUNET_TESTBED_get_next_op_id (peer->controller); + opc->type = OP_PEER_DESTROY; + opc->op = + GNUNET_TESTBED_operation_create_ (opc, &opstart_peer_destroy, + &oprelease_peer_destroy); + GNUNET_TESTBED_operation_queue_insert_ (opc->c->opq_parallel_operations, + opc->op); + GNUNET_TESTBED_operation_begin_wait_ (opc->op); + return opc->op; +} + + +/** * Manipulate the P2P underlay topology by configuring a link - * between two peers. + * between two peers. * * @param op_cls closure argument to give with the operation event * @param p1 first peer @@ -261,22 +645,24 @@ GNUNET_TESTBED_peer_update_configuration (struct GNUNET_TESTBED_Peer *peer, */ struct GNUNET_TESTBED_Operation * GNUNET_TESTBED_underlay_configure_link (void *op_cls, - struct GNUNET_TESTBED_Peer *p1, - struct GNUNET_TESTBED_Peer *p2, - enum GNUNET_TESTBED_ConnectOption co, ...) + struct GNUNET_TESTBED_Peer *p1, + struct GNUNET_TESTBED_Peer *p2, + enum GNUNET_TESTBED_ConnectOption co, + ...) { GNUNET_break (0); return NULL; } - /** * Both peers must have been started before calling this function. * This function then obtains a HELLO from 'p1', gives it to 'p2' * and asks 'p2' to connect to 'p1'. * * @param op_cls closure argument to give with the operation event + * @param cb the callback to call when this operation has finished + * @param cb_cls the closure for the above callback * @param p1 first peer * @param p2 second peer * @return handle to the operation, NULL if connecting these two @@ -285,11 +671,33 @@ GNUNET_TESTBED_underlay_configure_link (void *op_cls, */ struct GNUNET_TESTBED_Operation * GNUNET_TESTBED_overlay_connect (void *op_cls, - struct GNUNET_TESTBED_Peer *p1, - struct GNUNET_TESTBED_Peer *p2) + GNUNET_TESTBED_OperationCompletionCallback cb, + void *cb_cls, struct GNUNET_TESTBED_Peer *p1, + struct GNUNET_TESTBED_Peer *p2) { - GNUNET_break (0); - return NULL; + struct OperationContext *opc; + struct OverlayConnectData *data; + + GNUNET_assert ((PS_STARTED == p1->state) && (PS_STARTED == p2->state)); + data = GNUNET_malloc (sizeof (struct OverlayConnectData)); + data->p1 = p1; + data->p2 = p2; + data->cb = cb; + data->cb_cls = cb_cls; + opc = GNUNET_malloc (sizeof (struct OperationContext)); + opc->data = data; + opc->c = p1->controller; + opc->id = GNUNET_TESTBED_get_next_op_id (opc->c); + opc->type = OP_OVERLAY_CONNECT; + opc->op_cls = op_cls; + opc->op = + GNUNET_TESTBED_operation_create_ (opc, &opstart_overlay_connect, + &oprelease_overlay_connect); + GNUNET_TESTBED_operation_queue_insert_ (opc-> + c->opq_parallel_overlay_connect_operations, + opc->op); + GNUNET_TESTBED_operation_begin_wait_ (opc->op); + return opc->op; } diff --git a/src/testbed/testbed_api_peers.h b/src/testbed/testbed_api_peers.h index ea42c98..8598cc1 100644 --- a/src/testbed/testbed_api_peers.h +++ b/src/testbed/testbed_api_peers.h @@ -22,7 +22,9 @@ * @file testbed/testbed_api_peers.h * @brief internal API to access the 'peers' subsystem * @author Christian Grothoff + * @author Sree Harsha Totakura */ + #ifndef NEW_TESTING_API_PEERS_H #define NEW_TESTING_API_PEERS_H @@ -31,41 +33,220 @@ /** - * Create the given peer at the specified host using the given - * controller. If the given controller is not running on the target - * host, it should find or create a controller at the target host and - * delegate creating the peer. Explicit delegation paths can be setup - * using 'GNUNET_TESTBED_controller_link'. If no explicit delegation - * path exists, a direct link with a subordinate controller is setup - * for the first delegated peer to a particular host; the subordinate - * controller is then destroyed once the last peer that was delegated - * to the remote host is stopped. This function is used in particular - * if some other controller has already assigned a unique ID to the - * peer. - * - * Creating the peer only creates the handle to manipulate and further - * configure the peer; use "GNUNET_TESTBED_peer_start" and - * "GNUNET_TESTBED_peer_stop" to actually start/stop the peer's - * processes. - * - * Note that the given configuration will be adjusted by the - * controller to avoid port/path conflicts with other peers. - * The "final" configuration can be obtained using - * 'GNUNET_TESTBED_peer_get_information'. - * - * @param unique_id unique ID for this peer - * @param controller controller process to use - * @param host host to run the peer on - * @param cfg configuration to use for the peer - * @return handle to the peer (actual startup will happen asynchronously) + * Enumeration of possible states a peer could be in + */ +enum PeerState +{ + /** + * State to signify that this peer is invalid + */ + PS_INVALID, + + /** + * The peer has been created + */ + PS_CREATED, + + /** + * The peer is running + */ + PS_STARTED, + + /** + * The peer is stopped + */ + PS_STOPPED, +}; + + +/** + * A peer controlled by the testing framework. A peer runs + * at a particular host. + */ +struct GNUNET_TESTBED_Peer +{ + /** + * Our controller context (not necessarily the controller + * that is responsible for starting/running the peer!). + */ + struct GNUNET_TESTBED_Controller *controller; + + /** + * Which host does this peer run on? + */ + struct GNUNET_TESTBED_Host *host; + + /** + * Globally unique ID of the peer. + */ + uint32_t unique_id; + + /** + * Peer's state + */ + enum PeerState state; +}; + + +/** + * Data for the OperationType OP_PEER_CREATE + */ +struct PeerCreateData +{ + /** + * The host where the peer has to be created + */ + struct GNUNET_TESTBED_Host *host; + + /** + * The template configuration of the peer + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * The call back to call when we receive peer create success message + */ + GNUNET_TESTBED_PeerCreateCallback cb; + + /** + * The closure for the above callback + */ + void *cls; + + /** + * The peer structure to return when we get success message + */ + struct GNUNET_TESTBED_Peer *peer; + +}; + + +/** + * Data for OperationType OP_PEER_START and OP_PEER_STOP + */ +struct PeerEventData +{ + /** + * The handle of the peer to start + */ + struct GNUNET_TESTBED_Peer *peer; + + /** + * The Peer churn callback to call when this operation is completed + */ + GNUNET_TESTBED_PeerChurnCallback pcc; + + /** + * Closure for the above callback + */ + void *pcc_cls; + +}; + + +/** + * Data for the OperationType OP_PEER_DESTROY; */ -struct GNUNET_TESTBED_Peer * -GNUNET_TESTBED_peer_create_with_id_ (uint32_t unique_id, - struct GNUNET_TESTBED_Controller *controller, - struct GNUNET_TESTBED_Host *host, - const struct GNUNET_CONFIGURATION_Handle *cfg); +struct PeerDestroyData +{ + /** + * The peer structure + */ + struct GNUNET_TESTBED_Peer *peer; + + //PEERDESTROYDATA +}; +/** + * Data for the OperationType OP_PEER_INFO + */ +struct PeerInfoData +{ + /** + * The peer whose information has been requested + */ + struct GNUNET_TESTBED_Peer *peer; + + /** + * The Peer info callback to call when this operation has completed + */ + GNUNET_TESTBED_PeerInfoCallback cb; + + /** + * The closure for peer info callback + */ + void *cb_cls; + + /** + * The type of peer information requested + */ + enum GNUNET_TESTBED_PeerInformationType pit; +}; + + +/** + * Data structure for OperationType OP_OVERLAY_CONNECT + */ +struct OverlayConnectData +{ + + /** + * Peer A to connect to peer B + */ + struct GNUNET_TESTBED_Peer *p1; + + /** + * Peer B + */ + struct GNUNET_TESTBED_Peer *p2; + + /** + * The operation completion callback to call once this operation is done + */ + GNUNET_TESTBED_OperationCompletionCallback cb; + + /** + * The closure for the above callback + */ + void *cb_cls; + + /** + * OperationContext for forwarded operations generated when peer1's controller doesn't have the + * configuration of peer2's controller for linking laterally to attemp an + * overlay connection between peer 1 and peer 2. + */ + struct OperationContext *sub_opc; + + /** + * The starting time of this operation + */ + struct GNUNET_TIME_Absolute tstart; + + /** + * Has this operation failed + */ + int failed; + + /** + * The timing slot index for this operation + */ + unsigned int tslot_index; + +}; + + +/** + * Generate PeerGetConfigurationMessage + * + * @param peer_id the id of the peer whose information we have to get + * @param operation_id the ip of the operation that should be represented in + * the message + * @return the PeerGetConfigurationMessage + */ +struct GNUNET_TESTBED_PeerGetConfigurationMessage * +GNUNET_TESTBED_generate_peergetconfig_msg_ (uint32_t peer_id, + uint64_t operation_id); #endif /* end of testbed_api_peers.h */ diff --git a/src/testbed/testbed_api_services.c b/src/testbed/testbed_api_services.c index 34de0fd..923caed 100644 --- a/src/testbed/testbed_api_services.c +++ b/src/testbed/testbed_api_services.c @@ -24,7 +24,211 @@ * @author Christian Grothoff */ #include "platform.h" +#include "testbed_api.h" #include "testbed_api_peers.h" +#include "testbed_api_operations.h" + + +/** + * States for Service connect operations + */ +enum State +{ + /** + * Initial state + */ + INIT, + + /** + * The configuration request has been sent + */ + CFG_REQUEST_QUEUED, + + /** + * connected to service + */ + SERVICE_CONNECTED +}; + + +/** + * Data accessed during service connections + */ +struct ServiceConnectData +{ + /** + * helper function callback to establish the connection + */ + GNUNET_TESTBED_ConnectAdapter ca; + + /** + * helper function callback to close the connection + */ + GNUNET_TESTBED_DisconnectAdapter da; + + /** + * Closure to the above callbacks + */ + void *cada_cls; + + /** + * Service name + */ + char *service_name; + + /** + * Closure for operation event + */ + void *op_cls; + + /** + * The operation which created this structure + */ + struct GNUNET_TESTBED_Operation *operation; + + /** + * The operation context from GNUNET_TESTBED_forward_operation_msg_() + */ + struct OperationContext *opc; + + /** + * The peer handle + */ + struct GNUNET_TESTBED_Peer *peer; + + /** + * The acquired configuration of the peer + */ + struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * The op_result pointer from ConnectAdapter + */ + void *op_result; + + /** + * The operation completion callback + */ + GNUNET_TESTBED_ServiceConnectCompletionCallback cb; + + /** + * The closure for operation completion callback + */ + void *cb_cls; + + /** + * State information + */ + enum State state; + +}; + + +/** + * Type of a function to call when we receive a message + * from the service. + * + * @param cls ServiceConnectData + * @param msg message received, NULL on timeout or fatal error + */ +static void +configuration_receiver (void *cls, const struct GNUNET_MessageHeader *msg) +{ + struct ServiceConnectData *data = cls; + struct GNUNET_TESTBED_Controller *c; + const char *emsg; + struct GNUNET_TESTBED_EventInformation info; + uint16_t mtype; + + c = data->peer->controller; + mtype = ntohs (msg->type); + emsg = NULL; + info.type = GNUNET_TESTBED_ET_OPERATION_FINISHED; + info.details.operation_finished.operation = data->operation; + info.details.operation_finished.op_cls = data->op_cls; + if (GNUNET_MESSAGE_TYPE_TESTBED_OPERATION_FAIL_EVENT == mtype) + { + emsg = + GNUNET_TESTBED_parse_error_string_ ((const struct + GNUNET_TESTBED_OperationFailureEventMessage + *) msg); + if (NULL == emsg) + emsg = "Unknown error"; + info.details.operation_finished.emsg = emsg; + info.details.operation_finished.generic = NULL; + goto call_cb; + } + data->cfg = GNUNET_TESTBED_extract_config_ (msg); + GNUNET_assert (NULL == data->op_result); + data->op_result = data->ca (data->cada_cls, data->cfg); + info.details.operation_finished.emsg = NULL; + info.details.operation_finished.generic = data->op_result; + data->state = SERVICE_CONNECTED; + +call_cb: + if ((0 != (GNUNET_TESTBED_ET_OPERATION_FINISHED & c->event_mask)) && + (NULL != c->cc)) + c->cc (c->cc_cls, &info); + if (NULL != data->cb) + data->cb (data->cb_cls, data->operation, data->op_result, emsg); +} + + +/** + * Function called when a service connect operation is ready + * + * @param cls the closure from GNUNET_TESTBED_operation_create_() + */ +static void +opstart_service_connect (void *cls) +{ + struct ServiceConnectData *data = cls; + struct GNUNET_TESTBED_PeerGetConfigurationMessage *msg; + struct GNUNET_TESTBED_Controller *c; + uint64_t op_id; + + GNUNET_assert (NULL != data); + GNUNET_assert (NULL != data->peer); + c = data->peer->controller; + op_id = GNUNET_TESTBED_get_next_op_id (c); + msg = + GNUNET_TESTBED_generate_peergetconfig_msg_ (data->peer->unique_id, op_id); + data->opc = + GNUNET_TESTBED_forward_operation_msg_ (c, op_id, &msg->header, + &configuration_receiver, data); + GNUNET_free (msg); + data->state = CFG_REQUEST_QUEUED; +} + + +/** + * Callback which will be called when service connect type operation is + * released + * + * @param cls the closure from GNUNET_TESTBED_operation_create_() + */ +static void +oprelease_service_connect (void *cls) +{ + struct ServiceConnectData *data = cls; + + switch (data->state) + { + case INIT: + break; + case CFG_REQUEST_QUEUED: + GNUNET_assert (NULL != data->opc); + GNUNET_TESTBED_forward_operation_msg_cancel_ (data->opc); + break; + case SERVICE_CONNECTED: + GNUNET_assert (NULL != data->cfg); + GNUNET_CONFIGURATION_destroy (data->cfg); + if (NULL != data->da) + data->da (data->cada_cls, data->op_result); + break; + } + GNUNET_free (data); +} /** @@ -33,7 +237,7 @@ * maintain connections with other systems. The actual service * handle is then returned via the 'op_result' member in the event * callback. The 'ca' callback is used to create the connection - * when the time is right; the 'da' callback will be used to + * when the time is right; the 'da' callback will be used to * destroy the connection (upon 'GNUNET_TESTBED_operation_done'). * 'GNUNET_TESTBED_operation_cancel' can be used to abort this * operation until the event callback has been called. @@ -41,21 +245,44 @@ * @param op_cls closure to pass in operation event * @param peer peer that runs the service * @param service_name name of the service to connect to + * @param cb the callback to call when this operation finishes + * @param cb_cls closure for the above callback * @param ca helper function to establish the connection * @param da helper function to close the connection * @param cada_cls closure for ca and da * @return handle for the operation */ struct GNUNET_TESTBED_Operation * -GNUNET_TESTBED_service_connect (void *op_cls, - struct GNUNET_TESTBED_Peer *peer, - const char *service_name, - GNUNET_TESTBED_ConnectAdapter ca, - GNUNET_TESTBED_DisconnectAdapter da, - void *cada_cls) +GNUNET_TESTBED_service_connect (void *op_cls, struct GNUNET_TESTBED_Peer *peer, + const char *service_name, + GNUNET_TESTBED_ServiceConnectCompletionCallback + cb, void *cb_cls, + GNUNET_TESTBED_ConnectAdapter ca, + GNUNET_TESTBED_DisconnectAdapter da, + void *cada_cls) { - GNUNET_break (0); - return NULL; + struct ServiceConnectData *data; + + data = GNUNET_malloc (sizeof (struct ServiceConnectData)); + data->ca = ca; + data->da = da; + data->cada_cls = cada_cls; + data->op_cls = op_cls; + data->peer = peer; + data->state = INIT; + data->cb = cb; + data->cb_cls = cb_cls; + data->operation = + GNUNET_TESTBED_operation_create_ (data, &opstart_service_connect, + &oprelease_service_connect); + GNUNET_TESTBED_operation_queue_insert_ (peer-> + controller->opq_parallel_service_connections, + data->operation); + GNUNET_TESTBED_operation_queue_insert_ (peer-> + controller->opq_parallel_operations, + data->operation); + GNUNET_TESTBED_operation_begin_wait_ (data->operation); + return data->operation; } /* end of testbed_api_services.c */ diff --git a/src/testbed/testbed_api_statistics.c b/src/testbed/testbed_api_statistics.c new file mode 100644 index 0000000..f013c03 --- /dev/null +++ b/src/testbed/testbed_api_statistics.c @@ -0,0 +1,56 @@ +/* + This file is part of GNUnet + (C) 2008--2012 Christian Grothoff (and other contributing authors) + + GNUnet 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. + + 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file testbed/testbed_api_statistics.c + * @brief high-level statistics function + * @author Christian Grothoff + * @author Sree Harsha Totakura + */ +#include "platform.h" +#include "gnunet_testbed_service.h" + + +/** + * Convenience method that iterates over all (running) peers + * and retrieves all statistics from each peer. + * + * @param num_peers number of peers to iterate over + * @param peers array of peers to iterate over + * @param proc processing function for each statistic retrieved + * @param cont continuation to call once call is completed(?) + * @param cls closure to pass to proc and cont + * @return operation handle to cancel the operation + */ +struct GNUNET_TESTBED_Operation * +GNUNET_TESTBED_get_statistics (unsigned int num_peers, + struct GNUNET_TESTBED_Peer **peers, + GNUNET_TESTBED_StatisticsIterator proc, + GNUNET_TESTBED_OperationCompletionCallback cont, + void *cls) +{ + // FIXME: not implemented, but clients will kind-of work if we do this: + GNUNET_break (0); + cont (cls, NULL, "not implemented"); + return NULL; +} + + +/* end of testbed_api_statistics.c */ diff --git a/src/testbed/testbed_api_test.c b/src/testbed/testbed_api_test.c index 38e189b..af91cec 100644 --- a/src/testbed/testbed_api_test.c +++ b/src/testbed/testbed_api_test.c @@ -22,11 +22,66 @@ * @file testbed/testbed_api_test.c * @brief high-level test function * @author Christian Grothoff + * @author Sree Harsha Totakura */ #include "platform.h" #include "gnunet_testbed_service.h" +/** + * Context information for test run + */ +struct TestRunContext +{ + /** + * Test master callback + */ + GNUNET_TESTBED_TestMaster test_master; + + /** + * Closure for test master + */ + void *test_master_cls; + + /** + * The controller event callback + */ + GNUNET_TESTBED_ControllerCallback cc; + + /** + * Closure for the above callback + */ + void *cc_cls; + + /** + * event mask for the controller callback + */ + uint64_t event_mask; + + /** + * Number of peers to start + */ + unsigned int num_peers; +}; + + +/** + * Main run function. + * + * @param cls NULL + * @param args arguments passed to GNUNET_PROGRAM_run + * @param cfgfile the path to configuration file + * @param config the configuration file handle + */ +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *config) +{ + struct TestRunContext *rc = cls; + + GNUNET_TESTBED_run (NULL, config, rc->num_peers, rc->event_mask, rc->cc, + rc->cc_cls, rc->test_master, rc->test_master_cls); +} /** @@ -49,19 +104,57 @@ * @param cfg_filename configuration filename to use * (for testbed, controller and peers) * @param num_peers number of peers to start + * @param event_mask bit mask with set of events to call 'cc' for; + * or-ed values of "1LL" shifted by the + * respective 'enum GNUNET_TESTBED_EventType' + * (i.e. "(1LL << GNUNET_TESTBED_ET_CONNECT) || ...") + * @param cc controller callback to invoke on events; This callback is called + * for all peer start events even if GNUNET_TESTBED_ET_PEER_START isn't + * set in the event_mask as this is the only way get access to the + * handle of each peer + * @param cc_cls closure for cc * @param test_master task to run once the test is ready * @param test_master_cls closure for 'task'. + * @return GNUNET_SYSERR on error, GNUNET_OK on success */ -void -GNUNET_TESTBED_test_run (const char *testname, - const char *cfg_filename, - unsigned int num_peers, - GNUNET_TESTBED_TestMaster test_master, - void *test_master_cls) +int +GNUNET_TESTBED_test_run (const char *testname, const char *cfg_filename, + unsigned int num_peers, uint64_t event_mask, + GNUNET_TESTBED_ControllerCallback cc, void *cc_cls, + GNUNET_TESTBED_TestMaster test_master, + void *test_master_cls) { - GNUNET_break (0); -} - + char *argv2[] = { + NULL, + "-c", + NULL, + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + struct TestRunContext *rc; + int ret; + argv2[0] = GNUNET_strdup (testname); + argv2[2] = GNUNET_strdup (cfg_filename); + GNUNET_assert (NULL != test_master); + GNUNET_assert (num_peers > 0); + rc = GNUNET_malloc (sizeof (struct TestRunContext) + + (num_peers * sizeof (struct GNUNET_TESTBED_Peer *))); + rc->test_master = test_master; + rc->test_master_cls = test_master_cls; + rc->num_peers = num_peers; + rc->event_mask = event_mask; + rc->cc = cc; + rc->cc_cls = cc_cls; + ret = + GNUNET_PROGRAM_run ((sizeof (argv2) / sizeof (char *)) - 1, argv2, + testname, "nohelp", options, &run, rc); + GNUNET_free (rc); + GNUNET_free (argv2[0]); + GNUNET_free (argv2[2]); + return ret; +} /* end of testbed_api_test.c */ diff --git a/src/testbed/testbed_api_testbed.c b/src/testbed/testbed_api_testbed.c index 6311806..1c9363c 100644 --- a/src/testbed/testbed_api_testbed.c +++ b/src/testbed/testbed_api_testbed.c @@ -1,115 +1,1016 @@ /* - This file is part of GNUnet - (C) 2008--2012 Christian Grothoff (and other contributing authors) + This file is part of GNUnet + (C) 2008--2012 Christian Grothoff (and other contributing authors) - GNUnet 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. + GNUnet 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. - 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 - General Public License for more details. + 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 + General Public License for more details. - You should have received a copy of the GNU General Public License - along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. - */ + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ /** * @file testbed/testbed_api_testbed.c * @brief high-level testbed management * @author Christian Grothoff + * @author Sree Harsha Totakura */ + #include "platform.h" +#include "gnunet_util_lib.h" #include "gnunet_testbed_service.h" +#include "testbed_api_peers.h" +#include "testbed_api_hosts.h" +#include "testbed_api_topology.h" + +/** + * Generic loggins shorthand + */ +#define LOG(kind,...) \ + GNUNET_log_from (kind, "testbed-api-testbed", __VA_ARGS__) + +/** + * Debug logging shortcut + */ +#define DEBUG(...) \ + LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__) + +/** + * DLL of operations + */ +struct DLLOperation +{ + /** + * The testbed operation handle + */ + struct GNUNET_TESTBED_Operation *op; + + /** + * Context information for GNUNET_TESTBED_run() + */ + struct RunContext *rc; + + /** + * Closure + */ + void *cls; + + /** + * The next pointer for DLL + */ + struct DLLOperation *next; + + /** + * The prev pointer for DLL + */ + struct DLLOperation *prev; +}; /** - * Opaque handle to an abstract operation to be executed by the testing framework. + * States of RunContext */ -struct GNUNET_TESTBED_Testbed +enum State { - // FIXME! + /** + * Initial state + */ + RC_INIT = 0, + + /** + * Controllers on given hosts started and linked + */ + RC_LINKED, + + /** + * Peers are created + */ + RC_PEERS_CREATED, + + /** + * The testbed run is ready and the master callback can be called now. At this + * time the peers are all started and if a topology is provided in the + * configuration the topology would have been attempted + */ + RC_READY, + + /** + * Peers are stopped + */ + RC_PEERS_STOPPED, + + /** + * Peers are destroyed + */ + RC_PEERS_DESTROYED }; /** - * Configure and run a testbed using the given - * master controller on 'num_hosts' starting - * 'num_peers' using the given peer configuration. - * - * @param controller master controller for the testbed - * (must not be destroyed until after the - * testbed is destroyed). - * @param num_hosts number of hosts in 'hosts', 0 to only - * use 'localhost' - * @param hosts list of hosts to use for the testbed - * @param num_peers number of peers to start - * @param peer_cfg peer configuration template to use - * @param underlay_topology underlay topology to create - * @param va topology-specific options - * @return handle to the testbed - */ -struct GNUNET_TESTBED_Testbed * -GNUNET_TESTBED_create_va (struct GNUNET_TESTBED_Controller *controller, - unsigned int num_hosts, - struct GNUNET_TESTBED_Host **hosts, - unsigned int num_peers, - const struct GNUNET_CONFIGURATION_Handle *peer_cfg, - enum GNUNET_TESTBED_TopologyOption underlay_topology, - va_list va) -{ - GNUNET_break (0); - return NULL; + * Testbed Run Handle + */ +struct RunContext +{ + /** + * The controller handle + */ + struct GNUNET_TESTBED_Controller *c; + + /** + * The configuration of the controller. This is based on the cfg given to the + * function GNUNET_TESTBED_run(). We also use this config as a template while + * for peers + */ + struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Handle to the host on which the controller runs + */ + struct GNUNET_TESTBED_Host *h; + + /** + * The handle to the controller process + */ + struct GNUNET_TESTBED_ControllerProc *cproc; + + /** + * The callback to use as controller callback + */ + GNUNET_TESTBED_ControllerCallback cc; + + /** + * The pointer to the controller callback + */ + void *cc_cls; + + /** + * The trusted IP string + */ + char *trusted_ip; + + /** + * TestMaster callback to call when testbed initialization is done + */ + GNUNET_TESTBED_TestMaster test_master; + + /** + * The closure for the TestMaster callback + */ + void *test_master_cls; + + /** + * The head element of DLL operations + */ + struct DLLOperation *dll_op_head; + + /** + * The tail element of DLL operations + */ + struct DLLOperation *dll_op_tail; + + /** + * An array of hosts loaded from the hostkeys file + */ + struct GNUNET_TESTBED_Host **hosts; + + /** + * The handle for whether a host is habitable or not + */ + struct GNUNET_TESTBED_HostHabitableCheckHandle **hc_handles; + + /** + * Array of peers which we create + */ + struct GNUNET_TESTBED_Peer **peers; + + /** + * The topology generation operation. Will be null if no topology is set in + * the configuration + */ + struct GNUNET_TESTBED_Operation *topology_operation; + + /** + * The file containing topology data. Only used if the topology is set to 'FROM_FILE' + */ + char *topo_file; + + /** + * Host registration handle + */ + struct GNUNET_TESTBED_HostRegistrationHandle *reg_handle; + + /** + * Profiling start time + */ + struct GNUNET_TIME_Absolute pstart_time; + + /** + * Host registration task + */ + GNUNET_SCHEDULER_TaskIdentifier register_hosts_task; + + /** + * Task to be run while shutting down + */ + GNUNET_SCHEDULER_TaskIdentifier shutdown_run_task; + + /** + * The event mask for the controller + */ + uint64_t event_mask; + + /** + * State of this context + */ + enum State state; + + /** + * The topology which has to be achieved with the peers started in this context + */ + enum GNUNET_TESTBED_TopologyOption topology; + + /** + * Have we already shutdown + */ + int shutdown; + + /** + * Number of hosts in the given host file + */ + unsigned int num_hosts; + + /** + * Number of registered hosts. Also used as a counter while checking + * habitabillity of hosts + */ + unsigned int reg_hosts; + + /** + * Current peer count for an operation; Set this to 0 and increment for each + * successful operation on a peer + */ + unsigned int peer_count; + + /** + * number of peers to start + */ + unsigned int num_peers; + + /** + * counter to count overlay connect attempts. This counter includes both + * successful and failed overlay connects + */ + unsigned int oc_count; + + /** + * Expected overlay connects. Should be zero if no topology is relavant + */ + unsigned int num_oc; + + /** + * Number of random links to established + */ + unsigned int random_links; + +}; + + +/** + * Function to return the string representation of the duration between current + * time and `pstart_time' in `RunContext' + * + * @param rc the RunContext + * @return the representation string; this is NOT reentrant + */ +static const char * +prof_time (struct RunContext *rc) +{ + struct GNUNET_TIME_Relative ptime; + + ptime = GNUNET_TIME_absolute_get_duration (rc->pstart_time); + return GNUNET_STRINGS_relative_time_to_string (ptime, GNUNET_YES); +} + + +/** + * Task for starting peers + * + * @param cls the RunHandle + * @param tc the task context from scheduler + */ +static void +start_peers_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct RunContext *rc = cls; + struct DLLOperation *dll_op; + unsigned int peer; + + DEBUG ("Starting Peers\n"); + rc->pstart_time = GNUNET_TIME_absolute_get (); + for (peer = 0; peer < rc->num_peers; peer++) + { + dll_op = GNUNET_malloc (sizeof (struct DLLOperation)); + dll_op->op = GNUNET_TESTBED_peer_start (NULL, rc->peers[peer], NULL, NULL); + dll_op->cls = rc->peers[peer]; + GNUNET_CONTAINER_DLL_insert_tail (rc->dll_op_head, rc->dll_op_tail, dll_op); + } + rc->peer_count = 0; } /** - * Configure and run a testbed using the given - * master controller on 'num_hosts' starting - * 'num_peers' using the given peer configuration. - * - * @param controller master controller for the testbed - * (must not be destroyed until after the - * testbed is destroyed). - * @param num_hosts number of hosts in 'hosts', 0 to only - * use 'localhost' - * @param hosts list of hosts to use for the testbed - * @param num_peers number of peers to start - * @param peer_cfg peer configuration template to use - * @param underlay_topology underlay topology to create - * @param ... topology-specific options - */ -struct GNUNET_TESTBED_Testbed * -GNUNET_TESTBED_create (struct GNUNET_TESTBED_Controller *controller, - unsigned int num_hosts, - struct GNUNET_TESTBED_Host **hosts, - unsigned int num_peers, - const struct GNUNET_CONFIGURATION_Handle *peer_cfg, - enum GNUNET_TESTBED_TopologyOption underlay_topology, - ...) -{ - GNUNET_break (0); - return NULL; + * Functions of this signature are called when a peer has been successfully + * created + * + * @param cls the closure from GNUNET_TESTBED_peer_create() + * @param peer the handle for the created peer; NULL on any error during + * creation + * @param emsg NULL if peer is not NULL; else MAY contain the error description + */ +static void +peer_create_cb (void *cls, struct GNUNET_TESTBED_Peer *peer, const char *emsg) +{ + struct DLLOperation *dll_op = cls; + struct RunContext *rc; + + GNUNET_assert (NULL != dll_op); + rc = dll_op->rc; + GNUNET_assert (NULL != rc); + GNUNET_CONTAINER_DLL_remove (rc->dll_op_head, rc->dll_op_tail, dll_op); + GNUNET_TESTBED_operation_done (dll_op->op); + GNUNET_free (dll_op); + if (NULL == peer) + { + if (NULL != emsg) + LOG (GNUNET_ERROR_TYPE_WARNING, "Error while creating a peer: %s\n", + emsg); + /* FIXME: GNUNET_TESTBED_shutdown_run()? */ + return; + } + rc->peers[rc->peer_count] = peer; + rc->peer_count++; + if (rc->peer_count < rc->num_peers) + return; + DEBUG ("%u peers created in %s\n", rc->num_peers, prof_time (rc)); + rc->state = RC_PEERS_CREATED; + GNUNET_SCHEDULER_add_now (&start_peers_task, rc); } /** - * Destroy a testbed. Stops all running peers and then - * destroys all peers. Does NOT destroy the master controller. + * Assuming all peers have been destroyed cleanup run handle * - * @param testbed testbed to destroy + * @param cls the run handle + * @param tc the task context from scheduler */ -void -GNUNET_TESTBED_destroy (struct GNUNET_TESTBED_Testbed *testbed) +static void +cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct RunContext *rc = cls; + struct DLLOperation *dll_op; + unsigned int hid; + + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == rc->register_hosts_task); + GNUNET_assert (NULL == rc->reg_handle); + GNUNET_assert (NULL == rc->peers); + GNUNET_assert (NULL == rc->hc_handles); + GNUNET_assert (RC_PEERS_DESTROYED == rc->state); + if (NULL != rc->dll_op_head) + { /* cancel our pending operations */ + while (NULL != (dll_op = rc->dll_op_head)) + { + GNUNET_TESTBED_operation_done (dll_op->op); + GNUNET_CONTAINER_DLL_remove (rc->dll_op_head, rc->dll_op_tail, dll_op); + GNUNET_free (dll_op); + } + } + if (NULL != rc->c) + GNUNET_TESTBED_controller_disconnect (rc->c); + if (NULL != rc->cproc) + GNUNET_TESTBED_controller_stop (rc->cproc); + if (NULL != rc->h) + GNUNET_TESTBED_host_destroy (rc->h); + for (hid = 0; hid < rc->num_hosts; hid++) + GNUNET_TESTBED_host_destroy (rc->hosts[hid]); + GNUNET_free_non_null (rc->hosts); + if (NULL != rc->cfg) + GNUNET_CONFIGURATION_destroy (rc->cfg); + GNUNET_free_non_null (rc->topo_file); + GNUNET_free_non_null (rc->trusted_ip); + GNUNET_free (rc); +} + + +/** + * Stops the testbed run and releases any used resources + * + * @param cls the tesbed run handle + * @param tc the task context from scheduler + */ +static void +shutdown_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + + +/** + * Function to shutdown now + * + * @param rc the RunContext + */ +static void +shutdown_now (struct RunContext *rc) +{ + if (GNUNET_YES == rc->shutdown) + return; + if (GNUNET_SCHEDULER_NO_TASK != rc->shutdown_run_task) + GNUNET_SCHEDULER_cancel (rc->shutdown_run_task); + rc->shutdown_run_task = GNUNET_SCHEDULER_add_now (&shutdown_run, rc); +} + + +/** + * Stops the testbed run and releases any used resources + * + * @param cls the tesbed run handle + * @param tc the task context from scheduler + */ +static void +shutdown_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { - GNUNET_break (0); + struct RunContext *rc = cls; + struct DLLOperation *dll_op; + int all_peers_destroyed; + unsigned int peer; + unsigned int nhost; + + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != rc->shutdown_run_task); + rc->shutdown_run_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_assert (GNUNET_NO == rc->shutdown); + rc->shutdown = GNUNET_YES; + if (NULL != rc->hc_handles) + { + for (nhost = 0; nhost < rc->num_hosts; nhost++) + if (NULL != rc->hc_handles[nhost]) + GNUNET_TESTBED_is_host_habitable_cancel (rc->hc_handles[nhost]); + GNUNET_free (rc->hc_handles); + rc->hc_handles = NULL; + } + /* Stop register hosts task if it is running */ + if (GNUNET_SCHEDULER_NO_TASK != rc->register_hosts_task) + { + GNUNET_SCHEDULER_cancel (rc->register_hosts_task); + rc->register_hosts_task = GNUNET_SCHEDULER_NO_TASK; + } + if (NULL != rc->reg_handle) + { + GNUNET_TESTBED_cancel_registration (rc->reg_handle); + rc->reg_handle = NULL; + } + if (NULL != rc->c) + { + if (NULL != rc->peers) + { + if (NULL != rc->topology_operation) + { + GNUNET_TESTBED_operation_done (rc->topology_operation); + rc->topology_operation = NULL; + } + if (RC_INIT == rc->state) + rc->state = RC_READY; /* Even though we haven't called the master callback */ + rc->peer_count = 0; + /* Check if some peers are stopped */ + for (peer = 0; peer < rc->num_peers; peer++) + { + if (NULL == rc->peers[peer]) + continue; + if (PS_STOPPED != rc->peers[peer]->state) + break; + } + if (peer == rc->num_peers) + { + /* All peers are stopped */ + rc->state = RC_PEERS_STOPPED; + all_peers_destroyed = GNUNET_YES; + for (peer = 0; peer < rc->num_peers; peer++) + { + if (NULL == rc->peers[peer]) + continue; + all_peers_destroyed = GNUNET_NO; + dll_op = GNUNET_malloc (sizeof (struct DLLOperation)); + dll_op->op = GNUNET_TESTBED_peer_destroy (rc->peers[peer]); + GNUNET_CONTAINER_DLL_insert_tail (rc->dll_op_head, rc->dll_op_tail, + dll_op); + } + if (all_peers_destroyed == GNUNET_NO) + { + DEBUG ("Destroying peers\n"); + rc->pstart_time = GNUNET_TIME_absolute_get (); + return; + } + } + /* Some peers are stopped */ + DEBUG ("Stopping peers\n"); + rc->pstart_time = GNUNET_TIME_absolute_get (); + for (peer = 0; peer < rc->num_peers; peer++) + { + if ((NULL == rc->peers[peer]) || (PS_STARTED != rc->peers[peer]->state)) + { + rc->peer_count++; + continue; + } + dll_op = GNUNET_malloc (sizeof (struct DLLOperation)); + dll_op->op = GNUNET_TESTBED_peer_stop (rc->peers[peer], NULL, NULL); + dll_op->cls = rc->peers[peer]; + GNUNET_CONTAINER_DLL_insert_tail (rc->dll_op_head, rc->dll_op_tail, + dll_op); + } + if (rc->peer_count != rc->num_peers) + return; + GNUNET_free (rc->peers); + rc->peers = NULL; + } + } + rc->state = RC_PEERS_DESTROYED; /* No peers are present so we consider the + * state where all peers are destroyed */ + GNUNET_SCHEDULER_add_now (&cleanup_task, rc); } +/** + * Task to call master task + * + * @param cls the run context + * @param tc the task context + */ +static void +call_master (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct RunContext *rc = cls; + + if (NULL != rc->topology_operation) + { + DEBUG ("Overlay topology generated in %s\n", prof_time (rc)); + GNUNET_TESTBED_operation_done (rc->topology_operation); + rc->topology_operation = NULL; + } + if (NULL != rc->test_master) + rc->test_master (rc->test_master_cls, rc->num_peers, rc->peers); +} + + +/** + * Function to create peers + * + * @param rc the RunContext + */ +static void +create_peers (struct RunContext *rc) +{ + struct DLLOperation *dll_op; + unsigned int peer; + + DEBUG ("Creating peers\n"); + rc->pstart_time = GNUNET_TIME_absolute_get (); + rc->peers = + GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Peer *) * rc->num_peers); + GNUNET_assert (NULL != rc->c); + rc->peer_count = 0; + for (peer = 0; peer < rc->num_peers; peer++) + { + dll_op = GNUNET_malloc (sizeof (struct DLLOperation)); + dll_op->rc = rc; + dll_op->op = + GNUNET_TESTBED_peer_create (rc->c, + (0 == + rc->num_hosts) ? rc->h : rc->hosts[peer % + rc->num_hosts], + rc->cfg, peer_create_cb, dll_op); + GNUNET_CONTAINER_DLL_insert_tail (rc->dll_op_head, rc->dll_op_tail, dll_op); + } +} + + +/** + * Signature of the event handler function called by the + * respective event controller. + * + * @param cls closure + * @param event information about the event + */ +static void +event_cb (void *cls, const struct GNUNET_TESTBED_EventInformation *event) +{ + struct RunContext *rc = cls; + struct DLLOperation *dll_op; + unsigned int peer_id; + + if (RC_INIT == rc->state) + { + switch (event->type) + { + case GNUNET_TESTBED_ET_OPERATION_FINISHED: + dll_op = event->details.operation_finished.op_cls; + if (NULL != event->details.operation_finished.emsg) + { + LOG (GNUNET_ERROR_TYPE_ERROR, _("Linking controllers failed. Exiting")); + shutdown_now (rc); + } + else + rc->reg_hosts++; + GNUNET_assert (event->details.operation_finished.operation == dll_op->op); + GNUNET_CONTAINER_DLL_remove (rc->dll_op_head, rc->dll_op_tail, dll_op); + GNUNET_TESTBED_operation_done (dll_op->op); + GNUNET_free (dll_op); + if (rc->reg_hosts == rc->num_hosts) + { + rc->state = RC_LINKED; + create_peers (rc); + } + return; + default: + GNUNET_break (0); + shutdown_now (rc); + return; + } + } + if (NULL != rc->topology_operation) + { + switch (event->type) + { + case GNUNET_TESTBED_ET_OPERATION_FINISHED: + case GNUNET_TESTBED_ET_CONNECT: + rc->oc_count++; + break; + default: + GNUNET_break (0); + shutdown_now (rc); + return; + } + if (rc->oc_count == rc->num_oc) + { + rc->state = RC_READY; + GNUNET_SCHEDULER_add_continuation (&call_master, rc, + GNUNET_SCHEDULER_REASON_PREREQ_DONE); + } + goto call_cc; + } + for (dll_op = rc->dll_op_head; NULL != dll_op; dll_op = dll_op->next) + { + if ((GNUNET_TESTBED_ET_OPERATION_FINISHED == event->type) && + (event->details.operation_finished.operation == dll_op->op)) + break; + if ((GNUNET_TESTBED_ET_PEER_STOP == event->type) && + (event->details.peer_stop.peer == dll_op->cls)) + break; + } + if (NULL == dll_op) + goto call_cc; + GNUNET_CONTAINER_DLL_remove (rc->dll_op_head, rc->dll_op_tail, dll_op); + GNUNET_TESTBED_operation_done (dll_op->op); + GNUNET_free (dll_op); + rc->peer_count++; + if (rc->peer_count < rc->num_peers) + return; + switch (rc->state) + { + case RC_PEERS_CREATED: + case RC_READY: + rc->state = RC_PEERS_STOPPED; + DEBUG ("Peers stopped in %s\n", prof_time (rc)); + DEBUG ("Destroying peers\n"); + rc->pstart_time = GNUNET_TIME_absolute_get (); + rc->peer_count = 0; + for (peer_id = 0; peer_id < rc->num_peers; peer_id++) + { + dll_op = GNUNET_malloc (sizeof (struct DLLOperation)); + dll_op->op = GNUNET_TESTBED_peer_destroy (rc->peers[peer_id]); + GNUNET_CONTAINER_DLL_insert_tail (rc->dll_op_head, rc->dll_op_tail, + dll_op); + } + break; + case RC_PEERS_STOPPED: + rc->state = RC_PEERS_DESTROYED; + GNUNET_free (rc->peers); + rc->peers = NULL; + DEBUG ("Peers destroyed in %s\n", prof_time (rc)); + GNUNET_SCHEDULER_add_now (&cleanup_task, rc); + break; + default: + GNUNET_assert (0); + } + return; + +call_cc: + if ((0 != (rc->event_mask & (1LL << event->type))) && (NULL != rc->cc)) + rc->cc (rc->cc_cls, event); + if (GNUNET_TESTBED_ET_PEER_START != event->type) + return; + for (dll_op = rc->dll_op_head; NULL != dll_op; dll_op = dll_op->next) + if ((NULL != dll_op->cls) && + (event->details.peer_start.peer == dll_op->cls)) + break; + if (NULL == dll_op) /* Not our operation */ + return; + GNUNET_CONTAINER_DLL_remove (rc->dll_op_head, rc->dll_op_tail, dll_op); + GNUNET_TESTBED_operation_done (dll_op->op); + GNUNET_free (dll_op); + rc->peer_count++; + if (rc->peer_count < rc->num_peers) + return; + DEBUG ("%u peers started in %s\n", rc->num_peers, prof_time (rc)); + if (GNUNET_TESTBED_TOPOLOGY_NONE != rc->topology) + { + if ((GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI == rc->topology) || + (GNUNET_TESTBED_TOPOLOGY_SMALL_WORLD_RING == rc->topology) || + (GNUNET_TESTBED_TOPOLOGY_SMALL_WORLD == rc->topology)) + { + rc->topology_operation = + GNUNET_TESTBED_overlay_configure_topology (NULL, rc->num_peers, + rc->peers, &rc->num_oc, + NULL, + NULL, + rc->topology, + rc->random_links, + GNUNET_TESTBED_TOPOLOGY_OPTION_END); + } + else if (GNUNET_TESTBED_TOPOLOGY_FROM_FILE == rc->topology) + { + GNUNET_assert (NULL != rc->topo_file); + rc->topology_operation = + GNUNET_TESTBED_overlay_configure_topology (NULL, rc->num_peers, + rc->peers, &rc->num_oc, + NULL, + NULL, + rc->topology, + rc->topo_file, + GNUNET_TESTBED_TOPOLOGY_OPTION_END); + } + else + rc->topology_operation = + GNUNET_TESTBED_overlay_configure_topology (NULL, rc->num_peers, + rc->peers, &rc->num_oc, + NULL, + NULL, + rc->topology, + GNUNET_TESTBED_TOPOLOGY_OPTION_END); + if (NULL == rc->topology_operation) + LOG (GNUNET_ERROR_TYPE_WARNING, + "Not generating topology. Check number of peers\n"); + else + { + DEBUG ("Creating overlay topology\n"); + rc->pstart_time = GNUNET_TIME_absolute_get (); + return; + } + } + rc->state = RC_READY; + GNUNET_SCHEDULER_add_continuation (&call_master, rc, + GNUNET_SCHEDULER_REASON_PREREQ_DONE); +} + + +/** + * Task to register all hosts available in the global host list + * + * @param cls the RunContext + * @param tc the scheduler task context + */ +static void +register_hosts (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + + +/** + * Callback which will be called to after a host registration succeeded or failed + * + * @param cls the closure + * @param emsg the error message; NULL if host registration is successful + */ +static void +host_registration_completion (void *cls, const char *emsg) +{ + struct RunContext *rc = cls; + + rc->reg_handle = NULL; + if (NULL != emsg) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + _("Host registration failed for a host. Error: %s\n"), emsg); + shutdown_now (rc); + return; + } + rc->register_hosts_task = GNUNET_SCHEDULER_add_now (®ister_hosts, rc); +} + + +/** + * Task to register all hosts available in the global host list + * + * @param cls RunContext + * @param tc the scheduler task context + */ +static void +register_hosts (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct RunContext *rc = cls; + struct DLLOperation *dll_op; + unsigned int slave; + + rc->register_hosts_task = GNUNET_SCHEDULER_NO_TASK; + if (rc->reg_hosts == rc->num_hosts) + { + DEBUG ("All hosts successfully registered\n"); + /* Start slaves */ + for (slave = 0; slave < rc->num_hosts; slave++) + { + dll_op = GNUNET_malloc (sizeof (struct DLLOperation)); + dll_op->rc = rc; + dll_op->op = + GNUNET_TESTBED_controller_link (dll_op, rc->c, rc->hosts[slave], + rc->h, rc->cfg, GNUNET_YES); + GNUNET_CONTAINER_DLL_insert_tail (rc->dll_op_head, rc->dll_op_tail, + dll_op); + } + rc->reg_hosts = 0; + return; + } + rc->reg_handle = + GNUNET_TESTBED_register_host (rc->c, rc->hosts[rc->reg_hosts], + host_registration_completion, rc); + rc->reg_hosts++; +} + + +/** + * Callback to signal successfull startup of the controller process + * + * @param cls the closure from GNUNET_TESTBED_controller_start() + * @param cfg the configuration with which the controller has been started; + * NULL if status is not GNUNET_OK + * @param status GNUNET_OK if the startup is successfull; GNUNET_SYSERR if not, + * GNUNET_TESTBED_controller_stop() shouldn't be called in this case + */ +static void +controller_status_cb (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg, + int status) +{ + struct RunContext *rc = cls; + uint64_t event_mask; + + if (status != GNUNET_OK) + { + switch (rc->state) + { + case RC_INIT: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Testbed startup failed\n"); + return; + default: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Controller crash detected. Shutting down.\n"); + rc->cproc = NULL; + shutdown_now (rc); + return; + } + } + GNUNET_CONFIGURATION_destroy (rc->cfg); + rc->cfg = GNUNET_CONFIGURATION_dup (cfg); + event_mask = rc->event_mask; + event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_STOP); + event_mask |= (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED); + event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_START); + if (rc->topology < GNUNET_TESTBED_TOPOLOGY_NONE) + event_mask |= GNUNET_TESTBED_ET_CONNECT; + rc->c = + GNUNET_TESTBED_controller_connect (rc->cfg, rc->h, event_mask, &event_cb, + rc); + if (0 < rc->num_hosts) + { + rc->reg_hosts = 0; + rc->register_hosts_task = GNUNET_SCHEDULER_add_now (®ister_hosts, rc); + return; + } + rc->state = RC_LINKED; + create_peers (rc); +} + + +/** + * Callback function invoked for each interface found. + * + * @param cls closure + * @param name name of the interface (can be NULL for unknown) + * @param isDefault is this presumably the default interface + * @param addr address of this interface (can be NULL for unknown or unassigned) + * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned) + * @param netmask the network mask (can be NULL for unknown or unassigned)) + * @param addrlen length of the address + * @return GNUNET_OK to continue iteration, GNUNET_SYSERR to abort + */ +static int +netint_proc (void *cls, const char *name, int isDefault, + const struct sockaddr *addr, const struct sockaddr *broadcast_addr, + const struct sockaddr *netmask, socklen_t addrlen) +{ + struct RunContext *rc = cls; + char hostip[NI_MAXHOST]; + char *buf; + + if (sizeof (struct sockaddr_in) != addrlen) + return GNUNET_OK; /* Only consider IPv4 for now */ + if (0 != + getnameinfo (addr, addrlen, hostip, NI_MAXHOST, NULL, 0, NI_NUMERICHOST)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "getnameinfo"); + if (NULL == rc->trusted_ip) + { + rc->trusted_ip = GNUNET_strdup (hostip); + return GNUNET_YES; + } + (void) GNUNET_asprintf (&buf, "%s; %s", rc->trusted_ip, hostip); + GNUNET_free (rc->trusted_ip); + rc->trusted_ip = buf; + return GNUNET_YES; +} + + +/** + * Callbacks of this type are called by GNUNET_TESTBED_is_host_habitable to + * inform whether the given host is habitable or not. The Handle returned by + * GNUNET_TESTBED_is_host_habitable() is invalid after this callback is called + * + * @param cls NULL + * @param host the host whose status is being reported; will be NULL if the host + * given to GNUNET_TESTBED_is_host_habitable() is NULL + * @param status GNUNET_YES if it is habitable; GNUNET_NO if not + */ +static void +host_habitable_cb (void *cls, const struct GNUNET_TESTBED_Host *host, + int status) +{ + struct RunContext *rc = cls; + struct GNUNET_TESTBED_Host **old_hosts; + unsigned int nhost; + + for (nhost = 0; nhost < rc->num_hosts; nhost++) + { + if (host == rc->hosts[nhost]) + break; + } + GNUNET_assert (nhost != rc->num_hosts); + rc->hc_handles[nhost] = NULL; + if (GNUNET_NO == status) + { + if ((NULL != host) && (NULL != GNUNET_TESTBED_host_get_hostname (host))) + LOG (GNUNET_ERROR_TYPE_ERROR, _("Host %s cannot start testbed\n"), + GNUNET_TESTBED_host_get_hostname (host)); + else + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Testbed cannot be started on localhost\n")); + shutdown_now (rc); + return; + } + rc->reg_hosts++; + if (rc->reg_hosts < rc->num_hosts) + return; + GNUNET_free (rc->hc_handles); + rc->hc_handles = NULL; + rc->h = rc->hosts[0]; + rc->num_hosts--; + if (0 < rc->num_hosts) + { + old_hosts = rc->hosts; + rc->hosts = + GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Host *) * rc->num_hosts); + memcpy (rc->hosts, &old_hosts[1], + (sizeof (struct GNUNET_TESTBED_Host *) * rc->num_hosts)); + GNUNET_free (old_hosts); + } + else + { + GNUNET_free (rc->hosts); + rc->hosts = NULL; + } + GNUNET_OS_network_interfaces_list (netint_proc, rc); + if (NULL == rc->trusted_ip) + rc->trusted_ip = GNUNET_strdup ("127.0.0.1"); + rc->cproc = + GNUNET_TESTBED_controller_start (rc->trusted_ip, rc->h, rc->cfg, + &controller_status_cb, rc); + GNUNET_free (rc->trusted_ip); + rc->trusted_ip = NULL; + if (NULL == rc->cproc) + { + LOG (GNUNET_ERROR_TYPE_ERROR, _("Cannot start the master controller")); + shutdown_now (rc); + } +} + /** * Convenience method for running a testbed with @@ -130,24 +1031,150 @@ GNUNET_TESTBED_destroy (struct GNUNET_TESTBED_Testbed *testbed) * or-ed values of "1LL" shifted by the * respective 'enum GNUNET_TESTBED_EventType' * (i.e. "(1LL << GNUNET_TESTBED_ET_CONNECT) || ...") - * @param cc controller callback to invoke on events + * @param cc controller callback to invoke on events; This callback is called + * for all peer start events even if GNUNET_TESTBED_ET_PEER_START isn't + * set in the event_mask as this is the only way get access to the + * handle of each peer * @param cc_cls closure for cc - * @param master task to run once the testbed is ready - * @param master_cls closure for 'task'. + * @param test_master this callback will be called once the test is ready + * @param test_master_cls closure for 'test_master'. */ void GNUNET_TESTBED_run (const char *host_filename, - const struct GNUNET_CONFIGURATION_Handle *cfg, - unsigned int num_peers, - uint64_t event_mask, - GNUNET_TESTBED_ControllerCallback cc, - void *cc_cls, - GNUNET_SCHEDULER_Task master, - void *master_cls) -{ - GNUNET_break (0); -} + const struct GNUNET_CONFIGURATION_Handle *cfg, + unsigned int num_peers, uint64_t event_mask, + GNUNET_TESTBED_ControllerCallback cc, void *cc_cls, + GNUNET_TESTBED_TestMaster test_master, + void *test_master_cls) +{ + struct RunContext *rc; + char *topology; + unsigned long long random_links; + unsigned int hid; + unsigned int nhost; + GNUNET_assert (num_peers > 0); + rc = GNUNET_malloc (sizeof (struct RunContext)); + if (NULL != host_filename) + { + rc->num_hosts = + GNUNET_TESTBED_hosts_load_from_file (host_filename, &rc->hosts); + if (0 == rc->num_hosts) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + _("No hosts loaded. Need at least one host\n")); + goto error_cleanup; + } + } + else + rc->h = GNUNET_TESTBED_host_create (NULL, NULL, 0); + rc->cfg = GNUNET_CONFIGURATION_dup (cfg); + rc->num_peers = num_peers; + rc->event_mask = event_mask; + rc->cc = cc; + rc->cc_cls = cc_cls; + rc->test_master = test_master; + rc->test_master_cls = test_master_cls; + rc->state = RC_INIT; + rc->topology = GNUNET_TESTBED_TOPOLOGY_NONE; + if (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (rc->cfg, "testbed", + "OVERLAY_TOPOLOGY", &topology)) + { + if (GNUNET_NO == GNUNET_TESTBED_topology_get_ (&rc->topology, topology)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, "testbed", + "OVERLAY_TOPLOGY", + _ + ("Specified topology must be supported by testbed")); + } + GNUNET_free (topology); + } + switch (rc->topology) + { + case GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI: + case GNUNET_TESTBED_TOPOLOGY_SMALL_WORLD_RING: + case GNUNET_TESTBED_TOPOLOGY_SMALL_WORLD: + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (rc->cfg, "testbed", + "OVERLAY_RANDOM_LINKS", + &random_links)) + { + /* OVERLAY option RANDOM & SMALL_WORLD_RING requires OVERLAY_RANDOM_LINKS + * option to be set to the number of random links to be established */ + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "testbed", + "OVERLAY_RANDOM_LINKS"); + goto error_cleanup; + } + if (random_links > UINT32_MAX) + { + GNUNET_break (0); /* Too big number */ + goto error_cleanup; + } + rc->random_links = (unsigned int) random_links; + break; + case GNUNET_TESTBED_TOPOLOGY_FROM_FILE: + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (rc->cfg, "testbed", + "OVERLAY_TOPOLOGY_FILE", + &rc->topo_file)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "testbed", + "OVERLAY_TOPOLOGY_FILE"); + goto error_cleanup; + } + default: + /* Warn if OVERLAY_RANDOM_LINKS is present that it will be ignored */ + if (GNUNET_YES == + GNUNET_CONFIGURATION_have_value (rc->cfg, "testbed", + "OVERLAY_RANDOM_LINKS")) + LOG (GNUNET_ERROR_TYPE_WARNING, + "Ignoring value of `OVERLAY_RANDOM_LINKS' in given configuration\n"); + break; + } + if (NULL != host_filename) + { + rc->hc_handles = + GNUNET_malloc (sizeof (struct GNUNET_TESTBED_HostHabitableCheckHandle *) + * rc->num_hosts); + for (nhost = 0; nhost < rc->num_hosts; nhost++) + { + if (NULL == + (rc->hc_handles[nhost] = + GNUNET_TESTBED_is_host_habitable (rc->hosts[nhost], rc->cfg, + &host_habitable_cb, rc))) + { + GNUNET_break (0); + for (nhost = 0; nhost < rc->num_hosts; nhost++) + if (NULL != rc->hc_handles[nhost]) + GNUNET_TESTBED_is_host_habitable_cancel (rc->hc_handles[nhost]); + GNUNET_free (rc->hc_handles); + rc->hc_handles = NULL; + goto error_cleanup; + } + } + } + else + rc->cproc = + GNUNET_TESTBED_controller_start ("127.0.0.1", rc->h, rc->cfg, + &controller_status_cb, rc); + rc->shutdown_run_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_run, + rc); + return; + +error_cleanup: + if (NULL != rc->h) + GNUNET_TESTBED_host_destroy (rc->h); + if (NULL != rc->hosts) + { + for (hid = 0; hid < rc->num_hosts; hid++) + if (NULL != rc->hosts[hid]) + GNUNET_TESTBED_host_destroy (rc->hosts[hid]); + GNUNET_free (rc->hosts); + } + GNUNET_free (rc); +} /* end of testbed_api_testbed.c */ diff --git a/src/testbed/testbed_api_topology.c b/src/testbed/testbed_api_topology.c index c0e9f72..9618f98 100644 --- a/src/testbed/testbed_api_topology.c +++ b/src/testbed/testbed_api_topology.c @@ -25,6 +25,768 @@ */ #include "platform.h" #include "gnunet_testbed_service.h" +#include "testbed_api.h" +#include "testbed_api_peers.h" +#include "testbed_api_operations.h" +#include "testbed_api_topology.h" + +/** + * Generic loggins shorthand + */ +#define LOG(kind,...) \ + GNUNET_log_from (kind, "testbed-api-topology", __VA_ARGS__) + + +/** + * Default number of retires + */ +#define DEFAULT_RETRY_CNT 3 + + +/** + * Context information for topology operations + */ +struct TopologyContext; + + +/** + * Representation of an overlay link + */ +struct OverlayLink +{ + + /** + * An operation corresponding to this link + */ + struct GNUNET_TESTBED_Operation *op; + + /** + * The topology context this link is a part of + */ + struct TopologyContext *tc; + + /** + * position of peer A's handle in peers array + */ + uint32_t A; + + /** + * position of peer B's handle in peers array + */ + uint32_t B; + +}; + + +struct RetryListEntry +{ + /** + * the next pointer for the DLL + */ + struct RetryListEntry *next; + + /** + * the prev pointer for the DLL + */ + struct RetryListEntry *prev; + + /** + * The link to be retired + */ + struct OverlayLink *link; +}; + + +/** + * Context information for topology operations + */ +struct TopologyContext +{ + /** + * The array of peers + */ + struct GNUNET_TESTBED_Peer **peers; + + /** + * An array of links; this array is of size link_array_size + */ + struct OverlayLink *link_array; + + /** + * The operation closure + */ + void *op_cls; + + /** + * topology generation completion callback + */ + GNUNET_TESTBED_TopologyCompletionCallback comp_cb; + + /** + * The closure for the above callback + */ + void *comp_cb_cls; + + /** + * DLL head for retry list + */ + struct RetryListEntry *rl_head; + + /** + * DLL tail for retry list + */ + struct RetryListEntry *rl_tail; + + /** + * The number of peers + */ + unsigned int num_peers; + + /** + * The size of the link array + */ + unsigned int link_array_size; + + /** + * How many retries to do before we give up + */ + unsigned int retry_cnt; + + /** + * Number of links to try + */ + unsigned int nlinks; + + /** + * How many links have been completed + */ + unsigned int ncompleted; + + /** + * Total successfully established overlay connections + */ + unsigned int nsuccess; + + /** + * Total failed overlay connections + */ + unsigned int nfailures; +}; + + +/** + * A array of names representing topologies. Should be in sync with enum + * GNUNET_TESTBED_TopologyOption + */ +const char *topology_strings[] = { + + /** + * A clique (everyone connected to everyone else). No options. If there are N + * peers this topology results in (N * (N -1)) connections. + */ + "CLIQUE", + + /** + * Small-world network (2d torus plus random links). Followed + * by the number of random links to add (unsigned int). + */ + "SMALL_WORLD", + + /** + * Small-world network (ring plus random links). Followed + * by the number of random links to add (unsigned int). + */ + "SMALL_WORLD_RING", + + /** + * Ring topology. No options. + */ + "RING", + + /** + * 2-d torus. No options. + */ + "2D_TORUS", + + /** + * Random graph. Followed by the number of random links to be established + * (unsigned int) + */ + "RANDOM", // GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI + + /** + * Certain percentage of peers are unable to communicate directly + * replicating NAT conditions. Followed by the fraction of + * NAT'ed peers (float). + */ + "INTERNAT", + + /** + * Scale free topology. No options. + */ + "SCALE_FREE", + + /** + * Straight line topology. No options. + */ + "LINE", + + /** + * Read a topology from a given file. Followed by the name of the file (const char *). + */ + "FROM_FILE", + + /** + * All peers are disconnected. No options. + */ + "NONE", + + /** + * End of strings + */ + NULL +}; + + +/** + * Callback to be called when an overlay_link operation complete + * + * @param cls element of the link_op array which points to the corresponding operation + * @param op the operation that has been finished + * @param emsg error message in case the operation has failed; will be NULL if + * operation has executed successfully. + */ +static void +overlay_link_completed (void *cls, struct GNUNET_TESTBED_Operation *op, + const char *emsg) +{ + struct OverlayLink *link = cls; + struct TopologyContext *tc; + struct RetryListEntry *retry_entry; + + GNUNET_assert (op == link->op); + GNUNET_TESTBED_operation_done (op); + link->op = NULL; + tc = link->tc; + if (NULL != emsg) + { + tc->nfailures++; + if (0 != tc->retry_cnt) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + "Error while establishing a link: %s -- Retrying\n", emsg); + retry_entry = GNUNET_malloc (sizeof (struct RetryListEntry)); + retry_entry->link = link; + GNUNET_CONTAINER_DLL_insert_tail (tc->rl_head, tc->rl_tail, retry_entry); + } + } + else + tc->nsuccess++; + tc->ncompleted++; + if (tc->ncompleted < tc->nlinks) + return; + if ((0 != tc->retry_cnt) && (NULL != tc->rl_head)) + { + tc->retry_cnt--; + tc->ncompleted = 0; + tc->nlinks = 0; + while (NULL != (retry_entry = tc->rl_head)) + { + link = retry_entry->link; + link->op = + GNUNET_TESTBED_overlay_connect (tc->op_cls, &overlay_link_completed, + link, tc->peers[link->A], + tc->peers[link->B]); + tc->nlinks++; + GNUNET_CONTAINER_DLL_remove (tc->rl_head, tc->rl_tail, retry_entry); + GNUNET_free (retry_entry); + } + return; + } + if (NULL != tc->comp_cb) + { + tc->comp_cb (tc->comp_cb_cls, tc->nsuccess, tc->nfailures); + } +} + + + +/** + * Function called when a overlay connect operation is ready + * + * @param cls the Topology context + */ +static void +opstart_overlay_configure_topology (void *cls) +{ + struct TopologyContext *tc = cls; + unsigned int p; + + tc->nlinks = tc->link_array_size; + for (p = 0; p < tc->link_array_size; p++) + { + tc->link_array[p].op = + GNUNET_TESTBED_overlay_connect (tc->op_cls, &overlay_link_completed, + &tc->link_array[p], + tc->peers[tc->link_array[p].A], + tc->peers[tc->link_array[p].B]); + } +} + + +/** + * Callback which will be called when overlay connect operation is released + * + * @param cls the Topology context + */ +static void +oprelease_overlay_configure_topology (void *cls) +{ + struct TopologyContext *tc = cls; + struct RetryListEntry *retry_entry; + unsigned int p; + + while (NULL != (retry_entry = tc->rl_head)) + { + GNUNET_CONTAINER_DLL_remove (tc->rl_head, tc->rl_tail, retry_entry); + GNUNET_free (retry_entry); + } + if (NULL != tc->link_array) + { + for (p = 0; p < tc->link_array_size; p++) + if (NULL != tc->link_array[p].op) + GNUNET_TESTBED_operation_done (tc->link_array[p].op); + GNUNET_free (tc->link_array); + } + GNUNET_free (tc); +} + + +/** + * Populates the OverlayLink structure. + * + * @param link the OverlayLink + * @param A the peer A. Should be different from B + * @param B the peer B. Should be different from A + * @param tc the TopologyContext + * @return + */ +static void +make_link (struct OverlayLink *link, uint32_t A, uint32_t B, + struct TopologyContext *tc) +{ + GNUNET_assert (A != B); + LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting peer %u to %u\n", B, A); + link->A = A; + link->B = B; + link->op = NULL; + link->tc = tc; +} + + +/** + * Generates line topology + * + * @param tc the topology context + */ +static void +gen_topo_line (struct TopologyContext *tc) +{ + unsigned int cnt; + + tc->link_array_size = tc->num_peers - 1; + tc->link_array = + GNUNET_malloc (sizeof (struct OverlayLink) * tc->link_array_size); + for (cnt = 0; cnt < (tc->num_peers - 1); cnt++) + make_link (&tc->link_array[cnt], cnt, cnt + 1, tc); +} + + +/** + * Generates ring topology + * + * @param tc the topology context + */ +static void +gen_topo_ring (struct TopologyContext *tc) +{ + gen_topo_line (tc); + tc->link_array_size++; + tc->link_array = + GNUNET_realloc (tc->link_array, + sizeof (struct OverlayLink) * tc->link_array_size); + make_link (&tc->link_array[tc->link_array_size - 1], tc->num_peers - 1, 0, + tc); +} + + +/** + * Returns the number of links that are required to generate a 2d torus for the + * given number of peers. Also returns the arrangment (number of rows and the + * length of each row) + * + * @param num_peers number of peers + * @param rows number of rows in the 2d torus. Can be NULL + * @param rows_len the length of each row. This array will be allocated + * fresh. The caller should free it. Can be NULL + */ +unsigned int +GNUNET_TESTBED_2dtorus_calc_links (unsigned int num_peers, unsigned int *rows, + unsigned int **rows_len) +{ + double sq; + unsigned int sq_floor; + unsigned int _rows; + unsigned int *_rows_len; + unsigned int x; + unsigned int y; + unsigned int _num_peers; + unsigned int cnt; + + sq = sqrt (num_peers); + sq = floor (sq); + sq_floor = (unsigned int) sq; + _rows = (sq_floor + 1); + _rows_len = GNUNET_malloc (sizeof (unsigned int) * _rows); + for (y = 0; y < _rows - 1; y++) + _rows_len[y] = sq_floor; + _num_peers = sq_floor * sq_floor; + cnt = 2 * _num_peers; + x = 0; + y = 0; + while (_num_peers < num_peers) + { + if (x < y) + _rows_len[_rows - 1] = ++x; + else + _rows_len[y++]++; + _num_peers++; + } + cnt += (x < 2) ? x : 2 * x; + cnt += (y < 2) ? y : 2 * y; + if (0 == _rows_len[_rows - 1]) + _rows--; + if (NULL != rows) + *rows = _rows; + if (NULL != rows_len) + *rows_len = _rows_len; + else + GNUNET_free (_rows_len); + return cnt; +} + + +/** + * Generates ring topology + * + * @param tc the topology context + */ +static void +gen_topo_2dtorus (struct TopologyContext *tc) +{ + unsigned int rows; + unsigned int *rows_len; + unsigned int x; + unsigned int y; + unsigned int cnt; + unsigned int offset; + + tc->link_array_size = + GNUNET_TESTBED_2dtorus_calc_links (tc->num_peers, &rows, &rows_len); + tc->link_array = + GNUNET_malloc (sizeof (struct OverlayLink) * tc->link_array_size); + cnt = 0; + offset = 0; + for (y = 0; y < rows; y++) + { + for (x = 0; x < rows_len[y] - 1; x++) + { + make_link (&tc->link_array[cnt], offset + x, offset + x + 1, tc); + cnt++; + } + if (0 == x) + break; + make_link (&tc->link_array[cnt], offset + x, offset, tc); + cnt++; + offset += rows_len[y]; + } + for (x = 0; x < rows_len[0]; x++) + { + offset = 0; + for (y = 0; y < rows - 1; y++) + { + if (x >= rows_len[y + 1]) + break; + GNUNET_assert (x < rows_len[y + 1]); + make_link (&tc->link_array[cnt], offset + x, offset + rows_len[y] + x, + tc); + offset += rows_len[y]; + cnt++; + } + if (0 == offset) + break; + make_link (&tc->link_array[cnt], offset + x, x, tc); + cnt++; + } + GNUNET_assert (cnt == tc->link_array_size); + GNUNET_free (rows_len); +} + + +/** + * Generates ring topology + * + * @param tc the topology context + * @param links the number of random links to establish + * @param append GNUNET_YES to add links to existing link array; GNUNET_NO to + * create a new link array + */ +static void +gen_topo_random (struct TopologyContext *tc, unsigned int links, int append) +{ + unsigned int cnt; + unsigned int index; + uint32_t A_rand; + uint32_t B_rand; + + if (GNUNET_YES == append) + { + GNUNET_assert ((0 < tc->link_array_size) && (NULL != tc->link_array)); + index = tc->link_array_size; + tc->link_array_size += links; + tc->link_array = + GNUNET_realloc (tc->link_array, + sizeof (struct OverlayLink) * tc->link_array_size); + } + else + { + GNUNET_assert ((0 == tc->link_array_size) && (NULL == tc->link_array)); + index = 0; + tc->link_array_size = links; + tc->link_array = + GNUNET_malloc (sizeof (struct OverlayLink) * tc->link_array_size); + } + for (cnt = 0; cnt < links; cnt++) + { + do + { + A_rand = + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, tc->num_peers); + B_rand = + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, tc->num_peers); + } + while (A_rand == B_rand); + make_link (&tc->link_array[index + cnt], A_rand, B_rand, tc); + } +} + + +/** + * Generates scale free network. Its construction is described in: + * + * "Emergence of Scaling in Random Networks." Science 286, 509-512, 1999. + * + * @param tc the topology context + */ +static void +gen_scale_free (struct TopologyContext *tc) +{ + double random; + double probability; + unsigned int cnt; + unsigned int previous_connections; + unsigned int i; + uint16_t *popularity; + + popularity = GNUNET_malloc (sizeof (uint16_t) * tc->num_peers); + /* Initially connect peer 1 to peer 0 */ + tc->link_array_size = 1; + tc->link_array = GNUNET_malloc (sizeof (struct OverlayLink)); + make_link (&tc->link_array[0], 0, 1, tc); + popularity[0]++; /* increase popularity of 0 as 1 connected to it */ + for (cnt = 1; cnt < tc->num_peers; cnt++) + { + previous_connections = tc->link_array_size; + for (i = 0; i < cnt; i++) + { + probability = ((double) popularity[i]) / ((double) previous_connections); + random = + ((double) + GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, + UINT64_MAX)) / ((double) UINT64_MAX); + if (random < probability) + { + tc->link_array_size++; + tc->link_array = + GNUNET_realloc (tc->link_array, + (sizeof (struct OverlayLink) * + tc->link_array_size)); + make_link (&tc->link_array[tc->link_array_size - 1], cnt, i, tc); + popularity[cnt]++; + } + } + } + GNUNET_free (popularity); +} + + +/** + * Generates topology from the given file + * + * @param tc the topology context + * @param filename the filename of the file containing topology data + */ +static void +gen_topo_from_file (struct TopologyContext *tc, const char *filename) +{ + char *data; + char *end; + char *buf; + uint64_t fs; + uint64_t offset; + unsigned long int peer_id; + unsigned long int other_peer_id; + enum ParseState + { + + /** + * We read the peer index + */ + PEER_INDEX, + + /** + * We read the other peer indices + */ + OTHER_PEER_INDEX, + + } state; + int status; + + status = GNUNET_SYSERR; + if (GNUNET_YES != GNUNET_DISK_file_test (filename)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, _("Topology file %s not found\n"), filename); + return; + } + if (GNUNET_OK != + GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, _("Topology file %s has no data\n"), + filename); + return; + } + data = GNUNET_malloc (fs); + if (fs != GNUNET_DISK_fn_read (filename, data, fs)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, _("Topology file %s cannot be read\n"), + filename); + goto _exit; + } + + offset = 0; + peer_id = 0; + state = PEER_INDEX; + buf = data; + while (offset < fs) + { + if (0 != isspace (data[offset])) + { + offset++; + continue; + } + switch (state) + { + case PEER_INDEX: + buf = strchr (&data[offset], ':'); + if (NULL == buf) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Failed to read peer index from toology file: %s"), filename); + goto _exit; + } + *buf = '\0'; + errno = 0; + peer_id = (unsigned int) strtoul (&data[offset], &end, 10); + if (0 != errno) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Value in given topology file: %s out of range\n"), filename); + goto _exit; + } + if (&data[offset] == end) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Failed to read peer index from topology file: %s"), filename); + goto _exit; + } + if (tc->num_peers <= peer_id) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Topology file needs more peers than given ones\n"), filename); + goto _exit; + } + state = OTHER_PEER_INDEX; + offset += ((unsigned int) (buf - &data[offset])) + 1; + break; + case OTHER_PEER_INDEX: + errno = 0; + other_peer_id = (unsigned int) strtoul (&data[offset], &end, 10); + if (0 != errno) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Value in given topology file: %s out of range\n"), filename); + goto _exit; + } + if (&data[offset] == end) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Failed to read peer index from topology file: %s"), filename); + goto _exit; + } + if (tc->num_peers <= other_peer_id) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Topology file needs more peers than given ones\n"), filename); + goto _exit; + } + if (peer_id != other_peer_id) + { + tc->link_array_size++; + tc->link_array = + GNUNET_realloc (tc->link_array, + sizeof (struct OverlayLink) * tc->link_array_size); + offset += end - &data[offset]; + make_link (&tc->link_array[tc->link_array_size - 1], peer_id, + other_peer_id, tc); + } + else + LOG (GNUNET_ERROR_TYPE_WARNING, + _("Ignoring to connect peer %u to peer %u\n"), peer_id, + other_peer_id); + while (('\n' != data[offset]) && ('|' != data[offset]) && (offset < fs)) + offset++; + if ('\n' == data[offset]) + state = PEER_INDEX; + else if ('|' == data[offset]) + { + state = OTHER_PEER_INDEX; + offset++; + } + break; + } + } + status = GNUNET_OK; + +_exit: + GNUNET_free (data); + if (GNUNET_OK != status) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Removing link data read from the file\n"); + tc->link_array_size = 0; + GNUNET_free_non_null (tc->link_array); + tc->link_array = NULL; + } +} /** @@ -40,10 +802,12 @@ */ struct GNUNET_TESTBED_Operation * GNUNET_TESTBED_underlay_configure_topology_va (void *op_cls, - unsigned int num_peers, - struct GNUNET_TESTBED_Peer **peers, - enum GNUNET_TESTBED_TopologyOption topo, - va_list ap) + unsigned int num_peers, + struct GNUNET_TESTBED_Peer + **peers, + enum + GNUNET_TESTBED_TopologyOption + topo, va_list ap) { GNUNET_break (0); return NULL; @@ -63,10 +827,10 @@ GNUNET_TESTBED_underlay_configure_topology_va (void *op_cls, */ struct GNUNET_TESTBED_Operation * GNUNET_TESTBED_underlay_configure_topology (void *op_cls, - unsigned int num_peers, - struct GNUNET_TESTBED_Peer **peers, - enum GNUNET_TESTBED_TopologyOption topo, - ...) + unsigned int num_peers, + struct GNUNET_TESTBED_Peer **peers, + enum GNUNET_TESTBED_TopologyOption + topo, ...) { GNUNET_break (0); return NULL; @@ -78,24 +842,144 @@ GNUNET_TESTBED_underlay_configure_topology (void *op_cls, * This function then connects the given peers in the P2P overlay * using the given topology. * - * @param op_cls closure argument to give with the operation event + * @param op_cls closure argument to give with the peer connect operation events + * generated through this function * @param num_peers number of peers in 'peers' * @param peers array of 'num_peers' with the peers to configure + * @param max_connections the maximums number of overlay connections that will + * be made to achieve the given topology + * @param comp_cb the completion callback to call when the topology generation + * is completed + * @param comp_cb_cls closure for the above completion callback * @param topo desired underlay topology to use * @param va topology-specific options - * @return handle to the operation, NULL if connecting these + * @return handle to the operation, NULL if connecting these * peers is fundamentally not possible at this time (peers - * not running or underlay disallows) + * not running or underlay disallows) or if num_peers is less than 2 */ struct GNUNET_TESTBED_Operation * GNUNET_TESTBED_overlay_configure_topology_va (void *op_cls, - unsigned int num_peers, - struct GNUNET_TESTBED_Peer *peers, - enum GNUNET_TESTBED_TopologyOption topo, - va_list va) + unsigned int num_peers, + struct GNUNET_TESTBED_Peer **peers, + unsigned int *max_connections, + GNUNET_TESTBED_TopologyCompletionCallback + comp_cb, + void *comp_cb_cls, + enum GNUNET_TESTBED_TopologyOption topo, + va_list va) { - GNUNET_break (0); - return NULL; + struct TopologyContext *tc; + struct GNUNET_TESTBED_Operation *op; + struct GNUNET_TESTBED_Controller *c; + enum GNUNET_TESTBED_TopologyOption secondary_option; + unsigned int cnt; + + if (num_peers < 2) + return NULL; + c = peers[0]->controller; + tc = GNUNET_malloc (sizeof (struct TopologyContext)); + tc->peers = peers; + tc->num_peers = num_peers; + tc->op_cls = op_cls; + tc->retry_cnt = DEFAULT_RETRY_CNT; + switch (topo) + { + case GNUNET_TESTBED_TOPOLOGY_LINE: + gen_topo_line (tc); + break; + case GNUNET_TESTBED_TOPOLOGY_RING: + gen_topo_ring (tc); + break; + case GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI: + gen_topo_random (tc, va_arg (va, unsigned int), GNUNET_NO); + + break; + case GNUNET_TESTBED_TOPOLOGY_SMALL_WORLD_RING: + gen_topo_ring (tc); + gen_topo_random (tc, va_arg (va, unsigned int), GNUNET_YES); + + break; + case GNUNET_TESTBED_TOPOLOGY_CLIQUE: + tc->link_array_size = num_peers * (num_peers - 1); + tc->link_array = + GNUNET_malloc (sizeof (struct OverlayLink) * tc->link_array_size); + { + unsigned int offset; + + offset = 0; + for (cnt = 0; cnt < num_peers; cnt++) + { + unsigned int neighbour; + + for (neighbour = 0; neighbour < num_peers; neighbour++) + { + if (neighbour == cnt) + continue; + tc->link_array[offset].A = cnt; + tc->link_array[offset].B = neighbour; + tc->link_array[offset].tc = tc; + offset++; + } + } + } + break; + case GNUNET_TESTBED_TOPOLOGY_2D_TORUS: + gen_topo_2dtorus (tc); + break; + case GNUNET_TESTBED_TOPOLOGY_SMALL_WORLD: + gen_topo_2dtorus (tc); + gen_topo_random (tc, va_arg (va, unsigned int), GNUNET_YES); + + break; + case GNUNET_TESTBED_TOPOLOGY_SCALE_FREE: + gen_scale_free (tc); + break; + case GNUNET_TESTBED_TOPOLOGY_FROM_FILE: + { + const char *filename; + + filename = va_arg (va, const char *); + + GNUNET_assert (NULL != filename); + gen_topo_from_file (tc, filename); + } + break; + default: + GNUNET_break (0); + GNUNET_free (tc); + return NULL; + } + do + { + secondary_option = va_arg (va, enum GNUNET_TESTBED_TopologyOption); + + switch (secondary_option) + { + case GNUNET_TESTBED_TOPOLOGY_RETRY_CNT: + tc->retry_cnt = va_arg (va, unsigned int); + break; + case GNUNET_TESTBED_TOPOLOGY_OPTION_END: + break; + default: + GNUNET_break (0); /* Should not use any other option apart from + * the ones handled here */ + GNUNET_free_non_null (tc->link_array); + GNUNET_free (tc); + return NULL; + } + } + while (GNUNET_TESTBED_TOPOLOGY_OPTION_END != secondary_option); + op = GNUNET_TESTBED_operation_create_ (tc, + &opstart_overlay_configure_topology, + &oprelease_overlay_configure_topology); + GNUNET_TESTBED_operation_queue_insert_ + (c->opq_parallel_topology_config_operations, op); + GNUNET_TESTBED_operation_begin_wait_ (op); + LOG (GNUNET_ERROR_TYPE_DEBUG, "Generated %u connections\n", + tc->link_array_size); + if (NULL != max_connections) + *max_connections = tc->link_array_size; + return op; } @@ -104,24 +988,88 @@ GNUNET_TESTBED_overlay_configure_topology_va (void *op_cls, * This function then connects the given peers in the P2P overlay * using the given topology. * - * @param op_cls closure argument to give with the operation event + * @param op_cls closure argument to give with the peer connect operation events + * generated through this function * @param num_peers number of peers in 'peers' * @param peers array of 'num_peers' with the peers to configure + * @param max_connections the maximums number of overlay connections that will + * be made to achieve the given topology + * @param comp_cb the completion callback to call when the topology generation + * is completed + * @param comp_cb_cls closure for the above completion callback * @param topo desired underlay topology to use * @param ... topology-specific options - * @return handle to the operation, NULL if connecting these + * @return handle to the operation, NULL if connecting these * peers is fundamentally not possible at this time (peers - * not running or underlay disallows) + * not running or underlay disallows) or if num_peers is less than 2 */ struct GNUNET_TESTBED_Operation * GNUNET_TESTBED_overlay_configure_topology (void *op_cls, - unsigned int num_peers, - struct GNUNET_TESTBED_Peer *peers, - enum GNUNET_TESTBED_TopologyOption topo, - ...) + unsigned int num_peers, + struct GNUNET_TESTBED_Peer **peers, + unsigned int *max_connections, + GNUNET_TESTBED_TopologyCompletionCallback + comp_cb, + void *comp_cb_cls, + enum GNUNET_TESTBED_TopologyOption topo, + ...) { - GNUNET_break (0); - return NULL; + struct GNUNET_TESTBED_Operation *op; + va_list vargs; + + GNUNET_assert (topo < GNUNET_TESTBED_TOPOLOGY_OPTION_END); + va_start (vargs, topo); + op = GNUNET_TESTBED_overlay_configure_topology_va (op_cls, num_peers, peers, + max_connections, + comp_cb, comp_cb_cls, + topo, + vargs); + va_end (vargs); + return op; +} + + +/** + * Get a topology from a string input. + * + * @param topology where to write the retrieved topology + * @param topology_string The string to attempt to + * get a configuration value from + * @return GNUNET_YES if topology string matched a + * known topology, GNUNET_NO if not + */ +int +GNUNET_TESTBED_topology_get_ (enum GNUNET_TESTBED_TopologyOption *topology, + const char *topology_string) +{ + unsigned int cnt; + + for (cnt = 0; NULL != topology_strings[cnt]; cnt++) + { + if (0 == strcasecmp (topology_string, topology_strings[cnt])) + { + if (NULL != topology) + *topology = (enum GNUNET_TESTBED_TopologyOption) cnt; + return GNUNET_YES; + } + } + return GNUNET_NO; +} + + +/** + * Returns the string corresponding to the given topology + * + * @param topology the topology + * @return the string (freshly allocated) of given topology; NULL if topology cannot be + * expressed as a string + */ +char * +GNUNET_TESTBED_topology_to_str_ (enum GNUNET_TESTBED_TopologyOption topology) +{ + if (GNUNET_TESTBED_TOPOLOGY_OPTION_END <= topology) + return NULL; + return GNUNET_strdup (topology_strings[topology]); } /* end of testbed_api_topology.c */ diff --git a/src/testbed/testbed_api_topology.h b/src/testbed/testbed_api_topology.h new file mode 100644 index 0000000..b5f3685 --- /dev/null +++ b/src/testbed/testbed_api_topology.h @@ -0,0 +1,70 @@ +/* + This file is part of GNUnet + (C) 2008--2012 Christian Grothoff (and other contributing authors) + + GNUnet 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. + + 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file testbed/testbed_api_topology.h + * @brief header for intra library exported functions + * @author Sree Harsha Totakura <sreeharsha@totakura.in> + */ + +#ifndef TESTBED_API_TOPOLOGY_H +#define TESTBED_API_TOPOLOGY_H + +/** + * Returns the number of links that are required to generate a 2d torus for the + * given number of peers. Also returns the arrangment (number of rows and the + * length of each row) + * + * @param num_peers number of peers + * @param rows number of rows in the 2d torus. Can be NULL. + * @param rows_len the length of each row. This array will be allocated + * fresh. The caller should free it. Can be NULL. + */ +unsigned int +GNUNET_TESTBED_2dtorus_calc_links (unsigned int num_peers, unsigned int *rows, + unsigned int **rows_len); + + +/** + * Get a topology from a string input. + * + * @param topology where to write the retrieved topology + * @param topology_string The string to attempt to + * get a configuration value from + * @return GNUNET_YES if topology string matched a + * known topology, GNUNET_NO if not + */ +int +GNUNET_TESTBED_topology_get_ (enum GNUNET_TESTBED_TopologyOption *topology, + const char *topology_string); + + +/** + * Returns the string corresponding to the given topology + * + * @param topology the topology + * @return the string (freshly allocated) of given topology; NULL if topology cannot be + * expressed as a string + */ +char * +GNUNET_TESTBED_topology_to_str_ (enum GNUNET_TESTBED_TopologyOption topology); + +#endif +/* end of testbed_api_topology.h */ diff --git a/src/testbed/testbed_helper.h b/src/testbed/testbed_helper.h new file mode 100644 index 0000000..6b476cd --- /dev/null +++ b/src/testbed/testbed_helper.h @@ -0,0 +1,89 @@ +/* + This file is part of GNUnet + (C) 2012 Christian Grothoff (and other contributing authors) + + GNUnet 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. + + 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file testbed/testbed_helper.h + * @brief Message formats for communication between testbed api and + * gnunet-helper-testbed process + * @author Sree Harsha Totakura <sreeharsha@totakura.in> + */ + +#ifndef TESTBED_HELPER_H +#define TESTBED_HELPER_H + +GNUNET_NETWORK_STRUCT_BEGIN +/** + * Initialization message for gnunet-helper-testbed to start testbed service + */ + struct GNUNET_TESTBED_HelperInit +{ + /** + * Type is GNUNET_MESSAGE_TYPE_TESTBED_HELPER_INIT + */ + struct GNUNET_MessageHeader header; + + /** + * The controller hostname size excluding the NULL termination character - + * strlen (hostname); cannot be zero + */ + uint16_t trusted_ip_size GNUNET_PACKED; + + /** + * The hostname size excluding the NULL termination character - strlen + * (hostname); cannot be zero + */ + uint16_t hostname_size GNUNET_PACKED; + + /** + * The size of the uncompressed configuration + */ + uint16_t config_size GNUNET_PACKED; + + /* Followed by NULL terminated trusted ip */ + + /* Followed by hostname of the machine on which helper runs. This is not NULL + * terminated */ + + /* Followed by serialized and compressed configuration which should be + * config_size long when un-compressed */ +}; + +/** + * Reply message from helper process + */ +struct GNUNET_TESTBED_HelperReply +{ + /** + * Type is GNUNET_MESSAGE_TYPE_TESTBED_HELPER_REPLY + */ + struct GNUNET_MessageHeader header; + + /** + * Size of the uncompressed configuration + */ + uint16_t config_size GNUNET_PACKED; + + /* Followed by compressed configuration which should be config_size long when + * un-compressed */ +}; + +GNUNET_NETWORK_STRUCT_END +#endif +/* end of testbed_helper.h */ |