reduce config variables
[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 #define DEBUG_TESTING GNUNET_NO
41 #define DEBUG_TESTING_RECONNECT GNUNET_NO
42
43 /**
44  * How long do we wait after starting gnunet-service-arm
45  * for the core service to be alive?
46  */
47 #define ARM_START_WAIT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120)
48
49 /**
50  * How many times are we willing to try to wait for "scp" or
51  * "gnunet-service-arm" to complete (waitpid) before giving up?
52  */
53 #define MAX_EXEC_WAIT_RUNS 250
54
55 static struct GNUNET_CORE_MessageHandler no_handlers[] = { {NULL, 0, 0} };
56
57 /**
58  * Receive the HELLO from one peer, give it to the other
59  * and ask them to connect.
60  *
61  * @param cls "struct ConnectContext"
62  * @param message HELLO message of peer
63  */
64 static void
65 process_hello (void *cls, const struct GNUNET_MessageHeader *message)
66 {
67   struct GNUNET_TESTING_Daemon *daemon = cls;
68   int msize;
69   if (daemon == NULL)
70     return;
71
72   if (daemon->server != NULL)
73     {
74 #if DEBUG_TESTING
75       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
76                   "Received `%s' from transport service of `%4s', disconnecting core!\n",
77                   "HELLO", GNUNET_i2s (&daemon->id));
78 #endif
79       GNUNET_CORE_disconnect (daemon->server);
80       daemon->server = NULL;
81     }
82
83   GNUNET_assert (message != NULL);
84   msize = ntohs (message->size);
85   if (msize < 1)
86     {
87       return;
88     }
89   if (daemon->th != NULL)
90     {
91       GNUNET_TRANSPORT_get_hello_cancel (daemon->th, &process_hello, daemon);
92     }
93 #if DEBUG_TESTING
94   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
95               "Received `%s' from transport service of `%4s'\n",
96               "HELLO", GNUNET_i2s (&daemon->id));
97 #endif
98
99   GNUNET_free_non_null (daemon->hello);
100   daemon->hello = GNUNET_malloc (msize);
101   memcpy (daemon->hello, message, msize);
102
103   if (daemon->th != NULL)
104     {
105       GNUNET_TRANSPORT_disconnect (daemon->th);
106       daemon->th = NULL;
107     }
108
109 }
110
111 /**
112  * Function called after GNUNET_CORE_connect has succeeded
113  * (or failed for good).  Note that the private key of the
114  * peer is intentionally not exposed here; if you need it,
115  * your process should try to read the private key file
116  * directly (which should work if you are authorized...).
117  *
118  * @param cls closure
119  * @param server handle to the server, NULL if we failed
120  * @param my_identity ID of this peer, NULL if we failed
121  * @param publicKey public key of this peer, NULL if we failed
122  */
123 static void
124 testing_init (void *cls,
125               struct GNUNET_CORE_Handle *server,
126               const struct GNUNET_PeerIdentity *my_identity,
127               const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
128 {
129   struct GNUNET_TESTING_Daemon *d = cls;
130   GNUNET_TESTING_NotifyDaemonRunning cb;
131
132   GNUNET_assert (d->phase == SP_START_CORE);
133   d->phase = SP_START_DONE;
134   cb = d->cb;
135   d->cb = NULL;
136   if (server == NULL)
137     {
138       d->server = NULL;
139       if (GNUNET_YES == d->dead)
140         GNUNET_TESTING_daemon_stop (d,
141                                     GNUNET_TIME_absolute_get_remaining
142                                     (d->max_timeout), d->dead_cb,
143                                     d->dead_cb_cls, GNUNET_YES, GNUNET_NO);
144       else if (NULL != cb)
145         cb (d->cb_cls, NULL, d->cfg, d,
146             _("Failed to connect to core service\n"));
147       return;
148     }
149 #if DEBUG_TESTING
150   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
151               "Successfully started peer `%4s'.\n", GNUNET_i2s (my_identity));
152 #endif
153   d->id = *my_identity;
154   d->shortname = strdup (GNUNET_i2s (my_identity));
155   d->server = server;
156   d->running = GNUNET_YES;
157
158   if (NULL != cb)               /* FIXME: what happens when this callback calls GNUNET_TESTING_daemon_stop? */
159     cb (d->cb_cls, my_identity, d->cfg, d, NULL);
160
161   if (GNUNET_NO == d->running)
162     {
163 #if DEBUG_TESTING
164       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
165                   "Peer is dead (d->running == GNUNET_NO)\n");
166 #endif
167       return;
168     }
169 #if DEBUG_TESTING
170   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
171               "Successfully started peer `%4s', connecting to transport service.\n",
172               GNUNET_i2s (my_identity));
173 #endif
174
175   d->th = GNUNET_TRANSPORT_connect (d->cfg, &d->id, d, NULL, NULL, NULL);
176   if (d->th == NULL)
177     {
178       if (GNUNET_YES == d->dead)
179         GNUNET_TESTING_daemon_stop (d,
180                                     GNUNET_TIME_absolute_get_remaining
181                                     (d->max_timeout), d->dead_cb,
182                                     d->dead_cb_cls, GNUNET_YES, GNUNET_NO);
183       else if (NULL != d->cb)
184         d->cb (d->cb_cls, &d->id, d->cfg, d,
185                _("Failed to connect to transport service!\n"));
186       return;
187     }
188 #if DEBUG_TESTING
189   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
190               "Connected to transport service `%s', getting HELLO\n",
191               GNUNET_i2s (my_identity));
192 #endif
193
194   GNUNET_TRANSPORT_get_hello (d->th, &process_hello, d);
195 }
196
197
198 /**
199  * Finite-state machine for starting GNUnet.
200  *
201  * @param cls our "struct GNUNET_TESTING_Daemon"
202  * @param tc unused
203  */
204 static void
205 start_fsm (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
206 {
207   struct GNUNET_TESTING_Daemon *d = cls;
208   GNUNET_TESTING_NotifyDaemonRunning cb;
209   enum GNUNET_OS_ProcessStatusType type;
210   unsigned long code;
211   char *dst;
212   int bytes_read;
213
214 #if DEBUG_TESTING
215   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
216               "Peer FSM is in phase %u.\n", d->phase);
217 #endif
218
219   d->task = GNUNET_SCHEDULER_NO_TASK;
220   switch (d->phase)
221     {
222     case SP_COPYING:
223       /* confirm copying complete */
224       if (GNUNET_OK != GNUNET_OS_process_status (d->proc, &type, &code))
225         {
226           if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value ==
227               0)
228             {
229               cb = d->cb;
230               d->cb = NULL;
231               if (NULL != cb)
232                 cb (d->cb_cls,
233                     NULL,
234                     d->cfg, d,
235                     _
236                     ("`scp' does not seem to terminate (timeout copying config).\n"));
237               return;
238             }
239           /* wait some more */
240           d->task
241             = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT,
242                                             &start_fsm, d);
243           return;
244         }
245       if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0))
246         {
247           cb = d->cb;
248           d->cb = NULL;
249           if (NULL != cb)
250             cb (d->cb_cls,
251                 NULL, d->cfg, d, _("`scp' did not complete cleanly.\n"));
252           return;
253         }
254 #if DEBUG_TESTING
255       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
256                   "Successfully copied configuration file.\n");
257 #endif
258       d->phase = SP_COPIED;
259       /* fall-through */
260     case SP_COPIED:
261       /* Start create hostkey process */
262       d->pipe_stdout = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_YES);
263       if (d->pipe_stdout == NULL)
264         {
265           cb = d->cb;
266           d->cb = NULL;
267           if (NULL != cb)
268             cb (d->cb_cls,
269                 NULL,
270                 d->cfg,
271                 d,
272                 (NULL == d->hostname)
273                 ? _("Failed to create pipe for `gnunet-peerinfo' process.\n")
274                 : _("Failed to create pipe for `ssh' process.\n"));
275           return;
276         }
277       if (NULL == d->hostname)
278         {
279 #if DEBUG_TESTING
280           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
281                       "Starting `%s', with command `%s %s %s %s'.\n",
282                       "gnunet-peerinfo", "gnunet-peerinfo", "-c", d->cfgfile,
283                       "-sq");
284 #endif
285           d->proc =
286             GNUNET_OS_start_process (NULL, d->pipe_stdout, "gnunet-peerinfo",
287                                      "gnunet-peerinfo", "-c", d->cfgfile,
288                                      "-sq", NULL);
289           GNUNET_DISK_pipe_close_end (d->pipe_stdout,
290                                       GNUNET_DISK_PIPE_END_WRITE);
291         }
292       else
293         {
294           if (d->username != NULL)
295             GNUNET_asprintf (&dst, "%s@%s", d->username, d->hostname);
296           else
297             dst = GNUNET_strdup (d->hostname);
298
299 #if DEBUG_TESTING
300           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
301                       "Starting `%s', with command `%s %s %s %s %s %s'.\n",
302                       "gnunet-peerinfo", "ssh", dst, "gnunet-peerinfo", "-c",
303                       d->cfgfile, "-sq");
304 #endif
305           if (d->ssh_port_str == NULL)
306             {
307               d->proc = GNUNET_OS_start_process (NULL, d->pipe_stdout, "ssh",
308                                                  "ssh",
309 #if !DEBUG_TESTING
310                                                  "-q",
311 #endif
312                                                  dst,
313                                                  "gnunet-peerinfo",
314                                                  "-c", d->cfgfile, "-sq",
315                                                  NULL);
316             }
317           else
318             {
319               d->proc = GNUNET_OS_start_process (NULL, d->pipe_stdout, "ssh",
320                                                  "ssh", "-p", d->ssh_port_str,
321 #if !DEBUG_TESTING
322                                                  "-q",
323 #endif
324                                                  dst,
325                                                  "gnunet-peerinfo",
326                                                  "-c", d->cfgfile, "-sq",
327                                                  NULL);
328             }
329           GNUNET_DISK_pipe_close_end (d->pipe_stdout,
330                                       GNUNET_DISK_PIPE_END_WRITE);
331           GNUNET_free (dst);
332         }
333       if (NULL == d->proc)
334         {
335           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
336                       _("Could not start `%s' process to create hostkey.\n"),
337                       (NULL == d->hostname) ? "gnunet-peerinfo" : "ssh");
338           cb = d->cb;
339           d->cb = NULL;
340           if (NULL != cb)
341             cb (d->cb_cls,
342                 NULL,
343                 d->cfg,
344                 d,
345                 (NULL == d->hostname)
346                 ? _("Failed to start `gnunet-peerinfo' process.\n")
347                 : _("Failed to start `ssh' process.\n"));
348           GNUNET_DISK_pipe_close (d->pipe_stdout);
349           return;
350         }
351 #if DEBUG_TESTING
352       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
353                   "Started `%s', waiting for hostkey.\n", "gnunet-peerinfo");
354 #endif
355       d->phase = SP_HOSTKEY_CREATE;
356       d->task
357         =
358         GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
359                                         (d->max_timeout),
360                                         GNUNET_DISK_pipe_handle
361                                         (d->pipe_stdout,
362                                          GNUNET_DISK_PIPE_END_READ),
363                                         &start_fsm, d);
364       break;
365     case SP_HOSTKEY_CREATE:
366       bytes_read =
367         GNUNET_DISK_file_read (GNUNET_DISK_pipe_handle
368                                (d->pipe_stdout, GNUNET_DISK_PIPE_END_READ),
369                                &d->hostkeybuf[d->hostkeybufpos],
370                                sizeof (d->hostkeybuf) - d->hostkeybufpos);
371       if (bytes_read > 0)
372         d->hostkeybufpos += bytes_read;
373
374       if ((d->hostkeybufpos < 104) && (bytes_read > 0))
375         {
376           /* keep reading */
377           d->task
378             =
379             GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
380                                             (d->max_timeout),
381                                             GNUNET_DISK_pipe_handle
382                                             (d->pipe_stdout,
383                                              GNUNET_DISK_PIPE_END_READ),
384                                             &start_fsm, d);
385           return;
386         }
387       d->hostkeybuf[103] = '\0';
388       if ((bytes_read < 0) ||
389           (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (d->hostkeybuf,
390                                                         &d->id.hashPubKey)))
391         {
392           /* error */
393           if (bytes_read < 0)
394             GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
395                         _("Error reading from gnunet-peerinfo: %s\n"),
396                         STRERROR (errno));
397           else
398             GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
399                         _("Malformed output from gnunet-peerinfo!\n"));
400           cb = d->cb;
401           d->cb = NULL;
402           GNUNET_DISK_pipe_close (d->pipe_stdout);
403           d->pipe_stdout = NULL;
404           (void) GNUNET_OS_process_kill (d->proc, SIGKILL);
405           GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (d->proc));
406           GNUNET_OS_process_close (d->proc);
407           d->proc = NULL;
408           if (NULL != cb)
409             cb (d->cb_cls, NULL, d->cfg, d, _("`Failed to get hostkey!\n"));
410           return;
411         }
412       GNUNET_DISK_pipe_close (d->pipe_stdout);
413       d->pipe_stdout = NULL;
414       (void) GNUNET_OS_process_kill (d->proc, SIGKILL);
415       GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (d->proc));
416       GNUNET_OS_process_close (d->proc);
417       d->proc = NULL;
418 #if DEBUG_TESTING
419       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Successfully got hostkey!\n");
420 #endif
421       if (d->hostkey_callback != NULL)
422         {
423           d->hostkey_callback (d->hostkey_cls, &d->id, d, NULL);
424           d->phase = SP_HOSTKEY_CREATED;
425         }
426       else
427         {
428           d->phase = SP_TOPOLOGY_SETUP;
429         }
430       /* Fall through */
431     case SP_HOSTKEY_CREATED:
432       /* wait for topology finished */
433       if ((GNUNET_YES == d->dead)
434           || (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value ==
435               0))
436         {
437           cb = d->cb;
438           d->cb = NULL;
439           if (NULL != cb)
440             cb (d->cb_cls,
441                 NULL,
442                 d->cfg, d, _("`Failed while waiting for topology setup!\n"));
443           return;
444         }
445
446       d->task
447         = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT,
448                                         &start_fsm, d);
449       break;
450     case SP_TOPOLOGY_SETUP:
451       /* start GNUnet on remote host */
452       if (NULL == d->hostname)
453         {
454 #if DEBUG_TESTING
455           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
456                       "Starting `%s', with command `%s %s %s %s %s %s'.\n",
457                       "gnunet-arm", "gnunet-arm", "-c", d->cfgfile,
458                       "-L", "DEBUG", "-s");
459 #endif
460           d->proc = GNUNET_OS_start_process (NULL, NULL, "gnunet-arm",
461                                              "gnunet-arm", "-c", d->cfgfile,
462 #if DEBUG_TESTING
463                                              "-L", "DEBUG",
464 #endif
465                                              "-s", "-q", NULL);
466         }
467       else
468         {
469           if (d->username != NULL)
470             GNUNET_asprintf (&dst, "%s@%s", d->username, d->hostname);
471           else
472             dst = GNUNET_strdup (d->hostname);
473
474 #if DEBUG_TESTING
475           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
476                       "Starting `%s', with command `%s %s %s %s %s %s %s %s'.\n",
477                       "gnunet-arm", "ssh", dst, "gnunet-arm", "-c",
478                       d->cfgfile, "-L", "DEBUG", "-s", "-q");
479 #endif
480           if (d->ssh_port_str == NULL)
481             {
482               d->proc = GNUNET_OS_start_process (NULL, NULL, "ssh", "ssh",
483 #if !DEBUG_TESTING
484                                                  "-q",
485 #endif
486                                                  dst, "gnunet-arm",
487 #if DEBUG_TESTING
488                                                  "-L", "DEBUG",
489 #endif
490                                                  "-c", d->cfgfile, "-s", "-q",
491                                                  NULL);
492             }
493           else
494             {
495
496               d->proc = GNUNET_OS_start_process (NULL, NULL, "ssh",
497                                                  "ssh", "-p", d->ssh_port_str,
498 #if !DEBUG_TESTING
499                                                  "-q",
500 #endif
501                                                  dst, "gnunet-arm",
502 #if DEBUG_TESTING
503                                                  "-L", "DEBUG",
504 #endif
505                                                  "-c", d->cfgfile, "-s", "-q",
506                                                  NULL);
507             }
508           GNUNET_free (dst);
509         }
510       if (NULL == d->proc)
511         {
512           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
513                       _("Could not start `%s' process to start GNUnet.\n"),
514                       (NULL == d->hostname) ? "gnunet-arm" : "ssh");
515           cb = d->cb;
516           d->cb = NULL;
517           if (NULL != cb)
518             cb (d->cb_cls,
519                 NULL,
520                 d->cfg,
521                 d,
522                 (NULL == d->hostname)
523                 ? _("Failed to start `gnunet-arm' process.\n")
524                 : _("Failed to start `ssh' process.\n"));
525           return;
526         }
527 #if DEBUG_TESTING
528       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
529                   "Started `%s', waiting for `%s' to be up.\n",
530                   "gnunet-arm", "gnunet-service-core");
531 #endif
532       d->phase = SP_START_ARMING;
533       d->task
534         = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT,
535                                         &start_fsm, d);
536       break;
537     case SP_START_ARMING:
538       if (GNUNET_OK != GNUNET_OS_process_status (d->proc, &type, &code))
539         {
540           if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value ==
541               0)
542             {
543               cb = d->cb;
544               d->cb = NULL;
545               if (NULL != cb)
546                 cb (d->cb_cls,
547                     NULL,
548                     d->cfg,
549                     d,
550                     (NULL == d->hostname)
551                     ? _("`gnunet-arm' does not seem to terminate.\n")
552                     : _("`ssh' does not seem to terminate.\n"));
553               return;
554             }
555           /* wait some more */
556           d->task
557             = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT,
558                                             &start_fsm, d);
559           return;
560         }
561 #if DEBUG_TESTING
562       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
563                   "Successfully started `%s'.\n", "gnunet-arm");
564 #endif
565       d->phase = SP_START_CORE;
566       d->server = GNUNET_CORE_connect (d->cfg, 1,
567 #if NO_MORE_TIMEOUT_FIXME
568                                        ARM_START_WAIT,
569 #endif
570                                        d,
571                                        &testing_init,
572                                        NULL, NULL, NULL,
573                                        NULL, GNUNET_NO,
574                                        NULL, GNUNET_NO, no_handlers);
575       break;
576     case SP_START_CORE:
577       GNUNET_break (0);
578       break;
579     case SP_START_DONE:
580       GNUNET_break (0);
581       break;
582     case SP_SHUTDOWN_START:
583       /* confirm copying complete */
584       if (GNUNET_OK != GNUNET_OS_process_status (d->proc, &type, &code))
585         {
586           if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value ==
587               0)
588             {
589               if (NULL != d->dead_cb)
590                 d->dead_cb (d->dead_cb_cls,
591                             _
592                             ("either `gnunet-arm' or `ssh' does not seem to terminate.\n"));
593               if (d->th != NULL)
594                 {
595                   GNUNET_TRANSPORT_get_hello_cancel (d->th, &process_hello,
596                                                      d);
597                   GNUNET_TRANSPORT_disconnect (d->th);
598                   d->th = NULL;
599                 }
600               GNUNET_CONFIGURATION_destroy (d->cfg);
601               GNUNET_free (d->cfgfile);
602               GNUNET_free_non_null (d->hello);
603               GNUNET_free_non_null (d->hostname);
604               GNUNET_free_non_null (d->username);
605               GNUNET_free_non_null (d->shortname);
606               GNUNET_free (d);
607               return;
608             }
609           /* wait some more */
610           d->task
611             = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT,
612                                             &start_fsm, d);
613           return;
614         }
615       if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0))
616         {
617           if (NULL != d->dead_cb)
618             d->dead_cb (d->dead_cb_cls,
619                         _
620                         ("shutdown (either `gnunet-arm' or `ssh') did not complete cleanly.\n"));
621           if (d->th != NULL)
622             {
623               GNUNET_TRANSPORT_get_hello_cancel (d->th, &process_hello, d);
624               GNUNET_TRANSPORT_disconnect (d->th);
625               d->th = NULL;
626             }
627           if (d->server != NULL)
628             {
629               GNUNET_CORE_disconnect (d->server);
630               d->server = NULL;
631             }
632           GNUNET_CONFIGURATION_destroy (d->cfg);
633           GNUNET_free (d->cfgfile);
634           GNUNET_free_non_null (d->hello);
635           GNUNET_free_non_null (d->hostname);
636           GNUNET_free_non_null (d->username);
637           GNUNET_free_non_null (d->shortname);
638           GNUNET_free (d);
639           return;
640         }
641 #if DEBUG_TESTING
642       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer shutdown complete.\n");
643 #endif
644       if (d->server != NULL)
645         {
646           GNUNET_CORE_disconnect (d->server);
647           d->server = NULL;
648         }
649
650       if (d->th != NULL)
651         {
652           GNUNET_TRANSPORT_get_hello_cancel (d->th, &process_hello, d);
653           GNUNET_TRANSPORT_disconnect (d->th);
654           d->th = NULL;
655         }
656       /* state clean up and notifications */
657       if (d->churn == GNUNET_NO)
658         {
659           GNUNET_CONFIGURATION_destroy (d->cfg);
660           GNUNET_free (d->cfgfile);
661           GNUNET_free_non_null (d->hostname);
662           GNUNET_free_non_null (d->username);
663         }
664
665       GNUNET_free_non_null (d->hello);
666       d->hello = NULL;
667       GNUNET_free_non_null (d->shortname);
668       d->shortname = NULL;
669       if (NULL != d->dead_cb)
670         d->dead_cb (d->dead_cb_cls, NULL);
671
672       if (d->churn == GNUNET_NO)
673         GNUNET_free (d);
674
675       break;
676     case SP_CONFIG_UPDATE:
677       /* confirm copying complete */
678       if (GNUNET_OK != GNUNET_OS_process_status (d->proc, &type, &code))
679         {
680           if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0)       /* FIXME: config update should take timeout parameter! */
681             {
682               cb = d->cb;
683               d->cb = NULL;
684               if (NULL != cb)
685                 cb (d->cb_cls,
686                     NULL,
687                     d->cfg, d, _("`scp' does not seem to terminate.\n"));
688               return;
689             }
690           /* wait some more */
691           d->task
692             = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT,
693                                             &start_fsm, d);
694           return;
695         }
696       if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0))
697         {
698           if (NULL != d->update_cb)
699             d->update_cb (d->update_cb_cls,
700                           _("`scp' did not complete cleanly.\n"));
701           return;
702         }
703 #if DEBUG_TESTING
704       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
705                   "Successfully copied configuration file.\n");
706 #endif
707       if (NULL != d->update_cb)
708         d->update_cb (d->update_cb_cls, NULL);
709       d->phase = SP_START_DONE;
710       break;
711     }
712 }
713
714 /**
715  * Continues GNUnet daemon startup when user wanted to be notified
716  * once a hostkey was generated (for creating friends files, blacklists,
717  * etc.).
718  *
719  * @param daemon the daemon to finish starting
720  */
721 void
722 GNUNET_TESTING_daemon_continue_startup (struct GNUNET_TESTING_Daemon *daemon)
723 {
724   GNUNET_assert (daemon->phase == SP_HOSTKEY_CREATED);
725   daemon->phase = SP_TOPOLOGY_SETUP;
726 }
727
728 /**
729  * Check whether the given daemon is running.
730  *
731  * @param daemon the daemon to check
732  *
733  * @return GNUNET_YES if the daemon is up, GNUNET_NO if the
734  *         daemon is down, GNUNET_SYSERR on error.
735  */
736 int
737 GNUNET_TESTING_daemon_running (struct GNUNET_TESTING_Daemon *daemon)
738 {
739   if (daemon == NULL)
740     return GNUNET_SYSERR;
741
742   if (daemon->running == GNUNET_YES)
743     return GNUNET_YES;
744   return GNUNET_NO;
745 }
746
747
748 /**
749  * Start a peer that has previously been stopped using the daemon_stop
750  * call (and files weren't deleted and the allow restart flag)
751  *
752  * @param daemon the daemon to start (has been previously stopped)
753  * @param timeout how long to wait for restart
754  * @param cb the callback for notification when the peer is running
755  * @param cb_cls closure for the callback
756  */
757 void
758 GNUNET_TESTING_daemon_start_stopped (struct GNUNET_TESTING_Daemon *daemon,
759                                      struct GNUNET_TIME_Relative timeout,
760                                      GNUNET_TESTING_NotifyDaemonRunning cb,
761                                      void *cb_cls)
762 {
763   if (daemon->running == GNUNET_YES)
764     {
765       cb (cb_cls, &daemon->id, daemon->cfg, daemon,
766           "Daemon already running, can't restart!");
767       return;
768     }
769
770   daemon->cb = cb;
771   daemon->cb_cls = cb_cls;
772   daemon->phase = SP_TOPOLOGY_SETUP;
773   daemon->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
774
775   GNUNET_SCHEDULER_add_continuation (&start_fsm,
776                                      daemon,
777                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
778 }
779
780 /**
781  * Starts a GNUnet daemon.  GNUnet must be installed on the target
782  * system and available in the PATH.  The machine must furthermore be
783  * reachable via "ssh" (unless the hostname is "NULL") without the
784  * need to enter a password.
785  *
786  * @param cfg configuration to use
787  * @param timeout how long to wait starting up peers
788  * @param hostname name of the machine where to run GNUnet
789  *        (use NULL for localhost).
790  * @param ssh_username ssh username to use when connecting to hostname
791  * @param sshport port to pass to ssh process when connecting to hostname
792  * @param hostkey_callback function to call once the hostkey has been
793  *        generated for this peer, but it hasn't yet been started
794  *        (NULL to start immediately, otherwise waits on GNUNET_TESTING_daemon_continue_start)
795  * @param hostkey_cls closure for hostkey callback
796  * @param cb function to call once peer is up, or failed to start
797  * @param cb_cls closure for cb
798  * @return handle to the daemon (actual start will be completed asynchronously)
799  */
800 struct GNUNET_TESTING_Daemon *
801 GNUNET_TESTING_daemon_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
802                              struct GNUNET_TIME_Relative timeout,
803                              const char *hostname,
804                              const char *ssh_username,
805                              uint16_t sshport,
806                              GNUNET_TESTING_NotifyHostkeyCreated
807                              hostkey_callback, void *hostkey_cls,
808                              GNUNET_TESTING_NotifyDaemonRunning cb,
809                              void *cb_cls)
810 {
811   struct GNUNET_TESTING_Daemon *ret;
812   char *arg;
813   char *username;
814
815   ret = GNUNET_malloc (sizeof (struct GNUNET_TESTING_Daemon));
816   ret->hostname = (hostname == NULL) ? NULL : GNUNET_strdup (hostname);
817   if (sshport != 0)
818     {
819       GNUNET_asprintf (&ret->ssh_port_str, "%d", sshport);
820     }
821   else
822     ret->ssh_port_str = NULL;
823   ret->cfgfile = GNUNET_DISK_mktemp ("gnunet-testing-config");
824 #if DEBUG_TESTING
825   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
826               "Setting up peer with configuration file `%s'.\n",
827               ret->cfgfile);
828 #endif
829   if (NULL == ret->cfgfile)
830     {
831       GNUNET_free_non_null (ret->ssh_port_str);
832       GNUNET_free_non_null (ret->hostname);
833       GNUNET_free (ret);
834       return NULL;
835     }
836   ret->hostkey_callback = hostkey_callback;
837   ret->hostkey_cls = hostkey_cls;
838   ret->cb = cb;
839   ret->cb_cls = cb_cls;
840   ret->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
841   ret->cfg = GNUNET_CONFIGURATION_dup (cfg);
842   GNUNET_CONFIGURATION_set_value_string (ret->cfg,
843                                          "PATHS",
844                                          "DEFAULTCONFIG", ret->cfgfile);
845   /* 1) write configuration to temporary file */
846   if (GNUNET_OK != GNUNET_CONFIGURATION_write (ret->cfg, ret->cfgfile))
847     {
848       if (0 != UNLINK (ret->cfgfile))
849         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
850                                   "unlink", ret->cfgfile);
851       GNUNET_CONFIGURATION_destroy (ret->cfg);
852       GNUNET_free_non_null (ret->hostname);
853       GNUNET_free (ret->cfgfile);
854       GNUNET_free (ret);
855       return NULL;
856     }
857   if (ssh_username != NULL)
858     username = GNUNET_strdup (ssh_username);
859   if ((ssh_username == NULL) && (GNUNET_OK !=
860                                  GNUNET_CONFIGURATION_get_value_string (cfg,
861                                                                         "TESTING",
862                                                                         "USERNAME",
863                                                                         &username)))
864     {
865       if (NULL != getenv ("USER"))
866         username = GNUNET_strdup (getenv ("USER"));
867       else
868         username = NULL;
869     }
870   ret->username = username;
871
872   /* 2) copy file to remote host */
873   if (NULL != hostname)
874     {
875 #if DEBUG_TESTING
876       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
877                   "Copying configuration file to host `%s'.\n", hostname);
878 #endif
879       ret->phase = SP_COPYING;
880       if (NULL != username)
881         GNUNET_asprintf (&arg, "%s@%s:%s", username, hostname, ret->cfgfile);
882       else
883         GNUNET_asprintf (&arg, "%s:%s", hostname, ret->cfgfile);
884
885       if (ret->ssh_port_str == NULL)
886         {
887           ret->proc = GNUNET_OS_start_process (NULL, NULL, "scp", "scp",
888 #if !DEBUG_TESTING
889                                                "-q",
890 #endif
891                                                ret->cfgfile, arg, NULL);
892         }
893       else
894         {
895           ret->proc = GNUNET_OS_start_process (NULL, NULL, "scp",
896                                                "scp", "-P", ret->ssh_port_str,
897 #if !DEBUG_TESTING
898                                                "-q",
899 #endif
900                                                ret->cfgfile, arg, NULL);
901         }
902       GNUNET_free (arg);
903       if (NULL == ret->proc)
904         {
905           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
906                       _
907                       ("Could not start `%s' process to copy configuration file.\n"),
908                       "scp");
909           if (0 != UNLINK (ret->cfgfile))
910             GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
911                                       "unlink", ret->cfgfile);
912           GNUNET_CONFIGURATION_destroy (ret->cfg);
913           GNUNET_free_non_null (ret->hostname);
914           GNUNET_free_non_null (ret->username);
915           GNUNET_free (ret->cfgfile);
916           GNUNET_free (ret);
917           return NULL;
918         }
919       ret->task
920         = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT,
921                                         &start_fsm, ret);
922       return ret;
923     }
924 #if DEBUG_TESTING
925   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
926               "No need to copy configuration file since we are running locally.\n");
927 #endif
928   ret->phase = SP_COPIED;
929   GNUNET_SCHEDULER_add_continuation (&start_fsm,
930                                      ret,
931                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
932   return ret;
933 }
934
935
936 /**
937  * Restart (stop and start) a GNUnet daemon.
938  *
939  * @param d the daemon that should be restarted
940  * @param cb function called once the daemon is (re)started
941  * @param cb_cls closure for cb
942  */
943 void
944 GNUNET_TESTING_daemon_restart (struct GNUNET_TESTING_Daemon *d,
945                                GNUNET_TESTING_NotifyDaemonRunning cb,
946                                void *cb_cls)
947 {
948   char *arg;
949   char *del_arg;
950
951   del_arg = NULL;
952   if (NULL != d->cb)
953     {
954       d->dead = GNUNET_YES;
955       return;
956     }
957
958   d->cb = cb;
959   d->cb_cls = cb_cls;
960
961   if (d->phase == SP_CONFIG_UPDATE)
962     {
963       GNUNET_SCHEDULER_cancel (d->task);
964       d->phase = SP_START_DONE;
965     }
966   if (d->server != NULL)
967     {
968       GNUNET_CORE_disconnect (d->server);
969       d->server = NULL;
970     }
971
972   if (d->th != NULL)
973     {
974       GNUNET_TRANSPORT_get_hello_cancel (d->th, &process_hello, d);
975       GNUNET_TRANSPORT_disconnect (d->th);
976       d->th = NULL;
977     }
978   /* state clean up and notifications */
979   GNUNET_free_non_null (d->hello);
980
981 #if DEBUG_TESTING
982   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
983               _("Terminating peer `%4s'\n"), GNUNET_i2s (&d->id));
984 #endif
985
986   d->phase = SP_START_ARMING;
987
988   /* Check if this is a local or remote process */
989   if (NULL != d->hostname)
990     {
991 #if DEBUG_TESTING
992       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
993                   "Stopping gnunet-arm with config `%s' on host `%s'.\n",
994                   d->cfgfile, d->hostname);
995 #endif
996
997       if (d->username != NULL)
998         GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname);
999       else
1000         arg = GNUNET_strdup (d->hostname);
1001
1002       d->proc = GNUNET_OS_start_process (NULL, NULL, "ssh", "ssh",
1003 #if !DEBUG_TESTING
1004                                          "-q",
1005 #endif
1006                                          arg, "gnunet-arm",
1007 #if DEBUG_TESTING
1008                                          "-L", "DEBUG",
1009 #endif
1010                                          "-c", d->cfgfile, "-e", "-r", NULL);
1011       /* Use -r to restart arm and all services */
1012
1013       GNUNET_free (arg);
1014     }
1015   else
1016     {
1017 #if DEBUG_TESTING
1018       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1019                   "Stopping gnunet-arm with config `%s' locally.\n",
1020                   d->cfgfile);
1021 #endif
1022       d->proc = GNUNET_OS_start_process (NULL, NULL, "gnunet-arm",
1023                                          "gnunet-arm",
1024 #if DEBUG_TESTING
1025                                          "-L", "DEBUG",
1026 #endif
1027                                          "-c", d->cfgfile, "-e", "-r", NULL);
1028     }
1029
1030   GNUNET_free_non_null (del_arg);
1031   d->task
1032     = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT,
1033                                     &start_fsm, d);
1034
1035 }
1036
1037
1038 /**
1039  * Stops a GNUnet daemon.
1040  *
1041  * @param d the daemon that should be stopped
1042  * @param timeout how long to wait for process for shutdown to complete
1043  * @param cb function called once the daemon was stopped
1044  * @param cb_cls closure for cb
1045  * @param delete_files GNUNET_YES to remove files, GNUNET_NO
1046  *        to leave them
1047  * @param allow_restart GNUNET_YES to restart peer later (using this API)
1048  *        GNUNET_NO to kill off and clean up for good
1049  */
1050 void
1051 GNUNET_TESTING_daemon_stop (struct GNUNET_TESTING_Daemon *d,
1052                             struct GNUNET_TIME_Relative timeout,
1053                             GNUNET_TESTING_NotifyCompletion cb, void *cb_cls,
1054                             int delete_files, int allow_restart)
1055 {
1056   char *arg;
1057   char *del_arg;
1058   d->dead_cb = cb;
1059   d->dead_cb_cls = cb_cls;
1060
1061   if (NULL != d->cb)
1062     {
1063 #if DEBUG_TESTING
1064       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1065                   _("Setting d->dead on peer `%4s'\n"), GNUNET_i2s (&d->id));
1066 #endif
1067       d->dead = GNUNET_YES;
1068       return;
1069     }
1070
1071   if ((d->running == GNUNET_NO) && (d->churn == GNUNET_YES))    /* Peer has already been stopped in churn context! */
1072     {
1073       /* Free what was left from churning! */
1074       GNUNET_assert (d->cfg != NULL);
1075       GNUNET_CONFIGURATION_destroy (d->cfg);
1076       if (delete_files == GNUNET_YES)
1077         {
1078           if (0 != UNLINK (d->cfgfile))
1079             {
1080               GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "unlink");
1081             }
1082         }
1083       GNUNET_free (d->cfgfile);
1084       GNUNET_free_non_null (d->hostname);
1085       GNUNET_free_non_null (d->username);
1086       if (NULL != d->dead_cb)
1087         d->dead_cb (d->dead_cb_cls, NULL);
1088       GNUNET_free (d);
1089       return;
1090     }
1091
1092   del_arg = NULL;
1093   if (delete_files == GNUNET_YES)
1094     {
1095       GNUNET_asprintf (&del_arg, "-d");
1096     }
1097
1098   if (d->phase == SP_CONFIG_UPDATE)
1099     {
1100       GNUNET_SCHEDULER_cancel (d->task);
1101       d->phase = SP_START_DONE;
1102     }
1103   /** Move this call to scheduled shutdown as fix for CORE_connect calling daemon_stop?
1104   if (d->server != NULL)
1105     {
1106       GNUNET_CORE_disconnect (d->server);
1107       d->server = NULL;
1108     }
1109     */
1110   /* shutdown ARM process (will terminate others) */
1111 #if DEBUG_TESTING
1112   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1113               _("Terminating peer `%4s'\n"), GNUNET_i2s (&d->id));
1114 #endif
1115   d->phase = SP_SHUTDOWN_START;
1116   d->running = GNUNET_NO;
1117   if (allow_restart == GNUNET_YES)
1118     d->churn = GNUNET_YES;
1119   if (d->th != NULL)
1120     {
1121       GNUNET_TRANSPORT_get_hello_cancel (d->th, &process_hello, d);
1122       GNUNET_TRANSPORT_disconnect (d->th);
1123       d->th = NULL;
1124     }
1125   /* Check if this is a local or remote process */
1126   if (NULL != d->hostname)
1127     {
1128 #if DEBUG_TESTING
1129       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1130                   "Stopping gnunet-arm with config `%s' on host `%s'.\n",
1131                   d->cfgfile, d->hostname);
1132 #endif
1133
1134       if (d->username != NULL)
1135         GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname);
1136       else
1137         arg = GNUNET_strdup (d->hostname);
1138
1139       d->proc = GNUNET_OS_start_process (NULL, NULL, "ssh", "ssh",
1140 #if !DEBUG_TESTING
1141                                          "-q",
1142 #endif
1143                                          arg, "gnunet-arm",
1144 #if DEBUG_TESTING
1145                                          "-L", "DEBUG",
1146 #endif
1147                                          "-c", d->cfgfile, "-e", "-q",
1148                                          del_arg, NULL);
1149       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1150                   "Stopping gnunet-arm with command ssh %s gnunet-arm -c %s -e -q %s\n",
1151                   arg, "gnunet-arm", d->cfgfile, del_arg);
1152       /* Use -e to end arm, and -d to remove temp files */
1153       GNUNET_free (arg);
1154     }
1155   else
1156     {
1157 #if DEBUG_TESTING
1158       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1159                   "Stopping gnunet-arm with config `%s' locally.\n",
1160                   d->cfgfile);
1161 #endif
1162       d->proc = GNUNET_OS_start_process (NULL, NULL, "gnunet-arm",
1163                                          "gnunet-arm",
1164 #if DEBUG_TESTING
1165                                          "-L", "DEBUG",
1166 #endif
1167                                          "-c", d->cfgfile, "-e", "-q",
1168                                          del_arg, NULL);
1169     }
1170
1171   GNUNET_free_non_null (del_arg);
1172   d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
1173   d->task = GNUNET_SCHEDULER_add_now (&start_fsm, d);
1174 }
1175
1176
1177 /**
1178  * Changes the configuration of a GNUnet daemon.
1179  *
1180  * @param d the daemon that should be modified
1181  * @param cfg the new configuration for the daemon
1182  * @param cb function called once the configuration was changed
1183  * @param cb_cls closure for cb
1184  */
1185 void
1186 GNUNET_TESTING_daemon_reconfigure (struct GNUNET_TESTING_Daemon *d,
1187                                    struct GNUNET_CONFIGURATION_Handle *cfg,
1188                                    GNUNET_TESTING_NotifyCompletion cb,
1189                                    void *cb_cls)
1190 {
1191   char *arg;
1192
1193   if (d->phase != SP_START_DONE)
1194     {
1195       if (NULL != cb)
1196         cb (cb_cls,
1197             _
1198             ("Peer not yet running, can not change configuration at this point."));
1199       return;
1200     }
1201
1202   /* 1) write configuration to temporary file */
1203   if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, d->cfgfile))
1204     {
1205       if (NULL != cb)
1206         cb (cb_cls, _("Failed to write new configuration to disk."));
1207       return;
1208     }
1209
1210   /* 2) copy file to remote host (if necessary) */
1211   if (NULL == d->hostname)
1212     {
1213       /* signal success */
1214       if (NULL != cb)
1215         cb (cb_cls, NULL);
1216       return;
1217     }
1218 #if DEBUG_TESTING
1219   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1220               "Copying updated configuration file to remote host `%s'.\n",
1221               d->hostname);
1222 #endif
1223   d->phase = SP_CONFIG_UPDATE;
1224   if (NULL != d->username)
1225     GNUNET_asprintf (&arg, "%s@%s:%s", d->username, d->hostname, d->cfgfile);
1226   else
1227     GNUNET_asprintf (&arg, "%s:%s", d->hostname, d->cfgfile);
1228   d->proc = GNUNET_OS_start_process (NULL, NULL, "scp", "scp",
1229 #if !DEBUG_TESTING
1230                                      "-q",
1231 #endif
1232                                      d->cfgfile, arg, NULL);
1233   GNUNET_free (arg);
1234   if (NULL == d->proc)
1235     {
1236       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1237                   _
1238                   ("Could not start `%s' process to copy configuration file.\n"),
1239                   "scp");
1240       if (NULL != cb)
1241         cb (cb_cls, _("Failed to copy new configuration to remote machine."));
1242       d->phase = SP_START_DONE;
1243       return;
1244     }
1245   d->update_cb = cb;
1246   d->update_cb_cls = cb_cls;
1247   d->task
1248     = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT,
1249                                     &start_fsm, d);
1250 }
1251
1252
1253 /**
1254  * Data kept for each pair of peers that we try
1255  * to connect.
1256  */
1257 struct ConnectContext
1258 {
1259   /**
1260    * Testing handle to the first daemon.
1261    */
1262   struct GNUNET_TESTING_Daemon *d1;
1263
1264   /**
1265    * Handle to core of first daemon (to check connect)
1266    */
1267   struct GNUNET_CORE_Handle *d1core;
1268
1269   /**
1270    * Have we actually connected to the core of the first daemon yet?
1271    */
1272   int d1core_ready;
1273
1274   /**
1275    * Testing handle to the second daemon.
1276    */
1277   struct GNUNET_TESTING_Daemon *d2;
1278
1279   /**
1280    * Handler for the request to core to connect to this peer.
1281    */
1282   struct GNUNET_CORE_PeerRequestHandle *connect_request_handle;
1283
1284   /**
1285    * Transport handle to the first daemon (to offer the HELLO of the second daemon to).
1286    */
1287   struct GNUNET_TRANSPORT_Handle *d1th;
1288
1289   /**
1290    * Function to call once we are done (or have timed out).
1291    */
1292   GNUNET_TESTING_NotifyConnection cb;
1293
1294   /**
1295    * Closure for "nb".
1296    */
1297   void *cb_cls;
1298
1299   /**
1300    * When should this operation be complete (or we must trigger
1301    * a timeout).
1302    */
1303   struct GNUNET_TIME_Absolute timeout;
1304
1305   /**
1306    * The relative timeout from whence this connect attempt was
1307    * started.  Allows for reconnect attempts.
1308    */
1309   struct GNUNET_TIME_Relative relative_timeout;
1310
1311   /**
1312    * Maximum number of connect attempts, will retry connection
1313    * this number of times on failures.
1314    */
1315   unsigned int max_connect_attempts;
1316
1317   /**
1318    * Hello timeout task
1319    */
1320   GNUNET_SCHEDULER_TaskIdentifier hello_send_task;
1321
1322   /**
1323    * Connect timeout task
1324    */
1325   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
1326
1327   /**
1328    * When should this operation be complete (or we must trigger
1329    * a timeout).
1330    */
1331   struct GNUNET_TIME_Relative timeout_hello;
1332
1333   /**
1334    * Was the connection attempt successful?
1335    */
1336   int connected;
1337
1338   /**
1339    * The distance between the two connected peers
1340    */
1341   uint32_t distance;
1342 };
1343
1344
1345 /** Forward declaration **/
1346 static void
1347 reattempt_daemons_connect (void *cls,
1348                            const struct GNUNET_SCHEDULER_TaskContext *tc);
1349
1350
1351 /**
1352  * Notify callback about success or failure of the attempt
1353  * to connect the two peers
1354  *
1355  * @param cls our "struct ConnectContext" (freed)
1356  * @param tc reason tells us if we succeeded or failed
1357  */
1358 static void
1359 notify_connect_result (void *cls,
1360                        const struct GNUNET_SCHEDULER_TaskContext *tc)
1361 {
1362   struct ConnectContext *ctx = cls;
1363   struct GNUNET_TIME_Relative remaining;
1364
1365   ctx->timeout_task = GNUNET_SCHEDULER_NO_TASK;
1366   if (ctx->hello_send_task != GNUNET_SCHEDULER_NO_TASK)
1367     {
1368       GNUNET_SCHEDULER_cancel (ctx->hello_send_task);
1369       ctx->hello_send_task = GNUNET_SCHEDULER_NO_TASK;
1370     }
1371
1372   if (ctx->connect_request_handle != NULL)
1373     {
1374       GNUNET_CORE_peer_request_connect_cancel (ctx->connect_request_handle);
1375       ctx->connect_request_handle = NULL;
1376     }
1377   if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
1378     {
1379       if (ctx->d1th != NULL)
1380         GNUNET_TRANSPORT_disconnect (ctx->d1th);
1381       ctx->d1th = NULL;
1382       if (ctx->d1core != NULL)
1383         GNUNET_CORE_disconnect (ctx->d1core);
1384 #if CONNECT_CORE2
1385       if (ctx->d2core != NULL)
1386         GNUNET_CORE_disconnect (ctx->d2core);
1387       ctx->d2core = NULL;
1388 #endif
1389       ctx->d1core = NULL;
1390
1391       GNUNET_free (ctx);
1392       return;
1393     }
1394
1395   remaining = GNUNET_TIME_absolute_get_remaining (ctx->timeout);
1396
1397   if (ctx->connected == GNUNET_YES)
1398     {
1399       if (ctx->cb != NULL)
1400         {
1401           ctx->cb (ctx->cb_cls,
1402                    &ctx->d1->id,
1403                    &ctx->d2->id,
1404                    ctx->distance,
1405                    ctx->d1->cfg, ctx->d2->cfg, ctx->d1, ctx->d2, NULL);
1406         }
1407     }
1408   else if (remaining.rel_value > 0)
1409     {
1410       if (ctx->d1core != NULL)
1411         {
1412           GNUNET_CORE_disconnect (ctx->d1core);
1413           ctx->d1core = NULL;
1414         }
1415       ctx->d1core_ready = GNUNET_NO;
1416 #if CONNECT_CORE2
1417       if (ctx->d2core != NULL)
1418         {
1419           GNUNET_CORE_disconnect (ctx->d2core);
1420           ctx->d2core = NULL;
1421         }
1422 #endif
1423
1424       if (ctx->d1th != NULL)
1425         {
1426           GNUNET_TRANSPORT_disconnect (ctx->d1th);
1427           ctx->d1th = NULL;
1428         }
1429       GNUNET_SCHEDULER_add_now (&reattempt_daemons_connect, ctx);
1430       return;
1431     }
1432   else
1433     {
1434       if (ctx->cb != NULL)
1435         {
1436           ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, 0, ctx->d1->cfg,
1437                    ctx->d2->cfg, ctx->d1, ctx->d2,
1438                    _("Peers failed to connect"));
1439         }
1440     }
1441
1442   GNUNET_TRANSPORT_disconnect (ctx->d1th);
1443   ctx->d1th = NULL;
1444   GNUNET_CORE_disconnect (ctx->d1core);
1445   ctx->d1core = NULL;
1446   GNUNET_free (ctx);
1447 }
1448
1449
1450 /**
1451  * Success, connection is up.  Signal client our success.
1452  *
1453  * @param cls our "struct ConnectContext"
1454  * @param peer identity of the peer that has connected
1455  * @param atsi performance information
1456  *
1457  */
1458 static void
1459 connect_notify (void *cls,
1460                 const struct GNUNET_PeerIdentity *peer,
1461                 const struct GNUNET_TRANSPORT_ATS_Information *atsi)
1462 {
1463   struct ConnectContext *ctx = cls;
1464
1465   if (memcmp (&ctx->d2->id, peer, sizeof (struct GNUNET_PeerIdentity)) == 0)
1466     {
1467       ctx->connected = GNUNET_YES;
1468       ctx->distance = 0;        /* FIXME: distance */
1469       GNUNET_SCHEDULER_cancel (ctx->timeout_task);
1470       ctx->timeout_task = GNUNET_SCHEDULER_add_now (&notify_connect_result,
1471                                                     ctx);
1472     }
1473 }
1474
1475 #if CONNECT_CORE2
1476 /**
1477  * Success, connection is up.  Signal client our success.
1478  *
1479  * @param cls our "struct ConnectContext"
1480  * @param peer identity of the peer that has connected
1481  * @param atsi performance information
1482  *
1483  */
1484 static void
1485 connect_notify_core2 (void *cls,
1486                       const struct GNUNET_PeerIdentity *peer,
1487                       const struct GNUNET_TRANSPORT_ATS_Information *atsi)
1488 {
1489   struct ConnectContext *ctx = cls;
1490
1491   if (memcmp (&ctx->d2->id, peer, sizeof (struct GNUNET_PeerIdentity)) == 0)
1492     {
1493       ctx->connected = GNUNET_YES;
1494       ctx->distance = 0;        /* FIXME: distance */
1495       GNUNET_SCHEDULER_cancel (ctx->timeout_task);
1496       ctx->timeout_task = GNUNET_SCHEDULER_add_now (&notify_connect_result,
1497                                                     ctx);
1498     }
1499
1500 }
1501 #endif
1502
1503 /**
1504  * Task called once a core connect request has been transmitted.
1505  *
1506  * @param cls struct ConnectContext
1507  * @param tc context information (why was this task triggered now)
1508  */
1509 void
1510 core_connect_request_cont (void *cls,
1511                            const struct GNUNET_SCHEDULER_TaskContext *tc)
1512 {
1513   struct ConnectContext *ctx = cls;
1514
1515   if (tc->reason == GNUNET_SCHEDULER_REASON_PREREQ_DONE)
1516     ctx->connect_request_handle = NULL;
1517   return;
1518 }
1519
1520 static void
1521 send_hello (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1522 {
1523   struct ConnectContext *ctx = cls;
1524   struct GNUNET_MessageHeader *hello;
1525   ctx->hello_send_task = GNUNET_SCHEDULER_NO_TASK;
1526   if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
1527     return;
1528   if ((ctx->d1core_ready == GNUNET_YES) && (ctx->d2->hello != NULL)
1529       && (NULL != GNUNET_HELLO_get_header (ctx->d2->hello)))
1530     {
1531       hello = GNUNET_HELLO_get_header (ctx->d2->hello);
1532       GNUNET_assert (hello != NULL);
1533       GNUNET_TRANSPORT_offer_hello (ctx->d1th, hello);
1534       GNUNET_assert (ctx->d1core != NULL);
1535       ctx->connect_request_handle =
1536         GNUNET_CORE_peer_request_connect (ctx->d1core,
1537                                           GNUNET_TIME_relative_divide
1538                                           (ctx->relative_timeout,
1539                                            ctx->max_connect_attempts + 1),
1540                                           &ctx->d2->id,
1541                                           &core_connect_request_cont, ctx);
1542 #if DEBUG_TESTING
1543       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1544                   "Sending connect request to CORE of %s for peer %s\n",
1545                   GNUNET_i2s (&ctx->d1->id),
1546                   GNUNET_h2s (&ctx->d2->id.hashPubKey));
1547 #endif
1548       ctx->timeout_hello =
1549         GNUNET_TIME_relative_add (ctx->timeout_hello,
1550                                   GNUNET_TIME_relative_multiply
1551                                   (GNUNET_TIME_UNIT_MILLISECONDS, 500));
1552     }
1553   ctx->hello_send_task = GNUNET_SCHEDULER_add_delayed (ctx->timeout_hello,
1554                                                        &send_hello, ctx);
1555 }
1556
1557 void
1558 core_init_notify (void *cls,
1559                   struct GNUNET_CORE_Handle * server,
1560                   const struct GNUNET_PeerIdentity *
1561                   my_identity,
1562                   const struct
1563                   GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *
1564                   publicKey)
1565 {
1566   struct ConnectContext *connect_ctx = cls;
1567
1568   connect_ctx->d1core_ready = GNUNET_YES;
1569 }
1570
1571 /**
1572  * Establish a connection between two GNUnet daemons.
1573  *
1574  * @param d1 handle for the first daemon
1575  * @param d2 handle for the second daemon
1576  * @param timeout how long is the connection attempt
1577  *        allowed to take?
1578  * @param max_connect_attempts how many times should we try to reconnect
1579  *        (within timeout)
1580  * @param cb function to call at the end
1581  * @param cb_cls closure for cb
1582  */
1583 void
1584 GNUNET_TESTING_daemons_connect (struct GNUNET_TESTING_Daemon *d1,
1585                                 struct GNUNET_TESTING_Daemon *d2,
1586                                 struct GNUNET_TIME_Relative timeout,
1587                                 unsigned int max_connect_attempts,
1588                                 GNUNET_TESTING_NotifyConnection cb,
1589                                 void *cb_cls)
1590 {
1591   struct ConnectContext *ctx;
1592
1593   if ((d1->running == GNUNET_NO) || (d2->running == GNUNET_NO))
1594     {
1595       if (NULL != cb)
1596         cb (cb_cls, &d1->id, &d2->id, 0, d1->cfg, d2->cfg, d1, d2,
1597             _("Peers are not fully running yet, can not connect!\n"));
1598       return;
1599     }
1600   ctx = GNUNET_malloc (sizeof (struct ConnectContext));
1601   ctx->d1 = d1;
1602   ctx->d2 = d2;
1603   ctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1604   ctx->timeout_hello =
1605     GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 500);
1606   ctx->relative_timeout = timeout;
1607   ctx->cb = cb;
1608   ctx->cb_cls = cb_cls;
1609   ctx->max_connect_attempts = max_connect_attempts;
1610   ctx->connected = GNUNET_NO;
1611 #if DEBUG_TESTING
1612   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1613               "Asked to connect peer %s to peer %s\n",
1614               d1->shortname, d2->shortname);
1615 #endif
1616
1617   /* FIXME: possible bug, core gets connected after peers are connected, thus the connect_notify function is never called (?) */
1618   ctx->d1core = GNUNET_CORE_connect (d1->cfg, 1,
1619                                      ctx,
1620                                      &core_init_notify,
1621                                      &connect_notify, NULL, NULL,
1622                                      NULL, GNUNET_NO,
1623                                      NULL, GNUNET_NO, no_handlers);
1624   if (ctx->d1core == NULL)
1625     {
1626       GNUNET_free (ctx);
1627       if (NULL != cb)
1628         cb (cb_cls, &d1->id, &d2->id, 0, d1->cfg, d2->cfg, d1, d2,
1629             _("Failed to connect to core service of first peer!\n"));
1630       return;
1631     }
1632
1633 #if CONNECT_CORE2
1634   ctx->d2core = GNUNET_CORE_connect (d2->cfg, 1,
1635 #if NO_MORE_TIMEOUT_FIXME
1636                                      timeout,
1637 #endif
1638                                      ctx,
1639                                      NULL,
1640                                      NULL, NULL, NULL,
1641                                      NULL, GNUNET_NO,
1642                                      NULL, GNUNET_NO, no_handlers);
1643   if (ctx->d2core == NULL)
1644     {
1645       GNUNET_free (ctx);
1646       if (NULL != cb)
1647         cb (cb_cls, &d1->id, &d2->id, 0, d1->cfg, d2->cfg, d1, d2,
1648             _("Failed to connect to core service of second peer!\n"));
1649       return;
1650     }
1651 #endif
1652
1653 #if DEBUG_TESTING > 2
1654   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1655               "Asked to connect peer %s to peer %s\n",
1656               d1->shortname, d2->shortname);
1657   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1658               "Connecting to transport service of peer %s\n", d2->shortname);
1659
1660 #endif
1661
1662   ctx->d1th = GNUNET_TRANSPORT_connect (d1->cfg,
1663                                         &d1->id, d1, NULL, NULL, NULL);
1664   if (ctx->d1th == NULL)
1665     {
1666       GNUNET_CORE_disconnect (ctx->d1core);
1667       GNUNET_free (ctx);
1668       if (NULL != cb)
1669         cb (cb_cls, &d1->id, &d2->id, 0, d1->cfg, d2->cfg, d1, d2,
1670             _("Failed to connect to transport service!\n"));
1671       return;
1672     }
1673
1674   ctx->timeout_task =
1675     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_divide
1676                                   (ctx->relative_timeout,
1677                                    ctx->max_connect_attempts),
1678                                    &notify_connect_result, ctx);
1679
1680   ctx->hello_send_task = GNUNET_SCHEDULER_add_now (&send_hello, ctx);
1681 }
1682
1683 static void
1684 reattempt_daemons_connect (void *cls,
1685                            const struct GNUNET_SCHEDULER_TaskContext *tc)
1686 {
1687
1688   struct ConnectContext *ctx = cls;
1689   if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
1690     {
1691       return;
1692     }
1693 #if DEBUG_TESTING_RECONNECT
1694   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1695               "re-attempting connect of peer %s to peer %s\n",
1696               ctx->d1->shortname, ctx->d2->shortname);
1697 #endif
1698
1699   GNUNET_assert (ctx->d1core == NULL);
1700   ctx->d1core_ready = GNUNET_NO;
1701   ctx->d1core = GNUNET_CORE_connect (ctx->d1->cfg, 1,
1702                                      ctx,
1703                                      &core_init_notify,
1704                                      &connect_notify, NULL, NULL,
1705                                      NULL, GNUNET_NO,
1706                                      NULL, GNUNET_NO, no_handlers);
1707   if (ctx->d1core == NULL)
1708     {
1709       if (NULL != ctx->cb)
1710         ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, 0, ctx->d1->cfg,
1711                  ctx->d2->cfg, ctx->d1, ctx->d2,
1712                  _("Failed to connect to core service of first peer!\n"));
1713       GNUNET_free (ctx);
1714       return;
1715     }
1716
1717   ctx->d1th = GNUNET_TRANSPORT_connect (ctx->d1->cfg,
1718                                         &ctx->d1->id,
1719                                         ctx->d1, NULL, NULL, NULL);
1720   if (ctx->d1th == NULL)
1721     {
1722       GNUNET_CORE_disconnect (ctx->d1core);
1723       GNUNET_free (ctx);
1724       if (NULL != ctx->cb)
1725         ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, 0, ctx->d1->cfg,
1726                  ctx->d2->cfg, ctx->d1, ctx->d2,
1727                  _("Failed to connect to transport service!\n"));
1728       return;
1729     }
1730
1731   ctx->timeout_task =
1732     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_divide
1733                                   (ctx->relative_timeout,
1734                                    ctx->max_connect_attempts),
1735                                   &notify_connect_result, ctx);
1736
1737   ctx->hello_send_task = GNUNET_SCHEDULER_add_now (&send_hello, ctx);
1738 }
1739
1740 /* end of testing.c */