(no commit message)
[oweals/gnunet.git] / src / hostlist / test_gnunet_daemon_hostlist_learning.c
1 /*
2      This file is part of GNUnet
3      (C) 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  * @file hostlist/test_gnunet_daemon_hostlist.c
22  * @brief test for gnunet_daemon_hostslist.c
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include "gnunet_util_lib.h"
27 #include "gnunet_arm_service.h"
28 #include "gnunet_core_service.h"
29 #include "gnunet_transport_service.h"
30 #include "gnunet_resolver_service.h"
31 #include "gnunet_statistics_service.h"
32
33 #define VERBOSE GNUNET_YES
34
35 #define START_ARM GNUNET_YES
36 #define MAX_URL_LEN 1000
37
38 /**
39  * How long until wait until testcases fails
40  */
41 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 20)
42 #define CHECK_INTERVALL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 2)
43
44 static int timeout;
45 static int adv_arrived;
46 static int learned_hostlist_downloaded;
47
48 static struct GNUNET_SCHEDULER_Handle *sched;
49
50 static GNUNET_SCHEDULER_TaskIdentifier timeout_task;
51 static GNUNET_SCHEDULER_TaskIdentifier check_task;
52     
53 struct PeerContext
54 {
55   struct GNUNET_CONFIGURATION_Handle *cfg;
56   struct GNUNET_TRANSPORT_Handle *th;
57   struct GNUNET_MessageHeader *hello;
58   struct GNUNET_ARM_Handle *arm;
59   struct GNUNET_CORE_Handle *core;
60   struct GNUNET_STATISTICS_Handle *stats;
61 #if START_ARM
62   pid_t arm_pid;
63 #endif
64 };
65
66 static struct PeerContext adv_peer;
67
68 static struct PeerContext learn_peer;
69
70 static void
71 clean_up (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
72 {
73   if (adv_peer.th != NULL)
74   {
75     GNUNET_TRANSPORT_disconnect (adv_peer.th);
76     adv_peer.th = NULL;
77   }
78   if (learn_peer.th != NULL)
79   {
80     GNUNET_TRANSPORT_disconnect (learn_peer.th);
81     learn_peer.th = NULL;
82   }
83   if (adv_peer.core != NULL)
84   {
85     GNUNET_CORE_disconnect (adv_peer.core);
86     adv_peer.core = NULL;
87   }
88   if (learn_peer.core != NULL)
89   {
90     GNUNET_CORE_disconnect (learn_peer.core);
91     learn_peer.core = NULL;
92   }
93   GNUNET_SCHEDULER_shutdown (sched);
94 }
95
96 static void shutdown_testcase()
97 {
98   if (timeout_task != GNUNET_SCHEDULER_NO_TASK)
99   {
100     GNUNET_SCHEDULER_cancel (sched,
101                              timeout_task);
102     timeout_task = GNUNET_SCHEDULER_NO_TASK;
103   }
104   if (check_task != GNUNET_SCHEDULER_NO_TASK)
105   {
106     GNUNET_SCHEDULER_cancel (sched,
107         check_task);
108     check_task = GNUNET_SCHEDULER_NO_TASK;
109   }
110   GNUNET_SCHEDULER_add_now (sched,
111                             &clean_up, NULL);
112 }
113
114 /**
115  * Timeout, give up.
116  */
117 static void
118 timeout_error (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
119 {
120   timeout_task = GNUNET_SCHEDULER_NO_TASK;
121   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
122               "Timeout while executing testcase, test failed.\n");
123   timeout = GNUNET_YES;
124   clean_up (NULL, tc);
125 }
126
127 static int
128 process_stat (void *cls,
129               const char *subsystem,
130               const char *name,
131               uint64_t value,
132               int is_persistent)
133 {
134
135   if ( GNUNET_YES == GNUNET_YES)
136   {
137     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
138                 _("Client has successfully downloaded advertised URI \n"));
139     learned_hostlist_downloaded = GNUNET_YES;
140   }
141   if ( GNUNET_NO != learned_hostlist_downloaded )
142     shutdown_testcase();
143   return GNUNET_OK;
144 }
145
146 /**
147  * Check the server statistics regularly
148  */
149 static void
150 check_statistics (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
151 {
152   GNUNET_STATISTICS_get (learn_peer.stats,
153                          "hostlist",
154                          gettext_noop("Learned URI downloaded"),
155                          GNUNET_TIME_UNIT_MINUTES,
156                          NULL,
157                          &process_stat,
158                          NULL);
159   check_task = GNUNET_SCHEDULER_add_delayed (sched,
160                                 CHECK_INTERVALL,
161                                 &check_statistics,
162                                 NULL);
163 }
164
165 /**
166  * Core handler for p2p hostlist advertisements
167  */
168 static int ad_arrive_handler (void *cls,
169                              const struct GNUNET_PeerIdentity * peer,
170                              const struct GNUNET_MessageHeader * message,
171                              struct GNUNET_TIME_Relative latency,
172                              uint32_t distance)
173 {
174   char *hostname;
175   char *expected_uri = GNUNET_malloc (MAX_URL_LEN);
176   char *recv_uri;
177
178   unsigned long long port;
179   size_t size;
180   const struct GNUNET_MessageHeader * incoming;
181
182   if (-1 == GNUNET_CONFIGURATION_get_value_number (adv_peer.cfg,
183                                                    "HOSTLIST",
184                                                    "HTTPPORT",
185                                                    &port))
186     {
187     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
188                 "Could not read advertising server's configuration\n" );
189     return GNUNET_SYSERR;
190     }
191   hostname = GNUNET_RESOLVER_local_hostname_get ();
192   if (NULL != hostname)
193     {
194       size = strlen (hostname);
195       if (size + 15 > MAX_URL_LEN)
196         {
197           GNUNET_break (0);
198         }
199       else
200         {
201           GNUNET_asprintf (&expected_uri,
202                            "http://%s:%u/",
203                            hostname,
204                            (unsigned int) port);
205         }
206     }
207
208   incoming = (const struct GNUNET_MessageHeader *) message;
209   recv_uri = (char*) &incoming[1];
210   if ( 0 == strcmp( expected_uri, recv_uri ) )
211   {
212     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
213                 "Recieved hostlist advertisement with URI `%s'as expected\n", recv_uri);
214     adv_arrived = GNUNET_YES;
215   }
216   else
217     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
218                 "Expected URI `%s' and recieved URI `%s' differ\n", expected_uri, recv_uri);
219   GNUNET_free ( expected_uri );
220   GNUNET_free ( hostname );
221   return GNUNET_OK;
222 }
223
224 /**
225  * List of handlers if we are learning.
226  */
227 static struct GNUNET_CORE_MessageHandler learn_handlers[] = {
228   { &ad_arrive_handler, GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT, 0},
229   { NULL, 0, 0 }
230 };
231
232 static void
233 setup_learn_peer (struct PeerContext *p, const char *cfgname)
234 {
235   char * filename;
236   unsigned int result;
237   p->cfg = GNUNET_CONFIGURATION_create ();
238 #if START_ARM
239   p->arm_pid = GNUNET_OS_start_process (NULL, NULL, "gnunet-service-arm",
240                                         "gnunet-service-arm",
241 #if VERBOSE
242                                         "-L", "DEBUG",
243 #endif
244                                         "-c", cfgname, NULL);
245 #endif
246   GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
247   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (p->cfg,
248                                                           "HOSTLIST",
249                                                           "HOSTLISTFILE",
250                                                           &filename))
251   {
252   if ( GNUNET_YES == GNUNET_DISK_file_test (filename) )
253     {
254       result = remove (filename);
255       if (result == 0)
256       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
257             _("Hostlist file `%s' was removed\n"),filename);
258     }
259   }
260   if ( NULL != filename)  GNUNET_free ( filename );
261
262   GNUNET_ARM_start_services (p->cfg, sched, "core", NULL);
263
264   p->core = GNUNET_CORE_connect (sched, p->cfg,
265                               GNUNET_TIME_UNIT_FOREVER_REL,
266                               NULL,
267                               NULL,
268                               NULL, NULL,
269                               NULL, GNUNET_NO,
270                               NULL, GNUNET_NO,
271                               learn_handlers );
272   GNUNET_assert ( NULL != p->core );
273   p->stats = GNUNET_STATISTICS_create (sched, "hostlist", p->cfg);
274   GNUNET_assert ( NULL != p->stats );
275 }
276
277
278 static void
279 setup_adv_peer (struct PeerContext *p, const char *cfgname)
280 {
281   p->cfg = GNUNET_CONFIGURATION_create ();
282 #if START_ARM
283   p->arm_pid = GNUNET_OS_start_process (NULL, NULL, "gnunet-service-arm",
284                                         "gnunet-service-arm",
285 #if VERBOSE
286                                         "-L", "DEBUG",
287 #endif
288                                         "-c", cfgname, NULL);
289 #endif
290   GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
291   GNUNET_ARM_start_services (p->cfg, sched, "core", NULL);
292 }
293
294
295
296 static void
297 waitpid_task (void *cls, 
298               const struct GNUNET_SCHEDULER_TaskContext *tc)
299 {
300   struct PeerContext *p = cls;
301
302 #if START_ARM 
303   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
304               "Killing ARM process.\n");
305   if (0 != PLIBC_KILL (p->arm_pid, SIGTERM))
306     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
307   if (GNUNET_OS_process_wait(p->arm_pid) != GNUNET_OK)
308     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
309   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
310               "ARM process %u stopped\n", p->arm_pid);
311 #endif
312   GNUNET_CONFIGURATION_destroy (p->cfg);
313 }
314
315
316 static void
317 stop_cb (void *cls, 
318          int success)
319 {
320   struct PeerContext *p = cls;
321
322   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
323               success
324               ? "ARM stopped core service\n"
325               : "ARM failed to stop core service\n");
326   GNUNET_ARM_disconnect (p->arm);
327   p->arm = NULL;
328   /* make sure this runs after all other tasks are done */
329   GNUNET_SCHEDULER_add_delayed (sched,
330                                 GNUNET_TIME_UNIT_SECONDS,
331                                 &waitpid_task, p);
332 }
333
334
335 static void
336 stop_arm (struct PeerContext *p)
337 {
338   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
339               "Asking ARM to stop core service\n");
340   p->arm = GNUNET_ARM_connect (p->cfg, sched, NULL);
341   GNUNET_ARM_stop_service (p->arm, "core", GNUNET_TIME_UNIT_SECONDS,
342                            &stop_cb, p);
343 }
344
345
346 /**
347  * Try again to connect to transport service.
348  */
349 static void
350 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
351 {
352   stop_arm (&adv_peer);
353   stop_arm (&learn_peer);
354 }
355
356
357 static void
358 run (void *cls,
359      struct GNUNET_SCHEDULER_Handle *s,
360      char *const *args,
361      const char *cfgfile, 
362      const struct GNUNET_CONFIGURATION_Handle *cfg)
363 {
364   timeout = GNUNET_NO;
365   adv_arrived = GNUNET_NO;
366   learned_hostlist_downloaded = GNUNET_NO;
367   sched = s;
368   timeout_task = GNUNET_SCHEDULER_add_delayed (sched,
369                                                TIMEOUT,
370                                                &timeout_error,
371                                                NULL);
372   check_task = GNUNET_SCHEDULER_add_delayed (sched,
373                                 CHECK_INTERVALL,
374                                 &check_statistics,
375                                 NULL);
376   GNUNET_SCHEDULER_add_delayed (sched,
377                                 GNUNET_TIME_UNIT_FOREVER_REL,
378                                 &shutdown_task,
379                                 NULL);
380   setup_adv_peer (&adv_peer, "test_learning_adv_peer.conf");
381   setup_learn_peer (&learn_peer, "test_learning_learn_peer.conf");
382 }
383
384
385 static int
386 check ()
387 {
388   char *const argv[] = { "test-gnunet-daemon-hostlist",
389     "-c", "learning_data.conf",
390 #if VERBOSE
391     "-L", "DEBUG",
392 #endif
393     NULL
394   };
395   struct GNUNET_GETOPT_CommandLineOption options[] = {
396     GNUNET_GETOPT_OPTION_END
397   };
398
399   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
400                       argv, "test-gnunet-daemon-hostlist",
401                       "nohelp", options, &run, NULL);
402
403   if (timeout == GNUNET_YES)
404   {
405     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
406                 "Testcase could not set up two communicating peers, timeout\n");
407     return GNUNET_YES;
408   }
409   if (adv_arrived == GNUNET_NO)
410   {
411     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
412                 "Learning peer did not recieve advertisement from server\n");
413     return GNUNET_YES;
414   }
415   if (learned_hostlist_downloaded == GNUNET_NO)
416   {
417     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
418                 "Advertisement hostlist could not be downloaded from server\n");
419     return GNUNET_YES;
420   }
421   return GNUNET_NO;
422 }
423
424 int
425 main (int argc, char *argv[])
426 {
427   
428   int ret;
429
430   GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-hostlist-peer-1");
431   GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-hostlist-peer-2");
432   GNUNET_log_setup ("test-gnunet-daemon-hostlist",
433 #if VERBOSE
434                     "DEBUG",
435 #else
436                     "WARNING",
437 #endif
438                     NULL);
439   ret = check ();
440   GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-hostlist-peer-1");
441   GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-hostlist-peer-2");
442   return ret; 
443 }
444
445 /* end of test_gnunet_daemon_hostlist.c */