- distribute peers equally among island nodes on SuperMUC
[oweals/gnunet.git] / src / testbed / gnunet-service-testbed.c
index d9a2a6c133ca0e6ec9c16285a2b355441a827834..f3d629f5f8b63064a544d8b420694b6a628b893b 100644 (file)
@@ -127,10 +127,6 @@ static struct MessageQueue *mq_head;
  */
 static struct MessageQueue *mq_tail;
 
-/**
- * The hashmap of shared services
- */
-static struct GNUNET_CONTAINER_MultiHashMap *ss_map;
 
 /**
  * The shutdown task handle
@@ -234,19 +230,6 @@ host_list_add (struct GNUNET_TESTBED_Host *host)
 }
 
 
-/**
- * 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
  *
@@ -439,6 +422,99 @@ GST_forwarded_operation_timeout (void *cls,
 }
 
 
+/**
+ * Parse service sharing specification line.
+ * Format is "[<service:share>] [<service:share>] ..."
+ *
+ * @param ss_str the spec string to be parsed
+ * @param cfg the configuration to use for shared services
+ * @return an array suitable to pass to GNUNET_TESTING_system_create().  NULL
+ *           upon empty service sharing specification.
+ */
+static struct GNUNET_TESTING_SharedService *
+parse_shared_services (char *ss_str, struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+  struct GNUNET_TESTING_SharedService ss;
+  struct GNUNET_TESTING_SharedService *slist;
+  char service[256];
+  char *arg;
+  unsigned int n;
+#define GROW_SS                                 \
+  do {                                          \
+    GNUNET_array_grow (slist, n, n+1);                                  \
+    (void) memcpy (&slist[n - 1], &ss,                                  \
+                   sizeof (struct GNUNET_TESTING_SharedService));       \
+  } while (0)
+  
+  slist = NULL;
+  n = 0;
+  ss.cfg = cfg;
+  for (; NULL != (arg = strtok (ss_str, " ")); ss_str = NULL)
+  {
+    ss.service = NULL;
+    ss.share = 0;
+    if (2 != sscanf (arg, "%255[^:]:%u", service, &ss.share))
+    {
+      LOG (GNUNET_ERROR_TYPE_WARNING, "Ignoring shared service spec: %s", arg);
+      continue;
+    }
+    LOG_DEBUG ("Will be sharing %s service among %u peers\n", service, ss.share);
+    ss.service = GNUNET_strdup (service);
+    GROW_SS;
+  }
+  if (NULL != slist)
+  {
+    /* Add trailing NULL block */
+    (void) memset (&ss, 0, sizeof (struct GNUNET_TESTING_SharedService));
+    GROW_SS;
+  }
+  return slist;
+#undef GROW_SS
+}
+
+
+/**
+ * Callback function invoked for each interface found.
+ *
+ * @param cls NULL
+ * @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 
+addr_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 Context *ctx = cls;
+  const struct sockaddr_in *in_addr;
+  char *ipaddr;
+  char *tmp;  
+
+  if (sizeof (struct sockaddr_in) != addrlen)
+    return GNUNET_OK;
+  in_addr = (const struct sockaddr_in *) addr;
+  if (NULL == (ipaddr = inet_ntoa (in_addr->sin_addr)))
+    return GNUNET_OK;
+  if (NULL == ctx->master_ips)
+  {
+    ctx->master_ips = GNUNET_strdup (ipaddr);
+    return GNUNET_OK;
+  }
+  tmp = NULL;
+  (void) GNUNET_asprintf (&tmp, "%s; %s", ctx->master_ips, ipaddr);
+  GNUNET_free (ctx->master_ips);
+  ctx->master_ips = tmp;
+  return GNUNET_OK;
+}
+
+
+
 /**
  * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_INIT messages
  *
@@ -452,8 +528,11 @@ handle_init (void *cls, struct GNUNET_SERVER_Client *client,
 {
   const struct GNUNET_TESTBED_InitMessage *msg;
   struct GNUNET_TESTBED_Host *host;
-  const char *controller_hostname;
-  uint16_t msize;
+  char *ss_str;
+  struct GNUNET_TESTING_SharedService *ss;
+  char *hostname;
+  unsigned int cnt;
+  unsigned int len;
 
   if (NULL != GST_context)
   {
@@ -462,34 +541,54 @@ handle_init (void *cls, struct GNUNET_SERVER_Client *client,
     return;
   }
   msg = (const struct GNUNET_TESTBED_InitMessage *) message;
-  msize = ntohs (message->size);
-  if (msize <= sizeof (struct GNUNET_TESTBED_InitMessage))
+  len = GNUNET_OS_get_hostname_max_length ();
+  hostname = GNUNET_malloc (len);
+  if (0 != gethostname (hostname, len))
   {
-    GNUNET_break (0);
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "gethostname");
     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])
+  ss_str = NULL;
+  ss = NULL;
+  if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (our_config, "TESTBED",
+                                                          "SHARED_SERVICES",
+                                                          &ss_str))
   {
-    GNUNET_break (0);
-    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
-    return;
+    ss = parse_shared_services (ss_str, our_config);
+    GNUNET_free (ss_str);
+    ss_str = NULL;
   }
   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);
+  GNUNET_OS_network_interfaces_list (&addr_proc, GST_context);
+  if (NULL == GST_context->master_ips)
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR, 
+         "Testbed needs networking, but no network interfaces are found on this host.  Exiting\n");
+    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  LOG_DEBUG ("Our IP addresses: %s\n", GST_context->master_ips);
   GST_context->system =
-      GNUNET_TESTING_system_create ("testbed", GST_context->master_ip,
-                                    hostname, NULL);
-  host =
-      GNUNET_TESTBED_host_create_with_id (GST_context->host_id,
-                                          GST_context->master_ip, NULL,
-                                          our_config, 0);
+      GNUNET_TESTING_system_create ("testbed", GST_context->master_ips,
+                                    hostname, ss);
+  if (NULL != ss)
+  {
+    for (cnt = 0; NULL != ss[cnt].service; cnt++)
+    {
+      ss_str = (char *) ss[cnt].service;
+      GNUNET_free (ss_str);
+    }
+    GNUNET_free (ss);
+    ss = NULL;
+  }
+
+  host = GNUNET_TESTBED_host_create_with_id (GST_context->host_id, hostname,
+                                             NULL, our_config, 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);
@@ -613,93 +712,6 @@ handle_add_host (void *cls, struct GNUNET_SERVER_Client *client,
 }
 
 
-/**
- * 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);
-}
-
-
 /**
  * Handler for GNUNET_MESSAGE_TYPE_TESTBED_GETSLAVECONFIG messages
  *
@@ -792,6 +804,7 @@ GST_clear_fopcq ()
     case OP_LINK_CONTROLLERS:
     case OP_GET_SLAVE_CONFIG:
     case OP_MANAGE_SERVICE:
+    case OP_PEER_RECONFIGURE:
       break;
     case OP_FORWARDED:
       GNUNET_assert (0);
@@ -801,29 +814,6 @@ GST_clear_fopcq ()
 }
 
 
-/**
- * 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;
-}
-
-
 /**
  * Task to clean up and shutdown nicely
  *
@@ -837,10 +827,7 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   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);
+  LOG_DEBUG ("Shutting down testbed service\n");
   /* cleanup any remaining forwarded operations */
   GST_clear_fopcq ();
   GST_free_lcfq ();
@@ -849,6 +836,7 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   GST_free_roccq ();
   GST_free_nccq ();
   GST_neighbour_list_clean();
+  GST_free_prcq ();
   /* Clear peer list */
   GST_destroy_peers ();
   /* Clear route list */
@@ -862,7 +850,7 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   GNUNET_free_non_null (GST_host_list);
   if (NULL != GST_context)
   {
-    GNUNET_free_non_null (GST_context->master_ip);
+    GNUNET_free_non_null (GST_context->master_ips);
     if (NULL != GST_context->system)
       GNUNET_TESTING_system_destroy (GST_context->system, GNUNET_YES);
     GNUNET_SERVER_client_drop (GST_context->client);
@@ -924,10 +912,9 @@ 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_init, NULL, GNUNET_MESSAGE_TYPE_TESTBED_INIT,
+     sizeof (struct GNUNET_TESTBED_InitMessage)},
     {&handle_add_host, NULL, GNUNET_MESSAGE_TYPE_TESTBED_ADD_HOST, 0},
-    {&handle_configure_shared_service, NULL,
-     GNUNET_MESSAGE_TYPE_TESTBED_SHARE_SERVICE, 0},
     {&GST_handle_link_controllers, NULL,
      GNUNET_MESSAGE_TYPE_TESTBED_LINK_CONTROLLERS,
      sizeof (struct GNUNET_TESTBED_ControllerLinkRequest)},
@@ -953,11 +940,14 @@ testbed_run (void *cls, struct GNUNET_SERVER_Handle *server,
      sizeof (struct GNUNET_TESTBED_SlaveGetConfigurationMessage)},
     {&GST_handle_shutdown_peers, NULL, GNUNET_MESSAGE_TYPE_TESTBED_SHUTDOWN_PEERS,
      sizeof (struct GNUNET_TESTBED_ShutdownPeersMessage)},
+    {&GST_handle_peer_reconfigure, NULL, 
+     GNUNET_MESSAGE_TYPE_TESTBED_RECONFIGURE_PEER, 0},
     {NULL, NULL, 0, 0}
   };
   char *logfile;
   unsigned long long num;
 
+  LOG_DEBUG ("Starting testbed\n");
   if (GNUNET_OK ==
       GNUNET_CONFIGURATION_get_value_filename (cfg, "TESTBED", "LOG_FILE",
                                                &logfile))
@@ -985,7 +975,6 @@ testbed_run (void *cls, struct GNUNET_SERVER_Handle *server,
   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,