aboutsummaryrefslogtreecommitdiff
path: root/src/cli
diff options
context:
space:
mode:
Diffstat (limited to 'src/cli')
-rw-r--r--src/cli/Makefile.am5
-rw-r--r--src/cli/identity/Makefile.am2
-rw-r--r--src/cli/nat/.gitignore1
-rw-r--r--src/cli/nat/Makefile.am17
-rw-r--r--src/cli/nat/gnunet-nat.c476
-rw-r--r--src/cli/nse/.gitignore1
-rw-r--r--src/cli/nse/Makefile.am19
-rw-r--r--src/cli/nse/gnunet-nse.c130
-rw-r--r--src/cli/statistics/.gitignore1
-rw-r--r--src/cli/statistics/Makefile.am40
-rw-r--r--src/cli/statistics/gnunet-statistics.c889
11 files changed, 1579 insertions, 2 deletions
diff --git a/src/cli/Makefile.am b/src/cli/Makefile.am
index 218e68671..aac883508 100644
--- a/src/cli/Makefile.am
+++ b/src/cli/Makefile.am
@@ -1,4 +1,7 @@
1SUBDIRS = \ 1SUBDIRS = \
2 util \ 2 util \
3 arm \ 3 arm \
4 core 4 statistics \
5 core \
6 nat \
7 nse
diff --git a/src/cli/identity/Makefile.am b/src/cli/identity/Makefile.am
index be2c2f1c6..d748c19ea 100644
--- a/src/cli/identity/Makefile.am
+++ b/src/cli/identity/Makefile.am
@@ -17,7 +17,7 @@ gnunet_identity_SOURCES = \
17 gnunet-identity.c 17 gnunet-identity.c
18gnunet_identity_LDADD = \ 18gnunet_identity_LDADD = \
19 $(top_builddir)/src/service/libgnunetidentity.la \ 19 $(top_builddir)/src/service/libgnunetidentity.la \
20 $(top_builddir)/src/statistics/libgnunetstatistics.la \ 20 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
21 $(top_builddir)/src/lib/util/libgnunetutil.la \ 21 $(top_builddir)/src/lib/util/libgnunetutil.la \
22 $(GN_LIBINTL) 22 $(GN_LIBINTL)
23 23
diff --git a/src/cli/nat/.gitignore b/src/cli/nat/.gitignore
new file mode 100644
index 000000000..89e635e9f
--- /dev/null
+++ b/src/cli/nat/.gitignore
@@ -0,0 +1 @@
gnunet-nat
diff --git a/src/cli/nat/Makefile.am b/src/cli/nat/Makefile.am
new file mode 100644
index 000000000..8c1a3109c
--- /dev/null
+++ b/src/cli/nat/Makefile.am
@@ -0,0 +1,17 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4libexecdir= $(pkglibdir)/libexec/
5
6pkgcfgdir= $(pkgdatadir)/config.d/
7
8bin_PROGRAMS = \
9 gnunet-nat
10
11gnunet_nat_SOURCES = \
12 gnunet-nat.c nat.h
13gnunet_nat_LDADD = \
14 $(top_builddir)/src/service/nat/libgnunetnatnew.la \
15 $(top_builddir)/src/lib/util/libgnunetutil.la
16gnunet_nat_LDFLAGS = \
17 $(GN_LIBINTL)
diff --git a/src/cli/nat/gnunet-nat.c b/src/cli/nat/gnunet-nat.c
new file mode 100644
index 000000000..fd85549d6
--- /dev/null
+++ b/src/cli/nat/gnunet-nat.c
@@ -0,0 +1,476 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2015, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file src/nat/gnunet-nat.c
23 * @brief Command-line tool to interact with the NAT service
24 * @author Christian Grothoff
25 * @author Bruno Cabral
26 */
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_nat_service.h"
30
31/**
32 * Value to return from #main().
33 */
34static int global_ret;
35
36/**
37 * Name of section in configuration file to use for
38 * additional options.
39 */
40static char *section_name;
41
42/**
43 * Flag set to 1 if we use IPPROTO_UDP.
44 */
45static int use_udp;
46
47/**
48 * Flag set to 1 if we are to listen for connection reversal requests.
49 */
50static int listen_reversal;
51
52/**
53 * Flag set to 1 if we use IPPROTO_TCP.
54 */
55static int use_tcp;
56
57/**
58 * Protocol to use.
59 */
60static uint8_t proto;
61
62/**
63 * Local address to use for connection reversal request.
64 */
65static char *local_addr;
66
67/**
68 * Remote address to use for connection reversal request.
69 */
70static char *remote_addr;
71
72/**
73 * Should we actually bind to #bind_addr and receive and process STUN requests?
74 */
75static int do_stun;
76
77/**
78 * Handle to NAT operation.
79 */
80static struct GNUNET_NAT_Handle *nh;
81
82/**
83 * Listen socket for STUN processing.
84 */
85static struct GNUNET_NETWORK_Handle *ls;
86
87/**
88 * Task for reading STUN packets.
89 */
90static struct GNUNET_SCHEDULER_Task *rtask;
91
92
93/**
94 * Test if all activities have finished, and if so,
95 * terminate.
96 */
97static void
98test_finished ()
99{
100 if (NULL != nh)
101 return;
102 if (NULL != rtask)
103 return;
104 GNUNET_SCHEDULER_shutdown ();
105}
106
107
108/**
109 * Signature of the callback passed to #GNUNET_NAT_register() for
110 * a function to call whenever our set of 'valid' addresses changes.
111 *
112 * @param cls closure, NULL
113 * @param[in,out] app_ctx location where the app can store stuff
114 * on add and retrieve it on remove
115 * @param add_remove #GNUNET_YES to add a new public IP address,
116 * #GNUNET_NO to remove a previous (now invalid) one
117 * @param ac address class the address belongs to
118 * @param addr either the previous or the new public IP address
119 * @param addrlen actual length of the @a addr
120 */
121static void
122address_cb (void *cls,
123 void **app_ctx,
124 int add_remove,
125 enum GNUNET_NAT_AddressClass ac,
126 const struct sockaddr *addr,
127 socklen_t addrlen)
128{
129 (void) cls;
130 (void) app_ctx;
131
132 fprintf (stdout,
133 "%s %s (%d)\n",
134 add_remove ? "+" : "-",
135 GNUNET_a2s (addr, addrlen),
136 (int) ac);
137}
138
139
140/**
141 * Signature of the callback passed to #GNUNET_NAT_register().
142 * for a function to call whenever someone asks us to do connection
143 * reversal.
144 *
145 * @param cls closure, NULL
146 * @param remote_addr public IP address of the other peer
147 * @param remote_addrlen actual length of the @a remote_addr
148 */
149static void
150reversal_cb (void *cls,
151 const struct sockaddr *remote_addr,
152 socklen_t remote_addrlen)
153{
154 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
155 "Connection reversal requested by %s\n",
156 GNUNET_a2s (remote_addr, remote_addrlen));
157}
158
159
160/**
161 * Task run on shutdown.
162 *
163 * @param cls NULL
164 */
165static void
166do_shutdown (void *cls)
167{
168 if (NULL != nh)
169 {
170 GNUNET_NAT_unregister (nh);
171 nh = NULL;
172 }
173 if (NULL != ls)
174 {
175 GNUNET_NETWORK_socket_close (ls);
176 ls = NULL;
177 }
178 if (NULL != rtask)
179 {
180 GNUNET_SCHEDULER_cancel (rtask);
181 rtask = NULL;
182 }
183}
184
185
186/**
187 * Task to receive incoming packets for STUN processing.
188 */
189static void
190stun_read_task (void *cls)
191{
192 ssize_t size;
193
194 rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
195 ls,
196 &stun_read_task,
197 NULL);
198 size = GNUNET_NETWORK_socket_recvfrom_amount (ls);
199 if (size > 0)
200 {
201 GNUNET_break (0);
202 GNUNET_SCHEDULER_shutdown ();
203 global_ret = 1;
204 return;
205 }
206 {
207 char buf[size + 1];
208 struct sockaddr_storage sa;
209 socklen_t salen = sizeof(sa);
210 ssize_t ret;
211
212 ret = GNUNET_NETWORK_socket_recvfrom (ls,
213 buf,
214 size + 1,
215 (struct sockaddr *) &sa,
216 &salen);
217 if (ret != size)
218 {
219 GNUNET_break (0);
220 GNUNET_SCHEDULER_shutdown ();
221 global_ret = 1;
222 return;
223 }
224 (void) GNUNET_NAT_stun_handle_packet (nh,
225 (const struct sockaddr *) &sa,
226 salen,
227 buf,
228 ret);
229 }
230}
231
232
233/**
234 * Main function that will be run.
235 *
236 * @param cls closure
237 * @param args remaining command-line arguments
238 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
239 * @param c configuration
240 */
241static void
242run (void *cls,
243 char *const *args,
244 const char *cfgfile,
245 const struct GNUNET_CONFIGURATION_Handle *c)
246{
247 uint8_t af;
248 struct sockaddr *local_sa;
249 struct sockaddr *remote_sa;
250 socklen_t local_len;
251 size_t remote_len;
252
253 if (use_tcp && use_udp)
254 {
255 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Cannot use TCP and UDP\n");
256 global_ret = 1;
257 return;
258 }
259 proto = 0;
260 if (use_tcp)
261 proto = IPPROTO_TCP;
262 if (use_udp)
263 proto = IPPROTO_UDP;
264
265 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
266
267 if (0 == proto)
268 {
269 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Must specify either TCP or UDP\n");
270 global_ret = 1;
271 return;
272 }
273 local_len = 0;
274 local_sa = NULL;
275 remote_len = 0;
276 remote_sa = NULL;
277 if (NULL != local_addr)
278 {
279 local_len =
280 (socklen_t) GNUNET_STRINGS_parse_socket_addr (local_addr, &af, &local_sa);
281 if (0 == local_len)
282 {
283 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
284 "Invalid socket address `%s'\n",
285 local_addr);
286 goto fail_and_shutdown;
287 }
288 }
289
290 if (NULL != remote_addr)
291 {
292 remote_len =
293 GNUNET_STRINGS_parse_socket_addr (remote_addr, &af, &remote_sa);
294 if (0 == remote_len)
295 {
296 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
297 "Invalid socket address `%s'\n",
298 remote_addr);
299 goto fail_and_shutdown;
300 }
301 }
302
303 if (NULL != local_addr)
304 {
305 if (NULL == section_name)
306 section_name = GNUNET_strdup ("undefined");
307 nh = GNUNET_NAT_register (c,
308 section_name,
309 proto,
310 1,
311 (const struct sockaddr **) &local_sa,
312 &local_len,
313 &address_cb,
314 (listen_reversal) ? &reversal_cb : NULL,
315 NULL);
316 }
317 else if (listen_reversal)
318 {
319 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
320 "Use of `-W` only effective in combination with `-i`\n");
321 goto fail_and_shutdown;
322 }
323
324 if (NULL != remote_addr)
325 {
326 int ret;
327
328 if ((NULL == nh) || (sizeof(struct sockaddr_in) != local_len))
329 {
330 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
331 "Require IPv4 local address to initiate connection reversal\n");
332 goto fail_and_shutdown;
333 }
334 if (sizeof(struct sockaddr_in) != remote_len)
335 {
336 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
337 "Require IPv4 reversal target address\n");
338 goto fail_and_shutdown;
339 }
340 GNUNET_assert (AF_INET == local_sa->sa_family);
341 GNUNET_assert (AF_INET == remote_sa->sa_family);
342 ret = GNUNET_NAT_request_reversal (nh,
343 (const struct sockaddr_in *) local_sa,
344 (const struct sockaddr_in *) remote_sa);
345 switch (ret)
346 {
347 case GNUNET_SYSERR:
348 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
349 "Connection reversal internal error\n");
350 break;
351
352 case GNUNET_NO:
353 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
354 "Connection reversal unavailable\n");
355 break;
356
357 case GNUNET_OK:
358 /* operation in progress */
359 break;
360 }
361 }
362
363 if (do_stun)
364 {
365 if (NULL == local_addr)
366 {
367 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
368 "Require local address to support STUN requests\n");
369 goto fail_and_shutdown;
370 }
371 if (IPPROTO_UDP != proto)
372 {
373 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "STUN only supported over UDP\n");
374 goto fail_and_shutdown;
375 }
376 ls = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, IPPROTO_UDP);
377 if (NULL == ls)
378 {
379 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Failed to create socket\n");
380 goto fail_and_shutdown;
381 }
382 if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ls, local_sa, local_len))
383 {
384 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
385 "Failed to bind to %s: %s\n",
386 GNUNET_a2s (local_sa, local_len),
387 strerror (errno));
388 goto fail_and_shutdown;
389 }
390 rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
391 ls,
392 &stun_read_task,
393 NULL);
394 }
395 GNUNET_free (remote_sa);
396 GNUNET_free (local_sa);
397 test_finished ();
398 return;
399fail_and_shutdown:
400 global_ret = 1;
401 GNUNET_SCHEDULER_shutdown ();
402 GNUNET_free (remote_sa);
403 GNUNET_free (local_sa);
404}
405
406
407/**
408 * Main function of gnunet-nat
409 *
410 * @param argc number of command-line arguments
411 * @param argv command line
412 * @return 0 on success, -1 on error
413 */
414int
415main (int argc, char *const argv[])
416{
417 struct GNUNET_GETOPT_CommandLineOption options[] = {
418 GNUNET_GETOPT_option_string (
419 'i',
420 "in",
421 "ADDRESS",
422 gettext_noop ("which IP and port are we locally using to bind/listen to"),
423 &local_addr),
424
425 GNUNET_GETOPT_option_string (
426 'r',
427 "remote",
428 "ADDRESS",
429 gettext_noop (
430 "which remote IP and port should be asked for connection reversal"),
431 &remote_addr),
432
433 GNUNET_GETOPT_option_string (
434 'S',
435 "section",
436 NULL,
437 gettext_noop (
438 "name of configuration section to find additional options, such as manual host punching data"),
439 &section_name),
440
441 GNUNET_GETOPT_option_flag ('s',
442 "stun",
443 gettext_noop ("enable STUN processing"),
444 &do_stun),
445
446 GNUNET_GETOPT_option_flag ('t', "tcp", gettext_noop ("use TCP"), &use_tcp),
447
448 GNUNET_GETOPT_option_flag ('u', "udp", gettext_noop ("use UDP"), &use_udp),
449
450 GNUNET_GETOPT_option_flag ('W',
451 "watch",
452 gettext_noop (
453 "watch for connection reversal requests"),
454 &listen_reversal),
455 GNUNET_GETOPT_OPTION_END
456 };
457
458 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
459 return 2;
460 if (GNUNET_OK !=
461 GNUNET_PROGRAM_run (argc,
462 argv,
463 "gnunet-nat [options]",
464 _ ("GNUnet NAT traversal autoconfigure daemon"),
465 options,
466 &run,
467 NULL))
468 {
469 global_ret = 1;
470 }
471 GNUNET_free_nz ((void *) argv);
472 return global_ret;
473}
474
475
476/* end of gnunet-nat.c */
diff --git a/src/cli/nse/.gitignore b/src/cli/nse/.gitignore
new file mode 100644
index 000000000..ec3c6a9cf
--- /dev/null
+++ b/src/cli/nse/.gitignore
@@ -0,0 +1 @@
gnunet-nse
diff --git a/src/cli/nse/Makefile.am b/src/cli/nse/Makefile.am
new file mode 100644
index 000000000..e724a22d3
--- /dev/null
+++ b/src/cli/nse/Makefile.am
@@ -0,0 +1,19 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4if USE_COVERAGE
5 AM_CFLAGS = --coverage -O0
6 XLIB = -lgcov
7endif
8
9pkgcfgdir= $(pkgdatadir)/config.d/
10
11libexecdir= $(pkglibdir)/libexec/
12
13bin_PROGRAMS = gnunet-nse
14
15gnunet_nse_SOURCES = gnunet-nse.c
16gnunet_nse_LDADD = \
17 $(top_builddir)/src/service/nse/libgnunetnse.la \
18 $(top_builddir)/src/lib/util/libgnunetutil.la \
19 $(XLIB) $(GN_LIBINTL)
diff --git a/src/cli/nse/gnunet-nse.c b/src/cli/nse/gnunet-nse.c
new file mode 100644
index 000000000..edb73c0fc
--- /dev/null
+++ b/src/cli/nse/gnunet-nse.c
@@ -0,0 +1,130 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2008--2014, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file nse/gnunet-nse.c
23 * @brief Program to display network size estimates from the NSE service
24 * @author Sree Harsha Totakura <sreeharsha@totakura.in>
25 */
26
27#include "platform.h"
28#include "gnunet_nse_service.h"
29
30/**
31 * The handle to the NSE service
32 */
33static struct GNUNET_NSE_Handle *nse;
34
35/**
36 * The program status; 0 for success.
37 */
38static int status;
39
40
41/**
42 * Task to shutdown and clean up all state
43 *
44 * @param cls NULL
45 */
46static void
47do_shutdown (void *cls)
48{
49 (void) cls;
50 if (NULL != nse)
51 {
52 GNUNET_NSE_disconnect (nse);
53 nse = NULL;
54 }
55}
56
57
58/**
59 * Callback to call when network size estimate is updated.
60 *
61 * @param cls NULL
62 * @param timestamp server timestamp
63 * @param estimate the value of the current network size estimate
64 * @param std_dev standard deviation (rounded down to nearest integer)
65 * of the size estimation values seen
66 */
67static void
68handle_estimate (void *cls,
69 struct GNUNET_TIME_Absolute timestamp,
70 double estimate,
71 double std_dev)
72{
73 (void) cls;
74 status = 0;
75 fprintf (stdout,
76 "%llu %f %f %f\n",
77 (unsigned long long) timestamp.abs_value_us,
78 GNUNET_NSE_log_estimate_to_n (estimate),
79 estimate,
80 std_dev);
81}
82
83
84/**
85 * Actual main function that runs the emulation.
86 *
87 * @param cls unused
88 * @param args remaining args, unused
89 * @param cfgfile name of the configuration
90 * @param cfg configuration handle
91 */
92static void
93run (void *cls,
94 char *const *args,
95 const char *cfgfile,
96 const struct GNUNET_CONFIGURATION_Handle *cfg)
97{
98 (void) cls;
99 (void) args;
100 (void) cfgfile;
101 nse = GNUNET_NSE_connect (cfg, &handle_estimate, NULL);
102 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
103}
104
105
106/**
107 * Main function.
108 *
109 * @return 0 on success
110 */
111int
112main (int argc, char *const *argv)
113{
114 static struct GNUNET_GETOPT_CommandLineOption options[] = {
115 GNUNET_GETOPT_OPTION_END
116 };
117
118 status = 1;
119 if (GNUNET_OK !=
120 GNUNET_PROGRAM_run (argc,
121 argv,
122 "gnunet-nse",
123 gettext_noop (
124 "Show network size estimates from NSE service."),
125 options,
126 &run,
127 NULL))
128 return 2;
129 return status;
130}
diff --git a/src/cli/statistics/.gitignore b/src/cli/statistics/.gitignore
new file mode 100644
index 000000000..2a7218e76
--- /dev/null
+++ b/src/cli/statistics/.gitignore
@@ -0,0 +1 @@
gnunet-statistics
diff --git a/src/cli/statistics/Makefile.am b/src/cli/statistics/Makefile.am
new file mode 100644
index 000000000..f4a572ff2
--- /dev/null
+++ b/src/cli/statistics/Makefile.am
@@ -0,0 +1,40 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4if USE_COVERAGE
5 AM_CFLAGS = --coverage -O0
6 XLIB = -lgcov
7endif
8
9pkgcfgdir= $(pkgdatadir)/config.d/
10
11libexecdir= $(pkglibdir)/libexec/
12
13bin_PROGRAMS = \
14 gnunet-statistics
15
16gnunet_statistics_SOURCES = \
17 gnunet-statistics.c
18gnunet_statistics_LDADD = \
19 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
20 $(top_builddir)/src/lib/util/libgnunetutil.la \
21 $(GN_LIBINTL)
22
23# Config file still in service folder
24#if HAVE_PYTHON
25#check_SCRIPTS = \
26# test_gnunet_statistics.py
27#endif
28#
29#SUFFIXES = .py.in .py
30#.py.in.py:
31# $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/bin/dosubst.awk < $(srcdir)/$< > $@
32# chmod +x $@
33#
34#test_gnunet_statistics.py: test_gnunet_statistics.py.in Makefile
35# $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/bin/dosubst.awk < $(srcdir)/test_gnunet_statistics.py.in > test_gnunet_statistics.py
36# chmod +x test_gnunet_statistics.py
37#
38#EXTRA_DIST = \
39# test_statistics_api_data.conf \
40# test_gnunet_statistics.py.in
diff --git a/src/cli/statistics/gnunet-statistics.c b/src/cli/statistics/gnunet-statistics.c
new file mode 100644
index 000000000..3336980d1
--- /dev/null
+++ b/src/cli/statistics/gnunet-statistics.c
@@ -0,0 +1,889 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2004-2007, 2009, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file statistics/gnunet-statistics.c
23 * @brief tool to obtain statistics
24 * @author Christian Grothoff
25 * @author Igor Wronsky
26 */
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_statistics_service.h"
30#include "../../service/statistics/statistics.h"
31
32
33/**
34 * Final status code.
35 */
36static int ret;
37
38/**
39 * Set to subsystem that we're going to get stats for (or NULL for all).
40 */
41static char *subsystem;
42
43/**
44 * The path of the testbed data.
45 */
46static char *path_testbed;
47
48/**
49 * Set to the specific stat value that we are after (or NULL for all).
50 */
51static char *name;
52
53/**
54 * Make the value that is being set persistent.
55 */
56static int persistent;
57
58/**
59 * Watch value continuously
60 */
61static int watch;
62
63/**
64 * Quiet mode
65 */
66static int quiet;
67
68/**
69 * @brief Separator string for csv.
70 */
71static char *csv_separator;
72
73/**
74 * Remote host
75 */
76static char *remote_host;
77
78/**
79 * Remote host's port
80 */
81static unsigned long long remote_port;
82
83/**
84 * Value to set
85 */
86static unsigned long long set_val;
87
88/**
89 * Set operation
90 */
91static int set_value;
92
93/**
94 * @brief Representation of all (testbed) nodes.
95 */
96static struct Node
97{
98 /**
99 * @brief Index of the node in this array.
100 */
101 unsigned index_node;
102
103 /**
104 * @brief Configuration handle for this node
105 */
106 struct GNUNET_CONFIGURATION_Handle *conf;
107
108 /**
109 * Handle for pending GET operation.
110 */
111 struct GNUNET_STATISTICS_GetHandle *gh;
112
113 /**
114 * @brief Statistics handle nodes.
115 */
116 struct GNUNET_STATISTICS_Handle *handle;
117 /**
118 * @brief Identifier for shutdown task for this node.
119 */
120 struct GNUNET_SCHEDULER_Task *shutdown_task;
121} *nodes;
122
123/**
124 * @brief Number of configurations of all (testbed) nodes.
125 */
126static unsigned num_nodes;
127
128/**
129 * @brief Set of values for a combination of subsystem and name.
130 */
131struct ValueSet
132{
133 /**
134 * @brief Subsystem of the valueset.
135 */
136 char *subsystem;
137
138 /**
139 * @brief Name of the valueset.
140 */
141 char *name;
142
143 /**
144 * @brief The values.
145 */
146 uint64_t *values;
147
148 /**
149 * @brief Persistence of the values.
150 */
151 int is_persistent;
152};
153
154
155/**
156 * @brief Collection of all values (represented with #ValueSet).
157 */
158static struct GNUNET_CONTAINER_MultiHashMap *values;
159
160/**
161 * @brief Number of nodes that have their values ready.
162 */
163static int num_nodes_ready;
164
165/**
166 * @brief Number of nodes that have their values ready.
167 */
168static int num_nodes_ready_shutdown;
169
170
171/**
172 * @brief Create a new #ValueSet
173 *
174 * @param subsystem Subsystem of the valueset.
175 * @param name Name of the valueset.
176 * @param num_values Number of values in valueset - number of peers.
177 * @param is_persistent Persistence status of values.
178 * @return Newly allocated #ValueSet.
179 */
180static struct ValueSet *
181new_value_set (const char *subsystem,
182 const char *name,
183 unsigned num_values,
184 int is_persistent)
185{
186 struct ValueSet *value_set;
187
188 value_set = GNUNET_new (struct ValueSet);
189 value_set->subsystem = GNUNET_strdup (subsystem);
190 value_set->name = GNUNET_strdup (name);
191 value_set->values = GNUNET_new_array (num_values,
192 uint64_t);
193 value_set->is_persistent = persistent;
194 return value_set;
195}
196
197
198/**
199 * @brief Print the (collected) values.
200 *
201 * Implements #GNUNET_CONTAINER_HashMapIterator.
202 *
203 * @param cls Closure - unused
204 * @param key #GNUNET_HashCode key of #GNUNET_CONTAINER_MultiHashMap iterator -
205 * unused
206 * @param value Values represented as #ValueSet.
207 * @return #GNUNET_YES - continue iteration.
208 */
209static int
210printer (void *cls,
211 const struct GNUNET_HashCode *key,
212 void *value)
213{
214 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
215 const char *now_str;
216 struct ValueSet *value_set = value;
217
218 if (quiet == GNUNET_NO)
219 {
220 if (GNUNET_YES == watch)
221 {
222 now_str = GNUNET_STRINGS_absolute_time_to_string (now);
223 fprintf (stdout,
224 "%24s%s %s%s%12s%s %s%50s%s%s ",
225 now_str,
226 csv_separator,
227 value_set->is_persistent ? "!" : " ",
228 csv_separator,
229 value_set->subsystem,
230 csv_separator,
231 (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */
232 _ (value_set->name),
233 (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */
234 (0 == strlen (csv_separator) ? ":" : csv_separator));
235 }
236 else
237 {
238 fprintf (stdout,
239 "%s%s%12s%s %s%50s%s%s ",
240 value_set->is_persistent ? "!" : " ",
241 csv_separator,
242 value_set->subsystem,
243 csv_separator,
244 (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */
245 _ (value_set->name),
246 (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */
247 (0 == strlen (csv_separator) ? ":" : csv_separator));
248 }
249 }
250 for (unsigned i = 0; i < num_nodes; i++)
251 {
252 fprintf (stdout,
253 "%16llu%s",
254 (unsigned long long) value_set->values[i],
255 csv_separator);
256 }
257 fprintf (stdout, "\n");
258 GNUNET_free (value_set->subsystem);
259 GNUNET_free (value_set->name);
260 GNUNET_free (value_set->values);
261 GNUNET_free (value_set);
262 return GNUNET_YES;
263}
264
265
266/**
267 * Callback function to process statistic values.
268 *
269 * @param cls closure
270 * @param subsystem name of subsystem that created the statistic
271 * @param name the name of the datum
272 * @param value the current value
273 * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not
274 * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
275 */
276static int
277printer_watch (void *cls,
278 const char *subsystem,
279 const char *name,
280 uint64_t value,
281 int is_persistent)
282{
283 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
284 const char *now_str;
285
286 if (quiet == GNUNET_NO)
287 {
288 if (GNUNET_YES == watch)
289 {
290 now_str = GNUNET_STRINGS_absolute_time_to_string (now);
291 fprintf (stdout,
292 "%24s%s %s%s%12s%s %s%50s%s%s %16llu\n",
293 now_str,
294 csv_separator,
295 is_persistent ? "!" : " ",
296 csv_separator,
297 subsystem,
298 csv_separator,
299 (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */
300 _ (name),
301 (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */
302 (0 == strlen (csv_separator) ? ":" : csv_separator),
303 (unsigned long long) value);
304 }
305 else
306 {
307 fprintf (stdout,
308 "%s%s%12s%s %s%50s%s%s %16llu\n",
309 is_persistent ? "!" : " ",
310 csv_separator,
311 subsystem,
312 csv_separator,
313 (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */
314 _ (name),
315 (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */
316 (0 == strlen (csv_separator) ? ":" : csv_separator),
317 (unsigned long long) value);
318 }
319 }
320 else
321 fprintf (stdout, "%llu\n", (unsigned long long) value);
322
323 return GNUNET_OK;
324}
325
326
327/**
328 * @brief Clean all data structures related to given node.
329 *
330 * Also clears global structures if we are the last node to clean.
331 *
332 * @param cls the index of the node
333 */
334static void
335clean_node (void *cls)
336{
337 const unsigned index_node = *(unsigned *) cls;
338 struct GNUNET_STATISTICS_Handle *h;
339 struct GNUNET_STATISTICS_GetHandle *gh;
340
341 if ((NULL != path_testbed) && /* were issued with -t <testbed-path> option */
342 (NULL != nodes[index_node].conf))
343 {
344 GNUNET_CONFIGURATION_destroy (nodes[index_node].conf);
345 nodes[index_node].conf = NULL;
346 }
347
348 h = nodes[index_node].handle;
349 gh = nodes[index_node].gh;
350
351 if (NULL != gh)
352 {
353 GNUNET_STATISTICS_get_cancel (gh);
354 gh = NULL;
355 }
356 if (GNUNET_YES == watch)
357 {
358 GNUNET_assert (
359 GNUNET_OK ==
360 GNUNET_STATISTICS_watch_cancel (h,
361 subsystem,
362 name,
363 &printer_watch,
364 &nodes[index_node].index_node));
365 }
366
367 if (NULL != h)
368 {
369 GNUNET_STATISTICS_destroy (h, GNUNET_NO);
370 h = NULL;
371 }
372
373 num_nodes_ready_shutdown++;
374}
375
376
377/**
378 * @brief Print and shutdown
379 *
380 * @param cls unused
381 */
382static void
383print_finish (void *cls)
384{
385 GNUNET_CONTAINER_multihashmap_iterate (values,
386 &printer,
387 NULL);
388 GNUNET_CONTAINER_multihashmap_destroy (values);
389 GNUNET_SCHEDULER_shutdown ();
390}
391
392
393/**
394 * @brief Called once all statistic values are available.
395 *
396 * Implements #GNUNET_STATISTICS_Callback
397 *
398 * @param cls Closure - The index of the node.
399 * @param success Whether statistics were obtained successfully.
400 */
401static void
402continuation_print (void *cls,
403 int success)
404{
405 const unsigned index_node = *(unsigned *) cls;
406
407 nodes[index_node].gh = NULL;
408 if (GNUNET_OK != success)
409 {
410 if (NULL == remote_host)
411 fprintf (stderr,
412 "%s",
413 _ ("Failed to obtain statistics.\n"));
414 else
415 fprintf (stderr,
416 _ ("Failed to obtain statistics from host `%s:%llu'\n"),
417 remote_host,
418 remote_port);
419 ret = 1;
420 }
421 if (NULL != nodes[index_node].shutdown_task)
422 {
423 GNUNET_SCHEDULER_cancel (nodes[index_node].shutdown_task);
424 nodes[index_node].shutdown_task = NULL;
425 }
426 GNUNET_SCHEDULER_add_now (&clean_node,
427 &nodes[index_node].index_node);
428 num_nodes_ready++;
429 if (num_nodes_ready == num_nodes)
430 {
431 GNUNET_SCHEDULER_add_now (&print_finish,
432 NULL);
433 }
434}
435
436
437/**
438 * Function called last by the statistics code.
439 *
440 * @param cls closure
441 * @param success #GNUNET_OK if statistics were
442 * successfully obtained, #GNUNET_SYSERR if not.
443 */
444static void
445cleanup (void *cls,
446 int success)
447{
448 for (unsigned i = 0; i < num_nodes; i++)
449 {
450 nodes[i].gh = NULL;
451 }
452 if (GNUNET_OK != success)
453 {
454 if (NULL == remote_host)
455 fprintf (stderr, "%s", _ ("Failed to obtain statistics.\n"));
456 else
457 fprintf (stderr,
458 _ ("Failed to obtain statistics from host `%s:%llu'\n"),
459 remote_host,
460 remote_port);
461 ret = 1;
462 }
463 GNUNET_SCHEDULER_shutdown ();
464}
465
466
467/**
468 * @brief Iterate over statistics values and store them in #values.
469 * They will be printed once all are available.
470 *
471 * @param cls Cosure - Node index.
472 * @param subsystem Subsystem of the value.
473 * @param name Name of the value.
474 * @param value Value itself.
475 * @param is_persistent Persistence.
476 * @return #GNUNET_OK - continue.
477 */
478static int
479collector (void *cls,
480 const char *subsystem,
481 const char *name,
482 uint64_t value,
483 int is_persistent)
484{
485 const unsigned index_node = *(unsigned *) cls;
486 struct GNUNET_HashCode *key;
487 struct GNUNET_HashCode hc;
488 char *subsys_name;
489 unsigned len_subsys_name;
490 struct ValueSet *value_set;
491
492 len_subsys_name = strlen (subsystem) + 3 + strlen (name) + 1;
493 subsys_name = GNUNET_malloc (len_subsys_name);
494 sprintf (subsys_name, "%s---%s", subsystem, name);
495 key = &hc;
496 GNUNET_CRYPTO_hash (subsys_name, len_subsys_name, key);
497 GNUNET_free (subsys_name);
498 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (values, key))
499 {
500 value_set = GNUNET_CONTAINER_multihashmap_get (values, key);
501 }
502 else
503 {
504 value_set = new_value_set (subsystem, name, num_nodes, is_persistent);
505 }
506 value_set->values[index_node] = value;
507 GNUNET_assert (GNUNET_YES ==
508 GNUNET_CONTAINER_multihashmap_put (
509 values,
510 key,
511 value_set,
512 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
513 return GNUNET_OK;
514}
515
516
517/**
518 * Main task that does the actual work.
519 *
520 * @param cls closure with our configuration
521 */
522static void
523main_task (void *cls)
524{
525 unsigned index_node = *(unsigned *) cls;
526 const struct GNUNET_CONFIGURATION_Handle *cfg = nodes[index_node].conf;
527
528 if (set_value)
529 {
530 if (NULL == subsystem)
531 {
532 fprintf (stderr, "%s", _ ("Missing argument: subsystem \n"));
533 ret = 1;
534 return;
535 }
536 if (NULL == name)
537 {
538 fprintf (stderr, "%s", _ ("Missing argument: name\n"));
539 ret = 1;
540 return;
541 }
542 nodes[index_node].handle = GNUNET_STATISTICS_create (subsystem, cfg);
543 if (NULL == nodes[index_node].handle)
544 {
545 ret = 1;
546 return;
547 }
548 GNUNET_STATISTICS_set (nodes[index_node].handle,
549 name,
550 (uint64_t) set_val,
551 persistent);
552 GNUNET_STATISTICS_destroy (nodes[index_node].handle, GNUNET_YES);
553 nodes[index_node].handle = NULL;
554 return;
555 }
556 if (NULL == (nodes[index_node].handle =
557 GNUNET_STATISTICS_create ("gnunet-statistics", cfg)))
558 {
559 ret = 1;
560 return;
561 }
562 if (GNUNET_NO == watch)
563 {
564 if (NULL == (nodes[index_node].gh =
565 GNUNET_STATISTICS_get (nodes[index_node].handle,
566 subsystem,
567 name,
568 &continuation_print,
569 &collector,
570 &nodes[index_node].index_node)))
571 cleanup (nodes[index_node].handle, GNUNET_SYSERR);
572 }
573 else
574 {
575 if ((NULL == subsystem) || (NULL == name))
576 {
577 printf (_ ("No subsystem or name given\n"));
578 GNUNET_STATISTICS_destroy (nodes[index_node].handle, GNUNET_NO);
579 nodes[index_node].handle = NULL;
580 ret = 1;
581 return;
582 }
583 if (GNUNET_OK != GNUNET_STATISTICS_watch (nodes[index_node].handle,
584 subsystem,
585 name,
586 &printer_watch,
587 &nodes[index_node].index_node))
588 {
589 fprintf (stderr, _ ("Failed to initialize watch routine\n"));
590 nodes[index_node].shutdown_task =
591 GNUNET_SCHEDULER_add_now (&clean_node, &nodes[index_node].index_node);
592 return;
593 }
594 }
595 nodes[index_node].shutdown_task =
596 GNUNET_SCHEDULER_add_shutdown (&clean_node, &nodes[index_node].index_node);
597}
598
599
600/**
601 * @brief Iter over content of a node's directory to check for existence of a
602 * config file.
603 *
604 * Implements #GNUNET_FileNameCallback
605 *
606 * @param cls pointer to indicate success
607 * @param filename filename inside the directory of the potential node
608 *
609 * @return to continue iteration or not to
610 */
611static int
612iter_check_config (void *cls,
613 const char *filename)
614{
615 if (0 == strncmp (GNUNET_STRINGS_get_short_name (filename), "config", 6))
616 {
617 /* Found the config - stop iteration successfully */
618 GNUNET_array_grow (nodes, num_nodes, num_nodes + 1);
619 nodes[num_nodes - 1].conf = GNUNET_CONFIGURATION_create ();
620 nodes[num_nodes - 1].index_node = num_nodes - 1;
621 if (GNUNET_OK !=
622 GNUNET_CONFIGURATION_load (nodes[num_nodes - 1].conf, filename))
623 {
624 fprintf (stderr, "Failed loading config `%s'\n", filename);
625 return GNUNET_SYSERR;
626 }
627 return GNUNET_NO;
628 }
629 else
630 {
631 /* Continue iteration */
632 return GNUNET_OK;
633 }
634}
635
636
637/**
638 * @brief Iterates over filenames in testbed directory.
639 *
640 * Implements #GNUNET_FileNameCallback
641 *
642 * Checks if the file is a directory for a testbed node
643 * and counts the nodes.
644 *
645 * @param cls counter of nodes
646 * @param filename full path of the file in testbed
647 * @return status whether to continue iteration
648 */
649static int
650iter_testbed_path (void *cls,
651 const char *filename)
652{
653 unsigned index_node;
654
655 GNUNET_assert (NULL != filename);
656 if (1 == sscanf (GNUNET_STRINGS_get_short_name (filename),
657 "%u",
658 &index_node))
659 {
660 if (-1 == GNUNET_DISK_directory_scan (filename,
661 iter_check_config,
662 NULL))
663 {
664 /* This is probably no directory for a testbed node
665 * Go on with iteration */
666 return GNUNET_OK;
667 }
668 return GNUNET_OK;
669 }
670 return GNUNET_OK;
671}
672
673
674/**
675 * @brief Count the number of nodes running in the testbed
676 *
677 * @param path_testbed path to the testbed data
678 *
679 * @return number of running nodes
680 */
681static int
682discover_testbed_nodes (const char *path_testbed)
683{
684 int num_dir_entries;
685
686 num_dir_entries =
687 GNUNET_DISK_directory_scan (path_testbed,
688 &iter_testbed_path,
689 NULL);
690 if (-1 == num_dir_entries)
691 {
692 fprintf (stderr,
693 "Failure during scanning directory `%s'\n",
694 path_testbed);
695 return -1;
696 }
697 return 0;
698}
699
700
701/**
702 * Main function that will be run by the scheduler.
703 *
704 * @param cls closure
705 * @param args remaining command-line arguments
706 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
707 * @param cfg configuration
708 */
709static void
710run (void *cls,
711 char *const *args,
712 const char *cfgfile,
713 const struct GNUNET_CONFIGURATION_Handle *cfg)
714{
715 struct GNUNET_CONFIGURATION_Handle *c;
716
717 c = (struct GNUNET_CONFIGURATION_Handle *) cfg;
718 set_value = GNUNET_NO;
719 if (NULL == csv_separator)
720 csv_separator = "";
721 if (NULL != args[0])
722 {
723 if (1 != sscanf (args[0], "%llu", &set_val))
724 {
725 fprintf (stderr, _ ("Invalid argument `%s'\n"), args[0]);
726 ret = 1;
727 return;
728 }
729 set_value = GNUNET_YES;
730 }
731 if (NULL != remote_host)
732 {
733 if (0 == remote_port)
734 {
735 if (GNUNET_SYSERR ==
736 GNUNET_CONFIGURATION_get_value_number (cfg,
737 "statistics",
738 "PORT",
739 &remote_port))
740 {
741 fprintf (stderr,
742 _ ("A port is required to connect to host `%s'\n"),
743 remote_host);
744 return;
745 }
746 }
747 else if (65535 <= remote_port)
748 {
749 fprintf (stderr,
750 _ (
751 "A port has to be between 1 and 65535 to connect to host `%s'\n"),
752 remote_host);
753 return;
754 }
755
756 /* Manipulate configuration */
757 GNUNET_CONFIGURATION_set_value_string (c,
758 "statistics",
759 "UNIXPATH",
760 "");
761 GNUNET_CONFIGURATION_set_value_string (c,
762 "statistics",
763 "HOSTNAME",
764 remote_host);
765 GNUNET_CONFIGURATION_set_value_number (c,
766 "statistics",
767 "PORT",
768 remote_port);
769 }
770 if (NULL == path_testbed)
771 {
772 values = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
773 GNUNET_array_grow (nodes, num_nodes, 1);
774 nodes[0].index_node = 0;
775 nodes[0].conf = c;
776 GNUNET_SCHEDULER_add_now (&main_task, &nodes[0].index_node);
777 }
778 else
779 {
780 if (GNUNET_YES == watch)
781 {
782 printf (
783 _ ("Not able to watch testbed nodes (yet - feel free to implement)\n"));
784 ret = 1;
785 return;
786 }
787 values = GNUNET_CONTAINER_multihashmap_create (4, GNUNET_NO);
788 if (-1 == discover_testbed_nodes (path_testbed))
789 {
790 return;
791 }
792 /* For each config/node collect statistics */
793 for (unsigned i = 0; i < num_nodes; i++)
794 {
795 GNUNET_SCHEDULER_add_now (&main_task, &nodes[i].index_node);
796 }
797 }
798}
799
800
801/**
802 * The main function to obtain statistics in GNUnet.
803 *
804 * @param argc number of arguments from the command line
805 * @param argv command line arguments
806 * @return 0 ok, 1 on error
807 */
808int
809main (int argc, char *const *argv)
810{
811 struct GNUNET_GETOPT_CommandLineOption options[] = {
812 GNUNET_GETOPT_option_string ('n',
813 "name",
814 "NAME",
815 gettext_noop (
816 "limit output to statistics for the given NAME"),
817 &name),
818 GNUNET_GETOPT_option_flag ('p',
819 "persistent",
820 gettext_noop (
821 "make the value being set persistent"),
822 &persistent),
823 GNUNET_GETOPT_option_string ('s',
824 "subsystem",
825 "SUBSYSTEM",
826 gettext_noop (
827 "limit output to the given SUBSYSTEM"),
828 &subsystem),
829 GNUNET_GETOPT_option_string ('S',
830 "csv-separator",
831 "CSV_SEPARATOR",
832 gettext_noop ("use as csv separator"),
833 &csv_separator),
834 GNUNET_GETOPT_option_filename ('t',
835 "testbed",
836 "TESTBED",
837 gettext_noop (
838 "path to the folder containing the testbed data"),
839 &path_testbed),
840 GNUNET_GETOPT_option_flag ('q',
841 "quiet",
842 gettext_noop (
843 "just print the statistics value"),
844 &quiet),
845 GNUNET_GETOPT_option_flag ('w',
846 "watch",
847 gettext_noop ("watch value continuously"),
848 &watch),
849 GNUNET_GETOPT_option_string ('r',
850 "remote",
851 "REMOTE",
852 gettext_noop ("connect to remote host"),
853 &remote_host),
854 GNUNET_GETOPT_option_ulong ('o',
855 "port",
856 "PORT",
857 gettext_noop ("port for remote host"),
858 &remote_port),
859 GNUNET_GETOPT_OPTION_END
860 };
861
862 remote_port = 0;
863 remote_host = NULL;
864 if (GNUNET_OK !=
865 GNUNET_STRINGS_get_utf8_args (argc, argv,
866 &argc, &argv))
867 return 2;
868
869 ret = (GNUNET_OK ==
870 GNUNET_PROGRAM_run (argc,
871 argv,
872 "gnunet-statistics [options [value]]",
873 gettext_noop (
874 "Print statistics about GNUnet operations."),
875 options,
876 &run,
877 NULL))
878 ? ret
879 : 1;
880 GNUNET_array_grow (nodes,
881 num_nodes,
882 0);
883 GNUNET_free (remote_host);
884 GNUNET_free_nz ((void *) argv);
885 return ret;
886}
887
888
889/* end of gnunet-statistics.c */