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