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