56df0fbf622840003e91879fa14725b76ec04642
[oweals/gnunet.git] / src / transport / gnunet-transport.c
1 /*
2  This file is part of GNUnet.
3  (C) 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 /**
22  * @file src/transport/gnunet-transport.c
23  * @brief Tool to help configure, measure and control the transport subsystem.
24  * @author Christian Grothoff
25  * @author Nathan Evans
26  *
27  * This utility can be used to test if a transport mechanism for
28  * GNUnet is properly configured.
29  */
30
31 #include "platform.h"
32 #include "gnunet_util_lib.h"
33 #include "gnunet_resolver_service.h"
34 #include "gnunet_protocols.h"
35 #include "gnunet_transport_service.h"
36 #include "gnunet_nat_lib.h"
37
38 /**
39  * How long do we wait for the NAT test to report success?
40  * Should match NAT_SERVER_TIMEOUT in 'nat_test.c'.
41  */
42 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
43 #define RESOLUTION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
44 #define OP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
45
46 /**
47  * Benchmarking block size in KB
48  */
49 #define BLOCKSIZE 4
50
51 /**
52  * Which peer should we connect to?
53  */
54 static char *cpid;
55
56 /**
57  * Handle to transport service.
58  */
59 static struct GNUNET_TRANSPORT_Handle *handle;
60
61 /**
62  * Configuration handle
63  */
64 static struct GNUNET_CONFIGURATION_Handle *cfg;
65
66 /**
67  * Try_connect handle
68  */
69 struct GNUNET_TRANSPORT_TryConnectHandle * tc_handle;
70
71 /**
72  * Option -s.
73  */
74 static int benchmark_send;
75
76 /**
77  * Option -b.
78  */
79 static int benchmark_receive;
80
81 /**
82  * Option -l.
83  */
84 static int benchmark_receive;
85
86 /**
87  * Option -i.
88  */
89 static int iterate_connections;
90
91 /**
92  * Option -d.
93  */
94 static int iterate_validation;
95
96 /**
97  * Option -a.
98  */
99 static int iterate_all;
100
101 /**
102  * Option -t.
103  */
104 static int test_configuration;
105
106 /**
107  * Option -c.
108  */
109 static int monitor_connects;
110
111 /**
112  * Option -m.
113  */
114 static int monitor_connections;
115
116 /**
117  * Option -f.
118  */
119 static int monitor_validation;
120
121 /**
122  * Option -C.
123  */
124 static int try_connect;
125
126 /**
127  * Option -n.
128  */
129 static int numeric;
130
131 /**
132  * Global return value (0 success).
133  */
134 static int ret;
135
136 /**
137  * Current number of connections in monitor mode
138  */
139 static int monitor_connect_counter;
140
141 /**
142  * Number of bytes of traffic we received so far.
143  */
144 static unsigned long long traffic_received;
145
146 /**
147  * Number of bytes of traffic we sent so far.
148  */
149 static unsigned long long traffic_sent;
150
151 /**
152  * Starting time of transmitting/receiving data.
153  */
154 static struct GNUNET_TIME_Absolute start_time;
155
156 /**
157  * Handle for current transmission request.
158  */
159 static struct GNUNET_TRANSPORT_TransmitHandle *th;
160
161 /**
162  * Map storing information about monitored peers
163  */
164 static struct GNUNET_CONTAINER_MultiPeerMap *monitored_peers;
165
166 /**
167  *
168  */
169 static struct GNUNET_TRANSPORT_PeerMonitoringContext *pic;
170
171 static struct GNUNET_TRANSPORT_ValidationMonitoringContext *vic;
172
173 /**
174  * Identity of the peer we transmit to / connect to.
175  * (equivalent to 'cpid' string).
176  */
177 static struct GNUNET_PeerIdentity pid;
178
179 /**
180  * Task scheduled for cleanup / termination of the process.
181  */
182 static GNUNET_SCHEDULER_TaskIdentifier end;
183
184 /**
185  * Task for operation timeout
186  */
187 static GNUNET_SCHEDULER_TaskIdentifier op_timeout;
188
189 /**
190  * Selected level of verbosity.
191  */
192 static int verbosity;
193
194 /**
195  * Resolver process handle.
196  */
197 struct GNUNET_OS_Process *resolver;
198
199 /**
200  * Number of tasks running that still need the resolver.
201  */
202 static unsigned int resolver_users;
203
204 /**
205  * Number of address resolutions pending
206  */
207 static unsigned int address_resolutions;
208
209 /**
210  * Address resolutions pending in progress
211  */
212 static unsigned int address_resolution_in_progress;
213
214
215 /**
216  * Context for a plugin test.
217  */
218 struct TestContext
219 {
220
221   /**
222    * Handle to the active NAT test.
223    */
224   struct GNUNET_NAT_Test *tst;
225
226   /**
227    * Task identifier for the timeout.
228    */
229   GNUNET_SCHEDULER_TaskIdentifier tsk;
230
231   /**
232    * Name of plugin under test.
233    */
234   const char *name;
235
236 };
237
238 struct MonitoredPeer
239 {
240   enum GNUNET_TRANSPORT_PeerState state;
241   struct GNUNET_TIME_Absolute state_timeout;
242   struct GNUNET_HELLO_Address *address;
243 };
244
245
246  int destroy_it (void *cls,
247     const struct GNUNET_PeerIdentity *key,
248     void *value)
249 {
250    struct MonitoredPeer *m = value;
251    GNUNET_CONTAINER_multipeermap_remove (monitored_peers, key, value);
252    GNUNET_free_non_null (m->address);
253    GNUNET_free (value);
254    return GNUNET_OK;
255 }
256
257 /**
258  * Task run in monitor mode when the user presses CTRL-C to abort.
259  * Stops monitoring activity.
260  *
261  * @param cls the 'struct GNUNET_TRANSPORT_PeerIterateContext *'
262  * @param tc scheduler context
263  */
264 static void
265 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
266 {
267   struct GNUNET_TIME_Relative duration;
268   end = GNUNET_SCHEDULER_NO_TASK;
269   if (GNUNET_SCHEDULER_NO_TASK != op_timeout)
270   {
271     GNUNET_SCHEDULER_cancel (op_timeout);
272     op_timeout = GNUNET_SCHEDULER_NO_TASK;
273   }
274   if (NULL != tc_handle)
275   {
276     GNUNET_TRANSPORT_try_connect_cancel (tc_handle);
277     tc_handle = NULL;
278   }
279   if (NULL != pic)
280   {
281     GNUNET_TRANSPORT_monitor_peers_cancel (pic);
282     pic = NULL;
283   }
284   if (NULL != vic)
285   {
286     GNUNET_TRANSPORT_monitor_validation_entries_cancel (vic);
287     vic = NULL;
288   }
289   if (NULL != th)
290   {
291     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
292     th = NULL;
293   }
294   if (NULL != handle)
295   {
296     GNUNET_TRANSPORT_disconnect (handle);
297     handle = NULL;
298   }
299   if (benchmark_send)
300   {
301     duration = GNUNET_TIME_absolute_get_duration (start_time);
302     FPRINTF (stdout, _("Transmitted %llu bytes/s (%llu bytes in %s)\n"),
303         1000LL * 1000LL * traffic_sent / (1 + duration.rel_value_us),
304         traffic_sent,
305         GNUNET_STRINGS_relative_time_to_string (duration, GNUNET_YES));
306   }
307   if (benchmark_receive)
308   {
309     duration = GNUNET_TIME_absolute_get_duration (start_time);
310     FPRINTF (stdout, _("Received %llu bytes/s (%llu bytes in %s)\n"),
311         1000LL * 1000LL * traffic_received / (1 + duration.rel_value_us),
312         traffic_received,
313         GNUNET_STRINGS_relative_time_to_string (duration, GNUNET_YES));
314   }
315
316   if (NULL != monitored_peers)
317   {
318     GNUNET_CONTAINER_multipeermap_iterate (monitored_peers, &destroy_it, NULL);
319     GNUNET_CONTAINER_multipeermap_destroy (monitored_peers);
320     monitored_peers = NULL;
321   }
322 }
323
324 static struct PeerResolutionContext *rc_head;
325 static struct PeerResolutionContext *rc_tail;
326
327 struct PeerResolutionContext
328 {
329   struct PeerResolutionContext *next;
330   struct PeerResolutionContext *prev;
331   struct GNUNET_PeerIdentity id;
332   struct GNUNET_HELLO_Address *addrcp;
333   struct GNUNET_TRANSPORT_AddressToStringContext *asc;
334   enum GNUNET_TRANSPORT_PeerState state;
335   struct GNUNET_TIME_Absolute state_timeout;
336   char *transport;
337   int printed;
338 };
339
340 static struct ValidationResolutionContext *vc_head;
341 static struct ValidationResolutionContext *vc_tail;
342
343 struct ValidationResolutionContext
344 {
345   struct ValidationResolutionContext *next;
346   struct ValidationResolutionContext *prev;
347
348   struct GNUNET_PeerIdentity id;
349   struct GNUNET_HELLO_Address *addrcp;
350   struct GNUNET_TIME_Absolute last_validation;
351   struct GNUNET_TIME_Absolute valid_until;
352   struct GNUNET_TIME_Absolute next_validation;
353   enum GNUNET_TRANSPORT_ValidationState state;
354
355   struct GNUNET_TRANSPORT_AddressToStringContext *asc;
356
357   char *transport;
358   int printed;
359 };
360
361 static void
362 operation_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
363 {
364   struct PeerResolutionContext *cur;
365   struct PeerResolutionContext *next;
366   op_timeout = GNUNET_SCHEDULER_NO_TASK;
367   if ((try_connect) || (benchmark_send) || (benchmark_receive))
368   {
369     FPRINTF (stdout, _("Failed to connect to `%s'\n"), GNUNET_i2s_full (&pid));
370     if (GNUNET_SCHEDULER_NO_TASK != end)
371       GNUNET_SCHEDULER_cancel (end);
372     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL );
373     ret = 1;
374     return;
375   }
376   if (iterate_connections)
377   {
378     next = rc_head;
379     while (NULL != (cur = next))
380     {
381       next = cur->next;
382       FPRINTF (stdout, _("Failed to resolve address for peer `%s'\n"),
383           GNUNET_i2s (&cur->addrcp->peer));
384
385       GNUNET_CONTAINER_DLL_remove(rc_head, rc_tail, cur);
386       GNUNET_TRANSPORT_address_to_string_cancel (cur->asc);
387       GNUNET_free(cur->transport);
388       GNUNET_free(cur->addrcp);
389       GNUNET_free(cur);
390
391     }
392     FPRINTF (stdout, "%s", _("Failed to list connections, timeout occured\n") );
393     if (GNUNET_SCHEDULER_NO_TASK != end)
394       GNUNET_SCHEDULER_cancel (end);
395     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL );
396     ret = 1;
397     return;
398   }
399
400 }
401
402 /**
403  * Display the result of the test.
404  *
405  * @param tc test context
406  * @param result #GNUNET_YES on success
407  */
408 static void
409 display_test_result (struct TestContext *tc, int result)
410 {
411   if (GNUNET_YES != result)
412   {
413     FPRINTF (stderr, "Configuration for plugin `%s' did not work!\n", tc->name);
414   }
415   else
416   {
417     FPRINTF (stderr, "Configuration for plugin `%s' is working!\n", tc->name);
418   }
419   if (GNUNET_SCHEDULER_NO_TASK != tc->tsk)
420   {
421     GNUNET_SCHEDULER_cancel (tc->tsk);
422     tc->tsk = GNUNET_SCHEDULER_NO_TASK;
423   }
424   if (NULL != tc->tst)
425   {
426     GNUNET_NAT_test_stop (tc->tst);
427     tc->tst = NULL;
428   }
429   GNUNET_free(tc);
430   resolver_users--;
431   if ((0 == resolver_users) && (NULL != resolver))
432   {
433     GNUNET_break(0 == GNUNET_OS_process_kill (resolver, GNUNET_TERM_SIG));
434     GNUNET_OS_process_destroy (resolver);
435     resolver = NULL;
436   }
437 }
438
439 /**
440  * Function called by NAT on success.
441  * Clean up and update GUI (with success).
442  *
443  * @param cls test context
444  * @param success currently always #GNUNET_OK
445  * @param emsg error message, NULL on success
446  */
447 static void
448 result_callback (void *cls, int success, const char *emsg)
449 {
450   struct TestContext *tc = cls;
451
452   display_test_result (tc, success);
453 }
454
455 /**
456  * Function called if NAT failed to confirm success.
457  * Clean up and update GUI (with failure).
458  *
459  * @param cls test context
460  * @param tc scheduler callback
461  */
462 static void
463 fail_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
464 {
465   struct TestContext *tstc = cls;
466
467   tstc->tsk = GNUNET_SCHEDULER_NO_TASK;
468   display_test_result (tstc, GNUNET_NO);
469 }
470
471
472 static void
473 resolve_validation_address (const struct GNUNET_PeerIdentity *id,
474     const struct GNUNET_HELLO_Address *address, int numeric,
475     struct GNUNET_TIME_Absolute last_validation,
476     struct GNUNET_TIME_Absolute valid_until,
477     struct GNUNET_TIME_Absolute next_validation,
478     enum GNUNET_TRANSPORT_ValidationState state);
479
480 static void
481 process_validation_string (void *cls, const char *address)
482 {
483   struct ValidationResolutionContext *vc = cls;
484   char *s_valid;
485   char *s_last;
486   char *s_next;
487
488   if (address != NULL )
489   {
490     if (GNUNET_TIME_UNIT_ZERO_ABS.abs_value_us == vc->valid_until.abs_value_us)
491       s_valid = GNUNET_strdup("never");
492     else
493       s_valid = GNUNET_strdup(GNUNET_STRINGS_absolute_time_to_string (vc->valid_until));
494
495     if (GNUNET_TIME_UNIT_ZERO_ABS.abs_value_us == vc->last_validation.abs_value_us)
496       s_last = GNUNET_strdup("never");
497     else
498       s_last = GNUNET_strdup(GNUNET_STRINGS_absolute_time_to_string (vc->last_validation));
499
500     if (GNUNET_TIME_UNIT_ZERO_ABS.abs_value_us == vc->next_validation.abs_value_us)
501       s_next = GNUNET_strdup("never");
502     else
503       s_next = GNUNET_strdup(GNUNET_STRINGS_absolute_time_to_string (vc->next_validation));
504
505     FPRINTF (stdout,
506         _("Peer `%s' %s %s\n\t%s%s\n\t%s%s\n\t%s%s\n"),
507         GNUNET_i2s (&vc->id), address,
508         (monitor_validation) ? GNUNET_TRANSPORT_vs2s (vc->state) : "",
509         "Valid until    : ", s_valid,
510         "Last validation: ",s_last,
511         "Next validation: ", s_next);
512     GNUNET_free (s_valid);
513     GNUNET_free (s_last);
514     GNUNET_free (s_next);
515     vc->printed = GNUNET_YES;
516   }
517   else
518   {
519     /* done */
520     GNUNET_assert(address_resolutions > 0);
521     address_resolutions--;
522     if (GNUNET_NO == vc->printed)
523     {
524       if (numeric == GNUNET_NO)
525       {
526         /* Failed to resolve address, try numeric lookup */
527         resolve_validation_address (&vc->id, vc->addrcp, GNUNET_NO,
528            vc->last_validation, vc->valid_until, vc->next_validation,
529            vc->state);
530       }
531       else
532       {
533         FPRINTF (stdout, _("Peer `%s' %s `%s' \n"),
534             GNUNET_i2s (&vc->id), "<unable to resolve address>",
535             GNUNET_TRANSPORT_vs2s (vc->state));
536       }
537     }
538     GNUNET_free (vc->transport);
539     GNUNET_free (vc->addrcp);
540     GNUNET_CONTAINER_DLL_remove(vc_head, vc_tail, vc);
541     GNUNET_free(vc);
542     if ((0 == address_resolutions) && (iterate_validation))
543     {
544       if (GNUNET_SCHEDULER_NO_TASK != end)
545       {
546         GNUNET_SCHEDULER_cancel (end);
547         end = GNUNET_SCHEDULER_NO_TASK;
548       }
549       if (GNUNET_SCHEDULER_NO_TASK != op_timeout)
550       {
551         GNUNET_SCHEDULER_cancel (op_timeout);
552         op_timeout = GNUNET_SCHEDULER_NO_TASK;
553       }
554       ret = 0;
555       end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL );
556     }
557   }
558 }
559
560
561
562 static void
563 resolve_validation_address (const struct GNUNET_PeerIdentity *id,
564     const struct GNUNET_HELLO_Address *address, int numeric,
565     struct GNUNET_TIME_Absolute last_validation,
566     struct GNUNET_TIME_Absolute valid_until,
567     struct GNUNET_TIME_Absolute next_validation,
568     enum GNUNET_TRANSPORT_ValidationState state)
569 {
570   struct ValidationResolutionContext *vc;
571
572   vc = GNUNET_new (struct ValidationResolutionContext);
573   GNUNET_assert(NULL != vc);
574   GNUNET_CONTAINER_DLL_insert(vc_head, vc_tail, vc);
575   address_resolutions++;
576
577   vc->id = (*id);
578   vc->transport = GNUNET_strdup(address->transport_name);
579   vc->addrcp = GNUNET_HELLO_address_copy (address);
580   vc->printed = GNUNET_NO;
581   vc->state = state;
582   vc->last_validation = last_validation;
583   vc->valid_until = valid_until;
584   vc->next_validation = next_validation;
585
586   /* Resolve address to string */
587   vc->asc = GNUNET_TRANSPORT_address_to_string (cfg, address, numeric,
588       RESOLUTION_TIMEOUT, &process_validation_string, vc);
589 }
590
591
592 void process_validation_cb (void *cls,
593     const struct GNUNET_PeerIdentity *peer,
594     const struct GNUNET_HELLO_Address *address,
595     struct GNUNET_TIME_Absolute last_validation,
596     struct GNUNET_TIME_Absolute valid_until,
597     struct GNUNET_TIME_Absolute next_validation,
598     enum GNUNET_TRANSPORT_ValidationState state)
599 {
600   if ((NULL == peer) && (NULL == address))
601   {
602     /* done */
603     vic = NULL;
604     if (GNUNET_SCHEDULER_NO_TASK != end)
605       GNUNET_SCHEDULER_cancel (end);
606     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL );
607     return;
608   }
609   if ((NULL == peer) || (NULL == address))
610   {
611     /* invalid response */
612     vic = NULL;
613     if (GNUNET_SCHEDULER_NO_TASK != end)
614       GNUNET_SCHEDULER_cancel (end);
615     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL );
616     return;
617   }
618   resolve_validation_address (peer, address,
619      numeric, last_validation,
620      valid_until, next_validation, state);
621 }
622
623 /**
624  * Test our plugin's configuration (NAT traversal, etc.).
625  *
626  * @param cfg configuration to test
627  */
628 static void
629 do_test_configuration (const struct GNUNET_CONFIGURATION_Handle *cfg)
630 {
631   char *plugins;
632   char *tok;
633   unsigned long long bnd_port;
634   unsigned long long adv_port;
635   struct TestContext *tc;
636   char *binary;
637
638   if (GNUNET_OK
639       != GNUNET_CONFIGURATION_get_value_string (cfg, "transport", "plugins",
640           &plugins))
641   {
642     FPRINTF (stderr, "%s", _
643     ("No transport plugins configured, peer will never communicate\n") );
644     ret = 4;
645     return;
646   }
647   for (tok = strtok (plugins, " "); tok != NULL ; tok = strtok (NULL, " "))
648   {
649     char section[12 + strlen (tok)];
650
651     GNUNET_snprintf (section, sizeof(section), "transport-%s", tok);
652     if (GNUNET_OK
653         != GNUNET_CONFIGURATION_get_value_number (cfg, section, "PORT",
654             &bnd_port))
655     {
656       FPRINTF (stderr,
657           _("No port configured for plugin `%s', cannot test it\n"), tok);
658       continue;
659     }
660     if (GNUNET_OK
661         != GNUNET_CONFIGURATION_get_value_number (cfg, section,
662             "ADVERTISED_PORT", &adv_port))
663       adv_port = bnd_port;
664     if (NULL == resolver)
665     {
666       binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-resolver");
667       resolver = GNUNET_OS_start_process (GNUNET_YES,
668           GNUNET_OS_INHERIT_STD_OUT_AND_ERR, NULL, NULL, binary,
669           "gnunet-service-resolver", NULL );
670       GNUNET_free(binary);
671     }
672     resolver_users++;
673     GNUNET_RESOLVER_connect (cfg);
674     tc = GNUNET_new (struct TestContext);
675     tc->name = GNUNET_strdup (tok);
676     tc->tst = GNUNET_NAT_test_start (cfg,
677         (0 == strcasecmp (tok, "udp")) ? GNUNET_NO : GNUNET_YES,
678         (uint16_t) bnd_port, (uint16_t) adv_port, &result_callback, tc);
679     if (NULL == tc->tst)
680     {
681       display_test_result (tc, GNUNET_SYSERR);
682       continue;
683     }
684     tc->tsk = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &fail_timeout, tc);
685   }
686   GNUNET_free(plugins);
687 }
688
689 /**
690  * Function called to notify a client about the socket
691  * begin ready to queue more data.  @a buf will be
692  * NULL and @a size zero if the socket was closed for
693  * writing in the meantime.
694  *
695  * @param cls closure
696  * @param size number of bytes available in @a buf
697  * @param buf where the callee should write the message
698  * @return number of bytes written to @a buf
699  */
700 static size_t
701 transmit_data (void *cls, size_t size, void *buf)
702 {
703   struct GNUNET_MessageHeader *m = buf;
704
705   if ((NULL == buf) || (0 == size))
706   {
707     th = NULL;
708     return 0;
709   }
710
711   GNUNET_assert(size >= sizeof(struct GNUNET_MessageHeader));
712   GNUNET_assert(size < GNUNET_SERVER_MAX_MESSAGE_SIZE);
713   m->size = ntohs (size);
714   m->type = ntohs (GNUNET_MESSAGE_TYPE_DUMMY);
715   memset (&m[1], 52, size - sizeof(struct GNUNET_MessageHeader));
716   traffic_sent += size;
717   th = GNUNET_TRANSPORT_notify_transmit_ready (handle, &pid, BLOCKSIZE * 1024,
718       0, GNUNET_TIME_UNIT_FOREVER_REL, &transmit_data, NULL );
719   if (verbosity > 0)
720     FPRINTF (stdout, _("Transmitting %u bytes to %s\n"), (unsigned int) size,
721         GNUNET_i2s (&pid));
722   return size;
723 }
724
725 /**
726  * Function called to notify transport users that another
727  * peer connected to us.
728  *
729  * @param cls closure
730  * @param peer the peer that connected
731  */
732 static void
733 notify_connect (void *cls, const struct GNUNET_PeerIdentity *peer)
734 {
735   if (0 != memcmp (&pid, peer, sizeof(struct GNUNET_PeerIdentity)))
736     return;
737   ret = 0;
738   if (try_connect)
739   {
740     /* all done, terminate instantly */
741     FPRINTF (stdout, _("Successfully connected to `%s'\n"),
742         GNUNET_i2s_full (peer));
743     ret = 0;
744
745     if (GNUNET_SCHEDULER_NO_TASK != op_timeout)
746     {
747       GNUNET_SCHEDULER_cancel (op_timeout);
748       op_timeout = GNUNET_SCHEDULER_NO_TASK;
749     }
750
751     if (GNUNET_SCHEDULER_NO_TASK != end)
752       GNUNET_SCHEDULER_cancel (end);
753     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL );
754     return;
755   }
756   if (benchmark_send)
757   {
758     if (GNUNET_SCHEDULER_NO_TASK != op_timeout)
759     {
760       GNUNET_SCHEDULER_cancel (op_timeout);
761       op_timeout = GNUNET_SCHEDULER_NO_TASK;
762     }
763     if (verbosity > 0)
764       FPRINTF (stdout,
765           _("Successfully connected to `%s', starting to send benchmark data in %u Kb blocks\n"),
766           GNUNET_i2s (&pid), BLOCKSIZE);
767     start_time = GNUNET_TIME_absolute_get ();
768     if (NULL == th)
769       th = GNUNET_TRANSPORT_notify_transmit_ready (handle, peer,
770           BLOCKSIZE * 1024, 0, GNUNET_TIME_UNIT_FOREVER_REL, &transmit_data,
771           NULL );
772     else
773       GNUNET_break(0);
774     return;
775   }
776 }
777
778 /**
779  * Function called to notify transport users that another
780  * peer disconnected from us.
781  *
782  * @param cls closure
783  * @param peer the peer that disconnected
784  */
785 static void
786 notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
787 {
788   if (0 != memcmp (&pid, peer, sizeof(struct GNUNET_PeerIdentity)))
789     return;
790
791   if (NULL != th)
792   {
793     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
794     th = NULL;
795   }
796   if (benchmark_send)
797   {
798     FPRINTF (stdout, _("Disconnected from peer `%s' while benchmarking\n"),
799         GNUNET_i2s (&pid));
800     if (GNUNET_SCHEDULER_NO_TASK != end)
801       GNUNET_SCHEDULER_cancel (end);
802     return;
803   }
804 }
805
806 /**
807  * Function called to notify transport users that another
808  * peer connected to us.
809  *
810  * @param cls closure
811  * @param peer the peer that connected
812  */
813 static void
814 monitor_notify_connect (void *cls, const struct GNUNET_PeerIdentity *peer)
815 {
816   monitor_connect_counter++;
817   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
818   const char *now_str = GNUNET_STRINGS_absolute_time_to_string (now);
819
820   FPRINTF (stdout, _("%24s: %-17s %4s   (%u connections in total)\n"), now_str,
821       _("Connected to"), GNUNET_i2s (peer), monitor_connect_counter);
822 }
823
824 /**
825  * Function called to notify transport users that another
826  * peer disconnected from us.
827  *
828  * @param cls closure
829  * @param peer the peer that disconnected
830  */
831 static void
832 monitor_notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
833 {
834   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
835   const char *now_str = GNUNET_STRINGS_absolute_time_to_string (now);
836
837   GNUNET_assert(monitor_connect_counter > 0);
838   monitor_connect_counter--;
839
840   FPRINTF (stdout, _("%24s: %-17s %4s   (%u connections in total)\n"), now_str,
841       _("Disconnected from"), GNUNET_i2s (peer), monitor_connect_counter);
842 }
843
844 /**
845  * Function called by the transport for each received message.
846  *
847  * @param cls closure
848  * @param peer (claimed) identity of the other peer
849  * @param message the message
850  */
851 static void
852 notify_receive (void *cls, const struct GNUNET_PeerIdentity *peer,
853     const struct GNUNET_MessageHeader *message)
854 {
855   if (benchmark_receive)
856   {
857     if (GNUNET_MESSAGE_TYPE_DUMMY != ntohs (message->type))
858       return;
859     if (verbosity > 0)
860       FPRINTF (stdout, _("Received %u bytes from %s\n"),
861           (unsigned int) ntohs (message->size), GNUNET_i2s (peer));
862
863     if (traffic_received == 0)
864       start_time = GNUNET_TIME_absolute_get ();
865     traffic_received += ntohs (message->size);
866     return;
867   }
868 }
869
870 static void
871 resolve_peer_address (const struct GNUNET_PeerIdentity *id,
872     const struct GNUNET_HELLO_Address *address, int numeric,
873     enum GNUNET_TRANSPORT_PeerState state,
874     struct GNUNET_TIME_Absolute state_timeout);
875
876 static void
877 print_info (const struct GNUNET_PeerIdentity *id, const char *transport,
878     const char *addr, enum GNUNET_TRANSPORT_PeerState state,
879     struct GNUNET_TIME_Absolute state_timeout)
880 {
881   if ((GNUNET_YES == iterate_all) || (GNUNET_YES == monitor_connections) )
882   {
883     FPRINTF (stdout, _("Peer `%s': %s %s in state `%s' until %s\n"),
884         GNUNET_i2s (id),
885         (NULL == transport) ? "<none>" : transport,
886         (NULL == transport) ? "<none>" : addr,
887         GNUNET_TRANSPORT_ps2s (state),
888         GNUNET_STRINGS_absolute_time_to_string (state_timeout));
889   }
890   else
891   {
892     /* Only connected peers, skip state */
893     FPRINTF (stdout, _("Peer `%s': %s %s\n"), GNUNET_i2s (id), transport, addr);
894   }
895
896 }
897
898 static void
899 process_peer_string (void *cls, const char *address)
900 {
901   struct PeerResolutionContext *rc = cls;
902
903   if (address != NULL )
904   {
905     print_info (&rc->id, rc->transport, address, rc->state, rc->state_timeout);
906     rc->printed = GNUNET_YES;
907   }
908   else
909   {
910     /* done */
911     GNUNET_assert(address_resolutions > 0);
912     address_resolutions--;
913     if (GNUNET_NO == rc->printed)
914     {
915       if (numeric == GNUNET_NO)
916       {
917         /* Failed to resolve address, try numeric lookup */
918         resolve_peer_address (&rc->id, rc->addrcp, GNUNET_YES,
919             rc->state, rc->state_timeout);
920       }
921       else
922       {
923         print_info (&rc->id, rc->transport, "<unable to resolve address>",
924             rc->state, rc->state_timeout);
925       }
926     }
927     GNUNET_free (rc->transport);
928     GNUNET_free (rc->addrcp);
929     GNUNET_CONTAINER_DLL_remove(rc_head, rc_tail, rc);
930     GNUNET_free(rc);
931     if ((0 == address_resolutions) && (iterate_connections))
932     {
933       if (GNUNET_SCHEDULER_NO_TASK != end)
934       {
935         GNUNET_SCHEDULER_cancel (end);
936         end = GNUNET_SCHEDULER_NO_TASK;
937       }
938       if (GNUNET_SCHEDULER_NO_TASK != op_timeout)
939       {
940         GNUNET_SCHEDULER_cancel (op_timeout);
941         op_timeout = GNUNET_SCHEDULER_NO_TASK;
942       }
943       ret = 0;
944       end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL );
945     }
946   }
947 }
948
949 static void
950 resolve_peer_address (const struct GNUNET_PeerIdentity *id,
951     const struct GNUNET_HELLO_Address *address, int numeric,
952     enum GNUNET_TRANSPORT_PeerState state,
953     struct GNUNET_TIME_Absolute state_timeout)
954 {
955   struct PeerResolutionContext *rc;
956
957   rc = GNUNET_new (struct PeerResolutionContext);
958   GNUNET_assert(NULL != rc);
959   GNUNET_CONTAINER_DLL_insert(rc_head, rc_tail, rc);
960   address_resolutions++;
961
962   rc->id = (*id);
963   rc->transport = GNUNET_strdup(address->transport_name);
964   rc->addrcp = GNUNET_HELLO_address_copy (address);
965   rc->printed = GNUNET_NO;
966   rc->state = state;
967   rc->state_timeout = state_timeout;
968   /* Resolve address to string */
969   rc->asc = GNUNET_TRANSPORT_address_to_string (cfg, address, numeric,
970       RESOLUTION_TIMEOUT, &process_peer_string, rc);
971 }
972
973 /**
974  * Function called with information about a peers during a one shot iteration
975  *
976  * @param cls closure
977  * @param peer identity of the peer, NULL for final callback when operation done
978  * @param address binary address used to communicate with this peer,
979  *  NULL on disconnect or when done
980  * @param state current state this peer is in
981  * @param state_timeout time out for the current state
982  *
983  */
984 static void
985 process_peer_iteration_cb (void *cls, const struct GNUNET_PeerIdentity *peer,
986     const struct GNUNET_HELLO_Address *address,
987     enum GNUNET_TRANSPORT_PeerState state,
988     struct GNUNET_TIME_Absolute state_timeout)
989 {
990   if (peer == NULL )
991   {
992     /* done */
993     address_resolution_in_progress = GNUNET_NO;
994     pic = NULL;
995     if (GNUNET_SCHEDULER_NO_TASK != end)
996       GNUNET_SCHEDULER_cancel (end);
997     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL );
998     return;
999   }
1000
1001   if ((GNUNET_NO == iterate_all) && (GNUNET_NO == GNUNET_TRANSPORT_is_connected(state)) )
1002       return; /* Display only connected peers */
1003
1004   if (GNUNET_SCHEDULER_NO_TASK != op_timeout)
1005     GNUNET_SCHEDULER_cancel (op_timeout);
1006   op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT, &operation_timeout,
1007       NULL );
1008
1009   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Received address for peer `%s': %s\n",
1010       GNUNET_i2s (peer), address->transport_name);
1011
1012   if (NULL != address)
1013     resolve_peer_address (peer, address, numeric, state, state_timeout);
1014   else
1015     print_info (peer, NULL, NULL, state, state_timeout);
1016 }
1017
1018
1019 /**
1020  * Function called with information about a peers
1021  *
1022  * @param cls closure
1023  * @param peer identity of the peer, NULL for final callback when operation done
1024  * @param address binary address used to communicate with this peer,
1025  *  NULL on disconnect or when done
1026  * @param state current state this peer is in
1027  * @param state_timeout time out for the current state
1028  *
1029  */
1030 static void
1031 process_peer_monitoring_cb (void *cls, const struct GNUNET_PeerIdentity *peer,
1032     const struct GNUNET_HELLO_Address *address,
1033     enum GNUNET_TRANSPORT_PeerState state,
1034     struct GNUNET_TIME_Absolute state_timeout)
1035 {
1036   struct MonitoredPeer *m;
1037
1038   if (peer == NULL )
1039   {
1040     /* done */
1041     address_resolution_in_progress = GNUNET_NO;
1042     pic = NULL;
1043     if (GNUNET_SCHEDULER_NO_TASK != end)
1044       GNUNET_SCHEDULER_cancel (end);
1045     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL );
1046     return;
1047   }
1048
1049   if (GNUNET_SCHEDULER_NO_TASK != op_timeout)
1050     GNUNET_SCHEDULER_cancel (op_timeout);
1051   op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT, &operation_timeout,
1052       NULL );
1053
1054   if (NULL == (m = GNUNET_CONTAINER_multipeermap_get (monitored_peers, peer)))
1055   {
1056     m = GNUNET_new (struct MonitoredPeer);
1057     GNUNET_CONTAINER_multipeermap_put (monitored_peers, peer,
1058         m, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
1059   }
1060   else
1061   {
1062     if ( (m->state == state) &&
1063       (m->state_timeout.abs_value_us == state_timeout.abs_value_us) &&
1064       ((NULL == address) && (NULL == m->address)) )
1065     {
1066       return; /* No real change */
1067     }
1068     if ( ((NULL != address) && (NULL != m->address)) &&
1069         (0 == GNUNET_HELLO_address_cmp(m->address, address)) )
1070       return; /* No real change */
1071   }
1072
1073
1074   if (NULL != m->address)
1075   {
1076     GNUNET_free (m->address);
1077     m->address = NULL;
1078   }
1079   if (NULL != address)
1080     m->address = GNUNET_HELLO_address_copy (address);
1081   m->state = state;
1082   m->state_timeout = state_timeout;
1083
1084   if (NULL != address)
1085     resolve_peer_address (peer, m->address, numeric, m->state, m->state_timeout);
1086   else
1087     print_info (peer, NULL, NULL, m->state, m->state_timeout);
1088 }
1089
1090 static void
1091 try_connect_cb (void *cls, const int result)
1092 {
1093   static int retries = 0;
1094   if (GNUNET_OK == result)
1095   {
1096     tc_handle = NULL;
1097     return;
1098   }
1099   retries++;
1100   if (retries < 10)
1101     tc_handle = GNUNET_TRANSPORT_try_connect (handle, &pid, try_connect_cb,
1102         NULL );
1103   else
1104   {
1105     FPRINTF (stderr, "%s",
1106         _("Failed to send connect request to transport service\n") );
1107     if (GNUNET_SCHEDULER_NO_TASK != end)
1108       GNUNET_SCHEDULER_cancel (end);
1109     ret = 1;
1110     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL );
1111     return;
1112   }
1113 }
1114
1115 /**
1116  * Function called with the result of the check if the 'transport'
1117  * service is running.
1118  *
1119  * @param cls closure with our configuration
1120  * @param result #GNUNET_YES if transport is running
1121  */
1122 static void
1123 testservice_task (void *cls, int result)
1124 {
1125   int counter = 0;
1126   ret = 1;
1127
1128   if (GNUNET_YES != result)
1129   {
1130     FPRINTF (stderr, _("Service `%s' is not running\n"), "transport");
1131     return;
1132   }
1133
1134   if ((NULL != cpid)
1135       && (GNUNET_OK
1136           != GNUNET_CRYPTO_eddsa_public_key_from_string (cpid, strlen (cpid),
1137               &pid.public_key)))
1138   {
1139     FPRINTF (stderr, _("Failed to parse peer identity `%s'\n"), cpid);
1140     return;
1141   }
1142
1143   counter = benchmark_send + benchmark_receive + iterate_connections
1144       + monitor_connections + monitor_connects + try_connect
1145       + iterate_validation + monitor_validation;
1146
1147   if (1 < counter)
1148   {
1149     FPRINTF (stderr,
1150         _("Multiple operations given. Please choose only one operation: %s, %s, %s, %s, %s, %s\n"),
1151         "connect", "benchmark send", "benchmark receive", "information",
1152         "monitor", "events");
1153     return;
1154   }
1155   if (0 == counter)
1156   {
1157     FPRINTF (stderr,
1158         _("No operation given. Please choose one operation: %s, %s, %s, %s, %s, %s\n"),
1159         "connect", "benchmark send", "benchmark receive", "information",
1160         "monitor", "events");
1161     return;
1162   }
1163
1164   if (try_connect) /* -C: Connect to peer */
1165   {
1166     if (NULL == cpid)
1167     {
1168       FPRINTF (stderr, _("Option `%s' makes no sense without option `%s'.\n"),
1169           "-C", "-p");
1170       ret = 1;
1171       return;
1172     }
1173     handle = GNUNET_TRANSPORT_connect (cfg, NULL, NULL, &notify_receive,
1174         &notify_connect, &notify_disconnect);
1175     if (NULL == handle)
1176     {
1177       FPRINTF (stderr, "%s", _("Failed to connect to transport service\n") );
1178       ret = 1;
1179       return;
1180     }
1181     tc_handle = GNUNET_TRANSPORT_try_connect (handle, &pid, try_connect_cb,
1182         NULL );
1183     if (NULL == tc_handle)
1184     {
1185       FPRINTF (stderr, "%s",
1186           _("Failed to send request to transport service\n") );
1187       ret = 1;
1188       return;
1189     }
1190     op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT, &operation_timeout,
1191         NULL );
1192
1193   }
1194   else if (benchmark_send) /* -s: Benchmark sending */
1195   {
1196     if (NULL == cpid)
1197     {
1198       FPRINTF (stderr, _("Option `%s' makes no sense without option `%s'.\n"),
1199           "-s", "-p");
1200       ret = 1;
1201       return;
1202     }
1203     handle = GNUNET_TRANSPORT_connect (cfg, NULL, NULL, &notify_receive,
1204         &notify_connect, &notify_disconnect);
1205     if (NULL == handle)
1206     {
1207       FPRINTF (stderr, "%s", _("Failed to connect to transport service\n") );
1208       ret = 1;
1209       return;
1210     }
1211     tc_handle = GNUNET_TRANSPORT_try_connect (handle, &pid, try_connect_cb,
1212         NULL );
1213     if (NULL == tc_handle)
1214     {
1215       FPRINTF (stderr, "%s",
1216           _("Failed to send request to transport service\n") );
1217       ret = 1;
1218       return;
1219     }
1220     start_time = GNUNET_TIME_absolute_get ();
1221     op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT, &operation_timeout,
1222         NULL );
1223   }
1224   else if (benchmark_receive) /* -b: Benchmark receiving */
1225   {
1226     handle = GNUNET_TRANSPORT_connect (cfg, NULL, NULL, &notify_receive, NULL,
1227         NULL );
1228     if (NULL == handle)
1229     {
1230       FPRINTF (stderr, "%s", _("Failed to connect to transport service\n") );
1231       ret = 1;
1232       return;
1233     }
1234     if (verbosity > 0)
1235       FPRINTF (stdout, "%s", _("Starting to receive benchmark data\n") );
1236     start_time = GNUNET_TIME_absolute_get ();
1237
1238   }
1239   else if (iterate_connections) /* -i: List information about peers once */
1240   {
1241     address_resolution_in_progress = GNUNET_YES;
1242     pic = GNUNET_TRANSPORT_monitor_peers (cfg, (NULL == cpid) ? NULL : &pid,
1243         GNUNET_YES, TIMEOUT, &process_peer_iteration_cb, (void *) cfg);
1244     op_timeout = GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT, &operation_timeout,
1245         NULL );
1246   }
1247   else if (monitor_connections) /* -m: List information about peers continuously */
1248   {
1249     monitored_peers = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO);
1250     address_resolution_in_progress = GNUNET_YES;
1251     pic = GNUNET_TRANSPORT_monitor_peers (cfg, (NULL == cpid) ? NULL : &pid,
1252         GNUNET_NO, TIMEOUT, &process_peer_monitoring_cb, (void *) cfg);
1253   }
1254   else if (iterate_validation) /* -d: Print information about validations */
1255   {
1256     vic = GNUNET_TRANSPORT_monitor_validation_entries (cfg,
1257         (NULL == cpid) ? NULL : &pid,
1258         GNUNET_YES, TIMEOUT, &process_validation_cb, (void *) cfg);
1259   }
1260   else if (monitor_validation) /* -f: Print information about validations continuously */
1261   {
1262     vic = GNUNET_TRANSPORT_monitor_validation_entries (cfg,
1263         (NULL == cpid) ? NULL : &pid,
1264         GNUNET_NO, TIMEOUT, &process_validation_cb, (void *) cfg);
1265   }
1266   else if (monitor_connects) /* -e : Monitor (dis)connect events continuously */
1267   {
1268     monitor_connect_counter = 0;
1269     handle = GNUNET_TRANSPORT_connect (cfg, NULL, NULL, NULL,
1270         &monitor_notify_connect, &monitor_notify_disconnect);
1271     if (NULL == handle)
1272     {
1273       FPRINTF (stderr, "%s", _("Failed to connect to transport service\n") );
1274       ret = 1;
1275       return;
1276     }
1277     ret = 0;
1278   }
1279   else
1280   {
1281     GNUNET_break(0);
1282     return;
1283   }
1284
1285   end = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1286       &shutdown_task, NULL );
1287
1288 }
1289
1290 /**
1291  * Main function that will be run by the scheduler.
1292  *
1293  * @param cls closure
1294  * @param args remaining command-line arguments
1295  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1296  * @param mycfg configuration
1297  */
1298 static void
1299 run (void *cls, char * const *args, const char *cfgfile,
1300     const struct GNUNET_CONFIGURATION_Handle *mycfg)
1301 {
1302   cfg = (struct GNUNET_CONFIGURATION_Handle *) mycfg;
1303   if (test_configuration)
1304   {
1305     do_test_configuration (cfg);
1306     return;
1307   }
1308   GNUNET_CLIENT_service_test ("transport", cfg, GNUNET_TIME_UNIT_SECONDS,
1309       &testservice_task, (void *) cfg);
1310 }
1311
1312 int
1313 main (int argc, char * const *argv)
1314 {
1315   int res;
1316   static const struct GNUNET_GETOPT_CommandLineOption options[] =
1317       {
1318           { 'a', "all", NULL,
1319               gettext_noop ("print information for all peers (instead of only connected peers )"),
1320               0, &GNUNET_GETOPT_set_one, &iterate_all },
1321           { 'b', "benchmark", NULL,
1322               gettext_noop ("measure how fast we are receiving data from all peers (until CTRL-C)"),
1323               0, &GNUNET_GETOPT_set_one, &benchmark_receive }, { 'C', "connect",
1324               NULL, gettext_noop ("connect to a peer"), 0,
1325               &GNUNET_GETOPT_set_one, &try_connect },
1326           { 'd', "validation", NULL,
1327               gettext_noop ("print information for all pending validations "),
1328               0, &GNUNET_GETOPT_set_one, &iterate_validation },
1329           { 'f', "monitorvalidation", NULL,
1330               gettext_noop ("print information for all pending validations continously"),
1331               0, &GNUNET_GETOPT_set_one, &monitor_validation },
1332           { 'i', "information", NULL,
1333               gettext_noop ("provide information about all current connections (once)"),
1334               0, &GNUNET_GETOPT_set_one, &iterate_connections },
1335           { 'm', "monitor", NULL,
1336               gettext_noop ("provide information about all current connections (continuously)"),
1337               0, &GNUNET_GETOPT_set_one, &monitor_connections },
1338           { 'e', "events", NULL,
1339               gettext_noop ("provide information about all connects and disconnect events (continuously)"),
1340               0, &GNUNET_GETOPT_set_one, &monitor_connects }, { 'n', "numeric",
1341               NULL, gettext_noop ("do not resolve hostnames"), 0,
1342               &GNUNET_GETOPT_set_one, &numeric }, { 'p', "peer", "PEER",
1343               gettext_noop ("peer identity"), 1, &GNUNET_GETOPT_set_string,
1344               &cpid }, { 's', "send", NULL, gettext_noop
1345           ("send data for benchmarking to the other peer (until CTRL-C)"), 0,
1346               &GNUNET_GETOPT_set_one, &benchmark_send },
1347           { 't', "test", NULL,
1348               gettext_noop ("test transport configuration (involves external server)"),
1349               0, &GNUNET_GETOPT_set_one, &test_configuration },
1350               GNUNET_GETOPT_OPTION_VERBOSE (&verbosity),
1351               GNUNET_GETOPT_OPTION_END };
1352
1353   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1354     return 2;
1355
1356   res = GNUNET_PROGRAM_run (argc, argv, "gnunet-transport", gettext_noop
1357   ("Direct access to transport service."), options, &run, NULL );
1358   GNUNET_free((void * ) argv);
1359   if (GNUNET_OK == res)
1360     return ret;
1361   return 1;
1362 }
1363
1364 /* end of gnunet-transport.c */