036fff9e46280048960657da90ae6ecc04f41b52
[oweals/gnunet.git] / src / transport / transport-testing.c
1 /*
2      This file is part of GNUnet.
3      (C) 2006, 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 transport-testing.c
23  * @brief testing lib for transport service
24  *
25  * @author Matthias Wachs
26  */
27
28 #include "transport-testing.h"
29
30 #define VERBOSE GNUNET_EXTRA_LOGGING
31
32 struct PeerContext *
33 find_peer_context_by_pc ( struct TransportTestingHandle *tth,
34                           struct PeerContext *p)
35 {
36   struct PeerContext * t = tth->p_head;
37
38   while (t != NULL)
39   {
40     if (p == t)
41       break;
42     t = t->next;
43   }
44
45   return t;
46 }
47
48
49 struct PeerContext *
50 find_peer_context ( struct TransportTestingHandle *tth,
51                     const struct GNUNET_PeerIdentity *peer)
52 {
53   struct PeerContext * t = tth->p_head;
54
55   while (t != NULL)
56   {
57     if (0 == memcmp (&t->id, peer, sizeof (struct GNUNET_PeerIdentity)))
58       break;
59     t = t->next;
60   }
61
62   return t;
63 }
64
65 struct ConnectingContext *
66 find_connecting_context ( struct TransportTestingHandle *tth,
67                           struct PeerContext *p1,
68                           struct PeerContext * p2)
69 {
70   struct ConnectingContext * cc = tth->cc_head;
71
72   while (cc != NULL)
73   {
74     if ((cc->p1 == p1) && (cc->p2 == p2))
75         break;
76     if ((cc->p1 == p2) && (cc->p2 == p1))
77       break;
78     cc = cc->next;
79   }
80
81   return cc;
82 }
83
84 static void
85 notify_connect (void *cls, const struct GNUNET_PeerIdentity *peer,
86                 const struct GNUNET_TRANSPORT_ATS_Information *ats,
87                 uint32_t ats_count)
88 {
89   struct PeerContext *p = cls;
90   /* Find PeerContext */
91   struct PeerContext * p2 = find_peer_context (p->tth, peer);
92
93   if (p == NULL)
94     return;
95   if (p->nc != NULL)
96     p->nc (p->cb_cls, peer, ats, ats_count);
97
98 #if VERBOSE
99   char * p2_s;
100   if (p2 != NULL)
101     GNUNET_asprintf(&p2_s, "%u (`%s')", p2->no, GNUNET_i2s (&p2->id));
102   else
103     GNUNET_asprintf(&p2_s, "`%s'", GNUNET_i2s (peer));
104   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-testing",
105       "Peers %s connected to peer %u (`%s')\n",
106       p2_s,
107       p->no, GNUNET_i2s (&p->id));
108   GNUNET_free (p2_s);
109 #endif
110
111
112   /* Find ConnectingContext */
113   struct ConnectingContext * cc = find_connecting_context(p->tth, p, p2);
114   if (cc == NULL)
115     return;
116
117   if (p == cc->p1)
118     cc->p1_c = GNUNET_YES;
119
120   if (p == cc->p2)
121       cc->p2_c = GNUNET_YES;
122
123   if ((cc->p1_c == GNUNET_YES) && (cc->p2_c == GNUNET_YES))
124   {
125     cc->cb (cc->p1, cc->p2, cc->cb_cls);
126     GNUNET_TRANSPORT_TESTING_connect_peers_cancel(p->tth, cc);
127   }
128 }
129
130 static void
131 notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
132 {
133   struct PeerContext *p = cls;
134   /* Find PeerContext */
135   struct PeerContext * p2 = find_peer_context (p->tth, peer);
136
137   char * p2_s;
138   if (p2 != NULL)
139     GNUNET_asprintf(&p2_s, "%u (`%s')", p2->no, GNUNET_i2s (&p2->id));
140   else
141     GNUNET_asprintf(&p2_s, "`%s'", GNUNET_i2s (peer));
142   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-testing",
143       "Peers %s disconnected from peer %u (`%s')\n",
144       p2_s,
145       p->no, GNUNET_i2s (&p->id));
146   GNUNET_free (p2_s);
147
148   if (p == NULL)
149     return;
150   if (p->nd != NULL)
151     p->nd (p->cb_cls, peer);
152 }
153
154 static void
155 notify_receive (void *cls, const struct GNUNET_PeerIdentity *peer,
156                 const struct GNUNET_MessageHeader *message,
157                 const struct GNUNET_TRANSPORT_ATS_Information *ats,
158                 uint32_t ats_count)
159 {
160   struct PeerContext *p = cls;
161
162   if (p == NULL)
163     return;
164   if (p->rec != NULL)
165     p->rec (p->cb_cls, peer, message, ats, ats_count);
166 }
167
168 static void
169 get_hello (void *cb_cls, const struct GNUNET_MessageHeader *message)
170 {
171   struct PeerContext *p = cb_cls;
172
173   GNUNET_assert (message != NULL);
174   GNUNET_assert (GNUNET_OK ==
175                  GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message *)
176                                       message, &p->id));
177
178   if (p->hello != NULL)
179     GNUNET_free (p->hello);
180
181   size_t size = GNUNET_HELLO_size((const struct GNUNET_HELLO_Message *) message);
182   p->hello = GNUNET_malloc (size);
183   memcpy (p->hello,
184         (const struct GNUNET_HELLO_Message *) message,
185         size);
186
187   if (p->start_cb != NULL)
188   {
189 #if VERBOSE
190     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-testing",
191         "Peer %u (`%s') successfully started\n",
192         p->no, GNUNET_i2s (&p->id), size);
193 #endif
194     p->start_cb(p, p->cb_cls);
195     p->start_cb = NULL;
196   }
197 }
198
199
200 static void
201 try_connect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
202 {
203   struct ConnectingContext *cc = cls;
204   struct PeerContext *p1 = cc->p1;
205   struct PeerContext *p2 = cc->p2;
206
207   cc->tct = GNUNET_SCHEDULER_NO_TASK;
208   if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
209     return;
210
211   char * p2_s = strdup(GNUNET_i2s (&p2->id));
212   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-testing",
213       "Asking peers %u (`%s') to connect peer %u (`%s')\n",
214       p1->no, GNUNET_i2s (&p1->id), p2->no, p2_s);
215   GNUNET_free (p2_s);
216
217   GNUNET_TRANSPORT_offer_hello (cc->th_p1,
218       (const struct GNUNET_MessageHeader *) cc->p2->hello, NULL, NULL);
219   GNUNET_TRANSPORT_try_connect (cc->th_p1, &p2->id);
220
221   cc->tct =
222       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &try_connect, cc);
223 }
224
225
226 /**
227  * Start a peer with the given configuration
228  * @param rec receive callback
229  * @param nc connect callback
230  * @param nd disconnect callback
231  * @param cb_cls closure for callback
232  * @return the peer context
233  */
234 struct PeerContext *
235 GNUNET_TRANSPORT_TESTING_start_peer (struct TransportTestingHandle * tth,
236                                      const char *cfgname,
237                                      int peer_id,
238                                      GNUNET_TRANSPORT_ReceiveCallback rec,
239                                      GNUNET_TRANSPORT_NotifyConnect nc,
240                                      GNUNET_TRANSPORT_NotifyDisconnect nd,
241                                      GNUNET_TRANSPORT_TESTING_start_cb start_cb,
242                                      void *cb_cls)
243 {
244   if (GNUNET_DISK_file_test (cfgname) == GNUNET_NO)
245   {
246     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "transport-testing",
247         "File not found: `%s' \n", cfgname);
248     return NULL;
249   }
250
251   struct PeerContext *p = GNUNET_malloc (sizeof (struct PeerContext));
252
253   p->cfg = GNUNET_CONFIGURATION_create ();
254
255   GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
256   if (GNUNET_CONFIGURATION_have_value (p->cfg, "PATHS", "SERVICEHOME"))
257     GNUNET_CONFIGURATION_get_value_string (p->cfg, "PATHS", "SERVICEHOME",
258                                            &p->servicehome);
259   if (NULL != p->servicehome)
260     GNUNET_DISK_directory_remove (p->servicehome);
261   p->arm_proc =
262       GNUNET_OS_start_process (NULL, NULL, "gnunet-service-arm",
263                                "gnunet-service-arm", "-c", cfgname,
264 #if VERBOSE_PEERS
265                                "-L", "DEBUG",
266 #else
267                                "-L", "ERROR",
268 #endif
269                                NULL);
270
271   p->no = peer_id;
272   p->tth = tth;
273   p->nc = nc;
274   p->nd = nd;
275   p->rec = rec;
276   p->start_cb = start_cb;
277   if (cb_cls != NULL)
278     p->cb_cls = cb_cls;
279   else
280     p->cb_cls = p;
281
282   p->th =
283       GNUNET_TRANSPORT_connect (p->cfg, NULL, p, &notify_receive,
284                                 &notify_connect, &notify_disconnect);
285   GNUNET_assert (p->th != NULL);
286
287   p->ghh = GNUNET_TRANSPORT_get_hello (p->th, &get_hello, p);
288   GNUNET_assert (p->ghh != NULL);
289
290   GNUNET_CONTAINER_DLL_insert(tth->p_head, tth->p_tail, p);
291
292   return p;
293 }
294
295 /**
296  * shutdown the given peer
297  * @param p the peer
298  */
299 void
300 GNUNET_TRANSPORT_TESTING_stop_peer (struct TransportTestingHandle * tth,
301                                     struct PeerContext *p)
302 {
303   GNUNET_assert (p != NULL);
304
305   if (p->ghh != NULL)
306     GNUNET_TRANSPORT_get_hello_cancel (p->ghh);
307   p->ghh = NULL;
308
309   if (p->th != NULL)
310     GNUNET_TRANSPORT_disconnect (p->th);
311
312   if (NULL != p->arm_proc)
313   {
314     if (0 != GNUNET_OS_process_kill (p->arm_proc, SIGTERM))
315       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
316     GNUNET_OS_process_wait (p->arm_proc);
317     GNUNET_OS_process_close (p->arm_proc);
318     p->arm_proc = NULL;
319   }
320
321   if (p->servicehome != NULL)
322   {
323     GNUNET_DISK_directory_remove (p->servicehome);
324     GNUNET_free (p->servicehome);
325   }
326
327   if (p->hello != NULL)
328     GNUNET_free (p->hello);
329
330   if (p->cfg != NULL)
331     GNUNET_CONFIGURATION_destroy (p->cfg);
332
333   GNUNET_CONTAINER_DLL_remove (tth->p_head, tth->p_tail, p);
334
335   GNUNET_free (p);
336   p = NULL;
337 }
338
339 /**
340  * Initiate peer p1 to connect to peer p2
341  * Get peer p2's HELLO and offer it to p1
342  * p1 then tries to connect to p2
343  * @param p1 peer 1
344  * @param p2 peer 2
345  * @param cb the callback to call
346  * @param cb_cls callback cls
347  * @return connect context
348  */
349 GNUNET_TRANSPORT_TESTING_ConnectRequest
350 GNUNET_TRANSPORT_TESTING_connect_peers (struct TransportTestingHandle * tth,
351                                         struct PeerContext *p1,
352                                         struct PeerContext *p2,
353                                         GNUNET_TRANSPORT_TESTING_connect_cb cb,
354                                         void *cb_cls)
355
356 {
357   struct ConnectingContext *cc =
358       GNUNET_malloc (sizeof (struct ConnectingContext));
359
360   GNUNET_assert (p1 != NULL);
361   GNUNET_assert (p2 != NULL);
362
363   cc->p1 = p1;
364   cc->p2 = p2;
365
366   cc->cb = cb;
367   cc->cb_cls = cb_cls;
368
369   cc->th_p1 = p1->th;
370      /* GNUNET_TRANSPORT_connect (cc->p1->cfg, NULL, cc, NULL,
371                                 &notify_connect_internal, NULL);*/
372
373   cc->th_p2 = p2->th;
374       /* GNUNET_TRANSPORT_connect (cc->p2->cfg, NULL, cc, NULL,
375                                 &notify_connect_internal, NULL);*/
376
377   GNUNET_assert (cc->th_p1 != NULL);
378   GNUNET_assert (cc->th_p2 != NULL);
379
380   GNUNET_CONTAINER_DLL_insert (tth->cc_head, tth->cc_tail, cc);
381
382   cc->tct = GNUNET_SCHEDULER_add_now (&try_connect, cc);
383   return cc;
384
385
386 }
387
388 /**
389  * Cancel the request to connect two peers
390  * Tou MUST cancel the request if you stop the peers before the peers connected succesfully
391  * @param cc a connect request handle
392  */
393 void GNUNET_TRANSPORT_TESTING_connect_peers_cancel
394     (struct TransportTestingHandle * tth,
395         GNUNET_TRANSPORT_TESTING_ConnectRequest ccr)
396 {
397   struct ConnectingContext *cc = ccr;
398
399   if (cc->tct != GNUNET_SCHEDULER_NO_TASK)
400     GNUNET_SCHEDULER_cancel (cc->tct);
401
402   cc->tct = GNUNET_SCHEDULER_NO_TASK;
403
404   GNUNET_CONTAINER_DLL_remove (tth->cc_head, tth->cc_tail, cc);
405
406   GNUNET_free (cc);
407 }
408
409 void
410 GNUNET_TRANSPORT_TESTING_done (struct TransportTestingHandle * tth)
411 {
412   struct ConnectingContext *cc = tth->cc_head;
413   struct ConnectingContext *ct = NULL;
414   struct PeerContext *p = tth->p_head;
415   struct PeerContext *t = NULL;
416   while (cc != NULL)
417   {
418     ct = cc->next;
419     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "transport-testing",
420         "Developer forgot to cancel connect request!\n");
421     GNUNET_TRANSPORT_TESTING_connect_peers_cancel(tth, cc);
422     cc = ct;
423   }
424
425   while (p != NULL)
426   {
427     t = p->next;
428     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "transport-testing",
429         "Developer forgot to stop peer!\n");
430     GNUNET_TRANSPORT_TESTING_stop_peer (tth, p);
431     p = t;
432   }
433
434   GNUNET_free (tth);
435 }
436
437 struct TransportTestingHandle *
438 GNUNET_TRANSPORT_TESTING_init ()
439 {
440   struct TransportTestingHandle * tth = GNUNET_malloc (sizeof (struct TransportTestingHandle));
441
442   return tth;
443 }
444
445
446 /*
447  * Some utility functions
448  */
449
450 /**
451  * Removes all directory separators from absolute filename
452  * @param file the absolute file name, e.g. as found in argv[0]
453  * @return extracted file name, has to be freed by caller
454  */
455 char *
456 extract_filename (const char *file)
457 {
458   char *pch = strdup (file);
459   char *backup = pch;
460   char *filename = NULL;
461   char *res;
462
463   if (NULL != strstr (pch, "/"))
464   {
465     pch = strtok (pch, "/");
466     while (pch != NULL)
467     {
468       pch = strtok (NULL, "/");
469       if (pch != NULL)
470       {
471         filename = pch;
472       }
473     }
474   }
475   else
476     filename = pch;
477
478   res = strdup (filename);
479   GNUNET_free (backup);
480   return res;
481 }
482
483 /**
484  * Extracts the test filename from an absolute file name and removes the extension
485  * @param file absolute file name
486  * @param dest where to store result
487  */
488 void
489 GNUNET_TRANSPORT_TESTING_get_test_name (const char *file, char **dest)
490 {
491   char *filename = extract_filename (file);
492   char *backup = filename;
493   char *dotexe;
494
495   if (filename == NULL)
496     goto fail;
497
498   /* remove "lt-" */
499   filename = strstr (filename, "tes");
500   if (filename == NULL)
501     goto fail;
502
503   /* remove ".exe" */
504   if (NULL != (dotexe = strstr (filename, ".exe")))
505     dotexe[0] = '\0';
506
507   goto suc;
508
509 fail:
510   (*dest) = NULL;
511   return;
512
513 suc:
514   /* create filename */
515   GNUNET_asprintf (dest, "%s", filename);
516   GNUNET_free (backup);
517 }
518
519
520 /**
521  * Extracts the filename from an absolute file name and removes the extension
522  * @param file absolute file name
523  * @param dest where to store result
524  */
525 void
526 GNUNET_TRANSPORT_TESTING_get_test_source_name (const char *file, char **dest)
527 {
528   char *src = extract_filename (file);
529   char *split;
530
531   split = strstr (src, ".");
532   if (split != NULL)
533   {
534     split[0] = '\0';
535   }
536   GNUNET_asprintf (dest, "%s", src);
537   GNUNET_free (src);
538 }
539
540
541 /**
542  * Extracts the plugin anme from an absolute file name and the test name
543  * @param file absolute file name
544  * @param test test name
545  * @param dest where to store result
546  */
547 void
548 GNUNET_TRANSPORT_TESTING_get_test_plugin_name (const char *file,
549                                                const char *test, char **dest)
550 {
551   char *e = extract_filename (file);
552   char *t = extract_filename (test);
553
554   char *filename = NULL;
555   char *dotexe;
556
557   if (e == NULL)
558     goto fail;
559
560   /* remove "lt-" */
561   filename = strstr (e, "tes");
562   if (filename == NULL)
563     goto fail;
564
565   /* remove ".exe" */
566   if (NULL != (dotexe = strstr (filename, ".exe")))
567     dotexe[0] = '\0';
568
569   /* find last _ */
570   filename = strstr (filename, t);
571   if (filename == NULL)
572     goto fail;
573
574   /* copy plugin */
575   filename += strlen (t);
576   filename++;
577   GNUNET_asprintf (dest, "%s", filename);
578   goto suc;
579
580 fail:
581   (*dest) = NULL;
582 suc:
583   GNUNET_free (t);
584   GNUNET_free (e);
585
586 }
587
588 /**
589  * This function takes the filename (e.g. argv[0), removes a "lt-"-prefix and
590  * if existing ".exe"-prefix and adds the peer-number
591  * @param file filename of the test, e.g. argv[0]
592  * @param cfgname where to write the result
593  * @param count peer number
594  */
595 void
596 GNUNET_TRANSPORT_TESTING_get_config_name (const char *file, char **dest,
597                                           int count)
598 {
599   char *filename = extract_filename (file);
600   char *backup = filename;
601   char *dotexe;
602
603   if (filename == NULL)
604     goto fail;
605
606   /* remove "lt-" */
607   filename = strstr (filename, "tes");
608   if (filename == NULL)
609     goto fail;
610
611   /* remove ".exe" */
612   if (NULL != (dotexe = strstr (filename, ".exe")))
613     dotexe[0] = '\0';
614
615   goto suc;
616
617 fail:
618   (*dest) = NULL;
619   return;
620
621 suc:
622   /* create cfg filename */
623   GNUNET_asprintf (dest, "%s_peer%u.conf", filename, count);
624   GNUNET_free (backup);
625 }
626
627
628
629 /* end of transport_testing.h */