aboutsummaryrefslogtreecommitdiff
path: root/src/statistics/gnunet-service-statistics.c
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2010-07-07 06:27:10 +0000
committerChristian Grothoff <christian@grothoff.org>2010-07-07 06:27:10 +0000
commitac2de52a5de68f6f4f13b7aa01a481869fffc6aa (patch)
tree856c9f0b2f55f8cae99f3c7b3bf22beaecbf58da /src/statistics/gnunet-service-statistics.c
parentc1a3c11f8665432d9db6d549c28c0329668e66af (diff)
downloadgnunet-ac2de52a5de68f6f4f13b7aa01a481869fffc6aa.tar.gz
gnunet-ac2de52a5de68f6f4f13b7aa01a481869fffc6aa.zip
hacks from trip
Diffstat (limited to 'src/statistics/gnunet-service-statistics.c')
-rw-r--r--src/statistics/gnunet-service-statistics.c332
1 files changed, 310 insertions, 22 deletions
diff --git a/src/statistics/gnunet-service-statistics.c b/src/statistics/gnunet-service-statistics.c
index 3ff751efe..251302a66 100644
--- a/src/statistics/gnunet-service-statistics.c
+++ b/src/statistics/gnunet-service-statistics.c
@@ -1,6 +1,6 @@
1/* 1/*
2 This file is part of GNUnet. 2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors) 3 (C) 2009, 2010 Christian Grothoff (and other contributing authors)
4 4
5 GNUnet is free software; you can redistribute it and/or modify 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 6 it under the terms of the GNU General Public License as published
@@ -27,6 +27,7 @@
27 * - use BIO for IO operations 27 * - use BIO for IO operations
28 */ 28 */
29#include "platform.h" 29#include "platform.h"
30#include "gnunet_container_lib.h"
30#include "gnunet_disk_lib.h" 31#include "gnunet_disk_lib.h"
31#include "gnunet_getopt_lib.h" 32#include "gnunet_getopt_lib.h"
32#include "gnunet_protocols.h" 33#include "gnunet_protocols.h"
@@ -37,6 +38,41 @@
37#include "statistics.h" 38#include "statistics.h"
38 39
39/** 40/**
41 * Watch entry.
42 */
43struct WatchEntry
44{
45
46 struct WatchEntry *next;
47
48 struct WatchEntry *prev;
49
50 struct GNUNET_SERVER_Client *client;
51
52 uint64_t last_value;
53
54 uint32_t wid;
55
56};
57
58
59/**
60 * Client entry.
61 */
62struct ClientEntry
63{
64
65 struct ClientEntry *next;
66
67 struct ClientEntry *prev;
68
69 struct GNUNET_SERVER_Client *client;
70
71 uint32_t max_wid;
72
73};
74
75/**
40 * Entry in the statistics list. 76 * Entry in the statistics list.
41 */ 77 */
42struct StatsEntry 78struct StatsEntry
@@ -66,6 +102,18 @@ struct StatsEntry
66 struct GNUNET_STATISTICS_SetMessage *msg; 102 struct GNUNET_STATISTICS_SetMessage *msg;
67 103
68 /** 104 /**
105 * Watch context for changes to this
106 * value, or NULL for none.
107 */
108 struct WatchEntry *we_head;
109
110 /**
111 * Watch context for changes to this
112 * value, or NULL for none.
113 */
114 struct WatchEntry *we_tail;
115
116 /**
69 * Our value. 117 * Our value.
70 */ 118 */
71 uint64_t value; 119 uint64_t value;
@@ -92,6 +140,21 @@ static const struct GNUNET_CONFIGURATION_Handle *cfg;
92 */ 140 */
93static struct StatsEntry *start; 141static struct StatsEntry *start;
94 142
143static struct ClientEntry *client_head;
144
145static struct ClientEntry *client_tail;
146
147/**
148 * Our notification context.
149 */
150static struct GNUNET_SERVER_NotificationContext *nc;
151
152/**
153 * Counter used to generate unique values.
154 */
155static uint32_t uidgen;
156
157
95/** 158/**
96 * Load persistent values from disk. Disk format is 159 * Load persistent values from disk. Disk format is
97 * exactly the same format that we also use for 160 * exactly the same format that we also use for
@@ -209,7 +272,7 @@ save ()
209 * Transmit the given stats value. 272 * Transmit the given stats value.
210 */ 273 */
211static void 274static void
212transmit (struct GNUNET_SERVER_TransmitContext *tc, 275transmit (struct GNUNET_SERVER_Client *client,
213 const struct StatsEntry *e) 276 const struct StatsEntry *e)
214{ 277{
215 struct GNUNET_STATISTICS_ReplyMessage *m; 278 struct GNUNET_STATISTICS_ReplyMessage *m;
@@ -232,10 +295,11 @@ transmit (struct GNUNET_SERVER_TransmitContext *tc,
232 2, e->service, e->name)); 295 2, e->service, e->name));
233#if DEBUG_STATISTICS 296#if DEBUG_STATISTICS
234 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 297 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
235 "Transmitting value for `%s:%s': %llu\n", 298 "Transmitting value for `%s:%s' (%d): %llu\n",
236 e->service, e->name, e->value); 299 e->service, e->name,
300 e->persistent, e->value);
237#endif 301#endif
238 GNUNET_SERVER_transmit_context_append_message (tc, &m->header); 302 GNUNET_SERVER_notification_context_unicast (nc, client, &m->header, GNUNET_NO);
239 GNUNET_free (m); 303 GNUNET_free (m);
240} 304}
241 305
@@ -252,6 +316,32 @@ matches (const struct StatsEntry *e, const char *service, const char *name)
252} 316}
253 317
254 318
319static struct ClientEntry *
320make_client_entry (struct GNUNET_SERVER_Client *client)
321{
322 struct ClientEntry *ce;
323
324 if (client == NULL)
325 return NULL;
326 ce = client_head;
327 while (ce != NULL)
328 {
329 if (ce->client == client)
330 return ce;
331 ce = ce->next;
332 }
333 ce = GNUNET_malloc (sizeof (struct ClientEntry));
334 ce->client = client;
335 GNUNET_SERVER_client_keep (client);
336 GNUNET_CONTAINER_DLL_insert (client_head,
337 client_tail,
338 ce);
339 GNUNET_SERVER_notification_context_add (nc,
340 client);
341 return ce;
342}
343
344
255/** 345/**
256 * Handle GET-message. 346 * Handle GET-message.
257 * 347 *
@@ -266,12 +356,13 @@ handle_get (void *cls,
266 struct GNUNET_SERVER_Client *client, 356 struct GNUNET_SERVER_Client *client,
267 const struct GNUNET_MessageHeader *message) 357 const struct GNUNET_MessageHeader *message)
268{ 358{
359 struct GNUNET_MessageHeader end;
269 char *service; 360 char *service;
270 char *name; 361 char *name;
271 struct StatsEntry *pos; 362 struct StatsEntry *pos;
272 struct GNUNET_SERVER_TransmitContext *tc;
273 size_t size; 363 size_t size;
274 364
365 make_client_entry (client);
275 size = ntohs (message->size) - sizeof (struct GNUNET_MessageHeader); 366 size = ntohs (message->size) - sizeof (struct GNUNET_MessageHeader);
276 if (size != GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1], 367 if (size != GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1],
277 size, 2, &service, &name)) 368 size, 2, &service, &name))
@@ -285,20 +376,51 @@ handle_get (void *cls,
285 "Received request for statistics on `%s:%s'\n", 376 "Received request for statistics on `%s:%s'\n",
286 strlen (service) ? service : "*", strlen (name) ? name : "*"); 377 strlen (service) ? service : "*", strlen (name) ? name : "*");
287#endif 378#endif
288 tc = GNUNET_SERVER_transmit_context_create (client);
289 pos = start; 379 pos = start;
290 while (pos != NULL) 380 while (pos != NULL)
291 { 381 {
292 if (matches (pos, service, name)) 382 if (matches (pos, service, name))
293 transmit (tc, pos); 383 transmit (client, pos);
294 pos = pos->next; 384 pos = pos->next;
295 } 385 }
296 GNUNET_SERVER_transmit_context_append_data (tc, NULL, 0, 386 end.size = htons (sizeof (struct GNUNET_MessageHeader));
297 GNUNET_MESSAGE_TYPE_STATISTICS_END); 387 end.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_END);
298 GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL); 388 GNUNET_SERVER_notification_context_unicast (nc,
389 client,
390 &end,
391 GNUNET_NO);
392 GNUNET_SERVER_receive_done (client,
393 GNUNET_OK);
299} 394}
300 395
301 396
397static void
398notify_change (struct StatsEntry *se)
399{
400 struct GNUNET_STATISTICS_WatchValueMessage wvm;
401 struct WatchEntry *pos;
402
403 pos = se->we_head;
404 while (pos != NULL)
405 {
406 if (pos->last_value != se->value)
407 {
408 wvm.header.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_WATCH_VALUE);
409 wvm.header.size = htons (sizeof (struct GNUNET_STATISTICS_WatchValueMessage));
410 wvm.flags = htonl (se->persistent ? GNUNET_STATISTICS_PERSIST_BIT : 0);
411 wvm.wid = htonl (pos->wid);
412 wvm.reserved = htonl (0);
413 wvm.value = GNUNET_htonll (se->value);
414 GNUNET_SERVER_notification_context_unicast (nc,
415 pos->client,
416 &wvm.header,
417 GNUNET_NO);
418 pos->last_value = se->value;
419 }
420 pos = pos->next;
421 }
422}
423
302/** 424/**
303 * Handle SET-message. 425 * Handle SET-message.
304 * 426 *
@@ -311,11 +433,6 @@ handle_set (void *cls,
311 struct GNUNET_SERVER_Client *client, 433 struct GNUNET_SERVER_Client *client,
312 const struct GNUNET_MessageHeader *message) 434 const struct GNUNET_MessageHeader *message)
313{ 435{
314 /**
315 * Counter used to generate unique values.
316 */
317 static uint32_t uidgen;
318
319 char *service; 436 char *service;
320 char *name; 437 char *name;
321 uint16_t msize; 438 uint16_t msize;
@@ -326,7 +443,9 @@ handle_set (void *cls,
326 uint32_t flags; 443 uint32_t flags;
327 uint64_t value; 444 uint64_t value;
328 int64_t delta; 445 int64_t delta;
446 int changed;
329 447
448 make_client_entry (client);
330 msize = ntohs (message->size); 449 msize = ntohs (message->size);
331 if (msize < sizeof (struct GNUNET_STATISTICS_SetMessage)) 450 if (msize < sizeof (struct GNUNET_STATISTICS_SetMessage))
332 { 451 {
@@ -344,13 +463,15 @@ handle_set (void *cls,
344 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); 463 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
345 return; 464 return;
346 } 465 }
466 flags = ntohl (msg->flags);
467 value = GNUNET_ntohll (msg->value);
347#if DEBUG_STATISTICS 468#if DEBUG_STATISTICS
348 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 469 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
349 "Received request to update statistic on `%s:%s'\n", 470 "Received request to update statistic on `%s:%s' (%u) to/by %llu\n",
350 service, name); 471 service, name,
472 (unsigned int) flags,
473 (unsigned long long) value);
351#endif 474#endif
352 flags = ntohl (msg->flags);
353 value = GNUNET_ntohll (msg->value);
354 pos = start; 475 pos = start;
355 prev = NULL; 476 prev = NULL;
356 while (pos != NULL) 477 while (pos != NULL)
@@ -359,17 +480,20 @@ handle_set (void *cls,
359 { 480 {
360 if ((flags & GNUNET_STATISTICS_SETFLAG_RELATIVE) == 0) 481 if ((flags & GNUNET_STATISTICS_SETFLAG_RELATIVE) == 0)
361 { 482 {
483 changed = (pos->value != value);
362 pos->value = value; 484 pos->value = value;
363 } 485 }
364 else 486 else
365 { 487 {
366 delta = (int64_t) value; 488 delta = (int64_t) value;
367 if ((delta < 0) && (pos->value < -delta)) 489 if ((delta < 0) && (pos->value < -delta))
368 { 490 {
369 pos->value = 0; 491 changed = (pos->value != 0);
492 pos->value = 0;
370 } 493 }
371 else 494 else
372 { 495 {
496 changed = (delta != 0);
373 GNUNET_break ((delta <= 0) || 497 GNUNET_break ((delta <= 0) ||
374 (pos->value + delta > pos->value)); 498 (pos->value + delta > pos->value));
375 pos->value += delta; 499 pos->value += delta;
@@ -391,6 +515,8 @@ handle_set (void *cls,
391 "Statistic `%s:%s' updated to value %llu.\n", 515 "Statistic `%s:%s' updated to value %llu.\n",
392 service, name, pos->value); 516 service, name, pos->value);
393#endif 517#endif
518 if (changed)
519 notify_change (pos);
394 GNUNET_SERVER_receive_done (client, GNUNET_OK); 520 GNUNET_SERVER_receive_done (client, GNUNET_OK);
395 return; 521 return;
396 } 522 }
@@ -420,6 +546,86 @@ handle_set (void *cls,
420 546
421 547
422/** 548/**
549 * Handle WATCH-message.
550 *
551 * @param cls closure
552 * @param client identification of the client
553 * @param message the actual message
554 */
555static void
556handle_watch (void *cls,
557 struct GNUNET_SERVER_Client *client,
558 const struct GNUNET_MessageHeader *message)
559{
560 char *service;
561 char *name;
562 uint16_t msize;
563 uint16_t size;
564 struct StatsEntry *pos;
565 struct ClientEntry *ce;
566 struct WatchEntry *we;
567
568 ce = make_client_entry (client);
569 msize = ntohs (message->size);
570 if (msize < sizeof (struct GNUNET_MessageHeader))
571 {
572 GNUNET_break (0);
573 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
574 return;
575 }
576 size = msize - sizeof (struct GNUNET_MessageHeader);
577 if (size != GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1],
578 size, 2, &service, &name))
579 {
580 GNUNET_break (0);
581 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
582 return;
583 }
584#if DEBUG_STATISTICS
585 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
586 "Received request to watch statistic on `%s:%s'\n",
587 service, name);
588#endif
589 pos = start;
590 while (pos != NULL)
591 {
592 if (matches (pos, service, name))
593 break;
594 pos = pos->next;
595 }
596 if (pos == NULL)
597 {
598 pos = GNUNET_malloc (sizeof (struct StatsEntry) +
599 sizeof (struct GNUNET_STATISTICS_SetMessage) +
600 size);
601 pos->next = start;
602 pos->uid = uidgen++;
603 pos->msg = (void *) &pos[1];
604 pos->msg->header.size = htons (sizeof (struct GNUNET_STATISTICS_SetMessage) +
605 size);
606 pos->msg->header.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_SET);
607 memcpy (pos->msg, message, ntohs (message->size));
608 pos->service = (const char *) &pos->msg[1];
609 memcpy (&pos->msg[1], service, strlen (service)+1);
610 pos->name = &pos->service[strlen (pos->service) + 1];
611 memcpy ((void*) pos->name, name, strlen (name)+1);
612 start = pos;
613 }
614 we = GNUNET_malloc (sizeof (struct WatchEntry));
615 we->client = client;
616 GNUNET_SERVER_client_keep (client);
617 we->wid = ce->max_wid++;
618 GNUNET_CONTAINER_DLL_insert (pos->we_head,
619 pos->we_tail,
620 we);
621 if (pos->value != 0)
622 notify_change (pos);
623 GNUNET_SERVER_receive_done (client,
624 GNUNET_OK);
625}
626
627
628/**
423 * Task run during shutdown. 629 * Task run during shutdown.
424 * 630 *
425 * @param cls unused 631 * @param cls unused
@@ -429,7 +635,84 @@ static void
429shutdown_task (void *cls, 635shutdown_task (void *cls,
430 const struct GNUNET_SCHEDULER_TaskContext *tc) 636 const struct GNUNET_SCHEDULER_TaskContext *tc)
431{ 637{
638 struct ClientEntry *ce;
639 struct WatchEntry *we;
640 struct StatsEntry *se;
641
432 save (); 642 save ();
643 GNUNET_SERVER_notification_context_destroy (nc);
644 nc = NULL;
645 while (NULL != (ce = client_head))
646 {
647 GNUNET_SERVER_client_drop (ce->client);
648 GNUNET_CONTAINER_DLL_remove (client_head,
649 client_tail,
650 ce);
651 GNUNET_free (ce);
652 }
653 while (NULL != (se = start))
654 {
655 start = se->next;
656 while (NULL != (we = se->we_head))
657 {
658 GNUNET_SERVER_client_drop (we->client);
659 GNUNET_CONTAINER_DLL_remove (se->we_head,
660 se->we_tail,
661 we);
662 GNUNET_free (we);
663 }
664 GNUNET_free (se);
665 }
666}
667
668
669/**
670 * A client disconnected. Remove all of its data structure entries.
671 *
672 * @param cls closure, NULL
673 * @param client identification of the client
674 */
675static void
676handle_client_disconnect (void *cls,
677 struct GNUNET_SERVER_Client
678 * client)
679{
680 struct ClientEntry *ce;
681 struct WatchEntry *we;
682 struct WatchEntry *wen;
683 struct StatsEntry *se;
684
685 ce = client_head;
686 while (NULL != ce)
687 {
688 if (ce->client == client)
689 {
690 GNUNET_SERVER_client_drop (ce->client);
691 GNUNET_CONTAINER_DLL_remove (client_head,
692 client_tail,
693 ce);
694 GNUNET_free (ce);
695 break;
696 }
697 ce = ce->next;
698 }
699 se = start;
700 while (NULL != se)
701 {
702 wen = se->we_head;
703 while (NULL != (we = wen))
704 {
705 wen = we->next;
706 if (we->client != client)
707 continue;
708 GNUNET_SERVER_client_drop (we->client);
709 GNUNET_CONTAINER_DLL_remove (se->we_head,
710 se->we_tail,
711 we);
712 GNUNET_free (we);
713 }
714 se = se->next;
715 }
433} 716}
434 717
435 718
@@ -450,10 +733,15 @@ run (void *cls,
450 static const struct GNUNET_SERVER_MessageHandler handlers[] = { 733 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
451 {&handle_set, NULL, GNUNET_MESSAGE_TYPE_STATISTICS_SET, 0}, 734 {&handle_set, NULL, GNUNET_MESSAGE_TYPE_STATISTICS_SET, 0},
452 {&handle_get, NULL, GNUNET_MESSAGE_TYPE_STATISTICS_GET, 0}, 735 {&handle_get, NULL, GNUNET_MESSAGE_TYPE_STATISTICS_GET, 0},
736 {&handle_watch, NULL, GNUNET_MESSAGE_TYPE_STATISTICS_WATCH, 0},
453 {NULL, NULL, 0, 0} 737 {NULL, NULL, 0, 0}
454 }; 738 };
455 cfg = c; 739 cfg = c;
456 GNUNET_SERVER_add_handlers (server, handlers); 740 GNUNET_SERVER_add_handlers (server, handlers);
741 nc = GNUNET_SERVER_notification_context_create (server, 16);
742 GNUNET_SERVER_disconnect_notify (server,
743 &handle_client_disconnect,
744 NULL);
457 load (server); 745 load (server);
458 GNUNET_SCHEDULER_add_delayed (sched, 746 GNUNET_SCHEDULER_add_delayed (sched,
459 GNUNET_TIME_UNIT_FOREVER_REL, 747 GNUNET_TIME_UNIT_FOREVER_REL,