cleanup API for get_hello and get_hello_cancel
[oweals/gnunet.git] / src / core / test_core_api_preferences.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010, 2011 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 3, 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 core/test_core_api_preferences.c
22  * @brief testcase for core_api.c's GNUNET_CORE_change_preference
23  */
24 #include "platform.h"
25 #include "gnunet_common.h"
26 #include "gnunet_arm_service.h"
27 #include "gnunet_core_service.h"
28 #include "gnunet_getopt_lib.h"
29 #include "gnunet_os_lib.h"
30 #include "gnunet_program_lib.h"
31 #include "gnunet_scheduler_lib.h"
32 #include "gnunet_transport_service.h"
33
34 #define VERBOSE GNUNET_NO
35
36 #define START_ARM GNUNET_YES
37
38 #define MTYPE 12345
39
40 struct PeerContext
41 {
42   struct GNUNET_CONFIGURATION_Handle *cfg;
43   struct GNUNET_CORE_Handle *ch;
44   struct GNUNET_PeerIdentity id;
45   struct GNUNET_TRANSPORT_Handle *th;
46   struct GNUNET_TRANSPORT_GetHelloHandle *ghh;
47   struct GNUNET_MessageHeader *hello;
48   int connect_status;
49 #if START_ARM
50   struct GNUNET_OS_Process *arm_proc;
51 #endif
52 };
53
54 static struct PeerContext p1;
55
56 static struct PeerContext p2;
57
58 static GNUNET_SCHEDULER_TaskIdentifier err_task;
59
60 static int ok;
61
62 #if VERBOSE
63 #define OKPP do { ok++; fprintf (stderr, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0)
64 #else
65 #define OKPP do { ok++; } while (0)
66 #endif
67
68 static struct GNUNET_CORE_InformationRequestContext *irc;
69
70 static struct GNUNET_CORE_TransmitHandle *th;
71
72 static GNUNET_SCHEDULER_TaskIdentifier irc_task;
73
74 static GNUNET_SCHEDULER_TaskIdentifier ask_task;
75
76 static unsigned int total_reserve = 5;
77
78
79 static void
80 process_hello (void *cls, const struct GNUNET_MessageHeader *message);
81
82 static void
83 terminate_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
84 {
85 #if VERBOSE
86   fprintf (stderr, "Regular shutdown\n");
87 #endif
88   GNUNET_assert (ok == 6);
89   GNUNET_assert (NULL == irc);
90   GNUNET_TRANSPORT_get_hello_cancel (p1.ghh);
91   GNUNET_TRANSPORT_get_hello_cancel (p2.ghh);
92   GNUNET_CORE_disconnect (p1.ch);
93   GNUNET_CORE_disconnect (p2.ch);
94   GNUNET_TRANSPORT_disconnect (p1.th);
95   GNUNET_TRANSPORT_disconnect (p2.th);
96   ok = 0;
97 }
98
99
100 static void
101 terminate_task_error (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
102 {
103   err_task = GNUNET_SCHEDULER_NO_TASK;
104 #if VERBOSE
105   fprintf (stderr, "ENDING ANGRILY %u\n", ok);
106 #endif
107   GNUNET_break (0);
108   if (NULL != irc)
109   {
110     GNUNET_CORE_peer_change_preference_cancel (irc);
111     irc = NULL;
112   }
113   if (GNUNET_SCHEDULER_NO_TASK != irc_task)
114   {
115     GNUNET_SCHEDULER_cancel (irc_task);
116     irc_task = GNUNET_SCHEDULER_NO_TASK;
117   }
118   if (GNUNET_SCHEDULER_NO_TASK != ask_task)
119   {
120     GNUNET_SCHEDULER_cancel (ask_task);
121     ask_task = GNUNET_SCHEDULER_NO_TASK;
122   }
123   GNUNET_TRANSPORT_get_hello_cancel (p1.ghh);
124   GNUNET_TRANSPORT_get_hello_cancel (p2.ghh);
125   GNUNET_CORE_disconnect (p1.ch);
126   GNUNET_CORE_disconnect (p2.ch);
127   GNUNET_TRANSPORT_disconnect (p1.th);
128   GNUNET_TRANSPORT_disconnect (p2.th);
129   ok = 42;
130 }
131
132
133 static size_t
134 transmit_ready (void *cls, size_t size, void *buf)
135 {
136   struct PeerContext *p = cls;
137   struct GNUNET_MessageHeader *m;
138
139   th = NULL;
140   GNUNET_assert (ok == 4);
141   OKPP;
142   GNUNET_assert (p == &p1);
143   GNUNET_assert (buf != NULL);
144   m = (struct GNUNET_MessageHeader *) buf;
145   m->type = htons (MTYPE);
146   m->size = htons (sizeof (struct GNUNET_MessageHeader));
147   err_task =
148       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
149                                     (GNUNET_TIME_UNIT_SECONDS, 120),
150                                     &terminate_task_error, NULL);
151
152   return sizeof (struct GNUNET_MessageHeader);
153 }
154
155 static void
156 preference_cb (void *cls, const struct GNUNET_PeerIdentity *peer,
157                struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out, int32_t amount,
158                struct GNUNET_TIME_Relative res_delay, uint64_t preference);
159
160 static void
161 do_reserve (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
162 {
163   struct PeerContext *pc = cls;
164
165   irc_task = GNUNET_SCHEDULER_NO_TASK;
166   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
167   {
168     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
169                 "Aborted during attempt to request reservation\n");
170     return;
171   }
172   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
173               "Requesting reservatin of 32k from core in 1s!\n");
174   irc =
175       GNUNET_CORE_peer_change_preference (p1.ch, &p2.id,
176                                           GNUNET_TIME_UNIT_SECONDS,
177                                           GNUNET_BANDWIDTH_VALUE_MAX,
178                                           1000000 /* bandwidth for 1s */ ,
179                                           0, &preference_cb, pc);
180 }
181
182 static void
183 preference_cb (void *cls, const struct GNUNET_PeerIdentity *peer,
184                struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out, int32_t amount,
185                struct GNUNET_TIME_Relative res_delay, uint64_t preference)
186 {
187   struct PeerContext *pc = cls;
188
189   irc = NULL;
190   if (0 == amount)
191   {
192     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
193                 "Delaying reservation request by %llu ms!\n",
194                 (unsigned long long) res_delay.rel_value);
195     irc_task = GNUNET_SCHEDULER_add_delayed (res_delay, &do_reserve, pc);
196     return;
197   }
198   total_reserve--;
199   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Reservation done!\n");
200   if (total_reserve > 0)
201   {
202     irc_task = GNUNET_SCHEDULER_add_now (&do_reserve, pc);
203     return;
204   }
205   GNUNET_SCHEDULER_cancel (err_task);
206   err_task = GNUNET_SCHEDULER_NO_TASK;
207   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
208               "Asking core (1) for transmission to peer `%4s'\n",
209               GNUNET_i2s (&p2.id));
210   if (NULL ==
211       (th =
212        GNUNET_CORE_notify_transmit_ready (p1.ch, GNUNET_YES, 0,
213                                           GNUNET_TIME_relative_multiply
214                                           (GNUNET_TIME_UNIT_SECONDS, 45),
215                                           &p2.id,
216                                           sizeof (struct GNUNET_MessageHeader),
217                                           &transmit_ready, &p1)))
218   {
219     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
220                 "RECEIVED NULL when asking core (1) for transmission to peer `%4s'\n",
221                 GNUNET_i2s (&p2.id));
222   }
223 }
224
225
226 static void
227 connect_notify (void *cls, const struct GNUNET_PeerIdentity *peer,
228                 const struct GNUNET_TRANSPORT_ATS_Information *atsi)
229 {
230   struct PeerContext *pc = cls;
231
232   if (0 == memcmp (&pc->id, peer, sizeof (struct GNUNET_PeerIdentity)))
233     return;
234   GNUNET_assert (pc->connect_status == 0);
235   pc->connect_status = 1;
236   if (pc == &p1)
237   {
238     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
239                 "Encrypted connection established to peer `%4s'\n",
240                 GNUNET_i2s (peer));
241     if (GNUNET_SCHEDULER_NO_TASK != ask_task)
242     {
243       GNUNET_SCHEDULER_cancel (ask_task);
244       ask_task = GNUNET_SCHEDULER_NO_TASK;
245     }
246     GNUNET_SCHEDULER_cancel (err_task);
247     err_task =
248         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
249                                       (GNUNET_TIME_UNIT_SECONDS, 120),
250                                       &terminate_task_error, NULL);
251     irc_task = GNUNET_SCHEDULER_add_now (&do_reserve, pc);
252   }
253 }
254
255
256 static void
257 disconnect_notify (void *cls, const struct GNUNET_PeerIdentity *peer)
258 {
259   struct PeerContext *pc = cls;
260
261   if (0 == memcmp (&pc->id, peer, sizeof (struct GNUNET_PeerIdentity)))
262     return;
263   pc->connect_status = 0;
264   if (GNUNET_SCHEDULER_NO_TASK != irc_task)
265   {
266     GNUNET_SCHEDULER_cancel (irc_task);
267     irc_task = GNUNET_SCHEDULER_NO_TASK;
268   }
269   if (0 == memcmp (peer, &p1.id, sizeof (struct GNUNET_PeerIdentity)))
270   {
271     if (irc != NULL)
272     {
273       GNUNET_CORE_peer_change_preference_cancel (irc);
274       irc = NULL;
275     }
276     if (th != NULL)
277     {
278       GNUNET_CORE_notify_transmit_ready_cancel (th);
279       th = NULL;
280     }
281   }
282   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Encrypted connection to `%4s' cut\n",
283               GNUNET_i2s (peer));
284 }
285
286
287 static int
288 inbound_notify (void *cls, const struct GNUNET_PeerIdentity *other,
289                 const struct GNUNET_MessageHeader *message,
290                 const struct GNUNET_TRANSPORT_ATS_Information *atsi)
291 {
292   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
293               "Core provides inbound data from `%4s'.\n", GNUNET_i2s (other));
294   return GNUNET_OK;
295 }
296
297
298 static int
299 outbound_notify (void *cls, const struct GNUNET_PeerIdentity *other,
300                  const struct GNUNET_MessageHeader *message,
301                  const struct GNUNET_TRANSPORT_ATS_Information *atsi)
302 {
303   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
304               "Core notifies about outbound data for `%4s'.\n",
305               GNUNET_i2s (other));
306   return GNUNET_OK;
307 }
308
309
310
311 static int
312 process_mtype (void *cls, const struct GNUNET_PeerIdentity *peer,
313                const struct GNUNET_MessageHeader *message,
314                const struct GNUNET_TRANSPORT_ATS_Information *atsi)
315 {
316   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Receiving message from `%4s'.\n",
317               GNUNET_i2s (peer));
318   GNUNET_assert (ok == 5);
319   OKPP;
320   GNUNET_SCHEDULER_cancel (err_task);
321   err_task = GNUNET_SCHEDULER_NO_TASK;
322   GNUNET_SCHEDULER_add_now (&terminate_task, NULL);
323   return GNUNET_OK;
324 }
325
326
327 static struct GNUNET_CORE_MessageHandler handlers[] = {
328   {&process_mtype, MTYPE, sizeof (struct GNUNET_MessageHeader)},
329   {NULL, 0, 0}
330 };
331
332
333
334 static void
335 ask_connect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
336 {
337   ask_task =
338       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &ask_connect_task,
339                                     NULL);
340   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
341               "Asking core (1) AGAIN to connect to peer `%4s'\n",
342               GNUNET_i2s (&p2.id));
343   GNUNET_CORE_peer_request_connect (p1.ch, &p2.id, NULL, NULL);
344 }
345
346
347 static void
348 init_notify (void *cls, struct GNUNET_CORE_Handle *server,
349              const struct GNUNET_PeerIdentity *my_identity,
350              const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
351 {
352   struct PeerContext *p = cls;
353
354   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Core connection to `%4s' established\n",
355               GNUNET_i2s (my_identity));
356   GNUNET_assert (server != NULL);
357   p->id = *my_identity;
358   p->ch = server;
359   if (cls == &p1)
360   {
361     GNUNET_assert (ok == 2);
362     OKPP;
363     /* connect p2 */
364     GNUNET_CORE_connect (p2.cfg, 1, &p2, &init_notify, &connect_notify,
365                          &disconnect_notify, NULL, &inbound_notify, GNUNET_YES,
366                          &outbound_notify, GNUNET_YES, handlers);
367   }
368   else
369   {
370     GNUNET_assert (ok == 3);
371     OKPP;
372     GNUNET_assert (cls == &p2);
373     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
374                 "Asking core (1) to connect to peer `%4s'\n",
375                 GNUNET_i2s (&p2.id));
376     err_task =
377         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
378                                       (GNUNET_TIME_UNIT_SECONDS, 60),
379                                       &terminate_task_error, NULL);
380     if (GNUNET_SCHEDULER_NO_TASK != ask_task)
381       GNUNET_SCHEDULER_cancel (ask_task);
382     ask_task =
383         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
384                                       &ask_connect_task, NULL);
385     GNUNET_CORE_peer_request_connect (p1.ch, &p2.id, NULL, NULL);
386   }
387 }
388
389
390 static void
391 process_hello (void *cls, const struct GNUNET_MessageHeader *message)
392 {
393   struct PeerContext *p = cls;
394
395   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
396               "Received (my) `%s' from transport service\n", "HELLO");
397   GNUNET_assert (message != NULL);
398   p->hello = GNUNET_malloc (ntohs (message->size));
399   memcpy (p->hello, message, ntohs (message->size));
400   if ((p == &p1) && (p2.th != NULL))
401     GNUNET_TRANSPORT_offer_hello (p2.th, message, NULL, NULL);
402   if ((p == &p2) && (p1.th != NULL))
403     GNUNET_TRANSPORT_offer_hello (p1.th, message, NULL, NULL);
404
405   if ((p == &p1) && (p2.hello != NULL) && (p1.th != NULL))
406     GNUNET_TRANSPORT_offer_hello (p1.th, p2.hello, NULL, NULL);
407   if ((p == &p2) && (p1.hello != NULL) && (p2.th != NULL))
408     GNUNET_TRANSPORT_offer_hello (p2.th, p1.hello, NULL, NULL);
409 }
410
411
412
413 static void
414 setup_peer (struct PeerContext *p, const char *cfgname)
415 {
416   p->cfg = GNUNET_CONFIGURATION_create ();
417 #if START_ARM
418   p->arm_proc =
419       GNUNET_OS_start_process (NULL, NULL, "gnunet-service-arm",
420                                "gnunet-service-arm",
421 #if VERBOSE
422                                "-L", "DEBUG",
423 #endif
424                                "-c", cfgname, NULL);
425 #endif
426   GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
427   p->th = GNUNET_TRANSPORT_connect (p->cfg, NULL, p, NULL, NULL, NULL);
428   GNUNET_assert (p->th != NULL);
429   p->ghh = GNUNET_TRANSPORT_get_hello (p->th, &process_hello, p);
430 }
431
432
433 static void
434 run (void *cls, char *const *args, const char *cfgfile,
435      const struct GNUNET_CONFIGURATION_Handle *cfg)
436 {
437   GNUNET_assert (ok == 1);
438   OKPP;
439   setup_peer (&p1, "test_core_quota_asymmetric_recv_limited_peer1.conf");
440   setup_peer (&p2, "test_core_quota_asymmetric_recv_limited_peer2.conf");
441   GNUNET_CORE_connect (p1.cfg, 1, &p1, &init_notify, &connect_notify,
442                        &disconnect_notify, NULL, &inbound_notify, GNUNET_YES,
443                        &outbound_notify, GNUNET_YES, handlers);
444 }
445
446 static void
447 stop_arm (struct PeerContext *p)
448 {
449 #if START_ARM
450   if (0 != GNUNET_OS_process_kill (p->arm_proc, SIGTERM))
451     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
452   if (GNUNET_OS_process_wait (p->arm_proc) != GNUNET_OK)
453     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
454   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ARM process %u stopped\n",
455               GNUNET_OS_process_get_pid (p->arm_proc));
456   GNUNET_OS_process_close (p->arm_proc);
457   p->arm_proc = NULL;
458 #endif
459   GNUNET_CONFIGURATION_destroy (p->cfg);
460 }
461
462 static int
463 check ()
464 {
465   char *const argv[] = { "test-core-api-preferences",
466     "-c",
467     "test_core_api_data.conf",
468 #if VERBOSE
469     "-L", "DEBUG",
470 #endif
471     NULL
472   };
473   struct GNUNET_GETOPT_CommandLineOption options[] = {
474     GNUNET_GETOPT_OPTION_END
475   };
476   ok = 1;
477   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, argv,
478                       "test-core-api-preferences", "nohelp", options, &run,
479                       &ok);
480   stop_arm (&p1);
481   stop_arm (&p2);
482   return ok;
483 }
484
485 int
486 main (int argc, char *argv[])
487 {
488   int ret;
489
490   GNUNET_log_setup ("test-core-api",
491 #if VERBOSE
492                     "DEBUG",
493 #else
494                     "WARNING",
495 #endif
496                     NULL);
497   ret = check ();
498   GNUNET_DISK_directory_remove ("/tmp/test-gnunet-core-peer-1");
499   GNUNET_DISK_directory_remove ("/tmp/test-gnunet-core-peer-2");
500
501   return ret;
502 }
503
504 /* end of test_core_api_preferences.c */