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