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.c1064
1 files changed, 0 insertions, 1064 deletions
diff --git a/src/statistics/gnunet-service-statistics.c b/src/statistics/gnunet-service-statistics.c
deleted file mode 100644
index 31c367e91..000000000
--- a/src/statistics/gnunet-service-statistics.c
+++ /dev/null
@@ -1,1064 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2012, 2014, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file statistics/gnunet-service-statistics.c
23 * @brief program that tracks statistics
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_bio_lib.h"
28#include "gnunet_container_lib.h"
29#include "gnunet_disk_lib.h"
30#include "gnunet_getopt_lib.h"
31#include "gnunet_protocols.h"
32#include "gnunet_service_lib.h"
33#include "gnunet_statistics_service.h"
34#include "gnunet_strings_lib.h"
35#include "gnunet_time_lib.h"
36#include "statistics.h"
37
38/**
39 * Watch entry.
40 */
41struct WatchEntry
42{
43 /**
44 * Watch entries are kept in a linked list.
45 */
46 struct WatchEntry *next;
47
48 /**
49 * Watch entries are kept in a linked list.
50 */
51 struct WatchEntry *prev;
52
53 /**
54 * For which client is this watch entry?
55 */
56 struct ClientEntry *ce;
57
58 /**
59 * Last value we communicated to the client for this watch entry.
60 */
61 uint64_t last_value;
62
63 /**
64 * Unique watch number for this client and this watched value.
65 */
66 uint32_t wid;
67
68 /**
69 * Is last_value valid
70 * #GNUNET_NO : last_value is n/a, #GNUNET_YES: last_value is valid
71 */
72 int last_value_set;
73};
74
75
76/**
77 * We keep the statistics organized by subsystem for faster
78 * lookup during SET operations.
79 */
80struct SubsystemEntry;
81
82
83/**
84 * Entry in the statistics list.
85 */
86struct StatsEntry
87{
88 /**
89 * This is a linked list.
90 */
91 struct StatsEntry *next;
92
93 /**
94 * This is a linked list.
95 */
96 struct StatsEntry *prev;
97
98 /**
99 * Subsystem this entry belongs to.
100 */
101 struct SubsystemEntry *subsystem;
102
103 /**
104 * Name for the value stored by this entry, allocated at the end of
105 * this struct.
106 */
107 const char *name;
108
109 /**
110 * Watch context for changes to this value, or NULL for none.
111 */
112 struct WatchEntry *we_head;
113
114 /**
115 * Watch context for changes to this value, or NULL for none.
116 */
117 struct WatchEntry *we_tail;
118
119 /**
120 * Our value.
121 */
122 uint64_t value;
123
124 /**
125 * Unique ID.
126 */
127 uint32_t uid;
128
129 /**
130 * Is this value persistent?
131 */
132 int persistent;
133
134 /**
135 * Is this value set?
136 * #GNUNET_NO: value is n/a, #GNUNET_YES: value is valid
137 */
138 int set;
139};
140
141
142/**
143 * We keep the statistics organized by subsystem for faster
144 * lookup during SET operations.
145 */
146struct SubsystemEntry
147{
148 /**
149 * Subsystems are kept in a DLL.
150 */
151 struct SubsystemEntry *next;
152
153 /**
154 * Subsystems are kept in a DLL.
155 */
156 struct SubsystemEntry *prev;
157
158 /**
159 * Head of list of values kept for this subsystem.
160 */
161 struct StatsEntry *stat_head;
162
163 /**
164 * Tail of list of values kept for this subsystem.
165 */
166 struct StatsEntry *stat_tail;
167
168 /**
169 * Name of the subsystem this entry is for, allocated at
170 * the end of this struct, do not free().
171 */
172 const char *service;
173};
174
175
176/**
177 * Client entry.
178 */
179struct ClientEntry
180{
181 /**
182 * Corresponding server handle.
183 */
184 struct GNUNET_SERVICE_Client *client;
185
186 /**
187 * Corresponding message queue.
188 */
189 struct GNUNET_MQ_Handle *mq;
190
191 /**
192 * Which subsystem is this client writing to (SET/UPDATE)?
193 */
194 struct SubsystemEntry *subsystem;
195
196 /**
197 * Maximum watch ID used by this client so far.
198 */
199 uint32_t max_wid;
200};
201
202
203/**
204 * Our configuration.
205 */
206static const struct GNUNET_CONFIGURATION_Handle *cfg;
207
208/**
209 * Head of linked list of subsystems with active statistics.
210 */
211static struct SubsystemEntry *sub_head;
212
213/**
214 * Tail of linked list of subsystems with active statistics.
215 */
216static struct SubsystemEntry *sub_tail;
217
218/**
219 * Number of connected clients.
220 */
221static unsigned int client_count;
222
223/**
224 * Our notification context.
225 */
226static struct GNUNET_NotificationContext *nc;
227
228/**
229 * Counter used to generate unique values.
230 */
231static uint32_t uidgen;
232
233/**
234 * Set to #GNUNET_YES if we are shutting down as soon as possible.
235 */
236static int in_shutdown;
237
238
239/**
240 * Write persistent statistics to disk.
241 */
242static void
243save ()
244{
245 struct SubsystemEntry *se;
246 struct StatsEntry *pos;
247 char *fn;
248 struct GNUNET_BIO_WriteHandle *wh;
249 uint16_t size;
250 unsigned long long total;
251 size_t nlen;
252 size_t slen;
253 struct GNUNET_STATISTICS_SetMessage *msg;
254
255 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
256 "STATISTICS",
257 "DATABASE",
258 &fn))
259 {
260 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
261 "STATISTICS",
262 "DATABASE");
263 return;
264 }
265 (void) GNUNET_DISK_directory_create_for_file (fn);
266 wh = GNUNET_BIO_write_open_file (fn);
267 total = 0;
268 while (NULL != (se = sub_head))
269 {
270 GNUNET_CONTAINER_DLL_remove (sub_head, sub_tail, se);
271 slen = strlen (se->service) + 1;
272 while (NULL != (pos = se->stat_head))
273 {
274 GNUNET_CONTAINER_DLL_remove (se->stat_head, se->stat_tail, pos);
275 if ((pos->persistent) && (NULL != wh))
276 {
277 nlen = strlen (pos->name) + 1;
278 size = sizeof(struct GNUNET_STATISTICS_SetMessage) + nlen + slen;
279 GNUNET_assert (size < UINT16_MAX);
280 msg = GNUNET_malloc (size);
281
282 msg->header.size = htons ((uint16_t) size);
283 msg->header.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_SET);
284 GNUNET_assert (nlen + slen ==
285 GNUNET_STRINGS_buffer_fill ((char *) &msg[1],
286 nlen + slen,
287 2,
288 se->service,
289 pos->name));
290 msg->flags =
291 htonl (pos->persistent ? GNUNET_STATISTICS_SETFLAG_PERSISTENT : 0);
292 msg->value = GNUNET_htonll (pos->value);
293 if (GNUNET_OK != GNUNET_BIO_write (wh, "statistics-save-msg", msg,
294 size))
295 {
296 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn);
297 if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL))
298 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "close", fn);
299 wh = NULL;
300 }
301 else
302 {
303 total += size;
304 }
305 GNUNET_free (msg);
306 }
307 GNUNET_free (pos);
308 }
309 GNUNET_free (se);
310 }
311 if (NULL != wh)
312 {
313 if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL))
314 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "close", fn);
315 if (0 == total)
316 GNUNET_break (0 == unlink (fn));
317 else
318 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
319 _ ("Wrote %llu bytes of statistics to `%s'\n"),
320 total,
321 fn);
322 }
323 GNUNET_free (fn);
324}
325
326
327/**
328 * Transmit the given stats value.
329 *
330 * @param client receiver of the value
331 * @param e value to transmit
332 */
333static void
334transmit (struct ClientEntry *ce, const struct StatsEntry *e)
335{
336 struct GNUNET_MQ_Envelope *env;
337 struct GNUNET_STATISTICS_ReplyMessage *m;
338 size_t size;
339
340 size = strlen (e->subsystem->service) + 1 + strlen (e->name) + 1;
341 GNUNET_assert (size < GNUNET_MAX_MESSAGE_SIZE);
342 env = GNUNET_MQ_msg_extra (m, size, GNUNET_MESSAGE_TYPE_STATISTICS_VALUE);
343 m->uid = htonl (e->uid);
344 if (e->persistent)
345 m->uid |= htonl (GNUNET_STATISTICS_PERSIST_BIT);
346 m->value = GNUNET_htonll (e->value);
347 GNUNET_assert (size == GNUNET_STRINGS_buffer_fill ((char *) &m[1],
348 size,
349 2,
350 e->subsystem->service,
351 e->name));
352 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
353 "Transmitting value for `%s:%s' (%d): %llu\n",
354 e->subsystem->service,
355 e->name,
356 e->persistent,
357 (unsigned long long) e->value);
358 GNUNET_MQ_send (ce->mq, env);
359}
360
361
362/**
363 * Callback called when a client connects to the service.
364 *
365 * @param cls closure for the service
366 * @param c the new client that connected to the service
367 * @param mq the message queue used to send messages to the client
368 * @return @a c
369 */
370static void *
371client_connect_cb (void *cls,
372 struct GNUNET_SERVICE_Client *c,
373 struct GNUNET_MQ_Handle *mq)
374{
375 struct ClientEntry *ce;
376
377 ce = GNUNET_new (struct ClientEntry);
378 ce->client = c;
379 ce->mq = mq;
380 client_count++;
381 GNUNET_notification_context_add (nc, mq);
382 return ce;
383}
384
385
386/**
387 * Check integrity of GET-message.
388 *
389 * @param cls identification of the client
390 * @param message the actual message
391 * @return #GNUNET_OK if @a message is well-formed
392 */
393static int
394check_get (void *cls, const struct GNUNET_MessageHeader *message)
395{
396 const char *service;
397 const char *name;
398 size_t size;
399
400 size = ntohs (message->size) - sizeof(struct GNUNET_MessageHeader);
401 if (size != GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1],
402 size,
403 2,
404 &service,
405 &name))
406 {
407 GNUNET_break (0);
408 return GNUNET_SYSERR;
409 }
410 return GNUNET_OK;
411}
412
413
414/**
415 * Handle GET-message.
416 *
417 * @param cls identification of the client
418 * @param message the actual message
419 */
420static void
421handle_get (void *cls, const struct GNUNET_MessageHeader *message)
422{
423 struct ClientEntry *ce = cls;
424 struct GNUNET_MQ_Envelope *env;
425 struct GNUNET_MessageHeader *end;
426 const char *service;
427 const char *name;
428 size_t slen;
429 size_t nlen;
430 struct SubsystemEntry *se;
431 struct StatsEntry *pos;
432 size_t size;
433
434 size = ntohs (message->size) - sizeof(struct GNUNET_MessageHeader);
435 GNUNET_assert (size ==
436 GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1],
437 size,
438 2,
439 &service,
440 &name));
441 slen = strlen (service);
442 nlen = strlen (name);
443 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
444 "Received request for statistics on `%s:%s'\n",
445 slen ? service : "*",
446 nlen ? name : "*");
447 for (se = sub_head; NULL != se; se = se->next)
448 {
449 if (! ((0 == slen) || (0 == strcmp (service, se->service))))
450 continue;
451 for (pos = se->stat_head; NULL != pos; pos = pos->next)
452 {
453 if (! ((0 == nlen) || (0 == strcmp (name, pos->name))))
454 continue;
455 transmit (ce, pos);
456 }
457 }
458 env = GNUNET_MQ_msg (end, GNUNET_MESSAGE_TYPE_STATISTICS_END);
459 GNUNET_MQ_send (ce->mq, env);
460 GNUNET_SERVICE_client_continue (ce->client);
461}
462
463
464/**
465 * Notify all clients listening about a change to a value.
466 *
467 * @param se value that changed
468 */
469static void
470notify_change (struct StatsEntry *se)
471{
472 struct GNUNET_MQ_Envelope *env;
473 struct GNUNET_STATISTICS_WatchValueMessage *wvm;
474 struct WatchEntry *pos;
475
476 for (pos = se->we_head; NULL != pos; pos = pos->next)
477 {
478 if (GNUNET_YES == pos->last_value_set)
479 {
480 if (pos->last_value == se->value)
481 continue;
482 }
483 else
484 {
485 pos->last_value_set = GNUNET_YES;
486 }
487 env = GNUNET_MQ_msg (wvm, GNUNET_MESSAGE_TYPE_STATISTICS_WATCH_VALUE);
488 wvm->flags =
489 htonl (se->persistent ? GNUNET_STATISTICS_SETFLAG_PERSISTENT : 0);
490 wvm->wid = htonl (pos->wid);
491 wvm->reserved = htonl (0);
492 wvm->value = GNUNET_htonll (se->value);
493 GNUNET_MQ_send (pos->ce->mq, env);
494 pos->last_value = se->value;
495 }
496}
497
498
499/**
500 * Find the subsystem entry of the given name for the specified client.
501 *
502 * @param ce client looking for the subsystem, may contain a hint
503 * to find the entry faster, can be NULL
504 * @param service name of the subsystem to look for
505 * @return subsystem entry, never NULL (subsystem entry is created if necessary)
506 */
507static struct SubsystemEntry *
508find_subsystem_entry (struct ClientEntry *ce, const char *service)
509{
510 size_t slen;
511 struct SubsystemEntry *se;
512
513 if (NULL != ce)
514 se = ce->subsystem;
515 else
516 se = NULL;
517 if ((NULL == se) || (0 != strcmp (service, se->service)))
518 {
519 for (se = sub_head; NULL != se; se = se->next)
520 if (0 == strcmp (service, se->service))
521 break;
522 if (NULL != ce)
523 ce->subsystem = se;
524 }
525 if (NULL != se)
526 return se;
527 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
528 "Allocating new subsystem entry `%s'\n",
529 service);
530 slen = strlen (service) + 1;
531 se = GNUNET_malloc (sizeof(struct SubsystemEntry) + slen);
532 GNUNET_memcpy (&se[1], service, slen);
533 se->service = (const char *) &se[1];
534 GNUNET_CONTAINER_DLL_insert (sub_head, sub_tail, se);
535 if (NULL != ce)
536 ce->subsystem = se;
537 return se;
538}
539
540
541/**
542 * Find the statistics entry of the given subsystem.
543 *
544 * @param subsystem subsystem to look in
545 * @param name name of the entry to look for
546 * @return statistis entry, or NULL if not found
547 */
548static struct StatsEntry *
549find_stat_entry (struct SubsystemEntry *se, const char *name)
550{
551 struct StatsEntry *pos;
552
553 for (pos = se->stat_head; NULL != pos; pos = pos->next)
554 if (0 == strcmp (name, pos->name))
555 return pos;
556 return NULL;
557}
558
559
560/**
561 * Check format of SET-message.
562 *
563 * @param cls the `struct ClientEntry`
564 * @param message the actual message
565 * @return #GNUNET_OK if message is well-formed
566 */
567static int
568check_set (void *cls, const struct GNUNET_STATISTICS_SetMessage *msg)
569{
570 const char *service;
571 const char *name;
572 size_t msize;
573
574 msize = ntohs (msg->header.size) - sizeof(*msg);
575 if (msize != GNUNET_STRINGS_buffer_tokenize ((const char *) &msg[1],
576 msize,
577 2,
578 &service,
579 &name))
580 {
581 GNUNET_break (0);
582 return GNUNET_SYSERR;
583 }
584 return GNUNET_OK;
585}
586
587
588/**
589 * Handle SET-message.
590 *
591 * @param cls the `struct ClientEntry`
592 * @param message the actual message
593 */
594static void
595handle_set (void *cls, const struct GNUNET_STATISTICS_SetMessage *msg)
596{
597 struct ClientEntry *ce = cls;
598 const char *service;
599 const char *name;
600 size_t nlen;
601 uint16_t msize;
602 uint16_t size;
603 struct SubsystemEntry *se;
604 struct StatsEntry *pos;
605 uint32_t flags;
606 uint64_t value;
607 int64_t delta;
608 int changed;
609 int initial_set;
610
611 msize = ntohs (msg->header.size);
612 size = msize - sizeof(struct GNUNET_STATISTICS_SetMessage);
613 GNUNET_assert (size == GNUNET_STRINGS_buffer_tokenize ((const char *) &msg[1],
614 size,
615 2,
616 &service,
617 &name));
618 se = find_subsystem_entry (ce, service);
619 flags = ntohl (msg->flags);
620 value = GNUNET_ntohll (msg->value);
621 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
622 "Received request to update statistic on `%s:%s' (%u) to/by %llu\n",
623 service,
624 name,
625 (unsigned int) flags,
626 (unsigned long long) value);
627 pos = find_stat_entry (se, name);
628 if (NULL != pos)
629 {
630 initial_set = 0;
631 if (0 == (flags & GNUNET_STATISTICS_SETFLAG_RELATIVE))
632 {
633 changed = (pos->value != value);
634 pos->value = value;
635 }
636 else
637 {
638 delta = (int64_t) value;
639 if ((delta < 0) && (pos->value < -delta))
640 {
641 changed = (0 != pos->value);
642 pos->value = 0;
643 }
644 else
645 {
646 changed = (0 != delta);
647 GNUNET_break ((delta <= 0) || (pos->value + delta > pos->value));
648 pos->value += delta;
649 }
650 }
651 if (GNUNET_NO == pos->set)
652 {
653 pos->set = GNUNET_YES;
654 initial_set = 1;
655 }
656 pos->persistent = (0 != (flags & GNUNET_STATISTICS_SETFLAG_PERSISTENT));
657 if (pos != se->stat_head)
658 {
659 /* move to front for faster setting next time! */
660 GNUNET_CONTAINER_DLL_remove (se->stat_head, se->stat_tail, pos);
661 GNUNET_CONTAINER_DLL_insert (se->stat_head, se->stat_tail, pos);
662 }
663 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
664 "Statistic `%s:%s' updated to value %llu (%d).\n",
665 service,
666 name,
667 (unsigned long long) pos->value,
668 pos->persistent);
669 if ((changed) || (1 == initial_set))
670 notify_change (pos);
671 GNUNET_SERVICE_client_continue (ce->client);
672 return;
673 }
674 /* not found, create a new entry */
675 nlen = strlen (name) + 1;
676 pos = GNUNET_malloc (sizeof(struct StatsEntry) + nlen);
677 GNUNET_memcpy (&pos[1], name, nlen);
678 pos->name = (const char *) &pos[1];
679 pos->subsystem = se;
680 if ((0 == (flags & GNUNET_STATISTICS_SETFLAG_RELATIVE)) ||
681 (0 < (int64_t) GNUNET_ntohll (msg->value)))
682 {
683 pos->value = GNUNET_ntohll (msg->value);
684 pos->set = GNUNET_YES;
685 }
686 else
687 {
688 pos->set = GNUNET_NO;
689 }
690 pos->uid = uidgen++;
691 pos->persistent = (0 != (flags & GNUNET_STATISTICS_SETFLAG_PERSISTENT));
692 GNUNET_CONTAINER_DLL_insert (se->stat_head, se->stat_tail, pos);
693 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
694 "New statistic on `%s:%s' with value %llu created.\n",
695 service,
696 name,
697 (unsigned long long) pos->value);
698 if (NULL != ce)
699 GNUNET_SERVICE_client_continue (ce->client);
700}
701
702
703/**
704 * Check integrity of WATCH-message.
705 *
706 * @param cls the `struct ClientEntry *`
707 * @param message the actual message
708 * @return #GNUNET_OK if message is well-formed
709 */
710static int
711check_watch (void *cls, const struct GNUNET_MessageHeader *message)
712{
713 size_t size;
714 const char *service;
715 const char *name;
716
717 size = ntohs (message->size) - sizeof(struct GNUNET_MessageHeader);
718 if (size != GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1],
719 size,
720 2,
721 &service,
722 &name))
723 {
724 GNUNET_break (0);
725 return GNUNET_SYSERR;
726 }
727 return GNUNET_OK;
728}
729
730
731/**
732 * Handle WATCH-message.
733 *
734 * @param cls the `struct ClientEntry *`
735 * @param message the actual message
736 */
737static void
738handle_watch (void *cls, const struct GNUNET_MessageHeader *message)
739{
740 struct ClientEntry *ce = cls;
741 const char *service;
742 const char *name;
743 uint16_t msize;
744 uint16_t size;
745 struct SubsystemEntry *se;
746 struct StatsEntry *pos;
747 struct WatchEntry *we;
748 size_t nlen;
749
750 if (NULL == nc)
751 {
752 GNUNET_SERVICE_client_drop (ce->client);
753 return;
754 }
755 GNUNET_SERVICE_client_mark_monitor (ce->client);
756 msize = ntohs (message->size);
757 size = msize - sizeof(struct GNUNET_MessageHeader);
758 GNUNET_assert (size ==
759 GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1],
760 size,
761 2,
762 &service,
763 &name));
764 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
765 "Received request to watch statistic on `%s:%s'\n",
766 service,
767 name);
768 se = find_subsystem_entry (ce, service);
769 pos = find_stat_entry (se, name);
770 if (NULL == pos)
771 {
772 nlen = strlen (name) + 1;
773 pos = GNUNET_malloc (sizeof(struct StatsEntry) + nlen);
774 GNUNET_memcpy (&pos[1], name, nlen);
775 pos->name = (const char *) &pos[1];
776 pos->subsystem = se;
777 GNUNET_CONTAINER_DLL_insert (se->stat_head, se->stat_tail, pos);
778 pos->uid = uidgen++;
779 pos->set = GNUNET_NO;
780 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
781 "New statistic on `%s:%s' with value %llu created.\n",
782 service,
783 name,
784 (unsigned long long) pos->value);
785 }
786 we = GNUNET_new (struct WatchEntry);
787 we->ce = ce;
788 we->last_value_set = GNUNET_NO;
789 we->wid = ce->max_wid++;
790 GNUNET_CONTAINER_DLL_insert (pos->we_head, pos->we_tail, we);
791 if (0 != pos->value)
792 notify_change (pos);
793 GNUNET_SERVICE_client_continue (ce->client);
794}
795
796
797/**
798 * Handle DISCONNECT-message. Sync to disk and send
799 * back a #GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT_CONFIRM
800 * message.
801 *
802 * @param cls the `struct ClientEntry *`
803 * @param message the actual message
804 */
805static void
806handle_disconnect (void *cls, const struct GNUNET_MessageHeader *message)
807{
808 struct ClientEntry *ce = cls;
809 struct GNUNET_MQ_Envelope *env;
810 struct GNUNET_MessageHeader *msg;
811
812 env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT_CONFIRM);
813 GNUNET_MQ_send (ce->mq, env);
814 GNUNET_SERVICE_client_continue (ce->client);
815}
816
817
818/**
819 * Actually perform the shutdown.
820 */
821static void
822do_shutdown ()
823{
824 struct WatchEntry *we;
825 struct StatsEntry *pos;
826 struct SubsystemEntry *se;
827
828 if (NULL == nc)
829 return;
830 save ();
831 GNUNET_notification_context_destroy (nc);
832 nc = NULL;
833 GNUNET_assert (0 == client_count);
834 while (NULL != (se = sub_head))
835 {
836 GNUNET_CONTAINER_DLL_remove (sub_head, sub_tail, se);
837 while (NULL != (pos = se->stat_head))
838 {
839 GNUNET_CONTAINER_DLL_remove (se->stat_head, se->stat_tail, pos);
840 while (NULL != (we = pos->we_head))
841 {
842 GNUNET_break (0);
843 GNUNET_CONTAINER_DLL_remove (pos->we_head, pos->we_tail, we);
844 GNUNET_free (we);
845 }
846 GNUNET_free (pos);
847 }
848 GNUNET_free (se);
849 }
850}
851
852
853/**
854 * Task run during shutdown.
855 *
856 * @param cls unused
857 */
858static void
859shutdown_task (void *cls)
860{
861 in_shutdown = GNUNET_YES;
862 if (0 != client_count)
863 return;
864 do_shutdown ();
865}
866
867
868/**
869 * A client disconnected. Remove all of its data structure entries.
870 *
871 * @param cls closure, NULL
872 * @param client identification of the client
873 * @param app_cls the `struct ClientEntry *`
874 */
875static void
876client_disconnect_cb (void *cls,
877 struct GNUNET_SERVICE_Client *client,
878 void *app_cls)
879{
880 struct ClientEntry *ce = app_cls;
881 struct WatchEntry *we;
882 struct WatchEntry *wen;
883 struct StatsEntry *pos;
884 struct SubsystemEntry *se;
885
886 client_count--;
887 for (se = sub_head; NULL != se; se = se->next)
888 {
889 for (pos = se->stat_head; NULL != pos; pos = pos->next)
890 {
891 wen = pos->we_head;
892 while (NULL != (we = wen))
893 {
894 wen = we->next;
895 if (we->ce != ce)
896 continue;
897 GNUNET_CONTAINER_DLL_remove (pos->we_head, pos->we_tail, we);
898 GNUNET_free (we);
899 }
900 }
901 }
902 GNUNET_free (ce);
903 if ((0 == client_count) && (GNUNET_YES == in_shutdown))
904 do_shutdown ();
905}
906
907
908/**
909 * We've read a `struct GNUNET_STATISTICS_SetMessage *` from
910 * disk. Check that it is well-formed, and if so pass it to
911 * the handler for set messages.
912 *
913 * @param cls NULL
914 * @param message the message found on disk
915 * @return #GNUNET_OK on success,
916 * #GNUNET_NO to stop further processing (no error)
917 * #GNUNET_SYSERR to stop further processing with error
918 */
919static int
920inject_message (void *cls, const struct GNUNET_MessageHeader *message)
921{
922 uint16_t msize = ntohs (message->size);
923 const struct GNUNET_STATISTICS_SetMessage *sm;
924
925 sm = (const struct GNUNET_STATISTICS_SetMessage *) message;
926 if ((sizeof(struct GNUNET_STATISTICS_SetMessage) > msize) ||
927 (GNUNET_OK != check_set (NULL, sm)))
928 {
929 GNUNET_break (0);
930 return GNUNET_SYSERR;
931 }
932 handle_set (NULL, sm);
933 return GNUNET_OK;
934}
935
936
937/**
938 * Load persistent values from disk. Disk format is exactly the same
939 * format that we also use for setting the values over the network.
940 */
941static void
942load ()
943{
944 char *fn;
945 struct GNUNET_BIO_ReadHandle *rh;
946 uint64_t fsize;
947 char *buf;
948 struct GNUNET_MessageStreamTokenizer *mst;
949
950 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
951 "STATISTICS",
952 "DATABASE",
953 &fn))
954 {
955 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
956 "STATISTICS",
957 "DATABASE");
958 return;
959 }
960 if ((GNUNET_OK !=
961 GNUNET_DISK_file_size (fn, &fsize, GNUNET_NO, GNUNET_YES)) ||
962 (0 == fsize))
963 {
964 GNUNET_free (fn);
965 return;
966 }
967 buf = GNUNET_malloc (fsize);
968 rh = GNUNET_BIO_read_open_file (fn);
969 if (! rh)
970 {
971 GNUNET_free (buf);
972 GNUNET_free (fn);
973 return;
974 }
975 if (GNUNET_OK != GNUNET_BIO_read (rh, fn, buf, fsize))
976 {
977 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "read", fn);
978 GNUNET_break (GNUNET_OK == GNUNET_BIO_read_close (rh, NULL));
979 GNUNET_free (buf);
980 GNUNET_free (fn);
981 return;
982 }
983 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
984 _ ("Loading %llu bytes of statistics from `%s'\n"),
985 (unsigned long long) fsize,
986 fn);
987 mst = GNUNET_MST_create (&inject_message, NULL);
988 GNUNET_break (
989 GNUNET_OK ==
990 GNUNET_MST_from_buffer (mst, buf, (size_t) fsize, GNUNET_YES, GNUNET_NO));
991 GNUNET_MST_destroy (mst);
992 GNUNET_free (buf);
993 GNUNET_break (GNUNET_OK == GNUNET_BIO_read_close (rh, NULL));
994 GNUNET_free (fn);
995}
996
997
998/**
999 * Process statistics requests.
1000 *
1001 * @param cls closure
1002 * @param c configuration to use
1003 * @param service the initialized service
1004 */
1005static void
1006run (void *cls,
1007 const struct GNUNET_CONFIGURATION_Handle *c,
1008 struct GNUNET_SERVICE_Handle *service)
1009{
1010 cfg = c;
1011 nc = GNUNET_notification_context_create (16);
1012 load ();
1013 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
1014}
1015
1016
1017/**
1018 * Define "main" method using service macro.
1019 */
1020GNUNET_SERVICE_MAIN (
1021 "statistics",
1022 GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN,
1023 &run,
1024 &client_connect_cb,
1025 &client_disconnect_cb,
1026 NULL,
1027 GNUNET_MQ_hd_var_size (set,
1028 GNUNET_MESSAGE_TYPE_STATISTICS_SET,
1029 struct GNUNET_STATISTICS_SetMessage,
1030 NULL),
1031 GNUNET_MQ_hd_var_size (get,
1032 GNUNET_MESSAGE_TYPE_STATISTICS_GET,
1033 struct GNUNET_MessageHeader,
1034 NULL),
1035 GNUNET_MQ_hd_var_size (watch,
1036 GNUNET_MESSAGE_TYPE_STATISTICS_WATCH,
1037 struct GNUNET_MessageHeader,
1038 NULL),
1039 GNUNET_MQ_hd_fixed_size (disconnect,
1040 GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT,
1041 struct GNUNET_MessageHeader,
1042 NULL),
1043 GNUNET_MQ_handler_end ());
1044
1045
1046#if defined(__linux__) && defined(__GLIBC__)
1047#include <malloc.h>
1048
1049/**
1050 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
1051 */
1052void __attribute__ ((constructor))
1053GNUNET_STATISTICS_memory_init ()
1054{
1055 mallopt (M_TRIM_THRESHOLD, 4 * 1024);
1056 mallopt (M_TOP_PAD, 1 * 1024);
1057 malloc_trim (0);
1058}
1059
1060
1061#endif
1062
1063
1064/* end of gnunet-service-statistics.c */