d9956cd665c341f076b075d90c44a88bbc34f30a
[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     break;
482   case SP_START_ARMING:
483     if (GNUNET_OK != GNUNET_OS_process_status (d->proc_arm_start, &type, &code))
484     {
485       if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0)
486       {
487         cb = d->cb;
488         d->cb = NULL;
489         if (NULL != cb)
490           cb (d->cb_cls, NULL, d->cfg, d,
491               (NULL ==
492                d->hostname) ? _("`gnunet-arm' does not seem to terminate.\n") :
493               _("`ssh' does not seem to terminate.\n"));
494         if (d->cfg != NULL)
495         {
496           GNUNET_CONFIGURATION_destroy (d->cfg);
497           d->cfg = NULL;
498         }
499         if (d->cfgfile != NULL)
500         {
501           GNUNET_free (d->cfgfile);
502           d->cfgfile = NULL;
503         }
504         GNUNET_free_non_null (d->hostname);
505         GNUNET_free_non_null (d->username);
506         GNUNET_free (d->proc_arm_start);
507         d->proc_arm_start = NULL;
508         d->hostname = NULL;     // Quick hack to avoid crashing (testing need to be
509         d->cfg = NULL;          // overhauled anyway, and the error managing is
510         // GNUNET_free (d); // FIXME (could this leak)
511         // pretty broken anyway.
512         return;
513       }
514       /* wait some more */
515       d->task =
516           GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm,
517                                         d);
518       return;
519     }
520     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Successfully started `%s'.\n",
521                 "gnunet-arm");
522     GNUNET_free (d->proc_arm_start);
523     d->proc_arm_start = NULL;
524     d->phase = SP_START_CORE;
525     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Calling CORE_connect\n");
526     /* Fall through */
527   case SP_START_CORE:
528     if (d->server != NULL)
529       GNUNET_CORE_disconnect (d->server);
530
531     d->th = GNUNET_TRANSPORT_connect (d->cfg, &d->id, d, NULL, NULL, NULL);
532     if (d->th == NULL)
533     {
534       if (GNUNET_YES == d->dead)
535         GNUNET_TESTING_daemon_stop (d,
536                                     GNUNET_TIME_absolute_get_remaining
537                                     (d->max_timeout), d->dead_cb,
538                                     d->dead_cb_cls, GNUNET_YES, GNUNET_NO);
539       else if (NULL != d->cb)
540         d->cb (d->cb_cls, &d->id, d->cfg, d,
541                _("Failed to connect to transport service!\n"));
542       return;
543     }
544     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
545                 "Connected to transport service `%s', getting HELLO\n",
546                 GNUNET_i2s (&d->id));
547     d->ghh = GNUNET_TRANSPORT_get_hello (d->th, &process_hello, d);
548     /* FIXME: store task ID somewhere! */
549     GNUNET_SCHEDULER_add_now (&notify_daemon_started, d);
550     /*cb = d->cb;
551      * d->cb = NULL;
552      * if (NULL != cb)
553      * cb (d->cb_cls, &d->id, d->cfg, d, NULL); */
554     d->running = GNUNET_YES;
555     d->phase = SP_GET_HELLO;
556     break;
557   case SP_GET_HELLO:
558     if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0)
559     {
560       if (d->server != NULL)
561         GNUNET_CORE_disconnect (d->server);
562       if (d->th != NULL)
563         GNUNET_TRANSPORT_disconnect (d->th);
564       cb = d->cb;
565       d->cb = NULL;
566       if (NULL != cb)
567         cb (d->cb_cls, NULL, d->cfg, d, _("Unable to get HELLO for peer!\n"));
568       GNUNET_CONFIGURATION_destroy (d->cfg);
569       GNUNET_free (d->cfgfile);
570       GNUNET_free_non_null (d->hostname);
571       GNUNET_free_non_null (d->username);
572       GNUNET_free (d);
573       return;
574     }
575     if (d->hello != NULL)
576       return;
577     GNUNET_assert (d->task == GNUNET_SCHEDULER_NO_TASK);
578     d->task =
579         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
580                                       (GNUNET_CONSTANTS_SERVICE_RETRY, 2),
581                                       &start_fsm, d);
582     break;
583   case SP_START_DONE:
584     GNUNET_break (0);
585     break;
586   case SP_SERVICE_START:
587     /* confirm gnunet-arm exited */
588     if (GNUNET_OK != GNUNET_OS_process_status (d->proc_arm_srv_start, &type, &code))
589     {
590       if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0)
591       {
592         cb = d->cb;
593         d->cb = NULL;
594         if (NULL != cb)
595           cb (d->cb_cls, NULL, d->cfg, d,
596               (NULL ==
597                d->hostname) ? _("`gnunet-arm' does not seem to terminate.\n") :
598               _("`ssh' does not seem to terminate.\n"));
599         return;
600       }
601       /* wait some more */
602       d->task =
603           GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm,
604                                         d);
605       return;
606     }
607 #if EXTRA_CHECKS
608     if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0))
609     {
610       cb = d->cb;
611       d->cb = NULL;
612       if (NULL != cb)
613         cb (d->cb_cls, NULL, d->cfg, d,
614             (NULL ==
615              d->hostname) ?
616             _
617             ("`gnunet-arm' terminated with non-zero exit status (or timed out)!\n")
618             : _("`ssh' does not seem to terminate.\n"));
619       return;
620     }
621 #endif
622     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service startup complete!\n");
623     cb = d->cb;
624     d->cb = NULL;
625     d->phase = SP_START_DONE;
626     if (NULL != cb)
627       cb (d->cb_cls, &d->id, d->cfg, d, NULL);
628     GNUNET_free (d->proc_arm_srv_start);
629     d->proc_arm_srv_start = NULL;
630     break;
631   case SP_SERVICE_SHUTDOWN_START:
632     /* confirm copying complete */
633     if (GNUNET_OK != GNUNET_OS_process_status (d->proc_arm_srv_stop, &type, &code))
634     {
635       if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0)
636       {
637         if (NULL != d->dead_cb)
638           d->dead_cb (d->dead_cb_cls,
639                       _
640                       ("either `gnunet-arm' or `ssh' does not seem to terminate.\n"));
641         return;
642       }
643       /* wait some more */
644       d->task =
645           GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm,
646                                         d);
647       return;
648     }
649 #if EXTRA_CHECKS
650     if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0))
651     {
652       if (NULL != d->dead_cb)
653         d->dead_cb (d->dead_cb_cls,
654                     _
655                     ("shutdown (either `gnunet-arm' or `ssh') did not complete cleanly.\n"));
656       return;
657     }
658 #endif
659     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service shutdown complete.\n");
660     if (NULL != d->dead_cb)
661       d->dead_cb (d->dead_cb_cls, NULL);
662     break;
663   case SP_SHUTDOWN_START:
664     /* confirm copying complete !??? */
665     if (GNUNET_OK != GNUNET_OS_process_status (d->proc_arm_stop, &type, &code))
666     {
667       if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0)
668       {
669         if (NULL != d->dead_cb)
670           d->dead_cb (d->dead_cb_cls,
671                       _
672                       ("either `gnunet-arm' or `ssh' does not seem to terminate.\n"));
673         if (d->th != NULL)
674         {
675           GNUNET_TRANSPORT_get_hello_cancel (d->ghh);
676           d->ghh = NULL;
677           GNUNET_TRANSPORT_disconnect (d->th);
678           d->th = NULL;
679         }
680         if (d->cfg != NULL)
681         {
682           GNUNET_CONFIGURATION_destroy (d->cfg);
683           d->cfg = NULL;
684         }
685         if (d->cfgfile != NULL)
686         {
687           GNUNET_free (d->cfgfile);
688           d->cfgfile = NULL;
689         }
690         GNUNET_free_non_null (d->hello);
691         GNUNET_free_non_null (d->hostname);
692         GNUNET_free_non_null (d->username);
693         GNUNET_free_non_null (d->shortname);
694         // GNUNET_free_non_null (d->proc); // !? FIXME
695         // d->proc_arm_stop = NULL; 
696         GNUNET_free (d);
697         return;
698       }
699       /* wait some more */
700       d->task =
701           GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm,
702                                         d);
703       return;
704     }
705     if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0))
706     {
707       if (NULL != d->dead_cb)
708         d->dead_cb (d->dead_cb_cls,
709                     _
710                     ("shutdown (either `gnunet-arm' or `ssh') did not complete cleanly.\n"));
711       if (d->th != NULL)
712       {
713         GNUNET_TRANSPORT_get_hello_cancel (d->ghh);
714         d->ghh = NULL;
715         GNUNET_TRANSPORT_disconnect (d->th);
716         d->th = NULL;
717       }
718       if (d->server != NULL)
719       {
720         GNUNET_CORE_disconnect (d->server);
721         d->server = NULL;
722       }
723       GNUNET_CONFIGURATION_destroy (d->cfg);
724       d->cfg = NULL;
725       GNUNET_free (d->cfgfile);
726       GNUNET_free_non_null (d->hello);
727       GNUNET_free_non_null (d->hostname);
728       GNUNET_free_non_null (d->username);
729       GNUNET_free_non_null (d->shortname);
730       // GNUNET_free_non_null (d->proc); // !? FIXME!
731       // d->proc = NULL; // !? FIXME!
732       GNUNET_free (d);
733       return;
734     }
735     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer shutdown complete.\n");
736     if (d->server != NULL)
737     {
738       GNUNET_CORE_disconnect (d->server);
739       d->server = NULL;
740     }
741
742     if (d->th != NULL)
743     {
744       GNUNET_TRANSPORT_get_hello_cancel (d->ghh);
745       d->ghh = NULL;
746       GNUNET_TRANSPORT_disconnect (d->th);
747       d->th = NULL;
748     }
749
750     if (NULL != d->dead_cb)
751       d->dead_cb (d->dead_cb_cls, NULL);
752
753     /* state clean up and notifications */
754     if (d->churn == GNUNET_NO)
755     {
756       GNUNET_CONFIGURATION_destroy (d->cfg);
757       d->cfg = NULL;
758       GNUNET_free (d->cfgfile);
759       GNUNET_free_non_null (d->hostname);
760       GNUNET_free_non_null (d->username);
761     }
762
763     GNUNET_free_non_null (d->hello);
764     d->hello = NULL;
765     GNUNET_free_non_null (d->shortname);
766     // GNUNET_free_non_null (d->proc); // !? FIXME
767     // d->proc = NULL; // !? FIXME!
768     d->shortname = NULL;
769     if (d->churn == GNUNET_NO)
770       GNUNET_free (d);
771
772     break;
773   case SP_CONFIG_UPDATE:
774     /* confirm copying complete */
775     if (GNUNET_OK != GNUNET_OS_process_status (d->proc_arm_copying, &type, &code))
776     {
777       if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0)   /* FIXME: config update should take timeout parameter! */
778       {
779         cb = d->cb;
780         d->cb = NULL;
781         if (NULL != cb)
782           cb (d->cb_cls, NULL, d->cfg, d,
783               _("`scp' does not seem to terminate.\n"));
784         return;
785       }
786       /* wait some more */
787       d->task =
788           GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm,
789                                         d);
790       return;
791     }
792     if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0))
793     {
794       if (NULL != d->update_cb)
795         d->update_cb (d->update_cb_cls, _("`scp' did not complete cleanly.\n"));
796       return;
797     }
798     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
799                 "Successfully copied configuration file.\n");
800     if (NULL != d->update_cb)
801       d->update_cb (d->update_cb_cls, NULL);
802     d->phase = SP_START_DONE;
803     break;
804   }
805 }
806
807 /**
808  * Continues GNUnet daemon startup when user wanted to be notified
809  * once a hostkey was generated (for creating friends files, blacklists,
810  * etc.).
811  *
812  * @param daemon the daemon to finish starting
813  */
814 void
815 GNUNET_TESTING_daemon_continue_startup (struct GNUNET_TESTING_Daemon *daemon)
816 {
817   GNUNET_assert (daemon->phase == SP_HOSTKEY_CREATED);
818   daemon->phase = SP_TOPOLOGY_SETUP;
819 }
820
821 /**
822  * Check whether the given daemon is running.
823  *
824  * @param daemon the daemon to check
825  *
826  * @return GNUNET_YES if the daemon is up, GNUNET_NO if the
827  *         daemon is down, GNUNET_SYSERR on error.
828  */
829 int
830 GNUNET_TESTING_test_daemon_running (struct GNUNET_TESTING_Daemon *daemon)
831 {
832   if (daemon == NULL)
833     return GNUNET_SYSERR;
834
835   if (daemon->running == GNUNET_YES)
836     return GNUNET_YES;
837   return GNUNET_NO;
838 }
839
840
841 /**
842  * Starts a GNUnet daemon service which has been previously stopped.
843  *
844  * @param d the daemon for which the service should be started
845  * @param service the name of the service to start
846  * @param timeout how long to wait for process for shutdown to complete
847  * @param cb function called once the service starts
848  * @param cb_cls closure for cb
849  */
850 void
851 GNUNET_TESTING_daemon_start_stopped_service (struct GNUNET_TESTING_Daemon *d,
852                                              char *service,
853                                              struct GNUNET_TIME_Relative
854                                              timeout,
855                                              GNUNET_TESTING_NotifyDaemonRunning
856                                              cb, void *cb_cls)
857 {
858   char *arg;
859
860   d->cb = cb;
861   d->cb_cls = cb_cls;
862
863   GNUNET_assert (d->running == GNUNET_YES);
864
865   if (d->phase == SP_CONFIG_UPDATE)
866   {
867     GNUNET_SCHEDULER_cancel (d->task);
868     d->phase = SP_START_DONE;
869   }
870
871   if (d->churned_services == NULL)
872   {
873     d->cb (d->cb_cls, &d->id, d->cfg, d,
874            "No service has been churned off yet!!");
875     return;
876   }
877   d->phase = SP_SERVICE_START;
878   GNUNET_free (d->churned_services);
879   d->churned_services = NULL;
880   d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
881   /* Check if this is a local or remote process */
882   if (NULL != d->hostname)
883   {
884     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
885                 "Starting gnunet-arm with config `%s' on host `%s'.\n",
886                 d->cfgfile, d->hostname);
887     if (d->username != NULL)
888       GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname);
889     else
890       arg = GNUNET_strdup (d->hostname);
891
892     d->proc_arm_srv_start = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh",
893                                        "-q",
894                                        arg, "gnunet-arm",
895                                        "-c", d->cfgfile, "-i", service, "-q",
896                                        "-T",
897                                        GNUNET_TIME_relative_to_string (timeout),
898                                        NULL);
899     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
900                 "Starting gnunet-arm with command ssh %s gnunet-arm -c %s -i %s -q\n",
901                 arg, "gnunet-arm", d->cfgfile, service);
902     GNUNET_free (arg);
903   }
904   else
905   {
906     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
907                 "Starting gnunet-arm with config `%s' locally.\n", d->cfgfile);
908     d->proc_arm_srv_start = GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-arm", "gnunet-arm",
909                                        "-c", d->cfgfile, "-i", service, "-q",
910                                        "-T",
911                                        GNUNET_TIME_relative_to_string (timeout),
912                                        NULL);
913   }
914
915   d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
916   d->task = GNUNET_SCHEDULER_add_now (&start_fsm, d);
917 }
918
919 /**
920  * Starts a GNUnet daemon's service.
921  *
922  * @param d the daemon for which the service should be started
923  * @param service the name of the service to start
924  * @param timeout how long to wait for process for startup
925  * @param cb function called once gnunet-arm returns
926  * @param cb_cls closure for cb
927  */
928 void
929 GNUNET_TESTING_daemon_start_service (struct GNUNET_TESTING_Daemon *d,
930                                      const char *service,
931                                      struct GNUNET_TIME_Relative timeout,
932                                      GNUNET_TESTING_NotifyDaemonRunning cb,
933                                      void *cb_cls)
934 {
935   char *arg;
936
937   d->cb = cb;
938   d->cb_cls = cb_cls;
939
940   GNUNET_assert (service != NULL);
941   GNUNET_assert (d->running == GNUNET_YES);
942   GNUNET_assert (d->phase == SP_START_DONE);
943
944   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
945               _("Starting service %s for peer `%4s'\n"), service,
946               GNUNET_i2s (&d->id));
947   d->phase = SP_SERVICE_START;
948   d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
949   /* Check if this is a local or remote process */
950   if (NULL != d->hostname)
951   {
952     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
953                 "Starting gnunet-arm with config `%s' on host `%s'.\n",
954                 d->cfgfile, d->hostname);
955     if (d->username != NULL)
956       GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname);
957     else
958       arg = GNUNET_strdup (d->hostname);
959
960     d->proc_arm_srv_start = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh",
961                                        "-q",
962                                        arg, "gnunet-arm",
963                                        "-c", d->cfgfile, "-i", service, "-q",
964                                        "-T",
965                                        GNUNET_TIME_relative_to_string (timeout),
966                                        NULL);
967     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
968                 "Starting gnunet-arm with command ssh %s gnunet-arm -c %s -i %s -q -T %s\n",
969                 arg, "gnunet-arm", d->cfgfile, service,
970                 GNUNET_TIME_relative_to_string (timeout));
971     GNUNET_free (arg);
972   }
973   else
974   {
975     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
976                 "Starting gnunet-arm with config `%s' locally.\n", d->cfgfile);
977     d->proc_arm_srv_start = GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-arm", "gnunet-arm",
978                                        "-c", d->cfgfile, "-i", service, "-q",
979                                        "-T",
980                                        GNUNET_TIME_relative_to_string (timeout),
981                                        NULL);
982     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
983                 "Starting gnunet-arm with command %s -c %s -i %s -q -T %s\n",
984                 "gnunet-arm", d->cfgfile, service,
985                 GNUNET_TIME_relative_to_string (timeout));
986   }
987
988   d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
989   d->task = GNUNET_SCHEDULER_add_now (&start_fsm, d);
990 }
991
992 /**
993  * Start a peer that has previously been stopped using the daemon_stop
994  * call (and files weren't deleted and the allow restart flag)
995  *
996  * @param daemon the daemon to start (has been previously stopped)
997  * @param timeout how long to wait for restart
998  * @param cb the callback for notification when the peer is running
999  * @param cb_cls closure for the callback
1000  */
1001 void
1002 GNUNET_TESTING_daemon_start_stopped (struct GNUNET_TESTING_Daemon *daemon,
1003                                      struct GNUNET_TIME_Relative timeout,
1004                                      GNUNET_TESTING_NotifyDaemonRunning cb,
1005                                      void *cb_cls)
1006 {
1007   if (daemon->running == GNUNET_YES)
1008   {
1009     cb (cb_cls, &daemon->id, daemon->cfg, daemon,
1010         "Daemon already running, can't restart!");
1011     return;
1012   }
1013
1014   daemon->cb = cb;
1015   daemon->cb_cls = cb_cls;
1016   daemon->phase = SP_TOPOLOGY_SETUP;
1017   daemon->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
1018   /* FIXME: why add_continuation? */
1019   GNUNET_SCHEDULER_add_continuation (&start_fsm, daemon,
1020                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1021 }
1022
1023 /**
1024  * Starts a GNUnet daemon.  GNUnet must be installed on the target
1025  * system and available in the PATH.  The machine must furthermore be
1026  * reachable via "ssh" (unless the hostname is "NULL") without the
1027  * need to enter a password.
1028  *
1029  * @param cfg configuration to use
1030  * @param timeout how long to wait starting up peers
1031  * @param pretend GNUNET_YES to set up files but not start peer GNUNET_NO
1032  *                to really start the peer (default)
1033  * @param hostname name of the machine where to run GNUnet
1034  *        (use NULL for localhost).
1035  * @param ssh_username ssh username to use when connecting to hostname
1036  * @param sshport port to pass to ssh process when connecting to hostname
1037  * @param hostkey pointer to a hostkey to be written to disk (instead of being generated)
1038  * @param hostkey_callback function to call once the hostkey has been
1039  *        generated for this peer, but it hasn't yet been started
1040  *        (NULL to start immediately, otherwise waits on GNUNET_TESTING_daemon_continue_start)
1041  * @param hostkey_cls closure for hostkey callback
1042  * @param cb function to call once peer is up, or failed to start
1043  * @param cb_cls closure for cb
1044  * @return handle to the daemon (actual start will be completed asynchronously)
1045  */
1046 struct GNUNET_TESTING_Daemon *
1047 GNUNET_TESTING_daemon_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
1048                              struct GNUNET_TIME_Relative timeout, int pretend,
1049                              const char *hostname, const char *ssh_username,
1050                              uint16_t sshport, const char *hostkey,
1051                              GNUNET_TESTING_NotifyHostkeyCreated
1052                              hostkey_callback, void *hostkey_cls,
1053                              GNUNET_TESTING_NotifyDaemonRunning cb,
1054                              void *cb_cls)
1055 {
1056   struct GNUNET_TESTING_Daemon *ret;
1057   char *arg;
1058   char *username;
1059   char *servicehome;
1060   char *baseservicehome;
1061   char *slash;
1062   char *hostkeyfile;
1063   char *temp_file_name;
1064   struct GNUNET_DISK_FileHandle *fn;
1065   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
1066   struct GNUNET_CRYPTO_RsaPrivateKey *private_key;
1067
1068   ret = GNUNET_malloc (sizeof (struct GNUNET_TESTING_Daemon));
1069   ret->hostname = (hostname == NULL) ? NULL : GNUNET_strdup (hostname);
1070   if (sshport != 0)
1071   {
1072     GNUNET_asprintf (&ret->ssh_port_str, "%d", sshport);
1073   }
1074   else
1075     ret->ssh_port_str = NULL;
1076
1077   /* Find service home and base service home directories, create it if it doesn't exist */
1078   GNUNET_assert (GNUNET_OK ==
1079                  GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS",
1080                                                         "SERVICEHOME",
1081                                                         &servicehome));
1082
1083   GNUNET_assert (GNUNET_OK == GNUNET_DISK_directory_create (servicehome));
1084   GNUNET_asprintf (&temp_file_name, "%s/gnunet-testing-config", servicehome);
1085   ret->cfgfile = GNUNET_DISK_mktemp (temp_file_name);
1086   GNUNET_free (temp_file_name);
1087   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1088               "Setting up peer with configuration file `%s'.\n", ret->cfgfile);
1089   if (NULL == ret->cfgfile)
1090   {
1091     GNUNET_free_non_null (ret->ssh_port_str);
1092     GNUNET_free_non_null (ret->hostname);
1093     GNUNET_free (ret);
1094     return NULL;
1095   }
1096   ret->hostkey_callback = hostkey_callback;
1097   ret->hostkey_cls = hostkey_cls;
1098   ret->cb = cb;
1099   ret->cb_cls = cb_cls;
1100   ret->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
1101   ret->cfg = GNUNET_CONFIGURATION_dup (cfg);
1102   GNUNET_CONFIGURATION_set_value_string (ret->cfg, "PATHS", "DEFAULTCONFIG",
1103                                          ret->cfgfile);
1104
1105   if (hostkey != NULL)          /* Get the peer identity from the hostkey */
1106   {
1107     private_key = GNUNET_CRYPTO_rsa_decode_key (hostkey, HOSTKEYFILESIZE);
1108     GNUNET_assert (private_key != NULL);
1109     GNUNET_CRYPTO_rsa_key_get_public (private_key, &public_key);
1110     GNUNET_CRYPTO_hash (&public_key,
1111                         sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1112                         &ret->id.hashPubKey);
1113     ret->shortname = GNUNET_strdup (GNUNET_i2s (&ret->id));
1114     ret->have_hostkey = GNUNET_YES;
1115     GNUNET_CRYPTO_rsa_key_free (private_key);
1116   }
1117
1118   /* Write hostkey to file, if we were given one */
1119   hostkeyfile = NULL;
1120   if (hostkey != NULL)
1121   {
1122     GNUNET_asprintf (&hostkeyfile, "%s/.hostkey", servicehome);
1123     fn = GNUNET_DISK_file_open (hostkeyfile,
1124                                 GNUNET_DISK_OPEN_READWRITE |
1125                                 GNUNET_DISK_OPEN_CREATE,
1126                                 GNUNET_DISK_PERM_USER_READ |
1127                                 GNUNET_DISK_PERM_USER_WRITE);
1128     GNUNET_assert (fn != NULL);
1129     GNUNET_assert (HOSTKEYFILESIZE ==
1130                    GNUNET_DISK_file_write (fn, hostkey, HOSTKEYFILESIZE));
1131     GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fn));
1132   }
1133
1134   /* write configuration to temporary file */
1135   if (GNUNET_OK != GNUNET_CONFIGURATION_write (ret->cfg, ret->cfgfile))
1136   {
1137     if (0 != UNLINK (ret->cfgfile))
1138       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink",
1139                                 ret->cfgfile);
1140     GNUNET_CONFIGURATION_destroy (ret->cfg);
1141     GNUNET_free_non_null (ret->hostname);
1142     GNUNET_free (ret->cfgfile);
1143     GNUNET_free (ret);
1144     return NULL;
1145   }
1146   if (ssh_username != NULL)
1147     username = GNUNET_strdup (ssh_username);
1148   if ((ssh_username == NULL) &&
1149       (GNUNET_OK !=
1150        GNUNET_CONFIGURATION_get_value_string (cfg, "TESTING", "USERNAME",
1151                                               &username)))
1152   {
1153     if (NULL != getenv ("USER"))
1154       username = GNUNET_strdup (getenv ("USER"));
1155     else
1156       username = NULL;
1157   }
1158   ret->username = username;
1159
1160   if (GNUNET_NO == pretend)     /* Copy files, enter finite state machine */
1161   {
1162     /* copy directory to remote host */
1163     if (NULL != hostname)
1164     {
1165       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1166                   "Copying configuration directory to host `%s'.\n", hostname);
1167       baseservicehome = GNUNET_strdup (servicehome);
1168       /* Remove trailing /'s */
1169       while (baseservicehome[strlen (baseservicehome) - 1] == '/')
1170         baseservicehome[strlen (baseservicehome) - 1] = '\0';
1171       /* Find next directory /, jump one ahead */
1172       slash = strrchr (baseservicehome, '/');
1173       if (slash != NULL)
1174         *(++slash) = '\0';
1175
1176       ret->phase = SP_COPYING;
1177       if (NULL != username)
1178         GNUNET_asprintf (&arg, "%s@%s:%s", username, hostname, baseservicehome);
1179       else
1180         GNUNET_asprintf (&arg, "%s:%s", hostname, baseservicehome);
1181       GNUNET_free (baseservicehome);
1182       if (ret->ssh_port_str == NULL)
1183       {
1184         ret->proc_arm_copying = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "scp", "scp", "-r",
1185                                              "-q",
1186                                              servicehome, arg, NULL);
1187         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1188                     "copying directory with command scp -r %s %s\n",
1189                     servicehome, arg);
1190       }
1191       else
1192       {
1193         ret->proc_arm_copying =
1194             GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "scp", "scp", "-r", "-P",
1195                                      ret->ssh_port_str,
1196                                      "-q",
1197                                      servicehome, arg, NULL);
1198       }
1199       GNUNET_free (arg);
1200       if (NULL == ret->proc_arm_copying)
1201       {
1202         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1203                     _
1204                     ("Could not start `%s' process to copy configuration directory.\n"),
1205                     "scp");
1206         if (0 != UNLINK (ret->cfgfile))
1207           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink",
1208                                     ret->cfgfile);
1209         GNUNET_CONFIGURATION_destroy (ret->cfg);
1210         GNUNET_free_non_null (ret->hostname);
1211         GNUNET_free_non_null (ret->username);
1212         GNUNET_free (ret->cfgfile);
1213         GNUNET_free (ret);
1214         if ((hostkey != NULL) && (0 != UNLINK (hostkeyfile)))
1215           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink",
1216                                     hostkeyfile);
1217         GNUNET_free_non_null (hostkeyfile);
1218         GNUNET_assert (GNUNET_OK == GNUNET_DISK_directory_remove (servicehome));
1219         GNUNET_free (servicehome);
1220         return NULL;
1221       }
1222
1223       ret->task =
1224           GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm,
1225                                         ret);
1226       GNUNET_free_non_null (hostkeyfile);
1227       GNUNET_free (servicehome);
1228       return ret;
1229     }
1230     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1231                 "No need to copy configuration file since we are running locally.\n");
1232     ret->phase = SP_COPIED;
1233     /* FIXME: why add_cont? */
1234     GNUNET_SCHEDULER_add_continuation (&start_fsm, ret,
1235                                        GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1236   }
1237   GNUNET_free_non_null (hostkeyfile);
1238   GNUNET_free (servicehome);
1239   return ret;
1240 }
1241
1242
1243 /**
1244  * Restart (stop and start) a GNUnet daemon.
1245  *
1246  * @param d the daemon that should be restarted
1247  * @param cb function called once the daemon is (re)started
1248  * @param cb_cls closure for cb
1249  */
1250 void
1251 GNUNET_TESTING_daemon_restart (struct GNUNET_TESTING_Daemon *d,
1252                                GNUNET_TESTING_NotifyDaemonRunning cb,
1253                                void *cb_cls)
1254 {
1255   char *arg;
1256   char *del_arg;
1257
1258   del_arg = NULL;
1259   if (NULL != d->cb)
1260   {
1261     d->dead = GNUNET_YES;
1262     return;
1263   }
1264
1265   d->cb = cb;
1266   d->cb_cls = cb_cls;
1267
1268   if (d->phase == SP_CONFIG_UPDATE)
1269   {
1270     GNUNET_SCHEDULER_cancel (d->task);
1271     d->phase = SP_START_DONE;
1272   }
1273   if (d->server != NULL)
1274   {
1275     GNUNET_CORE_disconnect (d->server);
1276     d->server = NULL;
1277   }
1278
1279   if (d->th != NULL)
1280   {
1281     GNUNET_TRANSPORT_get_hello_cancel (d->ghh);
1282     d->ghh = NULL;
1283     GNUNET_TRANSPORT_disconnect (d->th);
1284     d->th = NULL;
1285   }
1286   /* state clean up and notifications */
1287   GNUNET_free_non_null (d->hello);
1288
1289   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Terminating peer `%4s'\n"),
1290               GNUNET_i2s (&d->id));
1291   d->phase = SP_START_ARMING;
1292
1293   /* Check if this is a local or remote process */
1294   if (NULL != d->hostname)
1295   {
1296     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1297                 "Stopping gnunet-arm with config `%s' on host `%s'.\n",
1298                 d->cfgfile, d->hostname);
1299     if (d->username != NULL)
1300       GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname);
1301     else
1302       arg = GNUNET_strdup (d->hostname);
1303
1304     d->proc_arm_stop = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh",
1305                                        "-q",
1306                                        arg, "gnunet-arm",
1307                                        "-c", d->cfgfile, "-e", "-r", NULL);
1308     /* Use -r to restart arm and all services */
1309
1310     GNUNET_free (arg);
1311   }
1312   else
1313   {
1314     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1315                 "Stopping gnunet-arm with config `%s' locally.\n", d->cfgfile);
1316     d->proc_arm_stop = GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-arm", "gnunet-arm",
1317                                        "-c", d->cfgfile, "-e", "-r", NULL);
1318   }
1319
1320   GNUNET_free_non_null (del_arg);
1321   d->task =
1322       GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm, d);
1323
1324 }
1325
1326
1327 /**
1328  * Stops a GNUnet daemon.
1329  *
1330  * @param d the daemon that should be stopped
1331  * @param service the name of the service to stop
1332  * @param timeout how long to wait for process for shutdown to complete
1333  * @param cb function called once the daemon was stopped
1334  * @param cb_cls closure for cb
1335  */
1336 void
1337 GNUNET_TESTING_daemon_stop_service (struct GNUNET_TESTING_Daemon *d,
1338                                     const char *service,
1339                                     struct GNUNET_TIME_Relative timeout,
1340                                     GNUNET_TESTING_NotifyCompletion cb,
1341                                     void *cb_cls)
1342 {
1343   char *arg;
1344
1345   d->dead_cb = cb;
1346   d->dead_cb_cls = cb_cls;
1347
1348   GNUNET_assert (d->running == GNUNET_YES);
1349
1350   if (d->phase == SP_CONFIG_UPDATE)
1351   {
1352     GNUNET_SCHEDULER_cancel (d->task);
1353     d->phase = SP_START_DONE;
1354   }
1355
1356   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Terminating peer `%4s'\n"),
1357               GNUNET_i2s (&d->id));
1358   if (d->churned_services != NULL)
1359   {
1360     d->dead_cb (d->dead_cb_cls, "A service has already been turned off!!");
1361     return;
1362   }
1363   d->phase = SP_SERVICE_SHUTDOWN_START;
1364   d->churned_services = GNUNET_strdup (service);
1365   d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
1366   /* Check if this is a local or remote process */
1367   if (NULL != d->hostname)
1368   {
1369     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1370                 "Stopping gnunet-arm with config `%s' on host `%s'.\n",
1371                 d->cfgfile, d->hostname);
1372     if (d->username != NULL)
1373       GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname);
1374     else
1375       arg = GNUNET_strdup (d->hostname);
1376     
1377     d->proc_arm_srv_stop = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh",
1378                                        "-q",
1379                                        arg, "gnunet-arm",
1380                                        "-c", d->cfgfile, "-k", service, "-q",
1381                                        "-T",
1382                                        GNUNET_TIME_relative_to_string (timeout),
1383                                        NULL);
1384     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1385                 "Stopping gnunet-arm with command ssh %s gnunet-arm -c %s -k %s -q\n",
1386                 arg, "gnunet-arm", d->cfgfile, service);
1387     GNUNET_free (arg);
1388   }
1389   else
1390   {
1391     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1392                 "Stopping gnunet-arm with config `%s' locally.\n", d->cfgfile);
1393     d->proc_arm_srv_stop = GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-arm", "gnunet-arm",
1394                                        "-c", d->cfgfile, "-k", service, "-q",
1395                                        "-T",
1396                                        GNUNET_TIME_relative_to_string (timeout),
1397                                        NULL);
1398   }
1399
1400   d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
1401   d->task = GNUNET_SCHEDULER_add_now (&start_fsm, d);
1402 }
1403
1404
1405 /**
1406  * Forcefully terminate a process and clean up the child.
1407  *
1408  * @param proc handle to process to kill
1409  */
1410 static void
1411 kill_and_close_process (struct GNUNET_OS_Process *proc)
1412 {
1413   (void) GNUNET_OS_process_kill (proc, SIGKILL);      
1414   GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (proc));
1415   GNUNET_OS_process_close (proc);
1416 }
1417
1418
1419 /**
1420  * Stops a GNUnet daemon.
1421  *
1422  * @param d the daemon that should be stopped
1423  * @param timeout how long to wait for process for shutdown to complete
1424  * @param cb function called once the daemon was stopped
1425  * @param cb_cls closure for cb
1426  * @param delete_files GNUNET_YES to remove files, GNUNET_NO
1427  *        to leave them
1428  * @param allow_restart GNUNET_YES to restart peer later (using this API)
1429  *        GNUNET_NO to kill off and clean up for good
1430  */
1431 void
1432 GNUNET_TESTING_daemon_stop (struct GNUNET_TESTING_Daemon *d,
1433                             struct GNUNET_TIME_Relative timeout,
1434                             GNUNET_TESTING_NotifyCompletion cb, void *cb_cls,
1435                             int delete_files, int allow_restart)
1436 {
1437   char *arg;
1438   char *del_arg;
1439
1440   d->dead_cb = cb;
1441   d->dead_cb_cls = cb_cls;
1442
1443   if (NULL != d->cb)
1444   {
1445     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Setting d->dead on peer `%4s'\n"),
1446                 GNUNET_i2s (&d->id));
1447     d->dead = GNUNET_YES;
1448     return;
1449   }
1450   if (NULL != d->proc_arm_start)
1451   {
1452     kill_and_close_process (d->proc_arm_start);
1453     d->proc_arm_start = NULL;
1454   }
1455   if (NULL != d->proc_arm_srv_start)
1456   {
1457     kill_and_close_process (d->proc_arm_srv_start);
1458     d->proc_arm_srv_start = NULL;
1459   }
1460   if (NULL != d->proc_arm_srv_stop)
1461   {
1462     kill_and_close_process (d->proc_arm_srv_stop);
1463     d->proc_arm_srv_stop = NULL;
1464   }
1465   if (NULL != d->proc_arm_copying)
1466   {
1467     kill_and_close_process (d->proc_arm_copying);
1468     d->proc_arm_copying = NULL;
1469   }
1470   if (NULL != d->proc_arm_peerinfo)
1471   {
1472     kill_and_close_process (d->proc_arm_peerinfo);
1473     d->proc_arm_peerinfo = NULL;
1474   }
1475   if ((d->running == GNUNET_NO) && (d->churn == GNUNET_YES))    /* Peer has already been stopped in churn context! */
1476   {
1477     /* Free what was left from churning! */
1478     GNUNET_assert (d->cfg != NULL);
1479     GNUNET_CONFIGURATION_destroy (d->cfg);
1480     if (delete_files == GNUNET_YES)
1481     {
1482       if (0 != UNLINK (d->cfgfile))
1483       {
1484         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "unlink");
1485       }
1486     }
1487     GNUNET_free (d->cfgfile);
1488     GNUNET_free_non_null (d->hostname);
1489     GNUNET_free_non_null (d->username);
1490     if (NULL != d->dead_cb)
1491       d->dead_cb (d->dead_cb_cls, NULL);
1492     /* FIXME: this should be an assert and the test below
1493        should not be required, but testing is broken... */
1494     GNUNET_break (NULL == d->proc_arm_stop);
1495     if (NULL == d->proc_arm_stop) 
1496     GNUNET_free (d);
1497     return;
1498   }
1499
1500   del_arg = NULL;
1501   if (delete_files == GNUNET_YES)
1502   {
1503     GNUNET_asprintf (&del_arg, "-d");
1504   }
1505
1506   if (d->phase == SP_CONFIG_UPDATE)
1507   {
1508     GNUNET_SCHEDULER_cancel (d->task);
1509     d->phase = SP_START_DONE;
1510   }
1511   /** Move this call to scheduled shutdown as fix for CORE_connect calling daemon_stop?
1512   if (d->server != NULL)
1513     {
1514       GNUNET_CORE_disconnect (d->server);
1515       d->server = NULL;
1516     }
1517     */
1518   /* shutdown ARM process (will terminate others) */
1519   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Terminating peer `%4s'\n" ,
1520               GNUNET_i2s (&d->id));
1521   d->phase = SP_SHUTDOWN_START;
1522   d->running = GNUNET_NO;
1523   if (allow_restart == GNUNET_YES)
1524     d->churn = GNUNET_YES;
1525   if (d->th != NULL)
1526   {
1527     GNUNET_TRANSPORT_get_hello_cancel (d->ghh);
1528     d->ghh = NULL;
1529     GNUNET_TRANSPORT_disconnect (d->th);
1530     d->th = NULL;
1531   }
1532   /* Check if this is a local or remote process */
1533
1534
1535   if (NULL != d->hostname)
1536   {
1537     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1538                 "Stopping gnunet-arm with config `%s' on host `%s'.\n",
1539                 d->cfgfile, d->hostname);
1540     if (d->username != NULL)
1541       GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname);
1542     else
1543       arg = GNUNET_strdup (d->hostname);
1544
1545     d->proc_arm_stop = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh",
1546                                        "-q",
1547                                        arg, "gnunet-arm",
1548                                        "-c", d->cfgfile, "-e", "-q", "-T",
1549                                        GNUNET_TIME_relative_to_string (timeout),
1550                                        del_arg, NULL);
1551     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1552                 "Stopping gnunet-arm with command ssh %s gnunet-arm -c %s -e -q %s\n",
1553                 arg, "gnunet-arm", d->cfgfile, del_arg);
1554     /* Use -e to end arm, and -d to remove temp files */
1555     GNUNET_free (arg);
1556   }
1557   else
1558   {
1559     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1560                 "Stopping gnunet-arm with config `%s' locally.\n", d->cfgfile);
1561     d->proc_arm_stop = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "gnunet-arm", "gnunet-arm",
1562                                        "-c", d->cfgfile, "-e", "-q", "-T",
1563                                        GNUNET_TIME_relative_to_string (timeout),
1564                                        del_arg, NULL);
1565     GNUNET_assert (NULL != d->proc_arm_stop);
1566   }
1567
1568   GNUNET_free_non_null (del_arg);
1569   d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
1570   if (GNUNET_SCHEDULER_NO_TASK != d->task)
1571     GNUNET_SCHEDULER_cancel(d->task);
1572   d->task = GNUNET_SCHEDULER_add_now (&start_fsm, d);
1573 }
1574
1575
1576 /**
1577  * Changes the configuration of a GNUnet daemon.
1578  *
1579  * @param d the daemon that should be modified
1580  * @param cfg the new configuration for the daemon
1581  * @param cb function called once the configuration was changed
1582  * @param cb_cls closure for cb
1583  */
1584 void
1585 GNUNET_TESTING_daemon_reconfigure (struct GNUNET_TESTING_Daemon *d,
1586                                    struct GNUNET_CONFIGURATION_Handle *cfg,
1587                                    GNUNET_TESTING_NotifyCompletion cb,
1588                                    void *cb_cls)
1589 {
1590   char *arg;
1591
1592   if (d->phase != SP_START_DONE)
1593   {
1594     if (NULL != cb)
1595       cb (cb_cls,
1596           _
1597           ("Peer not yet running, can not change configuration at this point."));
1598     return;
1599   }
1600
1601   /* 1) write configuration to temporary file */
1602   if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, d->cfgfile))
1603   {
1604     if (NULL != cb)
1605       cb (cb_cls, _("Failed to write new configuration to disk."));
1606     return;
1607   }
1608
1609   /* 2) copy file to remote host (if necessary) */
1610   if (NULL == d->hostname)
1611   {
1612     /* signal success */
1613     if (NULL != cb)
1614       cb (cb_cls, NULL);
1615     return;
1616   }
1617   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1618               "Copying updated configuration file to remote host `%s'.\n",
1619               d->hostname);
1620   d->phase = SP_CONFIG_UPDATE;
1621   if (NULL != d->username)
1622     GNUNET_asprintf (&arg, "%s@%s:%s", d->username, d->hostname, d->cfgfile);
1623   else
1624     GNUNET_asprintf (&arg, "%s:%s", d->hostname, d->cfgfile);
1625   d->proc_arm_copying = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "scp", "scp",
1626                                      "-q",
1627                                      d->cfgfile, arg, NULL);
1628   GNUNET_free (arg);
1629   if (NULL == d->proc_arm_copying)
1630   {
1631     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1632                 _("Could not start `%s' process to copy configuration file.\n"),
1633                 "scp");
1634     if (NULL != cb)
1635       cb (cb_cls, _("Failed to copy new configuration to remote machine."));
1636     d->phase = SP_START_DONE;
1637     return;
1638   }
1639   d->update_cb = cb;
1640   d->update_cb_cls = cb_cls;
1641   d->task =
1642       GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm, d);
1643 }
1644
1645
1646 /**
1647  * Data kept for each pair of peers that we try
1648  * to connect.
1649  */
1650 struct GNUNET_TESTING_ConnectContext
1651 {
1652   /**
1653    * Testing handle to the first daemon.
1654    */
1655   struct GNUNET_TESTING_Daemon *d1;
1656
1657   /**
1658    * Handle to core of first daemon (to check connect)
1659    */
1660   struct GNUNET_CORE_Handle *d1core;
1661
1662   /**
1663    * Have we actually connected to the core of the first daemon yet?
1664    */
1665   int d1core_ready;
1666
1667   /**
1668    * Testing handle to the second daemon.
1669    */
1670   struct GNUNET_TESTING_Daemon *d2;
1671
1672   /**
1673    * Transport handle to the first daemon (to offer the HELLO of the second daemon to).
1674    */
1675   struct GNUNET_TRANSPORT_Handle *d1th;
1676
1677   /**
1678    * Function to call once we are done (or have timed out).
1679    */
1680   GNUNET_TESTING_NotifyConnection cb;
1681
1682   /**
1683    * Closure for "nb".
1684    */
1685   void *cb_cls;
1686
1687   /**
1688    * The relative timeout from whence this connect attempt was
1689    * started.  Allows for reconnect attempts.
1690    */
1691   struct GNUNET_TIME_Relative relative_timeout;
1692
1693   /**
1694    * Maximum number of connect attempts, will retry connection
1695    * this number of times on failures.
1696    */
1697   unsigned int connect_attempts;
1698
1699   /**
1700    * Hello timeout task
1701    */
1702   GNUNET_SCHEDULER_TaskIdentifier hello_send_task;
1703
1704   /**
1705    * Connect timeout task
1706    */
1707   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
1708
1709   /**
1710    * When should this operation be complete (or we must trigger
1711    * a timeout).
1712    */
1713   struct GNUNET_TIME_Relative timeout_hello;
1714
1715   /**
1716    * Was the connection attempt successful?
1717    */
1718   int connected;
1719
1720   /**
1721    * When connecting, do we need to send the HELLO?
1722    */
1723   int send_hello;
1724
1725   /**
1726    * The distance between the two connected peers
1727    */
1728   uint32_t distance;
1729 };
1730
1731
1732 /** Forward declaration **/
1733 static void
1734 reattempt_daemons_connect (void *cls,
1735                            const struct GNUNET_SCHEDULER_TaskContext *tc);
1736
1737
1738 /**
1739  * Notify callback about success or failure of the attempt
1740  * to connect the two peers
1741  *
1742  * @param cls our "struct GNUNET_TESTING_ConnectContext" (freed)
1743  * @param tc reason tells us if we succeeded or failed
1744  */
1745 static void
1746 notify_connect_result (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1747 {
1748   struct GNUNET_TESTING_ConnectContext *ctx = cls;
1749
1750   ctx->timeout_task = GNUNET_SCHEDULER_NO_TASK;
1751   if (ctx->hello_send_task != GNUNET_SCHEDULER_NO_TASK)
1752   {
1753     GNUNET_SCHEDULER_cancel (ctx->hello_send_task);
1754     ctx->hello_send_task = GNUNET_SCHEDULER_NO_TASK;
1755   }
1756
1757   if (ctx->d1th != NULL)
1758     GNUNET_TRANSPORT_disconnect (ctx->d1th);
1759   ctx->d1th = NULL;
1760   if (ctx->d1core != NULL)
1761     GNUNET_CORE_disconnect (ctx->d1core);
1762   ctx->d1core = NULL;
1763
1764   if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
1765   {
1766     GNUNET_free (ctx);
1767     return;
1768   }
1769
1770   if (ctx->connected == GNUNET_YES)
1771   {
1772     if (ctx->cb != NULL)
1773     {
1774       ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, ctx->distance,
1775                ctx->d1->cfg, ctx->d2->cfg, ctx->d1, ctx->d2, NULL);
1776     }
1777   }
1778   else if (ctx->connect_attempts > 0)
1779   {
1780     ctx->d1core_ready = GNUNET_NO;
1781     ctx->timeout_task =
1782         GNUNET_SCHEDULER_add_now (&reattempt_daemons_connect, ctx);
1783     return;
1784   }
1785   else
1786   {
1787     if (ctx->cb != NULL)
1788     {
1789       ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, 0, ctx->d1->cfg,
1790                ctx->d2->cfg, ctx->d1, ctx->d2, _("Peers failed to connect"));
1791     }
1792   }
1793   GNUNET_free (ctx);
1794 }
1795
1796
1797 /**
1798  * Success, connection is up.  Signal client our success.
1799  *
1800  * @param cls our "struct GNUNET_TESTING_ConnectContext"
1801  * @param peer identity of the peer that has connected
1802  * @param atsi performance information
1803  * @param atsi_count number of records in 'atsi'
1804  *
1805  */
1806 static void
1807 connect_notify (void *cls, const struct GNUNET_PeerIdentity *peer,
1808                 const struct GNUNET_ATS_Information *atsi,
1809                 unsigned int atsi_count)
1810 {
1811   struct GNUNET_TESTING_ConnectContext *ctx = cls;
1812
1813   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected peer %s to peer %s\n",
1814               ctx->d1->shortname, GNUNET_i2s (peer));
1815   if (0 != memcmp (&ctx->d2->id, peer, sizeof (struct GNUNET_PeerIdentity)))
1816     return;
1817   ctx->connected = GNUNET_YES;
1818   ctx->distance = 0;            /* FIXME: distance */
1819   if (ctx->hello_send_task != GNUNET_SCHEDULER_NO_TASK)
1820   {
1821     GNUNET_SCHEDULER_cancel (ctx->hello_send_task);
1822     ctx->hello_send_task = GNUNET_SCHEDULER_NO_TASK;
1823   }
1824   GNUNET_SCHEDULER_cancel (ctx->timeout_task);
1825   ctx->timeout_task = GNUNET_SCHEDULER_add_now (&notify_connect_result, ctx);
1826 }
1827
1828
1829 static void
1830 send_hello (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1831 {
1832   struct GNUNET_TESTING_ConnectContext *ctx = cls;
1833   struct GNUNET_MessageHeader *hello;
1834
1835   ctx->hello_send_task = GNUNET_SCHEDULER_NO_TASK;
1836   if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
1837     return;
1838   if ((ctx->d1core_ready == GNUNET_YES) && (ctx->d2->hello != NULL) &&
1839       (NULL != GNUNET_HELLO_get_header (ctx->d2->hello)) &&
1840       (ctx->d1->phase == SP_START_DONE) && (ctx->d2->phase == SP_START_DONE))
1841   {
1842     hello = GNUNET_HELLO_get_header (ctx->d2->hello);
1843     GNUNET_assert (hello != NULL);
1844     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Offering hello of %s to %s\n",
1845                 ctx->d2->shortname, ctx->d1->shortname);
1846     GNUNET_TRANSPORT_offer_hello (ctx->d1th, hello, NULL, NULL);
1847     GNUNET_assert (ctx->d1core != NULL);
1848     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1849                 "Sending connect request to TRANSPORT of %s for peer %s\n",
1850                 GNUNET_i2s (&ctx->d1->id),
1851                 GNUNET_h2s (&ctx->d2->id.hashPubKey));
1852     GNUNET_TRANSPORT_try_connect (ctx->d1th, &ctx->d2->id);
1853     ctx->timeout_hello =
1854         GNUNET_TIME_relative_add (ctx->timeout_hello,
1855                                   GNUNET_TIME_relative_multiply
1856                                   (GNUNET_TIME_UNIT_MILLISECONDS, 500));
1857   }
1858   ctx->hello_send_task =
1859       GNUNET_SCHEDULER_add_delayed (ctx->timeout_hello, &send_hello, ctx);
1860 }
1861
1862 /**
1863  * Notify of a successful connection to the core service.
1864  *
1865  * @param cls a ConnectContext
1866  * @param server handle to the core service
1867  * @param my_identity the peer identity of this peer
1868  */
1869 void
1870 core_init_notify (void *cls, struct GNUNET_CORE_Handle *server,
1871                   const struct GNUNET_PeerIdentity *my_identity)
1872 {
1873   struct GNUNET_TESTING_ConnectContext *connect_ctx = cls;
1874
1875   connect_ctx->d1core_ready = GNUNET_YES;
1876
1877   if (connect_ctx->send_hello == GNUNET_NO)
1878   {
1879     GNUNET_TRANSPORT_try_connect (connect_ctx->d1th, &connect_ctx->d2->id);
1880     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1881                 "Sending connect request to TRANSPORT of %s for peer %s\n",
1882                 connect_ctx->d1->shortname, connect_ctx->d2->shortname);
1883   }
1884 }
1885
1886
1887 /**
1888  * Try to connect again some peers that failed in an earlier attempt. This will
1889  * be tried as many times as connection_attempts in the configuration file.
1890  *
1891  * @param cls Closure (connection context between the two peers).
1892  * @param tc TaskContext.
1893  */
1894 static void
1895 reattempt_daemons_connect (void *cls,
1896                            const struct GNUNET_SCHEDULER_TaskContext *tc)
1897 {
1898   struct GNUNET_TESTING_ConnectContext *ctx = cls;
1899
1900   ctx->timeout_task = GNUNET_SCHEDULER_NO_TASK;
1901   if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
1902     return;
1903   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1904               "re-attempting connect of peer %s to peer %s\n",
1905               ctx->d1->shortname, ctx->d2->shortname);
1906   ctx->connect_attempts--;
1907   GNUNET_assert (ctx->d1core == NULL);
1908   ctx->d1core_ready = GNUNET_NO;
1909   ctx->d1core =
1910       GNUNET_CORE_connect (ctx->d1->cfg, 1, ctx, &core_init_notify,
1911                            &connect_notify, NULL, NULL, GNUNET_NO, NULL,
1912                            GNUNET_NO, no_handlers);
1913   if (ctx->d1core == NULL)
1914   {
1915     if (NULL != ctx->cb)
1916       ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, 0, ctx->d1->cfg,
1917                ctx->d2->cfg, ctx->d1, ctx->d2,
1918                _("Failed to connect to core service of first peer!\n"));
1919     GNUNET_free (ctx);
1920     return;
1921   }
1922
1923   /* Don't know reason for initial connect failure, update the HELLO for the second peer */
1924   if (NULL != ctx->d2->hello)
1925   {
1926     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "updating %s's HELLO\n",
1927                 ctx->d2->shortname);
1928     GNUNET_free (ctx->d2->hello);
1929     ctx->d2->hello = NULL;
1930     if (NULL != ctx->d2->th)
1931     {
1932       GNUNET_TRANSPORT_get_hello_cancel (ctx->d2->ghh);
1933       ctx->d2->ghh = NULL;
1934       GNUNET_TRANSPORT_disconnect (ctx->d2->th);
1935     }
1936     ctx->d2->th =
1937         GNUNET_TRANSPORT_connect (ctx->d2->cfg, &ctx->d2->id, NULL, NULL, NULL,
1938                                   NULL);
1939     GNUNET_assert (ctx->d2->th != NULL);
1940     ctx->d2->ghh =
1941         GNUNET_TRANSPORT_get_hello (ctx->d2->th, &process_hello, ctx->d2);
1942   }
1943   else
1944   {
1945     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "didn't have %s's HELLO\n",
1946                 ctx->d2->shortname);
1947   }
1948
1949   if ((NULL == ctx->d2->hello) && (ctx->d2->th == NULL))
1950   {
1951     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1952                 "didn't have %s's HELLO, trying to get it now\n",
1953                 ctx->d2->shortname);
1954     ctx->d2->th =
1955         GNUNET_TRANSPORT_connect (ctx->d2->cfg, &ctx->d2->id, NULL, NULL, NULL,
1956                                   NULL);
1957     if (NULL == ctx->d2->th)
1958     {
1959       GNUNET_CORE_disconnect (ctx->d1core);
1960       GNUNET_free (ctx);
1961       if (NULL != ctx->cb)
1962         ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, 0, ctx->d1->cfg,
1963                  ctx->d2->cfg, ctx->d1, ctx->d2,
1964                  _("Failed to connect to transport service!\n"));
1965       return;
1966     }
1967     ctx->d2->ghh =
1968         GNUNET_TRANSPORT_get_hello (ctx->d2->th, &process_hello, ctx->d2);
1969   }
1970   else
1971   {
1972     if (NULL == ctx->d2->hello)
1973     {
1974       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1975                   "didn't have %s's HELLO but th wasn't NULL, not trying!!\n",
1976                   ctx->d2->shortname);
1977     }
1978   }
1979
1980   if (ctx->send_hello == GNUNET_YES)
1981   {
1982     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending %s's HELLO to %s\n",
1983                 ctx->d1->shortname, ctx->d2->shortname);
1984     ctx->d1th =
1985         GNUNET_TRANSPORT_connect (ctx->d1->cfg, &ctx->d1->id, ctx->d1, NULL,
1986                                   NULL, NULL);
1987     if (ctx->d1th == NULL)
1988     {
1989       GNUNET_CORE_disconnect (ctx->d1core);
1990       GNUNET_free (ctx);
1991       if (NULL != ctx->cb)
1992         ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, 0, ctx->d1->cfg,
1993                  ctx->d2->cfg, ctx->d1, ctx->d2,
1994                  _("Failed to connect to transport service!\n"));
1995       return;
1996     }
1997     GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == ctx->hello_send_task);
1998     ctx->hello_send_task = GNUNET_SCHEDULER_add_now (&send_hello, ctx);
1999   }
2000   else
2001   {
2002     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to reconnect %s to %s\n",
2003                 ctx->d1->shortname, ctx->d2->shortname);
2004     GNUNET_TRANSPORT_try_connect (ctx->d1th, &ctx->d2->id);
2005   }
2006   ctx->timeout_task =
2007       GNUNET_SCHEDULER_add_delayed (ctx->relative_timeout,
2008                                     &notify_connect_result, ctx);
2009 }
2010
2011 /**
2012  * Iterator for currently known peers, to ensure
2013  * that we don't try to send duplicate connect
2014  * requests to core.
2015  *
2016  * @param cls our "struct GNUNET_TESTING_ConnectContext"
2017  * @param peer identity of the peer that has connected,
2018  *        NULL when iteration has finished
2019  * @param atsi performance information
2020  * @param atsi_count number of records in 'atsi'
2021  *
2022  */
2023 static void
2024 core_initial_iteration (void *cls, const struct GNUNET_PeerIdentity *peer,
2025                         const struct GNUNET_ATS_Information *atsi,
2026                         unsigned int atsi_count)
2027 {
2028   struct GNUNET_TESTING_ConnectContext *ctx = cls;
2029
2030   if ((peer != NULL) &&
2031       (0 == memcmp (&ctx->d2->id, peer, sizeof (struct GNUNET_PeerIdentity))))
2032   {
2033     ctx->connected = GNUNET_YES;
2034     ctx->distance = 0;          /* FIXME: distance */
2035     return;
2036   }
2037   if (peer != NULL)
2038     return;                     /* ignore other peers */
2039   /* peer == NULL: End of iteration over peers */
2040
2041   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == ctx->timeout_task);
2042   if (ctx->connected == GNUNET_YES)
2043   {
2044     ctx->timeout_task = GNUNET_SCHEDULER_add_now (&notify_connect_result, ctx);
2045     return;
2046   }
2047
2048   /* Peer not already connected, need to schedule connect request! */
2049   if (ctx->d1core == NULL)
2050   {
2051     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2052                 "Peers are NOT connected, connecting to core!\n");
2053     ctx->d1core =
2054         GNUNET_CORE_connect (ctx->d1->cfg, 1, ctx, &core_init_notify,
2055                              &connect_notify, NULL, NULL, GNUNET_NO, NULL,
2056                              GNUNET_NO, no_handlers);
2057   }
2058
2059   if (ctx->d1core == NULL)
2060   {
2061     ctx->timeout_task = GNUNET_SCHEDULER_add_now (&notify_connect_result, ctx);
2062     return;
2063   }
2064
2065   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 */
2066   {
2067     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2068                 "Don't have d2's HELLO, trying to get it!\n");
2069     ctx->d2->th =
2070         GNUNET_TRANSPORT_connect (ctx->d2->cfg, &ctx->d2->id, NULL, NULL, NULL,
2071                                   NULL);
2072     if (ctx->d2->th == NULL)
2073     {
2074       GNUNET_CORE_disconnect (ctx->d1core);
2075       ctx->d1core = NULL;
2076       ctx->timeout_task =
2077           GNUNET_SCHEDULER_add_now (&notify_connect_result, ctx);
2078       return;
2079     }
2080     ctx->d2->ghh =
2081         GNUNET_TRANSPORT_get_hello (ctx->d2->th, &process_hello, ctx->d2);
2082   }
2083
2084   if (ctx->send_hello == GNUNET_YES)
2085   {
2086     ctx->d1th =
2087         GNUNET_TRANSPORT_connect (ctx->d1->cfg, &ctx->d1->id, ctx->d1, NULL,
2088                                   NULL, NULL);
2089     if (ctx->d1th == NULL)
2090     {
2091       GNUNET_CORE_disconnect (ctx->d1core);
2092       ctx->d1core = NULL;
2093       ctx->timeout_task =
2094           GNUNET_SCHEDULER_add_now (&notify_connect_result, ctx);
2095       return;
2096     }
2097     GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == ctx->hello_send_task);
2098     ctx->hello_send_task = GNUNET_SCHEDULER_add_now (&send_hello, ctx);
2099   }
2100
2101   ctx->timeout_task =
2102       GNUNET_SCHEDULER_add_delayed (ctx->relative_timeout,
2103                                     &notify_connect_result, ctx);
2104
2105 }
2106
2107
2108 /**
2109  * Establish a connection between two GNUnet daemons.  The daemons
2110  * must both be running and not be stopped until either the
2111  * 'cb' callback is called OR the connection request has been
2112  * explicitly cancelled.
2113  *
2114  * @param d1 handle for the first daemon
2115  * @param d2 handle for the second daemon
2116  * @param timeout how long is the connection attempt
2117  *        allowed to take?
2118  * @param max_connect_attempts how many times should we try to reconnect
2119  *        (within timeout)
2120  * @param send_hello GNUNET_YES to send the HELLO, GNUNET_NO to assume
2121  *                   the HELLO has already been exchanged
2122  * @param cb function to call at the end
2123  * @param cb_cls closure for cb
2124  * @return handle to cancel the request
2125  */
2126 struct GNUNET_TESTING_ConnectContext *
2127 GNUNET_TESTING_daemons_connect (struct GNUNET_TESTING_Daemon *d1,
2128                                 struct GNUNET_TESTING_Daemon *d2,
2129                                 struct GNUNET_TIME_Relative timeout,
2130                                 unsigned int max_connect_attempts,
2131                                 int send_hello,
2132                                 GNUNET_TESTING_NotifyConnection cb,
2133                                 void *cb_cls)
2134 {
2135   struct GNUNET_TESTING_ConnectContext *ctx;
2136
2137   if ((d1->running == GNUNET_NO) || (d2->running == GNUNET_NO))
2138   {
2139     if (NULL != cb)
2140       cb (cb_cls, &d1->id, &d2->id, 0, d1->cfg, d2->cfg, d1, d2,
2141           _("Peers are not fully running yet, can not connect!\n"));
2142     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Peers are not up!\n");
2143     return NULL;
2144   }
2145
2146   ctx = GNUNET_malloc (sizeof (struct GNUNET_TESTING_ConnectContext));
2147   ctx->d1 = d1;
2148   ctx->d2 = d2;
2149   ctx->timeout_hello =
2150       GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 500);
2151   ctx->relative_timeout =
2152       GNUNET_TIME_relative_divide (timeout, max_connect_attempts);
2153   ctx->cb = cb;
2154   ctx->cb_cls = cb_cls;
2155   ctx->connect_attempts = max_connect_attempts;
2156   ctx->connected = GNUNET_NO;
2157   ctx->send_hello = send_hello;
2158   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Asked to connect peer %s to peer %s\n",
2159               d1->shortname, d2->shortname);
2160   /* Core is up! Iterate over all _known_ peers first to check if we are already connected to the peer! */
2161   GNUNET_assert (NULL !=
2162                  GNUNET_CORE_is_peer_connected (ctx->d1->cfg, &ctx->d2->id,
2163                                                 &core_initial_iteration, ctx));
2164   return ctx;
2165 }
2166
2167
2168 /**
2169  * Cancel an attempt to connect two daemons.
2170  *
2171  * @param cc connect context
2172  */
2173 void
2174 GNUNET_TESTING_daemons_connect_cancel (struct GNUNET_TESTING_ConnectContext *cc)
2175 {
2176   if (GNUNET_SCHEDULER_NO_TASK != cc->timeout_task)
2177   {
2178     GNUNET_SCHEDULER_cancel (cc->timeout_task);
2179     cc->timeout_task = GNUNET_SCHEDULER_NO_TASK;
2180   }
2181   if (GNUNET_SCHEDULER_NO_TASK != cc->hello_send_task)
2182   {
2183     GNUNET_SCHEDULER_cancel (cc->hello_send_task);
2184     cc->hello_send_task = GNUNET_SCHEDULER_NO_TASK;
2185   }
2186   if (NULL != cc->d1core)
2187   {
2188     GNUNET_CORE_disconnect (cc->d1core);
2189     cc->d1core = NULL;
2190   }
2191   if (NULL != cc->d1th)
2192   {
2193     GNUNET_TRANSPORT_disconnect (cc->d1th);
2194     cc->d1th = NULL;
2195   }
2196   GNUNET_free (cc);
2197 }
2198
2199
2200 /* end of testing.c */