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