aboutsummaryrefslogtreecommitdiff
path: root/src/cli/statistics/gnunet-statistics.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cli/statistics/gnunet-statistics.c')
-rw-r--r--src/cli/statistics/gnunet-statistics.c889
1 files changed, 889 insertions, 0 deletions
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 */