change testing to use is peer connected
[oweals/gnunet.git] / src / testing / testing.c
1 /*
2       This file is part of GNUnet
3       (C) 2008, 2009 Christian Grothoff (and other contributing authors)
4
5       GNUnet is free software; you can redistribute it and/or modify
6       it under the terms of the GNU General Public License as published
7       by the Free Software Foundation; either version 3, or (at your
8       option) any later version.
9
10       GNUnet is distributed in the hope that it will be useful, but
11       WITHOUT ANY WARRANTY; without even the implied warranty of
12       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13       General Public License for more details.
14
15       You should have received a copy of the GNU General Public License
16       along with GNUnet; see the file COPYING.  If not, write to the
17       Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18       Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * @file testing/testing.c
23  * @brief convenience API for writing testcases for GNUnet
24  *        Many testcases need to start and stop gnunetd,
25  *        and this library is supposed to make that easier
26  *        for TESTCASES.  Normal programs should always
27  *        use functions from gnunet_{util,arm}_lib.h.  This API is
28  *        ONLY for writing testcases!
29  * @author Christian Grothoff
30  *
31  */
32 #include "platform.h"
33 #include "gnunet_arm_service.h"
34 #include "gnunet_core_service.h"
35 #include "gnunet_constants.h"
36 #include "gnunet_testing_lib.h"
37 #include "gnunet_transport_service.h"
38 #include "gnunet_hello_lib.h"
39
40 #define DEBUG_TESTING GNUNET_NO
41 #define DEBUG_TESTING_RECONNECT GNUNET_NO
42
43 /**
44  * How long do we wait after starting gnunet-service-arm
45  * for the core service to be alive?
46  */
47 #define ARM_START_WAIT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120)
48
49 /**
50  * How many times are we willing to try to wait for "scp" or
51  * "gnunet-service-arm" to complete (waitpid) before giving up?
52  */
53 #define MAX_EXEC_WAIT_RUNS 250
54
55 static struct GNUNET_CORE_MessageHandler no_handlers[] = { {NULL, 0, 0} };
56
57 /**
58  * Receive the HELLO from one peer, give it to the other
59  * and ask them to connect.
60  *
61  * @param cls "struct ConnectContext"
62  * @param message HELLO message of peer
63  */
64 static void
65 process_hello (void *cls, const struct GNUNET_MessageHeader *message)
66 {
67   struct GNUNET_TESTING_Daemon *daemon = cls;
68   int msize;
69   if (daemon == NULL)
70     return;
71
72   if (daemon->server != NULL)
73     {
74 #if DEBUG_TESTING
75       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
76                   "Received `%s' from transport service of `%4s', disconnecting core!\n",
77                   "HELLO", GNUNET_i2s (&daemon->id));
78 #endif
79       GNUNET_CORE_disconnect (daemon->server);
80       daemon->server = NULL;
81     }
82
83   GNUNET_assert (message != NULL);
84   msize = ntohs (message->size);
85   if (msize < 1)
86     {
87       return;
88     }
89   if (daemon->th != NULL)
90     {
91       GNUNET_TRANSPORT_get_hello_cancel (daemon->th, &process_hello, daemon);
92     }
93 #if DEBUG_TESTING
94   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
95               "Received `%s' from transport service of `%4s'\n",
96               "HELLO", GNUNET_i2s (&daemon->id));
97 #endif
98
99   GNUNET_free_non_null (daemon->hello);
100   daemon->hello = GNUNET_malloc (msize);
101   memcpy (daemon->hello, message, msize);
102
103   if (daemon->th != NULL)
104     {
105       GNUNET_TRANSPORT_disconnect (daemon->th);
106       daemon->th = NULL;
107     }
108
109 }
110
111 /**
112  * Function called after GNUNET_CORE_connect has succeeded
113  * (or failed for good).  Note that the private key of the
114  * peer is intentionally not exposed here; if you need it,
115  * your process should try to read the private key file
116  * directly (which should work if you are authorized...).
117  *
118  * @param cls closure
119  * @param server handle to the server, NULL if we failed
120  * @param my_identity ID of this peer, NULL if we failed
121  * @param publicKey public key of this peer, NULL if we failed
122  */
123 static void
124 testing_init (void *cls,
125               struct GNUNET_CORE_Handle *server,
126               const struct GNUNET_PeerIdentity *my_identity,
127               const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
128 {
129   struct GNUNET_TESTING_Daemon *d = cls;
130   GNUNET_TESTING_NotifyDaemonRunning cb;
131
132   GNUNET_assert (d->phase == SP_START_CORE);
133   d->phase = SP_START_DONE;
134   cb = d->cb;
135   d->cb = NULL;
136   if (server == NULL)
137     {
138       d->server = NULL;
139       if (GNUNET_YES == d->dead)
140         GNUNET_TESTING_daemon_stop (d,
141                                     GNUNET_TIME_absolute_get_remaining
142                                     (d->max_timeout), d->dead_cb,
143                                     d->dead_cb_cls, GNUNET_YES, GNUNET_NO);
144       else if (NULL != cb)
145         cb (d->cb_cls, NULL, d->cfg, d,
146             _("Failed to connect to core service\n"));
147       return;
148     }
149 #if DEBUG_TESTING
150   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
151               "Successfully started peer `%4s'.\n", GNUNET_i2s (my_identity));
152 #endif
153   d->id = *my_identity;
154   d->shortname = strdup (GNUNET_i2s (my_identity));
155   d->server = server;
156   d->running = GNUNET_YES;
157
158   if (NULL != cb)               /* FIXME: what happens when this callback calls GNUNET_TESTING_daemon_stop? */
159     cb (d->cb_cls, my_identity, d->cfg, d, NULL);
160
161   if (GNUNET_NO == d->running)
162     {
163 #if DEBUG_TESTING
164       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
165                   "Peer is dead (d->running == GNUNET_NO)\n");
166 #endif
167       return;
168     }
169 #if DEBUG_TESTING
170   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
171               "Successfully started peer `%4s', connecting to transport service.\n",
172               GNUNET_i2s (my_identity));
173 #endif
174
175   d->th = GNUNET_TRANSPORT_connect (d->cfg, &d->id, d, NULL, NULL, NULL);
176   if (d->th == NULL)
177     {
178       if (GNUNET_YES == d->dead)
179         GNUNET_TESTING_daemon_stop (d,
180                                     GNUNET_TIME_absolute_get_remaining
181                                     (d->max_timeout), d->dead_cb,
182                                     d->dead_cb_cls, GNUNET_YES, GNUNET_NO);
183       else if (NULL != d->cb)
184         d->cb (d->cb_cls, &d->id, d->cfg, d,
185                _("Failed to connect to transport service!\n"));
186       return;
187     }
188 #if DEBUG_TESTING
189   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
190               "Connected to transport service `%s', getting HELLO\n",
191               GNUNET_i2s (my_identity));
192 #endif
193
194   GNUNET_TRANSPORT_get_hello (d->th, &process_hello, d);
195 }
196
197
198 /**
199  * Finite-state machine for starting GNUnet.
200  *
201  * @param cls our "struct GNUNET_TESTING_Daemon"
202  * @param tc unused
203  */
204 static void
205 start_fsm (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
206 {
207   struct GNUNET_TESTING_Daemon *d = cls;
208   GNUNET_TESTING_NotifyDaemonRunning cb;
209   enum GNUNET_OS_ProcessStatusType type;
210   unsigned long code;
211   char *dst;
212   int bytes_read;
213
214 #if DEBUG_TESTING
215   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
216               "Peer FSM is in phase %u.\n", d->phase);
217 #endif
218
219   d->task = GNUNET_SCHEDULER_NO_TASK;
220   switch (d->phase)
221     {
222     case SP_COPYING:
223       /* confirm copying complete */
224       if (GNUNET_OK != GNUNET_OS_process_status (d->proc, &type, &code))
225         {
226           if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value ==
227               0)
228             {
229               cb = d->cb;
230               d->cb = NULL;
231               if (NULL != cb)
232                 cb (d->cb_cls,
233                     NULL,
234                     d->cfg, d,
235                     _
236                     ("`scp' does not seem to terminate (timeout copying config).\n"));
237               return;
238             }
239           /* wait some more */
240           d->task
241             = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT,
242                                             &start_fsm, d);
243           return;
244         }
245       if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0))
246         {
247           cb = d->cb;
248           d->cb = NULL;
249           if (NULL != cb)
250             cb (d->cb_cls,
251                 NULL, d->cfg, d, _("`scp' did not complete cleanly.\n"));
252           return;
253         }
254 #if DEBUG_TESTING
255       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
256                   "Successfully copied configuration file.\n");
257 #endif
258       d->phase = SP_COPIED;
259       /* fall-through */
260     case SP_COPIED:
261       /* Start create hostkey process */
262       d->pipe_stdout = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_YES);
263       if (d->pipe_stdout == NULL)
264         {
265           cb = d->cb;
266           d->cb = NULL;
267           if (NULL != cb)
268             cb (d->cb_cls,
269                 NULL,
270                 d->cfg,
271                 d,
272                 (NULL == d->hostname)
273                 ? _("Failed to create pipe for `gnunet-peerinfo' process.\n")
274                 : _("Failed to create pipe for `ssh' process.\n"));
275           return;
276         }
277       if (NULL == d->hostname)
278         {
279 #if DEBUG_TESTING
280           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
281                       "Starting `%s', with command `%s %s %s %s'.\n",
282                       "gnunet-peerinfo", "gnunet-peerinfo", "-c", d->cfgfile,
283                       "-sq");
284 #endif
285           d->proc =
286             GNUNET_OS_start_process (NULL, d->pipe_stdout, "gnunet-peerinfo",
287                                      "gnunet-peerinfo", "-c", d->cfgfile,
288                                      "-sq", NULL);
289           GNUNET_DISK_pipe_close_end (d->pipe_stdout,
290                                       GNUNET_DISK_PIPE_END_WRITE);
291         }
292       else
293         {
294           if (d->username != NULL)
295             GNUNET_asprintf (&dst, "%s@%s", d->username, d->hostname);
296           else
297             dst = GNUNET_strdup (d->hostname);
298
299 #if DEBUG_TESTING
300           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
301                       "Starting `%s', with command `%s %s %s %s %s %s'.\n",
302                       "gnunet-peerinfo", "ssh", dst, "gnunet-peerinfo", "-c",
303                       d->cfgfile, "-sq");
304 #endif
305           if (d->ssh_port_str == NULL)
306             {
307               d->proc = GNUNET_OS_start_process (NULL, d->pipe_stdout, "ssh",
308                                                  "ssh",
309 #if !DEBUG_TESTING
310                                                  "-q",
311 #endif
312                                                  dst,
313                                                  "gnunet-peerinfo",
314                                                  "-c", d->cfgfile, "-sq",
315                                                  NULL);
316             }
317           else
318             {
319               d->proc = GNUNET_OS_start_process (NULL, d->pipe_stdout, "ssh",
320                                                  "ssh", "-p", d->ssh_port_str,
321 #if !DEBUG_TESTING
322                                                  "-q",
323 #endif
324                                                  dst,
325                                                  "gnunet-peerinfo",
326                                                  "-c", d->cfgfile, "-sq",
327                                                  NULL);
328             }
329           GNUNET_DISK_pipe_close_end (d->pipe_stdout,
330                                       GNUNET_DISK_PIPE_END_WRITE);
331           GNUNET_free (dst);
332         }
333       if (NULL == d->proc)
334         {
335           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
336                       _("Could not start `%s' process to create hostkey.\n"),
337                       (NULL == d->hostname) ? "gnunet-peerinfo" : "ssh");
338           cb = d->cb;
339           d->cb = NULL;
340           if (NULL != cb)
341             cb (d->cb_cls,
342                 NULL,
343                 d->cfg,
344                 d,
345                 (NULL == d->hostname)
346                 ? _("Failed to start `gnunet-peerinfo' process.\n")
347                 : _("Failed to start `ssh' process.\n"));
348           GNUNET_DISK_pipe_close (d->pipe_stdout);
349           return;
350         }
351 #if DEBUG_TESTING
352       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
353                   "Started `%s', waiting for hostkey.\n", "gnunet-peerinfo");
354 #endif
355       d->phase = SP_HOSTKEY_CREATE;
356       d->task
357         =
358         GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
359                                         (d->max_timeout),
360                                         GNUNET_DISK_pipe_handle
361                                         (d->pipe_stdout,
362                                          GNUNET_DISK_PIPE_END_READ),
363                                         &start_fsm, d);
364       break;
365     case SP_HOSTKEY_CREATE:
366       bytes_read =
367         GNUNET_DISK_file_read (GNUNET_DISK_pipe_handle
368                                (d->pipe_stdout, GNUNET_DISK_PIPE_END_READ),
369                                &d->hostkeybuf[d->hostkeybufpos],
370                                sizeof (d->hostkeybuf) - d->hostkeybufpos);
371       if (bytes_read > 0)
372         d->hostkeybufpos += bytes_read;
373
374       if ((d->hostkeybufpos < 104) && (bytes_read > 0))
375         {
376           /* keep reading */
377           d->task
378             =
379             GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
380                                             (d->max_timeout),
381                                             GNUNET_DISK_pipe_handle
382                                             (d->pipe_stdout,
383                                              GNUNET_DISK_PIPE_END_READ),
384                                             &start_fsm, d);
385           return;
386         }
387       d->hostkeybuf[103] = '\0';
388
389       if ((bytes_read < 0) ||
390           (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (d->hostkeybuf,
391                                                         &d->id.hashPubKey)))
392         {
393           /* error */
394           if (bytes_read < 0)
395             GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
396                         _("Error reading from gnunet-peerinfo: %s\n"),
397                         STRERROR (errno));
398           else
399             GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
400                         _("Malformed output from gnunet-peerinfo!\n"));
401           cb = d->cb;
402           d->cb = NULL;
403           GNUNET_DISK_pipe_close (d->pipe_stdout);
404           d->pipe_stdout = NULL;
405           (void) GNUNET_OS_process_kill (d->proc, SIGKILL);
406           GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (d->proc));
407           GNUNET_OS_process_close (d->proc);
408           d->proc = NULL;
409           if (NULL != cb)
410             cb (d->cb_cls, NULL, d->cfg, d, _("`Failed to get hostkey!\n"));
411           return;
412         }
413       GNUNET_DISK_pipe_close (d->pipe_stdout);
414       d->pipe_stdout = NULL;
415       (void) GNUNET_OS_process_kill (d->proc, SIGKILL);
416       GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (d->proc));
417       GNUNET_OS_process_close (d->proc);
418       d->proc = NULL;
419 #if DEBUG_TESTING
420       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Successfully got hostkey!\n");
421 #endif
422       if (d->hostkey_callback != NULL)
423         {
424           d->hostkey_callback (d->hostkey_cls, &d->id, d, NULL);
425           d->phase = SP_HOSTKEY_CREATED;
426         }
427       else
428         {
429           d->phase = SP_TOPOLOGY_SETUP;
430         }
431       /* Fall through */
432     case SP_HOSTKEY_CREATED:
433       /* wait for topology finished */
434       if ((GNUNET_YES == d->dead)
435           || (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value ==
436               0))
437         {
438           cb = d->cb;
439           d->cb = NULL;
440           if (NULL != cb)
441             cb (d->cb_cls,
442                 NULL,
443                 d->cfg, d, _("`Failed while waiting for topology setup!\n"));
444           return;
445         }
446
447       d->task
448         = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT,
449                                         &start_fsm, d);
450       break;
451     case SP_TOPOLOGY_SETUP:
452       /* start GNUnet on remote host */
453       if (NULL == d->hostname)
454         {
455 #if DEBUG_TESTING
456           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
457                       "Starting `%s', with command `%s %s %s %s %s %s'.\n",
458                       "gnunet-arm", "gnunet-arm", "-c", d->cfgfile,
459                       "-L", "DEBUG", "-s");
460 #endif
461           d->proc = GNUNET_OS_start_process (NULL, NULL, "gnunet-arm",
462                                              "gnunet-arm", "-c", d->cfgfile,
463 #if DEBUG_TESTING
464                                              "-L", "DEBUG",
465 #endif
466                                              "-s", "-q", NULL);
467         }
468       else
469         {
470           if (d->username != NULL)
471             GNUNET_asprintf (&dst, "%s@%s", d->username, d->hostname);
472           else
473             dst = GNUNET_strdup (d->hostname);
474
475 #if DEBUG_TESTING
476           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
477                       "Starting `%s', with command `%s %s %s %s %s %s %s %s'.\n",
478                       "gnunet-arm", "ssh", dst, "gnunet-arm", "-c",
479                       d->cfgfile, "-L", "DEBUG", "-s", "-q");
480 #endif
481           if (d->ssh_port_str == NULL)
482             {
483               d->proc = GNUNET_OS_start_process (NULL, NULL, "ssh", "ssh",
484 #if !DEBUG_TESTING
485                                                  "-q",
486 #endif
487                                                  dst, "gnunet-arm",
488 #if DEBUG_TESTING
489                                                  "-L", "DEBUG",
490 #endif
491                                                  "-c", d->cfgfile, "-s", "-q",
492                                                  NULL);
493             }
494           else
495             {
496
497               d->proc = GNUNET_OS_start_process (NULL, NULL, "ssh",
498                                                  "ssh", "-p", d->ssh_port_str,
499 #if !DEBUG_TESTING
500                                                  "-q",
501 #endif
502                                                  dst, "gnunet-arm",
503 #if DEBUG_TESTING
504                                                  "-L", "DEBUG",
505 #endif
506                                                  "-c", d->cfgfile, "-s", "-q",
507                                                  NULL);
508             }
509           GNUNET_free (dst);
510         }
511       if (NULL == d->proc)
512         {
513           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
514                       _("Could not start `%s' process to start GNUnet.\n"),
515                       (NULL == d->hostname) ? "gnunet-arm" : "ssh");
516           cb = d->cb;
517           d->cb = NULL;
518           if (NULL != cb)
519             cb (d->cb_cls,
520                 NULL,
521                 d->cfg,
522                 d,
523                 (NULL == d->hostname)
524                 ? _("Failed to start `gnunet-arm' process.\n")
525                 : _("Failed to start `ssh' process.\n"));
526           return;
527         }
528 #if DEBUG_TESTING
529       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
530                   "Started `%s', waiting for `%s' to be up.\n",
531                   "gnunet-arm", "gnunet-service-core");
532 #endif
533       d->phase = SP_START_ARMING;
534       d->task
535         = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT,
536                                         &start_fsm, d);
537       break;
538     case SP_START_ARMING:
539       if (GNUNET_OK != GNUNET_OS_process_status (d->proc, &type, &code))
540         {
541           if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value ==
542               0)
543             {
544               cb = d->cb;
545               d->cb = NULL;
546               if (NULL != cb)
547                 cb (d->cb_cls,
548                     NULL,
549                     d->cfg,
550                     d,
551                     (NULL == d->hostname)
552                     ? _("`gnunet-arm' does not seem to terminate.\n")
553                     : _("`ssh' does not seem to terminate.\n"));
554               GNUNET_free(d->proc);
555               return;
556             }
557           /* wait some more */
558           d->task
559             = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT,
560                                             &start_fsm, d);
561           return;
562         }
563 #if DEBUG_TESTING
564       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
565                   "Successfully started `%s'.\n", "gnunet-arm");
566 #endif
567       GNUNET_free(d->proc);
568       d->phase = SP_START_CORE;
569 #if DEBUG_TESTING
570       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
571                   "Calling CORE_connect\n");
572 #endif
573       d->server = GNUNET_CORE_connect (d->cfg, 1,
574 #if NO_MORE_TIMEOUT_FIXME
575                                        ARM_START_WAIT,
576 #endif
577                                        d,
578                                        &testing_init,
579                                        NULL, NULL, NULL,
580                                        NULL, GNUNET_NO,
581                                        NULL, GNUNET_NO, no_handlers);
582       break;
583     case SP_START_CORE:
584       GNUNET_break (0);
585       break;
586     case SP_START_DONE:
587       GNUNET_break (0);
588       break;
589     case SP_SHUTDOWN_START:
590       /* confirm copying complete */
591       if (GNUNET_OK != GNUNET_OS_process_status (d->proc, &type, &code))
592         {
593           if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value ==
594               0)
595             {
596               if (NULL != d->dead_cb)
597                 d->dead_cb (d->dead_cb_cls,
598                             _
599                             ("either `gnunet-arm' or `ssh' does not seem to terminate.\n"));
600               if (d->th != NULL)
601                 {
602                   GNUNET_TRANSPORT_get_hello_cancel (d->th, &process_hello,
603                                                      d);
604                   GNUNET_TRANSPORT_disconnect (d->th);
605                   d->th = NULL;
606                 }
607               GNUNET_CONFIGURATION_destroy (d->cfg);
608               GNUNET_free (d->cfgfile);
609               GNUNET_free_non_null (d->hello);
610               GNUNET_free_non_null (d->hostname);
611               GNUNET_free_non_null (d->username);
612               GNUNET_free_non_null (d->shortname);
613               GNUNET_free_non_null (d->proc);
614               d->proc = NULL;
615               GNUNET_free (d);
616               return;
617             }
618           /* wait some more */
619           d->task
620             = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT,
621                                             &start_fsm, d);
622           return;
623         }
624       if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0))
625         {
626           if (NULL != d->dead_cb)
627             d->dead_cb (d->dead_cb_cls,
628                         _
629                         ("shutdown (either `gnunet-arm' or `ssh') did not complete cleanly.\n"));
630           if (d->th != NULL)
631             {
632               GNUNET_TRANSPORT_get_hello_cancel (d->th, &process_hello, d);
633               GNUNET_TRANSPORT_disconnect (d->th);
634               d->th = NULL;
635             }
636           if (d->server != NULL)
637             {
638               GNUNET_CORE_disconnect (d->server);
639               d->server = NULL;
640             }
641           GNUNET_CONFIGURATION_destroy (d->cfg);
642           GNUNET_free (d->cfgfile);
643           GNUNET_free_non_null (d->hello);
644           GNUNET_free_non_null (d->hostname);
645           GNUNET_free_non_null (d->username);
646           GNUNET_free_non_null (d->shortname);
647           GNUNET_free_non_null (d->proc);
648           d->proc = NULL;
649           GNUNET_free (d);
650           return;
651         }
652 #if DEBUG_TESTING
653       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer shutdown complete.\n");
654 #endif
655       if (d->server != NULL)
656         {
657           GNUNET_CORE_disconnect (d->server);
658           d->server = NULL;
659         }
660
661       if (d->th != NULL)
662         {
663           GNUNET_TRANSPORT_get_hello_cancel (d->th, &process_hello, d);
664           GNUNET_TRANSPORT_disconnect (d->th);
665           d->th = NULL;
666         }
667       /* state clean up and notifications */
668       if (d->churn == GNUNET_NO)
669         {
670           GNUNET_CONFIGURATION_destroy (d->cfg);
671           GNUNET_free (d->cfgfile);
672           GNUNET_free_non_null (d->hostname);
673           GNUNET_free_non_null (d->username);
674         }
675
676       GNUNET_free_non_null (d->hello);
677       d->hello = NULL;
678       GNUNET_free_non_null (d->shortname);
679       GNUNET_free_non_null (d->proc);
680       d->proc = NULL;
681       d->shortname = NULL;
682       if (NULL != d->dead_cb)
683         d->dead_cb (d->dead_cb_cls, NULL);
684
685       if (d->churn == GNUNET_NO)
686         GNUNET_free (d);
687
688       break;
689     case SP_CONFIG_UPDATE:
690       /* confirm copying complete */
691       if (GNUNET_OK != GNUNET_OS_process_status (d->proc, &type, &code))
692         {
693           if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0)       /* FIXME: config update should take timeout parameter! */
694             {
695               cb = d->cb;
696               d->cb = NULL;
697               if (NULL != cb)
698                 cb (d->cb_cls,
699                     NULL,
700                     d->cfg, d, _("`scp' does not seem to terminate.\n"));
701               return;
702             }
703           /* wait some more */
704           d->task
705             = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT,
706                                             &start_fsm, d);
707           return;
708         }
709       if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0))
710         {
711           if (NULL != d->update_cb)
712             d->update_cb (d->update_cb_cls,
713                           _("`scp' did not complete cleanly.\n"));
714           return;
715         }
716 #if DEBUG_TESTING
717       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
718                   "Successfully copied configuration file.\n");
719 #endif
720       if (NULL != d->update_cb)
721         d->update_cb (d->update_cb_cls, NULL);
722       d->phase = SP_START_DONE;
723       break;
724     }
725 }
726
727 /**
728  * Continues GNUnet daemon startup when user wanted to be notified
729  * once a hostkey was generated (for creating friends files, blacklists,
730  * etc.).
731  *
732  * @param daemon the daemon to finish starting
733  */
734 void
735 GNUNET_TESTING_daemon_continue_startup (struct GNUNET_TESTING_Daemon *daemon)
736 {
737   GNUNET_assert (daemon->phase == SP_HOSTKEY_CREATED);
738   daemon->phase = SP_TOPOLOGY_SETUP;
739 }
740
741 /**
742  * Check whether the given daemon is running.
743  *
744  * @param daemon the daemon to check
745  *
746  * @return GNUNET_YES if the daemon is up, GNUNET_NO if the
747  *         daemon is down, GNUNET_SYSERR on error.
748  */
749 int
750 GNUNET_TESTING_daemon_running (struct GNUNET_TESTING_Daemon *daemon)
751 {
752   if (daemon == NULL)
753     return GNUNET_SYSERR;
754
755   if (daemon->running == GNUNET_YES)
756     return GNUNET_YES;
757   return GNUNET_NO;
758 }
759
760
761 /**
762  * Start a peer that has previously been stopped using the daemon_stop
763  * call (and files weren't deleted and the allow restart flag)
764  *
765  * @param daemon the daemon to start (has been previously stopped)
766  * @param timeout how long to wait for restart
767  * @param cb the callback for notification when the peer is running
768  * @param cb_cls closure for the callback
769  */
770 void
771 GNUNET_TESTING_daemon_start_stopped (struct GNUNET_TESTING_Daemon *daemon,
772                                      struct GNUNET_TIME_Relative timeout,
773                                      GNUNET_TESTING_NotifyDaemonRunning cb,
774                                      void *cb_cls)
775 {
776   if (daemon->running == GNUNET_YES)
777     {
778       cb (cb_cls, &daemon->id, daemon->cfg, daemon,
779           "Daemon already running, can't restart!");
780       return;
781     }
782
783   daemon->cb = cb;
784   daemon->cb_cls = cb_cls;
785   daemon->phase = SP_TOPOLOGY_SETUP;
786   daemon->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
787
788   GNUNET_SCHEDULER_add_continuation (&start_fsm,
789                                      daemon,
790                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
791 }
792
793 /**
794  * Starts a GNUnet daemon.  GNUnet must be installed on the target
795  * system and available in the PATH.  The machine must furthermore be
796  * reachable via "ssh" (unless the hostname is "NULL") without the
797  * need to enter a password.
798  *
799  * @param cfg configuration to use
800  * @param timeout how long to wait starting up peers
801  * @param hostname name of the machine where to run GNUnet
802  *        (use NULL for localhost).
803  * @param ssh_username ssh username to use when connecting to hostname
804  * @param sshport port to pass to ssh process when connecting to hostname
805  * @param hostkey pointer to a hostkey to be written to disk (instead of being generated)
806  * @param hostkey_callback function to call once the hostkey has been
807  *        generated for this peer, but it hasn't yet been started
808  *        (NULL to start immediately, otherwise waits on GNUNET_TESTING_daemon_continue_start)
809  * @param hostkey_cls closure for hostkey callback
810  * @param cb function to call once peer is up, or failed to start
811  * @param cb_cls closure for cb
812  * @return handle to the daemon (actual start will be completed asynchronously)
813  */
814 struct GNUNET_TESTING_Daemon *
815 GNUNET_TESTING_daemon_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
816                              struct GNUNET_TIME_Relative timeout,
817                              const char *hostname,
818                              const char *ssh_username,
819                              uint16_t sshport,
820                              const char *hostkey,
821                              GNUNET_TESTING_NotifyHostkeyCreated
822                              hostkey_callback, void *hostkey_cls,
823                              GNUNET_TESTING_NotifyDaemonRunning cb,
824                              void *cb_cls)
825 {
826   struct GNUNET_TESTING_Daemon *ret;
827   char *arg;
828   char *username;
829   char *servicehome;
830   char *baseservicehome;
831   char *slash;
832   char *hostkeyfile;
833   char *temp_file_name;
834   struct GNUNET_DISK_FileHandle *fn;
835
836   ret = GNUNET_malloc (sizeof (struct GNUNET_TESTING_Daemon));
837   ret->hostname = (hostname == NULL) ? NULL : GNUNET_strdup (hostname);
838   if (sshport != 0)
839     {
840       GNUNET_asprintf (&ret->ssh_port_str, "%d", sshport);
841     }
842   else
843     ret->ssh_port_str = NULL;
844
845   /* Find service home and base service home directories, create it if it doesn't exist */
846   GNUNET_assert(GNUNET_OK ==
847                 GNUNET_CONFIGURATION_get_value_string (cfg,
848                                                        "PATHS",
849                                                        "SERVICEHOME",
850                                                        &servicehome));
851
852   GNUNET_assert (GNUNET_OK == GNUNET_DISK_directory_create (servicehome));
853   GNUNET_asprintf(&temp_file_name, "%s/gnunet-testing-config", servicehome);
854   ret->cfgfile = GNUNET_DISK_mktemp (temp_file_name);
855   GNUNET_free(temp_file_name);
856 #if DEBUG_TESTING
857   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
858               "Setting up peer with configuration file `%s'.\n",
859               ret->cfgfile);
860 #endif
861   if (NULL == ret->cfgfile)
862     {
863       GNUNET_free_non_null (ret->ssh_port_str);
864       GNUNET_free_non_null (ret->hostname);
865       GNUNET_free (ret);
866       return NULL;
867     }
868   ret->hostkey_callback = hostkey_callback;
869   ret->hostkey_cls = hostkey_cls;
870   ret->cb = cb;
871   ret->cb_cls = cb_cls;
872   ret->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
873   ret->cfg = GNUNET_CONFIGURATION_dup (cfg);
874   GNUNET_CONFIGURATION_set_value_string (ret->cfg,
875                                          "PATHS",
876                                          "DEFAULTCONFIG", ret->cfgfile);
877
878   /* Write hostkey to file, if we were given one */
879   hostkeyfile = NULL;
880   if (hostkey != NULL)
881     {
882       GNUNET_asprintf(&hostkeyfile, "%s/.hostkey", servicehome);
883       fn =
884       GNUNET_DISK_file_open (hostkeyfile,
885                              GNUNET_DISK_OPEN_READWRITE
886                              | GNUNET_DISK_OPEN_CREATE,
887                              GNUNET_DISK_PERM_USER_READ |
888                              GNUNET_DISK_PERM_USER_WRITE);
889       GNUNET_assert(fn != NULL);
890       GNUNET_assert(HOSTKEYFILESIZE == GNUNET_DISK_file_write(fn, hostkey, HOSTKEYFILESIZE));
891       GNUNET_assert(GNUNET_OK == GNUNET_DISK_file_close(fn));
892     }
893
894   /* write configuration to temporary file */
895   if (GNUNET_OK != GNUNET_CONFIGURATION_write (ret->cfg, ret->cfgfile))
896     {
897       if (0 != UNLINK (ret->cfgfile))
898         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
899                                   "unlink", ret->cfgfile);
900       GNUNET_CONFIGURATION_destroy (ret->cfg);
901       GNUNET_free_non_null (ret->hostname);
902       GNUNET_free (ret->cfgfile);
903       GNUNET_free (ret);
904       return NULL;
905     }
906   if (ssh_username != NULL)
907     username = GNUNET_strdup (ssh_username);
908   if ((ssh_username == NULL) && (GNUNET_OK !=
909                                  GNUNET_CONFIGURATION_get_value_string (cfg,
910                                                                         "TESTING",
911                                                                         "USERNAME",
912                                                                         &username)))
913     {
914       if (NULL != getenv ("USER"))
915         username = GNUNET_strdup (getenv ("USER"));
916       else
917         username = NULL;
918     }
919   ret->username = username;
920
921   /* copy directory to remote host */
922   if (NULL != hostname)
923     {
924 #if DEBUG_TESTING
925       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
926                   "Copying configuration directory to host `%s'.\n", hostname);
927 #endif
928       baseservicehome = GNUNET_strdup(servicehome);
929       /* Remove trailing /'s */
930       while (baseservicehome[strlen(baseservicehome) - 1] == '/')
931         baseservicehome[strlen(baseservicehome) - 1] = '\0';
932       /* Find next directory /, jump one ahead */
933       slash = strrchr(baseservicehome, '/');
934       if (slash != NULL)
935         *(++slash) = '\0';
936
937       ret->phase = SP_COPYING;
938       if (NULL != username)
939         GNUNET_asprintf (&arg, "%s@%s:%s", username, hostname, baseservicehome);
940       else
941         GNUNET_asprintf (&arg, "%s:%s", hostname, baseservicehome);
942
943       if (ret->ssh_port_str == NULL)
944         {
945           ret->proc = GNUNET_OS_start_process (NULL, NULL, "scp", "scp", "-r",
946 #if !DEBUG_TESTING
947                                                "-q",
948 #endif
949                                                servicehome, arg, NULL);
950 #if DEBUG_TESTING
951           GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "copying directory with command scp -r %s %s\n", servicehome, arg);
952 #endif
953         }
954       else
955         {
956           ret->proc = GNUNET_OS_start_process (NULL, NULL, "scp",
957                                                "scp", "-r", "-P", ret->ssh_port_str,
958 #if !DEBUG_TESTING
959                                                "-q",
960 #endif
961                                                servicehome, arg, NULL);
962         }
963       GNUNET_free (arg);
964       if (NULL == ret->proc)
965         {
966           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
967                       _
968                       ("Could not start `%s' process to copy configuration directory.\n"),
969                       "scp");
970           if (0 != UNLINK (ret->cfgfile))
971             GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
972                                       "unlink", ret->cfgfile);
973           GNUNET_CONFIGURATION_destroy (ret->cfg);
974           GNUNET_free_non_null (ret->hostname);
975           GNUNET_free_non_null (ret->username);
976           GNUNET_free (ret->cfgfile);
977           GNUNET_free (ret);
978           if ((hostkey != NULL) && (0 != UNLINK(hostkeyfile)))
979             GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
980                                       "unlink", hostkeyfile);
981           GNUNET_free_non_null(hostkeyfile);
982           GNUNET_assert (GNUNET_OK == GNUNET_DISK_directory_remove (servicehome));
983           GNUNET_free(servicehome);
984           return NULL;
985         }
986       ret->task
987         = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT,
988                                         &start_fsm, ret);
989       GNUNET_free_non_null(hostkeyfile);
990       GNUNET_free(servicehome);
991       return ret;
992     }
993 #if DEBUG_TESTING
994   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
995               "No need to copy configuration file since we are running locally.\n");
996 #endif
997   ret->phase = SP_COPIED;
998   GNUNET_SCHEDULER_add_continuation (&start_fsm,
999                                      ret,
1000                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1001   GNUNET_free_non_null(hostkeyfile);
1002   GNUNET_free(servicehome);
1003   return ret;
1004 }
1005
1006
1007 /**
1008  * Restart (stop and start) a GNUnet daemon.
1009  *
1010  * @param d the daemon that should be restarted
1011  * @param cb function called once the daemon is (re)started
1012  * @param cb_cls closure for cb
1013  */
1014 void
1015 GNUNET_TESTING_daemon_restart (struct GNUNET_TESTING_Daemon *d,
1016                                GNUNET_TESTING_NotifyDaemonRunning cb,
1017                                void *cb_cls)
1018 {
1019   char *arg;
1020   char *del_arg;
1021
1022   del_arg = NULL;
1023   if (NULL != d->cb)
1024     {
1025       d->dead = GNUNET_YES;
1026       return;
1027     }
1028
1029   d->cb = cb;
1030   d->cb_cls = cb_cls;
1031
1032   if (d->phase == SP_CONFIG_UPDATE)
1033     {
1034       GNUNET_SCHEDULER_cancel (d->task);
1035       d->phase = SP_START_DONE;
1036     }
1037   if (d->server != NULL)
1038     {
1039       GNUNET_CORE_disconnect (d->server);
1040       d->server = NULL;
1041     }
1042
1043   if (d->th != NULL)
1044     {
1045       GNUNET_TRANSPORT_get_hello_cancel (d->th, &process_hello, d);
1046       GNUNET_TRANSPORT_disconnect (d->th);
1047       d->th = NULL;
1048     }
1049   /* state clean up and notifications */
1050   GNUNET_free_non_null (d->hello);
1051
1052 #if DEBUG_TESTING
1053   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1054               _("Terminating peer `%4s'\n"), GNUNET_i2s (&d->id));
1055 #endif
1056
1057   d->phase = SP_START_ARMING;
1058
1059   /* Check if this is a local or remote process */
1060   if (NULL != d->hostname)
1061     {
1062 #if DEBUG_TESTING
1063       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1064                   "Stopping gnunet-arm with config `%s' on host `%s'.\n",
1065                   d->cfgfile, d->hostname);
1066 #endif
1067
1068       if (d->username != NULL)
1069         GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname);
1070       else
1071         arg = GNUNET_strdup (d->hostname);
1072
1073       d->proc = GNUNET_OS_start_process (NULL, NULL, "ssh", "ssh",
1074 #if !DEBUG_TESTING
1075                                          "-q",
1076 #endif
1077                                          arg, "gnunet-arm",
1078 #if DEBUG_TESTING
1079                                          "-L", "DEBUG",
1080 #endif
1081                                          "-c", d->cfgfile, "-e", "-r", NULL);
1082       /* Use -r to restart arm and all services */
1083
1084       GNUNET_free (arg);
1085     }
1086   else
1087     {
1088 #if DEBUG_TESTING
1089       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1090                   "Stopping gnunet-arm with config `%s' locally.\n",
1091                   d->cfgfile);
1092 #endif
1093       d->proc = GNUNET_OS_start_process (NULL, NULL, "gnunet-arm",
1094                                          "gnunet-arm",
1095 #if DEBUG_TESTING
1096                                          "-L", "DEBUG",
1097 #endif
1098                                          "-c", d->cfgfile, "-e", "-r", NULL);
1099     }
1100
1101   GNUNET_free_non_null (del_arg);
1102   d->task
1103     = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT,
1104                                     &start_fsm, d);
1105
1106 }
1107
1108
1109 /**
1110  * Stops a GNUnet daemon.
1111  *
1112  * @param d the daemon that should be stopped
1113  * @param timeout how long to wait for process for shutdown to complete
1114  * @param cb function called once the daemon was stopped
1115  * @param cb_cls closure for cb
1116  * @param delete_files GNUNET_YES to remove files, GNUNET_NO
1117  *        to leave them
1118  * @param allow_restart GNUNET_YES to restart peer later (using this API)
1119  *        GNUNET_NO to kill off and clean up for good
1120  */
1121 void
1122 GNUNET_TESTING_daemon_stop (struct GNUNET_TESTING_Daemon *d,
1123                             struct GNUNET_TIME_Relative timeout,
1124                             GNUNET_TESTING_NotifyCompletion cb, void *cb_cls,
1125                             int delete_files, int allow_restart)
1126 {
1127   char *arg;
1128   char *del_arg;
1129   d->dead_cb = cb;
1130   d->dead_cb_cls = cb_cls;
1131
1132   if (NULL != d->cb)
1133     {
1134 #if DEBUG_TESTING
1135       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1136                   _("Setting d->dead on peer `%4s'\n"), GNUNET_i2s (&d->id));
1137 #endif
1138       d->dead = GNUNET_YES;
1139       return;
1140     }
1141
1142   if ((d->running == GNUNET_NO) && (d->churn == GNUNET_YES))    /* Peer has already been stopped in churn context! */
1143     {
1144       /* Free what was left from churning! */
1145       GNUNET_assert (d->cfg != NULL);
1146       GNUNET_CONFIGURATION_destroy (d->cfg);
1147       if (delete_files == GNUNET_YES)
1148         {
1149           if (0 != UNLINK (d->cfgfile))
1150             {
1151               GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "unlink");
1152             }
1153         }
1154       GNUNET_free (d->cfgfile);
1155       GNUNET_free_non_null (d->hostname);
1156       GNUNET_free_non_null (d->username);
1157       if (NULL != d->dead_cb)
1158         d->dead_cb (d->dead_cb_cls, NULL);
1159       GNUNET_free (d);
1160       return;
1161     }
1162
1163   del_arg = NULL;
1164   if (delete_files == GNUNET_YES)
1165     {
1166       GNUNET_asprintf (&del_arg, "-d");
1167     }
1168
1169   if (d->phase == SP_CONFIG_UPDATE)
1170     {
1171       GNUNET_SCHEDULER_cancel (d->task);
1172       d->phase = SP_START_DONE;
1173     }
1174   /** Move this call to scheduled shutdown as fix for CORE_connect calling daemon_stop?
1175   if (d->server != NULL)
1176     {
1177       GNUNET_CORE_disconnect (d->server);
1178       d->server = NULL;
1179     }
1180     */
1181   /* shutdown ARM process (will terminate others) */
1182 #if DEBUG_TESTING
1183   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1184               _("Terminating peer `%4s'\n"), GNUNET_i2s (&d->id));
1185 #endif
1186   d->phase = SP_SHUTDOWN_START;
1187   d->running = GNUNET_NO;
1188   if (allow_restart == GNUNET_YES)
1189     d->churn = GNUNET_YES;
1190   if (d->th != NULL)
1191     {
1192       GNUNET_TRANSPORT_get_hello_cancel (d->th, &process_hello, d);
1193       GNUNET_TRANSPORT_disconnect (d->th);
1194       d->th = NULL;
1195     }
1196   /* Check if this is a local or remote process */
1197   if (NULL != d->hostname)
1198     {
1199 #if DEBUG_TESTING
1200       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1201                   "Stopping gnunet-arm with config `%s' on host `%s'.\n",
1202                   d->cfgfile, d->hostname);
1203 #endif
1204
1205       if (d->username != NULL)
1206         GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname);
1207       else
1208         arg = GNUNET_strdup (d->hostname);
1209
1210       d->proc = GNUNET_OS_start_process (NULL, NULL, "ssh", "ssh",
1211 #if !DEBUG_TESTING
1212                                          "-q",
1213 #endif
1214                                          arg, "gnunet-arm",
1215 #if DEBUG_TESTING
1216                                          "-L", "DEBUG",
1217 #endif
1218                                          "-c", d->cfgfile, "-e", "-q",
1219                                          del_arg, NULL);
1220       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1221                   "Stopping gnunet-arm with command ssh %s gnunet-arm -c %s -e -q %s\n",
1222                   arg, "gnunet-arm", d->cfgfile, del_arg);
1223       /* Use -e to end arm, and -d to remove temp files */
1224       GNUNET_free (arg);
1225     }
1226   else
1227     {
1228 #if DEBUG_TESTING
1229       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1230                   "Stopping gnunet-arm with config `%s' locally.\n",
1231                   d->cfgfile);
1232 #endif
1233       d->proc = GNUNET_OS_start_process (NULL, NULL, "gnunet-arm",
1234                                          "gnunet-arm",
1235 #if DEBUG_TESTING
1236                                          "-L", "DEBUG",
1237 #endif
1238                                          "-c", d->cfgfile, "-e", "-q",
1239                                          del_arg, NULL);
1240     }
1241
1242   GNUNET_free_non_null (del_arg);
1243   d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
1244   d->task = GNUNET_SCHEDULER_add_now (&start_fsm, d);
1245 }
1246
1247
1248 /**
1249  * Changes the configuration of a GNUnet daemon.
1250  *
1251  * @param d the daemon that should be modified
1252  * @param cfg the new configuration for the daemon
1253  * @param cb function called once the configuration was changed
1254  * @param cb_cls closure for cb
1255  */
1256 void
1257 GNUNET_TESTING_daemon_reconfigure (struct GNUNET_TESTING_Daemon *d,
1258                                    struct GNUNET_CONFIGURATION_Handle *cfg,
1259                                    GNUNET_TESTING_NotifyCompletion cb,
1260                                    void *cb_cls)
1261 {
1262   char *arg;
1263
1264   if (d->phase != SP_START_DONE)
1265     {
1266       if (NULL != cb)
1267         cb (cb_cls,
1268             _
1269             ("Peer not yet running, can not change configuration at this point."));
1270       return;
1271     }
1272
1273   /* 1) write configuration to temporary file */
1274   if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, d->cfgfile))
1275     {
1276       if (NULL != cb)
1277         cb (cb_cls, _("Failed to write new configuration to disk."));
1278       return;
1279     }
1280
1281   /* 2) copy file to remote host (if necessary) */
1282   if (NULL == d->hostname)
1283     {
1284       /* signal success */
1285       if (NULL != cb)
1286         cb (cb_cls, NULL);
1287       return;
1288     }
1289 #if DEBUG_TESTING
1290   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1291               "Copying updated configuration file to remote host `%s'.\n",
1292               d->hostname);
1293 #endif
1294   d->phase = SP_CONFIG_UPDATE;
1295   if (NULL != d->username)
1296     GNUNET_asprintf (&arg, "%s@%s:%s", d->username, d->hostname, d->cfgfile);
1297   else
1298     GNUNET_asprintf (&arg, "%s:%s", d->hostname, d->cfgfile);
1299   d->proc = GNUNET_OS_start_process (NULL, NULL, "scp", "scp",
1300 #if !DEBUG_TESTING
1301                                      "-q",
1302 #endif
1303                                      d->cfgfile, arg, NULL);
1304   GNUNET_free (arg);
1305   if (NULL == d->proc)
1306     {
1307       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1308                   _
1309                   ("Could not start `%s' process to copy configuration file.\n"),
1310                   "scp");
1311       if (NULL != cb)
1312         cb (cb_cls, _("Failed to copy new configuration to remote machine."));
1313       d->phase = SP_START_DONE;
1314       return;
1315     }
1316   d->update_cb = cb;
1317   d->update_cb_cls = cb_cls;
1318   d->task
1319     = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT,
1320                                     &start_fsm, d);
1321 }
1322
1323
1324 /**
1325  * Data kept for each pair of peers that we try
1326  * to connect.
1327  */
1328 struct ConnectContext
1329 {
1330   /**
1331    * Testing handle to the first daemon.
1332    */
1333   struct GNUNET_TESTING_Daemon *d1;
1334
1335   /**
1336    * Handle to core of first daemon (to check connect)
1337    */
1338   struct GNUNET_CORE_Handle *d1core;
1339
1340   /**
1341    * Have we actually connected to the core of the first daemon yet?
1342    */
1343   int d1core_ready;
1344
1345   /**
1346    * Testing handle to the second daemon.
1347    */
1348   struct GNUNET_TESTING_Daemon *d2;
1349
1350   /**
1351    * Handler for the request to core to connect to this peer.
1352    */
1353   struct GNUNET_CORE_PeerRequestHandle *connect_request_handle;
1354
1355   /**
1356    * Transport handle to the first daemon (to offer the HELLO of the second daemon to).
1357    */
1358   struct GNUNET_TRANSPORT_Handle *d1th;
1359
1360   /**
1361    * Function to call once we are done (or have timed out).
1362    */
1363   GNUNET_TESTING_NotifyConnection cb;
1364
1365   /**
1366    * Closure for "nb".
1367    */
1368   void *cb_cls;
1369
1370   /**
1371    * When should this operation be complete (or we must trigger
1372    * a timeout).
1373    */
1374   struct GNUNET_TIME_Absolute timeout;
1375
1376   /**
1377    * The relative timeout from whence this connect attempt was
1378    * started.  Allows for reconnect attempts.
1379    */
1380   struct GNUNET_TIME_Relative relative_timeout;
1381
1382   /**
1383    * Maximum number of connect attempts, will retry connection
1384    * this number of times on failures.
1385    */
1386   unsigned int max_connect_attempts;
1387
1388   /**
1389    * Hello timeout task
1390    */
1391   GNUNET_SCHEDULER_TaskIdentifier hello_send_task;
1392
1393   /**
1394    * Connect timeout task
1395    */
1396   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
1397
1398   /**
1399    * When should this operation be complete (or we must trigger
1400    * a timeout).
1401    */
1402   struct GNUNET_TIME_Relative timeout_hello;
1403
1404   /**
1405    * Was the connection attempt successful?
1406    */
1407   int connected;
1408
1409   /**
1410    * The distance between the two connected peers
1411    */
1412   uint32_t distance;
1413 };
1414
1415
1416 /** Forward declaration **/
1417 static void
1418 reattempt_daemons_connect (void *cls,
1419                            const struct GNUNET_SCHEDULER_TaskContext *tc);
1420
1421
1422 /**
1423  * Notify callback about success or failure of the attempt
1424  * to connect the two peers
1425  *
1426  * @param cls our "struct ConnectContext" (freed)
1427  * @param tc reason tells us if we succeeded or failed
1428  */
1429 static void
1430 notify_connect_result (void *cls,
1431                        const struct GNUNET_SCHEDULER_TaskContext *tc)
1432 {
1433   struct ConnectContext *ctx = cls;
1434   struct GNUNET_TIME_Relative remaining;
1435
1436   ctx->timeout_task = GNUNET_SCHEDULER_NO_TASK;
1437   if (ctx->hello_send_task != GNUNET_SCHEDULER_NO_TASK)
1438     {
1439       GNUNET_SCHEDULER_cancel (ctx->hello_send_task);
1440       ctx->hello_send_task = GNUNET_SCHEDULER_NO_TASK;
1441     }
1442
1443   if (ctx->connect_request_handle != NULL)
1444     {
1445       GNUNET_CORE_peer_request_connect_cancel (ctx->connect_request_handle);
1446       ctx->connect_request_handle = NULL;
1447     }
1448   if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
1449     {
1450       if (ctx->d1th != NULL)
1451         GNUNET_TRANSPORT_disconnect (ctx->d1th);
1452       ctx->d1th = NULL;
1453       if (ctx->d1core != NULL)
1454         GNUNET_CORE_disconnect (ctx->d1core);
1455 #if CONNECT_CORE2
1456       if (ctx->d2core != NULL)
1457         GNUNET_CORE_disconnect (ctx->d2core);
1458       ctx->d2core = NULL;
1459 #endif
1460       ctx->d1core = NULL;
1461
1462       GNUNET_free (ctx);
1463       return;
1464     }
1465
1466   remaining = GNUNET_TIME_absolute_get_remaining (ctx->timeout);
1467
1468   if (ctx->connected == GNUNET_YES)
1469     {
1470       if (ctx->cb != NULL)
1471         {
1472           ctx->cb (ctx->cb_cls,
1473                    &ctx->d1->id,
1474                    &ctx->d2->id,
1475                    ctx->distance,
1476                    ctx->d1->cfg, ctx->d2->cfg, ctx->d1, ctx->d2, NULL);
1477         }
1478     }
1479   else if (remaining.rel_value > 0)
1480     {
1481       if (ctx->d1core != NULL)
1482         {
1483           GNUNET_CORE_disconnect (ctx->d1core);
1484           ctx->d1core = NULL;
1485         }
1486       ctx->d1core_ready = GNUNET_NO;
1487 #if CONNECT_CORE2
1488       if (ctx->d2core != NULL)
1489         {
1490           GNUNET_CORE_disconnect (ctx->d2core);
1491           ctx->d2core = NULL;
1492         }
1493 #endif
1494
1495       if (ctx->d1th != NULL)
1496         {
1497           GNUNET_TRANSPORT_disconnect (ctx->d1th);
1498           ctx->d1th = NULL;
1499         }
1500       GNUNET_SCHEDULER_add_now (&reattempt_daemons_connect, ctx);
1501       return;
1502     }
1503   else
1504     {
1505       if (ctx->cb != NULL)
1506         {
1507           ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, 0, ctx->d1->cfg,
1508                    ctx->d2->cfg, ctx->d1, ctx->d2,
1509                    _("Peers failed to connect"));
1510         }
1511     }
1512
1513   if (ctx->d1th != NULL)
1514     GNUNET_TRANSPORT_disconnect (ctx->d1th);
1515   ctx->d1th = NULL;
1516   if (ctx->d1core != NULL)
1517     GNUNET_CORE_disconnect (ctx->d1core);
1518   ctx->d1core = NULL;
1519   GNUNET_free (ctx);
1520 }
1521
1522
1523 /**
1524  * Success, connection is up.  Signal client our success.
1525  *
1526  * @param cls our "struct ConnectContext"
1527  * @param peer identity of the peer that has connected
1528  * @param atsi performance information
1529  *
1530  */
1531 static void
1532 connect_notify (void *cls,
1533                 const struct GNUNET_PeerIdentity *peer,
1534                 const struct GNUNET_TRANSPORT_ATS_Information *atsi)
1535 {
1536   struct ConnectContext *ctx = cls;
1537
1538   if (0 == memcmp (&ctx->d2->id, peer, sizeof (struct GNUNET_PeerIdentity)))
1539     {
1540       ctx->connected = GNUNET_YES;
1541       ctx->distance = 0;        /* FIXME: distance */
1542       if (ctx->hello_send_task != GNUNET_SCHEDULER_NO_TASK)
1543         GNUNET_SCHEDULER_cancel(ctx->hello_send_task);
1544       GNUNET_SCHEDULER_cancel (ctx->timeout_task);
1545       ctx->timeout_task = GNUNET_SCHEDULER_add_now (&notify_connect_result,
1546                                                     ctx);
1547     }
1548 }
1549
1550 #if CONNECT_CORE2
1551 /**
1552  * Success, connection is up.  Signal client our success.
1553  *
1554  * @param cls our "struct ConnectContext"
1555  * @param peer identity of the peer that has connected
1556  * @param atsi performance information
1557  *
1558  */
1559 static void
1560 connect_notify_core2 (void *cls,
1561                       const struct GNUNET_PeerIdentity *peer,
1562                       const struct GNUNET_TRANSPORT_ATS_Information *atsi)
1563 {
1564   struct ConnectContext *ctx = cls;
1565
1566   if (memcmp (&ctx->d2->id, peer, sizeof (struct GNUNET_PeerIdentity)) == 0)
1567     {
1568       ctx->connected = GNUNET_YES;
1569       ctx->distance = 0;        /* FIXME: distance */
1570       GNUNET_SCHEDULER_cancel (ctx->timeout_task);
1571       ctx->timeout_task = GNUNET_SCHEDULER_add_now (&notify_connect_result,
1572                                                     ctx);
1573     }
1574
1575 }
1576 #endif
1577
1578 /**
1579  * Task called once a core connect request has been transmitted.
1580  *
1581  * @param cls struct ConnectContext
1582  * @param success was the request successful?
1583  */
1584 void
1585 core_connect_request_cont (void *cls,
1586                            int success)
1587 {
1588   struct ConnectContext *ctx = cls;
1589
1590   ctx->connect_request_handle = NULL;
1591 }
1592
1593 static void
1594 send_hello (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1595 {
1596   struct ConnectContext *ctx = cls;
1597   struct GNUNET_MessageHeader *hello;
1598   ctx->hello_send_task = GNUNET_SCHEDULER_NO_TASK;
1599   if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
1600     return;
1601   if ((ctx->d1core_ready == GNUNET_YES) && (ctx->d2->hello != NULL)
1602       && (NULL != GNUNET_HELLO_get_header (ctx->d2->hello)))
1603     {
1604       hello = GNUNET_HELLO_get_header (ctx->d2->hello);
1605       GNUNET_assert (hello != NULL);
1606       GNUNET_TRANSPORT_offer_hello (ctx->d1th, hello);
1607       GNUNET_assert (ctx->d1core != NULL);
1608       ctx->connect_request_handle =
1609         GNUNET_CORE_peer_request_connect (ctx->d1core,
1610                                           GNUNET_TIME_relative_divide
1611                                           (ctx->relative_timeout,
1612                                            ctx->max_connect_attempts + 1),
1613                                            &ctx->d2->id,
1614                                            &core_connect_request_cont, ctx);
1615 #if DEBUG_TESTING
1616       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1617                   "Sending connect request to CORE of %s for peer %s\n",
1618                   GNUNET_i2s (&ctx->d1->id),
1619                   GNUNET_h2s (&ctx->d2->id.hashPubKey));
1620 #endif
1621       ctx->timeout_hello =
1622         GNUNET_TIME_relative_add (ctx->timeout_hello,
1623                                   GNUNET_TIME_relative_multiply
1624                                   (GNUNET_TIME_UNIT_MILLISECONDS, 500));
1625     }
1626   ctx->hello_send_task = GNUNET_SCHEDULER_add_delayed (ctx->timeout_hello,
1627                                                        &send_hello, ctx);
1628 }
1629
1630 /**
1631  * Notify of a successful connection to the core service.
1632  *
1633  * @param cls a ConnectContext
1634  * @param server handle to the core service
1635  * @param my_identity the peer identity of this peer
1636  * @param publicKey the public key of the peer
1637  */
1638 void
1639 core_init_notify (void *cls,
1640                   struct GNUNET_CORE_Handle * server,
1641                   const struct GNUNET_PeerIdentity *
1642                   my_identity,
1643                   const struct
1644                   GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *
1645                   publicKey)
1646 {
1647   struct ConnectContext *connect_ctx = cls;
1648   connect_ctx->d1core_ready = GNUNET_YES;
1649 }
1650
1651 /**
1652  * Iterator for currently known peers, to ensure
1653  * that we don't try to send duplicate connect
1654  * requests to core.
1655  *
1656  * @param cls our "struct ConnectContext"
1657  * @param peer identity of the peer that has connected,
1658  *        NULL when iteration has finished
1659  * @param atsi performance information
1660  *
1661  */
1662 static void
1663 core_initial_iteration (void *cls,
1664                         const struct GNUNET_PeerIdentity *peer,
1665                         const struct GNUNET_TRANSPORT_ATS_Information *atsi)
1666 {
1667   struct ConnectContext *ctx = cls;
1668
1669   if ((peer != NULL) &&
1670       (0 == memcmp (&ctx->d2->id, peer, sizeof (struct GNUNET_PeerIdentity))))
1671     {
1672       ctx->connected = GNUNET_YES;
1673       ctx->distance = 0;        /* FIXME: distance */
1674       return;
1675     }
1676   else if (peer == NULL) /* Peer not already connected, need to schedule connect request! */
1677     {
1678       if (ctx->connected == GNUNET_YES)
1679         {
1680           ctx->timeout_task = GNUNET_SCHEDULER_add_now (&notify_connect_result,
1681                                                         ctx);
1682           return;
1683         }
1684
1685       ctx->d1core = GNUNET_CORE_connect (ctx->d1->cfg, 1,
1686                                          ctx,
1687                                          &core_init_notify,
1688                                          &connect_notify, NULL, NULL,
1689                                          NULL, GNUNET_NO,
1690                                          NULL, GNUNET_NO, no_handlers);
1691       if (ctx->d1core == NULL)
1692         {
1693           GNUNET_free (ctx);
1694           if (NULL != ctx->cb)
1695             ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, 0, ctx->d1->cfg, ctx->d2->cfg, ctx->d1, ctx->d2,
1696                 _("Failed to connect to core service of first peer!\n"));
1697           return;
1698         }
1699
1700 #if DEBUG_TESTING > 2
1701   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1702               "Asked to connect peer %s to peer %s\n",
1703               ctx->d1->shortname, ctx->d2->shortname);
1704   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1705               "Connecting to transport service of peer %s\n", ctx->d2->shortname);
1706
1707 #endif
1708
1709       ctx->d1th = GNUNET_TRANSPORT_connect (ctx->d1->cfg,
1710                                             &ctx->d1->id, ctx->d1, NULL, NULL, NULL);
1711       if (ctx->d1th == NULL)
1712         {
1713           GNUNET_CORE_disconnect (ctx->d1core);
1714           GNUNET_free (ctx);
1715           if (NULL != ctx->cb)
1716             ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, 0, ctx->d1->cfg, ctx->d2->cfg, ctx->d1, ctx->d2,
1717                 _("Failed to connect to transport service!\n"));
1718           return;
1719         }
1720
1721       ctx->timeout_task =
1722         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_divide
1723                                       (ctx->relative_timeout,
1724                                        ctx->max_connect_attempts),
1725                                        &notify_connect_result, ctx);
1726
1727       ctx->hello_send_task = GNUNET_SCHEDULER_add_now (&send_hello, ctx);
1728     }
1729 }
1730
1731
1732 /**
1733  * Establish a connection between two GNUnet daemons.
1734  *
1735  * @param d1 handle for the first daemon
1736  * @param d2 handle for the second daemon
1737  * @param timeout how long is the connection attempt
1738  *        allowed to take?
1739  * @param max_connect_attempts how many times should we try to reconnect
1740  *        (within timeout)
1741  * @param cb function to call at the end
1742  * @param cb_cls closure for cb
1743  */
1744 void
1745 GNUNET_TESTING_daemons_connect (struct GNUNET_TESTING_Daemon *d1,
1746                                 struct GNUNET_TESTING_Daemon *d2,
1747                                 struct GNUNET_TIME_Relative timeout,
1748                                 unsigned int max_connect_attempts,
1749                                 GNUNET_TESTING_NotifyConnection cb,
1750                                 void *cb_cls)
1751 {
1752   struct ConnectContext *ctx;
1753
1754   if ((d1->running == GNUNET_NO) || (d2->running == GNUNET_NO))
1755     {
1756       if (NULL != cb)
1757         cb (cb_cls, &d1->id, &d2->id, 0, d1->cfg, d2->cfg, d1, d2,
1758             _("Peers are not fully running yet, can not connect!\n"));
1759       return;
1760     }
1761   ctx = GNUNET_malloc (sizeof (struct ConnectContext));
1762   ctx->d1 = d1;
1763   ctx->d2 = d2;
1764   ctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1765   ctx->timeout_hello =
1766     GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 500);
1767   ctx->relative_timeout = timeout;
1768   ctx->cb = cb;
1769   ctx->cb_cls = cb_cls;
1770   ctx->max_connect_attempts = max_connect_attempts;
1771   ctx->connected = GNUNET_NO;
1772 #if DEBUG_TESTING
1773   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1774               "Asked to connect peer %s to peer %s\n",
1775               d1->shortname, d2->shortname);
1776 #endif
1777
1778   /* Core is up! Iterate over all _known_ peers first to check if we are already connected to the peer! */
1779   GNUNET_CORE_is_peer_connected (ctx->d1->cfg, &ctx->d2->id, &core_initial_iteration, ctx);
1780   /* GNUNET_CORE_iterate_peers(ctx->d1->cfg, &core_initial_iteration, ctx); */
1781
1782 }
1783
1784 static void
1785 reattempt_daemons_connect (void *cls,
1786                            const struct GNUNET_SCHEDULER_TaskContext *tc)
1787 {
1788
1789   struct ConnectContext *ctx = cls;
1790   if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
1791     {
1792       return;
1793     }
1794 #if DEBUG_TESTING_RECONNECT
1795   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1796               "re-attempting connect of peer %s to peer %s\n",
1797               ctx->d1->shortname, ctx->d2->shortname);
1798 #endif
1799
1800   GNUNET_assert (ctx->d1core == NULL);
1801   ctx->d1core_ready = GNUNET_NO;
1802   ctx->d1core = GNUNET_CORE_connect (ctx->d1->cfg, 1,
1803                                      ctx,
1804                                      &core_init_notify,
1805                                      &connect_notify, NULL, NULL,
1806                                      NULL, GNUNET_NO,
1807                                      NULL, GNUNET_NO, no_handlers);
1808   if (ctx->d1core == NULL)
1809     {
1810       if (NULL != ctx->cb)
1811         ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, 0, ctx->d1->cfg,
1812                  ctx->d2->cfg, ctx->d1, ctx->d2,
1813                  _("Failed to connect to core service of first peer!\n"));
1814       GNUNET_free (ctx);
1815       return;
1816     }
1817
1818   ctx->d1th = GNUNET_TRANSPORT_connect (ctx->d1->cfg,
1819                                         &ctx->d1->id,
1820                                         ctx->d1, NULL, NULL, NULL);
1821   if (ctx->d1th == NULL)
1822     {
1823       GNUNET_CORE_disconnect (ctx->d1core);
1824       GNUNET_free (ctx);
1825       if (NULL != ctx->cb)
1826         ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, 0, ctx->d1->cfg,
1827                  ctx->d2->cfg, ctx->d1, ctx->d2,
1828                  _("Failed to connect to transport service!\n"));
1829       return;
1830     }
1831
1832   ctx->timeout_task =
1833     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_divide
1834                                   (ctx->relative_timeout,
1835                                    ctx->max_connect_attempts),
1836                                   &notify_connect_result, ctx);
1837
1838   ctx->hello_send_task = GNUNET_SCHEDULER_add_now (&send_hello, ctx);
1839 }
1840
1841 /* end of testing.c */