documenting
[oweals/gnunet.git] / src / testing / testing.c
1 /*
2       This file is part of GNUnet
3       (C) 2008, 2009 Christian Grothoff (and other contributing authors)
4
5       GNUnet is free software; you can redistribute it and/or modify
6       it under the terms of the GNU General Public License as published
7       by the Free Software Foundation; either version 2, or (at your
8       option) any later version.
9
10       GNUnet is distributed in the hope that it will be useful, but
11       WITHOUT ANY WARRANTY; without even the implied warranty of
12       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13       General Public License for more details.
14
15       You should have received a copy of the GNU General Public License
16       along with GNUnet; see the file COPYING.  If not, write to the
17       Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18       Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * @file testing/testing.c
23  * @brief convenience API for writing testcases for GNUnet
24  *        Many testcases need to start and stop gnunetd,
25  *        and this library is supposed to make that easier
26  *        for TESTCASES.  Normal programs should always
27  *        use functions from gnunet_{util,arm}_lib.h.  This API is
28  *        ONLY for writing testcases!
29  * @author Christian Grothoff
30  *
31  * TODO:
32  * - modify configuration to allow 2087-connections from
33  *   controlling host (otherwise shutdown won't work)
34  * 
35  */
36 #include "platform.h"
37 #include "gnunet_arm_service.h"
38 #include "gnunet_core_service.h"
39 #include "gnunet_constants.h"
40 #include "gnunet_testing_lib.h"
41 #include "gnunet_transport_service.h"
42
43 #define DEBUG_TESTING GNUNET_YES
44
45 /**
46  * How long do we wait after starting gnunet-service-arm
47  * for the core service to be alive?
48  */
49 #define ARM_START_WAIT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120)
50
51 /**
52  * How many times are we willing to try to wait for "scp" or
53  * "gnunet-service-arm" to complete (waitpid) before giving up?
54  */
55 #define MAX_EXEC_WAIT_RUNS 50
56
57 /**
58  * Phases of starting GNUnet on a system.
59  */
60 enum StartPhase
61   {
62     /**
63      * Copy the configuration file to the target system.
64      */
65     SP_COPYING,
66
67     /**
68      * Configuration file has been copied, start ARM on target system.
69      */
70     SP_COPIED,
71
72     /**
73      * ARM has been started, check that it has properly daemonized and
74      * then try to connect to the CORE service (which should be
75      * auto-started by ARM).
76      */
77     SP_START_ARMING,
78
79     /**
80      * We're waiting for CORE to start.
81      */
82     SP_START_CORE,
83
84     /**
85      * Core has notified us that we've established a connection to the service.
86      * The main FSM halts here and waits to be moved to UPDATE or CLEANUP.
87      */
88     SP_START_DONE,
89
90     /**
91      * We've been asked to terminate the instance and are now waiting for
92      * the remote command to delete the configuration file to complete.
93      */
94     SP_CLEANUP,
95
96     /**
97      * We've received a configuration update and are currently waiting for
98      * the copy process for the update to complete.  Once it is, we will
99      * return to "SP_START_DONE" (and rely on ARM to restart all affected
100      * services).
101      */
102     SP_CONFIG_UPDATE
103   };
104
105
106 /**
107  * Handle for a GNUnet daemon (technically a set of
108  * daemons; the handle is really for the master ARM
109  * daemon) started by the testing library.
110  */
111 struct GNUNET_TESTING_Daemon
112 {
113   /**
114    * Our scheduler.
115    */
116   struct GNUNET_SCHEDULER_Handle *sched;
117
118   /**
119    * Our configuration.
120    */
121   struct GNUNET_CONFIGURATION_Handle *cfg;
122
123   /**
124    * Host to run GNUnet on.
125    */
126   char *hostname;
127
128   /**
129    * Username we are using.
130    */
131   char *username;
132
133   /**
134    * Name of the configuration file
135    */
136   char *cfgfile;
137
138   /**
139    * Function to call when the peer is running.
140    */
141   GNUNET_TESTING_NotifyDaemonRunning cb;
142
143   /**
144    * Closure for cb.
145    */
146   void *cb_cls;
147
148   /**
149    * Arguments from "daemon_stop" call.
150    */
151   GNUNET_TESTING_NotifyCompletion dead_cb;
152
153   /**
154    * Closure for 'dead_cb'.
155    */
156   void *dead_cb_cls;
157
158   /**
159    * Arguments from "daemon_stop" call.
160    */
161   GNUNET_TESTING_NotifyCompletion update_cb;
162
163   /**
164    * Closure for 'update_cb'.
165    */
166   void *update_cb_cls;
167
168   /**
169    * Identity of this peer (once started).
170    */
171   struct GNUNET_PeerIdentity id;
172
173   /**
174    * Flag to indicate that we've already been asked
175    * to terminate (but could not because some action
176    * was still pending).
177    */
178   int dead;
179
180   /**
181    * PID of the process that we started last.
182    */
183   pid_t pid;
184
185   /**
186    * How many iterations have we been waiting for
187    * the started process to complete?
188    */
189   unsigned int wait_runs;
190
191   /**
192    * In which phase are we during the start of
193    * this process?
194    */
195   enum StartPhase phase;
196
197   /**
198    * ID of the current task.
199    */
200   GNUNET_SCHEDULER_TaskIdentifier task;
201
202   /**
203    * Handle to the server.
204    */ 
205   struct GNUNET_CORE_Handle * server;
206 };
207
208
209 /**
210  * Function called after GNUNET_CORE_connect has succeeded
211  * (or failed for good).  Note that the private key of the
212  * peer is intentionally not exposed here; if you need it,
213  * your process should try to read the private key file
214  * directly (which should work if you are authorized...).
215  *
216  * @param cls closure
217  * @param server handle to the server, NULL if we failed
218  * @param my_identity ID of this peer, NULL if we failed
219  * @param publicKey public key of this peer, NULL if we failed
220  */
221 static void
222 testing_init (void *cls,
223               struct GNUNET_CORE_Handle * server,
224               const struct GNUNET_PeerIdentity *
225               my_identity,
226               const struct
227               GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *
228               publicKey)
229 {
230   struct GNUNET_TESTING_Daemon *d = cls;
231   GNUNET_TESTING_NotifyDaemonRunning cb;
232
233   GNUNET_assert (d->phase == SP_START_CORE);
234   d->phase = SP_START_DONE;
235   cb = d->cb;
236   d->cb = NULL;
237   if (server == NULL)
238     {
239       if (GNUNET_YES == d->dead)
240         GNUNET_TESTING_daemon_stop (d, d->dead_cb, d->dead_cb_cls);
241       else if (NULL != cb)
242         cb (d->cb_cls, NULL, d->cfg, d,
243             _("Failed to connect to core service\n"));
244       return;
245     }
246 #if DEBUG_TESTING
247   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
248               "Successfully started peer `%4s'.\n",
249               GNUNET_i2s(my_identity));
250 #endif
251   d->id = *my_identity;
252   d->server = server;
253   if (GNUNET_YES == d->dead)
254     GNUNET_TESTING_daemon_stop (d, d->dead_cb, d->dead_cb_cls);
255   else if (NULL != cb)
256     cb (d->cb_cls, my_identity, d->cfg, d, NULL);
257 }
258
259
260 /**
261  * Finite-state machine for starting GNUnet.
262  *
263  * @param cls our "struct GNUNET_TESTING_Daemon"
264  * @param tc unused
265  */
266 static void
267 start_fsm (void *cls,
268            const struct GNUNET_SCHEDULER_TaskContext *tc)
269 {
270   static struct GNUNET_CORE_MessageHandler no_handlers[] =
271     { { NULL, 0, 0 } };
272   struct GNUNET_TESTING_Daemon * d = cls;
273   GNUNET_TESTING_NotifyDaemonRunning cb;
274   enum GNUNET_OS_ProcessStatusType type;
275   unsigned long code;
276   char *dst;
277  
278 #if DEBUG_TESTING
279   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
280               "Peer FSM is in phase %u.\n",
281               d->phase);
282 #endif
283   d->task = GNUNET_SCHEDULER_NO_TASK;
284   switch (d->phase)
285     {
286     case SP_COPYING:
287       /* confirm copying complete */
288       if (GNUNET_OK != 
289           GNUNET_OS_process_status (d->pid,
290                                     &type,
291                                     &code))
292         {
293           d->wait_runs++;
294           if (d->wait_runs > MAX_EXEC_WAIT_RUNS)
295             {
296               cb = d->cb;
297               d->cb = NULL;
298               if (NULL != cb)
299                 cb (d->cb_cls,
300                     NULL,
301                     d->cfg,
302                     d,
303                     _("`scp' does not seem to terminate.\n"));
304               return;
305             }
306           /* wait some more */
307           d->task
308             = GNUNET_SCHEDULER_add_delayed (d->sched, 
309                                             GNUNET_NO,
310                                             GNUNET_SCHEDULER_PRIORITY_KEEP,
311                                             GNUNET_SCHEDULER_NO_TASK,
312                                             GNUNET_CONSTANTS_EXEC_WAIT,
313                                             &start_fsm,
314                                             d);
315           return;
316         }
317       if ( (type != GNUNET_OS_PROCESS_EXITED) ||
318            (code != 0) )
319         {
320           cb = d->cb;
321           d->cb = NULL;
322           if (NULL != cb)
323             cb (d->cb_cls,
324                 NULL,
325                 d->cfg,
326                 d,
327                 _("`scp' did not complete cleanly.\n"));          
328           return;
329         }         
330 #if DEBUG_TESTING
331       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
332                   "Successfully copied configuration file.\n");
333 #endif
334       d->phase = SP_COPIED;
335       /* fall-through */
336     case SP_COPIED:
337       /* start GNUnet on remote host */
338       if (NULL == d->hostname)        
339         {
340           d->pid = GNUNET_OS_start_process ("gnunet-service-arm",
341                                             "gnunet-service-arm",
342                                             "-c",
343                                             d->cfgfile,
344 #if DEBUG_TESTING
345                                             "-L", "DEBUG",
346 #endif
347                                             "-d",
348                                             NULL);
349         }
350       else
351         {
352           if (d->username != NULL)
353             GNUNET_asprintf (&dst,
354                              "%s@%s",
355                              d->username,
356                              d->hostname);
357           else
358             dst = GNUNET_strdup (d->hostname);
359           d->pid = GNUNET_OS_start_process ("ssh",
360                                             "ssh",
361                                             dst,
362                                             "gnunet-service-arm",
363                                             "-c",
364                                             d->cfgfile,
365                                             "-d",
366                                             NULL);
367           GNUNET_free (dst);
368         }
369       if (-1 == d->pid)
370         {
371           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
372                       _("Could not start `%s' process to start GNUnet.\n"),
373                       (NULL == d->hostname) ? "gnunet-service-arm" : "ssh");
374           cb = d->cb;
375           d->cb = NULL;
376           if (NULL != cb)
377             cb (d->cb_cls,
378                 NULL,
379                 d->cfg,
380                 d,
381                 (NULL == d->hostname) 
382                 ? _("Failed to start `gnunet-service-arm' process.\n") 
383                 : _("Failed to start `ssh' process.\n"));
384         }      
385 #if DEBUG_TESTING
386       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
387                   "Started `%s', waiting for `%s' to be up.\n",
388                   "gnunet-service-arm",
389                   "gnunet-service-core");
390 #endif
391       d->phase = SP_START_ARMING;
392       d->wait_runs = 0;
393       d->task
394         = GNUNET_SCHEDULER_add_delayed (d->sched, 
395                                         GNUNET_NO,
396                                         GNUNET_SCHEDULER_PRIORITY_KEEP,
397                                         GNUNET_SCHEDULER_NO_TASK,
398                                         GNUNET_CONSTANTS_EXEC_WAIT,
399                                         &start_fsm,
400                                         d);
401       break;     
402     case SP_START_ARMING:
403       if (GNUNET_OK != 
404           GNUNET_OS_process_status (d->pid,
405                                     &type,
406                                     &code))
407         {
408           d->wait_runs++;
409           if (d->wait_runs > MAX_EXEC_WAIT_RUNS)
410             {
411               cb = d->cb;
412               d->cb = NULL;
413               if (NULL != cb)
414                 cb (d->cb_cls,
415                     NULL,
416                     d->cfg,
417                     d,
418                     (NULL == d->hostname) 
419                     ? _("`gnunet-service-arm' does not seem to terminate.\n") 
420                     : _("`ssh' does not seem to terminate.\n"));
421               return;
422             }
423           /* wait some more */
424           d->task
425             = GNUNET_SCHEDULER_add_delayed (d->sched, 
426                                             GNUNET_NO,
427                                             GNUNET_SCHEDULER_PRIORITY_KEEP,
428                                             GNUNET_SCHEDULER_NO_TASK,
429                                             GNUNET_CONSTANTS_EXEC_WAIT,
430                                             &start_fsm,
431                                             d);
432           return;
433         }
434 #if DEBUG_TESTING
435       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
436                   "Successfully started `%s'.\n",
437                   "gnunet-service-arm");
438 #endif
439       d->phase = SP_START_CORE;
440       GNUNET_CORE_connect (d->sched,
441                            d->cfg,
442                            ARM_START_WAIT,
443                            d,
444                            &testing_init,
445                            NULL, NULL, NULL, 
446                            NULL, GNUNET_NO,
447                            NULL, GNUNET_NO,
448                            no_handlers);     
449       break;
450     case SP_START_CORE:
451       GNUNET_break (0);
452       break;
453     case SP_START_DONE:
454       GNUNET_break (0);
455       break;
456     case SP_CLEANUP:
457       /* confirm copying complete */
458       if (GNUNET_OK != 
459           GNUNET_OS_process_status (d->pid,
460                                     &type,
461                                     &code))
462         {
463           d->wait_runs++;
464           if (d->wait_runs > MAX_EXEC_WAIT_RUNS)
465             {
466               d->dead_cb (d->dead_cb_cls,
467                           _("`ssh' does not seem to terminate.\n"));
468               GNUNET_free (d->cfgfile);
469               GNUNET_free_non_null (d->hostname);
470               GNUNET_free_non_null (d->username);
471               GNUNET_free (d);
472               return;
473             }
474           /* wait some more */
475           d->task
476             = GNUNET_SCHEDULER_add_delayed (d->sched, 
477                                             GNUNET_NO,
478                                             GNUNET_SCHEDULER_PRIORITY_KEEP,
479                                             GNUNET_SCHEDULER_NO_TASK,
480                                             GNUNET_CONSTANTS_EXEC_WAIT,
481                                             &start_fsm,
482                                             d);
483           return;
484         }
485       if ( (type != GNUNET_OS_PROCESS_EXITED) ||
486            (code != 0) )
487         {
488           if (NULL != d->dead_cb)
489             d->dead_cb (d->dead_cb_cls,
490                         _("`ssh' did not complete cleanly.\n"));          
491           GNUNET_free (d->cfgfile);
492           GNUNET_free_non_null (d->hostname);
493           GNUNET_free_non_null (d->username);
494           GNUNET_free (d);
495           return;
496         }        
497 #if DEBUG_TESTING
498       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
499                   "Peer shutdown complete.\n");
500 #endif
501       GNUNET_free (d->cfgfile);
502       GNUNET_free_non_null (d->hostname);
503       GNUNET_free_non_null (d->username);
504       if (NULL != d->dead_cb)
505         d->dead_cb (d->dead_cb_cls, NULL);
506       GNUNET_free (d);
507       break;
508     case SP_CONFIG_UPDATE:
509       /* confirm copying complete */
510       if (GNUNET_OK != 
511           GNUNET_OS_process_status (d->pid,
512                                     &type,
513                                     &code))
514         {
515           d->wait_runs++;
516           if (d->wait_runs > MAX_EXEC_WAIT_RUNS)
517             {
518               cb = d->cb;
519               d->cb = NULL;
520               if (NULL != cb)
521                 cb (d->cb_cls,
522                     NULL,
523                     d->cfg,
524                     d,
525                     _("`scp' does not seem to terminate.\n"));
526               return;
527             }
528           /* wait some more */
529           d->task
530             = GNUNET_SCHEDULER_add_delayed (d->sched, 
531                                             GNUNET_NO,
532                                             GNUNET_SCHEDULER_PRIORITY_KEEP,
533                                             GNUNET_SCHEDULER_NO_TASK,
534                                             GNUNET_CONSTANTS_EXEC_WAIT,
535                                             &start_fsm,
536                                             d);
537           return;
538         }
539       if ( (type != GNUNET_OS_PROCESS_EXITED) ||
540            (code != 0) )
541         {
542           if (NULL != d->update_cb)
543             d->update_cb (d->update_cb_cls,
544                           _("`scp' did not complete cleanly.\n"));        
545           return;
546         }         
547 #if DEBUG_TESTING
548       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
549                   "Successfully copied configuration file.\n");
550 #endif
551       if  (NULL != d->update_cb)
552         d->update_cb (d->update_cb_cls, NULL);
553       d->phase = SP_START_DONE;
554       break;
555     }
556 }
557
558
559 /**
560  * Starts a GNUnet daemon.  GNUnet must be installed on the target
561  * system and available in the PATH.  The machine must furthermore be
562  * reachable via "ssh" (unless the hostname is "NULL") without the
563  * need to enter a password.
564  *
565  * @param sched scheduler to use 
566  * @param cfg configuration to use
567  * @param hostname name of the machine where to run GNUnet
568  *        (use NULL for localhost).
569  * @param cb function to call with the result
570  * @param cb_cls closure for cb
571  * @return handle to the daemon (actual start will be completed asynchronously)
572  */
573 struct GNUNET_TESTING_Daemon *
574 GNUNET_TESTING_daemon_start (struct GNUNET_SCHEDULER_Handle *sched,
575                              const struct GNUNET_CONFIGURATION_Handle *cfg,
576                              const char *hostname,
577                              GNUNET_TESTING_NotifyDaemonRunning cb,
578                              void *cb_cls)
579 {
580   struct GNUNET_TESTING_Daemon * ret;
581   char *arg;
582   char *username;
583
584   ret = GNUNET_malloc (sizeof(struct GNUNET_TESTING_Daemon));
585   ret->sched = sched;
586   ret->hostname = (hostname == NULL) ? NULL : GNUNET_strdup (hostname);
587   ret->cfgfile = GNUNET_DISK_mktemp ("gnunet-testing-config");
588 #if DEBUG_TESTING
589   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
590               "Setting up peer with configuration file `%s'.\n",
591               ret->cfgfile);
592 #endif
593   if (NULL == ret->cfgfile)
594     {                                           
595       GNUNET_free_non_null (ret->hostname);
596       GNUNET_free (ret);
597       return NULL;
598     }
599   ret->cb = cb;
600   ret->cb_cls = cb_cls;
601   ret->cfg = GNUNET_CONFIGURATION_dup (cfg);
602   GNUNET_CONFIGURATION_set_value_string (ret->cfg,
603                                          "PATHS",
604                                          "DEFAULTCONFIG",
605                                          ret->cfgfile);
606   /* 1) write configuration to temporary file */
607   if (GNUNET_OK != 
608       GNUNET_CONFIGURATION_write (ret->cfg,
609                                   ret->cfgfile))
610     {
611       if (0 != UNLINK (ret->cfgfile))
612           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
613                                     "unlink",
614                                     ret->cfgfile);
615       GNUNET_CONFIGURATION_destroy (ret->cfg);
616       GNUNET_free_non_null (ret->hostname);
617       GNUNET_free (ret->cfgfile);
618       GNUNET_free (ret);
619       return NULL;
620     }
621   if (GNUNET_OK !=
622       GNUNET_CONFIGURATION_get_value_string (cfg,
623                                              "TESTING",
624                                              "USERNAME",
625                                              &username))
626     {
627       if (NULL != getenv ("USER"))
628         username = GNUNET_strdup (getenv("USER"));
629       else
630         username = NULL;
631     }
632   ret->username = username;
633
634   /* 2) copy file to remote host */  
635   if (NULL != hostname)
636     {
637       ret->phase = SP_COPYING;
638       if (NULL != username)
639         GNUNET_asprintf (&arg,
640                          "%s@%s:%s", 
641                          username,
642                          hostname,
643                          ret->cfgfile);
644       else
645         GNUNET_asprintf (&arg,
646                          "%s:%s", 
647                          hostname,
648                          ret->cfgfile);
649       ret->pid = GNUNET_OS_start_process ("scp",
650                                           "scp",
651                                           ret->cfgfile,
652                                           arg,
653                                           NULL);
654       GNUNET_free (arg);
655       if (-1 == ret->pid)
656         {
657           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
658                       _("Could not start `%s' process to copy configuration file.\n"),
659                       "scp");
660           if (0 != UNLINK (ret->cfgfile))
661             GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
662                                       "unlink",
663                                       ret->cfgfile);
664           GNUNET_CONFIGURATION_destroy (ret->cfg);
665           GNUNET_free_non_null (ret->hostname);
666           GNUNET_free_non_null (ret->username);
667           GNUNET_free (ret->cfgfile);
668           GNUNET_free (ret);
669           return NULL;
670         }
671       ret->task
672         = GNUNET_SCHEDULER_add_delayed (sched, 
673                                         GNUNET_YES,
674                                         GNUNET_SCHEDULER_PRIORITY_KEEP,
675                                         GNUNET_SCHEDULER_NO_TASK,
676                                         GNUNET_CONSTANTS_EXEC_WAIT,
677                                         &start_fsm,
678                                         ret);
679       return ret;
680     }
681 #if DEBUG_TESTING
682   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
683               "No need to copy configuration file since we are running locally.\n");
684 #endif
685   ret->phase = SP_COPIED;
686   GNUNET_SCHEDULER_add_continuation (sched,
687                                      GNUNET_NO,
688                                      &start_fsm,
689                                      ret,
690                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
691   return ret;
692 }
693
694
695 /**
696  * Stops a GNUnet daemon.
697  *
698  * @param d the daemon that should be stopped
699  * @param cb function called once the daemon was stopped
700  * @param cb_cls closure for cb
701  */
702 void GNUNET_TESTING_daemon_stop (struct GNUNET_TESTING_Daemon *d,
703                                  GNUNET_TESTING_NotifyCompletion cb,
704                                  void * cb_cls)
705 {
706   struct GNUNET_CLIENT_Connection *cc;
707   char *dst;
708
709   if (NULL != d->cb)
710     {
711       d->dead = GNUNET_YES;
712       d->dead_cb = cb;
713       d->dead_cb_cls = cb_cls;
714       return;
715     }
716   if (d->phase == SP_CONFIG_UPDATE)
717     {
718       GNUNET_SCHEDULER_cancel (d->sched,
719                                d->task);
720       d->phase = SP_START_DONE;
721     }
722   if (d->server != NULL)
723     {
724       GNUNET_CORE_disconnect (d->server);
725       d->server = NULL;
726     }
727   /* shutdown ARM process (will also terminate others) */
728   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
729               _("Terminating peer `%4s'\n"),
730               GNUNET_i2s(&d->id));
731   cc = GNUNET_CLIENT_connect (d->sched,
732                               "arm",
733                               d->cfg);
734   GNUNET_CLIENT_service_shutdown (cc);
735   GNUNET_CLIENT_disconnect (cc);
736   
737   /* state clean up and notifications */
738   if (0 != UNLINK (d->cfgfile))
739     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
740                               "unlink",
741                               d->cfgfile);
742   if (d->hostname != NULL)
743     {
744       if (NULL != d->username)
745         GNUNET_asprintf (&dst,
746                          "%s@%s",
747                          d->username,
748                          d->hostname);
749       else
750         dst = GNUNET_strdup (d->hostname);
751       d->pid = GNUNET_OS_start_process ("ssh",
752                                         "ssh",
753                                         dst,
754                                         "rm",
755                                         d->cfgfile,
756                                         NULL);
757       GNUNET_free (dst);
758       if (-1 == d->pid)
759         {
760           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
761                       _("Could not start `%s' process to delete configuration file.\n"),
762                       "ssh");
763           GNUNET_free (d->cfgfile);
764           GNUNET_free_non_null (d->hostname);
765           GNUNET_free_non_null (d->username);
766           GNUNET_free (d);
767           cb (cb_cls, _("Error cleaning up configuration file.\n"));
768           return;
769         }
770       d->phase = SP_CLEANUP;
771       d->dead_cb = cb;
772       d->dead_cb_cls = cb_cls;
773       d->task
774         = GNUNET_SCHEDULER_add_delayed (d->sched, 
775                                         GNUNET_YES,
776                                         GNUNET_SCHEDULER_PRIORITY_KEEP,
777                                         GNUNET_SCHEDULER_NO_TASK,
778                                         GNUNET_CONSTANTS_EXEC_WAIT,
779                                         &start_fsm,
780                                         d);
781       return;
782     }
783   GNUNET_CONFIGURATION_destroy (d->cfg);
784   GNUNET_free (d->cfgfile);
785   GNUNET_free_non_null (d->hostname);
786   GNUNET_free_non_null (d->username);
787   GNUNET_free (d);
788   if (NULL != cb)
789     cb (cb_cls, NULL);
790 }
791
792
793 /**
794  * Changes the configuration of a GNUnet daemon.
795  *
796  * @param d the daemon that should be modified
797  * @param cfg the new configuration for the daemon
798  * @param cb function called once the configuration was changed
799  * @param cb_cls closure for cb
800  */
801 void GNUNET_TESTING_daemon_reconfigure (struct GNUNET_TESTING_Daemon *d,
802                                         struct GNUNET_CONFIGURATION_Handle *cfg,
803                                         GNUNET_TESTING_NotifyCompletion cb,
804                                         void * cb_cls)
805 {
806   char *arg;
807
808   if (d->phase != SP_START_DONE)
809     {
810       if (NULL != cb)
811         cb (cb_cls,
812             _("Peer not yet running, can not change configuration at this point."));
813       return;      
814     }
815
816   /* 1) write configuration to temporary file */
817   if (GNUNET_OK != 
818       GNUNET_CONFIGURATION_write (cfg,
819                                   d->cfgfile))
820     {
821       if (NULL != cb)
822         cb (cb_cls,
823           _("Failed to write new configuration to disk."));
824       return;
825     }
826
827   /* 2) copy file to remote host (if necessary) */  
828   if (NULL == d->hostname)
829     {
830       /* signal success */
831       if (NULL != cb)
832         cb (cb_cls, NULL); 
833       return;
834     }
835   d->phase = SP_CONFIG_UPDATE;
836   if (NULL != d->username)
837     GNUNET_asprintf (&arg,
838                      "%s@%s:%s", 
839                      d->username,
840                      d->hostname,
841                      d->cfgfile);
842   else
843     GNUNET_asprintf (&arg,
844                      "%s:%s", 
845                      d->hostname,
846                      d->cfgfile);
847   d->pid = GNUNET_OS_start_process ("scp",
848                                     "scp",
849                                     d->cfgfile,
850                                     arg,
851                                     NULL);
852   GNUNET_free (arg);
853   if (-1 == d->pid)
854     {
855       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
856                   _("Could not start `%s' process to copy configuration file.\n"),
857                   "scp");
858       if (NULL != cb)
859         cb (cb_cls,
860             _("Failed to copy new configuration to remote machine."));
861       d->phase = SP_START_DONE;
862       return;
863     }
864   d->update_cb = cb;
865   d->update_cb_cls = cb_cls;
866   d->task
867     = GNUNET_SCHEDULER_add_delayed (d->sched, 
868                                     GNUNET_NO,
869                                     GNUNET_SCHEDULER_PRIORITY_KEEP,
870                                     GNUNET_SCHEDULER_NO_TASK,
871                                     GNUNET_CONSTANTS_EXEC_WAIT,
872                                     &start_fsm,
873                                     d);
874 }
875
876 /**
877  * FIXME.
878  */
879 struct ConnectContext
880 {
881   /**
882    * FIXME.
883    */
884   struct GNUNET_TESTING_Daemon *d1;
885
886   /**
887    * FIXME.
888    */
889   struct GNUNET_TESTING_Daemon *d2;
890
891   /**
892    * FIXME.
893    */
894   struct GNUNET_TRANSPORT_Handle *d1th;
895
896   /**
897    * FIXME.
898    */
899   struct GNUNET_TRANSPORT_Handle *d2th;
900
901   /**
902    * When should this operation be complete (or we must trigger
903    * a timeout).
904    */
905   struct GNUNET_TIME_Absolute timeout;
906
907   /**
908    * Function to call once we are done (or have timed out).
909    */
910   GNUNET_TESTING_NotifyCompletion cb;
911
912   /**
913    * Closure for "nb".
914    */
915   void *cb_cls;
916 };
917
918
919 /**
920  * Success, connection is up.  Signal client our success.
921  *
922  * @param cls FIXME
923  * @param size number of bytes available in buf
924  * @param buf where to copy the message, NULL on error
925  * @return number of bytes copied to buf
926  */
927 static size_t
928 transmit_ready (void *cls, size_t size, void *buf)
929 {
930   struct ConnectContext *ctx = cls;
931
932   if (NULL != ctx->cb)
933     {
934       if (buf == NULL)
935         ctx->cb (ctx->cb_cls, _("Peers failed to connect"));
936       else
937         ctx->cb (ctx->cb_cls, NULL);
938     }
939   GNUNET_free (ctx);
940   return 0;
941 }
942
943
944 /**
945  * Receive the HELLO from one peer, give it to the other
946  * and ask them to connect.
947  * 
948  * @param cls "struct ConnectContext"
949  * @param latency how fast is the connection
950  * @param peer ID of peer giving us the HELLO
951  * @param message HELLO message of peer
952  */
953 static void
954 process_hello (void *cls,
955                struct GNUNET_TIME_Relative latency,
956                const struct GNUNET_PeerIdentity *peer,
957                const struct GNUNET_MessageHeader *message)
958 {
959   struct ConnectContext *ctx = cls;
960
961   if (peer == NULL)
962     {
963       /* signal error */
964       if (NULL != ctx->cb)
965         ctx->cb (ctx->cb_cls,
966                  _("Failed to receive `HELLO' from peer\n"));
967       GNUNET_TRANSPORT_disconnect (ctx->d1th);
968       GNUNET_TRANSPORT_disconnect (ctx->d2th);
969       GNUNET_free (ctx);
970       return;
971     }
972   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
973               "Received `%s' from transport service of `%4s'\n",
974               "HELLO", GNUNET_i2s (peer));
975   GNUNET_assert (message != NULL);
976   GNUNET_TRANSPORT_offer_hello (ctx->d2th, message);
977   GNUNET_CORE_notify_transmit_ready (ctx->d2->server,
978                                      0,
979                                      GNUNET_TIME_absolute_get_remaining (ctx->timeout),
980                                      &ctx->d1->id,
981                                      sizeof (struct GNUNET_MessageHeader),
982                                      &transmit_ready, ctx);
983 }
984
985
986 /**
987  * Establish a connection between two GNUnet daemons.
988  *
989  * @param d1 handle for the first daemon
990  * @param d2 handle for the second daemon
991  * @param timeout how long is the connection attempt
992  *        allowed to take?
993  * @param cb function to call at the end
994  * @param cb_cls closure for cb
995  */
996 void GNUNET_TESTING_daemons_connect (struct GNUNET_TESTING_Daemon *d1,
997                                      struct GNUNET_TESTING_Daemon *d2,
998                                      struct GNUNET_TIME_Relative timeout,
999                                      GNUNET_TESTING_NotifyCompletion cb,
1000                                      void *cb_cls)
1001 {
1002   struct ConnectContext *ctx;
1003
1004   if ( (d1->server == NULL) ||
1005        (d2->server == NULL) )
1006     {
1007       if (NULL != cb)
1008         cb (cb_cls, _("Peers are not fully running yet, can not connect!\n"));
1009       return;
1010     }
1011   ctx = GNUNET_malloc (sizeof(struct ConnectContext));
1012   ctx->d1 = d1;
1013   ctx->d2 = d2;
1014   ctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1015   ctx->cb = cb;
1016   ctx->cb_cls = cb_cls;
1017   ctx->d1th = GNUNET_TRANSPORT_connect (d1->sched, d1->cfg, d1, NULL, NULL, NULL);
1018   if (ctx->d1th == NULL)
1019     {
1020       GNUNET_free (ctx);
1021       if (NULL != cb)
1022         cb (cb_cls, _("Failed to connect to transport service!\n"));
1023       return;
1024     }
1025   ctx->d2th = GNUNET_TRANSPORT_connect (d2->sched, d2->cfg, d2, NULL, NULL, NULL);
1026   if (ctx->d2th == NULL)
1027     {
1028       GNUNET_TRANSPORT_disconnect (ctx->d1th);
1029       GNUNET_free (ctx);
1030       if (NULL != cb)
1031         cb (cb_cls, _("Failed to connect to transport service!\n"));
1032       return;
1033     }
1034   GNUNET_TRANSPORT_get_hello (ctx->d1th, 
1035                               timeout,
1036                               &process_hello, 
1037                               ctx);
1038 }
1039
1040
1041 /* end of testing.c */