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