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