-testbed hackery
authorChristian Grothoff <christian@grothoff.org>
Thu, 14 Jun 2012 09:25:54 +0000 (09:25 +0000)
committerChristian Grothoff <christian@grothoff.org>
Thu, 14 Jun 2012 09:25:54 +0000 (09:25 +0000)
src/include/gnunet_testbed_service.h
src/testbed/gnunet-service-testbed.c
src/testbed/testbed_api.c
src/testbed/testbed_api_hosts.c
src/testbed/testbed_api_hosts.h

index 6b50efe97c4e96534737070e9c3e50b718d9126f..a3f092172048239a256e04056a67e24582de335f 100644 (file)
@@ -88,6 +88,26 @@ GNUNET_TESTBED_host_create (const char *hostname,
                            uint16_t port);
 
 
+
+/**
+ * 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);
+
+
 /**
  * Load a set of hosts from a configuration file.
  *
@@ -785,7 +805,6 @@ GNUNET_TESTBED_overlay_configure_topology (void *op_cls,
                                           ...);
 
 
-
 /**
  * Ask the testbed controller to write the current overlay topology to
  * a file.  Naturally, the file will only contain a snapshot as the
index 665af9d3aa20cb355fb6a0b68b6baf320c35f0f3..327daf58462ed500c2730dcf4d127da67a454e1d 100644 (file)
@@ -54,6 +54,11 @@ struct Context
 };
 
 
+/**
+ * Wrapped stdin.
+ */
+static struct GNUNET_DISK_FileHandle *fh;
+
 /**
  * The master context; generated with the first INIT message
  */
@@ -139,8 +144,13 @@ handle_addhost (void *cls,
   host = GNUNET_TESTBED_host_create (hostname, username, ntohs
                                      (msg->ssh_port));
   /* Store host in a hashmap? But the host_id will be different */
+  /* hashmap? maybe array? 4-8 bytes/host and O(1) lookup vs.
+     > 80 bytes for hash map with slightly worse lookup; only
+     if we really get a tiny fraction of the hosts, the hash
+     map would result in any savings... (GNUNET_array_grow) */
 }
 
+
 /**
  * Task to clean up and shutdown nicely
  *
@@ -151,8 +161,16 @@ static void
 shutdown_task (void *cls,
                const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
+  shutdown_task_id = GNUNET_SCHEDULER_NO_TASK;
+  GNUNET_SCHEDULER_shutdown ();
   LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down testbed service\n");
+  if (NULL != fh)
+  {
+    GNUNET_DISK_file_close (fh);
+    fh = NULL;
+  }
   GNUNET_free_non_null (master_context);
+  master_context = NULL;
 }
 
 
@@ -171,9 +189,12 @@ client_disconnect_cb (void *cls, struct GNUNET_SERVER_Client *client)
   {
     LOG (GNUNET_ERROR_TYPE_DEBUG, "Master client disconnected\n");
     GNUNET_SERVER_client_drop (client);
-    GNUNET_SCHEDULER_cancel (shutdown_task_id);    
-    shutdown_task_id = 
-      GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
+    /* 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 ();
   }
 }
 
@@ -202,11 +223,19 @@ testbed_run (void *cls,
                               message_handlers);
   GNUNET_SERVER_disconnect_notify (server,
                                    &client_disconnect_cb,
-                                   NULL);
-  shutdown_task_id = 
-    GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
-                                  &shutdown_task,
-                                  NULL);
+                                   NULL);  
+  fh = GNUNET_DISK_get_handle_from_native (stdin);
+  if (NULL == fh)
+    shutdown_task_id = 
+      GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,                                 
+                                   &shutdown_task,
+                                   NULL);
+  else
+    shutdown_task_id = 
+      GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
+                                     fh,
+                                     &shutdown_task,
+                                     NULL);
 }
 
 
index 5d34640fa3e09fe85911a151384de32885917765..3f49890c43f225e8e0b97e3bd61aa0c6b576c409 100644 (file)
@@ -123,9 +123,37 @@ struct GNUNET_TESTBED_Controller
    * The controller event mask
    */
   uint64_t event_mask;
+
+  /**
+   * Did we start the receive loop yet?
+   */
+  int in_receive;
 };
 
 
+
+/**
+ * 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;
+
+  /* FIXME: Add checks for message integrity */
+  switch (ntohs (msg->type))
+  {
+  default:
+    GNUNET_break (0);
+  }
+  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
@@ -159,6 +187,13 @@ transmit_ready_notify (void *cls, size_t size, void *buf)
                                            GNUNET_TIME_UNIT_FOREVER_REL,
                                            GNUNET_NO, &transmit_ready_notify,
                                            c);
+  if ( (GNUNET_NO == c->in_receive) &&
+       (size > 0) )
+  {
+    c->in_receive = GNUNET_YES;
+    GNUNET_CLIENT_receive (c->client, &message_handler, c,
+                          GNUNET_TIME_UNIT_FOREVER_REL);
+  }
   return size;
 }
 
@@ -197,61 +232,6 @@ queue_message (struct GNUNET_TESTBED_Controller *controller,
  }
 
 
-/**
- * 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;
-
-  /* FIXME: Add checks for message integrity */
-  switch (ntohs (msg->type))
-  {
-  default:
-    GNUNET_break (0);
-  }
-  GNUNET_CLIENT_receive (c->client, &message_handler, c,
-                         GNUNET_TIME_UNIT_FOREVER_REL);
-}
-
-
-/**
- * ?Callback for messages recevied from server? 
- *
- * 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 
-server_mst_cb (void *cls, void *client,
-               const struct GNUNET_MessageHeader *message)
-{
-  struct GNUNET_TESTBED_Controller *c = cls;
-  struct GNUNET_TESTBED_InitMessage *msg;
-  
-  c->client = GNUNET_CLIENT_connect ("testbed", c->cfg);
-  if (NULL == c->client)
-    return GNUNET_SYSERR;       /* FIXME: Call controller startup_cb ? */
-  GNUNET_CLIENT_receive (c->client, &message_handler, c,
-                         GNUNET_TIME_UNIT_FOREVER_REL);
-  msg = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_InitMessage));
-  msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_INIT);
-  msg->header.size = htons (sizeof (struct GNUNET_TESTBED_InitMessage));
-  msg->host_id = htonl (GNUNET_TESTBED_host_get_id_ (c->host));
-  msg->event_mask = GNUNET_htonll (c->event_mask);
-  queue_message (c, (struct GNUNET_MessageHeader *) msg);
-  return GNUNET_OK;
-}
-
-
 /**
  * Start a controller process using the given configuration at the
  * given host.
@@ -279,9 +259,10 @@ GNUNET_TESTBED_controller_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
     "gnunet-service-testbed",
     NULL
   };
+  struct GNUNET_TESTBED_InitMessage *msg;
+
   controller = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Controller));
-  controller->helper = GNUNET_TESTBED_host_run_ (host, binary_argv,
-                                                &server_mst_cb, controller);
+  controller->helper = GNUNET_TESTBED_host_run_ (host, binary_argv);
   if (NULL == controller->helper)
   {
     GNUNET_free (controller);
@@ -292,6 +273,18 @@ GNUNET_TESTBED_controller_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
   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_stop (controller);
+    return NULL;
+  }  
+  msg = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_InitMessage));
+  msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_INIT);
+  msg->header.size = htons (sizeof (struct GNUNET_TESTBED_InitMessage));
+  msg->host_id = htonl (GNUNET_TESTBED_host_get_id_ (controller->host));
+  msg->event_mask = GNUNET_htonll (controller->event_mask);
+  queue_message (controller, (struct GNUNET_MessageHeader *) msg);
   return controller;
 }
 
@@ -338,7 +331,8 @@ GNUNET_TESTBED_controller_stop (struct GNUNET_TESTBED_Controller *controller)
     GNUNET_free (mq_entry->msg);
     GNUNET_free (mq_entry);
   }
-  GNUNET_CLIENT_disconnect (controller->client);
+  if (NULL != controller->client)
+    GNUNET_CLIENT_disconnect (controller->client);
   GNUNET_TESTBED_host_stop_ (controller->helper);
   GNUNET_CONFIGURATION_destroy (controller->cfg);
   GNUNET_free (controller);
@@ -391,6 +385,7 @@ void
 GNUNET_TESTBED_overlay_write_topology_to_file (struct GNUNET_TESTBED_Controller *controller,
                                               const char *filename)
 {
+  GNUNET_break (0);
 }
 
 
index a12d6ce11580f1c9c95a414c119bfd8bd28346c6..c4dfcf3c9e586ce27a139575ff08ffcc73d39a96 100644 (file)
@@ -132,7 +132,7 @@ GNUNET_TESTBED_host_create_by_id_ (uint32_t id)
 uint32_t
 GNUNET_TESTBED_host_get_id_ (const struct GNUNET_TESTBED_Host *host)
 {
-    return host->unique_id;
+  return host->unique_id;
 }
 
 
@@ -147,10 +147,10 @@ GNUNET_TESTBED_host_get_id_ (const 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)
 {
   struct GNUNET_TESTBED_Host *host;
 
@@ -180,10 +180,10 @@ GNUNET_TESTBED_host_create (const char *hostname,
   static uint32_t uid_generator;
 
   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);
+    return GNUNET_TESTBED_host_create_with_id (0, hostname, username, port);
+  return GNUNET_TESTBED_host_create_with_id (++uid_generator, 
+                                            hostname, username,
+                                            port);
 }
 
 
@@ -198,6 +198,7 @@ unsigned int
 GNUNET_TESTBED_hosts_load_from_file (const char *filename,
                                     struct GNUNET_TESTBED_Host **hosts)
 {
+  // see testing_group.c, GNUNET_TESTING_hosts_load
   GNUNET_break (0);
   return 0;
 }
@@ -223,9 +224,14 @@ GNUNET_TESTBED_host_destroy (struct GNUNET_TESTBED_Host *host)
 struct GNUNET_TESTBED_HelperHandle
 {
   /**
-   * The helper handle
+   * The process handle
    */
-  struct GNUNET_HELPER_Handle *handle;
+  struct GNUNET_OS_Process *process;
+
+  /**
+   * Pipe connecting to stdin of the process.
+   */
+  struct GNUNET_DISK_PipeHandle *cpipe;
 
   /**
    * The port number for ssh; used for helpers starting ssh
@@ -247,39 +253,62 @@ struct GNUNET_TESTBED_HelperHandle
  * 
  * @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
  */
 struct GNUNET_TESTBED_HelperHandle *
-GNUNET_TESTBED_host_run_ (struct GNUNET_TESTBED_Host *host,
-                         char *const binary_argv[],
-                         GNUNET_SERVER_MessageTokenizerCallback cb, void *cb_cls)
+GNUNET_TESTBED_host_run_ (const struct GNUNET_TESTBED_Host *host,
+                         char *const binary_argv[])
 {
-  /* FIXME: decide on the SSH command line, prepend it and
-     run GNUNET_HELPER_start with the modified binary_name and binary_argv! */
   struct GNUNET_TESTBED_HelperHandle *h;
-  char *const local_args[] = {NULL};  
+  unsigned int argc;
 
+  argc = 0;
+  while (NULL != binary_argv[argc]) 
+    argc++;
   h = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_HelperHandle));
+  h->cpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_YES, GNUNET_NO);
   if (0 == host->unique_id)
   {
-    h->handle = GNUNET_HELPER_start ("gnunet-service-testbed", local_args,
-                                     cb, cb_cls);
+    h->process = GNUNET_OS_start_process_vap (GNUNET_YES,
+                                             h->cpipe, NULL,
+                                             "gnunet-service-testbed", 
+                                             binary_argv);
   }
   else
   {    
+    char *remote_args[argc + 6 + 1];
+    unsigned int argp;
+
     GNUNET_asprintf (&h->port, "%d", host->port);
     GNUNET_asprintf (&h->dst, "%s@%s", host->hostname, host->username);
-    char *remote_args[] = {"ssh", "-p", h->port, "-q", h->dst,
-                           "gnunet-service-testbed", NULL};
-    h->handle = GNUNET_HELPER_start ("ssh", remote_args, cb, cb_cls);
+    argp = 0;
+    remote_args[argp++] = "ssh";
+    remote_args[argp++] = "-p";
+    remote_args[argp++] = h->port;
+    remote_args[argp++] = "-q";
+    remote_args[argp++] = h->dst;
+    remote_args[argp++] = "gnunet-service-testbed";
+    while (NULL != binary_argv[argp-6])
+    {
+      remote_args[argp] = binary_argv[argp - 6];
+      argp++;
+    } 
+    remote_args[argp++] = NULL;
+    GNUNET_assert (argp == argc + 6 + 1);
+    h->process = GNUNET_OS_start_process_vap (GNUNET_YES,
+                                             h->cpipe, NULL,
+                                             "ssh", 
+                                             remote_args);
   }
-  if (NULL == h->handle)
+  if (NULL == h->process)
   {
+    GNUNET_break (GNUNET_OK == GNUNET_DISK_pipe_close (h->cpipe));
+    GNUNET_free_non_null (h->port);
+    GNUNET_free_non_null (h->dst);
     GNUNET_free (h);
     return NULL;
-  }
+  } 
+  GNUNET_break (GNUNET_OK == GNUNET_DISK_pipe_close_end (h->cpipe, GNUNET_DISK_PIPE_END_READ));
   return h;
 }
 
@@ -292,7 +321,10 @@ GNUNET_TESTBED_host_run_ (struct GNUNET_TESTBED_Host *host,
 void
 GNUNET_TESTBED_host_stop_ (struct GNUNET_TESTBED_HelperHandle *handle)
 {
-  GNUNET_HELPER_stop (handle->handle);
+  GNUNET_break (GNUNET_OK == GNUNET_DISK_pipe_close (handle->cpipe));
+  GNUNET_break (0 == GNUNET_OS_process_kill (handle->process, SIGTERM));
+  GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (handle->process));
+  GNUNET_OS_process_destroy (handle->process);
   GNUNET_free_non_null (handle->port);
   GNUNET_free_non_null (handle->dst);
   GNUNET_free (handle);
index 0c36da0eee90e5cca7018b358bb1f9c58860de06..eaf2a4d119442faef8facb4b525bab3febe24a98 100644 (file)
@@ -54,25 +54,6 @@ struct GNUNET_TESTBED_Host *
 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.
  * 
@@ -98,14 +79,11 @@ struct GNUNET_TESTBED_HelperHandle;
  * 
  * @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
  */
 struct GNUNET_TESTBED_HelperHandle *
-GNUNET_TESTBED_host_run_ (struct GNUNET_TESTBED_Host *host,
-                         char *const binary_argv[],
-                         GNUNET_SERVER_MessageTokenizerCallback cb, void *cb_cls);
+GNUNET_TESTBED_host_run_ (const struct GNUNET_TESTBED_Host *host,
+                         char *const binary_argv[]);
 
 
 /**