aboutsummaryrefslogtreecommitdiff
path: root/src/statistics
diff options
context:
space:
mode:
Diffstat (limited to 'src/statistics')
-rw-r--r--src/statistics/Makefile.am59
-rw-r--r--src/statistics/gnunet-service-statistics.c470
-rw-r--r--src/statistics/gnunet-statistics.c179
-rw-r--r--src/statistics/statistics.h94
-rw-r--r--src/statistics/statistics_api.c688
-rwxr-xr-xsrc/statistics/test_gnunet_statistics.sh177
-rw-r--r--src/statistics/test_statistics_api.c177
-rw-r--r--src/statistics/test_statistics_api_data.conf5
8 files changed, 1849 insertions, 0 deletions
diff --git a/src/statistics/Makefile.am b/src/statistics/Makefile.am
new file mode 100644
index 000000000..2ae254cbf
--- /dev/null
+++ b/src/statistics/Makefile.am
@@ -0,0 +1,59 @@
1INCLUDES = -I$(top_srcdir)/src/include
2
3if MINGW
4 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
5endif
6
7if USE_COVERAGE
8 AM_CFLAGS = -fprofile-arcs -ftest-coverage
9endif
10
11
12lib_LTLIBRARIES = libgnunetstatistics.la
13
14libgnunetstatistics_la_SOURCES = \
15 statistics_api.c statistics.h
16libgnunetstatistics_la_LIBADD = \
17 $(top_builddir)/src/util/libgnunetutil.la \
18 $(GN_LIBINTL)
19libgnunetstatistics_la_LDFLAGS = \
20 $(GN_LIB_LDFLAGS) $(WINFLAGS) \
21 -version-info 0:0:0
22
23
24bin_PROGRAMS = \
25 gnunet-statistics \
26 gnunet-service-statistics
27
28gnunet_statistics_SOURCES = \
29 gnunet-statistics.c
30gnunet_statistics_LDADD = \
31 $(top_builddir)/src/statistics/libgnunetstatistics.la \
32 $(top_builddir)/src/util/libgnunetutil.la \
33 $(GN_LIBINTL)
34
35gnunet_service_statistics_SOURCES = \
36 gnunet-service-statistics.c
37gnunet_service_statistics_LDADD = \
38 $(top_builddir)/src/statistics/libgnunetstatistics.la \
39 $(top_builddir)/src/util/libgnunetutil.la \
40 $(GN_LIBINTL)
41
42
43check_PROGRAMS = \
44 test_statistics_api
45
46TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
47
48test_statistics_api_SOURCES = \
49 test_statistics_api.c
50test_statistics_api_LDADD = \
51 $(top_builddir)/src/statistics/libgnunetstatistics.la \
52 $(top_builddir)/src/util/libgnunetutil.la
53
54EXTRA_DIST = \
55 test_statistics_api_data.conf
56
57check_SCRIPTS = \
58 test_gnunet_statistics.sh
59
diff --git a/src/statistics/gnunet-service-statistics.c b/src/statistics/gnunet-service-statistics.c
new file mode 100644
index 000000000..0e7b4853d
--- /dev/null
+++ b/src/statistics/gnunet-service-statistics.c
@@ -0,0 +1,470 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, 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 statistics/gnunet-service-statistics.c
23 * @brief program that tracks statistics
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_disk_lib.h"
28#include "gnunet_getopt_lib.h"
29#include "gnunet_protocols.h"
30#include "gnunet_service_lib.h"
31#include "gnunet_statistics_service.h"
32#include "gnunet_strings_lib.h"
33#include "gnunet_time_lib.h"
34#include "statistics.h"
35
36/**
37 * Entry in the statistics list.
38 */
39struct StatsEntry
40{
41 /**
42 * This is a linked list.
43 */
44 struct StatsEntry *next;
45
46 /**
47 * Name of the service, points into the
48 * middle of msg.
49 */
50 const char *service;
51
52 /**
53 * Name for the value, points into
54 * the middle of msg.
55 */
56 const char *name;
57
58 /**
59 * Message that can be used to set this value,
60 * stored at the end of the memory used by
61 * this struct.
62 */
63 struct GNUNET_STATISTICS_SetMessage *msg;
64
65 /**
66 * Our value.
67 */
68 uint64_t value;
69
70 /**
71 * Unique ID.
72 */
73 uint32_t uid;
74
75 /**
76 * Is this value persistent?
77 */
78 int persistent;
79
80};
81
82/**
83 * Linked list of our active statistics.
84 */
85static struct StatsEntry *start;
86
87/**
88 * Counter used to generate unique values.
89 */
90static uint32_t uidgen;
91
92/**
93 * Load persistent values from disk. Disk format is
94 * exactly the same format that we also use for
95 * setting the values over the network.
96 */
97static void
98load (struct GNUNET_SERVER_Handle *server,
99 struct GNUNET_CONFIGURATION_Handle *cfg)
100{
101 char *fn;
102 int fd;
103 struct stat sb;
104 char *buf;
105 size_t off;
106 const struct GNUNET_MessageHeader *msg;
107
108 fn = GNUNET_DISK_get_home_filename (cfg,
109 "statistics", "statistics.data", NULL);
110 if (fn == NULL)
111 return;
112 if ((0 != stat (fn, &sb)) || (sb.st_size == 0))
113 {
114 GNUNET_free (fn);
115 return;
116 }
117 fd = GNUNET_DISK_file_open (fn, O_RDONLY);
118 if (fd == -1)
119 {
120 GNUNET_free (fn);
121 return;
122 }
123 buf = mmap (NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
124 if (MAP_FAILED == buf)
125 {
126 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "mmap", fn);
127 GNUNET_break (0 == CLOSE (fd));
128 GNUNET_free (fn);
129 return;
130 }
131 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
132 _("Loading %llu bytes of statistics from `%s'\n"),
133 (unsigned long long) sb.st_size, fn);
134 off = 0;
135 while (off + sizeof (struct GNUNET_MessageHeader) < sb.st_size)
136 {
137 msg = (const struct GNUNET_MessageHeader *) &buf[off];
138 if ((ntohs (msg->size) + off > sb.st_size) ||
139 (GNUNET_OK != GNUNET_SERVER_inject (server, NULL, msg)))
140 {
141 GNUNET_break (0);
142 break;
143 }
144 off += ntohs (msg->size);
145 }
146 GNUNET_break (0 == munmap (buf, sb.st_size));
147 GNUNET_break (0 == CLOSE (fd));
148 GNUNET_free (fn);
149}
150
151
152/**
153 * Write persistent statistics to disk.
154 *
155 * @param cls closure
156 * @param cfg configuration to use
157 */
158static void
159save (void *cls, struct GNUNET_CONFIGURATION_Handle *cfg)
160{
161 struct StatsEntry *pos;
162 char *fn;
163 int fd;
164 uint16_t size;
165 unsigned long long total;
166
167 fd = -1;
168 fn = GNUNET_DISK_get_home_filename (cfg,
169 "statistics", "statistics.data", NULL);
170 if (fn != NULL)
171 fd =
172 GNUNET_DISK_file_open (fn, O_WRONLY | O_CREAT | O_TRUNC,
173 S_IRUSR | S_IWUSR);
174 total = 0;
175 while (NULL != (pos = start))
176 {
177 start = pos->next;
178 if ((pos->persistent) && (fd != -1))
179 {
180 size = htons (pos->msg->header.size);
181 if (size != WRITE (fd, pos->msg, size))
182 {
183 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
184 "write", fn);
185 GNUNET_DISK_file_close (fn, fd);
186 fd = -1;
187 }
188 else
189 total += size;
190 }
191 GNUNET_free (pos);
192 }
193 if (fd != -1)
194 {
195 GNUNET_DISK_file_close (fn, fd);
196 if (total == 0)
197 GNUNET_break (0 == UNLINK (fn));
198 else
199 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
200 _("Wrote %llu bytes of statistics to `%s'\n"), total, fn);
201 }
202 GNUNET_free_non_null (fn);
203}
204
205
206/**
207 * Transmit the given stats value.
208 */
209static void
210transmit (struct GNUNET_SERVER_TransmitContext *tc,
211 const struct StatsEntry *e)
212{
213 struct GNUNET_STATISTICS_ReplyMessage *m;
214 struct GNUNET_MessageHeader *h;
215 size_t size;
216 uint16_t msize;
217
218 size =
219 sizeof (struct GNUNET_STATISTICS_ReplyMessage) + strlen (e->service) + 1 +
220 strlen (e->name) + 1;
221 GNUNET_assert (size < GNUNET_SERVER_MAX_MESSAGE_SIZE);
222 msize = size - sizeof (struct GNUNET_MessageHeader);
223 m = GNUNET_malloc (size);
224 m->uid = htonl (e->uid);
225 if (e->persistent)
226 m->uid |= htonl (GNUNET_STATISTICS_PERSIST_BIT);
227 m->value = GNUNET_htonll (e->value);
228 size -= sizeof (struct GNUNET_STATISTICS_ReplyMessage);
229 GNUNET_assert (size == GNUNET_STRINGS_buffer_fill ((char *) &m[1],
230 size,
231 2, e->service, e->name));
232#if DEBUG_STATISTICS
233 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
234 "Transmitting value for `%s:%s': %llu\n",
235 e->service, e->name, e->value);
236#endif
237 h = &m->header;
238 GNUNET_SERVER_transmit_context_append (tc,
239 &h[1],
240 msize,
241 GNUNET_MESSAGE_TYPE_STATISTICS_VALUE);
242 GNUNET_free (m);
243}
244
245
246/**
247 * Does this entry match the request?
248 */
249static int
250matches (const struct StatsEntry *e, const char *service, const char *name)
251{
252 return ((0 == strlen (service)) ||
253 (0 == strcmp (service, e->service)))
254 && ((0 == strlen (name)) || (0 == strcmp (name, e->name)));
255}
256
257
258/**
259 * Handle GET-message.
260 *
261 * @param cls closure
262 * @param server the server handling the message
263 * @param client identification of the client
264 * @param message the actual message
265 * @return GNUNET_OK to keep the connection open,
266 * GNUNET_SYSERR to close it (signal serious error)
267 */
268static void
269handle_get (void *cls,
270 struct GNUNET_SERVER_Handle *server,
271 struct GNUNET_SERVER_Client *client,
272 const struct GNUNET_MessageHeader *message)
273{
274 char *service;
275 char *name;
276 struct StatsEntry *pos;
277 struct GNUNET_SERVER_TransmitContext *tc;
278 size_t size;
279
280 size = ntohs (message->size) - sizeof (struct GNUNET_MessageHeader);
281 if (size != GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1],
282 size, 2, &service, &name))
283 {
284 GNUNET_break (0);
285 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
286 return;
287 }
288#if DEBUG_STATISTICS
289 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
290 "Received request for statistics on `%s:%s'\n",
291 strlen (service) ? service : "*", strlen (name) ? name : "*");
292#endif
293 tc = GNUNET_SERVER_transmit_context_create (client);
294 pos = start;
295 while (pos != NULL)
296 {
297 if (matches (pos, service, name))
298 transmit (tc, pos);
299 pos = pos->next;
300 }
301 GNUNET_SERVER_transmit_context_append (tc, NULL, 0,
302 GNUNET_MESSAGE_TYPE_STATISTICS_END);
303 GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
304}
305
306
307/**
308 * Handle SET-message.
309 *
310 * @param cls closure
311 * @param server the server handling the message
312 * @param client identification of the client
313 * @param message the actual message
314 */
315static void
316handle_set (void *cls,
317 struct GNUNET_SERVER_Handle *server,
318 struct GNUNET_SERVER_Client *client,
319 const struct GNUNET_MessageHeader *message)
320{
321 char *service;
322 char *name;
323 uint16_t msize;
324 uint16_t size;
325 const struct GNUNET_STATISTICS_SetMessage *msg;
326 struct StatsEntry *pos;
327 struct StatsEntry *prev;
328 uint32_t flags;
329 uint64_t value;
330 int64_t delta;
331
332 msize = ntohs (message->size);
333 if (msize < sizeof (struct GNUNET_STATISTICS_SetMessage))
334 {
335 GNUNET_break (0);
336 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
337 return;
338 }
339 size = msize - sizeof (struct GNUNET_STATISTICS_SetMessage);
340 msg = (const struct GNUNET_STATISTICS_SetMessage *) message;
341
342 if (size != GNUNET_STRINGS_buffer_tokenize ((const char *) &msg[1],
343 size, 2, &service, &name))
344 {
345 GNUNET_break (0);
346 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
347 return;
348 }
349#if DEBUG_STATISTICS
350 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
351 "Received request to update statistic on `%s:%s'\n",
352 service, name);
353#endif
354 flags = ntohl (msg->flags);
355 value = GNUNET_ntohll (msg->value);
356 pos = start;
357 prev = NULL;
358 while (pos != NULL)
359 {
360 if (matches (pos, service, name))
361 {
362 if ((flags & GNUNET_STATISTICS_SETFLAG_RELATIVE) == 0)
363 {
364 pos->value = value;
365 }
366 else
367 {
368 delta = (int64_t) value;
369 if ((delta < 0) && (pos->value < -delta))
370 {
371 pos->value = 0;
372 }
373 else
374 {
375 GNUNET_break ((delta <= 0) ||
376 (pos->value + delta > pos->value));
377 pos->value += delta;
378 }
379 }
380 pos->msg->value = GNUNET_htonll (pos->value);
381 pos->msg->flags = msg->flags;
382 pos->persistent =
383 (0 != (flags & GNUNET_STATISTICS_SETFLAG_PERSISTENT));
384 if (prev != NULL)
385 {
386 /* move to front for faster setting next time! */
387 prev->next = pos->next;
388 pos->next = start;
389 start = pos;
390 }
391#if DEBUG_STATISTICS
392 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
393 "Statistic `%s:%s' updated to value %llu.\n",
394 service, name, pos->value);
395#endif
396 GNUNET_SERVER_receive_done (client, GNUNET_OK);
397 return;
398 }
399 prev = pos;
400 pos = pos->next;
401 }
402 pos = GNUNET_malloc (sizeof (struct StatsEntry) + msize);
403 pos->next = start;
404 if (((flags & GNUNET_STATISTICS_SETFLAG_RELATIVE) == 0) ||
405 (0 < (int64_t) GNUNET_ntohll (msg->value)))
406 pos->value = GNUNET_ntohll (msg->value);
407 pos->uid = uidgen++;
408 pos->persistent = (0 != (flags & GNUNET_STATISTICS_SETFLAG_PERSISTENT));
409 pos->msg = (void *) &pos[1];
410 memcpy (pos->msg, message, ntohs (message->size));
411 pos->service = (const char *) &pos->msg[1];
412 pos->name = &pos->service[strlen (pos->service) + 1];
413
414 start = pos;
415#if DEBUG_STATISTICS
416 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
417 "New statistic on `%s:%s' with value %llu created.\n",
418 service, name, pos->value);
419#endif
420 GNUNET_SERVER_receive_done (client, GNUNET_OK);
421}
422
423
424/**
425 * List of handlers for the messages understood by this
426 * service.
427 */
428static struct GNUNET_SERVER_MessageHandler handlers[] = {
429 {&handle_set, NULL, GNUNET_MESSAGE_TYPE_STATISTICS_SET, 0},
430 {&handle_get, NULL, GNUNET_MESSAGE_TYPE_STATISTICS_GET, 0},
431 {NULL, NULL, 0, 0}
432};
433
434
435/**
436 * Process statistics requests.
437 *
438 * @param cls closure
439 * @param sched scheduler to use
440 * @param server the initialized server
441 * @param cfg configuration to use
442 */
443static void
444run (void *cls,
445 struct GNUNET_SCHEDULER_Handle *sched,
446 struct GNUNET_SERVER_Handle *server,
447 struct GNUNET_CONFIGURATION_Handle *cfg)
448{
449 GNUNET_SERVER_add_handlers (server, handlers);
450 load (server, cfg);
451}
452
453
454/**
455 * The main function for the statistics service.
456 *
457 * @param argc number of arguments from the command line
458 * @param argv command line arguments
459 * @return 0 ok, 1 on error
460 */
461int
462main (int argc, char *const *argv)
463{
464 return (GNUNET_OK ==
465 GNUNET_SERVICE_run (argc,
466 argv,
467 "statistics", &run, NULL, &save, NULL)) ? 0 : 1;
468}
469
470/* end of gnunet-service-statistics.c */
diff --git a/src/statistics/gnunet-statistics.c b/src/statistics/gnunet-statistics.c
new file mode 100644
index 000000000..bafb77c66
--- /dev/null
+++ b/src/statistics/gnunet-statistics.c
@@ -0,0 +1,179 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, 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 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_getopt_lib.h"
29#include "gnunet_program_lib.h"
30#include "gnunet_statistics_service.h"
31#include "statistics.h"
32
33#define GET_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
34
35/**
36 * Final status code.
37 */
38static int ret;
39
40/**
41 * Set to subsystem that we're going to get stats for (or NULL for all).
42 */
43static char *subsystem;
44
45/**
46 * Set to the specific stat value that we are after (or NULL for all).
47 */
48static char *name;
49
50/**
51 * Make the value that is being set persistent.
52 */
53static int persistent;
54
55/**
56 * Callback function to process statistic values.
57 *
58 * @param cls closure
59 * @param subsystem name of subsystem that created the statistic
60 * @param name the name of the datum
61 * @param value the current value
62 * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
63 * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
64 */
65static int
66printer (void *cls,
67 const char *subsystem,
68 const char *sname, unsigned long long value, int is_persistent)
69{
70 FPRINTF (stdout,
71 "%s%-20s %-40s: %16llu\n",
72 is_persistent ? "!" : " ", subsystem, _(sname), value);
73 return GNUNET_OK;
74}
75
76
77/**
78 * Function called last by the statistics code.
79 *
80 * @param cls closure
81 * @param success GNUNET_OK if statistics were
82 * successfully obtained, GNUNET_SYSERR if not.
83 */
84static void
85cleanup (void *cls, int success)
86{
87 struct GNUNET_STATISTICS_Handle *h = cls;
88
89 if (success != GNUNET_OK)
90 ret = 1;
91 if (h != NULL)
92 GNUNET_STATISTICS_destroy (h);
93}
94
95
96/**
97 * Main function that will be run by the scheduler.
98 *
99 * @param cls closure
100 * @param sched the scheduler to use
101 * @param args remaining command-line arguments
102 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
103 * @param cfg configuration
104 */
105static void
106run (void *cls,
107 struct GNUNET_SCHEDULER_Handle *sched,
108 char *const *args,
109 const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg)
110{
111 struct GNUNET_STATISTICS_Handle *h;
112 unsigned long long val;
113
114 if (args[0] != NULL)
115 {
116 if ((1 != SSCANF (args[0], "%llu", &val)) ||
117 (subsystem == NULL) || (name == NULL))
118 {
119 FPRINTF (stderr, _("Invalid argument `%s'\n"), args[0]);
120 ret = 1;
121 return;
122 }
123 h = GNUNET_STATISTICS_create (sched, subsystem, cfg);
124 if (h == NULL)
125 {
126 ret = 1;
127 return;
128 }
129 GNUNET_STATISTICS_set (h, name, val, persistent);
130 GNUNET_STATISTICS_destroy (h);
131 return;
132 }
133 h = GNUNET_STATISTICS_create (sched, "gnunet-statistics", cfg);
134 if (h == NULL)
135 {
136 ret = 1;
137 return;
138 }
139 GNUNET_STATISTICS_get (h,
140 subsystem, name, GET_TIMEOUT, &cleanup, &printer, h);
141}
142
143/**
144 * gnunet-statistics command line options
145 */
146static struct GNUNET_GETOPT_CommandLineOption options[] = {
147 {'n', "name", "NAME",
148 gettext_noop ("limit output to statistcs for the given NAME"), 1,
149 &GNUNET_GETOPT_set_string, &name},
150 {'p', "persistent", NULL,
151 gettext_noop ("make the value being set persistent"), 0,
152 &GNUNET_GETOPT_set_one, &persistent},
153 {'s', "subsystem", "SUBSYSTEM",
154 gettext_noop ("limit output to the given SUBSYSTEM"), 1,
155 &GNUNET_GETOPT_set_string, &subsystem},
156 GNUNET_GETOPT_OPTION_END
157};
158
159
160/**
161 * The main function to obtain statistics in GNUnet.
162 *
163 * @param argc number of arguments from the command line
164 * @param argv command line arguments
165 * @return 0 ok, 1 on error
166 */
167int
168main (int argc, char *const *argv)
169{
170 return (GNUNET_OK ==
171 GNUNET_PROGRAM_run (argc,
172 argv,
173 "gnunet-statistics",
174 gettext_noop
175 ("Print statistics about GNUnet operations."),
176 options, &run, NULL)) ? ret : 1;
177}
178
179/* end of gnunet-statistics.c */
diff --git a/src/statistics/statistics.h b/src/statistics/statistics.h
new file mode 100644
index 000000000..6eedd4d34
--- /dev/null
+++ b/src/statistics/statistics.h
@@ -0,0 +1,94 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, 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 * @author Christian Grothoff
23 * @file statistics/statistics.h
24 */
25#ifndef STATISTICS_H
26#define STATISTICS_H
27
28#include "gnunet_common.h"
29
30#define DEBUG_STATISTICS 0
31
32/**
33 * Statistics message. Contains how long the system is up
34 * and one value.
35 *
36 * The struct is be followed by the service name and
37 * name of the statistic, both 0-terminated.
38 */
39struct GNUNET_STATISTICS_ReplyMessage
40{
41 /**
42 * Type: GNUNET_MESSAGE_TYPE_STATISTICS_VALUE
43 */
44 struct GNUNET_MessageHeader header;
45
46 /**
47 * Unique numerical identifier for the value (will
48 * not change during the same client-session). Highest
49 * bit will be set for persistent values.
50 */
51 uint32_t uid GNUNET_PACKED;
52
53 /**
54 * The value.
55 */
56 uint64_t value GNUNET_PACKED;
57
58};
59
60#define GNUNET_STATISTICS_PERSIST_BIT (1<<31)
61
62#define GNUNET_STATISTICS_SETFLAG_ABSOLUTE 0
63
64#define GNUNET_STATISTICS_SETFLAG_RELATIVE 1
65
66#define GNUNET_STATISTICS_SETFLAG_PERSISTENT 2
67
68/**
69 * Message to set a statistic. Followed
70 * by the subsystem name and the name of
71 * the statistic (each 0-terminated).
72 */
73struct GNUNET_STATISTICS_SetMessage
74{
75 /**
76 * Type: GNUNET_MESSAGE_TYPE_STATISTICS_SET
77 */
78 struct GNUNET_MessageHeader header;
79
80 /**
81 * 0 for absolute value, 1 for relative value; 2 to make persistent
82 * (see GNUNET_STATISTICS_SETFLAG_*).
83 */
84 uint32_t flags GNUNET_PACKED;
85
86 /**
87 * Value. Note that if this is a relative value, it will
88 * be signed even though the type given here is unsigned.
89 */
90 uint64_t value GNUNET_PACKED;
91
92};
93
94#endif
diff --git a/src/statistics/statistics_api.c b/src/statistics/statistics_api.c
new file mode 100644
index 000000000..f68cf9396
--- /dev/null
+++ b/src/statistics/statistics_api.c
@@ -0,0 +1,688 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, 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 statistics/statistics_api.c
23 * @brief API of the statistics service
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_client_lib.h"
28#include "gnunet_protocols.h"
29#include "gnunet_server_lib.h"
30#include "gnunet_statistics_service.h"
31#include "gnunet_strings_lib.h"
32#include "statistics.h"
33
34#define SET_TRANSMIT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
35
36
37/**
38 * Types of actions.
39 */
40enum ActionType
41{
42 ACTION_GET,
43 ACTION_SET,
44 ACTION_UPDATE
45};
46
47
48/**
49 * Linked list of things we still need to do.
50 */
51struct ActionItem
52{
53 /**
54 * This is a linked list.
55 */
56 struct ActionItem *next;
57
58 /**
59 * What subsystem is this action about? (can be NULL)
60 */
61 char *subsystem;
62
63 /**
64 * What value is this action about? (can be NULL)
65 */
66 char *name;
67
68 /**
69 * Continuation to call once action is complete.
70 */
71 GNUNET_STATISTICS_Callback cont;
72
73 /**
74 * Function to call (for GET actions only).
75 */
76 GNUNET_STATISTICS_Iterator proc;
77
78 /**
79 * Closure for proc and cont.
80 */
81 void *cls;
82
83 /**
84 * Timeout for this action.
85 */
86 struct GNUNET_TIME_Absolute timeout;
87
88 /**
89 * Associated value.
90 */
91 unsigned long long value;
92
93 /**
94 * Flag for SET/UPDATE actions.
95 */
96 int make_persistent;
97
98 /**
99 * Has the current iteration been aborted; for GET actions.
100 */
101 int aborted;
102
103 /**
104 * Is this a GET, SET or UPDATE?
105 */
106 enum ActionType type;
107
108 /**
109 * Size of the message that we will be transmitting.
110 */
111 uint16_t msize;
112
113};
114
115
116/**
117 * Handle for the service.
118 */
119struct GNUNET_STATISTICS_Handle
120{
121 /**
122 * Our scheduler.
123 */
124 struct GNUNET_SCHEDULER_Handle *sched;
125
126 /**
127 * Name of our subsystem.
128 */
129 char *subsystem;
130
131 /**
132 * Configuration to use.
133 */
134 struct GNUNET_CONFIGURATION_Handle *cfg;
135
136 /**
137 * Socket (if available).
138 */
139 struct GNUNET_CLIENT_Connection *client;
140
141 /**
142 * Head of the linked list of pending actions (first action
143 * to be performed).
144 */
145 struct ActionItem *action_head;
146
147 /**
148 * Tail of the linked list of actions (for fast append).
149 */
150 struct ActionItem *action_tail;
151
152 /**
153 * Action we are currently busy with (action request has been
154 * transmitted, we're now receiving the response from the
155 * service).
156 */
157 struct ActionItem *current;
158
159 /**
160 * Should this handle be destroyed once we've processed
161 * all actions?
162 */
163 int do_destroy;
164
165};
166
167
168/**
169 * Try to (re)connect to the statistics service.
170 *
171 * @return GNUNET_YES on success, GNUNET_NO on failure.
172 */
173static int
174try_connect (struct GNUNET_STATISTICS_Handle *ret)
175{
176 if (ret->client != NULL)
177 return GNUNET_OK;
178 ret->client = GNUNET_CLIENT_connect (ret->sched, "statistics", ret->cfg);
179 if (ret->client != NULL)
180 return GNUNET_YES;
181 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
182 _("Failed to connect to statistics service!\n"));
183 return GNUNET_NO;
184}
185
186
187/**
188 * Free memory associated with the given action item.
189 */
190static void
191free_action_item (struct ActionItem *ai)
192{
193 GNUNET_free_non_null (ai->subsystem);
194 GNUNET_free_non_null (ai->name);
195 GNUNET_free (ai);
196}
197
198
199/**
200 * Get handle for the statistics service.
201 *
202 * @param subsystem name of subsystem using the service
203 * @param cfg services configuration in use
204 * @return handle to use
205 */
206struct GNUNET_STATISTICS_Handle *
207GNUNET_STATISTICS_create (struct GNUNET_SCHEDULER_Handle *sched,
208 const char *subsystem,
209 struct GNUNET_CONFIGURATION_Handle *cfg)
210{
211 struct GNUNET_STATISTICS_Handle *ret;
212
213 GNUNET_assert (subsystem != NULL);
214 GNUNET_assert (sched != NULL);
215 GNUNET_assert (cfg != NULL);
216 ret = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_Handle));
217 ret->sched = sched;
218 ret->cfg = cfg;
219 ret->subsystem = GNUNET_strdup (subsystem);
220 try_connect (ret);
221 return ret;
222}
223
224
225/**
226 * Actually free the handle.
227 */
228static void
229do_destroy (struct GNUNET_STATISTICS_Handle *h)
230{
231 GNUNET_assert (h->action_head == NULL);
232 GNUNET_assert (h->current == NULL);
233 if (h->client != NULL)
234 {
235 GNUNET_CLIENT_disconnect (h->client);
236 h->client = NULL;
237 }
238 GNUNET_free (h->subsystem);
239 GNUNET_free (h);
240}
241
242
243/**
244 * Destroy a handle (free all state associated with
245 * it).
246 */
247void
248GNUNET_STATISTICS_destroy (struct GNUNET_STATISTICS_Handle *handle)
249{
250 GNUNET_assert (handle->do_destroy == GNUNET_NO);
251 if ((handle->action_head != NULL) || (handle->current != NULL))
252 {
253 handle->do_destroy = GNUNET_YES;
254 return;
255 }
256 do_destroy (handle);
257}
258
259
260/**
261 * Process the message.
262 *
263 * @return GNUNET_OK if the message was well-formed
264 */
265static int
266process_message (struct GNUNET_STATISTICS_Handle *h,
267 const struct GNUNET_MessageHeader *msg)
268{
269 char *service;
270 char *name;
271 const struct GNUNET_STATISTICS_ReplyMessage *smsg;
272 uint16_t size;
273
274 if (h->current->aborted)
275 return GNUNET_OK; /* don't bother */
276 size = ntohs (msg->size);
277 if (size < sizeof (struct GNUNET_STATISTICS_ReplyMessage))
278 {
279 GNUNET_break (0);
280 return GNUNET_SYSERR;
281 }
282 smsg = (const struct GNUNET_STATISTICS_ReplyMessage *) msg;
283 size -= sizeof (struct GNUNET_STATISTICS_ReplyMessage);
284 if (size != GNUNET_STRINGS_buffer_tokenize ((const char *) &smsg[1],
285 size, 2, &service, &name))
286 {
287 GNUNET_break (0);
288 return GNUNET_SYSERR;
289 }
290#if DEBUG_STATISTICS
291 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
292 "Received valid statistic on `%s:%s': %llu\n",
293 service, name, GNUNET_ntohll (smsg->value));
294#endif
295 if (GNUNET_OK !=
296 h->current->proc (h->current->cls,
297 service,
298 name,
299 GNUNET_ntohll (smsg->value),
300 0 !=
301 (ntohl (smsg->uid) & GNUNET_STATISTICS_PERSIST_BIT)))
302 {
303#if DEBUG_STATISTICS
304 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
305 "Processing of remaining statistics aborted by client.\n");
306#endif
307 h->current->aborted = GNUNET_YES;
308 }
309 return GNUNET_OK;
310}
311
312
313
314/**
315 * Schedule the next action to be performed.
316 */
317static void schedule_action (struct GNUNET_STATISTICS_Handle *h);
318
319
320/**
321 * GET processing is complete, tell client about it.
322 */
323static void
324finish (struct GNUNET_STATISTICS_Handle *h, int code)
325{
326 struct ActionItem *pos = h->current;
327 h->current = NULL;
328 schedule_action (h);
329 if (pos->cont != NULL)
330 pos->cont (pos->cls, code);
331 free_action_item (pos);
332}
333
334
335/**
336 * Function called with messages from stats service.
337 *
338 * @param cls closure
339 * @param msg message received, NULL on timeout or fatal error
340 */
341static void
342receive_stats (void *cls, const struct GNUNET_MessageHeader *msg)
343{
344 struct GNUNET_STATISTICS_Handle *h = cls;
345
346 if (msg == NULL)
347 {
348 GNUNET_CLIENT_disconnect (h->client);
349 h->client = NULL;
350 GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
351 _
352 ("Error receiving statistics from service, is the service running?\n"));
353 finish (h, GNUNET_SYSERR);
354 return;
355 }
356 switch (ntohs (msg->type))
357 {
358 case GNUNET_MESSAGE_TYPE_STATISTICS_END:
359#if DEBUG_STATISTICS
360 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
361 "Received end of statistics marker\n");
362#endif
363 finish (h, GNUNET_OK);
364 return;
365 case GNUNET_MESSAGE_TYPE_STATISTICS_VALUE:
366 if (GNUNET_OK == process_message (h, msg))
367 {
368 /* finally, look for more! */
369 GNUNET_CLIENT_receive (h->client,
370 &receive_stats,
371 h,
372 GNUNET_TIME_absolute_get_remaining (h->
373 current->
374 timeout));
375 return;
376 }
377 GNUNET_break (0);
378 break;
379 default:
380 GNUNET_break (0);
381 break;
382 }
383 GNUNET_CLIENT_disconnect (h->client);
384 h->client = NULL;
385 finish (h, GNUNET_SYSERR);
386}
387
388
389/**
390 * Transmit a GET request (and if successful, start to receive
391 * the response).
392 */
393static size_t
394transmit_get (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf)
395{
396 struct GNUNET_MessageHeader *hdr;
397 size_t slen1;
398 size_t slen2;
399 uint16_t msize;
400
401 if (buf == NULL)
402 {
403 /* timeout / error */
404 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
405 _("Transmission of request for statistics failed!\n"));
406 finish (handle, GNUNET_SYSERR);
407 return 0;
408 }
409 slen1 = strlen (handle->current->subsystem) + 1;
410 slen2 = strlen (handle->current->name) + 1;
411 msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
412 GNUNET_assert (msize <= size);
413 hdr = (struct GNUNET_MessageHeader *) buf;
414 hdr->size = htons (msize);
415 hdr->type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_GET);
416 GNUNET_assert (slen1 + slen2 ==
417 GNUNET_STRINGS_buffer_fill ((char *) &hdr[1],
418 slen1 + slen2,
419 2,
420 handle->current->subsystem,
421 handle->current->name));
422 GNUNET_CLIENT_receive (handle->client,
423 &receive_stats,
424 handle,
425 GNUNET_TIME_absolute_get_remaining (handle->current->
426 timeout));
427 return msize;
428}
429
430
431
432/**
433 * Transmit a SET/UPDATE request.
434 */
435static size_t
436transmit_set (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf)
437{
438 struct GNUNET_STATISTICS_SetMessage *r;
439 size_t slen;
440 size_t nlen;
441 size_t nsize;
442
443 if (NULL == buf)
444 {
445 finish (handle, GNUNET_SYSERR);
446 return 0;
447 }
448
449 slen = strlen (handle->current->subsystem) + 1;
450 nlen = strlen (handle->current->name) + 1;
451 nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
452 if (size < nsize)
453 {
454 GNUNET_break (0);
455 finish (handle, GNUNET_SYSERR);
456 return 0;
457 }
458 r = buf;
459 r->header.size = htons (nsize);
460 r->header.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_SET);
461 r->flags = 0;
462 r->value = GNUNET_htonll (handle->current->value);
463 if (handle->current->make_persistent)
464 r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_PERSISTENT);
465 if (handle->current->type == ACTION_UPDATE)
466 r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_RELATIVE);
467 GNUNET_assert (slen + nlen ==
468 GNUNET_STRINGS_buffer_fill ((char *) &r[1],
469 slen + nlen,
470 2,
471 handle->current->subsystem,
472 handle->current->name));
473 finish (handle, GNUNET_OK);
474 return nsize;
475}
476
477
478static size_t
479transmit_action (void *cls, size_t size, void *buf)
480{
481 struct GNUNET_STATISTICS_Handle *handle = cls;
482 size_t ret;
483
484 switch (handle->current->type)
485 {
486 case ACTION_GET:
487 ret = transmit_get (handle, size, buf);
488 break;
489 case ACTION_SET:
490 case ACTION_UPDATE:
491 ret = transmit_set (handle, size, buf);
492 break;
493 }
494 return ret;
495}
496
497
498/**
499 * Schedule the next action to be performed.
500 */
501static void
502schedule_action (struct GNUNET_STATISTICS_Handle *h)
503{
504 struct GNUNET_TIME_Relative timeout;
505
506 if (h->current != NULL)
507 return; /* action already pending */
508 if (GNUNET_YES != try_connect (h))
509 {
510 finish (h, GNUNET_SYSERR);
511 return;
512 }
513
514 /* schedule next action */
515 h->current = h->action_head;
516 if (NULL == h->current)
517 {
518 /* no pending network action, check destroy! */
519 if (h->do_destroy != GNUNET_YES)
520 return;
521 do_destroy (h);
522 return;
523 }
524 h->action_head = h->action_head->next;
525 if (NULL == h->action_head)
526 h->action_tail = NULL;
527 h->current->next = NULL;
528
529 timeout = GNUNET_TIME_absolute_get_remaining (h->current->timeout);
530 if (NULL ==
531 GNUNET_CLIENT_notify_transmit_ready (h->client,
532 h->current->msize,
533 timeout, &transmit_action, h))
534 {
535 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
536 "Failed to transmit request to statistics service.\n");
537 finish (h, GNUNET_SYSERR);
538 }
539}
540
541
542static void
543insert_ai (struct GNUNET_STATISTICS_Handle *h, struct ActionItem *ai)
544{
545 if (h->action_tail == NULL)
546 {
547 h->action_head = ai;
548 h->action_tail = ai;
549 schedule_action (h);
550 }
551 else
552 {
553 h->action_tail->next = ai;
554 h->action_tail = ai;
555 }
556}
557
558
559/**
560 * Get statistic from the peer.
561 *
562 * @param handle identification of the statistics service
563 * @param subsystem limit to the specified subsystem, NULL for our subsystem
564 * @param name name of the statistic value, NULL for all values
565 * @param timeout after how long should we give up (and call
566 * cont with an error code)?
567 * @param cont continuation to call when done (can be NULL)
568 * @param proc function to call on each value
569 * @param cls closure for cont and proc
570 */
571void
572GNUNET_STATISTICS_get (struct GNUNET_STATISTICS_Handle *handle,
573 const char *subsystem,
574 const char *name,
575 struct GNUNET_TIME_Relative timeout,
576 GNUNET_STATISTICS_Callback cont,
577 GNUNET_STATISTICS_Iterator proc, void *cls)
578{
579 size_t slen1;
580 size_t slen2;
581 struct ActionItem *ai;
582
583 GNUNET_assert (handle != NULL);
584 GNUNET_assert (proc != NULL);
585 if (GNUNET_YES != try_connect (handle))
586 {
587 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
588 "Failed to connect to statistics service, can not get value `%s:%s'.\n",
589 strlen (subsystem) ? subsystem : "*",
590 strlen (name) ? name : "*");
591 cont (cls, GNUNET_SYSERR);
592 return;
593 }
594 if (subsystem == NULL)
595 subsystem = "";
596 if (name == NULL)
597 name = "";
598 slen1 = strlen (subsystem);
599 slen2 = strlen (name);
600 GNUNET_assert (slen1 + slen2 + sizeof (struct GNUNET_MessageHeader) <
601 GNUNET_SERVER_MAX_MESSAGE_SIZE);
602 ai = GNUNET_malloc (sizeof (struct ActionItem));
603 ai->subsystem = GNUNET_strdup (subsystem);
604 ai->name = GNUNET_strdup (name);
605 ai->cont = cont;
606 ai->proc = proc;
607 ai->cls = cls;
608 ai->timeout = GNUNET_TIME_relative_to_absolute (timeout);
609 ai->type = ACTION_GET;
610 ai->msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
611 insert_ai (handle, ai);
612}
613
614
615static void
616add_setter_action (struct GNUNET_STATISTICS_Handle *h,
617 const char *name,
618 int make_persistent,
619 unsigned long long value, enum ActionType type)
620{
621 struct ActionItem *ai;
622 size_t slen;
623 size_t nlen;
624 size_t nsize;
625
626 GNUNET_assert (h != NULL);
627 GNUNET_assert (name != NULL);
628 if (GNUNET_YES != try_connect (h))
629 return;
630 slen = strlen (h->subsystem) + 1;
631 nlen = strlen (name) + 1;
632 nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
633 if (nsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
634 {
635 GNUNET_break (0);
636 return;
637 }
638 ai = GNUNET_malloc (sizeof (struct ActionItem));
639 ai->subsystem = GNUNET_strdup (h->subsystem);
640 ai->name = GNUNET_strdup (name);
641 ai->timeout = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT);
642 ai->make_persistent = make_persistent;
643 ai->msize = nsize;
644 ai->value = value;
645 ai->type = type;
646 insert_ai (h, ai);
647 schedule_action (h);
648}
649
650
651/**
652 * Set statistic value for the peer. Will always use our
653 * subsystem (the argument used when "handle" was created).
654 *
655 * @param handle identification of the statistics service
656 * @param name name of the statistic value
657 * @param value new value to set
658 * @param make_persistent should the value be kept across restarts?
659 */
660void
661GNUNET_STATISTICS_set (struct GNUNET_STATISTICS_Handle *handle,
662 const char *name,
663 unsigned long long value, int make_persistent)
664{
665 add_setter_action (handle, name, make_persistent, value, ACTION_SET);
666}
667
668
669/**
670 * Set statistic value for the peer. Will always use our
671 * subsystem (the argument used when "handle" was created).
672 *
673 * @param handle identification of the statistics service
674 * @param name name of the statistic value
675 * @param delta change in value (added to existing value)
676 * @param make_persistent should the value be kept across restarts?
677 */
678void
679GNUNET_STATISTICS_update (struct GNUNET_STATISTICS_Handle *handle,
680 const char *name,
681 long long delta, int make_persistent)
682{
683 add_setter_action (handle, name, make_persistent,
684 (unsigned long long) delta, ACTION_UPDATE);
685}
686
687
688/* end of statistics_api.c */
diff --git a/src/statistics/test_gnunet_statistics.sh b/src/statistics/test_gnunet_statistics.sh
new file mode 100755
index 000000000..43f629f36
--- /dev/null
+++ b/src/statistics/test_gnunet_statistics.sh
@@ -0,0 +1,177 @@
1#!/bin/bash
2
3rm -rf /tmp/test-gnunetd-statistics/
4exe="./gnunet-statistics -c test_statistics_api_data.conf"
5base=/tmp/gnunet-test-statistics
6#DEBUG="-L DEBUG"
7# -----------------------------------
8echo -n "Preparing: Starting service..."
9./gnunet-service-statistics $DEBUG -c test_statistics_api_data.conf &
10sleep 1
11echo "DONE"
12
13# ----------------------------------------------------------------------------------
14echo -n "TEST: Bad argument checking..."
15
16if $exe -x 2> /dev/null; then
17 echo "FAIL: error running $exe"
18 kill %%
19 exit 1
20fi
21echo "PASS"
22
23# ----------------------------------------------------------------------------------
24echo -n "TEST: Set value..."
25
26if ! $exe $DEBUG -n test -s subsystem 42 ; then
27 echo "FAIL: error running $exe"
28 kill %%
29 exit 1
30fi
31echo "PASS"
32
33# ----------------------------------------------------------------------------------
34echo -n "TEST: Set another value..."
35
36if ! $exe $DEBUG -n other -s osystem 43 ; then
37 echo "FAIL: error running $exe"
38 kill %%
39 exit 1
40fi
41echo "PASS"
42
43# ----------------------------------------------------------------------------------
44echo -n "TEST: viewing all stats..."
45
46if ! $exe $DEBUG > $base.out; then
47 echo "FAIL: error running $exe"
48 kill %%
49 exit 1
50fi
51LINES=`cat $base.out | wc -l`
52if test $LINES -ne 2; then
53 echo "FAIL: unexpected output"
54 kill %%
55 exit 1
56fi
57echo "PASS"
58
59# ----------------------------------------------------------------------------------
60echo -n "TEST: viewing stats by name..."
61
62if ! $exe $DEBUG -n other > $base.out; then
63 echo "FAIL: error running $exe"
64 kill %%
65 exit 1
66fi
67LINES=`cat $base.out | grep 43 | wc -l`
68if test $LINES -ne 1; then
69 echo "FAIL: unexpected output"
70 kill %%
71 exit 1
72fi
73echo "PASS"
74
75# ----------------------------------------------------------------------------------
76echo -n "TEST: viewing stats by subsystem..."
77
78if ! $exe $DEBUG -s subsystem > $base.out; then
79 echo "FAIL: error running $exe"
80 kill %%
81 exit 1
82fi
83LINES=`cat $base.out | grep 42 | wc -l`
84if test $LINES -ne 1; then
85 echo "FAIL: unexpected output"
86 kill %%
87 exit 1
88fi
89echo "PASS"
90
91
92# ----------------------------------------------------------------------------------
93echo -n "TEST: Set persistent value..."
94
95if ! $exe $DEBUG -n lasting -s subsystem 40 -p; then
96 echo "FAIL: error running $exe"
97 kill %%
98 exit 1
99fi
100echo "PASS"
101
102# -----------------------------------
103echo -n "Restarting service..."
104sleep 1
105if ! kill %%;
106then
107 echo "FAIL: could not kill service"
108 kill %%
109 exit 1
110fi
111sleep 1
112./gnunet-service-statistics $DEBUG -c test_statistics_api_data.conf &
113sleep 1
114
115
116# ----------------------------------------------------------------------------------
117echo -n "TEST: checking persistence..."
118
119if ! $exe $DEBUG > $base.out; then
120 echo "FAIL: error running $exe"
121 kill %%
122 exit 1
123fi
124LINES=`cat $base.out | grep 40 | wc -l`
125if test $LINES -ne 1; then
126 echo "FAIL: unexpected output"
127 kill %%
128 exit 1
129fi
130echo "PASS"
131
132
133
134# ----------------------------------------------------------------------------------
135echo -n "TEST: Removing persistence..."
136
137if ! $exe $DEBUG -n lasting -s subsystem 40; then
138 echo "FAIL: error running $exe"
139 kill %%
140 exit 1
141fi
142echo "PASS"
143
144
145# -----------------------------------
146echo -n "Restarting service..."
147sleep 1
148if ! kill %%;
149then
150 echo "FAIL: could not kill service"
151 kill %%
152 exit 1
153fi
154sleep 1
155./gnunet-service-statistics $DEBUG -c test_statistics_api_data.conf &
156sleep 1
157
158
159# ----------------------------------------------------------------------------------
160echo -n "TEST: checking removed persistence..."
161
162if ! $exe $DEBUG > $base.out; then
163 echo "FAIL: error running $exe"
164 kill %%
165 exit 1
166fi
167LINES=`cat $base.out | grep 40 | wc -l`
168if test $LINES -ne 0; then
169 echo "FAIL: unexpected output"
170 kill %%
171 exit 1
172fi
173echo "PASS"
174
175kill %%
176rm -f $base.out
177rm -rf /tmp/test-gnunetd-statistics/
diff --git a/src/statistics/test_statistics_api.c b/src/statistics/test_statistics_api.c
new file mode 100644
index 000000000..7a39f54b6
--- /dev/null
+++ b/src/statistics/test_statistics_api.c
@@ -0,0 +1,177 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, 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 * @file statistics/test_statistics_api.c
22 * @brief testcase for statistics_api.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_getopt_lib.h"
27#include "gnunet_os_lib.h"
28#include "gnunet_program_lib.h"
29#include "gnunet_scheduler_lib.h"
30#include "gnunet_statistics_service.h"
31
32#define VERBOSE GNUNET_NO
33
34static int
35check_1 (void *cls,
36 const char *subsystem,
37 const char *name, unsigned long long value, int is_persistent)
38{
39 GNUNET_assert (0 == strcmp (name, "test-1"));
40 GNUNET_assert (0 == strcmp (subsystem, "test-statistics-api"));
41 GNUNET_assert (value == 1);
42 GNUNET_assert (is_persistent == GNUNET_NO);
43 return GNUNET_OK;
44}
45
46static int
47check_2 (void *cls,
48 const char *subsystem,
49 const char *name, unsigned long long value, int is_persistent)
50{
51 GNUNET_assert (0 == strcmp (name, "test-2"));
52 GNUNET_assert (0 == strcmp (subsystem, "test-statistics-api"));
53 GNUNET_assert (value == 2);
54 GNUNET_assert (is_persistent == GNUNET_NO);
55 return GNUNET_OK;
56}
57
58static int
59check_3 (void *cls,
60 const char *subsystem,
61 const char *name, unsigned long long value, int is_persistent)
62{
63 GNUNET_assert (0 == strcmp (name, "test-3"));
64 GNUNET_assert (0 == strcmp (subsystem, "test-statistics-api"));
65 GNUNET_assert (value == 3);
66 GNUNET_assert (is_persistent == GNUNET_YES);
67 return GNUNET_OK;
68}
69
70static struct GNUNET_STATISTICS_Handle *h;
71
72static void
73next_fin (void *cls, int success)
74{
75 int *ok = cls;
76
77 GNUNET_STATISTICS_destroy (h);
78 GNUNET_assert (success == GNUNET_OK);
79 *ok = 0;
80}
81
82static void
83next (void *cls, int success)
84{
85 GNUNET_assert (success == GNUNET_OK);
86 GNUNET_STATISTICS_get (h, NULL, "test-2",
87 GNUNET_TIME_UNIT_SECONDS, &next_fin, &check_2, cls);
88}
89
90static void
91run (void *cls,
92 struct GNUNET_SCHEDULER_Handle *sched,
93 char *const *args,
94 const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg)
95{
96
97 h = GNUNET_STATISTICS_create (sched, "test-statistics-api", cfg);
98 GNUNET_STATISTICS_set (h, "test-1", 1, GNUNET_NO);
99 GNUNET_STATISTICS_set (h, "test-2", 2, GNUNET_NO);
100 GNUNET_STATISTICS_set (h, "test-3", 2, GNUNET_NO);
101 GNUNET_STATISTICS_update (h, "test-3", 1, GNUNET_YES);
102 GNUNET_STATISTICS_get (h, NULL, "test-1",
103 GNUNET_TIME_UNIT_SECONDS, &next, &check_1, cls);
104}
105
106static void
107run_more (void *cls,
108 struct GNUNET_SCHEDULER_Handle *sched,
109 char *const *args,
110 const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg)
111{
112 h = GNUNET_STATISTICS_create (sched, "test-statistics-api", cfg);
113 GNUNET_STATISTICS_get (h, NULL, "test-3",
114 GNUNET_TIME_UNIT_SECONDS, &next_fin, &check_3, cls);
115}
116
117static int
118check ()
119{
120 int ok = 1;
121 pid_t pid;
122 char *const argv[] = { "test-statistics-api",
123 "-c",
124 "test_statistics_api_data.conf",
125 NULL
126 };
127 struct GNUNET_GETOPT_CommandLineOption options[] = {
128 GNUNET_GETOPT_OPTION_END
129 };
130 pid = GNUNET_OS_start_process ("gnunet-service-statistics",
131 "gnunet-service-statistics",
132#if DEBUG_STATISTICS
133 "-L", "DEBUG",
134#endif
135 "-c", "test_statistics_api_data.conf", NULL);
136 sleep (1);
137 GNUNET_PROGRAM_run (3, argv, "test-statistics-api", "nohelp",
138 options, &run, &ok);
139 if (0 != PLIBC_KILL (pid, SIGTERM))
140 {
141 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
142 ok = 1;
143 }
144 waitpid (pid, NULL, 0);
145 if (ok != 0)
146 return ok;
147 ok = 1;
148 /* restart to check persistence! */
149 pid = GNUNET_OS_start_process ("gnunet-service-statistics",
150 "gnunet-service-statistics",
151#if DEBUG_STATISTICS
152 "-L", "DEBUG",
153#endif
154 "-c", "test_statistics_api_data.conf", NULL);
155 sleep (1);
156 GNUNET_PROGRAM_run (3, argv, "test-statistics-api", "nohelp",
157 options, &run_more, &ok);
158 if (0 != PLIBC_KILL (pid, SIGTERM))
159 {
160 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
161 ok = 1;
162 }
163 waitpid (pid, NULL, 0);
164 return ok;
165}
166
167int
168main (int argc, char *argv[])
169{
170 int ret;
171
172 ret = check ();
173
174 return ret;
175}
176
177/* end of test_statistics_api.c */
diff --git a/src/statistics/test_statistics_api_data.conf b/src/statistics/test_statistics_api_data.conf
new file mode 100644
index 000000000..571a9b3e4
--- /dev/null
+++ b/src/statistics/test_statistics_api_data.conf
@@ -0,0 +1,5 @@
1[PATHS]
2SERVICEHOME = /tmp/test-gnunetd-statistics/
3
4[statistics]
5PORT = 22353