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