be less verbose
[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   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
716               _("Terminating peer `%4s'\n"),
717               GNUNET_i2s(&d->id));
718   cc = GNUNET_CLIENT_connect (d->sched,
719                               "arm",
720                               d->cfg);
721   GNUNET_CLIENT_service_shutdown (cc);
722   
723   /* state clean up and notifications */
724   if (0 != UNLINK (d->cfgfile))
725     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
726                               "unlink",
727                               d->cfgfile);
728   if (d->hostname != NULL)
729     {
730 #if DEBUG_TESTING
731       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
732                   "Removing configuration file on remote host `%s'.\n",
733                   d->hostname);
734 #endif
735       if (NULL != d->username)
736         GNUNET_asprintf (&dst,
737                          "%s@%s",
738                          d->username,
739                          d->hostname);
740       else
741         dst = GNUNET_strdup (d->hostname);
742       d->pid = GNUNET_OS_start_process ("ssh",
743                                         "ssh",
744                                         dst,
745                                         "rm",
746                                         d->cfgfile,
747                                         NULL);
748       GNUNET_free (dst);
749       if (-1 == d->pid)
750         {
751           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
752                       _("Could not start `%s' process to delete configuration file.\n"),
753                       "ssh");
754           GNUNET_free (d->cfgfile);
755           GNUNET_free_non_null (d->hostname);
756           GNUNET_free_non_null (d->username);
757           GNUNET_free (d);
758           cb (cb_cls, _("Error cleaning up configuration file.\n"));
759           return;
760         }
761       d->phase = SP_CLEANUP;
762       d->dead_cb = cb;
763       d->dead_cb_cls = cb_cls;
764       d->task
765         = GNUNET_SCHEDULER_add_delayed (d->sched, 
766                                         GNUNET_CONSTANTS_EXEC_WAIT,
767                                         &start_fsm,
768                                         d);
769       return;
770     }
771   GNUNET_CONFIGURATION_destroy (d->cfg);
772   GNUNET_free (d->cfgfile);
773   GNUNET_free_non_null (d->hostname);
774   GNUNET_free_non_null (d->username);
775   GNUNET_free (d);
776   if (NULL != cb)
777     cb (cb_cls, NULL);
778 }
779
780
781 /**
782  * Changes the configuration of a GNUnet daemon.
783  *
784  * @param d the daemon that should be modified
785  * @param cfg the new configuration for the daemon
786  * @param cb function called once the configuration was changed
787  * @param cb_cls closure for cb
788  */
789 void GNUNET_TESTING_daemon_reconfigure (struct GNUNET_TESTING_Daemon *d,
790                                         struct GNUNET_CONFIGURATION_Handle *cfg,
791                                         GNUNET_TESTING_NotifyCompletion cb,
792                                         void * cb_cls)
793 {
794   char *arg;
795
796   if (d->phase != SP_START_DONE)
797     {
798       if (NULL != cb)
799         cb (cb_cls,
800             _("Peer not yet running, can not change configuration at this point."));
801       return;      
802     }
803
804   /* 1) write configuration to temporary file */
805   if (GNUNET_OK != 
806       GNUNET_CONFIGURATION_write (cfg,
807                                   d->cfgfile))
808     {
809       if (NULL != cb)
810         cb (cb_cls,
811           _("Failed to write new configuration to disk."));
812       return;
813     }
814
815   /* 2) copy file to remote host (if necessary) */  
816   if (NULL == d->hostname)
817     {
818       /* signal success */
819       if (NULL != cb)
820         cb (cb_cls, NULL); 
821       return;
822     }
823 #if DEBUG_TESTING
824   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
825               "Copying updated configuration file to remote host `%s'.\n",
826               d->hostname);
827 #endif
828   d->phase = SP_CONFIG_UPDATE;
829   if (NULL != d->username)
830     GNUNET_asprintf (&arg,
831                      "%s@%s:%s", 
832                      d->username,
833                      d->hostname,
834                      d->cfgfile);
835   else
836     GNUNET_asprintf (&arg,
837                      "%s:%s", 
838                      d->hostname,
839                      d->cfgfile);
840   d->pid = GNUNET_OS_start_process ("scp",
841                                     "scp",
842                                     d->cfgfile,
843                                     arg,
844                                     NULL);
845   GNUNET_free (arg);
846   if (-1 == d->pid)
847     {
848       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
849                   _("Could not start `%s' process to copy configuration file.\n"),
850                   "scp");
851       if (NULL != cb)
852         cb (cb_cls,
853             _("Failed to copy new configuration to remote machine."));
854       d->phase = SP_START_DONE;
855       return;
856     }
857   d->update_cb = cb;
858   d->update_cb_cls = cb_cls;
859   d->task
860     = GNUNET_SCHEDULER_add_delayed (d->sched, 
861                                     GNUNET_CONSTANTS_EXEC_WAIT,
862                                     &start_fsm,
863                                     d);
864 }
865
866
867 /**
868  * Data kept for each pair of peers that we try
869  * to connect.
870  */
871 struct ConnectContext
872 {
873   /**
874    * Testing handle to the first daemon.
875    */
876   struct GNUNET_TESTING_Daemon *d1;
877
878   /**
879    * Testing handle to the second daemon.
880    */
881   struct GNUNET_TESTING_Daemon *d2;
882
883   /**
884    * Transport handle to the first daemon.
885    */
886   struct GNUNET_TRANSPORT_Handle *d1th;
887
888   /**
889    * Transport handle to the second daemon.
890    */
891   struct GNUNET_TRANSPORT_Handle *d2th;
892
893   /**
894    * Function to call once we are done (or have timed out).
895    */
896   GNUNET_TESTING_NotifyCompletion cb;
897
898   /**
899    * Closure for "nb".
900    */
901   void *cb_cls;
902
903   /**
904    * Transmit handle for our request for transmission
905    * (as given to d2 asking to talk to d1).
906    */
907   struct GNUNET_CORE_TransmitHandle *ntr;
908
909   /**
910    * When should this operation be complete (or we must trigger
911    * a timeout).
912    */
913   struct GNUNET_TIME_Absolute timeout;
914
915 };
916
917
918 /**
919  * Notify callback about success or failure of the attempt
920  * to connect the two peers
921  * 
922  * @param cls our "struct ConnectContext" (freed)
923  * @param tc reason tells us if we succeeded or failed
924  */
925 static void
926 notify_connect_result (void *cls,
927                        const struct GNUNET_SCHEDULER_TaskContext *tc)
928 {
929   struct ConnectContext *ctx = cls;
930
931   if (ctx->cb != NULL)
932     {
933       if ((tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT) != 0)
934         ctx->cb (ctx->cb_cls, _("Peers failed to connect"));
935       else
936         ctx->cb (ctx->cb_cls, NULL);
937     }
938   GNUNET_free (ctx);
939 }
940
941
942 /**
943  * Success, connection is up.  Signal client our success.
944  *
945  * @param cls our "struct ConnectContext"
946  * @param size number of bytes available in buf
947  * @param buf where to copy the message, NULL on error
948  * @return number of bytes copied to buf
949  */
950 static size_t
951 transmit_ready (void *cls, size_t size, void *buf)
952 {
953   struct ConnectContext *ctx = cls;
954
955 #if DEBUG_TESTING
956   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
957               "Core notified us about readiness to transmit message, connection must be up!\n");
958 #endif
959   ctx->ntr = NULL;
960   GNUNET_TRANSPORT_disconnect (ctx->d1th);
961   ctx->d1th = NULL;
962   GNUNET_TRANSPORT_disconnect (ctx->d2th);
963   ctx->d2th = NULL;
964   GNUNET_SCHEDULER_add_continuation (ctx->d1->sched,
965                                      &notify_connect_result,
966                                      ctx,
967                                      (buf == NULL) ? 
968                                      GNUNET_SCHEDULER_REASON_TIMEOUT :
969                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
970   return 0;
971 }
972
973
974 /**
975  * Receive the HELLO from one peer, give it to the other
976  * and ask them to connect.
977  * 
978  * @param cls "struct ConnectContext"
979  * @param latency how fast is the connection
980  * @param peer ID of peer giving us the HELLO
981  * @param message HELLO message of peer
982  */
983 static void
984 process_hello (void *cls,
985                struct GNUNET_TIME_Relative latency,
986                const struct GNUNET_PeerIdentity *peer,
987                const struct GNUNET_MessageHeader *message)
988 {
989   struct ConnectContext *ctx = cls;
990
991   if (peer == NULL)
992     {
993       /* signal error */
994       GNUNET_TRANSPORT_disconnect (ctx->d1th);
995       GNUNET_TRANSPORT_disconnect (ctx->d2th);
996       if (NULL != ctx->cb)
997         ctx->cb (ctx->cb_cls,
998                  _("Failed to receive `HELLO' from peer\n"));
999       GNUNET_free (ctx);
1000       return;
1001     }
1002 #if DEBUG_TESTING
1003   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1004               "Received `%s' from transport service of `%4s'\n",
1005               "HELLO", GNUNET_i2s (peer));
1006 #endif
1007   GNUNET_assert (message != NULL);
1008   GNUNET_TRANSPORT_offer_hello (ctx->d2th, message);
1009   ctx->ntr 
1010     = GNUNET_CORE_notify_transmit_ready (ctx->d2->server,
1011                                          0,
1012                                          GNUNET_TIME_absolute_get_remaining (ctx->timeout),
1013                                          &ctx->d1->id,
1014                                          sizeof (struct GNUNET_MessageHeader),
1015                                          &transmit_ready, ctx);
1016 }
1017
1018
1019 /**
1020  * Establish a connection between two GNUnet daemons.
1021  *
1022  * @param d1 handle for the first daemon
1023  * @param d2 handle for the second daemon
1024  * @param timeout how long is the connection attempt
1025  *        allowed to take?
1026  * @param cb function to call at the end
1027  * @param cb_cls closure for cb
1028  */
1029 void GNUNET_TESTING_daemons_connect (struct GNUNET_TESTING_Daemon *d1,
1030                                      struct GNUNET_TESTING_Daemon *d2,
1031                                      struct GNUNET_TIME_Relative timeout,
1032                                      GNUNET_TESTING_NotifyCompletion cb,
1033                                      void *cb_cls)
1034 {
1035   struct ConnectContext *ctx;
1036
1037   if ( (d1->server == NULL) ||
1038        (d2->server == NULL) )
1039     {
1040       if (NULL != cb)
1041         cb (cb_cls, _("Peers are not fully running yet, can not connect!\n"));
1042       return;
1043     }
1044   ctx = GNUNET_malloc (sizeof(struct ConnectContext));
1045   ctx->d1 = d1;
1046   ctx->d2 = d2;
1047   ctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1048   ctx->cb = cb;
1049   ctx->cb_cls = cb_cls;
1050   ctx->d1th = GNUNET_TRANSPORT_connect (d1->sched,
1051                                         d1->cfg, 
1052                                         d1,
1053                                         NULL, NULL, NULL);
1054   if (ctx->d1th == NULL)
1055     {
1056       GNUNET_free (ctx);
1057       if (NULL != cb)
1058         cb (cb_cls, _("Failed to connect to transport service!\n"));
1059       return;
1060     }
1061   ctx->d2th = GNUNET_TRANSPORT_connect (d2->sched,
1062                                         d2->cfg, 
1063                                         d2, 
1064                                         NULL, NULL, NULL);
1065   if (ctx->d2th == NULL)
1066     {
1067       GNUNET_TRANSPORT_disconnect (ctx->d1th);
1068       GNUNET_free (ctx);
1069       if (NULL != cb)
1070         cb (cb_cls, _("Failed to connect to transport service!\n"));
1071       return;
1072     }
1073   GNUNET_TRANSPORT_get_hello (ctx->d1th, 
1074                               timeout,
1075                               &process_hello, 
1076                               ctx);
1077 }
1078
1079
1080 /* end of testing.c */