2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2011, 2016 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU 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.
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.
17 * @file statistics/statistics_api.c
18 * @brief API of the statistics service
19 * @author Christian Grothoff
22 #include "gnunet_util_lib.h"
23 #include "gnunet_constants.h"
24 #include "gnunet_protocols.h"
25 #include "gnunet_statistics_service.h"
26 #include "statistics.h"
29 * How long do we wait until a statistics request for setting
30 * a value times out? (The update will be lost if the
31 * service does not react within this timeframe).
33 #define SET_TRANSMIT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 2)
35 #define LOG(kind,...) GNUNET_log_from (kind, "statistics-api",__VA_ARGS__)
65 * Entry kept for each value we are watching.
67 struct GNUNET_STATISTICS_WatchEntry
71 * What subsystem is this action about? (never NULL)
76 * What value is this action about? (never NULL)
83 GNUNET_STATISTICS_Iterator proc;
94 * Linked list of things we still need to do.
96 struct GNUNET_STATISTICS_GetHandle
100 * This is a doubly linked list.
102 struct GNUNET_STATISTICS_GetHandle *next;
105 * This is a doubly linked list.
107 struct GNUNET_STATISTICS_GetHandle *prev;
110 * Main statistics handle.
112 struct GNUNET_STATISTICS_Handle *sh;
115 * What subsystem is this action about? (can be NULL)
120 * What value is this action about? (can be NULL)
125 * Continuation to call once action is complete.
127 GNUNET_STATISTICS_Callback cont;
130 * Function to call (for GET actions only).
132 GNUNET_STATISTICS_Iterator proc;
135 * Closure for @e proc and @e cont.
140 * Timeout for this action.
142 struct GNUNET_TIME_Absolute timeout;
150 * Flag for SET/UPDATE actions.
155 * Has the current iteration been aborted; for GET actions.
160 * Is this a #ACTION_GET, #ACTION_SET, #ACTION_UPDATE or #ACTION_WATCH?
162 enum ActionType type;
165 * Size of the message that we will be transmitting.
173 * Handle for the service.
175 struct GNUNET_STATISTICS_Handle
178 * Name of our subsystem.
183 * Configuration to use.
185 const struct GNUNET_CONFIGURATION_Handle *cfg;
188 * Message queue to the service.
190 struct GNUNET_MQ_Handle *mq;
193 * Head of the linked list of pending actions (first action
196 struct GNUNET_STATISTICS_GetHandle *action_head;
199 * Tail of the linked list of actions (for fast append).
201 struct GNUNET_STATISTICS_GetHandle *action_tail;
204 * Action we are currently busy with (action request has been
205 * transmitted, we're now receiving the response from the
208 struct GNUNET_STATISTICS_GetHandle *current;
211 * Array of watch entries.
213 struct GNUNET_STATISTICS_WatchEntry **watches;
216 * Task doing exponential back-off trying to reconnect.
218 struct GNUNET_SCHEDULER_Task *backoff_task;
221 * Task for running #do_destroy().
223 struct GNUNET_SCHEDULER_Task *destroy_task;
226 * Time for next connect retry.
228 struct GNUNET_TIME_Relative backoff;
231 * Maximum heap size observed so far (if available).
233 uint64_t peak_heap_size;
236 * Maximum resident set side observed so far (if available).
241 * Size of the @e watches array.
243 unsigned int watches_size;
246 * Should this handle auto-destruct once all actions have
252 * Are we currently receiving from the service?
260 * Obtain statistics about this process's memory consumption and
261 * report those as well (if they changed).
264 update_memory_statistics (struct GNUNET_STATISTICS_Handle *h)
266 #if ENABLE_HEAP_STATISTICS
267 uint64_t current_heap_size = 0;
268 uint64_t current_rss = 0;
270 if (GNUNET_NO != h->do_destroy)
277 current_heap_size = mi.uordblks + mi.fordblks;
284 if (0 == getrusage (RUSAGE_SELF, &ru))
286 current_rss = 1024LL * ru.ru_maxrss;
290 if (current_heap_size > h->peak_heap_size)
292 h->peak_heap_size = current_heap_size;
293 GNUNET_STATISTICS_set (h,
298 if (current_rss > h->peak_rss)
300 h->peak_rss = current_rss;
301 GNUNET_STATISTICS_set (h,
302 "# peak resident set size",
311 * Reconnect at a later time, respecting back-off.
313 * @param h statistics handle
316 reconnect_later (struct GNUNET_STATISTICS_Handle *h);
320 * Schedule the next action to be performed.
322 * @param cls statistics handle to reconnect
325 schedule_action (void *cls);
329 * Transmit request to service that we want to watch
330 * the development of a particular value.
332 * @param h statistics handle
333 * @param watch watch entry of the value to watch
336 schedule_watch_request (struct GNUNET_STATISTICS_Handle *h,
337 struct GNUNET_STATISTICS_WatchEntry *watch)
339 struct GNUNET_STATISTICS_GetHandle *ai;
344 slen = strlen (watch->subsystem) + 1;
345 nlen = strlen (watch->name) + 1;
346 nsize = sizeof (struct GNUNET_MessageHeader) + slen + nlen;
347 if (nsize >= GNUNET_MAX_MESSAGE_SIZE)
352 ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle);
354 ai->subsystem = GNUNET_strdup (watch->subsystem);
355 ai->name = GNUNET_strdup (watch->name);
356 ai->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
358 ai->type = ACTION_WATCH;
359 ai->proc = watch->proc;
360 ai->cls = watch->proc_cls;
361 GNUNET_CONTAINER_DLL_insert_tail (h->action_head,
369 * Free memory associated with the given action item.
371 * @param gh action item to free
374 free_action_item (struct GNUNET_STATISTICS_GetHandle *gh)
376 GNUNET_free_non_null (gh->subsystem);
377 GNUNET_free_non_null (gh->name);
383 * Disconnect from the statistics service.
385 * @param h statistics handle to disconnect from
388 do_disconnect (struct GNUNET_STATISTICS_Handle *h)
390 struct GNUNET_STATISTICS_GetHandle *c;
392 h->receiving = GNUNET_NO;
393 if (NULL != (c = h->current))
396 if ( (NULL != c->cont) &&
397 (GNUNET_YES != c->aborted) )
403 free_action_item (c);
407 GNUNET_MQ_destroy (h->mq);
414 * Process a #GNUNET_MESSAGE_TYPE_STATISTICS_VALUE message.
416 * @param cls statistics handle
417 * @param smsg message received from the service, never NULL
418 * @return #GNUNET_OK if the message was well-formed
421 check_statistics_value (void *cls,
422 const struct GNUNET_STATISTICS_ReplyMessage *smsg)
428 size = ntohs (smsg->header.size);
429 size -= sizeof (struct GNUNET_STATISTICS_ReplyMessage);
431 GNUNET_STRINGS_buffer_tokenize ((const char *) &smsg[1],
438 return GNUNET_SYSERR;
445 * Process a #GNUNET_MESSAGE_TYPE_STATISTICS_VALUE message.
447 * @param cls statistics handle
448 * @param msg message received from the service, never NULL
449 * @return #GNUNET_OK if the message was well-formed
452 handle_statistics_value (void *cls,
453 const struct GNUNET_STATISTICS_ReplyMessage *smsg)
455 struct GNUNET_STATISTICS_Handle *h = cls;
460 if (h->current->aborted)
461 return; /* iteration aborted, don't bother */
463 size = ntohs (smsg->header.size);
464 size -= sizeof (struct GNUNET_STATISTICS_ReplyMessage);
465 GNUNET_assert (size ==
466 GNUNET_STRINGS_buffer_tokenize ((const char *) &smsg[1],
471 LOG (GNUNET_ERROR_TYPE_DEBUG,
472 "Received valid statistic on `%s:%s': %llu\n",
474 GNUNET_ntohll (smsg->value));
476 h->current->proc (h->current->cls,
479 GNUNET_ntohll (smsg->value),
481 (ntohl (smsg->uid) & GNUNET_STATISTICS_PERSIST_BIT)))
483 LOG (GNUNET_ERROR_TYPE_DEBUG,
484 "Processing of remaining statistics aborted by client.\n");
485 h->current->aborted = GNUNET_YES;
491 * We have received a watch value from the service. Process it.
493 * @param cls statistics handle
494 * @param msg the watch value message
497 handle_statistics_watch_value (void *cls,
498 const struct GNUNET_STATISTICS_WatchValueMessage *wvm)
500 struct GNUNET_STATISTICS_Handle *h = cls;
501 struct GNUNET_STATISTICS_WatchEntry *w;
504 GNUNET_break (0 == ntohl (wvm->reserved));
505 wid = ntohl (wvm->wid);
506 if (wid >= h->watches_size)
515 (void) w->proc (w->proc_cls,
518 GNUNET_ntohll (wvm->value),
519 0 != (ntohl (wvm->flags) & GNUNET_STATISTICS_PERSIST_BIT));
524 * Generic error handler, called with the appropriate error code and
525 * the same closure specified at the creation of the message queue.
526 * Not every message queue implementation supports an error handler.
528 * @param cls closure with the `struct GNUNET_STATISTICS_Handle *`
529 * @param error error code
532 mq_error_handler (void *cls,
533 enum GNUNET_MQ_Error error)
535 struct GNUNET_STATISTICS_Handle *h = cls;
537 if (GNUNET_NO != h->do_destroy)
539 h->do_destroy = GNUNET_NO;
540 if (NULL != h->destroy_task)
542 GNUNET_SCHEDULER_cancel (h->destroy_task);
543 h->destroy_task = NULL;
545 GNUNET_STATISTICS_destroy (h,
555 * Task used to destroy the statistics handle.
557 * @param cls the `struct GNUNET_STATISTICS_Handle`
560 do_destroy (void *cls)
562 struct GNUNET_STATISTICS_Handle *h = cls;
564 h->destroy_task = NULL;
565 h->do_destroy = GNUNET_NO;
566 LOG (GNUNET_ERROR_TYPE_DEBUG,
567 "Running final destruction\n");
568 GNUNET_STATISTICS_destroy (h,
574 * Handle a #GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT_CONFIRM
575 * message. We receive this message at the end of the shutdown when
576 * the service confirms that all data has been written to disk.
578 * @param cls our `struct GNUNET_STATISTICS_Handle *`
579 * @param msg the message
582 handle_disconnect_confirm (void *cls,
583 const struct GNUNET_MessageHeader *msg)
585 struct GNUNET_STATISTICS_Handle *h = cls;
587 if (GNUNET_SYSERR != h->do_destroy)
589 /* not in shutdown, why do we get 'TEST'? */
595 LOG (GNUNET_ERROR_TYPE_DEBUG,
596 "Received DISCONNNECT_CONFIRM message from statistics, can complete disconnect\n");
597 if (NULL != h->destroy_task)
598 GNUNET_SCHEDULER_cancel (h->destroy_task);
599 h->destroy_task = GNUNET_SCHEDULER_add_now (&do_destroy,
605 * Handle a #GNUNET_MESSAGE_TYPE_STATISTICS_END message. We receive
606 * this message in response to a query to indicate that there are no
607 * further matching results.
609 * @param cls our `struct GNUNET_STATISTICS_Handle *`
610 * @param msg the message
613 handle_statistics_end (void *cls,
614 const struct GNUNET_MessageHeader *msg)
616 struct GNUNET_STATISTICS_Handle *h = cls;
617 struct GNUNET_STATISTICS_GetHandle *c;
619 LOG (GNUNET_ERROR_TYPE_DEBUG,
620 "Received end of statistics marker\n");
621 if (NULL == (c = h->current))
628 h->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
637 free_action_item (c);
642 * Try to (re)connect to the statistics service.
644 * @param h statistics handle to reconnect
645 * @return #GNUNET_YES on success, #GNUNET_NO on failure.
648 try_connect (struct GNUNET_STATISTICS_Handle *h)
650 struct GNUNET_MQ_MessageHandler handlers[] = {
651 GNUNET_MQ_hd_fixed_size (disconnect_confirm,
652 GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT_CONFIRM,
653 struct GNUNET_MessageHeader,
655 GNUNET_MQ_hd_fixed_size (statistics_end,
656 GNUNET_MESSAGE_TYPE_STATISTICS_END,
657 struct GNUNET_MessageHeader,
659 GNUNET_MQ_hd_var_size (statistics_value,
660 GNUNET_MESSAGE_TYPE_STATISTICS_VALUE,
661 struct GNUNET_STATISTICS_ReplyMessage,
663 GNUNET_MQ_hd_fixed_size (statistics_watch_value,
664 GNUNET_MESSAGE_TYPE_STATISTICS_WATCH_VALUE,
665 struct GNUNET_STATISTICS_WatchValueMessage,
667 GNUNET_MQ_handler_end ()
669 struct GNUNET_STATISTICS_GetHandle *gh;
670 struct GNUNET_STATISTICS_GetHandle *gn;
672 if (NULL != h->backoff_task)
676 h->mq = GNUNET_CLIENT_connect (h->cfg,
683 LOG (GNUNET_ERROR_TYPE_DEBUG,
684 "Failed to connect to statistics service!\n");
688 while (NULL != (gh = gn))
691 if (gh->type == ACTION_WATCH)
693 GNUNET_CONTAINER_DLL_remove (h->action_head,
696 free_action_item (gh);
699 for (unsigned int i = 0; i < h->watches_size; i++)
700 if (NULL != h->watches[i])
701 schedule_watch_request (h,
708 * We've waited long enough, reconnect now.
710 * @param cls the `struct GNUNET_STATISTICS_Handle` to reconnect
713 reconnect_task (void *cls)
715 struct GNUNET_STATISTICS_Handle *h = cls;
717 h->backoff_task = NULL;
723 * Reconnect at a later time, respecting back-off.
725 * @param h statistics handle
728 reconnect_later (struct GNUNET_STATISTICS_Handle *h)
731 struct GNUNET_STATISTICS_GetHandle *gh;
733 GNUNET_assert (NULL == h->backoff_task);
734 if (GNUNET_YES == h->do_destroy)
736 /* So we are shutting down and the service is not reachable.
737 * Chances are that it's down for good and we are not going to connect to
739 * Give up and don't sync the rest of the data.
742 for (gh = h->action_head; NULL != gh; gh = gh->next)
743 if ( (gh->make_persistent) &&
744 (ACTION_SET == gh->type) )
746 if (GNUNET_YES == loss)
747 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
748 _("Could not save some persistent statistics\n"));
749 if (NULL != h->destroy_task)
750 GNUNET_SCHEDULER_cancel (h->destroy_task);
751 h->destroy_task = GNUNET_SCHEDULER_add_now (&do_destroy,
756 = GNUNET_SCHEDULER_add_delayed (h->backoff,
759 h->backoff = GNUNET_TIME_STD_BACKOFF (h->backoff);
765 * Transmit a GET request (and if successful, start to receive
768 * @param handle statistics handle
771 transmit_get (struct GNUNET_STATISTICS_Handle *handle)
773 struct GNUNET_STATISTICS_GetHandle *c;
774 struct GNUNET_MessageHeader *hdr;
775 struct GNUNET_MQ_Envelope *env;
779 GNUNET_assert (NULL != (c = handle->current));
780 slen1 = strlen (c->subsystem) + 1;
781 slen2 = strlen (c->name) + 1;
782 env = GNUNET_MQ_msg_extra (hdr,
784 GNUNET_MESSAGE_TYPE_STATISTICS_GET);
785 GNUNET_assert (slen1 + slen2 ==
786 GNUNET_STRINGS_buffer_fill ((char *) &hdr[1],
791 GNUNET_MQ_notify_sent (env,
794 GNUNET_MQ_send (handle->mq,
800 * Transmit a WATCH request (and if successful, start to receive
803 * @param handle statistics handle
806 transmit_watch (struct GNUNET_STATISTICS_Handle *handle)
808 struct GNUNET_MessageHeader *hdr;
809 struct GNUNET_MQ_Envelope *env;
813 LOG (GNUNET_ERROR_TYPE_DEBUG,
814 "Transmitting watch request for `%s'\n",
815 handle->current->name);
816 slen1 = strlen (handle->current->subsystem) + 1;
817 slen2 = strlen (handle->current->name) + 1;
818 env = GNUNET_MQ_msg_extra (hdr,
820 GNUNET_MESSAGE_TYPE_STATISTICS_WATCH);
821 GNUNET_assert (slen1 + slen2 ==
822 GNUNET_STRINGS_buffer_fill ((char *) &hdr[1],
825 handle->current->subsystem,
826 handle->current->name));
827 GNUNET_MQ_notify_sent (env,
830 GNUNET_MQ_send (handle->mq,
832 GNUNET_assert (NULL == handle->current->cont);
833 free_action_item (handle->current);
834 handle->current = NULL;
835 schedule_action (handle);
840 * Transmit a SET/UPDATE request.
842 * @param handle statistics handle
845 transmit_set (struct GNUNET_STATISTICS_Handle *handle)
847 struct GNUNET_STATISTICS_SetMessage *r;
848 struct GNUNET_MQ_Envelope *env;
852 slen = strlen (handle->current->subsystem) + 1;
853 nlen = strlen (handle->current->name) + 1;
854 env = GNUNET_MQ_msg_extra (r,
856 GNUNET_MESSAGE_TYPE_STATISTICS_SET);
858 r->value = GNUNET_htonll (handle->current->value);
859 if (handle->current->make_persistent)
860 r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_PERSISTENT);
861 if (handle->current->type == ACTION_UPDATE)
862 r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_RELATIVE);
863 GNUNET_assert (slen + nlen ==
864 GNUNET_STRINGS_buffer_fill ((char *) &r[1],
867 handle->current->subsystem,
868 handle->current->name));
869 GNUNET_assert (NULL == handle->current->cont);
870 free_action_item (handle->current);
871 handle->current = NULL;
872 update_memory_statistics (handle);
873 GNUNET_MQ_notify_sent (env,
876 GNUNET_MQ_send (handle->mq,
882 * Get handle for the statistics service.
884 * @param subsystem name of subsystem using the service
885 * @param cfg services configuration in use
886 * @return handle to use
888 struct GNUNET_STATISTICS_Handle *
889 GNUNET_STATISTICS_create (const char *subsystem,
890 const struct GNUNET_CONFIGURATION_Handle *cfg)
892 struct GNUNET_STATISTICS_Handle *h;
895 GNUNET_CONFIGURATION_get_value_yesno (cfg,
899 h = GNUNET_new (struct GNUNET_STATISTICS_Handle);
901 h->subsystem = GNUNET_strdup (subsystem);
902 h->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
908 * Destroy a handle (free all state associated with
911 * @param h statistics handle to destroy
912 * @param sync_first set to #GNUNET_YES if pending SET requests should
916 GNUNET_STATISTICS_destroy (struct GNUNET_STATISTICS_Handle *h,
919 struct GNUNET_STATISTICS_GetHandle *pos;
920 struct GNUNET_STATISTICS_GetHandle *next;
924 GNUNET_assert (GNUNET_NO == h->do_destroy); /* Don't call twice. */
927 (0 != GNUNET_MQ_get_length (h->mq)) )
929 if ( (NULL != h->current) &&
930 (ACTION_GET == h->current->type) )
931 h->current->aborted = GNUNET_YES;
932 next = h->action_head;
933 while (NULL != (pos = next))
936 if ( (ACTION_GET == pos->type) ||
937 (ACTION_WATCH == pos->type) )
939 GNUNET_CONTAINER_DLL_remove (h->action_head,
942 free_action_item (pos);
945 h->do_destroy = GNUNET_YES;
947 GNUNET_assert (NULL == h->destroy_task);
949 = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (h->backoff,
953 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
954 "Deferring destruction\n");
955 return; /* do not finish destruction just yet */
957 /* do clean up all */
958 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
959 "Cleaning all up\n");
960 while (NULL != (pos = h->action_head))
962 GNUNET_CONTAINER_DLL_remove (h->action_head,
965 free_action_item (pos);
968 if (NULL != h->backoff_task)
970 GNUNET_SCHEDULER_cancel (h->backoff_task);
971 h->backoff_task = NULL;
973 if (NULL != h->destroy_task)
976 GNUNET_SCHEDULER_cancel (h->destroy_task);
977 h->destroy_task = NULL;
979 for (unsigned int i = 0; i < h->watches_size; i++)
981 if (NULL == h->watches[i])
983 GNUNET_free (h->watches[i]->subsystem);
984 GNUNET_free (h->watches[i]->name);
985 GNUNET_free (h->watches[i]);
987 GNUNET_array_grow (h->watches,
990 GNUNET_free (h->subsystem);
996 * Schedule the next action to be performed.
998 * @param cls statistics handle
1001 schedule_action (void *cls)
1003 struct GNUNET_STATISTICS_Handle *h = cls;
1005 if (NULL != h->backoff_task)
1006 return; /* action already pending */
1007 if (GNUNET_YES != try_connect (h))
1009 reconnect_later (h);
1012 if (0 < GNUNET_MQ_get_length (h->mq))
1013 return; /* Wait for queue to be reduced more */
1014 /* schedule next action */
1015 while (NULL == h->current)
1017 h->current = h->action_head;
1018 if (NULL == h->current)
1020 struct GNUNET_MessageHeader *hdr;
1021 struct GNUNET_MQ_Envelope *env;
1023 if (GNUNET_YES != h->do_destroy)
1024 return; /* nothing to do */
1025 /* let service know that we're done */
1026 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1027 "Notifying service that we are done\n");
1028 h->do_destroy = GNUNET_SYSERR; /* in 'TEST' mode */
1029 env = GNUNET_MQ_msg (hdr,
1030 GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT);
1031 GNUNET_MQ_notify_sent (env,
1034 GNUNET_MQ_send (h->mq,
1038 GNUNET_CONTAINER_DLL_remove (h->action_head,
1041 switch (h->current->type)
1062 * Get statistic from the peer.
1064 * @param handle identification of the statistics service
1065 * @param subsystem limit to the specified subsystem, NULL for our subsystem
1066 * @param name name of the statistic value, NULL for all values
1067 * @param cont continuation to call when done (can be NULL)
1068 * This callback CANNOT destroy the statistics handle in the same call.
1069 * @param proc function to call on each value
1070 * @param cls closure for @a cont and @a proc
1071 * @return NULL on error
1073 struct GNUNET_STATISTICS_GetHandle *
1074 GNUNET_STATISTICS_get (struct GNUNET_STATISTICS_Handle *handle,
1075 const char *subsystem,
1077 GNUNET_STATISTICS_Callback cont,
1078 GNUNET_STATISTICS_Iterator proc,
1083 struct GNUNET_STATISTICS_GetHandle *ai;
1087 GNUNET_assert (NULL != proc);
1088 GNUNET_assert (GNUNET_NO == handle->do_destroy);
1089 if (NULL == subsystem)
1093 slen1 = strlen (subsystem) + 1;
1094 slen2 = strlen (name) + 1;
1095 GNUNET_assert (slen1 + slen2 + sizeof (struct GNUNET_MessageHeader) <
1096 GNUNET_MAX_MESSAGE_SIZE);
1097 ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle);
1099 ai->subsystem = GNUNET_strdup (subsystem);
1100 ai->name = GNUNET_strdup (name);
1104 ai->type = ACTION_GET;
1105 ai->msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
1106 GNUNET_CONTAINER_DLL_insert_tail (handle->action_head,
1107 handle->action_tail,
1109 schedule_action (handle);
1115 * Cancel a 'get' request. Must be called before the 'cont'
1116 * function is called.
1118 * @param gh handle of the request to cancel
1121 GNUNET_STATISTICS_get_cancel (struct GNUNET_STATISTICS_GetHandle *gh)
1126 if (gh->sh->current == gh)
1128 gh->aborted = GNUNET_YES;
1131 GNUNET_CONTAINER_DLL_remove (gh->sh->action_head,
1132 gh->sh->action_tail,
1134 GNUNET_free (gh->name);
1135 GNUNET_free (gh->subsystem);
1141 * Watch statistics from the peer (be notified whenever they change).
1143 * @param handle identification of the statistics service
1144 * @param subsystem limit to the specified subsystem, never NULL
1145 * @param name name of the statistic value, never NULL
1146 * @param proc function to call on each value
1147 * @param proc_cls closure for @a proc
1148 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1151 GNUNET_STATISTICS_watch (struct GNUNET_STATISTICS_Handle *handle,
1152 const char *subsystem,
1154 GNUNET_STATISTICS_Iterator proc,
1157 struct GNUNET_STATISTICS_WatchEntry *w;
1160 return GNUNET_SYSERR;
1161 w = GNUNET_new (struct GNUNET_STATISTICS_WatchEntry);
1162 w->subsystem = GNUNET_strdup (subsystem);
1163 w->name = GNUNET_strdup (name);
1165 w->proc_cls = proc_cls;
1166 GNUNET_array_append (handle->watches,
1167 handle->watches_size,
1169 schedule_watch_request (handle,
1176 * Stop watching statistics from the peer.
1178 * @param handle identification of the statistics service
1179 * @param subsystem limit to the specified subsystem, never NULL
1180 * @param name name of the statistic value, never NULL
1181 * @param proc function to call on each value
1182 * @param proc_cls closure for @a proc
1183 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error (no such watch)
1186 GNUNET_STATISTICS_watch_cancel (struct GNUNET_STATISTICS_Handle *handle,
1187 const char *subsystem,
1189 GNUNET_STATISTICS_Iterator proc,
1192 struct GNUNET_STATISTICS_WatchEntry *w;
1195 return GNUNET_SYSERR;
1196 for (unsigned int i=0;i<handle->watches_size;i++)
1198 w = handle->watches[i];
1201 if ( (w->proc == proc) &&
1202 (w->proc_cls == proc_cls) &&
1203 (0 == strcmp (w->name,
1205 (0 == strcmp (w->subsystem,
1208 GNUNET_free (w->name);
1209 GNUNET_free (w->subsystem);
1211 handle->watches[i] = NULL;
1215 return GNUNET_SYSERR;
1220 * Queue a request to change a statistic.
1222 * @param h statistics handle
1223 * @param name name of the value
1224 * @param make_persistent should the value be kept across restarts?
1225 * @param value new value or change
1226 * @param type type of the action (#ACTION_SET or #ACTION_UPDATE)
1229 add_setter_action (struct GNUNET_STATISTICS_Handle *h,
1231 int make_persistent,
1233 enum ActionType type)
1235 struct GNUNET_STATISTICS_GetHandle *ai;
1241 slen = strlen (h->subsystem) + 1;
1242 nlen = strlen (name) + 1;
1243 nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
1244 if (nsize >= GNUNET_MAX_MESSAGE_SIZE)
1249 for (ai = h->action_head; NULL != ai; ai = ai->next)
1251 if (! ( (0 == strcmp (ai->subsystem,
1253 (0 == strcmp (ai->name,
1255 ( (ACTION_UPDATE == ai->type) ||
1256 (ACTION_SET == ai->type) ) ) )
1258 if (ACTION_SET == ai->type)
1260 if (ACTION_UPDATE == type)
1262 delta = (int64_t) value;
1265 /* update old set by new delta */
1270 /* update old set by new delta, but never go negative */
1271 if (ai->value < -delta)
1279 /* new set overrides old set */
1285 if (ACTION_UPDATE == type)
1287 /* make delta cummulative */
1288 delta = (int64_t) value;
1293 /* drop old 'update', use new 'set' instead */
1299 = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT);
1304 /* no existing entry matches, create a fresh one */
1305 ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle);
1307 ai->subsystem = GNUNET_strdup (h->subsystem);
1308 ai->name = GNUNET_strdup (name);
1309 ai->timeout = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT);
1310 ai->make_persistent = make_persistent;
1314 GNUNET_CONTAINER_DLL_insert_tail (h->action_head,
1317 schedule_action (h);
1322 * Set statistic value for the peer. Will always use our
1323 * subsystem (the argument used when "handle" was created).
1325 * @param handle identification of the statistics service
1326 * @param name name of the statistic value
1327 * @param value new value to set
1328 * @param make_persistent should the value be kept across restarts?
1331 GNUNET_STATISTICS_set (struct GNUNET_STATISTICS_Handle *handle,
1334 int make_persistent)
1338 GNUNET_assert (GNUNET_NO == handle->do_destroy);
1339 add_setter_action (handle,
1348 * Set statistic value for the peer. Will always use our
1349 * subsystem (the argument used when "handle" was created).
1351 * @param handle identification of the statistics service
1352 * @param name name of the statistic value
1353 * @param delta change in value (added to existing value)
1354 * @param make_persistent should the value be kept across restarts?
1357 GNUNET_STATISTICS_update (struct GNUNET_STATISTICS_Handle *handle,
1360 int make_persistent)
1366 GNUNET_assert (GNUNET_NO == handle->do_destroy);
1367 add_setter_action (handle,
1375 /* end of statistics_api.c */