towards fixing blacklisting APIs and implementation
[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 2, 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
39 #define DEBUG_TESTING GNUNET_YES
40
41 /**
42  * How long do we wait after starting gnunet-service-arm
43  * for the core service to be alive?
44  */
45 #define ARM_START_WAIT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
46
47 /**
48  * How many times are we willing to try to wait for "scp" or
49  * "gnunet-service-arm" to complete (waitpid) before giving up?
50  */
51 #define MAX_EXEC_WAIT_RUNS 50
52
53
54 /**
55  * Function called after GNUNET_CORE_connect has succeeded
56  * (or failed for good).  Note that the private key of the
57  * peer is intentionally not exposed here; if you need it,
58  * your process should try to read the private key file
59  * directly (which should work if you are authorized...).
60  *
61  * @param cls closure
62  * @param server handle to the server, NULL if we failed
63  * @param my_identity ID of this peer, NULL if we failed
64  * @param publicKey public key of this peer, NULL if we failed
65  */
66 static void
67 testing_init (void *cls,
68               struct GNUNET_CORE_Handle *server,
69               const struct GNUNET_PeerIdentity *my_identity,
70               const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
71 {
72   struct GNUNET_TESTING_Daemon *d = cls;
73   GNUNET_TESTING_NotifyDaemonRunning cb;
74
75   GNUNET_assert (d->phase == SP_START_CORE);
76   d->phase = SP_START_DONE;
77   cb = d->cb;
78   d->cb = NULL;
79   if (server == NULL)
80     {
81       d->server = NULL;
82       if (GNUNET_YES == d->dead)
83         GNUNET_TESTING_daemon_stop (d, d->dead_cb, d->dead_cb_cls);
84       else if (NULL != cb)
85         cb (d->cb_cls, NULL, d->cfg, d,
86             _("Failed to connect to core service\n"));
87       return;
88     }
89 #if DEBUG_TESTING
90   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
91               "Successfully started peer `%4s'.\n", GNUNET_i2s (my_identity));
92 #endif
93   d->id = *my_identity;
94   d->shortname = strdup (GNUNET_i2s (my_identity));
95   d->server = server;
96   if (GNUNET_YES == d->dead)
97     GNUNET_TESTING_daemon_stop (d, d->dead_cb, d->dead_cb_cls);
98   else if (NULL != cb)
99     cb (d->cb_cls, my_identity, d->cfg, d, NULL);
100 }
101
102
103 /**
104  * Finite-state machine for starting GNUnet.
105  *
106  * @param cls our "struct GNUNET_TESTING_Daemon"
107  * @param tc unused
108  */
109 static void
110 start_fsm (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
111 {
112   static struct GNUNET_CORE_MessageHandler no_handlers[] = { {NULL, 0, 0} };
113   struct GNUNET_TESTING_Daemon *d = cls;
114   GNUNET_TESTING_NotifyDaemonRunning cb;
115   enum GNUNET_OS_ProcessStatusType type;
116   unsigned long code;
117   char *dst;
118
119 #if DEBUG_TESTING
120   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
121               "Peer FSM is in phase %u.\n", d->phase);
122 #endif
123   d->task = GNUNET_SCHEDULER_NO_TASK;
124   switch (d->phase)
125     {
126     case SP_COPYING:
127       /* confirm copying complete */
128       if (GNUNET_OK != GNUNET_OS_process_status (d->pid, &type, &code))
129         {
130           d->wait_runs++;
131           if (d->wait_runs > MAX_EXEC_WAIT_RUNS)
132             {
133               cb = d->cb;
134               d->cb = NULL;
135               if (NULL != cb)
136                 cb (d->cb_cls,
137                     NULL,
138                     d->cfg, d, _("`scp' does not seem to terminate.\n"));
139               return;
140             }
141           /* wait some more */
142           d->task
143             = GNUNET_SCHEDULER_add_delayed (d->sched,
144                                             GNUNET_CONSTANTS_EXEC_WAIT,
145                                             &start_fsm, d);
146           return;
147         }
148       if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0))
149         {
150           cb = d->cb;
151           d->cb = NULL;
152           if (NULL != cb)
153             cb (d->cb_cls,
154                 NULL, d->cfg, d, _("`scp' did not complete cleanly.\n"));
155           return;
156         }
157 #if DEBUG_TESTING
158       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
159                   "Successfully copied configuration file.\n");
160 #endif
161       d->phase = SP_COPIED;
162       /* fall-through */
163     case SP_COPIED:
164       /* start GNUnet on remote host */
165       if (NULL == d->hostname)
166         {
167 #if DEBUG_TESTING
168           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
169                       "Starting `%s', with command `%s %s %s %s %s %s'.\n",
170                       "gnunet-arm", "gnunet-arm", "-c", d->cfgfile,
171                       "-L", "DEBUG",
172                       "-s");
173 #endif
174           d->pid = GNUNET_OS_start_process (NULL, NULL, "gnunet-arm",
175                                             "gnunet-arm",
176                                             "-c", d->cfgfile,
177 #if DEBUG_TESTING
178                                             "-L", "DEBUG",
179 #endif
180                                             "-s", NULL);
181         }
182       else
183         {
184           if (d->username != NULL)
185             GNUNET_asprintf (&dst, "%s@%s", d->username, d->hostname);
186           else
187             dst = GNUNET_strdup (d->hostname);
188
189 #if DEBUG_TESTING
190           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
191                       "Starting `%s', with command `%s %s %s %s %s %s %s %s'.\n",
192                       "gnunet-arm", "ssh", dst, "gnunet-arm", "-c", d->cfgfile,
193                       "-L", "DEBUG", "-s");
194 #endif
195           d->pid = GNUNET_OS_start_process (NULL, NULL, "ssh",
196                                             "ssh",
197                                             dst,
198                                             "gnunet-arm",
199 #if DEBUG_TESTING
200                                             "-L", "DEBUG",
201 #endif
202                                             "-c", d->cfgfile, "-s", NULL);
203           GNUNET_free (dst);
204         }
205       if (-1 == d->pid)
206         {
207           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
208                       _("Could not start `%s' process to start GNUnet.\n"),
209                       (NULL == d->hostname) ? "gnunet-arm" : "ssh");
210           cb = d->cb;
211           d->cb = NULL;
212           if (NULL != cb)
213             cb (d->cb_cls,
214                 NULL,
215                 d->cfg,
216                 d,
217                 (NULL == d->hostname)
218                 ? _("Failed to start `gnunet-arm' process.\n")
219                 : _("Failed to start `ssh' process.\n"));
220         }
221 #if DEBUG_TESTING
222       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
223                   "Started `%s', waiting for `%s' to be up.\n",
224                   "gnunet-arm", "gnunet-service-core");
225 #endif
226       d->phase = SP_START_ARMING;
227       d->wait_runs = 0;
228       d->task
229         = GNUNET_SCHEDULER_add_delayed (d->sched,
230                                         GNUNET_CONSTANTS_EXEC_WAIT,
231                                         &start_fsm, d);
232       break;
233     case SP_START_ARMING:
234       if (GNUNET_OK != GNUNET_OS_process_status (d->pid, &type, &code))
235         {
236           d->wait_runs++;
237           if (d->wait_runs > MAX_EXEC_WAIT_RUNS)
238             {
239               cb = d->cb;
240               d->cb = NULL;
241               if (NULL != cb)
242                 cb (d->cb_cls,
243                     NULL,
244                     d->cfg,
245                     d,
246                     (NULL == d->hostname)
247                     ? _("`gnunet-arm' does not seem to terminate.\n")
248                     : _("`ssh' does not seem to terminate.\n"));
249               return;
250             }
251           /* wait some more */
252           d->task
253             = GNUNET_SCHEDULER_add_delayed (d->sched,
254                                             GNUNET_CONSTANTS_EXEC_WAIT,
255                                             &start_fsm, d);
256           return;
257         }
258 #if DEBUG_TESTING
259       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
260                   "Successfully started `%s'.\n", "gnunet-arm");
261 #endif
262       d->phase = SP_START_CORE;
263       d->server = GNUNET_CORE_connect (d->sched,
264                                        d->cfg,
265                                        ARM_START_WAIT,
266                                        d,
267                                        &testing_init,
268                                        NULL, NULL,
269                                        NULL, GNUNET_NO,
270                                        NULL, GNUNET_NO, no_handlers);
271       break;
272     case SP_START_CORE:
273       GNUNET_break (0);
274       break;
275     case SP_START_DONE:
276       GNUNET_break (0);
277       break;
278     case SP_SHUTDOWN_START:
279       /* confirm copying complete */
280       if (GNUNET_OK != GNUNET_OS_process_status (d->pid, &type, &code))
281         {
282           d->wait_runs++;
283           if (d->wait_runs > MAX_EXEC_WAIT_RUNS)
284             {
285               d->dead_cb (d->dead_cb_cls,
286                           _("either `gnunet-arm' or `ssh' does not seem to terminate.\n"));
287               GNUNET_CONFIGURATION_destroy (d->cfg);
288               GNUNET_free (d->cfgfile);
289               GNUNET_free_non_null (d->hostname);
290               GNUNET_free_non_null (d->username);
291               GNUNET_free_non_null (d->shortname);
292               GNUNET_free (d);
293               return;
294             }
295           /* wait some more */
296           d->task
297             = GNUNET_SCHEDULER_add_delayed (d->sched,
298                                             GNUNET_CONSTANTS_EXEC_WAIT,
299                                             &start_fsm, d);
300           return;
301         }
302       if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0))
303         {
304           if (NULL != d->dead_cb)
305             d->dead_cb (d->dead_cb_cls,
306                         _("shutdown (either `gnunet-arm' or `ssh') did not complete cleanly.\n"));
307           GNUNET_CONFIGURATION_destroy (d->cfg);
308           GNUNET_free (d->cfgfile);
309           GNUNET_free_non_null (d->hostname);
310           GNUNET_free_non_null (d->username);
311           GNUNET_free_non_null (d->shortname);
312           GNUNET_free (d);
313           return;
314         }
315 #if DEBUG_TESTING
316       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer shutdown complete.\n");
317 #endif
318       /* state clean up and notifications */
319       GNUNET_CONFIGURATION_destroy (d->cfg);
320       GNUNET_free (d->cfgfile);
321       GNUNET_free_non_null (d->hostname);
322       GNUNET_free_non_null (d->username);
323       GNUNET_free_non_null (d->shortname);
324       if (NULL != d->dead_cb)
325         d->dead_cb (d->dead_cb_cls, NULL);
326       GNUNET_free (d);
327       break;
328     case SP_CONFIG_UPDATE:
329       /* confirm copying complete */
330       if (GNUNET_OK != GNUNET_OS_process_status (d->pid, &type, &code))
331         {
332           d->wait_runs++;
333           if (d->wait_runs > MAX_EXEC_WAIT_RUNS)
334             {
335               cb = d->cb;
336               d->cb = NULL;
337               if (NULL != cb)
338                 cb (d->cb_cls,
339                     NULL,
340                     d->cfg, d, _("`scp' does not seem to terminate.\n"));
341               return;
342             }
343           /* wait some more */
344           d->task
345             = GNUNET_SCHEDULER_add_delayed (d->sched,
346                                             GNUNET_CONSTANTS_EXEC_WAIT,
347                                             &start_fsm, d);
348           return;
349         }
350       if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0))
351         {
352           if (NULL != d->update_cb)
353             d->update_cb (d->update_cb_cls,
354                           _("`scp' did not complete cleanly.\n"));
355           return;
356         }
357 #if DEBUG_TESTING
358       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
359                   "Successfully copied configuration file.\n");
360 #endif
361       if (NULL != d->update_cb)
362         d->update_cb (d->update_cb_cls, NULL);
363       d->phase = SP_START_DONE;
364       break;
365     }
366 }
367
368
369 /**
370  * Starts a GNUnet daemon.  GNUnet must be installed on the target
371  * system and available in the PATH.  The machine must furthermore be
372  * reachable via "ssh" (unless the hostname is "NULL") without the
373  * need to enter a password.
374  *
375  * @param sched scheduler to use
376  * @param cfg configuration to use
377  * @param hostname name of the machine where to run GNUnet
378  *        (use NULL for localhost).
379  * @param cb function to call with the result
380  * @param cb_cls closure for cb
381  * @return handle to the daemon (actual start will be completed asynchronously)
382  */
383 struct GNUNET_TESTING_Daemon *
384 GNUNET_TESTING_daemon_start (struct GNUNET_SCHEDULER_Handle *sched,
385                              const struct GNUNET_CONFIGURATION_Handle *cfg,
386                              const char *hostname,
387                              GNUNET_TESTING_NotifyDaemonRunning cb,
388                              void *cb_cls)
389 {
390   struct GNUNET_TESTING_Daemon *ret;
391   char *arg;
392   char *username;
393
394   ret = GNUNET_malloc (sizeof (struct GNUNET_TESTING_Daemon));
395   ret->sched = sched;
396   ret->hostname = (hostname == NULL) ? NULL : GNUNET_strdup (hostname);
397   ret->cfgfile = GNUNET_DISK_mktemp ("gnunet-testing-config");
398 #if DEBUG_TESTING
399   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
400               "Setting up peer with configuration file `%s'.\n",
401               ret->cfgfile);
402 #endif
403   if (NULL == ret->cfgfile)
404     {
405       GNUNET_free_non_null (ret->hostname);
406       GNUNET_free (ret);
407       return NULL;
408     }
409   ret->cb = cb;
410   ret->cb_cls = cb_cls;
411   ret->cfg = GNUNET_CONFIGURATION_dup (cfg);
412   GNUNET_CONFIGURATION_set_value_string (ret->cfg,
413                                          "PATHS",
414                                          "DEFAULTCONFIG", ret->cfgfile);
415   /* 1) write configuration to temporary file */
416   if (GNUNET_OK != GNUNET_CONFIGURATION_write (ret->cfg, ret->cfgfile))
417     {
418       if (0 != UNLINK (ret->cfgfile))
419         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
420                                   "unlink", ret->cfgfile);
421       GNUNET_CONFIGURATION_destroy (ret->cfg);
422       GNUNET_free_non_null (ret->hostname);
423       GNUNET_free (ret->cfgfile);
424       GNUNET_free (ret);
425       return NULL;
426     }
427   if (GNUNET_OK !=
428       GNUNET_CONFIGURATION_get_value_string (cfg,
429                                              "TESTING",
430                                              "USERNAME", &username))
431     {
432       if (NULL != getenv ("USER"))
433         username = GNUNET_strdup (getenv ("USER"));
434       else
435         username = NULL;
436     }
437   ret->username = username;
438
439   /* 2) copy file to remote host */
440   if (NULL != hostname)
441     {
442 #if DEBUG_TESTING
443       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
444                   "Copying configuration file to host `%s'.\n", hostname);
445 #endif
446       ret->phase = SP_COPYING;
447       if (NULL != username)
448         GNUNET_asprintf (&arg, "%s@%s:%s", username, hostname, ret->cfgfile);
449       else
450         GNUNET_asprintf (&arg, "%s:%s", hostname, ret->cfgfile);
451       ret->pid = GNUNET_OS_start_process (NULL, NULL, "scp",
452                                           "scp", ret->cfgfile, arg, NULL);
453       GNUNET_free (arg);
454       if (-1 == ret->pid)
455         {
456           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
457                       _
458                       ("Could not start `%s' process to copy configuration file.\n"),
459                       "scp");
460           if (0 != UNLINK (ret->cfgfile))
461             GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
462                                       "unlink", ret->cfgfile);
463           GNUNET_CONFIGURATION_destroy (ret->cfg);
464           GNUNET_free_non_null (ret->hostname);
465           GNUNET_free_non_null (ret->username);
466           GNUNET_free (ret->cfgfile);
467           GNUNET_free (ret);
468           return NULL;
469         }
470       ret->task
471         = GNUNET_SCHEDULER_add_delayed (sched,
472                                         GNUNET_CONSTANTS_EXEC_WAIT,
473                                         &start_fsm, ret);
474       return ret;
475     }
476 #if DEBUG_TESTING
477   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
478               "No need to copy configuration file since we are running locally.\n");
479 #endif
480   ret->phase = SP_COPIED;
481   GNUNET_SCHEDULER_add_continuation (sched,
482                                      &start_fsm,
483                                      ret,
484                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
485   return ret;
486 }
487
488
489 /**
490  * Stops a GNUnet daemon.
491  *
492  * @param d the daemon that should be stopped
493  * @param cb function called once the daemon was stopped
494  * @param cb_cls closure for cb
495  */
496 void
497 GNUNET_TESTING_daemon_stop (struct GNUNET_TESTING_Daemon *d,
498                             GNUNET_TESTING_NotifyCompletion cb, void *cb_cls)
499 {
500   char *arg;
501
502   d->dead_cb = cb;
503   d->dead_cb_cls = cb_cls;
504
505   if (NULL != d->cb)
506     {
507       d->dead = GNUNET_YES;
508       return;
509     }
510   if (d->phase == SP_CONFIG_UPDATE)
511     {
512       GNUNET_SCHEDULER_cancel (d->sched, d->task);
513       d->phase = SP_START_DONE;
514     }
515   if (d->server != NULL)
516     {
517       GNUNET_CORE_disconnect (d->server);
518       d->server = NULL;
519     }
520   /* shutdown ARM process (will terminate others) */
521 #if DEBUG_TESTING
522   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
523               _("Terminating peer `%4s'\n"), GNUNET_i2s (&d->id));
524   /* sleep(15); Manual check for running */
525 #endif
526
527   d->phase = SP_SHUTDOWN_START;
528
529   /* Check if this is a local or remote process */
530   if (NULL != d->hostname)
531     {
532 #if DEBUG_TESTING
533       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
534                   "Stopping gnunet-arm with config `%s' on host `%s'.\n", d->cfgfile, d->hostname);
535 #endif
536
537       if (d->username != NULL)
538         GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname);
539       else
540         arg = GNUNET_strdup (d->hostname);
541
542       d->pid = GNUNET_OS_start_process (NULL, NULL, "ssh", "ssh",
543                                               arg, "gnunet-arm",
544 #if DEBUG_TESTING
545                                               "-L", "DEBUG",
546 #endif
547                                               "-c", d->cfgfile, "-e", "-d", NULL);
548       /* Use -e to end arm, and -d to remove temp files */
549
550       GNUNET_free (arg);
551     }
552   else
553   {
554 #if DEBUG_TESTING
555       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
556                   "Stopping gnunet-arm with config `%s' locally.\n", d->cfgfile);
557 #endif
558     d->pid = GNUNET_OS_start_process (NULL, NULL, "gnunet-arm",
559                                             "gnunet-arm",
560 #if DEBUG_TESTING
561                                             "-L", "DEBUG",
562 #endif
563                                             "-c", d->cfgfile, "-e", "-d", NULL);
564   }
565
566   d->wait_runs = 0;
567   d->task
568     = GNUNET_SCHEDULER_add_delayed (d->sched,
569                                     GNUNET_CONSTANTS_EXEC_WAIT,
570                                     &start_fsm, d);
571   return;
572 }
573
574
575 /**
576  * Changes the configuration of a GNUnet daemon.
577  *
578  * @param d the daemon that should be modified
579  * @param cfg the new configuration for the daemon
580  * @param cb function called once the configuration was changed
581  * @param cb_cls closure for cb
582  */
583 void
584 GNUNET_TESTING_daemon_reconfigure (struct GNUNET_TESTING_Daemon *d,
585                                    struct GNUNET_CONFIGURATION_Handle *cfg,
586                                    GNUNET_TESTING_NotifyCompletion cb,
587                                    void *cb_cls)
588 {
589   char *arg;
590
591   if (d->phase != SP_START_DONE)
592     {
593       if (NULL != cb)
594         cb (cb_cls,
595             _
596             ("Peer not yet running, can not change configuration at this point."));
597       return;
598     }
599
600   /* 1) write configuration to temporary file */
601   if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, d->cfgfile))
602     {
603       if (NULL != cb)
604         cb (cb_cls, _("Failed to write new configuration to disk."));
605       return;
606     }
607
608   /* 2) copy file to remote host (if necessary) */
609   if (NULL == d->hostname)
610     {
611       /* signal success */
612       if (NULL != cb)
613         cb (cb_cls, NULL);
614       return;
615     }
616 #if DEBUG_TESTING
617   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
618               "Copying updated configuration file to remote host `%s'.\n",
619               d->hostname);
620 #endif
621   d->phase = SP_CONFIG_UPDATE;
622   if (NULL != d->username)
623     GNUNET_asprintf (&arg, "%s@%s:%s", d->username, d->hostname, d->cfgfile);
624   else
625     GNUNET_asprintf (&arg, "%s:%s", d->hostname, d->cfgfile);
626   d->pid = GNUNET_OS_start_process (NULL, NULL, "scp", "scp", d->cfgfile, arg, NULL);
627   GNUNET_free (arg);
628   if (-1 == d->pid)
629     {
630       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
631                   _
632                   ("Could not start `%s' process to copy configuration file.\n"),
633                   "scp");
634       if (NULL != cb)
635         cb (cb_cls, _("Failed to copy new configuration to remote machine."));
636       d->phase = SP_START_DONE;
637       return;
638     }
639   d->update_cb = cb;
640   d->update_cb_cls = cb_cls;
641   d->task
642     = GNUNET_SCHEDULER_add_delayed (d->sched,
643                                     GNUNET_CONSTANTS_EXEC_WAIT,
644                                     &start_fsm, d);
645 }
646
647
648 /**
649  * Data kept for each pair of peers that we try
650  * to connect.
651  */
652 struct ConnectContext
653 {
654   /**
655    * Testing handle to the first daemon.
656    */
657   struct GNUNET_TESTING_Daemon *d1;
658
659   /**
660    * Handle to core of first daemon (to check connect)
661    */
662   struct GNUNET_CORE_Handle * d1core;
663
664   /**
665    * Testing handle to the second daemon.
666    */
667   struct GNUNET_TESTING_Daemon *d2;
668
669   /**
670    * Handle to core of second daemon (to check connect)
671    */
672   struct GNUNET_CORE_Handle * d2core;
673
674   /**
675    * Transport handle to the first daemon.
676    */
677   struct GNUNET_TRANSPORT_Handle *d1th;
678
679   /**
680    * Transport handle to the second daemon.
681    */
682   struct GNUNET_TRANSPORT_Handle *d2th;
683
684   /**
685    * Function to call once we are done (or have timed out).
686    */
687   GNUNET_TESTING_NotifyConnection cb;
688
689   /**
690    * Closure for "nb".
691    */
692   void *cb_cls;
693
694   /**
695    * Transmit handle for our request for transmission
696    * (as given to d2 asking to talk to d1).
697    */
698   struct GNUNET_CORE_TransmitHandle *ntr;
699
700   /**
701    * When should this operation be complete (or we must trigger
702    * a timeout).
703    */
704   struct GNUNET_TIME_Absolute timeout;
705
706   /**
707    * Hello timeout task
708    */
709   GNUNET_SCHEDULER_TaskIdentifier hello_send_task;
710
711   /**
712    * Connect timeout task
713    */
714   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
715
716   /**
717    * When should this operation be complete (or we must trigger
718    * a timeout).
719    */
720   struct GNUNET_TIME_Relative timeout_hello;
721
722   /**
723    * The current hello message we have (for d1)
724    */
725   struct GNUNET_MessageHeader *hello;
726
727   /**
728    * Was the connection successful?
729    */
730   int connected;
731 };
732
733
734 /**
735  * Receive the HELLO from one peer, give it to the other
736  * and ask them to connect.
737  *
738  * @param cls "struct ConnectContext"
739  * @param message HELLO message of peer
740  */
741 static void
742 process_hello (void *cls, const struct GNUNET_MessageHeader *message)
743 {
744   struct ConnectContext *ctx = cls;
745
746 #if DEBUG_TESTING
747   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
748               "Received `%s' from transport service of `%4s'\n",
749               "HELLO", GNUNET_i2s (&ctx->d1->id));
750 #endif
751
752   GNUNET_assert (message != NULL);
753   GNUNET_free_non_null(ctx->hello);
754   ctx->hello = GNUNET_malloc(ntohs(message->size));
755   memcpy(ctx->hello, message, ntohs(message->size));
756
757 }
758
759
760 /**
761  * Notify callback about success or failure of the attempt
762  * to connect the two peers
763  *
764  * @param cls our "struct ConnectContext" (freed)
765  * @param tc reason tells us if we succeeded or failed
766  */
767 static void
768 notify_connect_result (void *cls,
769                        const struct GNUNET_SCHEDULER_TaskContext *tc)
770 {
771   struct ConnectContext *ctx = cls;
772
773   GNUNET_TRANSPORT_get_hello_cancel (ctx->d1th, &process_hello, ctx);
774   GNUNET_SCHEDULER_cancel(ctx->d1->sched, ctx->hello_send_task);
775
776   if (ctx->cb != NULL)
777     {
778       if (ctx->connected == GNUNET_NO)
779         {
780           ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, ctx->d1->cfg,
781                   ctx->d2->cfg, ctx->d1, ctx->d2,
782                   _("Peers failed to connect"));
783         }
784       else
785         {
786           ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, ctx->d1->cfg,
787                    ctx->d2->cfg, ctx->d1, ctx->d2, NULL);
788           GNUNET_SCHEDULER_cancel(ctx->d1->sched, ctx->timeout_task);
789         }
790     }
791
792   ctx->ntr = NULL;
793   GNUNET_TRANSPORT_disconnect (ctx->d1th);
794   ctx->d1th = NULL;
795   GNUNET_TRANSPORT_disconnect (ctx->d2th);
796   ctx->d2th = NULL;
797   GNUNET_CORE_disconnect (ctx->d1core);
798   ctx->d1core = NULL;
799   GNUNET_free_non_null (ctx->hello);
800   GNUNET_free (ctx);
801 }
802
803
804 /**
805  * Success, connection is up.  Signal client our success.
806  *
807  * @param cls our "struct ConnectContext"
808  * @param peer identity of the peer that has connected
809  * @param latency the round trip latency of the connection to this peer
810  * @param distance distance the transport level distance to this peer
811  *
812  */
813 static void
814 connect_notify (void *cls, const struct GNUNET_PeerIdentity * peer, struct GNUNET_TIME_Relative latency,
815                 uint32_t distance)
816 {
817   struct ConnectContext *ctx = cls;
818
819 #if DEBUG_TESTING
820   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
821               "Core notified us about connection to a peer\n");
822 #endif
823   if (memcmp(&ctx->d2->id, peer, sizeof(struct GNUNET_PeerIdentity)) == 0)
824     {
825 #if DEBUG_TESTING
826   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
827               "Core notified us about connection to peer %s\n", GNUNET_i2s(peer));
828 #endif
829       /*
830        * If we disconnect here, then the hello may never get sent (if it was delayed!)
831        * However I'm sure there was a reason it was here... so I'm just commenting.
832        */
833       ctx->connected = GNUNET_YES;
834       GNUNET_SCHEDULER_add_now (ctx->d1->sched,
835                                 &notify_connect_result,
836                                 ctx);
837     }
838
839 }
840
841 static void
842 send_hello(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
843 {
844   struct ConnectContext *ctx = cls;
845
846   if (ctx->hello != NULL)
847     {
848       GNUNET_TRANSPORT_offer_hello (ctx->d2th, ctx->hello);
849       ctx->timeout_hello = GNUNET_TIME_relative_add(ctx->timeout_hello,
850                                                     GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS,
851                                                                                   200));
852     }
853   ctx->hello_send_task = GNUNET_SCHEDULER_add_delayed(ctx->d1->sched,
854                                                       ctx->timeout_hello,
855                                                       &send_hello, ctx);
856 }
857
858
859 /**
860  * Establish a connection between two GNUnet daemons.
861  *
862  * @param d1 handle for the first daemon
863  * @param d2 handle for the second daemon
864  * @param timeout how long is the connection attempt
865  *        allowed to take?
866  * @param cb function to call at the end
867  * @param cb_cls closure for cb
868  */
869 void
870 GNUNET_TESTING_daemons_connect (struct GNUNET_TESTING_Daemon *d1,
871                                 struct GNUNET_TESTING_Daemon *d2,
872                                 struct GNUNET_TIME_Relative timeout,
873                                 GNUNET_TESTING_NotifyConnection cb,
874                                 void *cb_cls)
875 {
876   struct ConnectContext *ctx;
877   static struct GNUNET_CORE_MessageHandler no_handlers[] = { {NULL, 0, 0} };
878
879   if ((d1->server == NULL) || (d2->server == NULL))
880     {
881       if (NULL != cb)
882         cb (cb_cls, &d1->id, &d2->id, d1->cfg, d2->cfg, d1, d2,
883             _("Peers are not fully running yet, can not connect!\n"));
884       return;
885     }
886   ctx = GNUNET_malloc (sizeof (struct ConnectContext));
887   ctx->d1 = d1;
888   ctx->d2 = d2;
889   ctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
890   ctx->cb = cb;
891   ctx->cb_cls = cb_cls;
892   ctx->timeout_hello = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 400);
893   ctx->connected = GNUNET_NO;
894 #if DEBUG_TESTING
895   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
896               "Asked to connect peer %s to peer %s\n",
897               d1->shortname, d2->shortname);
898   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
899               "Connecting to transport service of peer %s\n", d1->shortname);
900 #endif
901
902   ctx->d1core = GNUNET_CORE_connect (d1->sched,
903                                      d1->cfg,
904                                      timeout,
905                                      ctx,
906                                      NULL,
907                                      &connect_notify, NULL,
908                                      NULL, GNUNET_NO,
909                                      NULL, GNUNET_NO, no_handlers);
910   if (ctx->d1core == NULL)
911     {
912       GNUNET_free (ctx);
913       if (NULL != cb)
914         cb (cb_cls, &d1->id, &d2->id, d1->cfg, d2->cfg, d1, d2,
915             _("Failed to connect to core service of first peer!\n"));
916       return;
917     }
918
919   ctx->d1th = GNUNET_TRANSPORT_connect (d1->sched,
920                                         d1->cfg, d1, NULL, NULL, NULL);
921   if (ctx->d1th == NULL)
922     {
923       GNUNET_free (ctx);
924       if (NULL != cb)
925         cb (cb_cls, &d1->id, &d2->id, d1->cfg, d2->cfg, d1, d2,
926             _("Failed to connect to transport service!\n"));
927       return;
928     }
929 #if DEBUG_TESTING
930   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
931               "Asked to connect peer %s to peer %s\n",
932               d1->shortname, d2->shortname);
933   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
934               "Connecting to transport service of peer %s\n", d2->shortname);
935
936 #endif
937
938   ctx->d2th = GNUNET_TRANSPORT_connect (d2->sched,
939                                         d2->cfg, d2, NULL, NULL, NULL);
940   if (ctx->d2th == NULL)
941     {
942       GNUNET_TRANSPORT_disconnect (ctx->d1th);
943       GNUNET_free (ctx);
944       if (NULL != cb)
945         cb (cb_cls, &d1->id, &d2->id, d1->cfg, d2->cfg, d1, d2,
946             _("Failed to connect to transport service!\n"));
947       return;
948     }
949
950 #if DEBUG_TESTING
951   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
952               "Asking for HELLO from peer %s\n", GNUNET_i2s (&d1->id));
953 #endif
954
955   ctx->timeout_task = GNUNET_SCHEDULER_add_delayed (d1->sched,
956                                                     timeout,
957                                                     &notify_connect_result, ctx);
958
959   GNUNET_TRANSPORT_get_hello (ctx->d1th, &process_hello, ctx);
960   ctx->hello_send_task = GNUNET_SCHEDULER_add_delayed(ctx->d1->sched, ctx->timeout_hello,
961                                                       &send_hello, ctx);
962 }
963
964
965 /* end of testing.c */