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