2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
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.
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.
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.
22 * @file statistics/statistics_api.c
23 * @brief API of the statistics service
24 * @author Christian Grothoff
27 #include "gnunet_client_lib.h"
28 #include "gnunet_container_lib.h"
29 #include "gnunet_protocols.h"
30 #include "gnunet_server_lib.h"
31 #include "gnunet_statistics_service.h"
32 #include "gnunet_strings_lib.h"
33 #include "statistics.h"
36 * How long do we wait until a statistics request for setting
37 * a value times out? (The update will be lost if the
38 * service does not react within this timeframe).
40 #define SET_TRANSMIT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 250)
55 * Linked list of things we still need to do.
57 struct GNUNET_STATISTICS_GetHandle
61 * This is a doubly linked list.
63 struct GNUNET_STATISTICS_GetHandle *next;
66 * This is a doubly linked list.
68 struct GNUNET_STATISTICS_GetHandle *prev;
71 * Main statistics handle.
73 struct GNUNET_STATISTICS_Handle *sh;
76 * What subsystem is this action about? (can be NULL)
81 * What value is this action about? (can be NULL)
86 * Continuation to call once action is complete.
88 GNUNET_STATISTICS_Callback cont;
91 * Function to call (for GET actions only).
93 GNUNET_STATISTICS_Iterator proc;
96 * Closure for proc and cont.
101 * Timeout for this action.
103 struct GNUNET_TIME_Absolute timeout;
111 * Flag for SET/UPDATE actions.
116 * Has the current iteration been aborted; for GET actions.
121 * Is this a GET, SET or UPDATE?
123 enum ActionType type;
126 * Size of the message that we will be transmitting.
134 * Handle for the service.
136 struct GNUNET_STATISTICS_Handle
141 struct GNUNET_SCHEDULER_Handle *sched;
144 * Name of our subsystem.
149 * Configuration to use.
151 const struct GNUNET_CONFIGURATION_Handle *cfg;
154 * Socket (if available).
156 struct GNUNET_CLIENT_Connection *client;
159 * Currently pending transmission request.
161 struct GNUNET_CLIENT_TransmitHandle *th;
164 * Head of the linked list of pending actions (first action
167 struct GNUNET_STATISTICS_GetHandle *action_head;
170 * Tail of the linked list of actions (for fast append).
172 struct GNUNET_STATISTICS_GetHandle *action_tail;
175 * Action we are currently busy with (action request has been
176 * transmitted, we're now receiving the response from the
179 struct GNUNET_STATISTICS_GetHandle *current;
182 * Should this handle auto-destruct once all actions have
191 * Try to (re)connect to the statistics service.
193 * @return GNUNET_YES on success, GNUNET_NO on failure.
196 try_connect (struct GNUNET_STATISTICS_Handle *ret)
198 if (ret->client != NULL)
200 ret->client = GNUNET_CLIENT_connect (ret->sched, "statistics", ret->cfg);
201 if (ret->client != NULL)
204 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
205 _("Failed to connect to statistics service!\n"));
212 * Free memory associated with the given action item.
215 free_action_item (struct GNUNET_STATISTICS_GetHandle *ai)
217 GNUNET_free_non_null (ai->subsystem);
218 GNUNET_free_non_null (ai->name);
224 * Schedule the next action to be performed.
226 static void schedule_action (struct GNUNET_STATISTICS_Handle *h);
230 * GET processing is complete, tell client about it.
233 finish (struct GNUNET_STATISTICS_Handle *h, int code)
235 struct GNUNET_STATISTICS_GetHandle *pos = h->current;
240 if (pos->cont != NULL)
241 pos->cont (pos->cls, code);
242 free_action_item (pos);
248 * Process the message.
250 * @return GNUNET_OK if the message was well-formed
253 process_message (struct GNUNET_STATISTICS_Handle *h,
254 const struct GNUNET_MessageHeader *msg)
258 const struct GNUNET_STATISTICS_ReplyMessage *smsg;
261 if (h->current->aborted)
262 return GNUNET_OK; /* don't bother */
263 size = ntohs (msg->size);
264 if (size < sizeof (struct GNUNET_STATISTICS_ReplyMessage))
267 return GNUNET_SYSERR;
269 smsg = (const struct GNUNET_STATISTICS_ReplyMessage *) msg;
270 size -= sizeof (struct GNUNET_STATISTICS_ReplyMessage);
271 if (size != GNUNET_STRINGS_buffer_tokenize ((const char *) &smsg[1],
272 size, 2, &service, &name))
275 return GNUNET_SYSERR;
278 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
279 "Received valid statistic on `%s:%s': %llu\n",
280 service, name, GNUNET_ntohll (smsg->value));
283 h->current->proc (h->current->cls,
286 GNUNET_ntohll (smsg->value),
288 (ntohl (smsg->uid) & GNUNET_STATISTICS_PERSIST_BIT)))
291 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
292 "Processing of remaining statistics aborted by client.\n");
294 h->current->aborted = GNUNET_YES;
301 * Function called with messages from stats service.
304 * @param msg message received, NULL on timeout or fatal error
307 receive_stats (void *cls, const struct GNUNET_MessageHeader *msg)
309 struct GNUNET_STATISTICS_Handle *h = cls;
313 if (NULL != h->client)
315 GNUNET_CLIENT_disconnect (h->client, GNUNET_NO);
319 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
320 "Error receiving statistics from service, is the service running?\n" );
322 finish (h, GNUNET_SYSERR);
325 switch (ntohs (msg->type))
327 case GNUNET_MESSAGE_TYPE_STATISTICS_END:
329 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
330 "Received end of statistics marker\n");
332 finish (h, GNUNET_OK);
334 case GNUNET_MESSAGE_TYPE_STATISTICS_VALUE:
335 if (GNUNET_OK == process_message (h, msg))
337 /* finally, look for more! */
338 GNUNET_CLIENT_receive (h->client,
341 GNUNET_TIME_absolute_get_remaining
342 (h->current->timeout));
351 if (NULL != h->client)
353 GNUNET_CLIENT_disconnect (h->client, GNUNET_NO);
356 finish (h, GNUNET_SYSERR);
361 * Transmit a GET request (and if successful, start to receive
365 transmit_get (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf)
367 struct GNUNET_MessageHeader *hdr;
374 /* timeout / error */
376 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
377 "Transmission of request for statistics failed!\n");
379 finish (handle, GNUNET_SYSERR);
382 slen1 = strlen (handle->current->subsystem) + 1;
383 slen2 = strlen (handle->current->name) + 1;
384 msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
385 GNUNET_assert (msize <= size);
386 hdr = (struct GNUNET_MessageHeader *) buf;
387 hdr->size = htons (msize);
388 hdr->type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_GET);
389 GNUNET_assert (slen1 + slen2 ==
390 GNUNET_STRINGS_buffer_fill ((char *) &hdr[1],
393 handle->current->subsystem,
394 handle->current->name));
395 GNUNET_CLIENT_receive (handle->client,
398 GNUNET_TIME_absolute_get_remaining (handle->
406 * Transmit a SET/UPDATE request.
409 transmit_set (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf)
411 struct GNUNET_STATISTICS_SetMessage *r;
418 finish (handle, GNUNET_SYSERR);
422 slen = strlen (handle->current->subsystem) + 1;
423 nlen = strlen (handle->current->name) + 1;
424 nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
428 finish (handle, GNUNET_SYSERR);
432 r->header.size = htons (nsize);
433 r->header.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_SET);
435 r->value = GNUNET_htonll (handle->current->value);
436 if (handle->current->make_persistent)
437 r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_PERSISTENT);
438 if (handle->current->type == ACTION_UPDATE)
439 r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_RELATIVE);
440 GNUNET_assert (slen + nlen ==
441 GNUNET_STRINGS_buffer_fill ((char *) &r[1],
444 handle->current->subsystem,
445 handle->current->name));
446 finish (handle, GNUNET_OK);
452 transmit_action (void *cls, size_t size, void *buf)
454 struct GNUNET_STATISTICS_Handle *handle = cls;
458 switch (handle->current->type)
461 ret = transmit_get (handle, size, buf);
465 ret = transmit_set (handle, size, buf);
477 * Get handle for the statistics service.
479 * @param sched scheduler to use
480 * @param subsystem name of subsystem using the service
481 * @param cfg services configuration in use
482 * @return handle to use
484 struct GNUNET_STATISTICS_Handle *
485 GNUNET_STATISTICS_create (struct GNUNET_SCHEDULER_Handle *sched,
486 const char *subsystem,
487 const struct GNUNET_CONFIGURATION_Handle *cfg)
489 struct GNUNET_STATISTICS_Handle *ret;
491 GNUNET_assert (subsystem != NULL);
492 GNUNET_assert (sched != NULL);
493 GNUNET_assert (cfg != NULL);
494 ret = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_Handle));
497 ret->subsystem = GNUNET_strdup (subsystem);
504 * Destroy a handle (free all state associated with
507 * @param h statistics handle to destroy
508 * @param sync_first set to GNUNET_YES if pending SET requests should
512 GNUNET_STATISTICS_destroy (struct GNUNET_STATISTICS_Handle *h,
515 struct GNUNET_STATISTICS_GetHandle *pos;
516 struct GNUNET_STATISTICS_GetHandle *next;
517 struct GNUNET_STATISTICS_GetHandle *prev;
518 struct GNUNET_TIME_Relative timeout;
522 if (h->current != NULL)
524 if (h->current->type == ACTION_GET)
526 GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
528 free_action_item (h->current);
532 pos = h->action_head;
537 if (pos->type == ACTION_GET)
540 h->action_head = next;
543 free_action_item (pos);
551 h->action_tail = prev;
552 if (h->current == NULL)
554 h->current = h->action_head;
555 if (h->action_head != NULL)
557 h->action_head = h->action_head->next;
558 if (h->action_head == NULL)
559 h->action_tail = NULL;
562 h->do_destroy = GNUNET_YES;
563 if ( (h->current != NULL) &&
566 timeout = GNUNET_TIME_absolute_get_remaining (h->current->timeout);
567 h->th = GNUNET_CLIENT_notify_transmit_ready (h->client,
571 &transmit_action, h);
572 GNUNET_assert (NULL != h->th);
579 GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
582 if (h->current != NULL)
583 free_action_item (h->current);
584 while (NULL != (pos = h->action_head))
586 h->action_head = pos->next;
587 free_action_item (pos);
589 if (h->client != NULL)
591 GNUNET_CLIENT_disconnect (h->client, GNUNET_YES);
594 GNUNET_free (h->subsystem);
601 * Schedule the next action to be performed.
604 schedule_action (struct GNUNET_STATISTICS_Handle *h)
606 struct GNUNET_TIME_Relative timeout;
608 if (h->current != NULL)
609 return; /* action already pending */
610 if (GNUNET_YES != try_connect (h))
612 finish (h, GNUNET_SYSERR);
616 /* schedule next action */
617 h->current = h->action_head;
618 if (NULL == h->current)
622 h->do_destroy = GNUNET_NO;
623 GNUNET_STATISTICS_destroy (h, GNUNET_YES);
627 GNUNET_CONTAINER_DLL_remove (h->action_head,
630 timeout = GNUNET_TIME_absolute_get_remaining (h->current->timeout);
632 (h->th = GNUNET_CLIENT_notify_transmit_ready (h->client,
636 &transmit_action, h)))
639 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
640 "Failed to transmit request to statistics service.\n");
642 finish (h, GNUNET_SYSERR);
648 insert_ai (struct GNUNET_STATISTICS_Handle *h, struct GNUNET_STATISTICS_GetHandle *ai)
650 GNUNET_CONTAINER_DLL_insert_after (h->action_head,
654 if (h->action_head == ai)
660 * Get statistic from the peer.
662 * @param handle identification of the statistics service
663 * @param subsystem limit to the specified subsystem, NULL for our subsystem
664 * @param name name of the statistic value, NULL for all values
665 * @param timeout after how long should we give up (and call
666 * cont with an error code)?
667 * @param cont continuation to call when done (can be NULL)
668 * @param proc function to call on each value
669 * @param cls closure for cont and proc
670 * @return NULL on error
672 struct GNUNET_STATISTICS_GetHandle *
673 GNUNET_STATISTICS_get (struct GNUNET_STATISTICS_Handle *handle,
674 const char *subsystem,
676 struct GNUNET_TIME_Relative timeout,
677 GNUNET_STATISTICS_Callback cont,
678 GNUNET_STATISTICS_Iterator proc, void *cls)
682 struct GNUNET_STATISTICS_GetHandle *ai;
684 GNUNET_assert (handle != NULL);
685 GNUNET_assert (proc != NULL);
686 GNUNET_assert (GNUNET_NO == handle->do_destroy);
687 if (GNUNET_YES != try_connect (handle))
690 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
691 "Failed to connect to statistics service, can not get value `%s:%s'.\n",
692 strlen (subsystem) ? subsystem : "*",
693 strlen (name) ? name : "*");
697 if (subsystem == NULL)
701 slen1 = strlen (subsystem) + 1;
702 slen2 = strlen (name) + 1;
703 GNUNET_assert (slen1 + slen2 + sizeof (struct GNUNET_MessageHeader) <
704 GNUNET_SERVER_MAX_MESSAGE_SIZE);
705 ai = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_GetHandle));
707 ai->subsystem = GNUNET_strdup (subsystem);
708 ai->name = GNUNET_strdup (name);
712 ai->timeout = GNUNET_TIME_relative_to_absolute (timeout);
713 ai->type = ACTION_GET;
714 ai->msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
715 insert_ai (handle, ai);
721 * Cancel a 'get' request. Must be called before the 'cont'
722 * function is called.
724 * @param gh handle of the request to cancel
727 GNUNET_STATISTICS_get_cancel (struct GNUNET_STATISTICS_GetHandle *gh)
729 if (gh->sh->current == gh)
731 gh->aborted = GNUNET_YES;
735 GNUNET_CONTAINER_DLL_remove (gh->sh->action_head,
738 GNUNET_free (gh->name);
739 GNUNET_free (gh->subsystem);
746 add_setter_action (struct GNUNET_STATISTICS_Handle *h,
749 uint64_t value, enum ActionType type)
751 struct GNUNET_STATISTICS_GetHandle *ai;
757 GNUNET_assert (h != NULL);
758 GNUNET_assert (name != NULL);
759 if (GNUNET_YES != try_connect (h))
761 slen = strlen (h->subsystem) + 1;
762 nlen = strlen (name) + 1;
763 nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
764 if (nsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
772 if ( (0 == strcmp (ai->subsystem, h->subsystem)) &&
773 (0 == strcmp (ai->name, name)) &&
774 ( (ai->type == ACTION_UPDATE) ||
775 (ai->type == ACTION_SET) ) )
777 if (ai->type == ACTION_SET)
779 if (type == ACTION_UPDATE)
781 delta = (int64_t) value;
788 if (ai->value < -delta)
801 if (type == ACTION_UPDATE)
803 delta = (int64_t) value;
812 ai->timeout = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT);
817 ai = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_GetHandle));
819 ai->subsystem = GNUNET_strdup (h->subsystem);
820 ai->name = GNUNET_strdup (name);
821 ai->timeout = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT);
822 ai->make_persistent = make_persistent;
832 * Set statistic value for the peer. Will always use our
833 * subsystem (the argument used when "handle" was created).
835 * @param handle identification of the statistics service
836 * @param name name of the statistic value
837 * @param value new value to set
838 * @param make_persistent should the value be kept across restarts?
841 GNUNET_STATISTICS_set (struct GNUNET_STATISTICS_Handle *handle,
843 uint64_t value, int make_persistent)
847 GNUNET_assert (GNUNET_NO == handle->do_destroy);
848 add_setter_action (handle, name, make_persistent, value, ACTION_SET);
853 * Set statistic value for the peer. Will always use our
854 * subsystem (the argument used when "handle" was created).
856 * @param handle identification of the statistics service
857 * @param name name of the statistic value
858 * @param delta change in value (added to existing value)
859 * @param make_persistent should the value be kept across restarts?
862 GNUNET_STATISTICS_update (struct GNUNET_STATISTICS_Handle *handle,
864 int64_t delta, int make_persistent)
870 GNUNET_assert (GNUNET_NO == handle->do_destroy);
871 add_setter_action (handle, name, make_persistent,
872 (uint64_t) delta, ACTION_UPDATE);
876 /* end of statistics_api.c */