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_protocols.h"
29 #include "gnunet_server_lib.h"
30 #include "gnunet_statistics_service.h"
31 #include "gnunet_strings_lib.h"
32 #include "statistics.h"
34 #define SET_TRANSMIT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
49 * Linked list of things we still need to do.
54 * This is a linked list.
56 struct ActionItem *next;
59 * What subsystem is this action about? (can be NULL)
64 * What value is this action about? (can be NULL)
69 * Continuation to call once action is complete.
71 GNUNET_STATISTICS_Callback cont;
74 * Function to call (for GET actions only).
76 GNUNET_STATISTICS_Iterator proc;
79 * Closure for proc and cont.
84 * Timeout for this action.
86 struct GNUNET_TIME_Absolute timeout;
94 * Flag for SET/UPDATE actions.
99 * Has the current iteration been aborted; for GET actions.
104 * Is this a GET, SET or UPDATE?
106 enum ActionType type;
109 * Size of the message that we will be transmitting.
117 * Handle for the service.
119 struct GNUNET_STATISTICS_Handle
124 struct GNUNET_SCHEDULER_Handle *sched;
127 * Name of our subsystem.
132 * Configuration to use.
134 const struct GNUNET_CONFIGURATION_Handle *cfg;
137 * Socket (if available).
139 struct GNUNET_CLIENT_Connection *client;
142 * Currently pending transmission request.
144 struct GNUNET_CLIENT_TransmitHandle *th;
147 * Head of the linked list of pending actions (first action
150 struct ActionItem *action_head;
153 * Tail of the linked list of actions (for fast append).
155 struct ActionItem *action_tail;
158 * Action we are currently busy with (action request has been
159 * transmitted, we're now receiving the response from the
162 struct ActionItem *current;
165 * Should this handle auto-destruct once all actions have
174 * Try to (re)connect to the statistics service.
176 * @return GNUNET_YES on success, GNUNET_NO on failure.
179 try_connect (struct GNUNET_STATISTICS_Handle *ret)
181 if (ret->client != NULL)
183 ret->client = GNUNET_CLIENT_connect (ret->sched, "statistics", ret->cfg);
184 if (ret->client != NULL)
187 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
188 _("Failed to connect to statistics service!\n"));
195 * Free memory associated with the given action item.
198 free_action_item (struct ActionItem *ai)
200 GNUNET_free_non_null (ai->subsystem);
201 GNUNET_free_non_null (ai->name);
210 * Schedule the next action to be performed.
212 static void schedule_action (struct GNUNET_STATISTICS_Handle *h);
216 * GET processing is complete, tell client about it.
219 finish (struct GNUNET_STATISTICS_Handle *h, int code)
221 struct ActionItem *pos = h->current;
224 if (pos->cont != NULL)
225 pos->cont (pos->cls, code);
226 free_action_item (pos);
231 * Process the message.
233 * @return GNUNET_OK if the message was well-formed
236 process_message (struct GNUNET_STATISTICS_Handle *h,
237 const struct GNUNET_MessageHeader *msg)
241 const struct GNUNET_STATISTICS_ReplyMessage *smsg;
244 if (h->current->aborted)
245 return GNUNET_OK; /* don't bother */
246 size = ntohs (msg->size);
247 if (size < sizeof (struct GNUNET_STATISTICS_ReplyMessage))
250 return GNUNET_SYSERR;
252 smsg = (const struct GNUNET_STATISTICS_ReplyMessage *) msg;
253 size -= sizeof (struct GNUNET_STATISTICS_ReplyMessage);
254 if (size != GNUNET_STRINGS_buffer_tokenize ((const char *) &smsg[1],
255 size, 2, &service, &name))
258 return GNUNET_SYSERR;
261 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
262 "Received valid statistic on `%s:%s': %llu\n",
263 service, name, GNUNET_ntohll (smsg->value));
266 h->current->proc (h->current->cls,
269 GNUNET_ntohll (smsg->value),
271 (ntohl (smsg->uid) & GNUNET_STATISTICS_PERSIST_BIT)))
274 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
275 "Processing of remaining statistics aborted by client.\n");
277 h->current->aborted = GNUNET_YES;
284 * Function called with messages from stats service.
287 * @param msg message received, NULL on timeout or fatal error
290 receive_stats (void *cls, const struct GNUNET_MessageHeader *msg)
292 struct GNUNET_STATISTICS_Handle *h = cls;
296 if (NULL != h->client)
298 GNUNET_CLIENT_disconnect (h->client);
302 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
303 "Error receiving statistics from service, is the service running?\n" );
305 finish (h, GNUNET_SYSERR);
308 switch (ntohs (msg->type))
310 case GNUNET_MESSAGE_TYPE_STATISTICS_END:
312 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
313 "Received end of statistics marker\n");
315 finish (h, GNUNET_OK);
317 case GNUNET_MESSAGE_TYPE_STATISTICS_VALUE:
318 if (GNUNET_OK == process_message (h, msg))
320 /* finally, look for more! */
321 GNUNET_CLIENT_receive (h->client,
324 GNUNET_TIME_absolute_get_remaining
325 (h->current->timeout));
334 if (NULL != h->client)
336 GNUNET_CLIENT_disconnect (h->client);
339 finish (h, GNUNET_SYSERR);
344 * Transmit a GET request (and if successful, start to receive
348 transmit_get (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf)
350 struct GNUNET_MessageHeader *hdr;
357 /* timeout / error */
359 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
360 "Transmission of request for statistics failed!\n");
362 finish (handle, GNUNET_SYSERR);
365 slen1 = strlen (handle->current->subsystem) + 1;
366 slen2 = strlen (handle->current->name) + 1;
367 msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
368 GNUNET_assert (msize <= size);
369 hdr = (struct GNUNET_MessageHeader *) buf;
370 hdr->size = htons (msize);
371 hdr->type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_GET);
372 GNUNET_assert (slen1 + slen2 ==
373 GNUNET_STRINGS_buffer_fill ((char *) &hdr[1],
376 handle->current->subsystem,
377 handle->current->name));
378 GNUNET_CLIENT_receive (handle->client,
381 GNUNET_TIME_absolute_get_remaining (handle->
389 * Transmit a SET/UPDATE request.
392 transmit_set (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf)
394 struct GNUNET_STATISTICS_SetMessage *r;
401 finish (handle, GNUNET_SYSERR);
405 slen = strlen (handle->current->subsystem) + 1;
406 nlen = strlen (handle->current->name) + 1;
407 nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
411 finish (handle, GNUNET_SYSERR);
415 r->header.size = htons (nsize);
416 r->header.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_SET);
418 r->value = GNUNET_htonll (handle->current->value);
419 if (handle->current->make_persistent)
420 r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_PERSISTENT);
421 if (handle->current->type == ACTION_UPDATE)
422 r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_RELATIVE);
423 GNUNET_assert (slen + nlen ==
424 GNUNET_STRINGS_buffer_fill ((char *) &r[1],
427 handle->current->subsystem,
428 handle->current->name));
429 finish (handle, GNUNET_OK);
435 transmit_action (void *cls, size_t size, void *buf)
437 struct GNUNET_STATISTICS_Handle *handle = cls;
441 switch (handle->current->type)
444 ret = transmit_get (handle, size, buf);
448 ret = transmit_set (handle, size, buf);
460 * Get handle for the statistics service.
462 * @param sched scheduler to use
463 * @param subsystem name of subsystem using the service
464 * @param cfg services configuration in use
465 * @return handle to use
467 struct GNUNET_STATISTICS_Handle *
468 GNUNET_STATISTICS_create (struct GNUNET_SCHEDULER_Handle *sched,
469 const char *subsystem,
470 const struct GNUNET_CONFIGURATION_Handle *cfg)
472 struct GNUNET_STATISTICS_Handle *ret;
474 GNUNET_assert (subsystem != NULL);
475 GNUNET_assert (sched != NULL);
476 GNUNET_assert (cfg != NULL);
477 ret = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_Handle));
480 ret->subsystem = GNUNET_strdup (subsystem);
487 * Destroy a handle (free all state associated with
490 * @param h statistics handle to destroy
491 * @param sync_first set to GNUNET_YES if pending SET requests should
495 GNUNET_STATISTICS_destroy (struct GNUNET_STATISTICS_Handle *h,
498 struct ActionItem *pos;
499 struct ActionItem *next;
500 struct ActionItem *prev;
501 struct GNUNET_TIME_Relative timeout;
505 if (h->current != NULL)
507 if (h->current->type == ACTION_GET)
509 GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
511 free_action_item (h->current);
515 pos = h->action_head;
520 if (pos->type == ACTION_GET)
523 h->action_head = next;
526 free_action_item (pos);
534 h->action_tail = prev;
535 if (h->current == NULL)
537 h->current = h->action_head;
538 if (h->action_head != NULL)
540 h->action_head = h->action_head->next;
541 if (h->action_head == NULL)
542 h->action_tail = NULL;
545 if ( (h->current != NULL) &&
548 timeout = GNUNET_TIME_absolute_get_remaining (h->current->timeout);
549 h->th = GNUNET_CLIENT_notify_transmit_ready (h->client,
553 &transmit_action, h);
554 GNUNET_assert (NULL != h->th);
556 h->do_destroy = GNUNET_YES;
561 GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
564 if (h->current != NULL)
565 free_action_item (h->current);
566 while (NULL != (pos = h->action_head))
568 h->action_head = pos->next;
569 free_action_item (pos);
571 if (h->client != NULL)
573 GNUNET_CLIENT_disconnect (h->client);
576 GNUNET_free (h->subsystem);
583 * Schedule the next action to be performed.
586 schedule_action (struct GNUNET_STATISTICS_Handle *h)
588 struct GNUNET_TIME_Relative timeout;
590 if (h->current != NULL)
591 return; /* action already pending */
592 if (GNUNET_YES != try_connect (h))
594 finish (h, GNUNET_SYSERR);
598 /* schedule next action */
599 h->current = h->action_head;
600 if (NULL == h->current)
604 h->do_destroy = GNUNET_NO;
605 GNUNET_STATISTICS_destroy (h, GNUNET_YES);
609 h->action_head = h->action_head->next;
610 if (NULL == h->action_head)
611 h->action_tail = NULL;
612 h->current->next = NULL;
613 timeout = GNUNET_TIME_absolute_get_remaining (h->current->timeout);
615 (h->th = GNUNET_CLIENT_notify_transmit_ready (h->client,
619 &transmit_action, h)))
622 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
623 "Failed to transmit request to statistics service.\n");
625 finish (h, GNUNET_SYSERR);
631 insert_ai (struct GNUNET_STATISTICS_Handle *h, struct ActionItem *ai)
633 if (h->action_tail == NULL)
641 h->action_tail->next = ai;
648 * Get statistic from the peer.
650 * @param handle identification of the statistics service
651 * @param subsystem limit to the specified subsystem, NULL for our subsystem
652 * @param name name of the statistic value, NULL for all values
653 * @param timeout after how long should we give up (and call
654 * cont with an error code)?
655 * @param cont continuation to call when done (can be NULL)
656 * @param proc function to call on each value
657 * @param cls closure for cont and proc
660 GNUNET_STATISTICS_get (struct GNUNET_STATISTICS_Handle *handle,
661 const char *subsystem,
663 struct GNUNET_TIME_Relative timeout,
664 GNUNET_STATISTICS_Callback cont,
665 GNUNET_STATISTICS_Iterator proc, void *cls)
669 struct ActionItem *ai;
671 GNUNET_assert (handle != NULL);
672 GNUNET_assert (proc != NULL);
673 GNUNET_assert (GNUNET_NO == handle->do_destroy);
674 if (GNUNET_YES != try_connect (handle))
677 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
678 "Failed to connect to statistics service, can not get value `%s:%s'.\n",
679 strlen (subsystem) ? subsystem : "*",
680 strlen (name) ? name : "*");
683 cont (cls, GNUNET_SYSERR);
686 if (subsystem == NULL)
690 slen1 = strlen (subsystem);
691 slen2 = strlen (name);
692 GNUNET_assert (slen1 + slen2 + sizeof (struct GNUNET_MessageHeader) <
693 GNUNET_SERVER_MAX_MESSAGE_SIZE);
694 ai = GNUNET_malloc (sizeof (struct ActionItem));
695 ai->subsystem = GNUNET_strdup (subsystem);
696 ai->name = GNUNET_strdup (name);
700 ai->timeout = GNUNET_TIME_relative_to_absolute (timeout);
701 ai->type = ACTION_GET;
702 ai->msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
703 insert_ai (handle, ai);
708 add_setter_action (struct GNUNET_STATISTICS_Handle *h,
711 uint64_t value, enum ActionType type)
713 struct ActionItem *ai;
718 GNUNET_assert (h != NULL);
719 GNUNET_assert (name != NULL);
720 if (GNUNET_YES != try_connect (h))
722 slen = strlen (h->subsystem) + 1;
723 nlen = strlen (name) + 1;
724 nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
725 if (nsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
730 ai = GNUNET_malloc (sizeof (struct ActionItem));
731 ai->subsystem = GNUNET_strdup (h->subsystem);
732 ai->name = GNUNET_strdup (name);
733 ai->timeout = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT);
734 ai->make_persistent = make_persistent;
744 * Set statistic value for the peer. Will always use our
745 * subsystem (the argument used when "handle" was created).
747 * @param handle identification of the statistics service
748 * @param name name of the statistic value
749 * @param value new value to set
750 * @param make_persistent should the value be kept across restarts?
753 GNUNET_STATISTICS_set (struct GNUNET_STATISTICS_Handle *handle,
755 uint64_t value, int make_persistent)
757 GNUNET_assert (GNUNET_NO == handle->do_destroy);
758 add_setter_action (handle, name, make_persistent, value, ACTION_SET);
763 * Set statistic value for the peer. Will always use our
764 * subsystem (the argument used when "handle" was created).
766 * @param handle identification of the statistics service
767 * @param name name of the statistic value
768 * @param delta change in value (added to existing value)
769 * @param make_persistent should the value be kept across restarts?
772 GNUNET_STATISTICS_update (struct GNUNET_STATISTICS_Handle *handle,
774 int64_t delta, int make_persistent)
776 GNUNET_assert (GNUNET_NO == handle->do_destroy);
777 add_setter_action (handle, name, make_persistent,
778 (unsigned long long) delta, ACTION_UPDATE);
782 /* end of statistics_api.c */