- towards on-demand controller linking
authorSree Harsha Totakura <totakura@in.tum.de>
Thu, 11 Apr 2013 16:35:18 +0000 (16:35 +0000)
committerSree Harsha Totakura <totakura@in.tum.de>
Thu, 11 Apr 2013 16:35:18 +0000 (16:35 +0000)
src/testbed/gnunet-service-testbed.c
src/testbed/gnunet-service-testbed.h
src/testbed/gnunet-service-testbed_links.c
src/testbed/gnunet-service-testbed_links.h [new file with mode: 0644]
src/testbed/gnunet-service-testbed_oc.c

index 25bded788419626b82a24f5ca94b02672c599d77..7864b09841f637395a12dc1b4fac7bcae7580866 100644 (file)
@@ -847,6 +847,8 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   GST_free_mctxq ();
   GST_free_occq ();
   GST_free_roccq ();
+  GST_free_nccq ();
+  GST_neighbour_list_clean();
   /* Clear peer list */
   GST_destroy_peers ();
   /* Clear host list */
index 2a410a850db25b317992e01512fbe4a5606814e4..b17c3d7818fe11402d19709a1790cef05d491a81 100644 (file)
@@ -35,6 +35,7 @@
 #include "testbed_api_operations.h"
 #include "testbed_api_hosts.h"
 #include "gnunet_testing_lib.h"
+#include "gnunet-service-testbed_links.h"
 
 
 /**
@@ -170,55 +171,6 @@ struct LinkControllersContext
 };
 
 
-/**
- * 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;
-
-  /**
-   * 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
  */
@@ -486,119 +438,6 @@ struct RegisteredHostContext
 };
 
 
-/**
- * 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 type of this data structure. Set this to CLOSURE_TYPE_LCF
-   */
-  enum ClosureType type;
-  
-  /**
-   * The gateway which will pass the link message to delegated host
-   */
-  struct Slave *gateway;
-
-  /**
-   * The client which has asked to perform this operation
-   */
-  struct GNUNET_SERVER_Client *client;
-
-  /**
-   * Handle for operations which are forwarded while linking controllers
-   */
-  struct GNUNET_TESTBED_Operation *op;
-
-  /**
-   * The configuration which has to be either used as a template while starting
-   * the delegated controller or for connecting to the delegated controller
-   */
-  struct GNUNET_CONFIGURATION_Handle *cfg;
-
-  /**
-   * The timeout task
-   */
-  GNUNET_SCHEDULER_TaskIdentifier timeout_task;
-
-  /**
-   * The id of the operation which created this context
-   */
-  uint64_t operation_id;
-  
-  /**
-   * should the slave controller start the delegated controller?
-   */
-  int is_subordinate;
-
-  /**
-   * 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;
-};
-
-
 /**
  * Context data for GNUNET_MESSAGE_TYPE_TESTBED_SHUTDOWN_PEERS handler
  */
@@ -648,11 +487,6 @@ extern struct Peer **GST_peer_list;
  */
 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
  */
@@ -673,11 +507,6 @@ extern unsigned int GST_peer_list_size;
  */
 extern unsigned int GST_host_list_size;
 
-/**
- * The size of directly linked neighbours list
- */
-extern unsigned int GST_slave_list_size;
-
 /**
  * The directory where to store load statistics data
  */
@@ -747,18 +576,6 @@ struct Route *
 GST_find_dest_route (uint32_t host_id);
 
 
-/**
- * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_LCONTROLLERS message
- *
- * @param cls NULL
- * @param client identification of the client
- * @param message the actual message
- */
-void
-GST_handle_link_controllers (void *cls, struct GNUNET_SERVER_Client *client,
-                             const struct GNUNET_MessageHeader *message);
-
-
 /**
  * Handler for GNUNET_MESSAGE_TYPE_TESTBED_OLCONNECT messages
  *
@@ -956,13 +773,6 @@ void
 GST_route_list_clear ();
 
 
-/**
- * Cleans up the slave list
- */
-void
-GST_slave_list_clear ();
-
-
 /**
  * Processes a forwarded overlay connect context in the queue of the given RegisteredHostContext
  *
index d09e4aff6e5d078c7b2b9f9e7fff66133ff3b9f4..ae984e331e9bde6143ee3d7db82f5ab4788bff2b 100644 (file)
 */
 
 /**
- * @file testbed/gnunet-service-testbed.c
- * @brief implementation of the TESTBED service
+ * @file testbed/gnunet-service-testbed_links.c
+ * @brief TESTBED service components that deals with starting slave controllers
+ *          and establishing lateral links between controllers
  * @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-links", __VA_ARGS__)
+
 /**
  * The event mask for the events we listen from sub-controllers
  */
 #define EVENT_MASK (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED)
 
+
+/**
+ * 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 type of this data structure. Set this to CLOSURE_TYPE_LCF
+   */
+  enum ClosureType type;
+  
+  /**
+   * The gateway which will pass the link message to delegated host
+   */
+  struct Slave *gateway;
+
+  /**
+   * The client which has asked to perform this operation
+   */
+  struct GNUNET_SERVER_Client *client;
+
+  /**
+   * Handle for operations which are forwarded while linking controllers
+   */
+  struct GNUNET_TESTBED_Operation *op;
+
+  /**
+   * The configuration which has to be either used as a template while starting
+   * the delegated controller or for connecting to the delegated controller
+   */
+  struct GNUNET_CONFIGURATION_Handle *cfg;
+
+  /**
+   * The timeout task
+   */
+  GNUNET_SCHEDULER_TaskIdentifier timeout_task;
+
+  /**
+   * The id of the operation which created this context
+   */
+  uint64_t operation_id;
+  
+  /**
+   * should the slave controller start the delegated controller?
+   */
+  int is_subordinate;
+
+  /**
+   * 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;
+};
+
+struct NeighbourConnectNotification
+{
+  struct NeighbourConnectNotification *next;
+  struct NeighbourConnectNotification *prev;
+  struct Neighbour *n;
+  GST_NeigbourConnectNotifyCallback cb;
+  void *cb_cls;
+};
+  
+/**
+ * A connected controller which is not our child
+ */
+struct Neighbour
+{
+  /**
+   * The controller handle
+   */
+  struct GNUNET_TESTBED_Controller *controller;
+  
+  /**
+   * Operation handle for opening a lateral connection to another controller.
+   * Will be NULL if the slave controller is started by this controller
+   */
+  struct GNUNET_TESTBED_Operation *conn_op;
+
+  struct NeighbourConnectNotification *nl_head;
+
+  struct NeighbourConnectNotification *nl_tail;
+
+  GNUNET_SCHEDULER_TaskIdentifier notify_task;
+
+  unsigned int reference_cnt;
+  
+  /**
+   * The id of the host this controller is running on
+   */
+  uint32_t host_id;
+  
+  int8_t inactive;
+};
+
+static struct Neighbour **neighbour_list;
+static unsigned int neighbour_list_size;
+
+struct NeighbourConnectCtxt
+{
+  struct NeighbourConnectCtxt *next;
+  struct NeighbourConnectCtxt *prev;
+  struct Neighbour *n;
+  struct GNUNET_SERVER_Client *client;
+  GNUNET_SCHEDULER_TaskIdentifier timeout_task;
+  struct NeighbourConnectNotification *nh;
+  uint64_t op_id;
+};
+
+struct NeighbourConnectCtxt *ncc_head;
+struct NeighbourConnectCtxt *ncc_tail;
+
 /**
  * A list of directly linked neighbours
  */
@@ -97,6 +278,15 @@ route_list_add (struct Route *route)
   route_list[route->dest] = route;
 }
 
+static void
+neighbour_list_add (struct Neighbour *n)
+{
+  if (n->host_id >= neighbour_list_size)
+    GST_array_grow_large_enough (neighbour_list, neighbour_list_size, n->host_id);
+  GNUNET_assert (NULL == neighbour_list[n->host_id]);
+  neighbour_list[n->host_id] = n;
+}
+
 
 /**
  * Cleans up the route list
@@ -425,7 +615,7 @@ lcf_proc_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 /**
  * Callback for event from slave controllers
  *
- * @param cls struct Slave *
+ * @param cls NULL
  * @param event information about the event
  */
 static void
@@ -491,6 +681,9 @@ slave_event_callback (void *cls,
   GNUNET_assert (0);
 }
 
+static void
+slave_status_callback (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg,
+                       int status);
 
 /**
  * Callback to signal successfull startup of the controller process
@@ -552,6 +745,201 @@ clean_lcc:
     slave->lcc = NULL;
 }
 
+static void
+neighbour_connect_notify_task (void *cls, 
+                               const struct GNUNET_SCHEDULER_TaskContext *tc);
+
+static void
+trigger_notifications (struct Neighbour *n)
+{
+  GNUNET_assert (NULL != n->conn_op);
+  if (NULL == n->nl_head)
+    return;
+  if (NULL == n->controller)
+    return;
+  if (GNUNET_SCHEDULER_NO_TASK != n->notify_task)
+    return;
+  n->notify_task = 
+      GNUNET_SCHEDULER_add_now (&neighbour_connect_notify_task, n->nl_head);
+}
+
+static void
+neighbour_connect_notify_task (void *cls, 
+                               const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct NeighbourConnectNotification *h = cls;
+  struct Neighbour *n;
+
+  n = h->n;  
+  GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != n->notify_task);  
+  n->notify_task = GNUNET_SCHEDULER_NO_TASK;
+  GNUNET_assert (NULL != n->controller);
+  GNUNET_CONTAINER_DLL_remove (n->nl_head, n->nl_tail, h);  
+  trigger_notifications (n);
+  if ((0 == n->reference_cnt) && (1 == n->inactive))
+  {
+    GNUNET_TESTBED_operation_activate_ (n->conn_op);
+    n->inactive = 0;
+  }
+  n->reference_cnt++;
+  h->cb (h->cb_cls, n->controller);
+}
+
+static void
+opstart_neighbour_conn (void *cls)
+{
+  struct Neighbour *n = cls;
+  
+  GNUNET_assert (NULL != n->conn_op);
+  GNUNET_assert (NULL == n->controller);
+  LOG_DEBUG ("Opening connection to controller on host %u\n", n->host_id);
+  n->controller = GNUNET_TESTBED_controller_connect (GST_host_list[n->host_id],
+                                                     EVENT_MASK,
+                                                     &slave_event_callback,
+                                                     NULL);
+  trigger_notifications (n);
+}
+
+static void
+oprelease_neighbour_conn (void *cls)
+{
+   struct Neighbour *n = cls;
+
+   GNUNET_assert (0 == n->reference_cnt);
+   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == n->notify_task);
+   GNUNET_assert (NULL == n->nl_head);
+   LOG_DEBUG ("Closing connection to controller on host %u\n", n->host_id);
+   GNUNET_TESTBED_controller_disconnect (n->controller);
+   n->controller = NULL;
+   n->conn_op = NULL;
+}
+
+struct NeighbourConnectNotification *
+GST_neighbour_get_connection (struct Neighbour *n,
+                              GST_NeigbourConnectNotifyCallback cb,
+                              void *cb_cls)
+{
+  struct NeighbourConnectNotification *h;
+
+  GNUNET_assert (NULL != cb);
+  LOG_DEBUG ("Attempting to get connection to controller on host %u\n",
+             n->host_id);
+  h = GNUNET_malloc (sizeof (struct NeighbourConnectNotification));
+  h->n = n;
+  h->cb  = cb;
+  h->cb_cls = cb_cls;
+  GNUNET_CONTAINER_DLL_insert_tail (n->nl_head, n->nl_tail, h);
+  if (NULL == n->conn_op)
+  {
+    GNUNET_assert (NULL == n->controller);
+    n->conn_op = GNUNET_TESTBED_operation_create_ (n, &opstart_neighbour_conn,
+                                                   &oprelease_neighbour_conn);
+    GNUNET_TESTBED_operation_queue_insert_ (GST_opq_openfds, n->conn_op);
+    GNUNET_TESTBED_operation_begin_wait_ (n->conn_op);
+    return h;
+  }
+  trigger_notifications (n);
+  return h;
+}
+
+void
+GST_neighbour_get_connection_cancel (struct NeighbourConnectNotification *h)
+{
+  struct Neighbour *n;
+  
+  n = h->n;
+  if ((h == n->nl_head) && (GNUNET_SCHEDULER_NO_TASK != n->notify_task))
+  {
+    GNUNET_SCHEDULER_cancel (n->notify_task);
+    n->notify_task = GNUNET_SCHEDULER_NO_TASK;
+  }
+  GNUNET_CONTAINER_DLL_remove (n->nl_head, n->nl_tail, h);
+  GNUNET_free (h);
+}
+
+void
+GST_neighbour_release_connection (struct Neighbour *n)
+{
+  GNUNET_assert (0 == n->inactive);
+  GNUNET_assert (0 < n->reference_cnt);
+  n->reference_cnt--;
+  if (0 == n->reference_cnt)
+  {
+    GNUNET_TESTBED_operation_inactivate_ (n->conn_op);
+    n->inactive = 1;
+  }
+}
+
+static void
+cleanup_ncc (struct NeighbourConnectCtxt *ncc)
+{
+  if (NULL != ncc->nh)
+    GST_neighbour_get_connection_cancel (ncc->nh);
+  if (GNUNET_SCHEDULER_NO_TASK != ncc->timeout_task)
+    GNUNET_SCHEDULER_cancel (ncc->timeout_task);
+  GNUNET_SERVER_client_drop (ncc->client);
+  GNUNET_CONTAINER_DLL_remove (ncc_head, ncc_tail, ncc);
+  GNUNET_free (ncc);
+}
+
+void
+GST_neighbour_list_clean()
+{
+  struct Neighbour *n;
+  unsigned int id;
+
+  for (id = 0; id < neighbour_list_size; id++)
+  {
+    if (NULL == (n = neighbour_list[id]))
+      continue;
+    if (NULL != n->conn_op)
+      GNUNET_TESTBED_operation_release_ (n->conn_op);
+    GNUNET_free (n);
+    neighbour_list[id] = NULL;
+  }
+  GNUNET_free_non_null (neighbour_list);
+}
+
+struct Neighbour *
+GST_get_neighbour (uint32_t id)
+{
+  if (neighbour_list_size <= id)
+    return NULL;
+  else
+    return neighbour_list[id];
+}
+
+void
+GST_free_nccq ()
+{
+  while (NULL != ncc_head)
+    cleanup_ncc (ncc_head);
+}
+
+static void
+timeout_neighbour_connect (void *cls, 
+                           const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct NeighbourConnectCtxt *ncc = cls;
+
+ ncc->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+ send_controller_link_response (ncc->client, ncc->op_id, NULL,
+                                "Could not connect to delegated controller");
+ cleanup_ncc (ncc);
+}
+
+static void
+neighbour_connect_cb (void *cls, struct GNUNET_TESTBED_Controller *c)
+{
+  struct NeighbourConnectCtxt *ncc = cls;
+
+  GNUNET_SCHEDULER_cancel (ncc->timeout_task);
+  ncc->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+  ncc->nh = NULL;
+  GST_neighbour_release_connection (ncc->n);
+  send_controller_link_response (ncc->client, ncc->op_id, NULL, NULL);
+  cleanup_ncc (ncc);
+}
 
 /**
  * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_LCONTROLLERS message
@@ -569,6 +957,7 @@ GST_handle_link_controllers (void *cls, struct GNUNET_SERVER_Client *client,
   struct LCFContextQueue *lcfq;
   struct Route *route;
   struct Route *new_route;
+  uint64_t op_id;
   uint32_t delegated_host_id;
   uint32_t slave_host_id;
   uint16_t msize;
@@ -625,11 +1014,43 @@ GST_handle_link_controllers (void *cls, struct GNUNET_SERVER_Client *client,
     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
     return;
   }
+  op_id = GNUNET_ntohll (msg->operation_id);
   if (slave_host_id == GST_context->host_id)    /* Link from us */
   {
     struct Slave *slave;
     struct LinkControllersContext *lcc;
 
+    
+    if (1 != msg->is_subordinate)
+    {
+      struct Neighbour *n;
+      struct NeighbourConnectCtxt *ncc;
+
+      if ((delegated_host_id < neighbour_list_size) &&
+        (NULL != neighbour_list[delegated_host_id]))
+      {
+        GNUNET_break (0);
+        GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+        return;
+      }
+      LOG_DEBUG ("Received request to establish a link to host %u\n",
+                 delegated_host_id);
+      n = GNUNET_malloc (sizeof (struct Neighbour));
+      n->host_id = delegated_host_id;
+      neighbour_list_add (n);   /* just add; connect on-demand */
+      ncc = GNUNET_malloc (sizeof (struct NeighbourConnectCtxt));
+      ncc->n = n;
+      ncc->op_id = op_id;
+      ncc->client = client;
+      GNUNET_SERVER_client_keep (client);      
+      ncc->nh = GST_neighbour_get_connection (n, neighbour_connect_cb, ncc);
+      ncc->timeout_task = GNUNET_SCHEDULER_add_delayed (GST_timeout,
+                                                        &timeout_neighbour_connect,
+                                                        ncc);
+      GNUNET_CONTAINER_DLL_insert_tail (ncc_head, ncc_tail, ncc);      
+      GNUNET_SERVER_receive_done (client, GNUNET_OK);
+      return;
+    }
     if ((delegated_host_id < GST_slave_list_size) &&
         (NULL != GST_slave_list[delegated_host_id]))
     {
@@ -637,31 +1058,14 @@ GST_handle_link_controllers (void *cls, struct GNUNET_SERVER_Client *client,
       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
       return;
     }
+    LOG_DEBUG ("Received request to start and establish a link to host %u\n",
+               delegated_host_id);
     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 (GST_host_list[slave->host_id],
-                                             EVENT_MASK, &slave_event_callback,
-                                             slave);
-      if (NULL != slave->controller)
-        send_controller_link_response (client,
-                                       GNUNET_ntohll (msg->operation_id),
-                                       NULL,
-                                       NULL);
-      else
-        send_controller_link_response (client,
-                                       GNUNET_ntohll (msg->operation_id),
-                                       NULL,
-                                       "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);
+    lcc->operation_id = op_id;
     GNUNET_SERVER_client_keep (client);
     lcc->client = client;
     slave->lcc = lcc;
@@ -696,7 +1100,7 @@ GST_handle_link_controllers (void *cls, struct GNUNET_SERVER_Client *client,
   lcfq->lcf->cfg = cfg;
   lcfq->lcf->is_subordinate = msg->is_subordinate;
   lcfq->lcf->state = INIT;
-  lcfq->lcf->operation_id = GNUNET_ntohll (msg->operation_id);
+  lcfq->lcf->operation_id = op_id;
   lcfq->lcf->gateway = GST_slave_list[route->dest];
   GNUNET_SERVER_client_keep (client);
   lcfq->lcf->client = client;
diff --git a/src/testbed/gnunet-service-testbed_links.h b/src/testbed/gnunet-service-testbed_links.h
new file mode 100644 (file)
index 0000000..b6a38c0
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+  This file is part of GNUnet.
+  (C) 2008--2013 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_links.h
+ * @brief TESTBED service components that deals with starting slave controllers
+ *          and establishing lateral links between controllers
+ * @author Sree Harsha Totakura
+ */
+
+struct Neighbour;
+
+
+/**
+ * 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;
+
+  /**
+   * 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;
+
+  /**
+   * Operation handle for opening a lateral connection to another controller.
+   * Will be NULL if the slave controller is started by this controller
+   */
+  struct GNUNET_TESTBED_Operation *conn_op;
+
+  /**
+   * The id of the host this controller is running on
+   */
+  uint32_t host_id;
+
+};
+
+/**
+ * A list of directly linked neighbours
+ */
+extern struct Slave **GST_slave_list;
+
+/**
+ * The size of directly linked neighbours list
+ */
+extern unsigned int GST_slave_list_size;
+
+void
+GST_neighbour_list_clean();
+
+struct Neighbour *
+GST_get_neighbour (uint32_t id);
+
+void
+GST_free_nccq ();
+
+struct NeighbourConnectNotification;
+
+typedef void (*GST_NeigbourConnectNotifyCallback) (void *cls,
+                                                   struct
+                                                   GNUNET_TESTBED_Controller
+                                                   *controller);
+
+struct NeighbourConnectNotification *
+GST_neighbour_get_connection (struct Neighbour *n,
+                              GST_NeigbourConnectNotifyCallback cb,
+                              void *cb_cls);
+
+void
+GST_neighbour_get_connection_cancel (struct NeighbourConnectNotification *h);
+
+void
+GST_neighbour_release_connection (struct Neighbour *n);
+
+/**
+ * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_LCONTROLLERS message
+ *
+ * @param cls NULL
+ * @param client identification of the client
+ * @param message the actual message
+ */
+void
+GST_handle_link_controllers (void *cls, struct GNUNET_SERVER_Client *client,
+                             const struct GNUNET_MessageHeader *message);
+
+
+/**
+ * Cleans up the slave list
+ */
+void
+GST_slave_list_clear ();
index 07a9fd0f01b4ee3ef874192d455f13fc311e0c9f..d27c2bbf75ff39bd0be026142d530d8c56abe83c 100644 (file)
@@ -150,9 +150,14 @@ struct OverlayConnectContext
   struct OperationContext *opc;
 
   /**
-   * Controller of peer 2; NULL if the peer is a local peer
+   * Controller of peer 2; NULL if the peer is a local peer or until the
+   * connection to the controller is established
    */
-  struct GNUNET_TESTBED_Controller *peer2_controller;
+  struct GNUNET_TESTBED_Controller *p2c;
+  
+  struct NeighbourConnectNotification *p2_ncn;
+
+  struct Neighbour *p2n;
 
   /**
    * The transport TryConnectContext. This will be NULL if the second peer is a
@@ -418,16 +423,23 @@ cleanup_occ (struct OverlayConnectContext *occ)
     GST_cache_get_handle_done (occ->cgh_p1th);
   if (NULL != occ->tcc.cgh_th)
     GST_cache_get_handle_done (occ->tcc.cgh_th);
+  if (NULL != occ->p2n)
+  {
+    if (NULL != occ->p2_ncn)
+      GST_neighbour_get_connection_cancel (occ->p2_ncn);
+    if (NULL != occ->p2c)
+      GST_neighbour_release_connection (occ->p2n);
+  }
   GNUNET_assert (NULL != GST_peer_list);
   GNUNET_assert (occ->peer->reference_cnt > 0);  
   occ->peer->reference_cnt--;
   if ( (GNUNET_YES == occ->peer->destroy_flag) &&
        (0 == occ->peer->reference_cnt) )
     GST_destroy_peer (occ->peer);
-  if (NULL == occ->peer2_controller)
+  if ( (occ->other_peer_id < GST_peer_list_size)
+       && (NULL != (other_peer = GST_peer_list[occ->other_peer_id]))
+       && (GNUNET_YES != other_peer->is_remote) )
   {
-    other_peer = GST_peer_list[occ->other_peer_id];
-    GNUNET_assert (NULL != other_peer);
     GNUNET_assert (other_peer->reference_cnt > 0);
     other_peer->reference_cnt--;
     if ( (GNUNET_YES == other_peer->destroy_flag) &&
@@ -675,7 +687,7 @@ send_hello (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
     return;
   GNUNET_assert (NULL != occ->hello);
   other_peer_str = GNUNET_strdup (GNUNET_i2s (&occ->other_peer_identity));
-  if (NULL != occ->peer2_controller)
+  if (NULL != occ->p2c)
   {
     struct GNUNET_TESTBED_RemoteOverlayConnectMessage *msg;
     uint16_t msize;
@@ -697,7 +709,7 @@ send_hello (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
     (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);
+    GNUNET_TESTBED_queue_message_ (occ->p2c, &msg->header);
   }
   else
   {
@@ -767,7 +779,7 @@ p2_transport_connect (struct OverlayConnectContext *occ)
   GNUNET_assert (NULL == occ->ghh);
   GNUNET_assert (NULL == occ->p1th_);
   GNUNET_assert (NULL == occ->cgh_p1th);
-  if (NULL == occ->peer2_controller)
+  if (NULL == occ->p2c)
   {
     GNUNET_assert (NULL != GST_peer_list[occ->other_peer_id]);
     occ->tcc.cgh_th =
@@ -1069,6 +1081,31 @@ hash_hosts (struct GNUNET_TESTBED_Host *reg_host,
   return hash;
 }
 
+static void
+p2_controller_connect_cb (void *cls, struct GNUNET_TESTBED_Controller *c)
+{
+  struct OverlayConnectContext *occ = cls;
+  struct GNUNET_TESTBED_PeerGetConfigurationMessage cmsg;
+
+  occ->p2_ncn = NULL;
+  occ->p2c = c;
+  cmsg.header.size =
+      htons (sizeof (struct GNUNET_TESTBED_PeerGetConfigurationMessage));
+  cmsg.header.type =
+      htons (GNUNET_MESSAGE_TYPE_TESTBED_GET_PEER_CONFIGURATION);
+  cmsg.peer_id = htonl (occ->other_peer_id);
+  cmsg.operation_id = GNUNET_htonll (occ->op_id);
+  occ->opc =
+      GNUNET_TESTBED_forward_operation_msg_ (occ->p2c,
+                                             occ->op_id, &cmsg.header,
+                                             &overlay_connect_get_config,
+                                             occ);
+  GNUNET_free_non_null (occ->emsg);
+  GNUNET_asprintf (&occ->emsg,
+                   "0x%llx: Timeout while getting peer identity of peer "
+                   "with id: %u", occ->op_id, occ->other_peer_id);
+}
+
 
 /**
  * Handler for GNUNET_MESSAGE_TYPE_TESTBED_OLCONNECT messages
@@ -1084,7 +1121,7 @@ GST_handle_overlay_connect (void *cls, struct GNUNET_SERVER_Client *client,
   const struct GNUNET_TESTBED_OverlayConnectMessage *msg;
   struct Peer *peer;
   struct OverlayConnectContext *occ;
-  struct GNUNET_TESTBED_Controller *peer2_controller;
+  struct GNUNET_TESTBED_Controller *p2c;
   uint64_t operation_id;
   uint32_t p1;
   uint32_t p2;
@@ -1225,23 +1262,17 @@ GST_handle_overlay_connect (void *cls, struct GNUNET_SERVER_Client *client,
     return;
   }
 
-  peer2_controller = NULL;
+  struct Neighbour *p2n;
+  p2n = NULL;
+  p2c = 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]))
+    if (NULL == (p2n = GST_get_neighbour (peer2_host_id)))
     {
       GNUNET_break (0);
       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? */
+           "0x%llx: Peer %u's host not in our neighbours list\n",
+           operation_id, p2);
       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
       return;
     }
@@ -1249,7 +1280,7 @@ GST_handle_overlay_connect (void *cls, struct GNUNET_SERVER_Client *client,
   else
   {
     if (GNUNET_YES == GST_peer_list[p2]->is_remote)
-      peer2_controller = GST_peer_list[p2]->details.remote.slave->controller;
+      p2c = GST_peer_list[p2]->details.remote.slave->controller;
   }
   occ = GNUNET_malloc (sizeof (struct OverlayConnectContext));
   GNUNET_CONTAINER_DLL_insert_tail (occq_head, occq_tail, occ);
@@ -1258,30 +1289,25 @@ GST_handle_overlay_connect (void *cls, struct GNUNET_SERVER_Client *client,
   occ->other_peer_id = p2;
   GST_peer_list[p1]->reference_cnt++;
   occ->peer = GST_peer_list[p1];  
-  occ->op_id = GNUNET_ntohll (msg->operation_id);
-  occ->peer2_controller = peer2_controller;
+  occ->op_id = operation_id;
+  occ->p2n = p2n;
   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == occ->timeout_task);
   occ->timeout_task =
       GNUNET_SCHEDULER_add_delayed (GST_timeout, &timeout_overlay_connect, occ);
   /* Get the identity of the second peer */
-  if (NULL != occ->peer2_controller)
+  if (NULL != p2n)
   {
-    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);
+                     "0x%llx: Timeout while acquiring connection to peer %u's "
+                     "host: %u\n", occ->op_id, occ->other_peer_id, peer2_host_id);
+    occ->p2_ncn = GST_neighbour_get_connection (p2n, &p2_controller_connect_cb,
+                                                occ);
+    GNUNET_SERVER_receive_done (client, GNUNET_OK);
+    return;
+  }
+  if (NULL != p2c)
+  {
+    p2_controller_connect_cb (occ, p2c);
     GNUNET_SERVER_receive_done (client, GNUNET_OK);
     return;
   }