cleaner handling of gnunet-peerinfo results
[oweals/gnunet.git] / src / testing / testing.c
index b05c02278377dde06a9a9784043f35e90140562a..adcc06193284c9442093d0986bf433132abdd445 100644 (file)
@@ -38,7 +38,7 @@
 #include "gnunet_hello_lib.h"
 
 #define DEBUG_TESTING GNUNET_NO
-#define DEBUG_TESTING_RECONNECT GNUNET_YES
+#define DEBUG_TESTING_RECONNECT GNUNET_NO
 
 /**
  * How long do we wait after starting gnunet-service-arm
@@ -126,7 +126,7 @@ testing_init (void *cls,
     {
       d->server = NULL;
       if (GNUNET_YES == d->dead)
-        GNUNET_TESTING_daemon_stop (d, GNUNET_TIME_absolute_get_remaining(d->max_timeout), d->dead_cb, d->dead_cb_cls, GNUNET_YES);
+        GNUNET_TESTING_daemon_stop (d, GNUNET_TIME_absolute_get_remaining(d->max_timeout), d->dead_cb, d->dead_cb_cls, GNUNET_YES, GNUNET_NO);
       else if (NULL != cb)
         cb (d->cb_cls, NULL, d->cfg, d,
             _("Failed to connect to core service\n"));
@@ -141,7 +141,7 @@ testing_init (void *cls,
   d->server = server;
   d->running = GNUNET_YES;
   if (GNUNET_YES == d->dead)
-    GNUNET_TESTING_daemon_stop (d, GNUNET_TIME_absolute_get_remaining(d->max_timeout), d->dead_cb, d->dead_cb_cls, GNUNET_YES);
+    GNUNET_TESTING_daemon_stop (d, GNUNET_TIME_absolute_get_remaining(d->max_timeout), d->dead_cb, d->dead_cb_cls, GNUNET_YES, GNUNET_NO);
   else if (NULL != cb)
     cb (d->cb_cls, my_identity, d->cfg, d, NULL);
 #if DEBUG_TESTING
@@ -155,7 +155,7 @@ testing_init (void *cls,
   if (d->th == NULL)
     {
       if (GNUNET_YES == d->dead)
-        GNUNET_TESTING_daemon_stop (d, GNUNET_TIME_absolute_get_remaining(d->max_timeout), d->dead_cb, d->dead_cb_cls, GNUNET_YES);
+        GNUNET_TESTING_daemon_stop (d, GNUNET_TIME_absolute_get_remaining(d->max_timeout), d->dead_cb, d->dead_cb_cls, GNUNET_YES, GNUNET_NO);
       else if (NULL != d->cb)
         d->cb (d->cb_cls, &d->id, d->cfg, d,
             _("Failed to connect to transport service!\n"));
@@ -173,7 +173,8 @@ testing_init (void *cls,
  * @param tc unused
  */
 static void
-start_fsm (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+start_fsm (void *cls, 
+          const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct GNUNET_TESTING_Daemon *d = cls;
   GNUNET_TESTING_NotifyDaemonRunning cb;
@@ -181,8 +182,6 @@ start_fsm (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   unsigned long code;
   char *dst;
   int bytes_read;
-  static char hostkeybuf[105];
-  static const char temphostkey[104];
 
 #if DEBUG_TESTING
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -305,76 +304,71 @@ start_fsm (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 #endif
       d->phase = SP_HOSTKEY_CREATE;
       d->task
-        = GNUNET_SCHEDULER_add_delayed (d->sched,
-                                        GNUNET_CONSTANTS_EXEC_WAIT,
-                                        &start_fsm, d);
+       = GNUNET_SCHEDULER_add_read_file (d->sched,
+                                         GNUNET_TIME_absolute_get_remaining(d->max_timeout),
+                                         GNUNET_DISK_pipe_handle(d->pipe_stdout, 
+                                                                 GNUNET_DISK_PIPE_END_READ),
+                                         &start_fsm, 
+                                         d);
       break;
     case SP_HOSTKEY_CREATE:
-
-      bytes_read = GNUNET_DISK_file_read(GNUNET_DISK_pipe_handle(d->pipe_stdout, GNUNET_DISK_PIPE_END_READ), &hostkeybuf, sizeof(hostkeybuf));
-      if (bytes_read == 104) /* Success, we have read in the hostkey */
-        {
-          if (hostkeybuf[103] == '\n')
-            hostkeybuf[103] = '\0';
-          else
-            GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Malformed output from gnunet-peerinfo!\n");
-          memcpy(&temphostkey, &hostkeybuf, bytes_read);
-
-          if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (&temphostkey[0],
-                                                           &d->id.hashPubKey))
-            {
-              GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to convert string to peer identity!\n");
-            }
-          else
-            {
-              GNUNET_DISK_pipe_close(d->pipe_stdout);
-              d->pipe_stdout = NULL;
-            }
-        }
-
-      if (GNUNET_OK != GNUNET_OS_process_status (d->pid, &type, &code))
-        {
-          if (GNUNET_TIME_absolute_get_remaining(d->max_timeout).value == 0)
-            {
-              cb = d->cb;
-              d->cb = NULL;
-              if (NULL != cb)
-                cb (d->cb_cls,
-                    NULL,
-                    d->cfg,
-                    d,
-                    (NULL == d->hostname)
-                    ? _("`gnunet-peerinfo' does not seem to terminate.\n")
-                    : _("`ssh' does not seem to terminate.\n"));
-
-              GNUNET_DISK_pipe_close(d->pipe_stdout);
-              return;
-            }
-          /* wait some more */
+      bytes_read = GNUNET_DISK_file_read(GNUNET_DISK_pipe_handle(d->pipe_stdout, 
+                                                                GNUNET_DISK_PIPE_END_READ),
+                                        &d->hostkeybuf[d->hostkeybufpos], 
+                                        sizeof(d->hostkeybuf) - d->hostkeybufpos);
+      if (bytes_read > 0)
+       d->hostkeybufpos += bytes_read;      
+
+      if ( (d->hostkeybufpos < 104) &&
+          (bytes_read > 0) )
+       {
+         /* keep reading */
           d->task
-            = GNUNET_SCHEDULER_add_delayed (d->sched,
-                                            GNUNET_CONSTANTS_EXEC_WAIT,
-                                            &start_fsm, d);
+            = GNUNET_SCHEDULER_add_read_file (d->sched,
+                                             GNUNET_TIME_absolute_get_remaining(d->max_timeout),
+                                             GNUNET_DISK_pipe_handle(d->pipe_stdout, 
+                                                                     GNUNET_DISK_PIPE_END_READ),
+                                             &start_fsm, 
+                                             d);
           return;
-        }
-#if DEBUG_TESTING
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Successfully got hostkey!\n");
-#endif
-      if (d->pipe_stdout != NULL)
-        {
+       }
+      d->hostkeybuf[103] = '\0';
+      if ( (bytes_read < 0) ||
+          (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (d->hostkeybuf,
+                                                        &d->id.hashPubKey)) )
+       {
+         /* error */
+         if (bytes_read < 0)
+           GNUNET_log(GNUNET_ERROR_TYPE_WARNING, 
+                      _("Error reading from gnunet-peerinfo: %s\n"),
+                      STRERROR (errno));
+         else
+           GNUNET_log(GNUNET_ERROR_TYPE_WARNING, 
+                      _("Malformed output from gnunet-peerinfo!\n"));
           cb = d->cb;
           d->cb = NULL;
+          GNUNET_DISK_pipe_close(d->pipe_stdout);
+         d->pipe_stdout = NULL;
+         (void) PLIBC_KILL (d->pid, SIGKILL);
+         GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (d->pid));
+         d->pid = 0;
           if (NULL != cb)
             cb (d->cb_cls,
                 NULL,
                 d->cfg,
                 d,
                 _("`Failed to get hostkey!\n"));
-          GNUNET_DISK_pipe_close(d->pipe_stdout);
-          return;
-        }
-
+         return;
+       } 
+      GNUNET_DISK_pipe_close(d->pipe_stdout);
+      d->pipe_stdout = NULL;
+      (void) PLIBC_KILL (d->pid, SIGKILL);
+      GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (d->pid));
+      d->pid = 0;
+#if DEBUG_TESTING
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Successfully got hostkey!\n");
+#endif
       if (d->hostkey_callback != NULL)
         {
           d->hostkey_callback(d->hostkey_cls, &d->id, d, NULL);
@@ -384,7 +378,6 @@ start_fsm (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
         {
           d->phase = SP_TOPOLOGY_SETUP;
         }
-
       /* Fall through */
     case SP_HOSTKEY_CREATED:
       /* wait for topology finished */
@@ -580,15 +573,24 @@ start_fsm (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
           d->th = NULL;
         }
       /* state clean up and notifications */
-      GNUNET_CONFIGURATION_destroy (d->cfg);
-      GNUNET_free (d->cfgfile);
+      if (d->churn == GNUNET_NO)
+        {
+          GNUNET_CONFIGURATION_destroy (d->cfg);
+          GNUNET_free (d->cfgfile);
+          GNUNET_free_non_null (d->hostname);
+          GNUNET_free_non_null (d->username);
+        }
+
       GNUNET_free_non_null(d->hello);
-      GNUNET_free_non_null (d->hostname);
-      GNUNET_free_non_null (d->username);
+      d->hello = NULL;
       GNUNET_free_non_null (d->shortname);
+      d->shortname = NULL;
       if (NULL != d->dead_cb)
         d->dead_cb (d->dead_cb_cls, NULL);
-      GNUNET_free (d);
+
+      if (d->churn == GNUNET_NO)
+        GNUNET_free (d);
+
       break;
     case SP_CONFIG_UPDATE:
       /* confirm copying complete */
@@ -643,6 +645,39 @@ GNUNET_TESTING_daemon_continue_startup(struct GNUNET_TESTING_Daemon *daemon)
   daemon->phase = SP_TOPOLOGY_SETUP;
 }
 
+
+/**
+ * Start a peer that has previously been stopped using the daemon_stop
+ * call (and files weren't deleted and the allow restart flag)
+ *
+ * @param daemon the daemon to start (has been previously stopped)
+ * @param timeout how long to wait for restart
+ * @param cb the callback for notification when the peer is running
+ * @param cb_cls closure for the callback
+ */
+void
+GNUNET_TESTING_daemon_start_stopped (struct GNUNET_TESTING_Daemon *daemon,
+                                     struct GNUNET_TIME_Relative timeout,
+                                     GNUNET_TESTING_NotifyDaemonRunning cb,
+                                     void *cb_cls)
+{
+  if (daemon->running == GNUNET_YES)
+  {
+    cb(cb_cls, &daemon->id, daemon->cfg, daemon, "Daemon already running, can't restart!");
+    return;
+  }
+
+  daemon->cb = cb;
+  daemon->cb_cls = cb_cls;
+  daemon->phase = SP_TOPOLOGY_SETUP;
+  daemon->max_timeout = GNUNET_TIME_relative_to_absolute(timeout);
+
+  GNUNET_SCHEDULER_add_continuation (daemon->sched,
+                                     &start_fsm,
+                                     daemon,
+                                     GNUNET_SCHEDULER_REASON_PREREQ_DONE);
+}
+
 /**
  * Starts a GNUnet daemon.  GNUnet must be installed on the target
  * system and available in the PATH.  The machine must furthermore be
@@ -879,14 +914,15 @@ GNUNET_TESTING_daemon_restart (struct GNUNET_TESTING_Daemon *d,
  * @param cb function called once the daemon was stopped
  * @param cb_cls closure for cb
  * @param delete_files GNUNET_YES to remove files, GNUNET_NO
- *        to leave them (i.e. for restarting at a later time,
- *        or logfile inspection once finished)
+ *        to leave them
+ * @param allow_restart GNUNET_YES to restart peer later (using this API)
+ *        GNUNET_NO to kill off and clean up for good
  */
 void
 GNUNET_TESTING_daemon_stop (struct GNUNET_TESTING_Daemon *d,
                             struct GNUNET_TIME_Relative timeout,
                             GNUNET_TESTING_NotifyCompletion cb, void *cb_cls,
-                            int delete_files)
+                            int delete_files, int allow_restart)
 {
   char *arg;
   char *del_arg;
@@ -926,6 +962,17 @@ GNUNET_TESTING_daemon_stop (struct GNUNET_TESTING_Daemon *d,
 #endif
 
   d->phase = SP_SHUTDOWN_START;
+  d->running = GNUNET_NO;
+
+  if (allow_restart == GNUNET_YES)
+    d->churn = GNUNET_YES;
+
+  if (d->th != NULL)
+    {
+      GNUNET_TRANSPORT_get_hello_cancel(d->th, &process_hello, d);
+      GNUNET_TRANSPORT_disconnect(d->th);
+      d->th = NULL;
+    }
   /* Check if this is a local or remote process */
   if (NULL != d->hostname)
     {
@@ -966,9 +1013,8 @@ GNUNET_TESTING_daemon_stop (struct GNUNET_TESTING_Daemon *d,
   GNUNET_free_non_null(del_arg);
   d->max_timeout = GNUNET_TIME_relative_to_absolute(timeout);
   d->task
-    = GNUNET_SCHEDULER_add_delayed (d->sched,
-                                    GNUNET_CONSTANTS_EXEC_WAIT,
-                                    &start_fsm, d);
+    = GNUNET_SCHEDULER_add_now (d->sched,
+                                &start_fsm, d);
 }
 
 
@@ -1142,18 +1188,12 @@ notify_connect_result (void *cls,
   struct ConnectContext *ctx = cls;
   struct GNUNET_TIME_Relative remaining;
 
+  ctx->timeout_task = GNUNET_SCHEDULER_NO_TASK;
   if (ctx->hello_send_task != GNUNET_SCHEDULER_NO_TASK)
     {
       GNUNET_SCHEDULER_cancel(ctx->d1->sched, ctx->hello_send_task);
       ctx->hello_send_task = GNUNET_SCHEDULER_NO_TASK;
     }
-
-  if ((ctx->timeout_task != GNUNET_SCHEDULER_NO_TASK) && (tc->reason != GNUNET_SCHEDULER_REASON_TIMEOUT))
-    {
-      GNUNET_SCHEDULER_cancel(ctx->d1->sched, ctx->timeout_task);
-      ctx->timeout_task = GNUNET_SCHEDULER_NO_TASK;
-    }
-
   if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
     {
       if (ctx->d2th != NULL)
@@ -1230,10 +1270,9 @@ connect_notify (void *cls, const struct GNUNET_PeerIdentity * peer, struct GNUNE
     {
       ctx->connected = GNUNET_YES;
       GNUNET_SCHEDULER_cancel(ctx->d1->sched, ctx->timeout_task);
-      ctx->timeout_task = GNUNET_SCHEDULER_NO_TASK;
-      GNUNET_SCHEDULER_add_now (ctx->d1->sched,
-                                &notify_connect_result,
-                                ctx);
+      ctx->timeout_task = GNUNET_SCHEDULER_add_now (ctx->d1->sched,
+                                                   &notify_connect_result,
+                                                   ctx);
     }
 
 }
@@ -1243,9 +1282,9 @@ send_hello(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct ConnectContext *ctx = cls;
 
+  ctx->hello_send_task = GNUNET_SCHEDULER_NO_TASK;
   if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
     return;
-
   if (ctx->d1->hello != NULL)
     {
       GNUNET_TRANSPORT_offer_hello (ctx->d2th, GNUNET_HELLO_get_header(ctx->d1->hello));
@@ -1342,7 +1381,8 @@ GNUNET_TESTING_daemons_connect (struct GNUNET_TESTING_Daemon *d1,
     }
 
   ctx->timeout_task = GNUNET_SCHEDULER_add_delayed (d1->sched,
-                                                    GNUNET_TIME_relative_divide(ctx->relative_timeout, max_connect_attempts), /* Allow up to 8 reconnect attempts */
+                                                    GNUNET_TIME_relative_divide(ctx->relative_timeout, 
+                                                                               max_connect_attempts), 
                                                     &notify_connect_result, ctx);
 
   ctx->hello_send_task = GNUNET_SCHEDULER_add_now(ctx->d1->sched, &send_hello, ctx);
@@ -1358,7 +1398,7 @@ reattempt_daemons_connect (void *cls, const struct GNUNET_SCHEDULER_TaskContext
       return;
     }
 #if DEBUG_TESTING_RECONNECT
-  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "re-attempting connect of peer %s to peer %s\n",
+  GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "re-attempting connect of peer %s to peer %s\n",
               ctx->d1->shortname, ctx->d2->shortname);
 #endif