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