complete state reset functionality
[oweals/gnunet.git] / src / transport / gnunet-transport.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 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_program_lib.h"
33 #include "gnunet_protocols.h"
34 #include "gnunet_transport_service.h"
35
36 /**
37  * Which peer should we connect to?
38  */
39 static char *cpid;
40
41 /**
42  * Handle to transport service.
43  */
44 static struct GNUNET_TRANSPORT_Handle *handle;
45
46 /**
47  * Option -s.
48  */
49 static int benchmark_send;
50
51 /**
52  * Option -b.
53  */
54 static int benchmark_receive;
55
56 /**
57  * Option -l.
58  */
59 static int benchmark_receive;
60
61 /**
62  * Option -i.
63  */
64 static int iterate_connections;
65
66 /**
67  * Global return value (0 success).
68  */
69 static int ret;
70
71 /**
72  * Number of bytes of traffic we received so far.
73  */
74 static unsigned long long traffic_received;
75
76 /**
77  * Number of bytes of traffic we sent so far.
78  */
79 static unsigned long long traffic_sent;
80
81 /**
82  * Starting time of transmitting/receiving data.
83  */
84 static struct GNUNET_TIME_Absolute start_time;
85
86 /**
87  * Handle for current transmission request.
88  */
89 static struct GNUNET_TRANSPORT_TransmitHandle *th;
90
91 /**
92  * Identity of the peer we transmit to / connect to.
93  * (equivalent to 'cpid' string).
94  */
95 static struct GNUNET_PeerIdentity pid;
96
97 /**
98  * Task scheduled for cleanup / termination of the process.
99  */
100 static GNUNET_SCHEDULER_TaskIdentifier end;
101
102 /**
103  * Selected level of verbosity.
104  */
105 static int verbosity;
106
107
108
109 /**
110  * Shutdown, print statistics.
111  */
112 static void
113 do_disconnect (void *cls,
114                const struct GNUNET_SCHEDULER_TaskContext *tc)
115 {
116   struct GNUNET_TIME_Relative duration;
117
118   if (NULL != th)
119   {
120     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
121     th = NULL;
122   }
123   GNUNET_TRANSPORT_disconnect (handle);
124   if (benchmark_receive)
125   {
126     duration = GNUNET_TIME_absolute_get_duration (start_time);
127     fprintf (stdout,
128              _("Received %llu bytes/s (%llu bytes in %llu ms)\n"),
129              1000 * traffic_received / (1+duration.rel_value),
130              traffic_received,
131              (unsigned long long) duration.rel_value);
132   }
133   if (benchmark_send)
134   {
135     duration = GNUNET_TIME_absolute_get_duration (start_time);
136     fprintf (stdout,
137              _("Transmitted %llu bytes/s (%llu bytes in %llu ms)\n"),
138              1000 * traffic_sent / (1+duration.rel_value),
139              traffic_sent,
140              (unsigned long long) duration.rel_value);
141   }
142 }
143
144
145 /**
146  * Function called to notify a client about the socket
147  * begin ready to queue more data.  "buf" will be
148  * NULL and "size" zero if the socket was closed for
149  * writing in the meantime.
150  *
151  * @param cls closure
152  * @param size number of bytes available in buf
153  * @param buf where the callee should write the message
154  * @return number of bytes written to buf
155  */
156 static size_t
157 transmit_data (void *cls, size_t size,
158                void *buf)
159 {
160   struct GNUNET_MessageHeader *m = buf;
161
162   GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader));
163   GNUNET_assert (size < GNUNET_SERVER_MAX_MESSAGE_SIZE);
164   m->size = ntohs (size);
165   m->type = ntohs (GNUNET_MESSAGE_TYPE_DUMMY);
166   memset (&m[1], 52, size - sizeof (struct GNUNET_MessageHeader));
167   traffic_sent += size;
168   th = GNUNET_TRANSPORT_notify_transmit_ready (handle,
169                                                &pid,
170                                                32 * 1024,
171                                                0,
172                                                GNUNET_TIME_UNIT_FOREVER_REL,
173                                                &transmit_data, NULL);
174   if (verbosity > 0)
175     fprintf (stdout,
176              _("Transmitting %u bytes to %s\n"),
177              (unsigned int) size,
178              GNUNET_i2s (&pid));
179   return size;
180 }
181
182
183 /**
184  * Function called to notify transport users that another
185  * peer connected to us.
186  *
187  * @param cls closure
188  * @param peer the peer that connected
189  * @param ats performance data
190  * @param ats_count number of entries in ats (excluding 0-termination)
191  */
192 static void
193 notify_connect (void *cls,
194                 const struct GNUNET_PeerIdentity
195                 * peer,
196                 const struct
197                 GNUNET_ATS_Information
198                 * ats, uint32_t ats_count)
199 {
200   if (verbosity > 0)
201     fprintf (stdout,
202              _("Connected to %s\n"),
203              GNUNET_i2s (peer));
204   if (0 != memcmp (&pid,
205                    peer,
206                    sizeof (struct GNUNET_PeerIdentity)))
207     return;
208   ret = 0;
209   if (benchmark_send) 
210   {
211     start_time = GNUNET_TIME_absolute_get ();
212     th = GNUNET_TRANSPORT_notify_transmit_ready (handle,
213                                                  peer,
214                                                  32 * 1024,
215                                                  0,
216                                                  GNUNET_TIME_UNIT_FOREVER_REL,
217                                                  &transmit_data, NULL);
218   }
219   else
220   {
221     /* all done, terminate instantly */
222     GNUNET_SCHEDULER_cancel (end);
223     end = GNUNET_SCHEDULER_add_now (&do_disconnect,
224                                     NULL);    
225   }  
226 }
227
228
229 /**
230  * Function called to notify transport users that another
231  * peer disconnected from us.
232  *
233  * @param cls closure
234  * @param peer the peer that disconnected
235  */
236 static void
237 notify_disconnect (void *cls,
238                    const struct
239                    GNUNET_PeerIdentity * peer)
240 {
241   if (verbosity > 0)
242     fprintf (stdout,
243              _("Disconnected from %s\n"),
244              GNUNET_i2s (peer));
245   if ( (0 == memcmp (&pid,
246                      peer,
247                      sizeof (struct GNUNET_PeerIdentity))) &&
248        (NULL != th) )
249   {
250     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
251     th = NULL;
252     GNUNET_SCHEDULER_cancel (end);
253     end = GNUNET_SCHEDULER_add_now (&do_disconnect,
254                                     NULL);    
255   }
256 }
257
258
259 /**
260  * Function called by the transport for each received message.
261  *
262  * @param cls closure
263  * @param peer (claimed) identity of the other peer
264  * @param message the message
265  * @param ats performance data
266  * @param ats_count number of entries in ats 
267  */
268 static void
269 notify_receive (void *cls,
270                 const struct
271                 GNUNET_PeerIdentity * peer,
272                 const struct
273                 GNUNET_MessageHeader *
274                 message,
275                 const struct
276                 GNUNET_ATS_Information
277                 * ats, uint32_t ats_count)
278 {
279   if (! benchmark_receive)
280     return;
281   if (verbosity > 0)
282     fprintf (stdout,
283              _("Received %u bytes from %s\n"),
284              (unsigned int) ntohs (message->size),
285              GNUNET_i2s (peer));
286   if (traffic_received == 0)
287     start_time = GNUNET_TIME_absolute_get ();
288   traffic_received += ntohs (message->size);
289 }
290
291
292 /**
293  * Function to call with a human-readable format of an address
294  *
295  * @param cls closure
296  * @param address NULL on error, otherwise 0-terminated printable UTF-8 string
297  */
298 static void
299 process_address (void *cls, const struct GNUNET_PeerIdentity *peer,
300                  const char *transport, const void *addr, size_t addrlen)
301 {
302   if ((peer != NULL) || (transport != NULL) ||
303       ((addr != NULL) && (addrlen > 0)))
304     fprintf (stdout, 
305              _("Peer `%s' plugin: `%s' address `%s'\n"),
306              (peer != NULL) ? GNUNET_i2s (peer) : "<unknown>",
307              (transport != NULL) ? transport : "<unknown>", 
308              ((addr != NULL) && (addrlen > 0) && (transport != NULL)) ?
309              "how do i resolve the name without transport service?" :
310              "<unknown>");
311 }
312
313
314 /**
315  * Main function that will be run by the scheduler.
316  *
317  * @param cls closure
318  * @param args remaining command-line arguments
319  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
320  * @param cfg configuration
321  */
322 static void
323 run (void *cls, char *const *args, const char *cfgfile,
324      const struct GNUNET_CONFIGURATION_Handle *cfg)
325 {
326   if (benchmark_send && (NULL == cpid))
327   {
328     fprintf (stderr, _("Option `%s' makes no sense without option `%s'.\n"),
329              "-s", "-C");
330     return;
331   }
332   if (NULL != cpid)
333   {
334     ret = 1;
335     if (GNUNET_OK !=
336         GNUNET_CRYPTO_hash_from_string (cpid, &pid.hashPubKey))
337     {
338       fprintf (stderr,
339                _("Failed to parse peer identity `%s'\n"),
340                cpid);
341       return;
342     }
343     handle = GNUNET_TRANSPORT_connect (cfg, NULL, NULL,
344                                        &notify_receive, 
345                                        &notify_connect,
346                                        &notify_disconnect);
347     GNUNET_TRANSPORT_try_connect (handle, &pid);
348     end = GNUNET_SCHEDULER_add_delayed (benchmark_send
349                                         ? GNUNET_TIME_UNIT_FOREVER_REL
350                                         : GNUNET_TIME_UNIT_SECONDS,
351                                         &do_disconnect,
352                                         NULL);    
353   } else if (benchmark_receive)
354   {
355     handle = GNUNET_TRANSPORT_connect (cfg, NULL, NULL,
356                                        &notify_receive, 
357                                        &notify_connect,
358                                        &notify_disconnect);
359     GNUNET_TRANSPORT_try_connect (handle, &pid);
360     end = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
361                                         &do_disconnect,
362                                         NULL); 
363   }
364   if (iterate_connections)
365   {
366     GNUNET_TRANSPORT_address_iterate (cfg, GNUNET_TIME_UNIT_MINUTES,
367                                       &process_address, NULL);
368   }
369 }
370
371
372 int
373 main (int argc, char *const *argv)
374 {
375   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
376     {'b', "benchmark", NULL,
377      gettext_noop ("measure how fast we are receiving data (until CTRL-C)"),
378      0, &GNUNET_GETOPT_set_one, &benchmark_receive},
379     {'C', "connect", "PEER",
380      gettext_noop ("try to connect to the given peer"),
381      1, &GNUNET_GETOPT_set_string, &cpid},
382     {'i', "information", NULL,
383      gettext_noop ("provide information about all current connections (once)"),
384      0, &GNUNET_GETOPT_set_one, &iterate_connections},  
385     {'s', "send", NULL,
386      gettext_noop ("send data for benchmarking to the other peer (until CTRL-C)"),
387      0, &GNUNET_GETOPT_set_one, &benchmark_send},  
388     GNUNET_GETOPT_OPTION_VERBOSE(&verbosity),
389     GNUNET_GETOPT_OPTION_END
390   };
391   return (GNUNET_OK ==
392           GNUNET_PROGRAM_run (argc, argv, "gnunet-transport",
393                               gettext_noop ("Direct access to transport service."),
394                               options, &run, NULL)) ? ret : 1;
395 }
396
397
398 /* end of gnunet-transport.c */