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"
35 #define SET_TRANSMIT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
50 * Linked list of things we still need to do.
52 struct GNUNET_STATISTICS_GetHandle
56 * This is a doubly linked list.
58 struct GNUNET_STATISTICS_GetHandle *next;
61 * This is a doubly linked list.
63 struct GNUNET_STATISTICS_GetHandle *prev;
66 * Main statistics handle.
68 struct GNUNET_STATISTICS_Handle *sh;
71 * What subsystem is this action about? (can be NULL)
76 * What value is this action about? (can be NULL)
81 * Continuation to call once action is complete.
83 GNUNET_STATISTICS_Callback cont;
86 * Function to call (for GET actions only).
88 GNUNET_STATISTICS_Iterator proc;
91 * Closure for proc and cont.
96 * Timeout for this action.
98 struct GNUNET_TIME_Absolute timeout;
106 * Flag for SET/UPDATE actions.
111 * Has the current iteration been aborted; for GET actions.
116 * Is this a GET, SET or UPDATE?
118 enum ActionType type;
121 * Size of the message that we will be transmitting.
129 * Handle for the service.
131 struct GNUNET_STATISTICS_Handle
136 struct GNUNET_SCHEDULER_Handle *sched;
139 * Name of our subsystem.
144 * Configuration to use.
146 const struct GNUNET_CONFIGURATION_Handle *cfg;
149 * Socket (if available).
151 struct GNUNET_CLIENT_Connection *client;
154 * Currently pending transmission request.
156 struct GNUNET_CLIENT_TransmitHandle *th;
159 * Head of the linked list of pending actions (first action
162 struct GNUNET_STATISTICS_GetHandle *action_head;
165 * Tail of the linked list of actions (for fast append).
167 struct GNUNET_STATISTICS_GetHandle *action_tail;
170 * Action we are currently busy with (action request has been
171 * transmitted, we're now receiving the response from the
174 struct GNUNET_STATISTICS_GetHandle *current;
177 * Should this handle auto-destruct once all actions have
186 * Try to (re)connect to the statistics service.
188 * @return GNUNET_YES on success, GNUNET_NO on failure.
191 try_connect (struct GNUNET_STATISTICS_Handle *ret)
193 if (ret->client != NULL)
195 ret->client = GNUNET_CLIENT_connect (ret->sched, "statistics", ret->cfg);
196 if (ret->client != NULL)
199 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
200 _("Failed to connect to statistics service!\n"));
207 * Free memory associated with the given action item.
210 free_action_item (struct GNUNET_STATISTICS_GetHandle *ai)
212 GNUNET_free_non_null (ai->subsystem);
213 GNUNET_free_non_null (ai->name);
219 * Schedule the next action to be performed.
221 static void schedule_action (struct GNUNET_STATISTICS_Handle *h);
225 * GET processing is complete, tell client about it.
228 finish (struct GNUNET_STATISTICS_Handle *h, int code)
230 struct GNUNET_STATISTICS_GetHandle *pos = h->current;
233 if (pos->cont != NULL)
234 pos->cont (pos->cls, code);
235 free_action_item (pos);
240 * Process the message.
242 * @return GNUNET_OK if the message was well-formed
245 process_message (struct GNUNET_STATISTICS_Handle *h,
246 const struct GNUNET_MessageHeader *msg)
250 const struct GNUNET_STATISTICS_ReplyMessage *smsg;
253 if (h->current->aborted)
254 return GNUNET_OK; /* don't bother */
255 size = ntohs (msg->size);
256 if (size < sizeof (struct GNUNET_STATISTICS_ReplyMessage))
259 return GNUNET_SYSERR;
261 smsg = (const struct GNUNET_STATISTICS_ReplyMessage *) msg;
262 size -= sizeof (struct GNUNET_STATISTICS_ReplyMessage);
263 if (size != GNUNET_STRINGS_buffer_tokenize ((const char *) &smsg[1],
264 size, 2, &service, &name))
267 return GNUNET_SYSERR;
270 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
271 "Received valid statistic on `%s:%s': %llu\n",
272 service, name, GNUNET_ntohll (smsg->value));
275 h->current->proc (h->current->cls,
278 GNUNET_ntohll (smsg->value),
280 (ntohl (smsg->uid) & GNUNET_STATISTICS_PERSIST_BIT)))
283 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
284 "Processing of remaining statistics aborted by client.\n");
286 h->current->aborted = GNUNET_YES;
293 * Function called with messages from stats service.
296 * @param msg message received, NULL on timeout or fatal error
299 receive_stats (void *cls, const struct GNUNET_MessageHeader *msg)
301 struct GNUNET_STATISTICS_Handle *h = cls;
305 if (NULL != h->client)
307 GNUNET_CLIENT_disconnect (h->client);
311 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
312 "Error receiving statistics from service, is the service running?\n" );
314 finish (h, GNUNET_SYSERR);
317 switch (ntohs (msg->type))
319 case GNUNET_MESSAGE_TYPE_STATISTICS_END:
321 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
322 "Received end of statistics marker\n");
324 finish (h, GNUNET_OK);
326 case GNUNET_MESSAGE_TYPE_STATISTICS_VALUE:
327 if (GNUNET_OK == process_message (h, msg))
329 /* finally, look for more! */
330 GNUNET_CLIENT_receive (h->client,
333 GNUNET_TIME_absolute_get_remaining
334 (h->current->timeout));
343 if (NULL != h->client)
345 GNUNET_CLIENT_disconnect (h->client);
348 finish (h, GNUNET_SYSERR);
353 * Transmit a GET request (and if successful, start to receive
357 transmit_get (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf)
359 struct GNUNET_MessageHeader *hdr;
366 /* timeout / error */
368 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
369 "Transmission of request for statistics failed!\n");
371 finish (handle, GNUNET_SYSERR);
374 slen1 = strlen (handle->current->subsystem) + 1;
375 slen2 = strlen (handle->current->name) + 1;
376 msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
377 GNUNET_assert (msize <= size);
378 hdr = (struct GNUNET_MessageHeader *) buf;
379 hdr->size = htons (msize);
380 hdr->type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_GET);
381 GNUNET_assert (slen1 + slen2 ==
382 GNUNET_STRINGS_buffer_fill ((char *) &hdr[1],
385 handle->current->subsystem,
386 handle->current->name));
387 GNUNET_CLIENT_receive (handle->client,
390 GNUNET_TIME_absolute_get_remaining (handle->
398 * Transmit a SET/UPDATE request.
401 transmit_set (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf)
403 struct GNUNET_STATISTICS_SetMessage *r;
410 finish (handle, GNUNET_SYSERR);
414 slen = strlen (handle->current->subsystem) + 1;
415 nlen = strlen (handle->current->name) + 1;
416 nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
420 finish (handle, GNUNET_SYSERR);
424 r->header.size = htons (nsize);
425 r->header.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_SET);
427 r->value = GNUNET_htonll (handle->current->value);
428 if (handle->current->make_persistent)
429 r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_PERSISTENT);
430 if (handle->current->type == ACTION_UPDATE)
431 r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_RELATIVE);
432 GNUNET_assert (slen + nlen ==
433 GNUNET_STRINGS_buffer_fill ((char *) &r[1],
436 handle->current->subsystem,
437 handle->current->name));
438 finish (handle, GNUNET_OK);
444 transmit_action (void *cls, size_t size, void *buf)
446 struct GNUNET_STATISTICS_Handle *handle = cls;
450 switch (handle->current->type)
453 ret = transmit_get (handle, size, buf);
457 ret = transmit_set (handle, size, buf);
469 * Get handle for the statistics service.
471 * @param sched scheduler to use
472 * @param subsystem name of subsystem using the service
473 * @param cfg services configuration in use
474 * @return handle to use
476 struct GNUNET_STATISTICS_Handle *
477 GNUNET_STATISTICS_create (struct GNUNET_SCHEDULER_Handle *sched,
478 const char *subsystem,
479 const struct GNUNET_CONFIGURATION_Handle *cfg)
481 struct GNUNET_STATISTICS_Handle *ret;
483 GNUNET_assert (subsystem != NULL);
484 GNUNET_assert (sched != NULL);
485 GNUNET_assert (cfg != NULL);
486 ret = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_Handle));
489 ret->subsystem = GNUNET_strdup (subsystem);
496 * Destroy a handle (free all state associated with
499 * @param h statistics handle to destroy
500 * @param sync_first set to GNUNET_YES if pending SET requests should
504 GNUNET_STATISTICS_destroy (struct GNUNET_STATISTICS_Handle *h,
507 struct GNUNET_STATISTICS_GetHandle *pos;
508 struct GNUNET_STATISTICS_GetHandle *next;
509 struct GNUNET_STATISTICS_GetHandle *prev;
510 struct GNUNET_TIME_Relative timeout;
514 if (h->current != NULL)
516 if (h->current->type == ACTION_GET)
518 GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
520 free_action_item (h->current);
524 pos = h->action_head;
529 if (pos->type == ACTION_GET)
532 h->action_head = next;
535 free_action_item (pos);
543 h->action_tail = prev;
544 if (h->current == NULL)
546 h->current = h->action_head;
547 if (h->action_head != NULL)
549 h->action_head = h->action_head->next;
550 if (h->action_head == NULL)
551 h->action_tail = NULL;
554 if ( (h->current != NULL) &&
557 timeout = GNUNET_TIME_absolute_get_remaining (h->current->timeout);
558 h->th = GNUNET_CLIENT_notify_transmit_ready (h->client,
562 &transmit_action, h);
563 GNUNET_assert (NULL != h->th);
565 h->do_destroy = GNUNET_YES;
570 GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
573 if (h->current != NULL)
574 free_action_item (h->current);
575 while (NULL != (pos = h->action_head))
577 h->action_head = pos->next;
578 free_action_item (pos);
580 if (h->client != NULL)
582 GNUNET_CLIENT_disconnect (h->client);
585 GNUNET_free (h->subsystem);
592 * Schedule the next action to be performed.
595 schedule_action (struct GNUNET_STATISTICS_Handle *h)
597 struct GNUNET_TIME_Relative timeout;
599 if (h->current != NULL)
600 return; /* action already pending */
601 if (GNUNET_YES != try_connect (h))
603 finish (h, GNUNET_SYSERR);
607 /* schedule next action */
608 h->current = h->action_head;
609 if (NULL == h->current)
613 h->do_destroy = GNUNET_NO;
614 GNUNET_STATISTICS_destroy (h, GNUNET_YES);
618 GNUNET_CONTAINER_DLL_remove (h->action_head,
621 timeout = GNUNET_TIME_absolute_get_remaining (h->current->timeout);
623 (h->th = GNUNET_CLIENT_notify_transmit_ready (h->client,
627 &transmit_action, h)))
630 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
631 "Failed to transmit request to statistics service.\n");
633 finish (h, GNUNET_SYSERR);
639 insert_ai (struct GNUNET_STATISTICS_Handle *h, struct GNUNET_STATISTICS_GetHandle *ai)
641 GNUNET_CONTAINER_DLL_insert_after (h->action_head,
645 if (h->action_head == ai)
651 * Get statistic from the peer.
653 * @param handle identification of the statistics service
654 * @param subsystem limit to the specified subsystem, NULL for our subsystem
655 * @param name name of the statistic value, NULL for all values
656 * @param timeout after how long should we give up (and call
657 * cont with an error code)?
658 * @param cont continuation to call when done (can be NULL)
659 * @param proc function to call on each value
660 * @param cls closure for cont and proc
661 * @return NULL on error
663 struct GNUNET_STATISTICS_GetHandle *
664 GNUNET_STATISTICS_get (struct GNUNET_STATISTICS_Handle *handle,
665 const char *subsystem,
667 struct GNUNET_TIME_Relative timeout,
668 GNUNET_STATISTICS_Callback cont,
669 GNUNET_STATISTICS_Iterator proc, void *cls)
673 struct GNUNET_STATISTICS_GetHandle *ai;
675 GNUNET_assert (handle != NULL);
676 GNUNET_assert (proc != NULL);
677 GNUNET_assert (GNUNET_NO == handle->do_destroy);
678 if (GNUNET_YES != try_connect (handle))
681 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
682 "Failed to connect to statistics service, can not get value `%s:%s'.\n",
683 strlen (subsystem) ? subsystem : "*",
684 strlen (name) ? name : "*");
688 if (subsystem == NULL)
692 slen1 = strlen (subsystem);
693 slen2 = strlen (name);
694 GNUNET_assert (slen1 + slen2 + sizeof (struct GNUNET_MessageHeader) <
695 GNUNET_SERVER_MAX_MESSAGE_SIZE);
696 ai = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_GetHandle));
698 ai->subsystem = GNUNET_strdup (subsystem);
699 ai->name = GNUNET_strdup (name);
703 ai->timeout = GNUNET_TIME_relative_to_absolute (timeout);
704 ai->type = ACTION_GET;
705 ai->msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
706 insert_ai (handle, ai);
712 * Cancel a 'get' request. Must be called before the 'cont'
713 * function is called.
715 * @param gh handle of the request to cancel
718 GNUNET_STATISTICS_get_cancel (struct GNUNET_STATISTICS_GetHandle *gh)
720 if (gh->sh->current == gh)
722 gh->aborted = GNUNET_YES;
726 GNUNET_CONTAINER_DLL_remove (gh->sh->action_head,
729 GNUNET_free (gh->name);
730 GNUNET_free (gh->subsystem);
737 add_setter_action (struct GNUNET_STATISTICS_Handle *h,
740 uint64_t value, enum ActionType type)
742 struct GNUNET_STATISTICS_GetHandle *ai;
747 GNUNET_assert (h != NULL);
748 GNUNET_assert (name != NULL);
749 if (GNUNET_YES != try_connect (h))
751 slen = strlen (h->subsystem) + 1;
752 nlen = strlen (name) + 1;
753 nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
754 if (nsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
759 ai = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_GetHandle));
761 ai->subsystem = GNUNET_strdup (h->subsystem);
762 ai->name = GNUNET_strdup (name);
763 ai->timeout = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT);
764 ai->make_persistent = make_persistent;
774 * Set statistic value for the peer. Will always use our
775 * subsystem (the argument used when "handle" was created).
777 * @param handle identification of the statistics service
778 * @param name name of the statistic value
779 * @param value new value to set
780 * @param make_persistent should the value be kept across restarts?
783 GNUNET_STATISTICS_set (struct GNUNET_STATISTICS_Handle *handle,
785 uint64_t value, int make_persistent)
787 GNUNET_assert (GNUNET_NO == handle->do_destroy);
788 add_setter_action (handle, name, make_persistent, value, ACTION_SET);
793 * Set statistic value for the peer. Will always use our
794 * subsystem (the argument used when "handle" was created).
796 * @param handle identification of the statistics service
797 * @param name name of the statistic value
798 * @param delta change in value (added to existing value)
799 * @param make_persistent should the value be kept across restarts?
802 GNUNET_STATISTICS_update (struct GNUNET_STATISTICS_Handle *handle,
804 int64_t delta, int make_persistent)
806 GNUNET_assert (GNUNET_NO == handle->do_destroy);
807 add_setter_action (handle, name, make_persistent,
808 (unsigned long long) delta, ACTION_UPDATE);
812 /* end of statistics_api.c */