diff options
author | Martin Schanzenbach <schanzen@gnunet.org> | 2023-10-18 19:35:11 +0200 |
---|---|---|
committer | Martin Schanzenbach <schanzen@gnunet.org> | 2023-10-18 19:35:11 +0200 |
commit | ddfee3f564bff9c5d5719af3132d7869b8783ec4 (patch) | |
tree | e6fd7801fe6808797f3418bf081ab68d5a5ec27b /src/statistics/gnunet-service-statistics.c | |
parent | 852718c2473e41bc01ada0d53ad93c7da78e6ec8 (diff) | |
download | gnunet-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.c | 1059 |
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 | */ | ||
36 | struct 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 | */ | ||
75 | struct SubsystemEntry; | ||
76 | |||
77 | |||
78 | /** | ||
79 | * Entry in the statistics list. | ||
80 | */ | ||
81 | struct 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 | */ | ||
141 | struct 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 | */ | ||
174 | struct 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 | */ | ||
201 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
202 | |||
203 | /** | ||
204 | * Head of linked list of subsystems with active statistics. | ||
205 | */ | ||
206 | static struct SubsystemEntry *sub_head; | ||
207 | |||
208 | /** | ||
209 | * Tail of linked list of subsystems with active statistics. | ||
210 | */ | ||
211 | static struct SubsystemEntry *sub_tail; | ||
212 | |||
213 | /** | ||
214 | * Number of connected clients. | ||
215 | */ | ||
216 | static unsigned int client_count; | ||
217 | |||
218 | /** | ||
219 | * Our notification context. | ||
220 | */ | ||
221 | static struct GNUNET_NotificationContext *nc; | ||
222 | |||
223 | /** | ||
224 | * Counter used to generate unique values. | ||
225 | */ | ||
226 | static uint32_t uidgen; | ||
227 | |||
228 | /** | ||
229 | * Set to #GNUNET_YES if we are shutting down as soon as possible. | ||
230 | */ | ||
231 | static int in_shutdown; | ||
232 | |||
233 | |||
234 | /** | ||
235 | * Write persistent statistics to disk. | ||
236 | */ | ||
237 | static void | ||
238 | save () | ||
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 | */ | ||
328 | static void | ||
329 | transmit (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 | */ | ||
365 | static void * | ||
366 | client_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 | */ | ||
388 | static int | ||
389 | check_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 | */ | ||
415 | static void | ||
416 | handle_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 | */ | ||
464 | static void | ||
465 | notify_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 | */ | ||
502 | static struct SubsystemEntry * | ||
503 | find_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 | */ | ||
543 | static struct StatsEntry * | ||
544 | find_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 | */ | ||
562 | static int | ||
563 | check_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 | */ | ||
589 | static void | ||
590 | handle_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 | */ | ||
705 | static int | ||
706 | check_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 | */ | ||
732 | static void | ||
733 | handle_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 | */ | ||
800 | static void | ||
801 | handle_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 | */ | ||
816 | static void | ||
817 | do_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 | */ | ||
853 | static void | ||
854 | shutdown_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 | */ | ||
870 | static void | ||
871 | client_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 | */ | ||
914 | static int | ||
915 | inject_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 | */ | ||
936 | static void | ||
937 | load () | ||
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 | */ | ||
1000 | static void | ||
1001 | run (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 | */ | ||
1015 | GNUNET_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 | */ | ||
1047 | void __attribute__ ((constructor)) | ||
1048 | GNUNET_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 */ | ||