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