7fed3320637efe560e59e44f6d24fcba4308502b
[oweals/gnunet.git] / src / testing / testing.c
1 /*
2       This file is part of GNUnet
3       (C) 2008, 2009 Christian Grothoff (and other contributing authors)
4
5       GNUnet is free software; you can redistribute it and/or modify
6       it under the terms of the GNU General Public License as published
7       by the Free Software Foundation; either version 3, or (at your
8       option) any later version.
9
10       GNUnet is distributed in the hope that it will be useful, but
11       WITHOUT ANY WARRANTY; without even the implied warranty of
12       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13       General Public License for more details.
14
15       You should have received a copy of the GNU General Public License
16       along with GNUnet; see the file COPYING.  If not, write to the
17       Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18       Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * @file testing/testing.c
23  * @brief convenience API for writing testcases for GNUnet
24  *        Many testcases need to start and stop gnunetd,
25  *        and this library is supposed to make that easier
26  *        for TESTCASES.  Normal programs should always
27  *        use functions from gnunet_{util,arm}_lib.h.  This API is
28  *        ONLY for writing testcases!
29  * @author Christian Grothoff
30  *
31  */
32 #include "platform.h"
33 #include "gnunet_arm_service.h"
34 #include "gnunet_core_service.h"
35 #include "gnunet_constants.h"
36 #include "gnunet_testing_lib.h"
37 #include "gnunet_transport_service.h"
38 #include "gnunet_hello_lib.h"
39
40 /**
41  * Hack to deal with initial HELLO's being often devoid of addresses.
42  * This hack causes 'process_hello' to ignore HELLOs without addresses.
43  * The correct implementation would continue with 'process_hello' until
44  * the connection could be established...
45  */
46 #define EMPTY_HACK GNUNET_YES
47
48 /**
49  * How long do we wait after starting gnunet-service-arm
50  * for the core service to be alive?
51  */
52 #define ARM_START_WAIT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120)
53
54 /**
55  * How many times are we willing to try to wait for "scp" or
56  * "gnunet-service-arm" to complete (waitpid) before giving up?
57  */
58 #define MAX_EXEC_WAIT_RUNS 250
59
60 static struct GNUNET_CORE_MessageHandler no_handlers[] = { {NULL, 0, 0} };
61
62 #if EMPTY_HACK
63 static int
64 test_address (void *cls, const struct GNUNET_HELLO_Address *address,
65               struct GNUNET_TIME_Absolute expiration)
66 {
67   int *empty = cls;
68
69   *empty = GNUNET_NO;
70   return GNUNET_OK;
71 }
72 #endif
73
74 /**
75  * Receive the HELLO from one peer, give it to the other
76  * and ask them to connect.
77  *
78  * @param cls Closure (daemon whose hello is this).
79  * @param message HELLO message of peer
80  */
81 static void
82 process_hello (void *cls, const struct GNUNET_MessageHeader *message)
83 {
84   struct GNUNET_TESTING_Daemon *daemon = cls;
85   int msize;
86
87 #if EMPTY_HACK
88   int empty;
89
90   empty = GNUNET_YES;
91   GNUNET_assert (message != NULL);
92   GNUNET_HELLO_iterate_addresses ((const struct GNUNET_HELLO_Message *) message,
93                                   GNUNET_NO, &test_address, &empty);
94   if (GNUNET_YES == empty)
95   {
96     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
97                 "Skipping empty HELLO address of peer %s\n",
98                 GNUNET_i2s (&daemon->id));
99     return;
100   }
101 #endif
102   GNUNET_assert (daemon->phase == SP_GET_HELLO ||
103                  daemon->phase == SP_START_DONE);
104   daemon->cb = NULL;            // FIXME: why??? (see fsm:SP_START_CORE, notify_daemon_started)
105   if (daemon->task != GNUNET_SCHEDULER_NO_TASK) /* Assertion here instead? */
106     GNUNET_SCHEDULER_cancel (daemon->task);
107
108   if (daemon->server != NULL)
109   {
110     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
111                 "Received `%s' from transport service of `%4s', disconnecting core!\n",
112                 "HELLO", GNUNET_i2s (&daemon->id));
113     GNUNET_CORE_disconnect (daemon->server);
114     daemon->server = NULL;
115   }
116
117   msize = ntohs (message->size);
118   if (msize < 1)
119   {
120     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
121                 "HELLO message of peer %s is of size 0\n",
122                 GNUNET_i2s (&daemon->id));
123     return;
124   }
125   if (daemon->ghh != NULL)
126   {
127     GNUNET_TRANSPORT_get_hello_cancel (daemon->ghh);
128     daemon->ghh = NULL;
129   }
130   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
131               "Received `%s' from transport service of `%4s'\n", "HELLO",
132               GNUNET_i2s (&daemon->id));
133   GNUNET_free_non_null (daemon->hello);
134   daemon->hello = GNUNET_malloc (msize);
135   memcpy (daemon->hello, message, msize);
136
137   if (daemon->th != NULL)
138   {
139     GNUNET_TRANSPORT_disconnect (daemon->th);
140     daemon->th = NULL;
141   }
142   daemon->phase = SP_START_DONE;
143 }
144
145
146 /**
147  * Notify of a peer being up and running.  Scheduled as a task
148  * so that variables which may need to be set are set before
149  * the connect callback can set up new operations.
150  * FIXME: what variables?????? where from????
151  *
152  * @param cls the testing daemon
153  * @param tc task scheduler context
154  */
155 static void
156 notify_daemon_started (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
157 {
158   struct GNUNET_TESTING_Daemon *d = cls;
159   GNUNET_TESTING_NotifyDaemonRunning cb;
160
161   cb = d->cb;
162   d->cb = NULL;
163   if (NULL != cb)
164     cb (d->cb_cls, &d->id, d->cfg, d, NULL);
165 }
166
167
168 /**
169  * Finite-state machine for starting GNUnet.
170  *
171  * @param cls our "struct GNUNET_TESTING_Daemon"
172  * @param tc unused
173  */
174 static void
175 start_fsm (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
176 {
177   struct GNUNET_TESTING_Daemon *d = cls;
178   GNUNET_TESTING_NotifyDaemonRunning cb;
179   enum GNUNET_OS_ProcessStatusType type;
180   unsigned long code;
181   char *dst;
182   int bytes_read;
183
184   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer %s FSM is in phase %u.\n",
185               GNUNET_i2s (&d->id), d->phase);
186   d->task = GNUNET_SCHEDULER_NO_TASK;
187   switch (d->phase)
188   {
189   case SP_COPYING:
190     /* confirm copying complete */
191     if (GNUNET_OK != GNUNET_OS_process_status (d->proc_arm_copying, &type, &code))
192     {
193       if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0)
194       {
195         cb = d->cb;
196         d->cb = NULL;
197         if (NULL != cb)
198           cb (d->cb_cls, NULL, d->cfg, d,
199               _
200               ("`scp' does not seem to terminate (timeout copying config).\n"));
201         return;
202       }
203       /* wait some more */
204       d->task =
205           GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm,
206                                         d);
207       return;
208     }
209     if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0))
210     {
211       cb = d->cb;
212       d->cb = NULL;
213       if (NULL != cb)
214         cb (d->cb_cls, NULL, d->cfg, d, _("`scp' did not complete cleanly.\n"));
215       return;
216     }
217     GNUNET_OS_process_close (d->proc_arm_copying);
218     d->proc_arm_copying = NULL;
219     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
220                 "Successfully copied configuration file.\n");
221     d->phase = SP_COPIED;
222     /* fall-through */
223   case SP_COPIED:
224     /* Start create hostkey process if we don't already know the peer identity! */
225     if (GNUNET_NO == d->have_hostkey)
226     {
227       GNUNET_assert (NULL == d->proc_arm_peerinfo);
228       d->pipe_stdout = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO, GNUNET_YES);
229       if (d->pipe_stdout == NULL)
230       {
231         cb = d->cb;
232         d->cb = NULL;
233         if (NULL != cb)
234           cb (d->cb_cls, NULL, d->cfg, d,
235               (NULL ==
236                d->hostname) ?
237               _("Failed to create pipe for `gnunet-peerinfo' process.\n") :
238               _("Failed to create pipe for `ssh' process.\n"));
239         return;
240       }
241       if (NULL == d->hostname)
242       {
243         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
244                     "Starting `%s', with command `%s %s %s %s'.\n",
245                     "gnunet-peerinfo", "gnunet-peerinfo", "-c", d->cfgfile,
246                     "-sq");
247         d->proc_arm_peerinfo =
248             GNUNET_OS_start_process (GNUNET_YES, NULL, d->pipe_stdout, "gnunet-peerinfo",
249                                      "gnunet-peerinfo", "-c", d->cfgfile, "-sq",
250                                      NULL);
251         GNUNET_DISK_pipe_close_end (d->pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
252       }
253       else
254       {
255         if (d->username != NULL)
256           GNUNET_asprintf (&dst, "%s@%s", d->username, d->hostname);
257         else
258           dst = GNUNET_strdup (d->hostname);
259
260         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
261                     "Starting `%s', with command `%s %s %s %s %s %s'.\n",
262                     "gnunet-peerinfo", "ssh", dst, "gnunet-peerinfo", "-c",
263                     d->cfgfile, "-sq");
264         if (d->ssh_port_str == NULL)
265         {
266           d->proc_arm_peerinfo = GNUNET_OS_start_process (GNUNET_NO, NULL, d->pipe_stdout, "ssh", "ssh",
267                                              "-q",
268                                              dst, "gnunet-peerinfo", "-c",
269                                              d->cfgfile, "-sq", NULL);
270         }
271         else
272         {
273           d->proc_arm_peerinfo =
274               GNUNET_OS_start_process (GNUNET_NO, NULL, d->pipe_stdout, "ssh", "ssh", "-p",
275                                        d->ssh_port_str,
276                                        "-q",
277                                        dst, "gnunet-peerinfo", "-c", d->cfgfile,
278                                        "-sq", NULL);
279         }
280         GNUNET_DISK_pipe_close_end (d->pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
281         GNUNET_free (dst);
282       }
283       if (NULL == d->proc_arm_peerinfo)
284       {
285         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
286                     _("Could not start `%s' process to create hostkey.\n"),
287                     (NULL == d->hostname) ? "gnunet-peerinfo" : "ssh");
288         cb = d->cb;
289         d->cb = NULL;
290         if (NULL != cb)
291           cb (d->cb_cls, NULL, d->cfg, d,
292               (NULL ==
293                d->hostname) ? _("Failed to start `gnunet-peerinfo' process.\n")
294               : _("Failed to start `ssh' process.\n"));
295         GNUNET_DISK_pipe_close (d->pipe_stdout);
296         return;
297       }
298       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
299                   "Started `%s', waiting for hostkey.\n", "gnunet-peerinfo");
300       d->phase = SP_HOSTKEY_CREATE;
301       d->task =
302           GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
303                                           (d->max_timeout),
304                                           GNUNET_DISK_pipe_handle
305                                           (d->pipe_stdout,
306                                            GNUNET_DISK_PIPE_END_READ),
307                                           &start_fsm, d);
308     }
309     else                        /* Already have a hostkey! */
310     {
311       if (d->hostkey_callback != NULL)
312       {
313         d->hostkey_callback (d->hostkey_cls, &d->id, d, NULL);
314         d->hostkey_callback = NULL;
315         d->phase = SP_HOSTKEY_CREATED;
316       }
317       else
318         d->phase = SP_TOPOLOGY_SETUP;
319
320       /* wait some more */
321       d->task = GNUNET_SCHEDULER_add_now (&start_fsm, d);
322     }
323     break;
324   case SP_HOSTKEY_CREATE:
325     bytes_read =
326         GNUNET_DISK_file_read (GNUNET_DISK_pipe_handle
327                                (d->pipe_stdout, GNUNET_DISK_PIPE_END_READ),
328                                &d->hostkeybuf[d->hostkeybufpos],
329                                sizeof (d->hostkeybuf) - d->hostkeybufpos);
330     if (bytes_read > 0)
331       d->hostkeybufpos += bytes_read;
332
333     if ((d->hostkeybufpos < 104) && (bytes_read > 0))
334     {
335       /* keep reading */
336       d->task =
337           GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
338                                           (d->max_timeout),
339                                           GNUNET_DISK_pipe_handle
340                                           (d->pipe_stdout,
341                                            GNUNET_DISK_PIPE_END_READ),
342                                           &start_fsm, d);
343       return;
344     }
345     d->hostkeybuf[103] = '\0';
346
347     if ((bytes_read < 0) ||
348         (GNUNET_OK !=
349          GNUNET_CRYPTO_hash_from_string (d->hostkeybuf, &d->id.hashPubKey)))
350     {
351       /* error */
352       if (bytes_read < 0)
353         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
354                     _("Error reading from gnunet-peerinfo: %s\n"),
355                     STRERROR (errno));
356       else
357         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
358                     _("Malformed output from gnunet-peerinfo!\n"));
359       cb = d->cb;
360       d->cb = NULL;
361       GNUNET_DISK_pipe_close (d->pipe_stdout);
362       d->pipe_stdout = NULL;
363       (void) GNUNET_OS_process_kill (d->proc_arm_peerinfo, SIGKILL);      
364       GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (d->proc_arm_peerinfo));
365       GNUNET_OS_process_close (d->proc_arm_peerinfo);      
366       d->proc_arm_peerinfo = NULL;
367       if (NULL != cb)
368         cb (d->cb_cls, NULL, d->cfg, d, _("Failed to get hostkey!\n"));
369       return;
370     }
371     d->shortname = GNUNET_strdup (GNUNET_i2s (&d->id));
372     GNUNET_DISK_pipe_close (d->pipe_stdout);
373     d->pipe_stdout = NULL;
374     (void) GNUNET_OS_process_kill (d->proc_arm_peerinfo, SIGKILL);
375     GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (d->proc_arm_peerinfo));
376     GNUNET_OS_process_close (d->proc_arm_peerinfo);
377     d->proc_arm_peerinfo = NULL;
378     d->have_hostkey = GNUNET_YES;
379     if (d->hostkey_callback != NULL)
380     {
381       d->hostkey_callback (d->hostkey_cls, &d->id, d, NULL);
382       d->hostkey_callback = NULL;
383       d->phase = SP_HOSTKEY_CREATED;
384     }
385     else
386     {
387       d->phase = SP_TOPOLOGY_SETUP;
388     }
389     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Successfully got hostkey!\n");
390     /* Fall through */
391   case SP_HOSTKEY_CREATED:
392     /* wait for topology finished */
393     if ((GNUNET_YES == d->dead) ||
394         (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0))
395     {
396       cb = d->cb;
397       d->cb = NULL;
398       if (NULL != cb)
399         cb (d->cb_cls, NULL, d->cfg, d,
400             _("`Failed while waiting for topology setup!\n"));
401       return;
402     }
403
404     d->task =
405         GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm,
406                                       d);
407     break;
408   case SP_TOPOLOGY_SETUP:      /* Indicates topology setup has completed! */
409     /* start GNUnet on remote host */
410     if (NULL == d->hostname)
411     {
412       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
413                   "Starting `%s', with command `%s %s %s %s'.\n",
414                   "gnunet-arm", "gnunet-arm", "-c", d->cfgfile, 
415                   "-s");
416       d->proc_arm_start =
417           GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-arm", "gnunet-arm", "-c",
418                                    d->cfgfile,
419                                    "-s", "-q", "-T",
420                                    GNUNET_TIME_relative_to_string
421                                    (GNUNET_TIME_absolute_get_remaining
422                                     (d->max_timeout)), NULL);
423     }
424     else
425     {
426       if (d->username != NULL)
427         GNUNET_asprintf (&dst, "%s@%s", d->username, d->hostname);
428       else
429         dst = GNUNET_strdup (d->hostname);
430
431       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
432                   "Starting `%s', with command `%s %s %s %s %s %s %s'.\n",
433                   "gnunet-arm", "ssh", dst, "gnunet-arm", "-c", d->cfgfile,
434                   "-s", "-q");
435       if (d->ssh_port_str == NULL)
436       {
437         d->proc_arm_start = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh",
438                                            "-q",
439                                            dst, "gnunet-arm",
440                                            "-c", d->cfgfile, "-s", "-q", "-T",
441                                            GNUNET_TIME_relative_to_string
442                                            (GNUNET_TIME_absolute_get_remaining
443                                             (d->max_timeout)), NULL);
444       }
445       else
446       {
447
448         d->proc_arm_start =
449             GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh", "-p",
450                                      d->ssh_port_str,
451                                      "-q",
452                                      dst, "gnunet-arm",
453                                      "-c", d->cfgfile, "-s", "-q", "-T",
454                                      GNUNET_TIME_relative_to_string
455                                      (GNUNET_TIME_absolute_get_remaining
456                                       (d->max_timeout)), NULL);
457       }
458       GNUNET_free (dst);
459     }
460     if (NULL == d->proc_arm_start)
461     {
462       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
463                   _("Could not start `%s' process to start GNUnet.\n"),
464                   (NULL == d->hostname) ? "gnunet-arm" : "ssh");
465       cb = d->cb;
466       d->cb = NULL;
467       if (NULL != cb)
468         cb (d->cb_cls, NULL, d->cfg, d,
469             (NULL ==
470              d->hostname) ? _("Failed to start `gnunet-arm' process.\n") :
471             _("Failed to start `ssh' process.\n"));
472       return;
473     }
474     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
475                 "Started `%s', waiting for `%s' to be up.\n", "gnunet-arm",
476                 "gnunet-service-core");
477     d->phase = SP_START_ARMING;
478     d->task =
479         GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm,
480                                       d);
481         // FIXME: busy wait?
482     break;
483   case SP_START_ARMING:
484     if (GNUNET_OK != GNUNET_OS_process_status (d->proc_arm_start, &type, &code))
485     {
486       if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0)
487       {
488         cb = d->cb;
489         d->cb = NULL;
490         if (NULL != cb)
491           cb (d->cb_cls, NULL, d->cfg, d,
492               (NULL ==
493                d->hostname) ? _("`gnunet-arm' does not seem to terminate.\n") :
494               _("`ssh' does not seem to terminate.\n"));
495         if (d->cfg != NULL)
496         {
497           GNUNET_CONFIGURATION_destroy (d->cfg);
498           d->cfg = NULL;
499         }
500         if (d->cfgfile != NULL)
501         {
502           GNUNET_free (d->cfgfile);
503           d->cfgfile = NULL;
504         }
505         GNUNET_free_non_null (d->hostname);
506         GNUNET_free_non_null (d->username);
507         GNUNET_free (d->proc_arm_start);
508         d->proc_arm_start = NULL;
509         d->hostname = NULL;     // Quick hack to avoid crashing (testing need to be
510         d->cfg = NULL;          // overhauled anyway, and the error managing is
511         // GNUNET_free (d); // FIXME (could this leak)
512         // pretty broken anyway.
513         return;
514       }
515       /* wait some more */
516       d->task =
517           GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm,
518                                         d);
519           // FIXME: busy wait?
520       return;
521     }
522     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Successfully started `%s'.\n",
523                 "gnunet-arm");
524     GNUNET_free (d->proc_arm_start);
525     d->proc_arm_start = NULL;
526     d->phase = SP_START_CORE;
527     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Calling CORE_connect\n");
528     /* Fall through */
529   case SP_START_CORE:
530     if (d->server != NULL)
531       GNUNET_CORE_disconnect (d->server);
532
533     d->th = GNUNET_TRANSPORT_connect (d->cfg, &d->id, d, NULL, NULL, NULL);
534     if (d->th == NULL)
535     {
536       if (GNUNET_YES == d->dead)
537         GNUNET_TESTING_daemon_stop (d,
538                                     GNUNET_TIME_absolute_get_remaining
539                                     (d->max_timeout), d->dead_cb,
540                                     d->dead_cb_cls, GNUNET_YES, GNUNET_NO);
541       else if (NULL != d->cb)
542         d->cb (d->cb_cls, &d->id, d->cfg, d,
543                _("Failed to connect to transport service!\n"));
544       return;
545     }
546     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
547                 "Connected to transport service `%s', getting HELLO\n",
548                 GNUNET_i2s (&d->id));
549     d->ghh = GNUNET_TRANSPORT_get_hello (d->th, &process_hello, d);
550     /* FIXME: store task ID somewhere! */
551     GNUNET_SCHEDULER_add_now (&notify_daemon_started, d);
552     /*cb = d->cb;
553      * d->cb = NULL;
554      * if (NULL != cb)
555      * cb (d->cb_cls, &d->id, d->cfg, d, NULL); */
556     d->running = GNUNET_YES;
557     d->phase = SP_GET_HELLO;
558     break;
559   case SP_GET_HELLO:
560     if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0)
561     {
562       if (d->server != NULL)
563         GNUNET_CORE_disconnect (d->server);
564       if (d->th != NULL)
565         GNUNET_TRANSPORT_disconnect (d->th);
566       cb = d->cb;
567       d->cb = NULL;
568       if (NULL != cb)
569         cb (d->cb_cls, NULL, d->cfg, d, _("Unable to get HELLO for peer!\n"));
570       GNUNET_CONFIGURATION_destroy (d->cfg);
571       GNUNET_free (d->cfgfile);
572       GNUNET_free_non_null (d->hostname);
573       GNUNET_free_non_null (d->username);
574       GNUNET_free (d);
575       return;
576     }
577     if (d->hello != NULL)
578       return;
579     GNUNET_assert (d->task == GNUNET_SCHEDULER_NO_TASK);
580     d->task =
581         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
582                                       (GNUNET_CONSTANTS_SERVICE_RETRY, 2),
583                                       &start_fsm, d);
584     break;
585   case SP_START_DONE:
586     GNUNET_break (0);
587     break;
588   case SP_SERVICE_START:
589     /* confirm gnunet-arm exited */
590     if (GNUNET_OK != GNUNET_OS_process_status (d->proc_arm_srv_start, &type, &code))
591     {
592       if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0)
593       {
594         cb = d->cb;
595         d->cb = NULL;
596         if (NULL != cb)
597           cb (d->cb_cls, NULL, d->cfg, d,
598               (NULL ==
599                d->hostname) ? _("`gnunet-arm' does not seem to terminate.\n") :
600               _("`ssh' does not seem to terminate.\n"));
601         return;
602       }
603       /* wait some more */
604       d->task =
605           GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm,
606                                         d);
607       return;
608     }
609 #if EXTRA_CHECKS
610     if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0))
611     {
612       cb = d->cb;
613       d->cb = NULL;
614       if (NULL != cb)
615         cb (d->cb_cls, NULL, d->cfg, d,
616             (NULL ==
617              d->hostname) ?
618             _
619             ("`gnunet-arm' terminated with non-zero exit status (or timed out)!\n")
620             : _("`ssh' does not seem to terminate.\n"));
621       return;
622     }
623 #endif
624     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service startup complete!\n");
625     cb = d->cb;
626     d->cb = NULL;
627     d->phase = SP_START_DONE;
628     if (NULL != cb)
629       cb (d->cb_cls, &d->id, d->cfg, d, NULL);
630     GNUNET_free (d->proc_arm_srv_start);
631     d->proc_arm_srv_start = NULL;
632     break;
633   case SP_SERVICE_SHUTDOWN_START:
634     /* confirm copying complete */
635     if (GNUNET_OK != GNUNET_OS_process_status (d->proc_arm_srv_stop, &type, &code))
636     {
637       if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0)
638       {
639         if (NULL != d->dead_cb)
640           d->dead_cb (d->dead_cb_cls,
641                       _
642                       ("either `gnunet-arm' or `ssh' does not seem to terminate.\n"));
643         return;
644       }
645       /* wait some more */
646       d->task =
647           GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm,
648                                         d);
649       return;
650     }
651 #if EXTRA_CHECKS
652     if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0))
653     {
654       if (NULL != d->dead_cb)
655         d->dead_cb (d->dead_cb_cls,
656                     _
657                     ("shutdown (either `gnunet-arm' or `ssh') did not complete cleanly.\n"));
658       return;
659     }
660 #endif
661     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service shutdown complete.\n");
662     if (NULL != d->dead_cb)
663       d->dead_cb (d->dead_cb_cls, NULL);
664     break;
665   case SP_SHUTDOWN_START:
666     /* confirm copying complete !??? */
667     if (GNUNET_OK != GNUNET_OS_process_status (d->proc_arm_stop, &type, &code))
668     {
669       if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0)
670       {
671         if (NULL != d->dead_cb)
672           d->dead_cb (d->dead_cb_cls,
673                       _
674                       ("either `gnunet-arm' or `ssh' does not seem to terminate.\n"));
675         if (d->th != NULL)
676         {
677           GNUNET_TRANSPORT_get_hello_cancel (d->ghh);
678           d->ghh = NULL;
679           GNUNET_TRANSPORT_disconnect (d->th);
680           d->th = NULL;
681         }
682         if (d->cfg != NULL)
683         {
684           GNUNET_CONFIGURATION_destroy (d->cfg);
685           d->cfg = NULL;
686         }
687         if (d->cfgfile != NULL)
688         {
689           GNUNET_free (d->cfgfile);
690           d->cfgfile = NULL;
691         }
692         GNUNET_free_non_null (d->hello);
693         GNUNET_free_non_null (d->hostname);
694         GNUNET_free_non_null (d->username);
695         GNUNET_free_non_null (d->shortname);
696         // GNUNET_free_non_null (d->proc); // !? FIXME
697         // d->proc_arm_stop = NULL; 
698         GNUNET_free (d);
699         return;
700       }
701       /* wait some more */
702       d->task =
703           GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm,
704                                         d);
705       return;
706     }
707     if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0))
708     {
709       if (NULL != d->dead_cb)
710         d->dead_cb (d->dead_cb_cls,
711                     _
712                     ("shutdown (either `gnunet-arm' or `ssh') did not complete cleanly.\n"));
713       if (d->th != NULL)
714       {
715         GNUNET_TRANSPORT_get_hello_cancel (d->ghh);
716         d->ghh = NULL;
717         GNUNET_TRANSPORT_disconnect (d->th);
718         d->th = NULL;
719       }
720       if (d->server != NULL)
721       {
722         GNUNET_CORE_disconnect (d->server);
723         d->server = NULL;
724       }
725       GNUNET_CONFIGURATION_destroy (d->cfg);
726       d->cfg = NULL;
727       GNUNET_free (d->cfgfile);
728       GNUNET_free_non_null (d->hello);
729       GNUNET_free_non_null (d->hostname);
730       GNUNET_free_non_null (d->username);
731       GNUNET_free_non_null (d->shortname);
732       // GNUNET_free_non_null (d->proc); // !? FIXME!
733       // d->proc = NULL; // !? FIXME!
734       GNUNET_free (d);
735       return;
736     }
737     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer shutdown complete.\n");
738     if (d->server != NULL)
739     {
740       GNUNET_CORE_disconnect (d->server);
741       d->server = NULL;
742     }
743
744     if (d->th != NULL)
745     {
746       GNUNET_TRANSPORT_get_hello_cancel (d->ghh);
747       d->ghh = NULL;
748       GNUNET_TRANSPORT_disconnect (d->th);
749       d->th = NULL;
750     }
751
752     if (NULL != d->dead_cb)
753       d->dead_cb (d->dead_cb_cls, NULL);
754
755     /* state clean up and notifications */
756     if (d->churn == GNUNET_NO)
757     {
758       GNUNET_CONFIGURATION_destroy (d->cfg);
759       d->cfg = NULL;
760       GNUNET_free (d->cfgfile);
761       GNUNET_free_non_null (d->hostname);
762       GNUNET_free_non_null (d->username);
763     }
764
765     GNUNET_free_non_null (d->hello);
766     d->hello = NULL;
767     GNUNET_free_non_null (d->shortname);
768     // GNUNET_free_non_null (d->proc); // !? FIXME
769     // d->proc = NULL; // !? FIXME!
770     d->shortname = NULL;
771     if (d->churn == GNUNET_NO)
772       GNUNET_free (d);
773
774     break;
775   case SP_CONFIG_UPDATE:
776     /* confirm copying complete */
777     if (GNUNET_OK != GNUNET_OS_process_status (d->proc_arm_copying, &type, &code))
778     {
779       if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0)   /* FIXME: config update should take timeout parameter! */
780       {
781         cb = d->cb;
782         d->cb = NULL;
783         if (NULL != cb)
784           cb (d->cb_cls, NULL, d->cfg, d,
785               _("`scp' does not seem to terminate.\n"));
786         return;
787       }
788       /* wait some more */
789       d->task =
790           GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm,
791                                         d);
792       return;
793     }
794     if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0))
795     {
796       if (NULL != d->update_cb)
797         d->update_cb (d->update_cb_cls, _("`scp' did not complete cleanly.\n"));
798       return;
799     }
800     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
801                 "Successfully copied configuration file.\n");
802     if (NULL != d->update_cb)
803       d->update_cb (d->update_cb_cls, NULL);
804     d->phase = SP_START_DONE;
805     break;
806   }
807 }
808
809 /**
810  * Continues GNUnet daemon startup when user wanted to be notified
811  * once a hostkey was generated (for creating friends files, blacklists,
812  * etc.).
813  *
814  * @param daemon the daemon to finish starting
815  */
816 void
817 GNUNET_TESTING_daemon_continue_startup (struct GNUNET_TESTING_Daemon *daemon)
818 {
819   GNUNET_assert (daemon->phase == SP_HOSTKEY_CREATED);
820   daemon->phase = SP_TOPOLOGY_SETUP;
821 }
822
823 /**
824  * Check whether the given daemon is running.
825  *
826  * @param daemon the daemon to check
827  *
828  * @return GNUNET_YES if the daemon is up, GNUNET_NO if the
829  *         daemon is down, GNUNET_SYSERR on error.
830  */
831 int
832 GNUNET_TESTING_test_daemon_running (struct GNUNET_TESTING_Daemon *daemon)
833 {
834   if (daemon == NULL)
835     return GNUNET_SYSERR;
836
837   if (daemon->running == GNUNET_YES)
838     return GNUNET_YES;
839   return GNUNET_NO;
840 }
841
842
843 /**
844  * Starts a GNUnet daemon service which has been previously stopped.
845  *
846  * @param d the daemon for which the service should be started
847  * @param service the name of the service to start
848  * @param timeout how long to wait for process for shutdown to complete
849  * @param cb function called once the service starts
850  * @param cb_cls closure for cb
851  */
852 void
853 GNUNET_TESTING_daemon_start_stopped_service (struct GNUNET_TESTING_Daemon *d,
854                                              char *service,
855                                              struct GNUNET_TIME_Relative
856                                              timeout,
857                                              GNUNET_TESTING_NotifyDaemonRunning
858                                              cb, void *cb_cls)
859 {
860   char *arg;
861
862   d->cb = cb;
863   d->cb_cls = cb_cls;
864
865   GNUNET_assert (d->running == GNUNET_YES);
866
867   if (d->phase == SP_CONFIG_UPDATE)
868   {
869     GNUNET_SCHEDULER_cancel (d->task);
870     d->phase = SP_START_DONE;
871   }
872
873   if (d->churned_services == NULL)
874   {
875     d->cb (d->cb_cls, &d->id, d->cfg, d,
876            "No service has been churned off yet!!");
877     return;
878   }
879   d->phase = SP_SERVICE_START;
880   GNUNET_free (d->churned_services);
881   d->churned_services = NULL;
882   d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
883   /* Check if this is a local or remote process */
884   if (NULL != d->hostname)
885   {
886     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
887                 "Starting gnunet-arm with config `%s' on host `%s'.\n",
888                 d->cfgfile, d->hostname);
889     if (d->username != NULL)
890       GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname);
891     else
892       arg = GNUNET_strdup (d->hostname);
893
894     d->proc_arm_srv_start = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh",
895                                        "-q",
896                                        arg, "gnunet-arm",
897                                        "-c", d->cfgfile, "-i", service, "-q",
898                                        "-T",
899                                        GNUNET_TIME_relative_to_string (timeout),
900                                        NULL);
901     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
902                 "Starting gnunet-arm with command ssh %s gnunet-arm -c %s -i %s -q\n",
903                 arg, "gnunet-arm", d->cfgfile, service);
904     GNUNET_free (arg);
905   }
906   else
907   {
908     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
909                 "Starting gnunet-arm with config `%s' locally.\n", d->cfgfile);
910     d->proc_arm_srv_start = GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-arm", "gnunet-arm",
911                                        "-c", d->cfgfile, "-i", service, "-q",
912                                        "-T",
913                                        GNUNET_TIME_relative_to_string (timeout),
914                                        NULL);
915   }
916
917   d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
918   d->task = GNUNET_SCHEDULER_add_now (&start_fsm, d);
919 }
920
921 /**
922  * Starts a GNUnet daemon's service.
923  *
924  * @param d the daemon for which the service should be started
925  * @param service the name of the service to start
926  * @param timeout how long to wait for process for startup
927  * @param cb function called once gnunet-arm returns
928  * @param cb_cls closure for cb
929  */
930 void
931 GNUNET_TESTING_daemon_start_service (struct GNUNET_TESTING_Daemon *d,
932                                      const char *service,
933                                      struct GNUNET_TIME_Relative timeout,
934                                      GNUNET_TESTING_NotifyDaemonRunning cb,
935                                      void *cb_cls)
936 {
937   char *arg;
938
939   d->cb = cb;
940   d->cb_cls = cb_cls;
941
942   GNUNET_assert (service != NULL);
943   GNUNET_assert (d->running == GNUNET_YES);
944   GNUNET_assert (d->phase == SP_START_DONE);
945
946   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
947               _("Starting service %s for peer `%4s'\n"), service,
948               GNUNET_i2s (&d->id));
949   d->phase = SP_SERVICE_START;
950   d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
951   /* Check if this is a local or remote process */
952   if (NULL != d->hostname)
953   {
954     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
955                 "Starting gnunet-arm with config `%s' on host `%s'.\n",
956                 d->cfgfile, d->hostname);
957     if (d->username != NULL)
958       GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname);
959     else
960       arg = GNUNET_strdup (d->hostname);
961
962     d->proc_arm_srv_start = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh",
963                                        "-q",
964                                        arg, "gnunet-arm",
965                                        "-c", d->cfgfile, "-i", service, "-q",
966                                        "-T",
967                                        GNUNET_TIME_relative_to_string (timeout),
968                                        NULL);
969     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
970                 "Starting gnunet-arm with command ssh %s gnunet-arm -c %s -i %s -q -T %s\n",
971                 arg, "gnunet-arm", d->cfgfile, service,
972                 GNUNET_TIME_relative_to_string (timeout));
973     GNUNET_free (arg);
974   }
975   else
976   {
977     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
978                 "Starting gnunet-arm with config `%s' locally.\n", d->cfgfile);
979     d->proc_arm_srv_start = GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-arm", "gnunet-arm",
980                                        "-c", d->cfgfile, "-i", service, "-q",
981                                        "-T",
982                                        GNUNET_TIME_relative_to_string (timeout),
983                                        NULL);
984     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
985                 "Starting gnunet-arm with command %s -c %s -i %s -q -T %s\n",
986                 "gnunet-arm", d->cfgfile, service,
987                 GNUNET_TIME_relative_to_string (timeout));
988   }
989
990   d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
991   d->task = GNUNET_SCHEDULER_add_now (&start_fsm, d);
992 }
993
994 /**
995  * Start a peer that has previously been stopped using the daemon_stop
996  * call (and files weren't deleted and the allow restart flag)
997  *
998  * @param daemon the daemon to start (has been previously stopped)
999  * @param timeout how long to wait for restart
1000  * @param cb the callback for notification when the peer is running
1001  * @param cb_cls closure for the callback
1002  */
1003 void
1004 GNUNET_TESTING_daemon_start_stopped (struct GNUNET_TESTING_Daemon *daemon,
1005                                      struct GNUNET_TIME_Relative timeout,
1006                                      GNUNET_TESTING_NotifyDaemonRunning cb,
1007                                      void *cb_cls)
1008 {
1009   if (daemon->running == GNUNET_YES)
1010   {
1011     cb (cb_cls, &daemon->id, daemon->cfg, daemon,
1012         "Daemon already running, can't restart!");
1013     return;
1014   }
1015
1016   daemon->cb = cb;
1017   daemon->cb_cls = cb_cls;
1018   daemon->phase = SP_TOPOLOGY_SETUP;
1019   daemon->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
1020   /* FIXME: why add_continuation? */
1021   GNUNET_SCHEDULER_add_continuation (&start_fsm, daemon,
1022                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1023 }
1024
1025 /**
1026  * Starts a GNUnet daemon.  GNUnet must be installed on the target
1027  * system and available in the PATH.  The machine must furthermore be
1028  * reachable via "ssh" (unless the hostname is "NULL") without the
1029  * need to enter a password.
1030  *
1031  * @param cfg configuration to use
1032  * @param timeout how long to wait starting up peers
1033  * @param pretend GNUNET_YES to set up files but not start peer GNUNET_NO
1034  *                to really start the peer (default)
1035  * @param hostname name of the machine where to run GNUnet
1036  *        (use NULL for localhost).
1037  * @param ssh_username ssh username to use when connecting to hostname
1038  * @param sshport port to pass to ssh process when connecting to hostname
1039  * @param hostkey pointer to a hostkey to be written to disk (instead of being generated)
1040  * @param hostkey_callback function to call once the hostkey has been
1041  *        generated for this peer, but it hasn't yet been started
1042  *        (NULL to start immediately, otherwise waits on GNUNET_TESTING_daemon_continue_start)
1043  * @param hostkey_cls closure for hostkey callback
1044  * @param cb function to call once peer is up, or failed to start
1045  * @param cb_cls closure for cb
1046  * @return handle to the daemon (actual start will be completed asynchronously)
1047  */
1048 struct GNUNET_TESTING_Daemon *
1049 GNUNET_TESTING_daemon_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
1050                              struct GNUNET_TIME_Relative timeout, int pretend,
1051                              const char *hostname, const char *ssh_username,
1052                              uint16_t sshport, const char *hostkey,
1053                              GNUNET_TESTING_NotifyHostkeyCreated
1054                              hostkey_callback, void *hostkey_cls,
1055                              GNUNET_TESTING_NotifyDaemonRunning cb,
1056                              void *cb_cls)
1057 {
1058   struct GNUNET_TESTING_Daemon *ret;
1059   char *arg;
1060   char *username;
1061   char *servicehome;
1062   char *baseservicehome;
1063   char *slash;
1064   char *hostkeyfile;
1065   char *temp_file_name;
1066   struct GNUNET_DISK_FileHandle *fn;
1067   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
1068   struct GNUNET_CRYPTO_RsaPrivateKey *private_key;
1069
1070   ret = GNUNET_malloc (sizeof (struct GNUNET_TESTING_Daemon));
1071   ret->hostname = (hostname == NULL) ? NULL : GNUNET_strdup (hostname);
1072   if (sshport != 0)
1073   {
1074     GNUNET_asprintf (&ret->ssh_port_str, "%d", sshport);
1075   }
1076   else
1077     ret->ssh_port_str = NULL;
1078
1079   /* Find service home and base service home directories, create it if it doesn't exist */
1080   GNUNET_assert (GNUNET_OK ==
1081                  GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS",
1082                                                         "SERVICEHOME",
1083                                                         &servicehome));
1084
1085   GNUNET_assert (GNUNET_OK == GNUNET_DISK_directory_create (servicehome));
1086   GNUNET_asprintf (&temp_file_name, "%s/gnunet-testing-config", servicehome);
1087   ret->cfgfile = GNUNET_DISK_mktemp (temp_file_name);
1088   GNUNET_free (temp_file_name);
1089   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1090               "Setting up peer with configuration file `%s'.\n", ret->cfgfile);
1091   if (NULL == ret->cfgfile)
1092   {
1093     GNUNET_free_non_null (ret->ssh_port_str);
1094     GNUNET_free_non_null (ret->hostname);
1095     GNUNET_free (ret);
1096     return NULL;
1097   }
1098   ret->hostkey_callback = hostkey_callback;
1099   ret->hostkey_cls = hostkey_cls;
1100   ret->cb = cb;
1101   ret->cb_cls = cb_cls;
1102   ret->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
1103   ret->cfg = GNUNET_CONFIGURATION_dup (cfg);
1104   GNUNET_CONFIGURATION_set_value_string (ret->cfg, "PATHS", "DEFAULTCONFIG",
1105                                          ret->cfgfile);
1106
1107   if (hostkey != NULL)          /* Get the peer identity from the hostkey */
1108   {
1109     private_key = GNUNET_CRYPTO_rsa_decode_key (hostkey, HOSTKEYFILESIZE);
1110     GNUNET_assert (private_key != NULL);
1111     GNUNET_CRYPTO_rsa_key_get_public (private_key, &public_key);
1112     GNUNET_CRYPTO_hash (&public_key,
1113                         sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1114                         &ret->id.hashPubKey);
1115     ret->shortname = GNUNET_strdup (GNUNET_i2s (&ret->id));
1116     ret->have_hostkey = GNUNET_YES;
1117     GNUNET_CRYPTO_rsa_key_free (private_key);
1118   }
1119
1120   /* Write hostkey to file, if we were given one */
1121   hostkeyfile = NULL;
1122   if (hostkey != NULL)
1123   {
1124     GNUNET_asprintf (&hostkeyfile, "%s/.hostkey", servicehome);
1125     fn = GNUNET_DISK_file_open (hostkeyfile,
1126                                 GNUNET_DISK_OPEN_READWRITE |
1127                                 GNUNET_DISK_OPEN_CREATE,
1128                                 GNUNET_DISK_PERM_USER_READ |
1129                                 GNUNET_DISK_PERM_USER_WRITE);
1130     GNUNET_assert (fn != NULL);
1131     GNUNET_assert (HOSTKEYFILESIZE ==
1132                    GNUNET_DISK_file_write (fn, hostkey, HOSTKEYFILESIZE));
1133     GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fn));
1134   }
1135
1136   /* write configuration to temporary file */
1137   if (GNUNET_OK != GNUNET_CONFIGURATION_write (ret->cfg, ret->cfgfile))
1138   {
1139     if (0 != UNLINK (ret->cfgfile))
1140       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink",
1141                                 ret->cfgfile);
1142     GNUNET_CONFIGURATION_destroy (ret->cfg);
1143     GNUNET_free_non_null (ret->hostname);
1144     GNUNET_free (ret->cfgfile);
1145     GNUNET_free (ret);
1146     return NULL;
1147   }
1148   if (ssh_username != NULL)
1149     username = GNUNET_strdup (ssh_username);
1150   if ((ssh_username == NULL) &&
1151       (GNUNET_OK !=
1152        GNUNET_CONFIGURATION_get_value_string (cfg, "TESTING", "USERNAME",
1153                                               &username)))
1154   {
1155     if (NULL != getenv ("USER"))
1156       username = GNUNET_strdup (getenv ("USER"));
1157     else
1158       username = NULL;
1159   }
1160   ret->username = username;
1161
1162   if (GNUNET_NO == pretend)     /* Copy files, enter finite state machine */
1163   {
1164     /* copy directory to remote host */
1165     if (NULL != hostname)
1166     {
1167       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1168                   "Copying configuration directory to host `%s'.\n", hostname);
1169       baseservicehome = GNUNET_strdup (servicehome);
1170       /* Remove trailing /'s */
1171       while (baseservicehome[strlen (baseservicehome) - 1] == '/')
1172         baseservicehome[strlen (baseservicehome) - 1] = '\0';
1173       /* Find next directory /, jump one ahead */
1174       slash = strrchr (baseservicehome, '/');
1175       if (slash != NULL)
1176         *(++slash) = '\0';
1177
1178       ret->phase = SP_COPYING;
1179       if (NULL != username)
1180         GNUNET_asprintf (&arg, "%s@%s:%s", username, hostname, baseservicehome);
1181       else
1182         GNUNET_asprintf (&arg, "%s:%s", hostname, baseservicehome);
1183       GNUNET_free (baseservicehome);
1184       if (ret->ssh_port_str == NULL)
1185       {
1186         ret->proc_arm_copying = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "scp", "scp", "-r",
1187                                              "-q",
1188                                              servicehome, arg, NULL);
1189         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1190                     "copying directory with command scp -r %s %s\n",
1191                     servicehome, arg);
1192       }
1193       else
1194       {
1195         ret->proc_arm_copying =
1196             GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "scp", "scp", "-r", "-P",
1197                                      ret->ssh_port_str,
1198                                      "-q",
1199                                      servicehome, arg, NULL);
1200       }
1201       GNUNET_free (arg);
1202       if (NULL == ret->proc_arm_copying)
1203       {
1204         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1205                     _
1206                     ("Could not start `%s' process to copy configuration directory.\n"),
1207                     "scp");
1208         if (0 != UNLINK (ret->cfgfile))
1209           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink",
1210                                     ret->cfgfile);
1211         GNUNET_CONFIGURATION_destroy (ret->cfg);
1212         GNUNET_free_non_null (ret->hostname);
1213         GNUNET_free_non_null (ret->username);
1214         GNUNET_free (ret->cfgfile);
1215         GNUNET_free (ret);
1216         if ((hostkey != NULL) && (0 != UNLINK (hostkeyfile)))
1217           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink",
1218                                     hostkeyfile);
1219         GNUNET_free_non_null (hostkeyfile);
1220         GNUNET_assert (GNUNET_OK == GNUNET_DISK_directory_remove (servicehome));
1221         GNUNET_free (servicehome);
1222         return NULL;
1223       }
1224
1225       ret->task =
1226           GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm,
1227                                         ret);
1228       GNUNET_free_non_null (hostkeyfile);
1229       GNUNET_free (servicehome);
1230       return ret;
1231     }
1232     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1233                 "No need to copy configuration file since we are running locally.\n");
1234     ret->phase = SP_COPIED;
1235     /* FIXME: why add_cont? */
1236     GNUNET_SCHEDULER_add_continuation (&start_fsm, ret,
1237                                        GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1238   }
1239   GNUNET_free_non_null (hostkeyfile);
1240   GNUNET_free (servicehome);
1241   return ret;
1242 }
1243
1244
1245 /**
1246  * Restart (stop and start) a GNUnet daemon.
1247  *
1248  * @param d the daemon that should be restarted
1249  * @param cb function called once the daemon is (re)started
1250  * @param cb_cls closure for cb
1251  */
1252 void
1253 GNUNET_TESTING_daemon_restart (struct GNUNET_TESTING_Daemon *d,
1254                                GNUNET_TESTING_NotifyDaemonRunning cb,
1255                                void *cb_cls)
1256 {
1257   char *arg;
1258   char *del_arg;
1259
1260   del_arg = NULL;
1261   if (NULL != d->cb)
1262   {
1263     d->dead = GNUNET_YES;
1264     return;
1265   }
1266
1267   d->cb = cb;
1268   d->cb_cls = cb_cls;
1269
1270   if (d->phase == SP_CONFIG_UPDATE)
1271   {
1272     GNUNET_SCHEDULER_cancel (d->task);
1273     d->phase = SP_START_DONE;
1274   }
1275   if (d->server != NULL)
1276   {
1277     GNUNET_CORE_disconnect (d->server);
1278     d->server = NULL;
1279   }
1280
1281   if (d->th != NULL)
1282   {
1283     GNUNET_TRANSPORT_get_hello_cancel (d->ghh);
1284     d->ghh = NULL;
1285     GNUNET_TRANSPORT_disconnect (d->th);
1286     d->th = NULL;
1287   }
1288   /* state clean up and notifications */
1289   GNUNET_free_non_null (d->hello);
1290
1291   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Terminating peer `%4s'\n"),
1292               GNUNET_i2s (&d->id));
1293   d->phase = SP_START_ARMING;
1294
1295   /* Check if this is a local or remote process */
1296   if (NULL != d->hostname)
1297   {
1298     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1299                 "Stopping gnunet-arm with config `%s' on host `%s'.\n",
1300                 d->cfgfile, d->hostname);
1301     if (d->username != NULL)
1302       GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname);
1303     else
1304       arg = GNUNET_strdup (d->hostname);
1305
1306     d->proc_arm_stop = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh",
1307                                        "-q",
1308                                        arg, "gnunet-arm",
1309                                        "-c", d->cfgfile, "-e", "-r", NULL);
1310     /* Use -r to restart arm and all services */
1311
1312     GNUNET_free (arg);
1313   }
1314   else
1315   {
1316     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1317                 "Stopping gnunet-arm with config `%s' locally.\n", d->cfgfile);
1318     d->proc_arm_stop = GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-arm", "gnunet-arm",
1319                                        "-c", d->cfgfile, "-e", "-r", NULL);
1320   }
1321
1322   GNUNET_free_non_null (del_arg);
1323   d->task =
1324       GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm, d);
1325
1326 }
1327
1328
1329 /**
1330  * Stops a GNUnet daemon.
1331  *
1332  * @param d the daemon that should be stopped
1333  * @param service the name of the service to stop
1334  * @param timeout how long to wait for process for shutdown to complete
1335  * @param cb function called once the daemon was stopped
1336  * @param cb_cls closure for cb
1337  */
1338 void
1339 GNUNET_TESTING_daemon_stop_service (struct GNUNET_TESTING_Daemon *d,
1340                                     const char *service,
1341                                     struct GNUNET_TIME_Relative timeout,
1342                                     GNUNET_TESTING_NotifyCompletion cb,
1343                                     void *cb_cls)
1344 {
1345   char *arg;
1346
1347   d->dead_cb = cb;
1348   d->dead_cb_cls = cb_cls;
1349
1350   GNUNET_assert (d->running == GNUNET_YES);
1351
1352   if (d->phase == SP_CONFIG_UPDATE)
1353   {
1354     GNUNET_SCHEDULER_cancel (d->task);
1355     d->phase = SP_START_DONE;
1356   }
1357
1358   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Terminating peer `%4s'\n"),
1359               GNUNET_i2s (&d->id));
1360   if (d->churned_services != NULL)
1361   {
1362     d->dead_cb (d->dead_cb_cls, "A service has already been turned off!!");
1363     return;
1364   }
1365   d->phase = SP_SERVICE_SHUTDOWN_START;
1366   d->churned_services = GNUNET_strdup (service);
1367   d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
1368   /* Check if this is a local or remote process */
1369   if (NULL != d->hostname)
1370   {
1371     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1372                 "Stopping gnunet-arm with config `%s' on host `%s'.\n",
1373                 d->cfgfile, d->hostname);
1374     if (d->username != NULL)
1375       GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname);
1376     else
1377       arg = GNUNET_strdup (d->hostname);
1378     
1379     d->proc_arm_srv_stop = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh",
1380                                        "-q",
1381                                        arg, "gnunet-arm",
1382                                        "-c", d->cfgfile, "-k", service, "-q",
1383                                        "-T",
1384                                        GNUNET_TIME_relative_to_string (timeout),
1385                                        NULL);
1386     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1387                 "Stopping gnunet-arm with command ssh %s gnunet-arm -c %s -k %s -q\n",
1388                 arg, "gnunet-arm", d->cfgfile, service);
1389     GNUNET_free (arg);
1390   }
1391   else
1392   {
1393     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1394                 "Stopping gnunet-arm with config `%s' locally.\n", d->cfgfile);
1395     d->proc_arm_srv_stop = GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-arm", "gnunet-arm",
1396                                        "-c", d->cfgfile, "-k", service, "-q",
1397                                        "-T",
1398                                        GNUNET_TIME_relative_to_string (timeout),
1399                                        NULL);
1400   }
1401
1402   d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
1403   d->task = GNUNET_SCHEDULER_add_now (&start_fsm, d);
1404 }
1405
1406
1407 /**
1408  * Forcefully terminate a process and clean up the child.
1409  *
1410  * @param proc handle to process to kill
1411  */
1412 static void
1413 kill_and_close_process (struct GNUNET_OS_Process *proc)
1414 {
1415   (void) GNUNET_OS_process_kill (proc, SIGKILL); 
1416   GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (proc));
1417   GNUNET_OS_process_close (proc);
1418 }
1419
1420
1421 /**
1422  * Stops a GNUnet daemon.
1423  *
1424  * @param d the daemon that should be stopped
1425  * @param timeout how long to wait for process for shutdown to complete
1426  * @param cb function called once the daemon was stopped
1427  * @param cb_cls closure for cb
1428  * @param delete_files GNUNET_YES to remove files, GNUNET_NO
1429  *        to leave them
1430  * @param allow_restart GNUNET_YES to restart peer later (using this API)
1431  *        GNUNET_NO to kill off and clean up for good
1432  */
1433 void
1434 GNUNET_TESTING_daemon_stop (struct GNUNET_TESTING_Daemon *d,
1435                             struct GNUNET_TIME_Relative timeout,
1436                             GNUNET_TESTING_NotifyCompletion cb, void *cb_cls,
1437                             int delete_files, int allow_restart)
1438 {
1439   char *arg;
1440   char *del_arg;
1441
1442   d->dead_cb = cb;
1443   d->dead_cb_cls = cb_cls;
1444
1445   if (NULL != d->cb)
1446   {
1447     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Setting d->dead on peer `%4s'\n"),
1448                 GNUNET_i2s (&d->id));
1449     d->dead = GNUNET_YES;
1450     return;
1451   }
1452   if (NULL != d->proc_arm_start)
1453   {
1454     kill_and_close_process (d->proc_arm_start);
1455     d->proc_arm_start = NULL;
1456   }
1457   if (NULL != d->proc_arm_srv_start)
1458   {
1459     kill_and_close_process (d->proc_arm_srv_start);
1460     d->proc_arm_srv_start = NULL;
1461   }
1462   if (NULL != d->proc_arm_srv_stop)
1463   {
1464     kill_and_close_process (d->proc_arm_srv_stop);
1465     d->proc_arm_srv_stop = NULL;
1466   }
1467   if (NULL != d->proc_arm_copying)
1468   {
1469     kill_and_close_process (d->proc_arm_copying);
1470     d->proc_arm_copying = NULL;
1471   }
1472   if (NULL != d->proc_arm_peerinfo)
1473   {
1474     kill_and_close_process (d->proc_arm_peerinfo);
1475     d->proc_arm_peerinfo = NULL;
1476   }
1477   if ((d->running == GNUNET_NO) && (d->churn == GNUNET_YES))    /* Peer has already been stopped in churn context! */
1478   {
1479     /* Free what was left from churning! */
1480     GNUNET_assert (d->cfg != NULL);
1481     GNUNET_CONFIGURATION_destroy (d->cfg);
1482     if (delete_files == GNUNET_YES)
1483     {
1484       if (0 != UNLINK (d->cfgfile))
1485       {
1486         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "unlink");
1487       }
1488     }
1489     GNUNET_free (d->cfgfile);
1490     GNUNET_free_non_null (d->hostname);
1491     GNUNET_free_non_null (d->username);
1492     if (NULL != d->dead_cb)
1493       d->dead_cb (d->dead_cb_cls, NULL);
1494     /* FIXME: this should be an assert and the test below
1495        should not be required, but testing is broken... */
1496     GNUNET_break (NULL == d->proc_arm_stop);
1497     if (NULL == d->proc_arm_stop) 
1498     GNUNET_free (d);
1499     return;
1500   }
1501
1502   del_arg = NULL;
1503   if (delete_files == GNUNET_YES)
1504   {
1505     GNUNET_asprintf (&del_arg, "-d");
1506   }
1507
1508   if (d->phase == SP_CONFIG_UPDATE)
1509   {
1510     GNUNET_SCHEDULER_cancel (d->task);
1511     d->phase = SP_START_DONE;
1512   }
1513   /** Move this call to scheduled shutdown as fix for CORE_connect calling daemon_stop?
1514   if (d->server != NULL)
1515     {
1516       GNUNET_CORE_disconnect (d->server);
1517       d->server = NULL;
1518     }
1519     */
1520   /* shutdown ARM process (will terminate others) */
1521   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Terminating peer `%4s'\n" ,
1522               GNUNET_i2s (&d->id));
1523   d->phase = SP_SHUTDOWN_START;
1524   d->running = GNUNET_NO;
1525   if (allow_restart == GNUNET_YES)
1526     d->churn = GNUNET_YES;
1527   if (d->th != NULL)
1528   {
1529     GNUNET_TRANSPORT_get_hello_cancel (d->ghh);
1530     d->ghh = NULL;
1531     GNUNET_TRANSPORT_disconnect (d->th);
1532     d->th = NULL;
1533   }
1534   /* Check if this is a local or remote process */
1535
1536
1537   if (NULL != d->hostname)
1538   {
1539     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1540                 "Stopping gnunet-arm with config `%s' on host `%s'.\n",
1541                 d->cfgfile, d->hostname);
1542     if (d->username != NULL)
1543       GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname);
1544     else
1545       arg = GNUNET_strdup (d->hostname);
1546
1547     d->proc_arm_stop = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh",
1548                                        "-q",
1549                                        arg, "gnunet-arm",
1550                                        "-c", d->cfgfile, "-e", "-q", "-T",
1551                                        GNUNET_TIME_relative_to_string (timeout),
1552                                        del_arg, NULL);
1553     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1554                 "Stopping gnunet-arm with command ssh %s gnunet-arm -c %s -e -q %s\n",
1555                 arg, "gnunet-arm", d->cfgfile, del_arg);
1556     /* Use -e to end arm, and -d to remove temp files */
1557     GNUNET_free (arg);
1558   }
1559   else
1560   {
1561     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1562                 "Stopping gnunet-arm with config `%s' locally.\n", d->cfgfile);
1563     d->proc_arm_stop = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "gnunet-arm", "gnunet-arm",
1564                                        "-c", d->cfgfile, "-e", "-q", "-T",
1565                                        GNUNET_TIME_relative_to_string (timeout),
1566                                        del_arg, NULL);
1567     GNUNET_assert (NULL != d->proc_arm_stop);
1568   }
1569
1570   GNUNET_free_non_null (del_arg);
1571   d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
1572   if (GNUNET_SCHEDULER_NO_TASK != d->task)
1573     GNUNET_SCHEDULER_cancel(d->task);
1574   d->task = GNUNET_SCHEDULER_add_now (&start_fsm, d);
1575 }
1576
1577
1578 /**
1579  * Changes the configuration of a GNUnet daemon.
1580  *
1581  * @param d the daemon that should be modified
1582  * @param cfg the new configuration for the daemon
1583  * @param cb function called once the configuration was changed
1584  * @param cb_cls closure for cb
1585  */
1586 void
1587 GNUNET_TESTING_daemon_reconfigure (struct GNUNET_TESTING_Daemon *d,
1588                                    struct GNUNET_CONFIGURATION_Handle *cfg,
1589                                    GNUNET_TESTING_NotifyCompletion cb,
1590                                    void *cb_cls)
1591 {
1592   char *arg;
1593
1594   if (d->phase != SP_START_DONE)
1595   {
1596     if (NULL != cb)
1597       cb (cb_cls,
1598           _
1599           ("Peer not yet running, can not change configuration at this point."));
1600     return;
1601   }
1602
1603   /* 1) write configuration to temporary file */
1604   if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, d->cfgfile))
1605   {
1606     if (NULL != cb)
1607       cb (cb_cls, _("Failed to write new configuration to disk."));
1608     return;
1609   }
1610
1611   /* 2) copy file to remote host (if necessary) */
1612   if (NULL == d->hostname)
1613   {
1614     /* signal success */
1615     if (NULL != cb)
1616       cb (cb_cls, NULL);
1617     return;
1618   }
1619   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1620               "Copying updated configuration file to remote host `%s'.\n",
1621               d->hostname);
1622   d->phase = SP_CONFIG_UPDATE;
1623   if (NULL != d->username)
1624     GNUNET_asprintf (&arg, "%s@%s:%s", d->username, d->hostname, d->cfgfile);
1625   else
1626     GNUNET_asprintf (&arg, "%s:%s", d->hostname, d->cfgfile);
1627   d->proc_arm_copying = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "scp", "scp",
1628                                      "-q",
1629                                      d->cfgfile, arg, NULL);
1630   GNUNET_free (arg);
1631   if (NULL == d->proc_arm_copying)
1632   {
1633     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1634                 _("Could not start `%s' process to copy configuration file.\n"),
1635                 "scp");
1636     if (NULL != cb)
1637       cb (cb_cls, _("Failed to copy new configuration to remote machine."));
1638     d->phase = SP_START_DONE;
1639     return;
1640   }
1641   d->update_cb = cb;
1642   d->update_cb_cls = cb_cls;
1643   d->task =
1644       GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm, d);
1645 }
1646
1647
1648 /**
1649  * Data kept for each pair of peers that we try
1650  * to connect.
1651  */
1652 struct GNUNET_TESTING_ConnectContext
1653 {
1654   /**
1655    * Testing handle to the first daemon.
1656    */
1657   struct GNUNET_TESTING_Daemon *d1;
1658
1659   /**
1660    * Handle to core of first daemon (to check connect)
1661    */
1662   struct GNUNET_CORE_Handle *d1core;
1663
1664   /**
1665    * Have we actually connected to the core of the first daemon yet?
1666    */
1667   int d1core_ready;
1668
1669   /**
1670    * Testing handle to the second daemon.
1671    */
1672   struct GNUNET_TESTING_Daemon *d2;
1673
1674   /**
1675    * Transport handle to the first daemon (to offer the HELLO of the second daemon to).
1676    */
1677   struct GNUNET_TRANSPORT_Handle *d1th;
1678
1679   /**
1680    * Function to call once we are done (or have timed out).
1681    */
1682   GNUNET_TESTING_NotifyConnection cb;
1683
1684   /**
1685    * Closure for "nb".
1686    */
1687   void *cb_cls;
1688
1689   /**
1690    * The relative timeout from whence this connect attempt was
1691    * started.  Allows for reconnect attempts.
1692    */
1693   struct GNUNET_TIME_Relative relative_timeout;
1694
1695   /**
1696    * Maximum number of connect attempts, will retry connection
1697    * this number of times on failures.
1698    */
1699   unsigned int connect_attempts;
1700
1701   /**
1702    * Hello timeout task
1703    */
1704   GNUNET_SCHEDULER_TaskIdentifier hello_send_task;
1705
1706   /**
1707    * Connect timeout task
1708    */
1709   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
1710
1711   /**
1712    * When should this operation be complete (or we must trigger
1713    * a timeout).
1714    */
1715   struct GNUNET_TIME_Relative timeout_hello;
1716
1717   /**
1718    * Was the connection attempt successful?
1719    */
1720   int connected;
1721
1722   /**
1723    * When connecting, do we need to send the HELLO?
1724    */
1725   int send_hello;
1726
1727   /**
1728    * The distance between the two connected peers
1729    */
1730   uint32_t distance;
1731 };
1732
1733
1734 /** Forward declaration **/
1735 static void
1736 reattempt_daemons_connect (void *cls,
1737                            const struct GNUNET_SCHEDULER_TaskContext *tc);
1738
1739
1740 /**
1741  * Notify callback about success or failure of the attempt
1742  * to connect the two peers
1743  *
1744  * @param cls our "struct GNUNET_TESTING_ConnectContext" (freed)
1745  * @param tc reason tells us if we succeeded or failed
1746  */
1747 static void
1748 notify_connect_result (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1749 {
1750   struct GNUNET_TESTING_ConnectContext *ctx = cls;
1751
1752   ctx->timeout_task = GNUNET_SCHEDULER_NO_TASK;
1753   if (ctx->hello_send_task != GNUNET_SCHEDULER_NO_TASK)
1754   {
1755     GNUNET_SCHEDULER_cancel (ctx->hello_send_task);
1756     ctx->hello_send_task = GNUNET_SCHEDULER_NO_TASK;
1757   }
1758
1759   if (ctx->d1th != NULL)
1760     GNUNET_TRANSPORT_disconnect (ctx->d1th);
1761   ctx->d1th = NULL;
1762   if (ctx->d1core != NULL)
1763     GNUNET_CORE_disconnect (ctx->d1core);
1764   ctx->d1core = NULL;
1765
1766   if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
1767   {
1768     GNUNET_free (ctx);
1769     return;
1770   }
1771
1772   if (ctx->connected == GNUNET_YES)
1773   {
1774     if (ctx->cb != NULL)
1775     {
1776       ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, ctx->distance,
1777                ctx->d1->cfg, ctx->d2->cfg, ctx->d1, ctx->d2, NULL);
1778     }
1779   }
1780   else if (ctx->connect_attempts > 0)
1781   {
1782     ctx->d1core_ready = GNUNET_NO;
1783     ctx->timeout_task =
1784         GNUNET_SCHEDULER_add_now (&reattempt_daemons_connect, ctx);
1785     return;
1786   }
1787   else
1788   {
1789     if (ctx->cb != NULL)
1790     {
1791       ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, 0, ctx->d1->cfg,
1792                ctx->d2->cfg, ctx->d1, ctx->d2, _("Peers failed to connect"));
1793     }
1794   }
1795   GNUNET_free (ctx);
1796 }
1797
1798
1799 /**
1800  * Success, connection is up.  Signal client our success.
1801  *
1802  * @param cls our "struct GNUNET_TESTING_ConnectContext"
1803  * @param peer identity of the peer that has connected
1804  * @param atsi performance information
1805  * @param atsi_count number of records in 'atsi'
1806  *
1807  */
1808 static void
1809 connect_notify (void *cls, const struct GNUNET_PeerIdentity *peer,
1810                 const struct GNUNET_ATS_Information *atsi,
1811                 unsigned int atsi_count)
1812 {
1813   struct GNUNET_TESTING_ConnectContext *ctx = cls;
1814
1815   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected peer %s to peer %s\n",
1816               ctx->d1->shortname, GNUNET_i2s (peer));
1817   if (0 != memcmp (&ctx->d2->id, peer, sizeof (struct GNUNET_PeerIdentity)))
1818     return;
1819   ctx->connected = GNUNET_YES;
1820   ctx->distance = 0;            /* FIXME: distance */
1821   if (ctx->hello_send_task != GNUNET_SCHEDULER_NO_TASK)
1822   {
1823     GNUNET_SCHEDULER_cancel (ctx->hello_send_task);
1824     ctx->hello_send_task = GNUNET_SCHEDULER_NO_TASK;
1825   }
1826   GNUNET_SCHEDULER_cancel (ctx->timeout_task);
1827   ctx->timeout_task = GNUNET_SCHEDULER_add_now (&notify_connect_result, ctx);
1828 }
1829
1830
1831 static void
1832 send_hello (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1833 {
1834   struct GNUNET_TESTING_ConnectContext *ctx = cls;
1835   struct GNUNET_MessageHeader *hello;
1836
1837   ctx->hello_send_task = GNUNET_SCHEDULER_NO_TASK;
1838   if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
1839     return;
1840   if ((ctx->d1core_ready == GNUNET_YES) && (ctx->d2->hello != NULL) &&
1841       (NULL != GNUNET_HELLO_get_header (ctx->d2->hello)) &&
1842       (ctx->d1->phase == SP_START_DONE) && (ctx->d2->phase == SP_START_DONE))
1843   {
1844     hello = GNUNET_HELLO_get_header (ctx->d2->hello);
1845     GNUNET_assert (hello != NULL);
1846     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Offering hello of %s to %s\n",
1847                 ctx->d2->shortname, ctx->d1->shortname);
1848     GNUNET_TRANSPORT_offer_hello (ctx->d1th, hello, NULL, NULL);
1849     GNUNET_assert (ctx->d1core != NULL);
1850     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1851                 "Sending connect request to TRANSPORT of %s for peer %s\n",
1852                 GNUNET_i2s (&ctx->d1->id),
1853                 GNUNET_h2s (&ctx->d2->id.hashPubKey));
1854     GNUNET_TRANSPORT_try_connect (ctx->d1th, &ctx->d2->id);
1855     ctx->timeout_hello =
1856         GNUNET_TIME_relative_add (ctx->timeout_hello,
1857                                   GNUNET_TIME_relative_multiply
1858                                   (GNUNET_TIME_UNIT_MILLISECONDS, 500));
1859   }
1860   ctx->hello_send_task =
1861       GNUNET_SCHEDULER_add_delayed (ctx->timeout_hello, &send_hello, ctx);
1862 }
1863
1864 /**
1865  * Notify of a successful connection to the core service.
1866  *
1867  * @param cls a ConnectContext
1868  * @param server handle to the core service
1869  * @param my_identity the peer identity of this peer
1870  */
1871 void
1872 core_init_notify (void *cls, struct GNUNET_CORE_Handle *server,
1873                   const struct GNUNET_PeerIdentity *my_identity)
1874 {
1875   struct GNUNET_TESTING_ConnectContext *connect_ctx = cls;
1876
1877   connect_ctx->d1core_ready = GNUNET_YES;
1878
1879   if (connect_ctx->send_hello == GNUNET_NO)
1880   {
1881     GNUNET_TRANSPORT_try_connect (connect_ctx->d1th, &connect_ctx->d2->id);
1882     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1883                 "Sending connect request to TRANSPORT of %s for peer %s\n",
1884                 connect_ctx->d1->shortname, connect_ctx->d2->shortname);
1885   }
1886 }
1887
1888
1889 /**
1890  * Try to connect again some peers that failed in an earlier attempt. This will
1891  * be tried as many times as connection_attempts in the configuration file.
1892  *
1893  * @param cls Closure (connection context between the two peers).
1894  * @param tc TaskContext.
1895  */
1896 static void
1897 reattempt_daemons_connect (void *cls,
1898                            const struct GNUNET_SCHEDULER_TaskContext *tc)
1899 {
1900   struct GNUNET_TESTING_ConnectContext *ctx = cls;
1901
1902   ctx->timeout_task = GNUNET_SCHEDULER_NO_TASK;
1903   if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
1904     return;
1905   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1906               "re-attempting connect of peer %s to peer %s\n",
1907               ctx->d1->shortname, ctx->d2->shortname);
1908   ctx->connect_attempts--;
1909   GNUNET_assert (ctx->d1core == NULL);
1910   ctx->d1core_ready = GNUNET_NO;
1911   ctx->d1core =
1912       GNUNET_CORE_connect (ctx->d1->cfg, 1, ctx, &core_init_notify,
1913                            &connect_notify, NULL, NULL, GNUNET_NO, NULL,
1914                            GNUNET_NO, no_handlers);
1915   if (ctx->d1core == NULL)
1916   {
1917     if (NULL != ctx->cb)
1918       ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, 0, ctx->d1->cfg,
1919                ctx->d2->cfg, ctx->d1, ctx->d2,
1920                _("Failed to connect to core service of first peer!\n"));
1921     GNUNET_free (ctx);
1922     return;
1923   }
1924
1925   /* Don't know reason for initial connect failure, update the HELLO for the second peer */
1926   if (NULL != ctx->d2->hello)
1927   {
1928     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "updating %s's HELLO\n",
1929                 ctx->d2->shortname);
1930     GNUNET_free (ctx->d2->hello);
1931     ctx->d2->hello = NULL;
1932     if (NULL != ctx->d2->th)
1933     {
1934       GNUNET_TRANSPORT_get_hello_cancel (ctx->d2->ghh);
1935       ctx->d2->ghh = NULL;
1936       GNUNET_TRANSPORT_disconnect (ctx->d2->th);
1937     }
1938     ctx->d2->th =
1939         GNUNET_TRANSPORT_connect (ctx->d2->cfg, &ctx->d2->id, NULL, NULL, NULL,
1940                                   NULL);
1941     GNUNET_assert (ctx->d2->th != NULL);
1942     ctx->d2->ghh =
1943         GNUNET_TRANSPORT_get_hello (ctx->d2->th, &process_hello, ctx->d2);
1944   }
1945   else
1946   {
1947     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "didn't have %s's HELLO\n",
1948                 ctx->d2->shortname);
1949   }
1950
1951   if ((NULL == ctx->d2->hello) && (ctx->d2->th == NULL))
1952   {
1953     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1954                 "didn't have %s's HELLO, trying to get it now\n",
1955                 ctx->d2->shortname);
1956     ctx->d2->th =
1957         GNUNET_TRANSPORT_connect (ctx->d2->cfg, &ctx->d2->id, NULL, NULL, NULL,
1958                                   NULL);
1959     if (NULL == ctx->d2->th)
1960     {
1961       GNUNET_CORE_disconnect (ctx->d1core);
1962       GNUNET_free (ctx);
1963       if (NULL != ctx->cb)
1964         ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, 0, ctx->d1->cfg,
1965                  ctx->d2->cfg, ctx->d1, ctx->d2,
1966                  _("Failed to connect to transport service!\n"));
1967       return;
1968     }
1969     ctx->d2->ghh =
1970         GNUNET_TRANSPORT_get_hello (ctx->d2->th, &process_hello, ctx->d2);
1971   }
1972   else
1973   {
1974     if (NULL == ctx->d2->hello)
1975     {
1976       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1977                   "didn't have %s's HELLO but th wasn't NULL, not trying!!\n",
1978                   ctx->d2->shortname);
1979     }
1980   }
1981
1982   if (ctx->send_hello == GNUNET_YES)
1983   {
1984     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending %s's HELLO to %s\n",
1985                 ctx->d1->shortname, ctx->d2->shortname);
1986     ctx->d1th =
1987         GNUNET_TRANSPORT_connect (ctx->d1->cfg, &ctx->d1->id, ctx->d1, NULL,
1988                                   NULL, NULL);
1989     if (ctx->d1th == NULL)
1990     {
1991       GNUNET_CORE_disconnect (ctx->d1core);
1992       GNUNET_free (ctx);
1993       if (NULL != ctx->cb)
1994         ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, 0, ctx->d1->cfg,
1995                  ctx->d2->cfg, ctx->d1, ctx->d2,
1996                  _("Failed to connect to transport service!\n"));
1997       return;
1998     }
1999     GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == ctx->hello_send_task);
2000     ctx->hello_send_task = GNUNET_SCHEDULER_add_now (&send_hello, ctx);
2001   }
2002   else
2003   {
2004     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to reconnect %s to %s\n",
2005                 ctx->d1->shortname, ctx->d2->shortname);
2006     GNUNET_TRANSPORT_try_connect (ctx->d1th, &ctx->d2->id);
2007   }
2008   ctx->timeout_task =
2009       GNUNET_SCHEDULER_add_delayed (ctx->relative_timeout,
2010                                     &notify_connect_result, ctx);
2011 }
2012
2013 /**
2014  * Iterator for currently known peers, to ensure
2015  * that we don't try to send duplicate connect
2016  * requests to core.
2017  *
2018  * @param cls our "struct GNUNET_TESTING_ConnectContext"
2019  * @param peer identity of the peer that has connected,
2020  *        NULL when iteration has finished
2021  * @param atsi performance information
2022  * @param atsi_count number of records in 'atsi'
2023  *
2024  */
2025 static void
2026 core_initial_iteration (void *cls, const struct GNUNET_PeerIdentity *peer,
2027                         const struct GNUNET_ATS_Information *atsi,
2028                         unsigned int atsi_count)
2029 {
2030   struct GNUNET_TESTING_ConnectContext *ctx = cls;
2031
2032   if ((peer != NULL) &&
2033       (0 == memcmp (&ctx->d2->id, peer, sizeof (struct GNUNET_PeerIdentity))))
2034   {
2035     ctx->connected = GNUNET_YES;
2036     ctx->distance = 0;          /* FIXME: distance */
2037     return;
2038   }
2039   if (peer != NULL)
2040     return;                     /* ignore other peers */
2041   /* peer == NULL: End of iteration over peers */
2042
2043   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == ctx->timeout_task);
2044   if (ctx->connected == GNUNET_YES)
2045   {
2046     ctx->timeout_task = GNUNET_SCHEDULER_add_now (&notify_connect_result, ctx);
2047     return;
2048   }
2049
2050   /* Peer not already connected, need to schedule connect request! */
2051   if (ctx->d1core == NULL)
2052   {
2053     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2054                 "Peers are NOT connected, connecting to core!\n");
2055     ctx->d1core =
2056         GNUNET_CORE_connect (ctx->d1->cfg, 1, ctx, &core_init_notify,
2057                              &connect_notify, NULL, NULL, GNUNET_NO, NULL,
2058                              GNUNET_NO, no_handlers);
2059   }
2060
2061   if (ctx->d1core == NULL)
2062   {
2063     ctx->timeout_task = GNUNET_SCHEDULER_add_now (&notify_connect_result, ctx);
2064     return;
2065   }
2066
2067   if ((NULL == ctx->d2->hello) && (ctx->d2->th == NULL))        /* Do not yet have the second peer's hello, set up a task to get it */
2068   {
2069     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2070                 "Don't have d2's HELLO, trying to get it!\n");
2071     ctx->d2->th =
2072         GNUNET_TRANSPORT_connect (ctx->d2->cfg, &ctx->d2->id, NULL, NULL, NULL,
2073                                   NULL);
2074     if (ctx->d2->th == NULL)
2075     {
2076       GNUNET_CORE_disconnect (ctx->d1core);
2077       ctx->d1core = NULL;
2078       ctx->timeout_task =
2079           GNUNET_SCHEDULER_add_now (&notify_connect_result, ctx);
2080       return;
2081     }
2082     ctx->d2->ghh =
2083         GNUNET_TRANSPORT_get_hello (ctx->d2->th, &process_hello, ctx->d2);
2084   }
2085
2086   if (ctx->send_hello == GNUNET_YES)
2087   {
2088     ctx->d1th =
2089         GNUNET_TRANSPORT_connect (ctx->d1->cfg, &ctx->d1->id, ctx->d1, NULL,
2090                                   NULL, NULL);
2091     if (ctx->d1th == NULL)
2092     {
2093       GNUNET_CORE_disconnect (ctx->d1core);
2094       ctx->d1core = NULL;
2095       ctx->timeout_task =
2096           GNUNET_SCHEDULER_add_now (&notify_connect_result, ctx);
2097       return;
2098     }
2099     GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == ctx->hello_send_task);
2100     ctx->hello_send_task = GNUNET_SCHEDULER_add_now (&send_hello, ctx);
2101   }
2102
2103   ctx->timeout_task =
2104       GNUNET_SCHEDULER_add_delayed (ctx->relative_timeout,
2105                                     &notify_connect_result, ctx);
2106
2107 }
2108
2109
2110 /**
2111  * Establish a connection between two GNUnet daemons.  The daemons
2112  * must both be running and not be stopped until either the
2113  * 'cb' callback is called OR the connection request has been
2114  * explicitly cancelled.
2115  *
2116  * @param d1 handle for the first daemon
2117  * @param d2 handle for the second daemon
2118  * @param timeout how long is the connection attempt
2119  *        allowed to take?
2120  * @param max_connect_attempts how many times should we try to reconnect
2121  *        (within timeout)
2122  * @param send_hello GNUNET_YES to send the HELLO, GNUNET_NO to assume
2123  *                   the HELLO has already been exchanged
2124  * @param cb function to call at the end
2125  * @param cb_cls closure for cb
2126  * @return handle to cancel the request
2127  */
2128 struct GNUNET_TESTING_ConnectContext *
2129 GNUNET_TESTING_daemons_connect (struct GNUNET_TESTING_Daemon *d1,
2130                                 struct GNUNET_TESTING_Daemon *d2,
2131                                 struct GNUNET_TIME_Relative timeout,
2132                                 unsigned int max_connect_attempts,
2133                                 int send_hello,
2134                                 GNUNET_TESTING_NotifyConnection cb,
2135                                 void *cb_cls)
2136 {
2137   struct GNUNET_TESTING_ConnectContext *ctx;
2138
2139   if ((d1->running == GNUNET_NO) || (d2->running == GNUNET_NO))
2140   {
2141     if (NULL != cb)
2142       cb (cb_cls, &d1->id, &d2->id, 0, d1->cfg, d2->cfg, d1, d2,
2143           _("Peers are not fully running yet, can not connect!\n"));
2144     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Peers are not up!\n");
2145     return NULL;
2146   }
2147
2148   ctx = GNUNET_malloc (sizeof (struct GNUNET_TESTING_ConnectContext));
2149   ctx->d1 = d1;
2150   ctx->d2 = d2;
2151   ctx->timeout_hello =
2152       GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 500);
2153   ctx->relative_timeout =
2154       GNUNET_TIME_relative_divide (timeout, max_connect_attempts);
2155   ctx->cb = cb;
2156   ctx->cb_cls = cb_cls;
2157   ctx->connect_attempts = max_connect_attempts;
2158   ctx->connected = GNUNET_NO;
2159   ctx->send_hello = send_hello;
2160   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Asked to connect peer %s to peer %s\n",
2161               d1->shortname, d2->shortname);
2162   /* Core is up! Iterate over all _known_ peers first to check if we are already connected to the peer! */
2163   GNUNET_assert (NULL !=
2164                  GNUNET_CORE_is_peer_connected (ctx->d1->cfg, &ctx->d2->id,
2165                                                 &core_initial_iteration, ctx));
2166   return ctx;
2167 }
2168
2169
2170 /**
2171  * Cancel an attempt to connect two daemons.
2172  *
2173  * @param cc connect context
2174  */
2175 void
2176 GNUNET_TESTING_daemons_connect_cancel (struct GNUNET_TESTING_ConnectContext *cc)
2177 {
2178   if (GNUNET_SCHEDULER_NO_TASK != cc->timeout_task)
2179   {
2180     GNUNET_SCHEDULER_cancel (cc->timeout_task);
2181     cc->timeout_task = GNUNET_SCHEDULER_NO_TASK;
2182   }
2183   if (GNUNET_SCHEDULER_NO_TASK != cc->hello_send_task)
2184   {
2185     GNUNET_SCHEDULER_cancel (cc->hello_send_task);
2186     cc->hello_send_task = GNUNET_SCHEDULER_NO_TASK;
2187   }
2188   if (NULL != cc->d1core)
2189   {
2190     GNUNET_CORE_disconnect (cc->d1core);
2191     cc->d1core = NULL;
2192   }
2193   if (NULL != cc->d1th)
2194   {
2195     GNUNET_TRANSPORT_disconnect (cc->d1th);
2196     cc->d1th = NULL;
2197   }
2198   GNUNET_free (cc);
2199 }
2200
2201
2202 /* end of testing.c */