longer timeout to make test work on slower machines, but the problem is still that...
[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_NO
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, 60)
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       d->server = NULL;
240       if (GNUNET_YES == d->dead)
241         GNUNET_TESTING_daemon_stop (d, d->dead_cb, d->dead_cb_cls);
242       else if (NULL != cb)
243         cb (d->cb_cls, NULL, d->cfg, d,
244             _("Failed to connect to core service\n"));
245       return;
246     }
247 #if DEBUG_TESTING
248   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
249               "Successfully started peer `%4s'.\n",
250               GNUNET_i2s(my_identity));
251 #endif
252   d->id = *my_identity;
253   d->server = server;
254   if (GNUNET_YES == d->dead)
255     GNUNET_TESTING_daemon_stop (d, d->dead_cb, d->dead_cb_cls);
256   else if (NULL != cb)
257     cb (d->cb_cls, my_identity, d->cfg, d, NULL);
258 }
259
260
261 /**
262  * Finite-state machine for starting GNUnet.
263  *
264  * @param cls our "struct GNUNET_TESTING_Daemon"
265  * @param tc unused
266  */
267 static void
268 start_fsm (void *cls,
269            const struct GNUNET_SCHEDULER_TaskContext *tc)
270 {
271   static struct GNUNET_CORE_MessageHandler no_handlers[] =
272     { { NULL, 0, 0 } };
273   struct GNUNET_TESTING_Daemon * d = cls;
274   GNUNET_TESTING_NotifyDaemonRunning cb;
275   enum GNUNET_OS_ProcessStatusType type;
276   unsigned long code;
277   char *dst;
278  
279 #if DEBUG_TESTING
280   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
281               "Peer FSM is in phase %u.\n",
282               d->phase);
283 #endif
284   d->task = GNUNET_SCHEDULER_NO_TASK;
285   switch (d->phase)
286     {
287     case SP_COPYING:
288       /* confirm copying complete */
289       if (GNUNET_OK != 
290           GNUNET_OS_process_status (d->pid,
291                                     &type,
292                                     &code))
293         {
294           d->wait_runs++;
295           if (d->wait_runs > MAX_EXEC_WAIT_RUNS)
296             {
297               cb = d->cb;
298               d->cb = NULL;
299               if (NULL != cb)
300                 cb (d->cb_cls,
301                     NULL,
302                     d->cfg,
303                     d,
304                     _("`scp' does not seem to terminate.\n"));
305               return;
306             }
307           /* wait some more */
308           d->task
309             = GNUNET_SCHEDULER_add_delayed (d->sched, 
310                                             GNUNET_CONSTANTS_EXEC_WAIT,
311                                             &start_fsm,
312                                             d);
313           return;
314         }
315       if ( (type != GNUNET_OS_PROCESS_EXITED) ||
316            (code != 0) )
317         {
318           cb = d->cb;
319           d->cb = NULL;
320           if (NULL != cb)
321             cb (d->cb_cls,
322                 NULL,
323                 d->cfg,
324                 d,
325                 _("`scp' did not complete cleanly.\n"));          
326           return;
327         }         
328 #if DEBUG_TESTING
329       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
330                   "Successfully copied configuration file.\n");
331 #endif
332       d->phase = SP_COPIED;
333       /* fall-through */
334     case SP_COPIED:
335       /* start GNUnet on remote host */
336       if (NULL == d->hostname)        
337         {
338           d->pid = GNUNET_OS_start_process ("gnunet-service-arm",
339                                             "gnunet-service-arm",
340                                             "-c",
341                                             d->cfgfile,
342 #if DEBUG_TESTING
343                                             "-L", "DEBUG",
344 #endif
345                                             "-d",
346                                             NULL);
347         }
348       else
349         {
350           if (d->username != NULL)
351             GNUNET_asprintf (&dst,
352                              "%s@%s",
353                              d->username,
354                              d->hostname);
355           else
356             dst = GNUNET_strdup (d->hostname);
357           d->pid = GNUNET_OS_start_process ("ssh",
358                                             "ssh",
359                                             dst,
360                                             "gnunet-service-arm",
361                                             "-c",
362                                             d->cfgfile,
363                                             "-d",
364                                             NULL);
365           GNUNET_free (dst);
366         }
367       if (-1 == d->pid)
368         {
369           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
370                       _("Could not start `%s' process to start GNUnet.\n"),
371                       (NULL == d->hostname) ? "gnunet-service-arm" : "ssh");
372           cb = d->cb;
373           d->cb = NULL;
374           if (NULL != cb)
375             cb (d->cb_cls,
376                 NULL,
377                 d->cfg,
378                 d,
379                 (NULL == d->hostname) 
380                 ? _("Failed to start `gnunet-service-arm' process.\n") 
381                 : _("Failed to start `ssh' process.\n"));
382         }      
383 #if DEBUG_TESTING
384       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
385                   "Started `%s', waiting for `%s' to be up.\n",
386                   "gnunet-service-arm",
387                   "gnunet-service-core");
388 #endif
389       d->phase = SP_START_ARMING;
390       d->wait_runs = 0;
391       d->task
392         = GNUNET_SCHEDULER_add_delayed (d->sched, 
393                                         GNUNET_CONSTANTS_EXEC_WAIT,
394                                         &start_fsm,
395                                         d);
396       break;     
397     case SP_START_ARMING:
398       if (GNUNET_OK != 
399           GNUNET_OS_process_status (d->pid,
400                                     &type,
401                                     &code))
402         {
403           d->wait_runs++;
404           if (d->wait_runs > MAX_EXEC_WAIT_RUNS)
405             {
406               cb = d->cb;
407               d->cb = NULL;
408               if (NULL != cb)
409                 cb (d->cb_cls,
410                     NULL,
411                     d->cfg,
412                     d,
413                     (NULL == d->hostname) 
414                     ? _("`gnunet-service-arm' does not seem to terminate.\n") 
415                     : _("`ssh' does not seem to terminate.\n"));
416               return;
417             }
418           /* wait some more */
419           d->task
420             = GNUNET_SCHEDULER_add_delayed (d->sched, 
421                                             GNUNET_CONSTANTS_EXEC_WAIT,
422                                             &start_fsm,
423                                             d);
424           return;
425         }
426 #if DEBUG_TESTING
427       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
428                   "Successfully started `%s'.\n",
429                   "gnunet-service-arm");
430 #endif
431       d->phase = SP_START_CORE;
432       d->server = GNUNET_CORE_connect (d->sched,
433                                        d->cfg,
434                                        ARM_START_WAIT,
435                                        d,
436                                        &testing_init,
437                                        NULL, NULL, NULL,
438                                        NULL, GNUNET_NO,
439                                        NULL, GNUNET_NO,
440                                        no_handlers);     
441       break;
442     case SP_START_CORE:
443       GNUNET_break (0);
444       break;
445     case SP_START_DONE:
446       GNUNET_break (0);
447       break;
448     case SP_CLEANUP:
449       /* confirm copying complete */
450       if (GNUNET_OK != 
451           GNUNET_OS_process_status (d->pid,
452                                     &type,
453                                     &code))
454         {
455           d->wait_runs++;
456           if (d->wait_runs > MAX_EXEC_WAIT_RUNS)
457             {
458               d->dead_cb (d->dead_cb_cls,
459                           _("`ssh' does not seem to terminate.\n"));
460               GNUNET_free (d->cfgfile);
461               GNUNET_free_non_null (d->hostname);
462               GNUNET_free_non_null (d->username);
463               GNUNET_free (d);
464               return;
465             }
466           /* wait some more */
467           d->task
468             = GNUNET_SCHEDULER_add_delayed (d->sched, 
469                                             GNUNET_CONSTANTS_EXEC_WAIT,
470                                             &start_fsm,
471                                             d);
472           return;
473         }
474       if ( (type != GNUNET_OS_PROCESS_EXITED) ||
475            (code != 0) )
476         {
477           if (NULL != d->dead_cb)
478             d->dead_cb (d->dead_cb_cls,
479                         _("`ssh' did not complete cleanly.\n"));          
480           GNUNET_free (d->cfgfile);
481           GNUNET_free_non_null (d->hostname);
482           GNUNET_free_non_null (d->username);
483           GNUNET_free (d);
484           return;
485         }        
486 #if DEBUG_TESTING
487       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
488                   "Peer shutdown complete.\n");
489 #endif
490       GNUNET_free (d->cfgfile);
491       GNUNET_free_non_null (d->hostname);
492       GNUNET_free_non_null (d->username);
493       if (NULL != d->dead_cb)
494         d->dead_cb (d->dead_cb_cls, NULL);
495       GNUNET_free (d);
496       break;
497     case SP_CONFIG_UPDATE:
498       /* confirm copying complete */
499       if (GNUNET_OK != 
500           GNUNET_OS_process_status (d->pid,
501                                     &type,
502                                     &code))
503         {
504           d->wait_runs++;
505           if (d->wait_runs > MAX_EXEC_WAIT_RUNS)
506             {
507               cb = d->cb;
508               d->cb = NULL;
509               if (NULL != cb)
510                 cb (d->cb_cls,
511                     NULL,
512                     d->cfg,
513                     d,
514                     _("`scp' does not seem to terminate.\n"));
515               return;
516             }
517           /* wait some more */
518           d->task
519             = GNUNET_SCHEDULER_add_delayed (d->sched, 
520                                             GNUNET_CONSTANTS_EXEC_WAIT,
521                                             &start_fsm,
522                                             d);
523           return;
524         }
525       if ( (type != GNUNET_OS_PROCESS_EXITED) ||
526            (code != 0) )
527         {
528           if (NULL != d->update_cb)
529             d->update_cb (d->update_cb_cls,
530                           _("`scp' did not complete cleanly.\n"));        
531           return;
532         }         
533 #if DEBUG_TESTING
534       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
535                   "Successfully copied configuration file.\n");
536 #endif
537       if  (NULL != d->update_cb)
538         d->update_cb (d->update_cb_cls, NULL);
539       d->phase = SP_START_DONE;
540       break;
541     }
542 }
543
544
545 /**
546  * Starts a GNUnet daemon.  GNUnet must be installed on the target
547  * system and available in the PATH.  The machine must furthermore be
548  * reachable via "ssh" (unless the hostname is "NULL") without the
549  * need to enter a password.
550  *
551  * @param sched scheduler to use 
552  * @param cfg configuration to use
553  * @param hostname name of the machine where to run GNUnet
554  *        (use NULL for localhost).
555  * @param cb function to call with the result
556  * @param cb_cls closure for cb
557  * @return handle to the daemon (actual start will be completed asynchronously)
558  */
559 struct GNUNET_TESTING_Daemon *
560 GNUNET_TESTING_daemon_start (struct GNUNET_SCHEDULER_Handle *sched,
561                              const struct GNUNET_CONFIGURATION_Handle *cfg,
562                              const char *hostname,
563                              GNUNET_TESTING_NotifyDaemonRunning cb,
564                              void *cb_cls)
565 {
566   struct GNUNET_TESTING_Daemon * ret;
567   char *arg;
568   char *username;
569
570   ret = GNUNET_malloc (sizeof(struct GNUNET_TESTING_Daemon));
571   ret->sched = sched;
572   ret->hostname = (hostname == NULL) ? NULL : GNUNET_strdup (hostname);
573   ret->cfgfile = GNUNET_DISK_mktemp ("gnunet-testing-config");
574 #if DEBUG_TESTING
575   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
576               "Setting up peer with configuration file `%s'.\n",
577               ret->cfgfile);
578 #endif
579   if (NULL == ret->cfgfile)
580     {                                           
581       GNUNET_free_non_null (ret->hostname);
582       GNUNET_free (ret);
583       return NULL;
584     }
585   ret->cb = cb;
586   ret->cb_cls = cb_cls;
587   ret->cfg = GNUNET_CONFIGURATION_dup (cfg);
588   GNUNET_CONFIGURATION_set_value_string (ret->cfg,
589                                          "PATHS",
590                                          "DEFAULTCONFIG",
591                                          ret->cfgfile);
592   /* 1) write configuration to temporary file */
593   if (GNUNET_OK != 
594       GNUNET_CONFIGURATION_write (ret->cfg,
595                                   ret->cfgfile))
596     {
597       if (0 != UNLINK (ret->cfgfile))
598           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
599                                     "unlink",
600                                     ret->cfgfile);
601       GNUNET_CONFIGURATION_destroy (ret->cfg);
602       GNUNET_free_non_null (ret->hostname);
603       GNUNET_free (ret->cfgfile);
604       GNUNET_free (ret);
605       return NULL;
606     }
607   if (GNUNET_OK !=
608       GNUNET_CONFIGURATION_get_value_string (cfg,
609                                              "TESTING",
610                                              "USERNAME",
611                                              &username))
612     {
613       if (NULL != getenv ("USER"))
614         username = GNUNET_strdup (getenv("USER"));
615       else
616         username = NULL;
617     }
618   ret->username = username;
619
620   /* 2) copy file to remote host */  
621   if (NULL != hostname) 
622     {
623 #if DEBUG_TESTING
624       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
625                   "Copying configuration file to host `%s'.\n",
626                   hostname);
627 #endif
628       ret->phase = SP_COPYING;
629       if (NULL != username)
630         GNUNET_asprintf (&arg,
631                          "%s@%s:%s", 
632                          username,
633                          hostname,
634                          ret->cfgfile);
635       else
636         GNUNET_asprintf (&arg,
637                          "%s:%s", 
638                          hostname,
639                          ret->cfgfile);
640       ret->pid = GNUNET_OS_start_process ("scp",
641                                           "scp",
642                                           ret->cfgfile,
643                                           arg,
644                                           NULL);
645       GNUNET_free (arg);
646       if (-1 == ret->pid)
647         {
648           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
649                       _("Could not start `%s' process to copy configuration file.\n"),
650                       "scp");
651           if (0 != UNLINK (ret->cfgfile))
652             GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
653                                       "unlink",
654                                       ret->cfgfile);
655           GNUNET_CONFIGURATION_destroy (ret->cfg);
656           GNUNET_free_non_null (ret->hostname);
657           GNUNET_free_non_null (ret->username);
658           GNUNET_free (ret->cfgfile);
659           GNUNET_free (ret);
660           return NULL;
661         }
662       ret->task
663         = GNUNET_SCHEDULER_add_delayed (sched, 
664                                         GNUNET_CONSTANTS_EXEC_WAIT,
665                                         &start_fsm,
666                                         ret);
667       return ret;
668     }
669 #if DEBUG_TESTING
670   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
671               "No need to copy configuration file since we are running locally.\n");
672 #endif
673   ret->phase = SP_COPIED;
674   GNUNET_SCHEDULER_add_continuation (sched,
675                                      &start_fsm,
676                                      ret,
677                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
678   return ret;
679 }
680
681
682 /**
683  * Stops a GNUnet daemon.
684  *
685  * @param d the daemon that should be stopped
686  * @param cb function called once the daemon was stopped
687  * @param cb_cls closure for cb
688  */
689 void GNUNET_TESTING_daemon_stop (struct GNUNET_TESTING_Daemon *d,
690                                  GNUNET_TESTING_NotifyCompletion cb,
691                                  void * cb_cls)
692 {
693   struct GNUNET_CLIENT_Connection *cc;
694   char *dst;
695
696   if (NULL != d->cb)
697     {
698       d->dead = GNUNET_YES;
699       d->dead_cb = cb;
700       d->dead_cb_cls = cb_cls;
701       return;
702     }
703   if (d->phase == SP_CONFIG_UPDATE)
704     {
705       GNUNET_SCHEDULER_cancel (d->sched,
706                                d->task);
707       d->phase = SP_START_DONE;
708     }
709   if (d->server != NULL)
710     {
711       GNUNET_CORE_disconnect (d->server);
712       d->server = NULL;
713     }
714   /* shutdown ARM process (will also terminate others) */
715 #if DEBUG_TESTING
716   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
717               _("Terminating peer `%4s'\n"),
718               GNUNET_i2s(&d->id));
719 #endif
720   cc = GNUNET_CLIENT_connect (d->sched,
721                               "arm",
722                               d->cfg);
723   GNUNET_CLIENT_service_shutdown (cc);
724   
725   /* state clean up and notifications */
726   if (0 != UNLINK (d->cfgfile))
727     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
728                               "unlink",
729                               d->cfgfile);
730   if (d->hostname != NULL)
731     {
732 #if DEBUG_TESTING
733       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
734                   "Removing configuration file on remote host `%s'.\n",
735                   d->hostname);
736 #endif
737       if (NULL != d->username)
738         GNUNET_asprintf (&dst,
739                          "%s@%s",
740                          d->username,
741                          d->hostname);
742       else
743         dst = GNUNET_strdup (d->hostname);
744       d->pid = GNUNET_OS_start_process ("ssh",
745                                         "ssh",
746                                         dst,
747                                         "rm",
748                                         d->cfgfile,
749                                         NULL);
750       GNUNET_free (dst);
751       if (-1 == d->pid)
752         {
753           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
754                       _("Could not start `%s' process to delete configuration file.\n"),
755                       "ssh");
756           GNUNET_free (d->cfgfile);
757           GNUNET_free_non_null (d->hostname);
758           GNUNET_free_non_null (d->username);
759           GNUNET_free (d);
760           cb (cb_cls, _("Error cleaning up configuration file.\n"));
761           return;
762         }
763       d->phase = SP_CLEANUP;
764       d->dead_cb = cb;
765       d->dead_cb_cls = cb_cls;
766       d->task
767         = GNUNET_SCHEDULER_add_delayed (d->sched, 
768                                         GNUNET_CONSTANTS_EXEC_WAIT,
769                                         &start_fsm,
770                                         d);
771       return;
772     }
773   GNUNET_CONFIGURATION_destroy (d->cfg);
774   GNUNET_free (d->cfgfile);
775   GNUNET_free_non_null (d->hostname);
776   GNUNET_free_non_null (d->username);
777   GNUNET_free (d);
778   if (NULL != cb)
779     cb (cb_cls, NULL);
780 }
781
782
783 /**
784  * Changes the configuration of a GNUnet daemon.
785  *
786  * @param d the daemon that should be modified
787  * @param cfg the new configuration for the daemon
788  * @param cb function called once the configuration was changed
789  * @param cb_cls closure for cb
790  */
791 void GNUNET_TESTING_daemon_reconfigure (struct GNUNET_TESTING_Daemon *d,
792                                         struct GNUNET_CONFIGURATION_Handle *cfg,
793                                         GNUNET_TESTING_NotifyCompletion cb,
794                                         void * cb_cls)
795 {
796   char *arg;
797
798   if (d->phase != SP_START_DONE)
799     {
800       if (NULL != cb)
801         cb (cb_cls,
802             _("Peer not yet running, can not change configuration at this point."));
803       return;      
804     }
805
806   /* 1) write configuration to temporary file */
807   if (GNUNET_OK != 
808       GNUNET_CONFIGURATION_write (cfg,
809                                   d->cfgfile))
810     {
811       if (NULL != cb)
812         cb (cb_cls,
813           _("Failed to write new configuration to disk."));
814       return;
815     }
816
817   /* 2) copy file to remote host (if necessary) */  
818   if (NULL == d->hostname)
819     {
820       /* signal success */
821       if (NULL != cb)
822         cb (cb_cls, NULL); 
823       return;
824     }
825 #if DEBUG_TESTING
826   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
827               "Copying updated configuration file to remote host `%s'.\n",
828               d->hostname);
829 #endif
830   d->phase = SP_CONFIG_UPDATE;
831   if (NULL != d->username)
832     GNUNET_asprintf (&arg,
833                      "%s@%s:%s", 
834                      d->username,
835                      d->hostname,
836                      d->cfgfile);
837   else
838     GNUNET_asprintf (&arg,
839                      "%s:%s", 
840                      d->hostname,
841                      d->cfgfile);
842   d->pid = GNUNET_OS_start_process ("scp",
843                                     "scp",
844                                     d->cfgfile,
845                                     arg,
846                                     NULL);
847   GNUNET_free (arg);
848   if (-1 == d->pid)
849     {
850       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
851                   _("Could not start `%s' process to copy configuration file.\n"),
852                   "scp");
853       if (NULL != cb)
854         cb (cb_cls,
855             _("Failed to copy new configuration to remote machine."));
856       d->phase = SP_START_DONE;
857       return;
858     }
859   d->update_cb = cb;
860   d->update_cb_cls = cb_cls;
861   d->task
862     = GNUNET_SCHEDULER_add_delayed (d->sched, 
863                                     GNUNET_CONSTANTS_EXEC_WAIT,
864                                     &start_fsm,
865                                     d);
866 }
867
868
869 /**
870  * Data kept for each pair of peers that we try
871  * to connect.
872  */
873 struct ConnectContext
874 {
875   /**
876    * Testing handle to the first daemon.
877    */
878   struct GNUNET_TESTING_Daemon *d1;
879
880   /**
881    * Testing handle to the second daemon.
882    */
883   struct GNUNET_TESTING_Daemon *d2;
884
885   /**
886    * Transport handle to the first daemon.
887    */
888   struct GNUNET_TRANSPORT_Handle *d1th;
889
890   /**
891    * Transport handle to the second daemon.
892    */
893   struct GNUNET_TRANSPORT_Handle *d2th;
894
895   /**
896    * Function to call once we are done (or have timed out).
897    */
898   GNUNET_TESTING_NotifyCompletion cb;
899
900   /**
901    * Closure for "nb".
902    */
903   void *cb_cls;
904
905   /**
906    * Transmit handle for our request for transmission
907    * (as given to d2 asking to talk to d1).
908    */
909   struct GNUNET_CORE_TransmitHandle *ntr;
910
911   /**
912    * When should this operation be complete (or we must trigger
913    * a timeout).
914    */
915   struct GNUNET_TIME_Absolute timeout;
916
917 };
918
919
920 /**
921  * Notify callback about success or failure of the attempt
922  * to connect the two peers
923  * 
924  * @param cls our "struct ConnectContext" (freed)
925  * @param tc reason tells us if we succeeded or failed
926  */
927 static void
928 notify_connect_result (void *cls,
929                        const struct GNUNET_SCHEDULER_TaskContext *tc)
930 {
931   struct ConnectContext *ctx = cls;
932
933   if (ctx->cb != NULL)
934     {
935       if ((tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT) != 0)
936         ctx->cb (ctx->cb_cls, _("Peers failed to connect"));
937       else
938         ctx->cb (ctx->cb_cls, NULL);
939     }
940   GNUNET_free (ctx);
941 }
942
943
944 /**
945  * Success, connection is up.  Signal client our success.
946  *
947  * @param cls our "struct ConnectContext"
948  * @param size number of bytes available in buf
949  * @param buf where to copy the message, NULL on error
950  * @return number of bytes copied to buf
951  */
952 static size_t
953 transmit_ready (void *cls, size_t size, void *buf)
954 {
955   struct ConnectContext *ctx = cls;
956
957 #if DEBUG_TESTING
958   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
959               "Core notified us about readiness to transmit message, connection must be up!\n");
960 #endif
961   ctx->ntr = NULL;
962   GNUNET_TRANSPORT_disconnect (ctx->d1th);
963   ctx->d1th = NULL;
964   GNUNET_TRANSPORT_disconnect (ctx->d2th);
965   ctx->d2th = NULL;
966   GNUNET_SCHEDULER_add_continuation (ctx->d1->sched,
967                                      &notify_connect_result,
968                                      ctx,
969                                      (buf == NULL) ? 
970                                      GNUNET_SCHEDULER_REASON_TIMEOUT :
971                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
972   return 0;
973 }
974
975
976 #if 0
977 static void
978 timeout_hello_task (void *cls,
979                     const struct GNUNET_SCHEDULER_TaskContext *tc)
980 {
981   GNUNET_TRANSPORT_get_hello_cancel (ctx->d1th, 
982                                      &process_hello, 
983                                      ctx);
984   GNUNET_TRANSPORT_disconnect (ctx->d1th);
985   GNUNET_TRANSPORT_disconnect (ctx->d2th);
986   if (NULL != ctx->cb)
987     ctx->cb (ctx->cb_cls,
988              _("Failed to receive `HELLO' from peer\n"));
989   GNUNET_free (ctx);
990 }
991 #endif
992
993
994 /**
995  * Receive the HELLO from one peer, give it to the other
996  * and ask them to connect.
997  * 
998  * @param cls "struct ConnectContext"
999  * @param message HELLO message of peer
1000  */
1001 static void
1002 process_hello (void *cls,
1003                const struct GNUNET_MessageHeader *message)
1004 {
1005   struct ConnectContext *ctx = cls;
1006
1007   /* first of all, stop the notification stuff */
1008   GNUNET_TRANSPORT_get_hello_cancel (ctx->d1th, 
1009                                      &process_hello, 
1010                                      ctx);
1011 #if DEBUG_TESTING
1012   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1013               "Received `%s' from transport service of `%4s'\n",
1014               "HELLO", GNUNET_i2s (peer));
1015 #endif
1016   GNUNET_assert (message != NULL);
1017   GNUNET_TRANSPORT_offer_hello (ctx->d2th, message);
1018   ctx->ntr 
1019     = GNUNET_CORE_notify_transmit_ready (ctx->d2->server,
1020                                          0,
1021                                          GNUNET_TIME_absolute_get_remaining (ctx->timeout),
1022                                          &ctx->d1->id,
1023                                          sizeof (struct GNUNET_MessageHeader),
1024                                          &transmit_ready, ctx);
1025 }
1026
1027
1028 /**
1029  * Establish a connection between two GNUnet daemons.
1030  *
1031  * @param d1 handle for the first daemon
1032  * @param d2 handle for the second daemon
1033  * @param timeout how long is the connection attempt
1034  *        allowed to take?
1035  * @param cb function to call at the end
1036  * @param cb_cls closure for cb
1037  */
1038 void GNUNET_TESTING_daemons_connect (struct GNUNET_TESTING_Daemon *d1,
1039                                      struct GNUNET_TESTING_Daemon *d2,
1040                                      struct GNUNET_TIME_Relative timeout,
1041                                      GNUNET_TESTING_NotifyCompletion cb,
1042                                      void *cb_cls)
1043 {
1044   struct ConnectContext *ctx;
1045
1046   if ( (d1->server == NULL) ||
1047        (d2->server == NULL) )
1048     {
1049       if (NULL != cb)
1050         cb (cb_cls, _("Peers are not fully running yet, can not connect!\n"));
1051       return;
1052     }
1053   ctx = GNUNET_malloc (sizeof(struct ConnectContext));
1054   ctx->d1 = d1;
1055   ctx->d2 = d2;
1056   ctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1057   ctx->cb = cb;
1058   ctx->cb_cls = cb_cls;
1059   ctx->d1th = GNUNET_TRANSPORT_connect (d1->sched,
1060                                         d1->cfg, 
1061                                         d1,
1062                                         NULL, NULL, NULL);
1063   if (ctx->d1th == NULL)
1064     {
1065       GNUNET_free (ctx);
1066       if (NULL != cb)
1067         cb (cb_cls, _("Failed to connect to transport service!\n"));
1068       return;
1069     }
1070   ctx->d2th = GNUNET_TRANSPORT_connect (d2->sched,
1071                                         d2->cfg, 
1072                                         d2, 
1073                                         NULL, NULL, NULL);
1074   if (ctx->d2th == NULL)
1075     {
1076       GNUNET_TRANSPORT_disconnect (ctx->d1th);
1077       GNUNET_free (ctx);
1078       if (NULL != cb)
1079         cb (cb_cls, _("Failed to connect to transport service!\n"));
1080       return;
1081     }                                          
1082   /* FIXME: need to handle timeout: start timeout task 
1083      as well here! (use 'timeout_hello_task') */
1084   GNUNET_TRANSPORT_get_hello (ctx->d1th, 
1085                               &process_hello, 
1086                               ctx);
1087 }
1088
1089
1090 /* end of testing.c */