- hide temp config file, show config template
[oweals/gnunet.git] / src / mesh / gnunet-mesh.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012 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 mesh/gnunet-mesh.c
23  * @brief Print information about mesh tunnels and peers.
24  * @author Bartlomiej Polot
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_mesh_service.h"
29
30
31 /**
32  * Option -m.
33  */
34 static int monitor_connections;
35
36 /**
37  * Option -P.
38  */
39 static int request_peers;
40
41 /**
42  * Option -T.
43  */
44 static int request_tunnels;
45
46 /**
47  * Option --tunnel
48  */
49 static char *tunnel_id;
50
51 /**
52  * Option --connection
53  */
54 static char *conn_id;
55
56 /**
57  * Option --channel
58  */
59 static char *channel_id;
60
61 /**
62  * Port to listen on (-p).
63  */
64 static uint32_t listen_port;
65
66 /**
67  * Request echo service
68  */
69 int echo;
70
71 /**
72  * Time of last echo request.
73  */
74 struct GNUNET_TIME_Absolute echo_time;
75
76 /**
77  * Task for next echo request.
78  */
79 GNUNET_SCHEDULER_TaskIdentifier echo_task;
80
81 /**
82  * Peer to connect to.
83  */
84 static char *target_id;
85
86 /**
87  * Port to connect to
88  */
89 static uint32_t target_port;
90
91 /**
92  * Data pending in netcat mode.
93  */
94 size_t data_size;
95
96
97 /**
98  * Mesh handle.
99  */
100 static struct GNUNET_MESH_Handle *mh;
101
102 /**
103  * Channel handle.
104  */
105 static struct GNUNET_MESH_Channel *ch;
106
107 /**
108  * Shutdown task handle.
109  */
110 GNUNET_SCHEDULER_TaskIdentifier sd;
111
112
113
114 static void
115 listen_stdio (void);
116
117
118
119 /**
120  * Task run in monitor mode when the user presses CTRL-C to abort.
121  * Stops monitoring activity.
122  *
123  * @param cls Closure (unused).
124  * @param tc scheduler context
125  */
126 static void
127 shutdown_task (void *cls,
128                const struct GNUNET_SCHEDULER_TaskContext *tc)
129 {
130   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutdown\n");
131   if (NULL != ch)
132   {
133     GNUNET_MESH_channel_destroy (ch);
134     ch = NULL;
135   }
136   if (NULL != mh)
137   {
138     GNUNET_MESH_disconnect (mh);
139         mh = NULL;
140   }
141 }
142
143
144 /**
145  * Function called to notify a client about the connection
146  * begin ready to queue more data.  "buf" will be
147  * NULL and "size" zero if the connection was closed for
148  * writing in the meantime.
149  *
150  * FIXME
151  *
152  * @param cls closure
153  * @param size number of bytes available in buf
154  * @param buf where the callee should write the message
155  * @return number of bytes written to buf
156  */
157 size_t
158 data_ready (void *cls, size_t size, void *buf)
159 {
160   struct GNUNET_MessageHeader *msg;
161   size_t total_size;
162
163   if (NULL == buf || 0 == size)
164   {
165     GNUNET_SCHEDULER_shutdown();
166     return 0;
167   }
168
169   total_size = data_size + sizeof (struct GNUNET_MessageHeader);
170   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "sending %u bytes\n", data_size);
171   GNUNET_assert (size >= total_size);
172
173   msg = buf;
174   msg->size = htons (total_size);
175   msg->type = htons (GNUNET_MESSAGE_TYPE_MESH_CLI);
176   memcpy (&msg[1], cls, data_size);
177   if (GNUNET_NO == echo)
178   {
179     listen_stdio ();
180   }
181   else
182   {
183     echo_time = GNUNET_TIME_absolute_get ();
184   }
185
186   return total_size;
187 }
188
189
190 /**
191  * Task run in monitor mode when the user presses CTRL-C to abort.
192  * Stops monitoring activity.
193  *
194  * @param cls Closure (unused).
195  * @param tc scheduler context
196  */
197 static void
198 read_stdio (void *cls,
199             const struct GNUNET_SCHEDULER_TaskContext *tc)
200 {
201   static char buf[60000];
202
203   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
204   {
205     return;
206   }
207
208   data_size = read (0, buf, 60000);
209   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "stdio read %u bytes\n", data_size);
210   if (data_size < 1)
211   {
212     GNUNET_SCHEDULER_shutdown();
213     return;
214   }
215   GNUNET_MESH_notify_transmit_ready (ch, GNUNET_NO,
216                                      GNUNET_TIME_UNIT_FOREVER_REL,
217                                      data_size
218                                      + sizeof (struct GNUNET_MessageHeader),
219                                      &data_ready, buf);
220 }
221
222
223 /**
224  * Start listening to stdin
225  */
226 static void
227 listen_stdio (void)
228 {
229   struct GNUNET_NETWORK_FDSet *rs;
230
231   rs = GNUNET_NETWORK_fdset_create ();
232   GNUNET_NETWORK_fdset_set_native (rs, 0);
233   GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
234                                GNUNET_TIME_UNIT_FOREVER_REL,
235                                rs, NULL,
236                                &read_stdio, NULL);
237   GNUNET_NETWORK_fdset_destroy (rs);
238 }
239
240
241 /**
242  * Function called whenever a channel is destroyed.  Should clean up
243  * any associated state.
244  *
245  * It must NOT call #GNUNET_MESH_channel_destroy on the channel.
246  *
247  * @param cls closure (set from #GNUNET_MESH_connect)
248  * @param channel connection to the other end (henceforth invalid)
249  * @param channel_ctx place where local state associated
250  *                   with the channel is stored
251  */
252 static void
253 channel_ended (void *cls,
254                const struct GNUNET_MESH_Channel *channel,
255                void *channel_ctx)
256 {
257   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Channel ended!\n");
258   GNUNET_break (channel == ch);
259   ch = NULL;
260   GNUNET_SCHEDULER_shutdown ();
261 }
262
263
264 /**
265  * Method called whenever another peer has added us to a channel
266  * the other peer initiated.
267  * Only called (once) upon reception of data with a message type which was
268  * subscribed to in #GNUNET_MESH_connect.
269  *
270  * A call to #GNUNET_MESH_channel_destroy causes te channel to be ignored. In
271  * this case the handler MUST return NULL.
272  *
273  * @param cls closure
274  * @param channel new handle to the channel
275  * @param initiator peer that started the channel
276  * @param port Port this channel is for.
277  * @param options MeshOption flag field, with all active option bits set to 1.
278  *
279  * @return initial channel context for the channel
280  *         (can be NULL -- that's not an error)
281  */
282 static void *
283 channel_incoming (void *cls,
284                   struct GNUNET_MESH_Channel * channel,
285                   const struct GNUNET_PeerIdentity * initiator,
286                   uint32_t port, enum GNUNET_MESH_ChannelOption options)
287 {
288   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
289               "Incoming channel %p on port %u\n",
290               channel, port);
291   if (NULL != ch)
292   {
293     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "A channel already exists\n");
294     return NULL;
295   }
296   if (0 == listen_port)
297   {
298     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Not listening to channels\n");
299     return NULL;
300   }
301   ch = channel;
302   if (GNUNET_NO == echo)
303   {
304     listen_stdio ();
305     return NULL;
306   }
307   data_size = 0;
308   return NULL;
309 }
310
311 /**
312  * @brief Send an echo request to the remote peer.
313  *
314  * @param cls Closure (NULL).
315  * @param tc Task context.
316  */
317 static void
318 send_echo (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
319 {
320   GNUNET_MESH_notify_transmit_ready (ch, GNUNET_NO,
321                                      GNUNET_TIME_UNIT_FOREVER_REL,
322                                      sizeof (struct GNUNET_MessageHeader),
323                                      &data_ready, NULL);
324 }
325
326
327
328 /**
329  * Call MESH's monitor API, get info of one connection.
330  *
331  * @param cls Closure (unused).
332  * @param tc TaskContext
333  */
334 static void
335 create_channel (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
336 {
337   struct GNUNET_PeerIdentity pid;
338   enum GNUNET_MESH_ChannelOption opt;
339
340   GNUNET_assert (NULL == ch);
341
342   if (GNUNET_OK !=
343       GNUNET_CRYPTO_eddsa_public_key_from_string (target_id,
344                                                   strlen (target_id),
345                                                   &pid.public_key))
346   {
347     FPRINTF (stderr,
348              _("Invalid target `%s'\n"),
349              target_id);
350     GNUNET_SCHEDULER_shutdown ();
351     return;
352   }
353   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting to `%s'\n", target_id);
354   opt = GNUNET_MESH_OPTION_DEFAULT | GNUNET_MESH_OPTION_RELIABLE;
355   ch = GNUNET_MESH_channel_create (mh, NULL, &pid, target_port, opt);
356   if (GNUNET_NO == echo)
357     listen_stdio ();
358   else
359     GNUNET_SCHEDULER_add_now (send_echo, NULL);
360 }
361
362
363 /**
364  * Function called whenever a message is received.
365  *
366  * Each time the function must call #GNUNET_MESH_receive_done on the channel
367  * in order to receive the next message. This doesn't need to be immediate:
368  * can be delayed if some processing is done on the message.
369  *
370  * @param cls Closure (set from #GNUNET_MESH_connect).
371  * @param channel Connection to the other end.
372  * @param channel_ctx Place to store local state associated with the channel.
373  * @param message The actual message.
374  * @return #GNUNET_OK to keep the channel open,
375  *         #GNUNET_SYSERR to close it (signal serious error).
376  */
377 static int
378 data_callback (void *cls,
379                struct GNUNET_MESH_Channel *channel,
380                void **channel_ctx,
381                const struct GNUNET_MessageHeader *message)
382 {
383   uint16_t len;
384   ssize_t done;
385   uint16_t off;
386   const char *buf;
387   GNUNET_break (ch == channel);
388
389   if (GNUNET_YES == echo)
390   {
391     if (0 != listen_port)
392     {
393       /* Just listening to echo incoming messages*/
394       GNUNET_MESH_notify_transmit_ready (channel, GNUNET_NO,
395                                         GNUNET_TIME_UNIT_FOREVER_REL,
396                                         sizeof (struct GNUNET_MessageHeader),
397                                         &data_ready, NULL);
398       return GNUNET_OK;
399     }
400     else
401     {
402       struct GNUNET_TIME_Relative latency;
403
404       latency = GNUNET_TIME_absolute_get_duration (echo_time);
405       echo_time = GNUNET_TIME_UNIT_FOREVER_ABS;
406       FPRINTF (stdout, "time: %s\n",
407                GNUNET_STRINGS_relative_time_to_string (latency, GNUNET_NO));
408       echo_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
409                                                 &send_echo, NULL);
410     }
411   }
412
413   len = ntohs (message->size) - sizeof (*message);
414   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got %u bytes\n", len);
415   buf = (const char *) &message[1];
416   off = 0;
417   while (off < len)
418   {
419     done = write (1, &buf[off], len - off);
420     if (done <= 0)
421     {
422       if (-1 == done)
423         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
424                              "write");
425       return GNUNET_SYSERR;
426     }
427     off += done;
428   }
429   return GNUNET_OK;
430 }
431
432
433 /**
434  * Method called to retrieve information about all peers in MESH, called
435  * once per peer.
436  *
437  * After last peer has been reported, an additional call with NULL is done.
438  *
439  * @param cls Closure.
440  * @param peer Peer, or NULL on "EOF".
441  * @param tunnel Do we have a tunnel towards this peer?
442  * @param n_paths Number of known paths towards this peer.
443  * @param best_path How long is the best path?
444  *                  (0 = unknown, 1 = ourselves, 2 = neighbor)
445  */
446 static void
447 peers_callback (void *cls, const struct GNUNET_PeerIdentity *peer,
448                 int tunnel, unsigned int n_paths, unsigned int best_path)
449 {
450   if (NULL == peer)
451   {
452     if (GNUNET_YES != monitor_connections)
453     {
454       GNUNET_SCHEDULER_shutdown();
455     }
456     return;
457   }
458   FPRINTF (stdout, "%s tunnel: %c, paths: %u\n",
459            GNUNET_i2s_full (peer), tunnel ? 'Y' : 'N', n_paths);
460 }
461
462
463 /**
464  * Method called to retrieve information about all tunnels in MESH.
465  *
466  * @param cls Closure.
467  * @param peer Destination peer.
468  * @param channels Number of channels.
469  * @param connections Number of connections.
470  * @param estate Encryption state.
471  * @param cstate Connectivity state.
472  */
473 void
474 tunnels_callback (void *cls,
475                   const struct GNUNET_PeerIdentity *peer,
476                   unsigned int channels,
477                   unsigned int connections,
478                   uint16_t estate,
479                   uint16_t cstate)
480 {
481   if (NULL == peer)
482   {
483     if (GNUNET_YES != monitor_connections)
484     {
485       GNUNET_SCHEDULER_shutdown();
486     }
487     return;
488   }
489   FPRINTF (stdout, "%s [ENC: %u, CON: %u] CHs: %u, CONNs: %u\n",
490            GNUNET_i2s_full (peer), estate, cstate, channels, connections);
491 }
492
493
494 /**
495  * Method called to retrieve information about a specific tunnel the mesh peer
496  * has established, o`r is trying to establish.
497  *
498  * @param cls Closure.
499  * @param peer Peer towards whom the tunnel is directed.
500  * @param n_channels Number of channels.
501  * @param n_connections Number of connections.
502  * @param channels Channels.
503  * @param connections Connections.
504  * @param estate Encryption status.
505  * @param cstate Connectivity status.
506  */
507 void
508 tunnel_callback (void *cls,
509                  const struct GNUNET_PeerIdentity *peer,
510                  unsigned int n_channels,
511                  unsigned int n_connections,
512                  uint32_t *channels,
513                  struct GNUNET_HashCode *connections,
514                  unsigned int estate,
515                  unsigned int cstate)
516 {
517   unsigned int i;
518
519   if (NULL != peer)
520   {
521     FPRINTF (stdout, "Tunnel %s\n", GNUNET_i2s_full (peer));
522     FPRINTF (stdout, "- %u channels\n", n_channels);
523     for (i = 0; i < n_channels; i++)
524       FPRINTF (stdout, "   %u\n", channels[i]);
525     FPRINTF (stdout, "- %u connections\n", n_connections);
526     for (i = 0; i < n_connections; i++)
527       FPRINTF (stdout, "   %s\n", GNUNET_h2s_full (&connections[i]));
528     FPRINTF (stdout, "- enc state: %u\n", estate);
529     FPRINTF (stdout, "- con state: %u\n", cstate);
530   }
531   if (GNUNET_YES != monitor_connections)
532   {
533     GNUNET_SCHEDULER_shutdown();
534   }
535   return;
536
537 }
538
539
540 /**
541  * Call MESH's meta API, get all peers known to a peer.
542  *
543  * @param cls Closure (unused).
544  * @param tc TaskContext
545  */
546 static void
547 get_peers (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
548 {
549   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
550   {
551     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutdown\n");
552     return;
553   }
554   GNUNET_MESH_get_peers (mh, &peers_callback, NULL);
555 }
556
557 /**
558  * Call MESH's meta API, get all tunnels known to a peer.
559  *
560  * @param cls Closure (unused).
561  * @param tc TaskContext
562  */
563 static void
564 get_tunnels (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
565 {
566   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
567   {
568     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutdown\n");
569     return;
570   }
571   GNUNET_MESH_get_tunnels (mh, &tunnels_callback, NULL);
572 }
573
574
575 /**
576  * Call MESH's monitor API, get info of one tunnel.
577  *
578  * @param cls Closure (unused).
579  * @param tc TaskContext
580  */
581 static void
582 show_tunnel (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
583 {
584   struct GNUNET_PeerIdentity pid;
585
586   if (GNUNET_OK !=
587       GNUNET_CRYPTO_eddsa_public_key_from_string (tunnel_id,
588                                                   strlen (tunnel_id),
589                                                   &pid.public_key))
590   {
591     fprintf (stderr,
592              _("Invalid tunnel owner `%s'\n"),
593              tunnel_id);
594     GNUNET_SCHEDULER_shutdown();
595     return;
596   }
597   GNUNET_MESH_get_tunnel (mh, &pid, tunnel_callback, NULL);
598 }
599
600
601 /**
602  * Call MESH's monitor API, get info of one channel.
603  *
604  * @param cls Closure (unused).
605  * @param tc TaskContext
606  */
607 static void
608 show_channel (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
609 {
610
611 }
612
613
614 /**
615  * Call MESH's monitor API, get info of one connection.
616  *
617  * @param cls Closure (unused).
618  * @param tc TaskContext
619  */
620 static void
621 show_connection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
622 {
623
624 }
625
626
627 /**
628  * Main function that will be run by the scheduler.
629  *
630  * @param cls closure
631  * @param args remaining command-line arguments
632  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
633  * @param cfg configuration
634  */
635 static void
636 run (void *cls, char *const *args, const char *cfgfile,
637      const struct GNUNET_CONFIGURATION_Handle *cfg)
638 {
639   GNUNET_MESH_InboundChannelNotificationHandler *newch = NULL;
640   GNUNET_MESH_ChannelEndHandler *endch = NULL;
641   static const struct GNUNET_MESH_MessageHandler handlers[] = {
642     {&data_callback, GNUNET_MESSAGE_TYPE_MESH_CLI, 0},
643     {NULL, 0, 0} /* FIXME add option to monitor msg types */
644   };
645   static uint32_t *ports = NULL;
646   /* FIXME add option to monitor apps */
647
648   target_id = args[0];
649   target_port = args[0] && args[1] ? atoi(args[1]) : 0;
650   if ( (0 != (request_peers | request_tunnels)
651         || 0 != monitor_connections
652         || NULL != tunnel_id
653         || NULL != conn_id
654         || NULL != channel_id)
655        && target_id != NULL)
656   {
657     FPRINTF (stderr,
658              _("You must NOT give a TARGET"
659                "when using 'request all' options\n"));
660     return;
661   }
662
663   if (NULL != target_id)
664   {
665     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
666                 "Creating channel to %s\n",
667                 target_id);
668     GNUNET_SCHEDULER_add_now (&create_channel, NULL);
669     endch = &channel_ended;
670   }
671   else if (0 != listen_port)
672   {
673     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Listen\n");
674     newch = &channel_incoming;
675     endch = &channel_ended;
676     ports = GNUNET_malloc (sizeof (uint32_t) * 2);
677     ports[0] = listen_port;
678   }
679   else if (NULL != tunnel_id)
680   {
681     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show tunnel\n");
682     GNUNET_SCHEDULER_add_now (&show_tunnel, NULL);
683   }
684   else if (NULL != channel_id)
685   {
686     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show channel\n");
687     GNUNET_SCHEDULER_add_now (&show_channel, NULL);
688   }
689   else if (NULL != conn_id)
690   {
691     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show connection\n");
692     GNUNET_SCHEDULER_add_now (&show_connection, NULL);
693   }
694   else if (GNUNET_YES == request_peers)
695   {
696     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show all peers\n");
697     GNUNET_SCHEDULER_add_now (&get_peers, NULL);
698   }
699   else if (GNUNET_YES == request_tunnels)
700   {
701     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Show all tunnels\n");
702     GNUNET_SCHEDULER_add_now (&get_tunnels, NULL);
703   }
704   else
705   {
706     FPRINTF (stderr, "No action requested\n");
707     return;
708   }
709
710   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting to mesh\n");
711   mh = GNUNET_MESH_connect (cfg,
712                             NULL, /* cls */
713                             newch, /* new channel */
714                             endch, /* cleaner */
715                             handlers,
716                             ports);
717   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Done\n");
718   if (NULL == mh)
719     GNUNET_SCHEDULER_add_now (shutdown_task, NULL);
720   else
721     sd = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
722                                        shutdown_task, NULL);
723
724 }
725
726
727 /**
728  * The main function to obtain peer information.
729  *
730  * @param argc number of arguments from the command line
731  * @param argv command line arguments
732  * @return 0 ok, 1 on error
733  */
734 int
735 main (int argc, char *const *argv)
736 {
737   int res;
738   const char helpstr[] = "Create channels and retreive info about meshs status.";
739   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
740 //     {'a', "channel", "TUNNEL_ID:CHANNEL_ID",
741 //      gettext_noop ("provide information about a particular channel"),
742 //      GNUNET_YES, &GNUNET_GETOPT_set_string, &channel_id},
743 //     {'b', "connection", "TUNNEL_ID:CONNECTION_ID",
744 //      gettext_noop ("provide information about a particular connection"),
745 //      GNUNET_YES, &GNUNET_GETOPT_set_string, &conn_id},
746     {'e', "echo", NULL,
747      gettext_noop ("activate echo mode"),
748      GNUNET_NO, &GNUNET_GETOPT_set_one, &echo},
749 //     {'m', "monitor", NULL,
750 //      gettext_noop ("provide information about all tunnels (continuously) NOT IMPLEMENTED"), /* FIXME */
751 //      GNUNET_NO, &GNUNET_GETOPT_set_one, &monitor_connections},
752     {'p', "port", NULL,
753      gettext_noop ("port to listen to (default; 0)"),
754      GNUNET_YES, &GNUNET_GETOPT_set_uint, &listen_port},
755     {'P', "peers", NULL,
756     gettext_noop ("provide information about all peers"),
757     GNUNET_NO, &GNUNET_GETOPT_set_one, &request_peers},
758     {'t', "tunnel", "TUNNEL_ID",
759      gettext_noop ("provide information about a particular tunnel"),
760      GNUNET_YES, &GNUNET_GETOPT_set_string, &tunnel_id},
761     {'T', "tunnels", NULL,
762      gettext_noop ("provide information about all tunnels"),
763      GNUNET_NO, &GNUNET_GETOPT_set_one, &request_tunnels},
764
765     GNUNET_GETOPT_OPTION_END
766   };
767
768   monitor_connections = GNUNET_NO;
769
770   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
771     return 2;
772
773   res = GNUNET_PROGRAM_run (argc, argv, "gnunet-mesh (OPTIONS | TARGET PORT)",
774                             gettext_noop (helpstr),
775                             options, &run, NULL);
776
777   GNUNET_free ((void *) argv);
778
779   if (GNUNET_OK == res)
780     return 0;
781   else
782     return 1;
783 }
784
785 /* end of gnunet-mesh.c */