/* This file is part of GNUnet. (C) 2003, 2004 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 applications/testbed/gnunet-testbed.c * @brief A Testbed tool for performing distributed experiments * @author Ronaldo Alves Ferreira * @author Christian Grothoff * @author Murali Krishan Ramanathan * * Todo: * - test secure sign-on (and test testbed setup script!) * - allow removing of peers (explicitly AND when peer shuts down!) * Problem: what happens to the peer-IDs in that case? * - general problem: the way we use tcpio means that any rouge * testbed-gnunetd can stall gnunet-testbed indefinitely! * - security: limit "exec" to certain processes */ #include "gnunet_util.h" #include "platform.h" #include "testbed.h" #include "socket.h" #include "commands.h" #include #ifndef sighandler_t typedef void (*sighandler_t)(int); #endif #define TESTBED_VERSION "0.0.5" #define HELPER "==HELPER==" /* we may want to change SHELL and PORT into values obtained from the configuration... */ #define SHELL (NULL == getenv("BASH") ? "/bin/bash" : getenv("BASH")) #define PORT getConfigurationInt("GNUNET-TESTBED","PORT") /* TB_ALIASES should probably be forced to live somewhere under ~/.gnunet */ #define TB_ALIASES "/tmp/gnunet-testbed-aliasdefinitions" /** * Name of gnunet-testbed binary. */ static char * testbedArg0; /* ****************** scriptability *************** */ #ifndef MINGW /* FIXME MINGW */ /** * Parse the options, set the timeout. * * @param argc the number of options * @param argv the option list (including keywords) * @return OK on error, SYSERR if we should exit */ static int helperParseOptions(int argc, char *argv[]) { int c, option_index; FREENONNULL(setConfigurationString("GNUNETD", "LOGFILE", NULL)); while (1) { static struct GNoption long_options[] = { LONG_DEFAULT_OPTIONS, { 0,0,0,0 } }; option_index = 0; c = GNgetopt_long(argc, argv, "vhdc:L:", long_options, &option_index); if (c == -1) break; /* No more flags to process */ if (YES == parseDefaultOptions(c, GNoptarg)) continue; switch(c) { case 'v': printf("GNUnet v%s, gnunet-testbed v%s\n", VERSION, TESTBED_VERSION); return SYSERR; case 'h': { static Help help[] = { HELP_CONFIG, HELP_HELP, HELP_LOGLEVEL, HELP_VERSION, HELP_END, }; formatHelp("gnunet-testbed ==HELPER== [OPTIONS] [COMMAND]", _("Start GNUnet-testbed helper."), help); return SYSERR; } default: LOG(LOG_FAILURE, _("Use --help to get a list of options.\n")); return -1; } /* end of parsing commandline */ } /* while (1) */ setConfigurationStringList(&argv[GNoptind], argc - GNoptind); return OK; } /** * This is the main method of the helper-process that * is invoked from the bash-process. Helper encapsulates * the command from the shell in a stream, sends it * over the socket to the main gnunet-testbed process, * retrieves there result and outputs he result back * to bash. */ static int helper_main(int argc, char * argv[]) { int i, retVal, len, res; char *buf; struct sockaddr_in soaddr; if (SYSERR == initUtil(argc, argv, &helperParseOptions)) return -1; argc = getConfigurationStringList(&argv); if (argc == 0) { fprintf(stderr, " must have at least one argument!\n"); return -1; } sock = SOCKET(PF_INET, SOCK_STREAM, 6); /* 6: TCP */ if (sock == -1) { LOG_STRERROR(LOG_FAILURE, "socket"); return SYSERR; } soaddr.sin_family = AF_INET; soaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); soaddr.sin_port = htons(PORT); res = CONNECT(sock, (struct sockaddr*)&soaddr, sizeof(soaddr)); if ( (res < 0) && (errno != EINPROGRESS) ) { LOG(LOG_INFO, _("Cannot connect to LOOPBACK port %d: %s\n"), PORT, STRERROR(errno)); CLOSE(sock); sock = -1; return SYSERR; } /* write command to socket */ socketSend(strlen(argv[0]), SOCKET_BEGIN_COMMAND, argv[0]); FREE(argv[0]); /* write args to socket */ for (i=1;i 1) { if (strcmp(argv[1], HELPER) == 0) { argv[1] = argv[0]; return helper_main(argc -1, &argv[1]); } } if (SYSERR == initUtil(argc, argv, &parseOptions)) return -1; ch = getConfigurationString("GNUNET-TESTBED", "TRUSTED"); if (ch == NULL) { trustedNetworks_ = parseRoutes("127.0.0.0/8;"); /* by default, trust localhost only */ } else { trustedNetworks_ = parseRoutes(ch); if (trustedNetworks_ == NULL) errexit(_("Malformed entry in the configuration in section %s under %s: %s\n"), "GNUNET-TESTBED", "TRUSTED", ch); FREE(ch); } /* we are the main testbed process. Fork of a shell and start processing from the socket */ pid = fork(); if (pid < 0) DIE_STRERROR("fork"); if (pid == 0) { FREE(trustedNetworks_); bash_main(); return 0; /* unreached */ } else { int ret; /* run actual main loop */ ret = server_main(pid); /* just to be certain */ kill(pid, SIGHUP); /* proper shutdown... */ doneUtil(); FREE(trustedNetworks_); UNLINK(TB_ALIASES); FREE(testbedArg0); return ret; } #endif } /* end of gnunet-testbed.c */