-support truncated HELLO names to work with 99-character per filename limitations...
[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_EXTRA_LOGGING
41
42 #define DEBUG_TESTING_RECONNECT GNUNET_EXTRA_LOGGING
43
44 /**
45  * Hack to deal with initial HELLO's being often devoid of addresses.
46  * This hack causes 'process_hello' to ignore HELLOs without addresses.
47  * The correct implementation would continue with 'process_hello' until
48  * the connection could be established...
49  */
50 #define EMPTY_HACK GNUNET_YES
51
52 /**
53  * How long do we wait after starting gnunet-service-arm
54  * for the core service to be alive?
55  */
56 #define ARM_START_WAIT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120)
57
58 /**
59  * How many times are we willing to try to wait for "scp" or
60  * "gnunet-service-arm" to complete (waitpid) before giving up?
61  */
62 #define MAX_EXEC_WAIT_RUNS 250
63
64 static struct GNUNET_CORE_MessageHandler no_handlers[] = { {NULL, 0, 0} };
65
66 #if EMPTY_HACK
67 static int
68 test_address (void *cls, const struct GNUNET_HELLO_Address *address,
69               struct GNUNET_TIME_Absolute expiration)
70 {
71   int *empty = cls;
72
73   *empty = GNUNET_NO;
74   return GNUNET_OK;
75 }
76 #endif
77
78 /**
79  * Receive the HELLO from one peer, give it to the other
80  * and ask them to connect.
81  *
82  * @param cls Closure (daemon whose hello is this).
83  * @param message HELLO message of peer
84  */
85 static void
86 process_hello (void *cls, const struct GNUNET_MessageHeader *message)
87 {
88   struct GNUNET_TESTING_Daemon *daemon = cls;
89   int msize;
90
91 #if EMPTY_HACK
92   int empty;
93
94   empty = GNUNET_YES;
95   GNUNET_assert (message != NULL);
96   GNUNET_HELLO_iterate_addresses ((const struct GNUNET_HELLO_Message *) message,
97                                   GNUNET_NO, &test_address, &empty);
98   if (GNUNET_YES == empty)
99   {
100     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
101                 "Skipping empty HELLO address of peer %s\n",
102                 GNUNET_i2s (&daemon->id));
103     return;
104   }
105 #endif
106   GNUNET_assert (daemon->phase == SP_GET_HELLO ||
107                  daemon->phase == SP_START_DONE);
108   daemon->cb = NULL;            // FIXME: why??? (see fsm:SP_START_CORE, notify_daemon_started)
109   if (daemon->task != GNUNET_SCHEDULER_NO_TASK) /* Assertion here instead? */
110     GNUNET_SCHEDULER_cancel (daemon->task);
111
112   if (daemon->server != NULL)
113   {
114     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
115                 "Received `%s' from transport service of `%4s', disconnecting core!\n",
116                 "HELLO", GNUNET_i2s (&daemon->id));
117     GNUNET_CORE_disconnect (daemon->server);
118     daemon->server = NULL;
119   }
120
121   msize = ntohs (message->size);
122   if (msize < 1)
123   {
124     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
125                 "HELLO message of peer %s is of size 0\n",
126                 GNUNET_i2s (&daemon->id));
127     return;
128   }
129   if (daemon->ghh != NULL)
130   {
131     GNUNET_TRANSPORT_get_hello_cancel (daemon->ghh);
132     daemon->ghh = NULL;
133   }
134   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
135               "Received `%s' from transport service of `%4s'\n", "HELLO",
136               GNUNET_i2s (&daemon->id));
137   GNUNET_free_non_null (daemon->hello);
138   daemon->hello = GNUNET_malloc (msize);
139   memcpy (daemon->hello, message, msize);
140
141   if (daemon->th != NULL)
142   {
143     GNUNET_TRANSPORT_disconnect (daemon->th);
144     daemon->th = NULL;
145   }
146   daemon->phase = SP_START_DONE;
147 }
148
149
150 /**
151  * Notify of a peer being up and running.  Scheduled as a task
152  * so that variables which may need to be set are set before
153  * the connect callback can set up new operations.
154  * FIXME: what variables?????? where from????
155  *
156  * @param cls the testing daemon
157  * @param tc task scheduler context
158  */
159 static void
160 notify_daemon_started (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
161 {
162   struct GNUNET_TESTING_Daemon *d = cls;
163   GNUNET_TESTING_NotifyDaemonRunning cb;
164
165   cb = d->cb;
166   d->cb = NULL;
167   if (NULL != cb)
168     cb (d->cb_cls, &d->id, d->cfg, d, NULL);
169 }
170
171
172 /**
173  * Finite-state machine for starting GNUnet.
174  *
175  * @param cls our "struct GNUNET_TESTING_Daemon"
176  * @param tc unused
177  */
178 static void
179 start_fsm (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
180 {
181   struct GNUNET_TESTING_Daemon *d = cls;
182   GNUNET_TESTING_NotifyDaemonRunning cb;
183   enum GNUNET_OS_ProcessStatusType type;
184   unsigned long code;
185   char *dst;
186   int bytes_read;
187
188   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer %s FSM is in phase %u.\n",
189               GNUNET_i2s (&d->id), d->phase);
190   d->task = GNUNET_SCHEDULER_NO_TASK;
191   switch (d->phase)
192   {
193   case SP_COPYING:
194     /* confirm copying complete */
195     if (GNUNET_OK != GNUNET_OS_process_status (d->proc_arm_copying, &type, &code))
196     {
197       if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0)
198       {
199         cb = d->cb;
200         d->cb = NULL;
201         if (NULL != cb)
202           cb (d->cb_cls, NULL, d->cfg, d,
203               _
204               ("`scp' does not seem to terminate (timeout copying config).\n"));
205         return;
206       }
207       /* wait some more */
208       d->task =
209           GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm,
210                                         d);
211       return;
212     }
213     if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0))
214     {
215       cb = d->cb;
216       d->cb = NULL;
217       if (NULL != cb)
218         cb (d->cb_cls, NULL, d->cfg, d, _("`scp' did not complete cleanly.\n"));
219       return;
220     }
221     GNUNET_OS_process_close (d->proc_arm_copying);
222     d->proc_arm_copying = NULL;
223     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
224                 "Successfully copied configuration file.\n");
225     d->phase = SP_COPIED;
226     /* fall-through */
227   case SP_COPIED:
228     /* Start create hostkey process if we don't already know the peer identity! */
229     if (GNUNET_NO == d->have_hostkey)
230     {
231       GNUNET_assert (NULL == d->proc_arm_peerinfo);
232       d->pipe_stdout = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO, GNUNET_YES);
233       if (d->pipe_stdout == NULL)
234       {
235         cb = d->cb;
236         d->cb = NULL;
237         if (NULL != cb)
238           cb (d->cb_cls, NULL, d->cfg, d,
239               (NULL ==
240                d->hostname) ?
241               _("Failed to create pipe for `gnunet-peerinfo' process.\n") :
242               _("Failed to create pipe for `ssh' process.\n"));
243         return;
244       }
245       if (NULL == d->hostname)
246       {
247         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
248                     "Starting `%s', with command `%s %s %s %s'.\n",
249                     "gnunet-peerinfo", "gnunet-peerinfo", "-c", d->cfgfile,
250                     "-sq");
251         d->proc_arm_peerinfo =
252             GNUNET_OS_start_process (GNUNET_YES, NULL, d->pipe_stdout, "gnunet-peerinfo",
253                                      "gnunet-peerinfo", "-c", d->cfgfile, "-sq",
254                                      NULL);
255         GNUNET_DISK_pipe_close_end (d->pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
256       }
257       else
258       {
259         if (d->username != NULL)
260           GNUNET_asprintf (&dst, "%s@%s", d->username, d->hostname);
261         else
262           dst = GNUNET_strdup (d->hostname);
263
264         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
265                     "Starting `%s', with command `%s %s %s %s %s %s'.\n",
266                     "gnunet-peerinfo", "ssh", dst, "gnunet-peerinfo", "-c",
267                     d->cfgfile, "-sq");
268         if (d->ssh_port_str == NULL)
269         {
270           d->proc_arm_peerinfo = GNUNET_OS_start_process (GNUNET_NO, NULL, d->pipe_stdout, "ssh", "ssh",
271 #if !DEBUG_TESTING
272                                              "-q",
273 #endif
274                                              dst, "gnunet-peerinfo", "-c",
275                                              d->cfgfile, "-sq", NULL);
276         }
277         else
278         {
279           d->proc_arm_peerinfo =
280               GNUNET_OS_start_process (GNUNET_NO, NULL, d->pipe_stdout, "ssh", "ssh", "-p",
281                                        d->ssh_port_str,
282 #if !DEBUG_TESTING
283                                        "-q",
284 #endif
285                                        dst, "gnunet-peerinfo", "-c", d->cfgfile,
286                                        "-sq", NULL);
287         }
288         GNUNET_DISK_pipe_close_end (d->pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
289         GNUNET_free (dst);
290       }
291       if (NULL == d->proc_arm_peerinfo)
292       {
293         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
294                     _("Could not start `%s' process to create hostkey.\n"),
295                     (NULL == d->hostname) ? "gnunet-peerinfo" : "ssh");
296         cb = d->cb;
297         d->cb = NULL;
298         if (NULL != cb)
299           cb (d->cb_cls, NULL, d->cfg, d,
300               (NULL ==
301                d->hostname) ? _("Failed to start `gnunet-peerinfo' process.\n")
302               : _("Failed to start `ssh' process.\n"));
303         GNUNET_DISK_pipe_close (d->pipe_stdout);
304         return;
305       }
306       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
307                   "Started `%s', waiting for hostkey.\n", "gnunet-peerinfo");
308       d->phase = SP_HOSTKEY_CREATE;
309       d->task =
310           GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
311                                           (d->max_timeout),
312                                           GNUNET_DISK_pipe_handle
313                                           (d->pipe_stdout,
314                                            GNUNET_DISK_PIPE_END_READ),
315                                           &start_fsm, d);
316     }
317     else                        /* Already have a hostkey! */
318     {
319       if (d->hostkey_callback != NULL)
320       {
321         d->hostkey_callback (d->hostkey_cls, &d->id, d, NULL);
322         d->hostkey_callback = NULL;
323         d->phase = SP_HOSTKEY_CREATED;
324       }
325       else
326         d->phase = SP_TOPOLOGY_SETUP;
327
328       /* wait some more */
329       d->task = GNUNET_SCHEDULER_add_now (&start_fsm, d);
330     }
331     break;
332   case SP_HOSTKEY_CREATE:
333     bytes_read =
334         GNUNET_DISK_file_read (GNUNET_DISK_pipe_handle
335                                (d->pipe_stdout, GNUNET_DISK_PIPE_END_READ),
336                                &d->hostkeybuf[d->hostkeybufpos],
337                                sizeof (d->hostkeybuf) - d->hostkeybufpos);
338     if (bytes_read > 0)
339       d->hostkeybufpos += bytes_read;
340
341     if ((d->hostkeybufpos < 104) && (bytes_read > 0))
342     {
343       /* keep reading */
344       d->task =
345           GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
346                                           (d->max_timeout),
347                                           GNUNET_DISK_pipe_handle
348                                           (d->pipe_stdout,
349                                            GNUNET_DISK_PIPE_END_READ),
350                                           &start_fsm, d);
351       return;
352     }
353     d->hostkeybuf[103] = '\0';
354
355     if ((bytes_read < 0) ||
356         (GNUNET_OK !=
357          GNUNET_CRYPTO_hash_from_string (d->hostkeybuf, &d->id.hashPubKey)))
358     {
359       /* error */
360       if (bytes_read < 0)
361         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
362                     _("Error reading from gnunet-peerinfo: %s\n"),
363                     STRERROR (errno));
364       else
365         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
366                     _("Malformed output from gnunet-peerinfo!\n"));
367       cb = d->cb;
368       d->cb = NULL;
369       GNUNET_DISK_pipe_close (d->pipe_stdout);
370       d->pipe_stdout = NULL;
371       (void) GNUNET_OS_process_kill (d->proc_arm_peerinfo, SIGKILL);      
372       GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (d->proc_arm_peerinfo));
373       GNUNET_OS_process_close (d->proc_arm_peerinfo);      
374       d->proc_arm_peerinfo = NULL;
375       if (NULL != cb)
376         cb (d->cb_cls, NULL, d->cfg, d, _("Failed to get hostkey!\n"));
377       return;
378     }
379     d->shortname = GNUNET_strdup (GNUNET_i2s (&d->id));
380     GNUNET_DISK_pipe_close (d->pipe_stdout);
381     d->pipe_stdout = NULL;
382     (void) GNUNET_OS_process_kill (d->proc_arm_peerinfo, SIGKILL);
383     GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (d->proc_arm_peerinfo));
384     GNUNET_OS_process_close (d->proc_arm_peerinfo);
385     d->proc_arm_peerinfo = NULL;
386     d->have_hostkey = GNUNET_YES;
387     if (d->hostkey_callback != NULL)
388     {
389       d->hostkey_callback (d->hostkey_cls, &d->id, d, NULL);
390       d->hostkey_callback = NULL;
391       d->phase = SP_HOSTKEY_CREATED;
392     }
393     else
394     {
395       d->phase = SP_TOPOLOGY_SETUP;
396     }
397     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Successfully got hostkey!\n");
398     /* Fall through */
399   case SP_HOSTKEY_CREATED:
400     /* wait for topology finished */
401     if ((GNUNET_YES == d->dead) ||
402         (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0))
403     {
404       cb = d->cb;
405       d->cb = NULL;
406       if (NULL != cb)
407         cb (d->cb_cls, NULL, d->cfg, d,
408             _("`Failed while waiting for topology setup!\n"));
409       return;
410     }
411
412     d->task =
413         GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm,
414                                       d);
415     break;
416   case SP_TOPOLOGY_SETUP:      /* Indicates topology setup has completed! */
417     /* start GNUnet on remote host */
418     if (NULL == d->hostname)
419     {
420       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
421                   "Starting `%s', with command `%s %s %s %s %s %s'.\n",
422                   "gnunet-arm", "gnunet-arm", "-c", d->cfgfile, "-L", "DEBUG",
423                   "-s");
424       d->proc_arm_start =
425           GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-arm", "gnunet-arm", "-c",
426                                    d->cfgfile,
427                                    "-L", "DEBUG",
428                                    "-s", "-q", "-T",
429                                    GNUNET_TIME_relative_to_string
430                                    (GNUNET_TIME_absolute_get_remaining
431                                     (d->max_timeout)), NULL);
432     }
433     else
434     {
435       if (d->username != NULL)
436         GNUNET_asprintf (&dst, "%s@%s", d->username, d->hostname);
437       else
438         dst = GNUNET_strdup (d->hostname);
439
440       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
441                   "Starting `%s', with command `%s %s %s %s %s %s %s %s'.\n",
442                   "gnunet-arm", "ssh", dst, "gnunet-arm", "-c", d->cfgfile,
443                   "-L", "DEBUG", "-s", "-q");
444       if (d->ssh_port_str == NULL)
445       {
446         d->proc_arm_start = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh",
447 #if !DEBUG_TESTING
448                                            "-q",
449 #endif
450                                            dst, "gnunet-arm",
451 #if DEBUG_TESTING
452                                            "-L", "DEBUG",
453 #endif
454                                            "-c", d->cfgfile, "-s", "-q", "-T",
455                                            GNUNET_TIME_relative_to_string
456                                            (GNUNET_TIME_absolute_get_remaining
457                                             (d->max_timeout)), NULL);
458       }
459       else
460       {
461
462         d->proc_arm_start =
463             GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh", "-p",
464                                      d->ssh_port_str,
465 #if !DEBUG_TESTING
466                                      "-q",
467 #endif
468                                      dst, "gnunet-arm",
469 #if DEBUG_TESTING
470                                      "-L", "DEBUG",
471 #endif
472                                      "-c", d->cfgfile, "-s", "-q", "-T",
473                                      GNUNET_TIME_relative_to_string
474                                      (GNUNET_TIME_absolute_get_remaining
475                                       (d->max_timeout)), NULL);
476       }
477       GNUNET_free (dst);
478     }
479     if (NULL == d->proc_arm_start)
480     {
481       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
482                   _("Could not start `%s' process to start GNUnet.\n"),
483                   (NULL == d->hostname) ? "gnunet-arm" : "ssh");
484       cb = d->cb;
485       d->cb = NULL;
486       if (NULL != cb)
487         cb (d->cb_cls, NULL, d->cfg, d,
488             (NULL ==
489              d->hostname) ? _("Failed to start `gnunet-arm' process.\n") :
490             _("Failed to start `ssh' process.\n"));
491       return;
492     }
493     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
494                 "Started `%s', waiting for `%s' to be up.\n", "gnunet-arm",
495                 "gnunet-service-core");
496     d->phase = SP_START_ARMING;
497     d->task =
498         GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm,
499                                       d);
500     break;
501   case SP_START_ARMING:
502     if (GNUNET_OK != GNUNET_OS_process_status (d->proc_arm_start, &type, &code))
503     {
504       if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0)
505       {
506         cb = d->cb;
507         d->cb = NULL;
508         if (NULL != cb)
509           cb (d->cb_cls, NULL, d->cfg, d,
510               (NULL ==
511                d->hostname) ? _("`gnunet-arm' does not seem to terminate.\n") :
512               _("`ssh' does not seem to terminate.\n"));
513         if (d->cfg != NULL)
514         {
515           GNUNET_CONFIGURATION_destroy (d->cfg);
516           d->cfg = NULL;
517         }
518         if (d->cfgfile != NULL)
519         {
520           GNUNET_free (d->cfgfile);
521           d->cfgfile = NULL;
522         }
523         GNUNET_free_non_null (d->hostname);
524         GNUNET_free_non_null (d->username);
525         GNUNET_free (d->proc_arm_start);
526         d->proc_arm_start = NULL;
527         d->hostname = NULL;     // Quick hack to avoid crashing (testing need to be
528         d->cfg = NULL;          // overhauled anyway, and the error managing is
529         // GNUNET_free (d); // FIXME (could this leak)
530         // pretty broken anyway.
531         return;
532       }
533       /* wait some more */
534       d->task =
535           GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm,
536                                         d);
537       return;
538     }
539     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Successfully started `%s'.\n",
540                 "gnunet-arm");
541     GNUNET_free (d->proc_arm_start);
542     d->proc_arm_start = NULL;
543     d->phase = SP_START_CORE;
544     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Calling CORE_connect\n");
545     /* Fall through */
546   case SP_START_CORE:
547     if (d->server != NULL)
548       GNUNET_CORE_disconnect (d->server);
549
550     d->th = GNUNET_TRANSPORT_connect (d->cfg, &d->id, d, NULL, NULL, NULL);
551     if (d->th == NULL)
552     {
553       if (GNUNET_YES == d->dead)
554         GNUNET_TESTING_daemon_stop (d,
555                                     GNUNET_TIME_absolute_get_remaining
556                                     (d->max_timeout), d->dead_cb,
557                                     d->dead_cb_cls, GNUNET_YES, GNUNET_NO);
558       else if (NULL != d->cb)
559         d->cb (d->cb_cls, &d->id, d->cfg, d,
560                _("Failed to connect to transport service!\n"));
561       return;
562     }
563     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
564                 "Connected to transport service `%s', getting HELLO\n",
565                 GNUNET_i2s (&d->id));
566     d->ghh = GNUNET_TRANSPORT_get_hello (d->th, &process_hello, d);
567     /* FIXME: store task ID somewhere! */
568     GNUNET_SCHEDULER_add_now (&notify_daemon_started, d);
569     /*cb = d->cb;
570      * d->cb = NULL;
571      * if (NULL != cb)
572      * cb (d->cb_cls, &d->id, d->cfg, d, NULL); */
573     d->running = GNUNET_YES;
574     d->phase = SP_GET_HELLO;
575     break;
576   case SP_GET_HELLO:
577     if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0)
578     {
579       if (d->server != NULL)
580         GNUNET_CORE_disconnect (d->server);
581       if (d->th != NULL)
582         GNUNET_TRANSPORT_disconnect (d->th);
583       cb = d->cb;
584       d->cb = NULL;
585       if (NULL != cb)
586         cb (d->cb_cls, NULL, d->cfg, d, _("Unable to get HELLO for peer!\n"));
587       GNUNET_CONFIGURATION_destroy (d->cfg);
588       GNUNET_free (d->cfgfile);
589       GNUNET_free_non_null (d->hostname);
590       GNUNET_free_non_null (d->username);
591       GNUNET_free (d);
592       return;
593     }
594     if (d->hello != NULL)
595       return;
596     GNUNET_assert (d->task == GNUNET_SCHEDULER_NO_TASK);
597     d->task =
598         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
599                                       (GNUNET_CONSTANTS_SERVICE_RETRY, 2),
600                                       &start_fsm, d);
601     break;
602   case SP_START_DONE:
603     GNUNET_break (0);
604     break;
605   case SP_SERVICE_START:
606     /* confirm gnunet-arm exited */
607     if (GNUNET_OK != GNUNET_OS_process_status (d->proc_arm_srv_start, &type, &code))
608     {
609       if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0)
610       {
611         cb = d->cb;
612         d->cb = NULL;
613         if (NULL != cb)
614           cb (d->cb_cls, NULL, d->cfg, d,
615               (NULL ==
616                d->hostname) ? _("`gnunet-arm' does not seem to terminate.\n") :
617               _("`ssh' does not seem to terminate.\n"));
618         return;
619       }
620       /* wait some more */
621       d->task =
622           GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm,
623                                         d);
624       return;
625     }
626 #if EXTRA_CHECKS
627     if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0))
628     {
629       cb = d->cb;
630       d->cb = NULL;
631       if (NULL != cb)
632         cb (d->cb_cls, NULL, d->cfg, d,
633             (NULL ==
634              d->hostname) ?
635             _
636             ("`gnunet-arm' terminated with non-zero exit status (or timed out)!\n")
637             : _("`ssh' does not seem to terminate.\n"));
638       return;
639     }
640 #endif
641     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service startup complete!\n");
642     cb = d->cb;
643     d->cb = NULL;
644     d->phase = SP_START_DONE;
645     if (NULL != cb)
646       cb (d->cb_cls, &d->id, d->cfg, d, NULL);
647     break;
648   case SP_SERVICE_SHUTDOWN_START:
649     /* confirm copying complete */
650     if (GNUNET_OK != GNUNET_OS_process_status (d->proc_arm_srv_stop, &type, &code))
651     {
652       if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0)
653       {
654         if (NULL != d->dead_cb)
655           d->dead_cb (d->dead_cb_cls,
656                       _
657                       ("either `gnunet-arm' or `ssh' does not seem to terminate.\n"));
658         return;
659       }
660       /* wait some more */
661       d->task =
662           GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm,
663                                         d);
664       return;
665     }
666 #if EXTRA_CHECKS
667     if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0))
668     {
669       if (NULL != d->dead_cb)
670         d->dead_cb (d->dead_cb_cls,
671                     _
672                     ("shutdown (either `gnunet-arm' or `ssh') did not complete cleanly.\n"));
673       return;
674     }
675 #endif
676     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service shutdown complete.\n");
677     if (NULL != d->dead_cb)
678       d->dead_cb (d->dead_cb_cls, NULL);
679     break;
680   case SP_SHUTDOWN_START:
681     /* confirm copying complete !??? */
682     if (GNUNET_OK != GNUNET_OS_process_status (d->proc_arm_stop, &type, &code))
683     {
684       if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0)
685       {
686         if (NULL != d->dead_cb)
687           d->dead_cb (d->dead_cb_cls,
688                       _
689                       ("either `gnunet-arm' or `ssh' does not seem to terminate.\n"));
690         if (d->th != NULL)
691         {
692           GNUNET_TRANSPORT_get_hello_cancel (d->ghh);
693           d->ghh = NULL;
694           GNUNET_TRANSPORT_disconnect (d->th);
695           d->th = NULL;
696         }
697         if (d->cfg != NULL)
698         {
699           GNUNET_CONFIGURATION_destroy (d->cfg);
700           d->cfg = NULL;
701         }
702         if (d->cfgfile != NULL)
703         {
704           GNUNET_free (d->cfgfile);
705           d->cfgfile = NULL;
706         }
707         GNUNET_free_non_null (d->hello);
708         GNUNET_free_non_null (d->hostname);
709         GNUNET_free_non_null (d->username);
710         GNUNET_free_non_null (d->shortname);
711         // GNUNET_free_non_null (d->proc); // !? FIXME
712         // d->proc_arm_stop = NULL; 
713         GNUNET_free (d);
714         return;
715       }
716       /* wait some more */
717       d->task =
718           GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm,
719                                         d);
720       return;
721     }
722     if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0))
723     {
724       if (NULL != d->dead_cb)
725         d->dead_cb (d->dead_cb_cls,
726                     _
727                     ("shutdown (either `gnunet-arm' or `ssh') did not complete cleanly.\n"));
728       if (d->th != NULL)
729       {
730         GNUNET_TRANSPORT_get_hello_cancel (d->ghh);
731         d->ghh = NULL;
732         GNUNET_TRANSPORT_disconnect (d->th);
733         d->th = NULL;
734       }
735       if (d->server != NULL)
736       {
737         GNUNET_CORE_disconnect (d->server);
738         d->server = NULL;
739       }
740       GNUNET_CONFIGURATION_destroy (d->cfg);
741       d->cfg = NULL;
742       GNUNET_free (d->cfgfile);
743       GNUNET_free_non_null (d->hello);
744       GNUNET_free_non_null (d->hostname);
745       GNUNET_free_non_null (d->username);
746       GNUNET_free_non_null (d->shortname);
747       // GNUNET_free_non_null (d->proc); // !? FIXME!
748       // d->proc = NULL; // !? FIXME!
749       GNUNET_free (d);
750       return;
751     }
752     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer shutdown complete.\n");
753     if (d->server != NULL)
754     {
755       GNUNET_CORE_disconnect (d->server);
756       d->server = NULL;
757     }
758
759     if (d->th != NULL)
760     {
761       GNUNET_TRANSPORT_get_hello_cancel (d->ghh);
762       d->ghh = NULL;
763       GNUNET_TRANSPORT_disconnect (d->th);
764       d->th = NULL;
765     }
766
767     if (NULL != d->dead_cb)
768       d->dead_cb (d->dead_cb_cls, NULL);
769
770     /* state clean up and notifications */
771     if (d->churn == GNUNET_NO)
772     {
773       GNUNET_CONFIGURATION_destroy (d->cfg);
774       d->cfg = NULL;
775       GNUNET_free (d->cfgfile);
776       GNUNET_free_non_null (d->hostname);
777       GNUNET_free_non_null (d->username);
778     }
779
780     GNUNET_free_non_null (d->hello);
781     d->hello = NULL;
782     GNUNET_free_non_null (d->shortname);
783     // GNUNET_free_non_null (d->proc); // !? FIXME
784     // d->proc = NULL; // !? FIXME!
785     d->shortname = NULL;
786     if (d->churn == GNUNET_NO)
787       GNUNET_free (d);
788
789     break;
790   case SP_CONFIG_UPDATE:
791     /* confirm copying complete */
792     if (GNUNET_OK != GNUNET_OS_process_status (d->proc_arm_copying, &type, &code))
793     {
794       if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0)   /* FIXME: config update should take timeout parameter! */
795       {
796         cb = d->cb;
797         d->cb = NULL;
798         if (NULL != cb)
799           cb (d->cb_cls, NULL, d->cfg, d,
800               _("`scp' does not seem to terminate.\n"));
801         return;
802       }
803       /* wait some more */
804       d->task =
805           GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm,
806                                         d);
807       return;
808     }
809     if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0))
810     {
811       if (NULL != d->update_cb)
812         d->update_cb (d->update_cb_cls, _("`scp' did not complete cleanly.\n"));
813       return;
814     }
815     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
816                 "Successfully copied configuration file.\n");
817     if (NULL != d->update_cb)
818       d->update_cb (d->update_cb_cls, NULL);
819     d->phase = SP_START_DONE;
820     break;
821   }
822 }
823
824 /**
825  * Continues GNUnet daemon startup when user wanted to be notified
826  * once a hostkey was generated (for creating friends files, blacklists,
827  * etc.).
828  *
829  * @param daemon the daemon to finish starting
830  */
831 void
832 GNUNET_TESTING_daemon_continue_startup (struct GNUNET_TESTING_Daemon *daemon)
833 {
834   GNUNET_assert (daemon->phase == SP_HOSTKEY_CREATED);
835   daemon->phase = SP_TOPOLOGY_SETUP;
836 }
837
838 /**
839  * Check whether the given daemon is running.
840  *
841  * @param daemon the daemon to check
842  *
843  * @return GNUNET_YES if the daemon is up, GNUNET_NO if the
844  *         daemon is down, GNUNET_SYSERR on error.
845  */
846 int
847 GNUNET_TESTING_test_daemon_running (struct GNUNET_TESTING_Daemon *daemon)
848 {
849   if (daemon == NULL)
850     return GNUNET_SYSERR;
851
852   if (daemon->running == GNUNET_YES)
853     return GNUNET_YES;
854   return GNUNET_NO;
855 }
856
857
858 /**
859  * Starts a GNUnet daemon service which has been previously stopped.
860  *
861  * @param d the daemon for which the service should be started
862  * @param service the name of the service to start
863  * @param timeout how long to wait for process for shutdown to complete
864  * @param cb function called once the service starts
865  * @param cb_cls closure for cb
866  */
867 void
868 GNUNET_TESTING_daemon_start_stopped_service (struct GNUNET_TESTING_Daemon *d,
869                                              char *service,
870                                              struct GNUNET_TIME_Relative
871                                              timeout,
872                                              GNUNET_TESTING_NotifyDaemonRunning
873                                              cb, void *cb_cls)
874 {
875   char *arg;
876
877   d->cb = cb;
878   d->cb_cls = cb_cls;
879
880   GNUNET_assert (d->running == GNUNET_YES);
881
882   if (d->phase == SP_CONFIG_UPDATE)
883   {
884     GNUNET_SCHEDULER_cancel (d->task);
885     d->phase = SP_START_DONE;
886   }
887
888   if (d->churned_services == NULL)
889   {
890     d->cb (d->cb_cls, &d->id, d->cfg, d,
891            "No service has been churned off yet!!");
892     return;
893   }
894   d->phase = SP_SERVICE_START;
895   GNUNET_free (d->churned_services);
896   d->churned_services = NULL;
897   d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
898   /* Check if this is a local or remote process */
899   if (NULL != d->hostname)
900   {
901     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
902                 "Starting gnunet-arm with config `%s' on host `%s'.\n",
903                 d->cfgfile, d->hostname);
904     if (d->username != NULL)
905       GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname);
906     else
907       arg = GNUNET_strdup (d->hostname);
908
909     d->proc_arm_srv_start = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh",
910 #if !DEBUG_TESTING
911                                        "-q",
912 #endif
913                                        arg, "gnunet-arm",
914 #if DEBUG_TESTING
915                                        "-L", "DEBUG",
916 #endif
917                                        "-c", d->cfgfile, "-i", service, "-q",
918                                        "-T",
919                                        GNUNET_TIME_relative_to_string (timeout),
920                                        NULL);
921     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
922                 "Starting gnunet-arm with command ssh %s gnunet-arm -c %s -i %s -q\n",
923                 arg, "gnunet-arm", d->cfgfile, service);
924     GNUNET_free (arg);
925   }
926   else
927   {
928     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
929                 "Starting gnunet-arm with config `%s' locally.\n", d->cfgfile);
930     d->proc_arm_srv_start = GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-arm", "gnunet-arm",
931 #if DEBUG_TESTING
932                                        "-L", "DEBUG",
933 #endif
934                                        "-c", d->cfgfile, "-i", service, "-q",
935                                        "-T",
936                                        GNUNET_TIME_relative_to_string (timeout),
937                                        NULL);
938   }
939
940   d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
941   d->task = GNUNET_SCHEDULER_add_now (&start_fsm, d);
942 }
943
944 /**
945  * Starts a GNUnet daemon's service.
946  *
947  * @param d the daemon for which the service should be started
948  * @param service the name of the service to start
949  * @param timeout how long to wait for process for startup
950  * @param cb function called once gnunet-arm returns
951  * @param cb_cls closure for cb
952  */
953 void
954 GNUNET_TESTING_daemon_start_service (struct GNUNET_TESTING_Daemon *d,
955                                      const char *service,
956                                      struct GNUNET_TIME_Relative timeout,
957                                      GNUNET_TESTING_NotifyDaemonRunning cb,
958                                      void *cb_cls)
959 {
960   char *arg;
961
962   d->cb = cb;
963   d->cb_cls = cb_cls;
964
965   GNUNET_assert (service != NULL);
966   GNUNET_assert (d->running == GNUNET_YES);
967   GNUNET_assert (d->phase == SP_START_DONE);
968
969   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
970               _("Starting service %s for peer `%4s'\n"), service,
971               GNUNET_i2s (&d->id));
972   d->phase = SP_SERVICE_START;
973   d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
974   /* Check if this is a local or remote process */
975   if (NULL != d->hostname)
976   {
977     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
978                 "Starting gnunet-arm with config `%s' on host `%s'.\n",
979                 d->cfgfile, d->hostname);
980     if (d->username != NULL)
981       GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname);
982     else
983       arg = GNUNET_strdup (d->hostname);
984
985     d->proc_arm_srv_start = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh",
986 #if !DEBUG_TESTING
987                                        "-q",
988 #endif
989                                        arg, "gnunet-arm",
990 #if DEBUG_TESTING
991                                        "-L", "DEBUG",
992 #endif
993                                        "-c", d->cfgfile, "-i", service, "-q",
994                                        "-T",
995                                        GNUNET_TIME_relative_to_string (timeout),
996                                        NULL);
997     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
998                 "Starting gnunet-arm with command ssh %s gnunet-arm -c %s -i %s -q -T %s\n",
999                 arg, "gnunet-arm", d->cfgfile, service,
1000                 GNUNET_TIME_relative_to_string (timeout));
1001     GNUNET_free (arg);
1002   }
1003   else
1004   {
1005     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1006                 "Starting gnunet-arm with config `%s' locally.\n", d->cfgfile);
1007     d->proc_arm_srv_start = GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-arm", "gnunet-arm",
1008 #if DEBUG_TESTING
1009                                        "-L", "DEBUG",
1010 #endif
1011                                        "-c", d->cfgfile, "-i", service, "-q",
1012                                        "-T",
1013                                        GNUNET_TIME_relative_to_string (timeout),
1014                                        NULL);
1015     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1016                 "Starting gnunet-arm with command %s -c %s -i %s -q -T %s\n",
1017                 "gnunet-arm", d->cfgfile, service,
1018                 GNUNET_TIME_relative_to_string (timeout));
1019   }
1020
1021   d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
1022   d->task = GNUNET_SCHEDULER_add_now (&start_fsm, d);
1023 }
1024
1025 /**
1026  * Start a peer that has previously been stopped using the daemon_stop
1027  * call (and files weren't deleted and the allow restart flag)
1028  *
1029  * @param daemon the daemon to start (has been previously stopped)
1030  * @param timeout how long to wait for restart
1031  * @param cb the callback for notification when the peer is running
1032  * @param cb_cls closure for the callback
1033  */
1034 void
1035 GNUNET_TESTING_daemon_start_stopped (struct GNUNET_TESTING_Daemon *daemon,
1036                                      struct GNUNET_TIME_Relative timeout,
1037                                      GNUNET_TESTING_NotifyDaemonRunning cb,
1038                                      void *cb_cls)
1039 {
1040   if (daemon->running == GNUNET_YES)
1041   {
1042     cb (cb_cls, &daemon->id, daemon->cfg, daemon,
1043         "Daemon already running, can't restart!");
1044     return;
1045   }
1046
1047   daemon->cb = cb;
1048   daemon->cb_cls = cb_cls;
1049   daemon->phase = SP_TOPOLOGY_SETUP;
1050   daemon->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
1051   /* FIXME: why add_continuation? */
1052   GNUNET_SCHEDULER_add_continuation (&start_fsm, daemon,
1053                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1054 }
1055
1056 /**
1057  * Starts a GNUnet daemon.  GNUnet must be installed on the target
1058  * system and available in the PATH.  The machine must furthermore be
1059  * reachable via "ssh" (unless the hostname is "NULL") without the
1060  * need to enter a password.
1061  *
1062  * @param cfg configuration to use
1063  * @param timeout how long to wait starting up peers
1064  * @param pretend GNUNET_YES to set up files but not start peer GNUNET_NO
1065  *                to really start the peer (default)
1066  * @param hostname name of the machine where to run GNUnet
1067  *        (use NULL for localhost).
1068  * @param ssh_username ssh username to use when connecting to hostname
1069  * @param sshport port to pass to ssh process when connecting to hostname
1070  * @param hostkey pointer to a hostkey to be written to disk (instead of being generated)
1071  * @param hostkey_callback function to call once the hostkey has been
1072  *        generated for this peer, but it hasn't yet been started
1073  *        (NULL to start immediately, otherwise waits on GNUNET_TESTING_daemon_continue_start)
1074  * @param hostkey_cls closure for hostkey callback
1075  * @param cb function to call once peer is up, or failed to start
1076  * @param cb_cls closure for cb
1077  * @return handle to the daemon (actual start will be completed asynchronously)
1078  */
1079 struct GNUNET_TESTING_Daemon *
1080 GNUNET_TESTING_daemon_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
1081                              struct GNUNET_TIME_Relative timeout, int pretend,
1082                              const char *hostname, const char *ssh_username,
1083                              uint16_t sshport, const char *hostkey,
1084                              GNUNET_TESTING_NotifyHostkeyCreated
1085                              hostkey_callback, void *hostkey_cls,
1086                              GNUNET_TESTING_NotifyDaemonRunning cb,
1087                              void *cb_cls)
1088 {
1089   struct GNUNET_TESTING_Daemon *ret;
1090   char *arg;
1091   char *username;
1092   char *servicehome;
1093   char *baseservicehome;
1094   char *slash;
1095   char *hostkeyfile;
1096   char *temp_file_name;
1097   struct GNUNET_DISK_FileHandle *fn;
1098   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
1099   struct GNUNET_CRYPTO_RsaPrivateKey *private_key;
1100
1101   ret = GNUNET_malloc (sizeof (struct GNUNET_TESTING_Daemon));
1102   ret->hostname = (hostname == NULL) ? NULL : GNUNET_strdup (hostname);
1103   if (sshport != 0)
1104   {
1105     GNUNET_asprintf (&ret->ssh_port_str, "%d", sshport);
1106   }
1107   else
1108     ret->ssh_port_str = NULL;
1109
1110   /* Find service home and base service home directories, create it if it doesn't exist */
1111   GNUNET_assert (GNUNET_OK ==
1112                  GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS",
1113                                                         "SERVICEHOME",
1114                                                         &servicehome));
1115
1116   GNUNET_assert (GNUNET_OK == GNUNET_DISK_directory_create (servicehome));
1117   GNUNET_asprintf (&temp_file_name, "%s/gnunet-testing-config", servicehome);
1118   ret->cfgfile = GNUNET_DISK_mktemp (temp_file_name);
1119   GNUNET_free (temp_file_name);
1120   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1121               "Setting up peer with configuration file `%s'.\n", ret->cfgfile);
1122   if (NULL == ret->cfgfile)
1123   {
1124     GNUNET_free_non_null (ret->ssh_port_str);
1125     GNUNET_free_non_null (ret->hostname);
1126     GNUNET_free (ret);
1127     return NULL;
1128   }
1129   ret->hostkey_callback = hostkey_callback;
1130   ret->hostkey_cls = hostkey_cls;
1131   ret->cb = cb;
1132   ret->cb_cls = cb_cls;
1133   ret->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
1134   ret->cfg = GNUNET_CONFIGURATION_dup (cfg);
1135   GNUNET_CONFIGURATION_set_value_string (ret->cfg, "PATHS", "DEFAULTCONFIG",
1136                                          ret->cfgfile);
1137
1138   if (hostkey != NULL)          /* Get the peer identity from the hostkey */
1139   {
1140     private_key = GNUNET_CRYPTO_rsa_decode_key (hostkey, HOSTKEYFILESIZE);
1141     GNUNET_assert (private_key != NULL);
1142     GNUNET_CRYPTO_rsa_key_get_public (private_key, &public_key);
1143     GNUNET_CRYPTO_hash (&public_key,
1144                         sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1145                         &ret->id.hashPubKey);
1146     ret->shortname = GNUNET_strdup (GNUNET_i2s (&ret->id));
1147     ret->have_hostkey = GNUNET_YES;
1148     GNUNET_CRYPTO_rsa_key_free (private_key);
1149   }
1150
1151   /* Write hostkey to file, if we were given one */
1152   hostkeyfile = NULL;
1153   if (hostkey != NULL)
1154   {
1155     GNUNET_asprintf (&hostkeyfile, "%s/.hostkey", servicehome);
1156     fn = GNUNET_DISK_file_open (hostkeyfile,
1157                                 GNUNET_DISK_OPEN_READWRITE |
1158                                 GNUNET_DISK_OPEN_CREATE,
1159                                 GNUNET_DISK_PERM_USER_READ |
1160                                 GNUNET_DISK_PERM_USER_WRITE);
1161     GNUNET_assert (fn != NULL);
1162     GNUNET_assert (HOSTKEYFILESIZE ==
1163                    GNUNET_DISK_file_write (fn, hostkey, HOSTKEYFILESIZE));
1164     GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fn));
1165   }
1166
1167   /* write configuration to temporary file */
1168   if (GNUNET_OK != GNUNET_CONFIGURATION_write (ret->cfg, ret->cfgfile))
1169   {
1170     if (0 != UNLINK (ret->cfgfile))
1171       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink",
1172                                 ret->cfgfile);
1173     GNUNET_CONFIGURATION_destroy (ret->cfg);
1174     GNUNET_free_non_null (ret->hostname);
1175     GNUNET_free (ret->cfgfile);
1176     GNUNET_free (ret);
1177     return NULL;
1178   }
1179   if (ssh_username != NULL)
1180     username = GNUNET_strdup (ssh_username);
1181   if ((ssh_username == NULL) &&
1182       (GNUNET_OK !=
1183        GNUNET_CONFIGURATION_get_value_string (cfg, "TESTING", "USERNAME",
1184                                               &username)))
1185   {
1186     if (NULL != getenv ("USER"))
1187       username = GNUNET_strdup (getenv ("USER"));
1188     else
1189       username = NULL;
1190   }
1191   ret->username = username;
1192
1193   if (GNUNET_NO == pretend)     /* Copy files, enter finite state machine */
1194   {
1195     /* copy directory to remote host */
1196     if (NULL != hostname)
1197     {
1198       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1199                   "Copying configuration directory to host `%s'.\n", hostname);
1200       baseservicehome = GNUNET_strdup (servicehome);
1201       /* Remove trailing /'s */
1202       while (baseservicehome[strlen (baseservicehome) - 1] == '/')
1203         baseservicehome[strlen (baseservicehome) - 1] = '\0';
1204       /* Find next directory /, jump one ahead */
1205       slash = strrchr (baseservicehome, '/');
1206       if (slash != NULL)
1207         *(++slash) = '\0';
1208
1209       ret->phase = SP_COPYING;
1210       if (NULL != username)
1211         GNUNET_asprintf (&arg, "%s@%s:%s", username, hostname, baseservicehome);
1212       else
1213         GNUNET_asprintf (&arg, "%s:%s", hostname, baseservicehome);
1214       GNUNET_free (baseservicehome);
1215       if (ret->ssh_port_str == NULL)
1216       {
1217         ret->proc_arm_copying = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "scp", "scp", "-r",
1218 #if !DEBUG_TESTING
1219                                              "-q",
1220 #endif
1221                                              servicehome, arg, NULL);
1222         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1223                     "copying directory with command scp -r %s %s\n",
1224                     servicehome, arg);
1225       }
1226       else
1227       {
1228         ret->proc_arm_copying =
1229             GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "scp", "scp", "-r", "-P",
1230                                      ret->ssh_port_str,
1231 #if !DEBUG_TESTING
1232                                      "-q",
1233 #endif
1234                                      servicehome, arg, NULL);
1235       }
1236       GNUNET_free (arg);
1237       if (NULL == ret->proc_arm_copying)
1238       {
1239         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1240                     _
1241                     ("Could not start `%s' process to copy configuration directory.\n"),
1242                     "scp");
1243         if (0 != UNLINK (ret->cfgfile))
1244           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink",
1245                                     ret->cfgfile);
1246         GNUNET_CONFIGURATION_destroy (ret->cfg);
1247         GNUNET_free_non_null (ret->hostname);
1248         GNUNET_free_non_null (ret->username);
1249         GNUNET_free (ret->cfgfile);
1250         GNUNET_free (ret);
1251         if ((hostkey != NULL) && (0 != UNLINK (hostkeyfile)))
1252           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink",
1253                                     hostkeyfile);
1254         GNUNET_free_non_null (hostkeyfile);
1255         GNUNET_assert (GNUNET_OK == GNUNET_DISK_directory_remove (servicehome));
1256         GNUNET_free (servicehome);
1257         return NULL;
1258       }
1259
1260       ret->task =
1261           GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm,
1262                                         ret);
1263       GNUNET_free_non_null (hostkeyfile);
1264       GNUNET_free (servicehome);
1265       return ret;
1266     }
1267     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1268                 "No need to copy configuration file since we are running locally.\n");
1269     ret->phase = SP_COPIED;
1270     /* FIXME: why add_cont? */
1271     GNUNET_SCHEDULER_add_continuation (&start_fsm, ret,
1272                                        GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1273   }
1274   GNUNET_free_non_null (hostkeyfile);
1275   GNUNET_free (servicehome);
1276   return ret;
1277 }
1278
1279
1280 /**
1281  * Restart (stop and start) a GNUnet daemon.
1282  *
1283  * @param d the daemon that should be restarted
1284  * @param cb function called once the daemon is (re)started
1285  * @param cb_cls closure for cb
1286  */
1287 void
1288 GNUNET_TESTING_daemon_restart (struct GNUNET_TESTING_Daemon *d,
1289                                GNUNET_TESTING_NotifyDaemonRunning cb,
1290                                void *cb_cls)
1291 {
1292   char *arg;
1293   char *del_arg;
1294
1295   del_arg = NULL;
1296   if (NULL != d->cb)
1297   {
1298     d->dead = GNUNET_YES;
1299     return;
1300   }
1301
1302   d->cb = cb;
1303   d->cb_cls = cb_cls;
1304
1305   if (d->phase == SP_CONFIG_UPDATE)
1306   {
1307     GNUNET_SCHEDULER_cancel (d->task);
1308     d->phase = SP_START_DONE;
1309   }
1310   if (d->server != NULL)
1311   {
1312     GNUNET_CORE_disconnect (d->server);
1313     d->server = NULL;
1314   }
1315
1316   if (d->th != NULL)
1317   {
1318     GNUNET_TRANSPORT_get_hello_cancel (d->ghh);
1319     d->ghh = NULL;
1320     GNUNET_TRANSPORT_disconnect (d->th);
1321     d->th = NULL;
1322   }
1323   /* state clean up and notifications */
1324   GNUNET_free_non_null (d->hello);
1325
1326   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Terminating peer `%4s'\n"),
1327               GNUNET_i2s (&d->id));
1328   d->phase = SP_START_ARMING;
1329
1330   /* Check if this is a local or remote process */
1331   if (NULL != d->hostname)
1332   {
1333     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1334                 "Stopping gnunet-arm with config `%s' on host `%s'.\n",
1335                 d->cfgfile, d->hostname);
1336     if (d->username != NULL)
1337       GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname);
1338     else
1339       arg = GNUNET_strdup (d->hostname);
1340
1341     d->proc_arm_stop = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh",
1342 #if !DEBUG_TESTING
1343                                        "-q",
1344 #endif
1345                                        arg, "gnunet-arm",
1346 #if DEBUG_TESTING
1347                                        "-L", "DEBUG",
1348 #endif
1349                                        "-c", d->cfgfile, "-e", "-r", NULL);
1350     /* Use -r to restart arm and all services */
1351
1352     GNUNET_free (arg);
1353   }
1354   else
1355   {
1356     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1357                 "Stopping gnunet-arm with config `%s' locally.\n", d->cfgfile);
1358     d->proc_arm_stop = GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-arm", "gnunet-arm",
1359 #if DEBUG_TESTING
1360                                        "-L", "DEBUG",
1361 #endif
1362                                        "-c", d->cfgfile, "-e", "-r", NULL);
1363   }
1364
1365   GNUNET_free_non_null (del_arg);
1366   d->task =
1367       GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm, d);
1368
1369 }
1370
1371
1372 /**
1373  * Stops a GNUnet daemon.
1374  *
1375  * @param d the daemon that should be stopped
1376  * @param service the name of the service to stop
1377  * @param timeout how long to wait for process for shutdown to complete
1378  * @param cb function called once the daemon was stopped
1379  * @param cb_cls closure for cb
1380  */
1381 void
1382 GNUNET_TESTING_daemon_stop_service (struct GNUNET_TESTING_Daemon *d,
1383                                     const char *service,
1384                                     struct GNUNET_TIME_Relative timeout,
1385                                     GNUNET_TESTING_NotifyCompletion cb,
1386                                     void *cb_cls)
1387 {
1388   char *arg;
1389
1390   d->dead_cb = cb;
1391   d->dead_cb_cls = cb_cls;
1392
1393   GNUNET_assert (d->running == GNUNET_YES);
1394
1395   if (d->phase == SP_CONFIG_UPDATE)
1396   {
1397     GNUNET_SCHEDULER_cancel (d->task);
1398     d->phase = SP_START_DONE;
1399   }
1400
1401   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Terminating peer `%4s'\n"),
1402               GNUNET_i2s (&d->id));
1403   if (d->churned_services != NULL)
1404   {
1405     d->dead_cb (d->dead_cb_cls, "A service has already been turned off!!");
1406     return;
1407   }
1408   d->phase = SP_SERVICE_SHUTDOWN_START;
1409   d->churned_services = GNUNET_strdup (service);
1410   d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
1411   /* Check if this is a local or remote process */
1412   if (NULL != d->hostname)
1413   {
1414     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1415                 "Stopping gnunet-arm with config `%s' on host `%s'.\n",
1416                 d->cfgfile, d->hostname);
1417     if (d->username != NULL)
1418       GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname);
1419     else
1420       arg = GNUNET_strdup (d->hostname);
1421     
1422     d->proc_arm_srv_stop = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh",
1423 #if !DEBUG_TESTING
1424                                        "-q",
1425 #endif
1426                                        arg, "gnunet-arm",
1427 #if DEBUG_TESTING
1428                                        "-L", "DEBUG",
1429 #endif
1430                                        "-c", d->cfgfile, "-k", service, "-q",
1431                                        "-T",
1432                                        GNUNET_TIME_relative_to_string (timeout),
1433                                        NULL);
1434     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1435                 "Stopping gnunet-arm with command ssh %s gnunet-arm -c %s -k %s -q\n",
1436                 arg, "gnunet-arm", d->cfgfile, service);
1437     GNUNET_free (arg);
1438   }
1439   else
1440   {
1441     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1442                 "Stopping gnunet-arm with config `%s' locally.\n", d->cfgfile);
1443     d->proc_arm_srv_stop = GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-arm", "gnunet-arm",
1444 #if DEBUG_TESTING
1445                                        "-L", "DEBUG",
1446 #endif
1447                                        "-c", d->cfgfile, "-k", service, "-q",
1448                                        "-T",
1449                                        GNUNET_TIME_relative_to_string (timeout),
1450                                        NULL);
1451   }
1452
1453   d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
1454   d->task = GNUNET_SCHEDULER_add_now (&start_fsm, d);
1455 }
1456
1457
1458 /**
1459  * Forcefully terminate a process and clean up the child.
1460  *
1461  * @param proc handle to process to kill
1462  */
1463 static void
1464 kill_and_close_process (struct GNUNET_OS_Process *proc)
1465 {
1466   (void) GNUNET_OS_process_kill (proc, SIGKILL);      
1467   GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (proc));
1468   GNUNET_OS_process_close (proc);
1469 }
1470
1471
1472 /**
1473  * Stops a GNUnet daemon.
1474  *
1475  * @param d the daemon that should be stopped
1476  * @param timeout how long to wait for process for shutdown to complete
1477  * @param cb function called once the daemon was stopped
1478  * @param cb_cls closure for cb
1479  * @param delete_files GNUNET_YES to remove files, GNUNET_NO
1480  *        to leave them
1481  * @param allow_restart GNUNET_YES to restart peer later (using this API)
1482  *        GNUNET_NO to kill off and clean up for good
1483  */
1484 void
1485 GNUNET_TESTING_daemon_stop (struct GNUNET_TESTING_Daemon *d,
1486                             struct GNUNET_TIME_Relative timeout,
1487                             GNUNET_TESTING_NotifyCompletion cb, void *cb_cls,
1488                             int delete_files, int allow_restart)
1489 {
1490   char *arg;
1491   char *del_arg;
1492
1493   d->dead_cb = cb;
1494   d->dead_cb_cls = cb_cls;
1495
1496   if (NULL != d->cb)
1497   {
1498     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Setting d->dead on peer `%4s'\n"),
1499                 GNUNET_i2s (&d->id));
1500     d->dead = GNUNET_YES;
1501     return;
1502   }
1503   if (NULL != d->proc_arm_start)
1504   {
1505     kill_and_close_process (d->proc_arm_start);
1506     d->proc_arm_start = NULL;
1507   }
1508   if (NULL != d->proc_arm_srv_start)
1509   {
1510     kill_and_close_process (d->proc_arm_srv_start);
1511     d->proc_arm_srv_start = NULL;
1512   }
1513   if (NULL != d->proc_arm_srv_stop)
1514   {
1515     kill_and_close_process (d->proc_arm_srv_stop);
1516     d->proc_arm_srv_stop = NULL;
1517   }
1518   if (NULL != d->proc_arm_copying)
1519   {
1520     kill_and_close_process (d->proc_arm_copying);
1521     d->proc_arm_copying = NULL;
1522   }
1523   if (NULL != d->proc_arm_peerinfo)
1524   {
1525     kill_and_close_process (d->proc_arm_peerinfo);
1526     d->proc_arm_peerinfo = NULL;
1527   }
1528   if ((d->running == GNUNET_NO) && (d->churn == GNUNET_YES))    /* Peer has already been stopped in churn context! */
1529   {
1530     /* Free what was left from churning! */
1531     GNUNET_assert (d->cfg != NULL);
1532     GNUNET_CONFIGURATION_destroy (d->cfg);
1533     if (delete_files == GNUNET_YES)
1534     {
1535       if (0 != UNLINK (d->cfgfile))
1536       {
1537         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "unlink");
1538       }
1539     }
1540     GNUNET_free (d->cfgfile);
1541     GNUNET_free_non_null (d->hostname);
1542     GNUNET_free_non_null (d->username);
1543     if (NULL != d->dead_cb)
1544       d->dead_cb (d->dead_cb_cls, NULL);
1545     GNUNET_assert (NULL == d->proc_arm_stop);
1546     GNUNET_free (d);
1547     return;
1548   }
1549
1550   del_arg = NULL;
1551   if (delete_files == GNUNET_YES)
1552   {
1553     GNUNET_asprintf (&del_arg, "-d");
1554   }
1555
1556   if (d->phase == SP_CONFIG_UPDATE)
1557   {
1558     GNUNET_SCHEDULER_cancel (d->task);
1559     d->phase = SP_START_DONE;
1560   }
1561   /** Move this call to scheduled shutdown as fix for CORE_connect calling daemon_stop?
1562   if (d->server != NULL)
1563     {
1564       GNUNET_CORE_disconnect (d->server);
1565       d->server = NULL;
1566     }
1567     */
1568   /* shutdown ARM process (will terminate others) */
1569   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Terminating peer `%4s'\n" ,
1570               GNUNET_i2s (&d->id));
1571   d->phase = SP_SHUTDOWN_START;
1572   d->running = GNUNET_NO;
1573   if (allow_restart == GNUNET_YES)
1574     d->churn = GNUNET_YES;
1575   if (d->th != NULL)
1576   {
1577     GNUNET_TRANSPORT_get_hello_cancel (d->ghh);
1578     d->ghh = NULL;
1579     GNUNET_TRANSPORT_disconnect (d->th);
1580     d->th = NULL;
1581   }
1582   /* Check if this is a local or remote process */
1583
1584
1585   if (NULL != d->hostname)
1586   {
1587     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1588                 "Stopping gnunet-arm with config `%s' on host `%s'.\n",
1589                 d->cfgfile, d->hostname);
1590     if (d->username != NULL)
1591       GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname);
1592     else
1593       arg = GNUNET_strdup (d->hostname);
1594
1595     d->proc_arm_stop = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh",
1596 #if !DEBUG_TESTING
1597                                        "-q",
1598 #endif
1599                                        arg, "gnunet-arm",
1600 #if DEBUG_TESTING
1601                                        "-L", "DEBUG",
1602 #endif
1603                                        "-c", d->cfgfile, "-e", "-q", "-T",
1604                                        GNUNET_TIME_relative_to_string (timeout),
1605                                        del_arg, NULL);
1606     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1607                 "Stopping gnunet-arm with command ssh %s gnunet-arm -c %s -e -q %s\n",
1608                 arg, "gnunet-arm", d->cfgfile, del_arg);
1609     /* Use -e to end arm, and -d to remove temp files */
1610     GNUNET_free (arg);
1611   }
1612   else
1613   {
1614     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1615                 "Stopping gnunet-arm with config `%s' locally.\n", d->cfgfile);
1616     d->proc_arm_stop = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "gnunet-arm", "gnunet-arm",
1617 #if DEBUG_TESTING
1618                                        "-L", "DEBUG",
1619 #endif
1620                                        "-c", d->cfgfile, "-e", "-q", "-T",
1621                                        GNUNET_TIME_relative_to_string (timeout),
1622                                        del_arg, NULL);
1623     GNUNET_assert (NULL != d->proc_arm_stop);
1624   }
1625
1626   GNUNET_free_non_null (del_arg);
1627   d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
1628   if (GNUNET_SCHEDULER_NO_TASK != d->task)
1629     GNUNET_SCHEDULER_cancel(d->task);
1630   d->task = GNUNET_SCHEDULER_add_now (&start_fsm, d);
1631 }
1632
1633
1634 /**
1635  * Changes the configuration of a GNUnet daemon.
1636  *
1637  * @param d the daemon that should be modified
1638  * @param cfg the new configuration for the daemon
1639  * @param cb function called once the configuration was changed
1640  * @param cb_cls closure for cb
1641  */
1642 void
1643 GNUNET_TESTING_daemon_reconfigure (struct GNUNET_TESTING_Daemon *d,
1644                                    struct GNUNET_CONFIGURATION_Handle *cfg,
1645                                    GNUNET_TESTING_NotifyCompletion cb,
1646                                    void *cb_cls)
1647 {
1648   char *arg;
1649
1650   if (d->phase != SP_START_DONE)
1651   {
1652     if (NULL != cb)
1653       cb (cb_cls,
1654           _
1655           ("Peer not yet running, can not change configuration at this point."));
1656     return;
1657   }
1658
1659   /* 1) write configuration to temporary file */
1660   if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, d->cfgfile))
1661   {
1662     if (NULL != cb)
1663       cb (cb_cls, _("Failed to write new configuration to disk."));
1664     return;
1665   }
1666
1667   /* 2) copy file to remote host (if necessary) */
1668   if (NULL == d->hostname)
1669   {
1670     /* signal success */
1671     if (NULL != cb)
1672       cb (cb_cls, NULL);
1673     return;
1674   }
1675   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1676               "Copying updated configuration file to remote host `%s'.\n",
1677               d->hostname);
1678   d->phase = SP_CONFIG_UPDATE;
1679   if (NULL != d->username)
1680     GNUNET_asprintf (&arg, "%s@%s:%s", d->username, d->hostname, d->cfgfile);
1681   else
1682     GNUNET_asprintf (&arg, "%s:%s", d->hostname, d->cfgfile);
1683   d->proc_arm_copying = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "scp", "scp",
1684 #if !DEBUG_TESTING
1685                                      "-q",
1686 #endif
1687                                      d->cfgfile, arg, NULL);
1688   GNUNET_free (arg);
1689   if (NULL == d->proc_arm_copying)
1690   {
1691     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1692                 _("Could not start `%s' process to copy configuration file.\n"),
1693                 "scp");
1694     if (NULL != cb)
1695       cb (cb_cls, _("Failed to copy new configuration to remote machine."));
1696     d->phase = SP_START_DONE;
1697     return;
1698   }
1699   d->update_cb = cb;
1700   d->update_cb_cls = cb_cls;
1701   d->task =
1702       GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm, d);
1703 }
1704
1705
1706 /**
1707  * Data kept for each pair of peers that we try
1708  * to connect.
1709  */
1710 struct GNUNET_TESTING_ConnectContext
1711 {
1712   /**
1713    * Testing handle to the first daemon.
1714    */
1715   struct GNUNET_TESTING_Daemon *d1;
1716
1717   /**
1718    * Handle to core of first daemon (to check connect)
1719    */
1720   struct GNUNET_CORE_Handle *d1core;
1721
1722   /**
1723    * Have we actually connected to the core of the first daemon yet?
1724    */
1725   int d1core_ready;
1726
1727   /**
1728    * Testing handle to the second daemon.
1729    */
1730   struct GNUNET_TESTING_Daemon *d2;
1731
1732   /**
1733    * Transport handle to the first daemon (to offer the HELLO of the second daemon to).
1734    */
1735   struct GNUNET_TRANSPORT_Handle *d1th;
1736
1737   /**
1738    * Function to call once we are done (or have timed out).
1739    */
1740   GNUNET_TESTING_NotifyConnection cb;
1741
1742   /**
1743    * Closure for "nb".
1744    */
1745   void *cb_cls;
1746
1747   /**
1748    * The relative timeout from whence this connect attempt was
1749    * started.  Allows for reconnect attempts.
1750    */
1751   struct GNUNET_TIME_Relative relative_timeout;
1752
1753   /**
1754    * Maximum number of connect attempts, will retry connection
1755    * this number of times on failures.
1756    */
1757   unsigned int connect_attempts;
1758
1759   /**
1760    * Hello timeout task
1761    */
1762   GNUNET_SCHEDULER_TaskIdentifier hello_send_task;
1763
1764   /**
1765    * Connect timeout task
1766    */
1767   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
1768
1769   /**
1770    * When should this operation be complete (or we must trigger
1771    * a timeout).
1772    */
1773   struct GNUNET_TIME_Relative timeout_hello;
1774
1775   /**
1776    * Was the connection attempt successful?
1777    */
1778   int connected;
1779
1780   /**
1781    * When connecting, do we need to send the HELLO?
1782    */
1783   int send_hello;
1784
1785   /**
1786    * The distance between the two connected peers
1787    */
1788   uint32_t distance;
1789 };
1790
1791
1792 /** Forward declaration **/
1793 static void
1794 reattempt_daemons_connect (void *cls,
1795                            const struct GNUNET_SCHEDULER_TaskContext *tc);
1796
1797
1798 /**
1799  * Notify callback about success or failure of the attempt
1800  * to connect the two peers
1801  *
1802  * @param cls our "struct GNUNET_TESTING_ConnectContext" (freed)
1803  * @param tc reason tells us if we succeeded or failed
1804  */
1805 static void
1806 notify_connect_result (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1807 {
1808   struct GNUNET_TESTING_ConnectContext *ctx = cls;
1809
1810   ctx->timeout_task = GNUNET_SCHEDULER_NO_TASK;
1811   if (ctx->hello_send_task != GNUNET_SCHEDULER_NO_TASK)
1812   {
1813     GNUNET_SCHEDULER_cancel (ctx->hello_send_task);
1814     ctx->hello_send_task = GNUNET_SCHEDULER_NO_TASK;
1815   }
1816
1817   if (ctx->d1th != NULL)
1818     GNUNET_TRANSPORT_disconnect (ctx->d1th);
1819   ctx->d1th = NULL;
1820   if (ctx->d1core != NULL)
1821     GNUNET_CORE_disconnect (ctx->d1core);
1822   ctx->d1core = NULL;
1823
1824   if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
1825   {
1826     GNUNET_free (ctx);
1827     return;
1828   }
1829
1830   if (ctx->connected == GNUNET_YES)
1831   {
1832     if (ctx->cb != NULL)
1833     {
1834       ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, ctx->distance,
1835                ctx->d1->cfg, ctx->d2->cfg, ctx->d1, ctx->d2, NULL);
1836     }
1837   }
1838   else if (ctx->connect_attempts > 0)
1839   {
1840     ctx->d1core_ready = GNUNET_NO;
1841     ctx->timeout_task =
1842         GNUNET_SCHEDULER_add_now (&reattempt_daemons_connect, ctx);
1843     return;
1844   }
1845   else
1846   {
1847     if (ctx->cb != NULL)
1848     {
1849       ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, 0, ctx->d1->cfg,
1850                ctx->d2->cfg, ctx->d1, ctx->d2, _("Peers failed to connect"));
1851     }
1852   }
1853   GNUNET_free (ctx);
1854 }
1855
1856
1857 /**
1858  * Success, connection is up.  Signal client our success.
1859  *
1860  * @param cls our "struct GNUNET_TESTING_ConnectContext"
1861  * @param peer identity of the peer that has connected
1862  * @param atsi performance information
1863  * @param atsi_count number of records in 'atsi'
1864  *
1865  */
1866 static void
1867 connect_notify (void *cls, const struct GNUNET_PeerIdentity *peer,
1868                 const struct GNUNET_ATS_Information *atsi,
1869                 unsigned int atsi_count)
1870 {
1871   struct GNUNET_TESTING_ConnectContext *ctx = cls;
1872
1873   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected peer %s to peer %s\n",
1874               ctx->d1->shortname, GNUNET_i2s (peer));
1875   if (0 != memcmp (&ctx->d2->id, peer, sizeof (struct GNUNET_PeerIdentity)))
1876     return;
1877   ctx->connected = GNUNET_YES;
1878   ctx->distance = 0;            /* FIXME: distance */
1879   if (ctx->hello_send_task != GNUNET_SCHEDULER_NO_TASK)
1880   {
1881     GNUNET_SCHEDULER_cancel (ctx->hello_send_task);
1882     ctx->hello_send_task = GNUNET_SCHEDULER_NO_TASK;
1883   }
1884   GNUNET_SCHEDULER_cancel (ctx->timeout_task);
1885   ctx->timeout_task = GNUNET_SCHEDULER_add_now (&notify_connect_result, ctx);
1886 }
1887
1888
1889 static void
1890 send_hello (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1891 {
1892   struct GNUNET_TESTING_ConnectContext *ctx = cls;
1893   struct GNUNET_MessageHeader *hello;
1894
1895   ctx->hello_send_task = GNUNET_SCHEDULER_NO_TASK;
1896   if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
1897     return;
1898   if ((ctx->d1core_ready == GNUNET_YES) && (ctx->d2->hello != NULL) &&
1899       (NULL != GNUNET_HELLO_get_header (ctx->d2->hello)) &&
1900       (ctx->d1->phase == SP_START_DONE) && (ctx->d2->phase == SP_START_DONE))
1901   {
1902     hello = GNUNET_HELLO_get_header (ctx->d2->hello);
1903     GNUNET_assert (hello != NULL);
1904     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Offering hello of %s to %s\n",
1905                 ctx->d2->shortname, ctx->d1->shortname);
1906     GNUNET_TRANSPORT_offer_hello (ctx->d1th, hello, NULL, NULL);
1907     GNUNET_assert (ctx->d1core != NULL);
1908     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1909                 "Sending connect request to TRANSPORT of %s for peer %s\n",
1910                 GNUNET_i2s (&ctx->d1->id),
1911                 GNUNET_h2s (&ctx->d2->id.hashPubKey));
1912     GNUNET_TRANSPORT_try_connect (ctx->d1th, &ctx->d2->id);
1913     ctx->timeout_hello =
1914         GNUNET_TIME_relative_add (ctx->timeout_hello,
1915                                   GNUNET_TIME_relative_multiply
1916                                   (GNUNET_TIME_UNIT_MILLISECONDS, 500));
1917   }
1918   ctx->hello_send_task =
1919       GNUNET_SCHEDULER_add_delayed (ctx->timeout_hello, &send_hello, ctx);
1920 }
1921
1922 /**
1923  * Notify of a successful connection to the core service.
1924  *
1925  * @param cls a ConnectContext
1926  * @param server handle to the core service
1927  * @param my_identity the peer identity of this peer
1928  */
1929 void
1930 core_init_notify (void *cls, struct GNUNET_CORE_Handle *server,
1931                   const struct GNUNET_PeerIdentity *my_identity)
1932 {
1933   struct GNUNET_TESTING_ConnectContext *connect_ctx = cls;
1934
1935   connect_ctx->d1core_ready = GNUNET_YES;
1936
1937   if (connect_ctx->send_hello == GNUNET_NO)
1938   {
1939     GNUNET_TRANSPORT_try_connect (connect_ctx->d1th, &connect_ctx->d2->id);
1940     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1941                 "Sending connect request to TRANSPORT of %s for peer %s\n",
1942                 connect_ctx->d1->shortname, connect_ctx->d2->shortname);
1943   }
1944 }
1945
1946
1947 /**
1948  * Try to connect again some peers that failed in an earlier attempt. This will
1949  * be tried as many times as connection_attempts in the configuration file.
1950  *
1951  * @param cls Closure (connection context between the two peers).
1952  * @param tc TaskContext.
1953  */
1954 static void
1955 reattempt_daemons_connect (void *cls,
1956                            const struct GNUNET_SCHEDULER_TaskContext *tc)
1957 {
1958   struct GNUNET_TESTING_ConnectContext *ctx = cls;
1959
1960   ctx->timeout_task = GNUNET_SCHEDULER_NO_TASK;
1961   if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
1962     return;
1963   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1964               "re-attempting connect of peer %s to peer %s\n",
1965               ctx->d1->shortname, ctx->d2->shortname);
1966   ctx->connect_attempts--;
1967   GNUNET_assert (ctx->d1core == NULL);
1968   ctx->d1core_ready = GNUNET_NO;
1969   ctx->d1core =
1970       GNUNET_CORE_connect (ctx->d1->cfg, 1, ctx, &core_init_notify,
1971                            &connect_notify, NULL, NULL, GNUNET_NO, NULL,
1972                            GNUNET_NO, no_handlers);
1973   if (ctx->d1core == NULL)
1974   {
1975     if (NULL != ctx->cb)
1976       ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, 0, ctx->d1->cfg,
1977                ctx->d2->cfg, ctx->d1, ctx->d2,
1978                _("Failed to connect to core service of first peer!\n"));
1979     GNUNET_free (ctx);
1980     return;
1981   }
1982
1983   /* Don't know reason for initial connect failure, update the HELLO for the second peer */
1984   if (NULL != ctx->d2->hello)
1985   {
1986     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "updating %s's HELLO\n",
1987                 ctx->d2->shortname);
1988     GNUNET_free (ctx->d2->hello);
1989     ctx->d2->hello = NULL;
1990     if (NULL != ctx->d2->th)
1991     {
1992       GNUNET_TRANSPORT_get_hello_cancel (ctx->d2->ghh);
1993       ctx->d2->ghh = NULL;
1994       GNUNET_TRANSPORT_disconnect (ctx->d2->th);
1995     }
1996     ctx->d2->th =
1997         GNUNET_TRANSPORT_connect (ctx->d2->cfg, &ctx->d2->id, NULL, NULL, NULL,
1998                                   NULL);
1999     GNUNET_assert (ctx->d2->th != NULL);
2000     ctx->d2->ghh =
2001         GNUNET_TRANSPORT_get_hello (ctx->d2->th, &process_hello, ctx->d2);
2002   }
2003   else
2004   {
2005     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "didn't have %s's HELLO\n",
2006                 ctx->d2->shortname);
2007   }
2008
2009   if ((NULL == ctx->d2->hello) && (ctx->d2->th == NULL))
2010   {
2011     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2012                 "didn't have %s's HELLO, trying to get it now\n",
2013                 ctx->d2->shortname);
2014     ctx->d2->th =
2015         GNUNET_TRANSPORT_connect (ctx->d2->cfg, &ctx->d2->id, NULL, NULL, NULL,
2016                                   NULL);
2017     if (NULL == ctx->d2->th)
2018     {
2019       GNUNET_CORE_disconnect (ctx->d1core);
2020       GNUNET_free (ctx);
2021       if (NULL != ctx->cb)
2022         ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, 0, ctx->d1->cfg,
2023                  ctx->d2->cfg, ctx->d1, ctx->d2,
2024                  _("Failed to connect to transport service!\n"));
2025       return;
2026     }
2027     ctx->d2->ghh =
2028         GNUNET_TRANSPORT_get_hello (ctx->d2->th, &process_hello, ctx->d2);
2029   }
2030   else
2031   {
2032     if (NULL == ctx->d2->hello)
2033     {
2034       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2035                   "didn't have %s's HELLO but th wasn't NULL, not trying!!\n",
2036                   ctx->d2->shortname);
2037     }
2038   }
2039
2040   if (ctx->send_hello == GNUNET_YES)
2041   {
2042     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending %s's HELLO to %s\n",
2043                 ctx->d1->shortname, ctx->d2->shortname);
2044     ctx->d1th =
2045         GNUNET_TRANSPORT_connect (ctx->d1->cfg, &ctx->d1->id, ctx->d1, NULL,
2046                                   NULL, NULL);
2047     if (ctx->d1th == NULL)
2048     {
2049       GNUNET_CORE_disconnect (ctx->d1core);
2050       GNUNET_free (ctx);
2051       if (NULL != ctx->cb)
2052         ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, 0, ctx->d1->cfg,
2053                  ctx->d2->cfg, ctx->d1, ctx->d2,
2054                  _("Failed to connect to transport service!\n"));
2055       return;
2056     }
2057     GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == ctx->hello_send_task);
2058     ctx->hello_send_task = GNUNET_SCHEDULER_add_now (&send_hello, ctx);
2059   }
2060   else
2061   {
2062     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to reconnect %s to %s\n",
2063                 ctx->d1->shortname, ctx->d2->shortname);
2064     GNUNET_TRANSPORT_try_connect (ctx->d1th, &ctx->d2->id);
2065   }
2066   ctx->timeout_task =
2067       GNUNET_SCHEDULER_add_delayed (ctx->relative_timeout,
2068                                     &notify_connect_result, ctx);
2069 }
2070
2071 /**
2072  * Iterator for currently known peers, to ensure
2073  * that we don't try to send duplicate connect
2074  * requests to core.
2075  *
2076  * @param cls our "struct GNUNET_TESTING_ConnectContext"
2077  * @param peer identity of the peer that has connected,
2078  *        NULL when iteration has finished
2079  * @param atsi performance information
2080  * @param atsi_count number of records in 'atsi'
2081  *
2082  */
2083 static void
2084 core_initial_iteration (void *cls, const struct GNUNET_PeerIdentity *peer,
2085                         const struct GNUNET_ATS_Information *atsi,
2086                         unsigned int atsi_count)
2087 {
2088   struct GNUNET_TESTING_ConnectContext *ctx = cls;
2089
2090   if ((peer != NULL) &&
2091       (0 == memcmp (&ctx->d2->id, peer, sizeof (struct GNUNET_PeerIdentity))))
2092   {
2093     ctx->connected = GNUNET_YES;
2094     ctx->distance = 0;          /* FIXME: distance */
2095     return;
2096   }
2097   if (peer != NULL)
2098     return;                     /* ignore other peers */
2099   /* peer == NULL: End of iteration over peers */
2100
2101   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == ctx->timeout_task);
2102   if (ctx->connected == GNUNET_YES)
2103   {
2104     ctx->timeout_task = GNUNET_SCHEDULER_add_now (&notify_connect_result, ctx);
2105     return;
2106   }
2107
2108   /* Peer not already connected, need to schedule connect request! */
2109   if (ctx->d1core == NULL)
2110   {
2111     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2112                 "Peers are NOT connected, connecting to core!\n");
2113     ctx->d1core =
2114         GNUNET_CORE_connect (ctx->d1->cfg, 1, ctx, &core_init_notify,
2115                              &connect_notify, NULL, NULL, GNUNET_NO, NULL,
2116                              GNUNET_NO, no_handlers);
2117   }
2118
2119   if (ctx->d1core == NULL)
2120   {
2121     ctx->timeout_task = GNUNET_SCHEDULER_add_now (&notify_connect_result, ctx);
2122     return;
2123   }
2124
2125   if ((NULL == ctx->d2->hello) && (ctx->d2->th == NULL))        /* Do not yet have the second peer's hello, set up a task to get it */
2126   {
2127     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2128                 "Don't have d2's HELLO, trying to get it!\n");
2129     ctx->d2->th =
2130         GNUNET_TRANSPORT_connect (ctx->d2->cfg, &ctx->d2->id, NULL, NULL, NULL,
2131                                   NULL);
2132     if (ctx->d2->th == NULL)
2133     {
2134       GNUNET_CORE_disconnect (ctx->d1core);
2135       ctx->d1core = NULL;
2136       ctx->timeout_task =
2137           GNUNET_SCHEDULER_add_now (&notify_connect_result, ctx);
2138       return;
2139     }
2140     ctx->d2->ghh =
2141         GNUNET_TRANSPORT_get_hello (ctx->d2->th, &process_hello, ctx->d2);
2142   }
2143
2144   if (ctx->send_hello == GNUNET_YES)
2145   {
2146     ctx->d1th =
2147         GNUNET_TRANSPORT_connect (ctx->d1->cfg, &ctx->d1->id, ctx->d1, NULL,
2148                                   NULL, NULL);
2149     if (ctx->d1th == NULL)
2150     {
2151       GNUNET_CORE_disconnect (ctx->d1core);
2152       ctx->d1core = NULL;
2153       ctx->timeout_task =
2154           GNUNET_SCHEDULER_add_now (&notify_connect_result, ctx);
2155       return;
2156     }
2157     GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == ctx->hello_send_task);
2158     ctx->hello_send_task = GNUNET_SCHEDULER_add_now (&send_hello, ctx);
2159   }
2160
2161   ctx->timeout_task =
2162       GNUNET_SCHEDULER_add_delayed (ctx->relative_timeout,
2163                                     &notify_connect_result, ctx);
2164
2165 }
2166
2167
2168 /**
2169  * Establish a connection between two GNUnet daemons.  The daemons
2170  * must both be running and not be stopped until either the
2171  * 'cb' callback is called OR the connection request has been
2172  * explicitly cancelled.
2173  *
2174  * @param d1 handle for the first daemon
2175  * @param d2 handle for the second daemon
2176  * @param timeout how long is the connection attempt
2177  *        allowed to take?
2178  * @param max_connect_attempts how many times should we try to reconnect
2179  *        (within timeout)
2180  * @param send_hello GNUNET_YES to send the HELLO, GNUNET_NO to assume
2181  *                   the HELLO has already been exchanged
2182  * @param cb function to call at the end
2183  * @param cb_cls closure for cb
2184  * @return handle to cancel the request
2185  */
2186 struct GNUNET_TESTING_ConnectContext *
2187 GNUNET_TESTING_daemons_connect (struct GNUNET_TESTING_Daemon *d1,
2188                                 struct GNUNET_TESTING_Daemon *d2,
2189                                 struct GNUNET_TIME_Relative timeout,
2190                                 unsigned int max_connect_attempts,
2191                                 int send_hello,
2192                                 GNUNET_TESTING_NotifyConnection cb,
2193                                 void *cb_cls)
2194 {
2195   struct GNUNET_TESTING_ConnectContext *ctx;
2196
2197   if ((d1->running == GNUNET_NO) || (d2->running == GNUNET_NO))
2198   {
2199     if (NULL != cb)
2200       cb (cb_cls, &d1->id, &d2->id, 0, d1->cfg, d2->cfg, d1, d2,
2201           _("Peers are not fully running yet, can not connect!\n"));
2202     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Peers are not up!\n");
2203     return NULL;
2204   }
2205
2206   ctx = GNUNET_malloc (sizeof (struct GNUNET_TESTING_ConnectContext));
2207   ctx->d1 = d1;
2208   ctx->d2 = d2;
2209   ctx->timeout_hello =
2210       GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 500);
2211   ctx->relative_timeout =
2212       GNUNET_TIME_relative_divide (timeout, max_connect_attempts);
2213   ctx->cb = cb;
2214   ctx->cb_cls = cb_cls;
2215   ctx->connect_attempts = max_connect_attempts;
2216   ctx->connected = GNUNET_NO;
2217   ctx->send_hello = send_hello;
2218   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Asked to connect peer %s to peer %s\n",
2219               d1->shortname, d2->shortname);
2220   /* Core is up! Iterate over all _known_ peers first to check if we are already connected to the peer! */
2221   GNUNET_assert (GNUNET_OK ==
2222                  GNUNET_CORE_is_peer_connected (ctx->d1->cfg, &ctx->d2->id,
2223                                                 &core_initial_iteration, ctx));
2224   return ctx;
2225 }
2226
2227
2228 /**
2229  * Cancel an attempt to connect two daemons.
2230  *
2231  * @param cc connect context
2232  */
2233 void
2234 GNUNET_TESTING_daemons_connect_cancel (struct GNUNET_TESTING_ConnectContext *cc)
2235 {
2236   if (GNUNET_SCHEDULER_NO_TASK != cc->timeout_task)
2237   {
2238     GNUNET_SCHEDULER_cancel (cc->timeout_task);
2239     cc->timeout_task = GNUNET_SCHEDULER_NO_TASK;
2240   }
2241   if (GNUNET_SCHEDULER_NO_TASK != cc->hello_send_task)
2242   {
2243     GNUNET_SCHEDULER_cancel (cc->hello_send_task);
2244     cc->hello_send_task = GNUNET_SCHEDULER_NO_TASK;
2245   }
2246   if (NULL != cc->d1core)
2247   {
2248     GNUNET_CORE_disconnect (cc->d1core);
2249     cc->d1core = NULL;
2250   }
2251   if (NULL != cc->d1th)
2252   {
2253     GNUNET_TRANSPORT_disconnect (cc->d1th);
2254     cc->d1th = NULL;
2255   }
2256   GNUNET_free (cc);
2257 }
2258
2259
2260 /* end of testing.c */