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