aboutsummaryrefslogtreecommitdiff
path: root/src/statistics/statistics_api.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/statistics/statistics_api.c')
-rw-r--r--src/statistics/statistics_api.c688
1 files changed, 688 insertions, 0 deletions
diff --git a/src/statistics/statistics_api.c b/src/statistics/statistics_api.c
new file mode 100644
index 000000000..f68cf9396
--- /dev/null
+++ b/src/statistics/statistics_api.c
@@ -0,0 +1,688 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file statistics/statistics_api.c
23 * @brief API of the statistics service
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_client_lib.h"
28#include "gnunet_protocols.h"
29#include "gnunet_server_lib.h"
30#include "gnunet_statistics_service.h"
31#include "gnunet_strings_lib.h"
32#include "statistics.h"
33
34#define SET_TRANSMIT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
35
36
37/**
38 * Types of actions.
39 */
40enum ActionType
41{
42 ACTION_GET,
43 ACTION_SET,
44 ACTION_UPDATE
45};
46
47
48/**
49 * Linked list of things we still need to do.
50 */
51struct ActionItem
52{
53 /**
54 * This is a linked list.
55 */
56 struct ActionItem *next;
57
58 /**
59 * What subsystem is this action about? (can be NULL)
60 */
61 char *subsystem;
62
63 /**
64 * What value is this action about? (can be NULL)
65 */
66 char *name;
67
68 /**
69 * Continuation to call once action is complete.
70 */
71 GNUNET_STATISTICS_Callback cont;
72
73 /**
74 * Function to call (for GET actions only).
75 */
76 GNUNET_STATISTICS_Iterator proc;
77
78 /**
79 * Closure for proc and cont.
80 */
81 void *cls;
82
83 /**
84 * Timeout for this action.
85 */
86 struct GNUNET_TIME_Absolute timeout;
87
88 /**
89 * Associated value.
90 */
91 unsigned long long value;
92
93 /**
94 * Flag for SET/UPDATE actions.
95 */
96 int make_persistent;
97
98 /**
99 * Has the current iteration been aborted; for GET actions.
100 */
101 int aborted;
102
103 /**
104 * Is this a GET, SET or UPDATE?
105 */
106 enum ActionType type;
107
108 /**
109 * Size of the message that we will be transmitting.
110 */
111 uint16_t msize;
112
113};
114
115
116/**
117 * Handle for the service.
118 */
119struct GNUNET_STATISTICS_Handle
120{
121 /**
122 * Our scheduler.
123 */
124 struct GNUNET_SCHEDULER_Handle *sched;
125
126 /**
127 * Name of our subsystem.
128 */
129 char *subsystem;
130
131 /**
132 * Configuration to use.
133 */
134 struct GNUNET_CONFIGURATION_Handle *cfg;
135
136 /**
137 * Socket (if available).
138 */
139 struct GNUNET_CLIENT_Connection *client;
140
141 /**
142 * Head of the linked list of pending actions (first action
143 * to be performed).
144 */
145 struct ActionItem *action_head;
146
147 /**
148 * Tail of the linked list of actions (for fast append).
149 */
150 struct ActionItem *action_tail;
151
152 /**
153 * Action we are currently busy with (action request has been
154 * transmitted, we're now receiving the response from the
155 * service).
156 */
157 struct ActionItem *current;
158
159 /**
160 * Should this handle be destroyed once we've processed
161 * all actions?
162 */
163 int do_destroy;
164
165};
166
167
168/**
169 * Try to (re)connect to the statistics service.
170 *
171 * @return GNUNET_YES on success, GNUNET_NO on failure.
172 */
173static int
174try_connect (struct GNUNET_STATISTICS_Handle *ret)
175{
176 if (ret->client != NULL)
177 return GNUNET_OK;
178 ret->client = GNUNET_CLIENT_connect (ret->sched, "statistics", ret->cfg);
179 if (ret->client != NULL)
180 return GNUNET_YES;
181 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
182 _("Failed to connect to statistics service!\n"));
183 return GNUNET_NO;
184}
185
186
187/**
188 * Free memory associated with the given action item.
189 */
190static void
191free_action_item (struct ActionItem *ai)
192{
193 GNUNET_free_non_null (ai->subsystem);
194 GNUNET_free_non_null (ai->name);
195 GNUNET_free (ai);
196}
197
198
199/**
200 * Get handle for the statistics service.
201 *
202 * @param subsystem name of subsystem using the service
203 * @param cfg services configuration in use
204 * @return handle to use
205 */
206struct GNUNET_STATISTICS_Handle *
207GNUNET_STATISTICS_create (struct GNUNET_SCHEDULER_Handle *sched,
208 const char *subsystem,
209 struct GNUNET_CONFIGURATION_Handle *cfg)
210{
211 struct GNUNET_STATISTICS_Handle *ret;
212
213 GNUNET_assert (subsystem != NULL);
214 GNUNET_assert (sched != NULL);
215 GNUNET_assert (cfg != NULL);
216 ret = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_Handle));
217 ret->sched = sched;
218 ret->cfg = cfg;
219 ret->subsystem = GNUNET_strdup (subsystem);
220 try_connect (ret);
221 return ret;
222}
223
224
225/**
226 * Actually free the handle.
227 */
228static void
229do_destroy (struct GNUNET_STATISTICS_Handle *h)
230{
231 GNUNET_assert (h->action_head == NULL);
232 GNUNET_assert (h->current == NULL);
233 if (h->client != NULL)
234 {
235 GNUNET_CLIENT_disconnect (h->client);
236 h->client = NULL;
237 }
238 GNUNET_free (h->subsystem);
239 GNUNET_free (h);
240}
241
242
243/**
244 * Destroy a handle (free all state associated with
245 * it).
246 */
247void
248GNUNET_STATISTICS_destroy (struct GNUNET_STATISTICS_Handle *handle)
249{
250 GNUNET_assert (handle->do_destroy == GNUNET_NO);
251 if ((handle->action_head != NULL) || (handle->current != NULL))
252 {
253 handle->do_destroy = GNUNET_YES;
254 return;
255 }
256 do_destroy (handle);
257}
258
259
260/**
261 * Process the message.
262 *
263 * @return GNUNET_OK if the message was well-formed
264 */
265static int
266process_message (struct GNUNET_STATISTICS_Handle *h,
267 const struct GNUNET_MessageHeader *msg)
268{
269 char *service;
270 char *name;
271 const struct GNUNET_STATISTICS_ReplyMessage *smsg;
272 uint16_t size;
273
274 if (h->current->aborted)
275 return GNUNET_OK; /* don't bother */
276 size = ntohs (msg->size);
277 if (size < sizeof (struct GNUNET_STATISTICS_ReplyMessage))
278 {
279 GNUNET_break (0);
280 return GNUNET_SYSERR;
281 }
282 smsg = (const struct GNUNET_STATISTICS_ReplyMessage *) msg;
283 size -= sizeof (struct GNUNET_STATISTICS_ReplyMessage);
284 if (size != GNUNET_STRINGS_buffer_tokenize ((const char *) &smsg[1],
285 size, 2, &service, &name))
286 {
287 GNUNET_break (0);
288 return GNUNET_SYSERR;
289 }
290#if DEBUG_STATISTICS
291 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
292 "Received valid statistic on `%s:%s': %llu\n",
293 service, name, GNUNET_ntohll (smsg->value));
294#endif
295 if (GNUNET_OK !=
296 h->current->proc (h->current->cls,
297 service,
298 name,
299 GNUNET_ntohll (smsg->value),
300 0 !=
301 (ntohl (smsg->uid) & GNUNET_STATISTICS_PERSIST_BIT)))
302 {
303#if DEBUG_STATISTICS
304 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
305 "Processing of remaining statistics aborted by client.\n");
306#endif
307 h->current->aborted = GNUNET_YES;
308 }
309 return GNUNET_OK;
310}
311
312
313
314/**
315 * Schedule the next action to be performed.
316 */
317static void schedule_action (struct GNUNET_STATISTICS_Handle *h);
318
319
320/**
321 * GET processing is complete, tell client about it.
322 */
323static void
324finish (struct GNUNET_STATISTICS_Handle *h, int code)
325{
326 struct ActionItem *pos = h->current;
327 h->current = NULL;
328 schedule_action (h);
329 if (pos->cont != NULL)
330 pos->cont (pos->cls, code);
331 free_action_item (pos);
332}
333
334
335/**
336 * Function called with messages from stats service.
337 *
338 * @param cls closure
339 * @param msg message received, NULL on timeout or fatal error
340 */
341static void
342receive_stats (void *cls, const struct GNUNET_MessageHeader *msg)
343{
344 struct GNUNET_STATISTICS_Handle *h = cls;
345
346 if (msg == NULL)
347 {
348 GNUNET_CLIENT_disconnect (h->client);
349 h->client = NULL;
350 GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
351 _
352 ("Error receiving statistics from service, is the service running?\n"));
353 finish (h, GNUNET_SYSERR);
354 return;
355 }
356 switch (ntohs (msg->type))
357 {
358 case GNUNET_MESSAGE_TYPE_STATISTICS_END:
359#if DEBUG_STATISTICS
360 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
361 "Received end of statistics marker\n");
362#endif
363 finish (h, GNUNET_OK);
364 return;
365 case GNUNET_MESSAGE_TYPE_STATISTICS_VALUE:
366 if (GNUNET_OK == process_message (h, msg))
367 {
368 /* finally, look for more! */
369 GNUNET_CLIENT_receive (h->client,
370 &receive_stats,
371 h,
372 GNUNET_TIME_absolute_get_remaining (h->
373 current->
374 timeout));
375 return;
376 }
377 GNUNET_break (0);
378 break;
379 default:
380 GNUNET_break (0);
381 break;
382 }
383 GNUNET_CLIENT_disconnect (h->client);
384 h->client = NULL;
385 finish (h, GNUNET_SYSERR);
386}
387
388
389/**
390 * Transmit a GET request (and if successful, start to receive
391 * the response).
392 */
393static size_t
394transmit_get (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf)
395{
396 struct GNUNET_MessageHeader *hdr;
397 size_t slen1;
398 size_t slen2;
399 uint16_t msize;
400
401 if (buf == NULL)
402 {
403 /* timeout / error */
404 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
405 _("Transmission of request for statistics failed!\n"));
406 finish (handle, GNUNET_SYSERR);
407 return 0;
408 }
409 slen1 = strlen (handle->current->subsystem) + 1;
410 slen2 = strlen (handle->current->name) + 1;
411 msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
412 GNUNET_assert (msize <= size);
413 hdr = (struct GNUNET_MessageHeader *) buf;
414 hdr->size = htons (msize);
415 hdr->type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_GET);
416 GNUNET_assert (slen1 + slen2 ==
417 GNUNET_STRINGS_buffer_fill ((char *) &hdr[1],
418 slen1 + slen2,
419 2,
420 handle->current->subsystem,
421 handle->current->name));
422 GNUNET_CLIENT_receive (handle->client,
423 &receive_stats,
424 handle,
425 GNUNET_TIME_absolute_get_remaining (handle->current->
426 timeout));
427 return msize;
428}
429
430
431
432/**
433 * Transmit a SET/UPDATE request.
434 */
435static size_t
436transmit_set (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf)
437{
438 struct GNUNET_STATISTICS_SetMessage *r;
439 size_t slen;
440 size_t nlen;
441 size_t nsize;
442
443 if (NULL == buf)
444 {
445 finish (handle, GNUNET_SYSERR);
446 return 0;
447 }
448
449 slen = strlen (handle->current->subsystem) + 1;
450 nlen = strlen (handle->current->name) + 1;
451 nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
452 if (size < nsize)
453 {
454 GNUNET_break (0);
455 finish (handle, GNUNET_SYSERR);
456 return 0;
457 }
458 r = buf;
459 r->header.size = htons (nsize);
460 r->header.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_SET);
461 r->flags = 0;
462 r->value = GNUNET_htonll (handle->current->value);
463 if (handle->current->make_persistent)
464 r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_PERSISTENT);
465 if (handle->current->type == ACTION_UPDATE)
466 r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_RELATIVE);
467 GNUNET_assert (slen + nlen ==
468 GNUNET_STRINGS_buffer_fill ((char *) &r[1],
469 slen + nlen,
470 2,
471 handle->current->subsystem,
472 handle->current->name));
473 finish (handle, GNUNET_OK);
474 return nsize;
475}
476
477
478static size_t
479transmit_action (void *cls, size_t size, void *buf)
480{
481 struct GNUNET_STATISTICS_Handle *handle = cls;
482 size_t ret;
483
484 switch (handle->current->type)
485 {
486 case ACTION_GET:
487 ret = transmit_get (handle, size, buf);
488 break;
489 case ACTION_SET:
490 case ACTION_UPDATE:
491 ret = transmit_set (handle, size, buf);
492 break;
493 }
494 return ret;
495}
496
497
498/**
499 * Schedule the next action to be performed.
500 */
501static void
502schedule_action (struct GNUNET_STATISTICS_Handle *h)
503{
504 struct GNUNET_TIME_Relative timeout;
505
506 if (h->current != NULL)
507 return; /* action already pending */
508 if (GNUNET_YES != try_connect (h))
509 {
510 finish (h, GNUNET_SYSERR);
511 return;
512 }
513
514 /* schedule next action */
515 h->current = h->action_head;
516 if (NULL == h->current)
517 {
518 /* no pending network action, check destroy! */
519 if (h->do_destroy != GNUNET_YES)
520 return;
521 do_destroy (h);
522 return;
523 }
524 h->action_head = h->action_head->next;
525 if (NULL == h->action_head)
526 h->action_tail = NULL;
527 h->current->next = NULL;
528
529 timeout = GNUNET_TIME_absolute_get_remaining (h->current->timeout);
530 if (NULL ==
531 GNUNET_CLIENT_notify_transmit_ready (h->client,
532 h->current->msize,
533 timeout, &transmit_action, h))
534 {
535 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
536 "Failed to transmit request to statistics service.\n");
537 finish (h, GNUNET_SYSERR);
538 }
539}
540
541
542static void
543insert_ai (struct GNUNET_STATISTICS_Handle *h, struct ActionItem *ai)
544{
545 if (h->action_tail == NULL)
546 {
547 h->action_head = ai;
548 h->action_tail = ai;
549 schedule_action (h);
550 }
551 else
552 {
553 h->action_tail->next = ai;
554 h->action_tail = ai;
555 }
556}
557
558
559/**
560 * Get statistic from the peer.
561 *
562 * @param handle identification of the statistics service
563 * @param subsystem limit to the specified subsystem, NULL for our subsystem
564 * @param name name of the statistic value, NULL for all values
565 * @param timeout after how long should we give up (and call
566 * cont with an error code)?
567 * @param cont continuation to call when done (can be NULL)
568 * @param proc function to call on each value
569 * @param cls closure for cont and proc
570 */
571void
572GNUNET_STATISTICS_get (struct GNUNET_STATISTICS_Handle *handle,
573 const char *subsystem,
574 const char *name,
575 struct GNUNET_TIME_Relative timeout,
576 GNUNET_STATISTICS_Callback cont,
577 GNUNET_STATISTICS_Iterator proc, void *cls)
578{
579 size_t slen1;
580 size_t slen2;
581 struct ActionItem *ai;
582
583 GNUNET_assert (handle != NULL);
584 GNUNET_assert (proc != NULL);
585 if (GNUNET_YES != try_connect (handle))
586 {
587 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
588 "Failed to connect to statistics service, can not get value `%s:%s'.\n",
589 strlen (subsystem) ? subsystem : "*",
590 strlen (name) ? name : "*");
591 cont (cls, GNUNET_SYSERR);
592 return;
593 }
594 if (subsystem == NULL)
595 subsystem = "";
596 if (name == NULL)
597 name = "";
598 slen1 = strlen (subsystem);
599 slen2 = strlen (name);
600 GNUNET_assert (slen1 + slen2 + sizeof (struct GNUNET_MessageHeader) <
601 GNUNET_SERVER_MAX_MESSAGE_SIZE);
602 ai = GNUNET_malloc (sizeof (struct ActionItem));
603 ai->subsystem = GNUNET_strdup (subsystem);
604 ai->name = GNUNET_strdup (name);
605 ai->cont = cont;
606 ai->proc = proc;
607 ai->cls = cls;
608 ai->timeout = GNUNET_TIME_relative_to_absolute (timeout);
609 ai->type = ACTION_GET;
610 ai->msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
611 insert_ai (handle, ai);
612}
613
614
615static void
616add_setter_action (struct GNUNET_STATISTICS_Handle *h,
617 const char *name,
618 int make_persistent,
619 unsigned long long value, enum ActionType type)
620{
621 struct ActionItem *ai;
622 size_t slen;
623 size_t nlen;
624 size_t nsize;
625
626 GNUNET_assert (h != NULL);
627 GNUNET_assert (name != NULL);
628 if (GNUNET_YES != try_connect (h))
629 return;
630 slen = strlen (h->subsystem) + 1;
631 nlen = strlen (name) + 1;
632 nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
633 if (nsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
634 {
635 GNUNET_break (0);
636 return;
637 }
638 ai = GNUNET_malloc (sizeof (struct ActionItem));
639 ai->subsystem = GNUNET_strdup (h->subsystem);
640 ai->name = GNUNET_strdup (name);
641 ai->timeout = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT);
642 ai->make_persistent = make_persistent;
643 ai->msize = nsize;
644 ai->value = value;
645 ai->type = type;
646 insert_ai (h, ai);
647 schedule_action (h);
648}
649
650
651/**
652 * Set statistic value for the peer. Will always use our
653 * subsystem (the argument used when "handle" was created).
654 *
655 * @param handle identification of the statistics service
656 * @param name name of the statistic value
657 * @param value new value to set
658 * @param make_persistent should the value be kept across restarts?
659 */
660void
661GNUNET_STATISTICS_set (struct GNUNET_STATISTICS_Handle *handle,
662 const char *name,
663 unsigned long long value, int make_persistent)
664{
665 add_setter_action (handle, name, make_persistent, value, ACTION_SET);
666}
667
668
669/**
670 * Set statistic value for the peer. Will always use our
671 * subsystem (the argument used when "handle" was created).
672 *
673 * @param handle identification of the statistics service
674 * @param name name of the statistic value
675 * @param delta change in value (added to existing value)
676 * @param make_persistent should the value be kept across restarts?
677 */
678void
679GNUNET_STATISTICS_update (struct GNUNET_STATISTICS_Handle *handle,
680 const char *name,
681 long long delta, int make_persistent)
682{
683 add_setter_action (handle, name, make_persistent,
684 (unsigned long long) delta, ACTION_UPDATE);
685}
686
687
688/* end of statistics_api.c */