Close file descriptor on error handling.
[oweals/gnunet.git] / src / statistics / gnunet-statistics.c
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
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
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 "statistics.h"
31
32
33 /**
34  * Final status code.
35  */
36 static int ret;
37
38 /**
39  * Set to subsystem that we're going to get stats for (or NULL for all).
40  */
41 static char *subsystem;
42
43 /**
44  * Set to the specific stat value that we are after (or NULL for all).
45  */
46 static char *name;
47
48 /**
49  * Make the value that is being set persistent.
50  */
51 static int persistent;
52
53 /**
54  * Watch value continuously
55  */
56 static int watch;
57
58 /**
59  * Quiet mode
60  */
61 static int quiet;
62
63 /**
64  * Remote host
65  */
66 static char *remote_host;
67
68 /**
69  * Remote host's port
70  */
71 static unsigned long long remote_port;
72
73 /**
74  * Value to set
75  */
76 static unsigned long long set_val;
77
78 /**
79  * Set operation
80  */
81 static int set_value;
82
83 /**
84  * Handle for pending GET operation.
85  */
86 static struct GNUNET_STATISTICS_GetHandle *gh;
87
88
89 /**
90  * Callback function to process statistic values.
91  *
92  * @param cls closure
93  * @param subsystem name of subsystem that created the statistic
94  * @param name the name of the datum
95  * @param value the current value
96  * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not
97  * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
98  */
99 static int
100 printer (void *cls,
101          const char *subsystem,
102          const char *name,
103          uint64_t value,
104          int is_persistent)
105 {
106   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get();
107   const char *now_str;
108
109   if (quiet == GNUNET_NO)
110   {
111     if (GNUNET_YES == watch)
112     {
113       now_str = GNUNET_STRINGS_absolute_time_to_string (now);
114       FPRINTF (stdout,
115                "%24s %s%12s %50s: %16llu\n",
116                now_str,
117                is_persistent ? "!" : " ",
118                subsystem,
119                _(name),
120                (unsigned long long) value);
121     }
122     else
123     {
124       FPRINTF (stdout,
125                "%s%12s %50s: %16llu\n",
126                is_persistent ? "!" : " ",
127                subsystem,
128                _(name),
129                (unsigned long long) value);
130     }
131   }
132   else
133     FPRINTF (stdout,
134              "%llu\n",
135              (unsigned long long) value);
136
137   return GNUNET_OK;
138 }
139
140
141 /**
142  * Function called last by the statistics code.
143  *
144  * @param cls closure
145  * @param success #GNUNET_OK if statistics were
146  *        successfully obtained, #GNUNET_SYSERR if not.
147  */
148 static void
149 cleanup (void *cls,
150          int success)
151 {
152   gh = NULL;
153   if (GNUNET_OK != success)
154   {
155     if (NULL == remote_host)
156       FPRINTF (stderr,
157                "%s",
158                _("Failed to obtain statistics.\n"));
159     else
160       FPRINTF (stderr,
161                _("Failed to obtain statistics from host `%s:%llu'\n"),
162                remote_host,
163                remote_port);
164     ret = 1;
165   }
166   GNUNET_SCHEDULER_shutdown ();
167 }
168
169
170 /**
171  * Function run on shutdown to clean up.
172  *
173  * @param cls the statistics handle
174  */
175 static void
176 shutdown_task (void *cls)
177 {
178   struct GNUNET_STATISTICS_Handle *h = cls;
179
180   if (NULL == h)
181     return;
182   if (NULL != gh)
183   {
184     GNUNET_STATISTICS_get_cancel (gh);
185     gh = NULL;
186   }
187   if ( (GNUNET_YES == watch) &&
188        (NULL != subsystem) &&
189        (NULL != name) )
190     GNUNET_assert (GNUNET_OK ==
191                    GNUNET_STATISTICS_watch_cancel (h,
192                                                    subsystem,
193                                                    name,
194                                                    &printer,
195                                                    h));
196   GNUNET_STATISTICS_destroy (h,
197                              GNUNET_NO);
198   h = NULL;
199 }
200
201
202 /**
203  * Main task that does the actual work.
204  *
205  * @param cls closure with our configuration
206  */
207 static void
208 main_task (void *cls)
209 {
210   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
211   struct GNUNET_STATISTICS_Handle *h;
212
213   if (set_value)
214   {
215     if (NULL == subsystem)
216     {
217       FPRINTF (stderr,
218                "%s",
219                _("Missing argument: subsystem \n"));
220       ret = 1;
221       return;
222     }
223     if (NULL == name)
224     {
225       FPRINTF (stderr,
226                "%s",
227                _("Missing argument: name\n"));
228       ret = 1;
229       return;
230     }
231     h = GNUNET_STATISTICS_create (subsystem,
232                                   cfg);
233     if (NULL == h)
234     {
235       ret = 1;
236       return;
237     }
238     GNUNET_STATISTICS_set (h,
239                            name,
240                            (uint64_t) set_val,
241                            persistent);
242     GNUNET_STATISTICS_destroy (h,
243                                GNUNET_YES);
244     h = NULL;
245     return;
246   }
247   if (NULL == (h = GNUNET_STATISTICS_create ("gnunet-statistics",
248                                              cfg)))
249   {
250     ret = 1;
251     return;
252   }
253   if (GNUNET_NO == watch)
254   {
255     if (NULL ==
256         (gh = GNUNET_STATISTICS_get (h,
257                                      subsystem,
258                                      name,
259                                      &cleanup,
260                                      &printer,
261                                      h)) )
262       cleanup (h,
263                GNUNET_SYSERR);
264   }
265   else
266   {
267     if ( (NULL == subsystem) ||
268          (NULL == name) )
269     {
270       printf (_("No subsystem or name given\n"));
271       GNUNET_STATISTICS_destroy (h,
272                                  GNUNET_NO);
273       h = NULL;
274       ret = 1;
275       return;
276     }
277     if (GNUNET_OK !=
278         GNUNET_STATISTICS_watch (h,
279                                  subsystem,
280                                  name,
281                                  &printer,
282                                  h))
283     {
284       fprintf (stderr,
285                _("Failed to initialize watch routine\n"));
286       GNUNET_SCHEDULER_add_now (&shutdown_task,
287                                 h);
288       return;
289     }
290   }
291   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
292                                  h);
293 }
294
295
296 /**
297  * Main function that will be run by the scheduler.
298  *
299  * @param cls closure
300  * @param args remaining command-line arguments
301  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
302  * @param cfg configuration
303  */
304 static void
305 run (void *cls,
306      char *const *args,
307      const char *cfgfile,
308      const struct GNUNET_CONFIGURATION_Handle *cfg)
309 {
310   struct GNUNET_CONFIGURATION_Handle *c;
311
312   c = (struct GNUNET_CONFIGURATION_Handle *) cfg;
313   set_value = GNUNET_NO;
314   if (NULL != args[0])
315   {
316     if (1 != SSCANF (args[0],
317                      "%llu",
318                      &set_val))
319     {
320       FPRINTF (stderr,
321                _("Invalid argument `%s'\n"),
322                args[0]);
323       ret = 1;
324       return;
325     }
326     set_value = GNUNET_YES;
327   }
328   if (NULL != remote_host)
329   {
330     if (0 == remote_port)
331     {
332       if (GNUNET_SYSERR ==
333           GNUNET_CONFIGURATION_get_value_number (cfg,
334                                                  "statistics",
335                                                  "PORT",
336                                                  &remote_port))
337       {
338         FPRINTF (stderr,
339                  _("A port is required to connect to host `%s'\n"),
340                  remote_host);
341         return;
342       }
343     }
344     else if (65535 <= remote_port)
345     {
346       FPRINTF (stderr,
347                _("A port has to be between 1 and 65535 to connect to host `%s'\n"),
348                remote_host);
349       return;
350     }
351
352     /* Manipulate configuration */
353     GNUNET_CONFIGURATION_set_value_string (c,
354                                            "statistics",
355                                            "UNIXPATH",
356                                            "");
357     GNUNET_CONFIGURATION_set_value_string (c,
358                                            "statistics",
359                                            "HOSTNAME",
360                                            remote_host);
361     GNUNET_CONFIGURATION_set_value_number (c,
362                                            "statistics",
363                                            "PORT",
364                                            remote_port);
365   }
366   GNUNET_SCHEDULER_add_now (&main_task,
367                             c);
368 }
369
370
371 /**
372  * The main function to obtain statistics in GNUnet.
373  *
374  * @param argc number of arguments from the command line
375  * @param argv command line arguments
376  * @return 0 ok, 1 on error
377  */
378 int
379 main (int argc, char *const *argv)
380 {
381   struct GNUNET_GETOPT_CommandLineOption options[] = {
382     GNUNET_GETOPT_option_string ('n',
383                                  "name",
384                                  "NAME",
385                                  gettext_noop ("limit output to statistics for the given NAME"),
386                                  &name),
387
388     GNUNET_GETOPT_option_flag ('p',
389                                   "persistent",
390                                   gettext_noop ("make the value being set persistent"),
391                                   &persistent),
392
393     GNUNET_GETOPT_option_string ('s',
394                                  "subsystem",
395                                  "SUBSYSTEM",
396                                  gettext_noop ("limit output to the given SUBSYSTEM"),
397                                  &subsystem),
398
399     GNUNET_GETOPT_option_flag ('q',
400                                   "quiet",
401                                   gettext_noop ("just print the statistics value"),
402                                   &quiet),
403
404     GNUNET_GETOPT_option_flag ('w',
405                                   "watch",
406                                   gettext_noop ("watch value continuously"),
407                                   &watch),
408
409     GNUNET_GETOPT_option_string ('r',
410                                  "remote",
411                                  "REMOTE",
412                                  gettext_noop ("connect to remote host"),
413                                  &remote_host),
414     GNUNET_GETOPT_option_ulong ('o',
415                                     "port",
416                                     "PORT",
417                                     gettext_noop ("port for remote host"),
418                                     &remote_port),
419     GNUNET_GETOPT_OPTION_END
420   };
421   remote_port = 0;
422   remote_host = NULL;
423   if (GNUNET_OK !=
424       GNUNET_STRINGS_get_utf8_args (argc, argv,
425                                     &argc, &argv))
426     return 2;
427
428   ret = (GNUNET_OK ==
429          GNUNET_PROGRAM_run (argc,
430                              argv,
431                              "gnunet-statistics [options [value]]",
432                              gettext_noop
433                              ("Print statistics about GNUnet operations."),
434                              options,
435                              &run,
436                              NULL)) ? ret : 1;
437   GNUNET_free_non_null (remote_host);
438   GNUNET_free ((void*) argv);
439   return ret;
440 }
441
442 /* end of gnunet-statistics.c */