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