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
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, 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., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
22 * @file statistics/statistics_api.c
23 * @brief API of the statistics service
24 * @author Christian Grothoff
27 #include "gnunet_util_lib.h"
28 #include "gnunet_constants.h"
29 #include "gnunet_protocols.h"
30 #include "gnunet_statistics_service.h"
31 #include "statistics.h"
34 * How long do we wait until a statistics request for setting
35 * a value times out? (The update will be lost if the
36 * service does not react within this timeframe).
38 #define SET_TRANSMIT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 2)
40 #define LOG(kind,...) GNUNET_log_from (kind, "statistics-api",__VA_ARGS__)
70 * Entry kept for each value we are watching.
72 struct GNUNET_STATISTICS_WatchEntry
76 * What subsystem is this action about? (never NULL)
81 * What value is this action about? (never NULL)
88 GNUNET_STATISTICS_Iterator proc;
99 * Linked list of things we still need to do.
101 struct GNUNET_STATISTICS_GetHandle
105 * This is a doubly linked list.
107 struct GNUNET_STATISTICS_GetHandle *next;
110 * This is a doubly linked list.
112 struct GNUNET_STATISTICS_GetHandle *prev;
115 * Main statistics handle.
117 struct GNUNET_STATISTICS_Handle *sh;
120 * What subsystem is this action about? (can be NULL)
125 * What value is this action about? (can be NULL)
130 * Continuation to call once action is complete.
132 GNUNET_STATISTICS_Callback cont;
135 * Function to call (for GET actions only).
137 GNUNET_STATISTICS_Iterator proc;
140 * Closure for @e proc and @e cont.
145 * Timeout for this action.
147 struct GNUNET_TIME_Absolute timeout;
155 * Flag for SET/UPDATE actions.
160 * Has the current iteration been aborted; for GET actions.
165 * Is this a #ACTION_GET, #ACTION_SET, #ACTION_UPDATE or #ACTION_WATCH?
167 enum ActionType type;
170 * Size of the message that we will be transmitting.
178 * Handle for the service.
180 struct GNUNET_STATISTICS_Handle
183 * Name of our subsystem.
188 * Configuration to use.
190 const struct GNUNET_CONFIGURATION_Handle *cfg;
193 * Message queue to the service.
195 struct GNUNET_MQ_Handle *mq;
198 * Head of the linked list of pending actions (first action
201 struct GNUNET_STATISTICS_GetHandle *action_head;
204 * Tail of the linked list of actions (for fast append).
206 struct GNUNET_STATISTICS_GetHandle *action_tail;
209 * Action we are currently busy with (action request has been
210 * transmitted, we're now receiving the response from the
213 struct GNUNET_STATISTICS_GetHandle *current;
216 * Array of watch entries.
218 struct GNUNET_STATISTICS_WatchEntry **watches;
221 * Task doing exponential back-off trying to reconnect.
223 struct GNUNET_SCHEDULER_Task *backoff_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, "# peak heap size", current_heap_size, GNUNET_NO);
295 if (current_rss > h->peak_rss)
297 h->peak_rss = current_rss;
298 GNUNET_STATISTICS_set (h, "# peak resident set size", current_rss, GNUNET_NO);
305 * Schedule the next action to be performed.
307 * @param h statistics handle to reconnect
310 schedule_action (struct GNUNET_STATISTICS_Handle *h);
314 * Reconnect at a later time, respecting back-off.
316 * @param h statistics handle
319 reconnect_later (struct GNUNET_STATISTICS_Handle *h);
323 * Transmit request to service that we want to watch
324 * the development of a particular value.
326 * @param h statistics handle
327 * @param watch watch entry of the value to watch
330 schedule_watch_request (struct GNUNET_STATISTICS_Handle *h,
331 struct GNUNET_STATISTICS_WatchEntry *watch)
333 struct GNUNET_STATISTICS_GetHandle *ai;
338 slen = strlen (watch->subsystem) + 1;
339 nlen = strlen (watch->name) + 1;
340 nsize = sizeof (struct GNUNET_MessageHeader) + slen + nlen;
341 if (nsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
346 ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle);
348 ai->subsystem = GNUNET_strdup (watch->subsystem);
349 ai->name = GNUNET_strdup (watch->name);
350 ai->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
352 ai->type = ACTION_WATCH;
353 ai->proc = watch->proc;
354 ai->cls = watch->proc_cls;
355 GNUNET_CONTAINER_DLL_insert_tail (h->action_head,
363 * Free memory associated with the given action item.
365 * @param gh action item to free
368 free_action_item (struct GNUNET_STATISTICS_GetHandle *gh)
370 GNUNET_free_non_null (gh->subsystem);
371 GNUNET_free_non_null (gh->name);
377 * Disconnect from the statistics service.
379 * @param h statistics handle to disconnect from
382 do_disconnect (struct GNUNET_STATISTICS_Handle *h)
384 struct GNUNET_STATISTICS_GetHandle *c;
386 h->receiving = GNUNET_NO;
387 if (NULL != (c = h->current))
390 if ( (NULL != c->cont) &&
391 (GNUNET_YES != c->aborted) )
397 free_action_item (c);
401 GNUNET_MQ_destroy (h->mq);
408 * Process a #GNUNET_MESSAGE_TYPE_STATISTICS_VALUE message.
410 * @param cls statistics handle
411 * @param smsg message received from the service, never NULL
412 * @return #GNUNET_OK if the message was well-formed
415 check_statistics_value (void *cls,
416 const struct GNUNET_STATISTICS_ReplyMessage *smsg)
422 size = ntohs (smsg->header.size);
423 size -= sizeof (struct GNUNET_STATISTICS_ReplyMessage);
425 GNUNET_STRINGS_buffer_tokenize ((const char *) &smsg[1],
432 return GNUNET_SYSERR;
439 * Process a #GNUNET_MESSAGE_TYPE_STATISTICS_VALUE message.
441 * @param cls statistics handle
442 * @param msg message received from the service, never NULL
443 * @return #GNUNET_OK if the message was well-formed
446 handle_statistics_value (void *cls,
447 const struct GNUNET_STATISTICS_ReplyMessage *smsg)
449 struct GNUNET_STATISTICS_Handle *h = cls;
454 if (h->current->aborted)
455 return; /* iteration aborted, don't bother */
457 size = ntohs (smsg->header.size);
458 size -= sizeof (struct GNUNET_STATISTICS_ReplyMessage);
459 GNUNET_assert (size ==
460 GNUNET_STRINGS_buffer_tokenize ((const char *) &smsg[1],
465 LOG (GNUNET_ERROR_TYPE_DEBUG,
466 "Received valid statistic on `%s:%s': %llu\n",
468 GNUNET_ntohll (smsg->value));
470 h->current->proc (h->current->cls,
473 GNUNET_ntohll (smsg->value),
475 (ntohl (smsg->uid) & GNUNET_STATISTICS_PERSIST_BIT)))
477 LOG (GNUNET_ERROR_TYPE_DEBUG,
478 "Processing of remaining statistics aborted by client.\n");
479 h->current->aborted = GNUNET_YES;
485 * We have received a watch value from the service. Process it.
487 * @param cls statistics handle
488 * @param msg the watch value message
491 handle_statistics_watch_value (void *cls,
492 const struct GNUNET_STATISTICS_WatchValueMessage *wvm)
494 struct GNUNET_STATISTICS_Handle *h = cls;
495 struct GNUNET_STATISTICS_WatchEntry *w;
498 GNUNET_break (0 == ntohl (wvm->reserved));
499 wid = ntohl (wvm->wid);
500 if (wid >= h->watches_size)
509 (void) w->proc (w->proc_cls,
512 GNUNET_ntohll (wvm->value),
513 0 != (ntohl (wvm->flags) & GNUNET_STATISTICS_PERSIST_BIT));
518 * Generic error handler, called with the appropriate error code and
519 * the same closure specified at the creation of the message queue.
520 * Not every message queue implementation supports an error handler.
522 * @param cls closure with the `struct GNUNET_STATISTICS_Handle *`
523 * @param error error code
526 mq_error_handler (void *cls,
527 enum GNUNET_MQ_Error error)
529 struct GNUNET_STATISTICS_Handle *h = cls;
531 if (GNUNET_NO != h->do_destroy)
533 h->do_destroy = GNUNET_NO;
534 GNUNET_STATISTICS_destroy (h,
544 * Task used to destroy the statistics handle.
546 * @param cls the `struct GNUNET_STATISTICS_Handle`
549 destroy_task (void *cls)
551 struct GNUNET_STATISTICS_Handle *h = cls;
553 GNUNET_STATISTICS_destroy (h, GNUNET_NO);
558 * Handle a #GNUNET_MESSAGE_TYPE_TEST (sic) message. We receive this
559 * message at the end of the shutdown when the service confirms that
560 * all data has been written to disk.
562 * @param cls our `struct GNUNET_STATISTICS_Handle *`
563 * @param msg the message
566 handle_test (void *cls,
567 const struct GNUNET_MessageHeader *msg)
569 struct GNUNET_STATISTICS_Handle *h = cls;
571 if (GNUNET_SYSERR != h->do_destroy)
573 /* not in shutdown, why do we get 'TEST'? */
579 h->do_destroy = GNUNET_NO;
580 GNUNET_SCHEDULER_add_now (&destroy_task,
586 * Handle a #GNUNET_MESSAGE_TYPE_STATISTICS_END message. We receive
587 * this message in response to a query to indicate that there are no
588 * further matching results.
590 * @param cls our `struct GNUNET_STATISTICS_Handle *`
591 * @param msg the message
594 handle_statistics_end (void *cls,
595 const struct GNUNET_MessageHeader *msg)
597 struct GNUNET_STATISTICS_Handle *h = cls;
598 struct GNUNET_STATISTICS_GetHandle *c;
600 LOG (GNUNET_ERROR_TYPE_DEBUG,
601 "Received end of statistics marker\n");
602 if (NULL == (c = h->current))
609 h->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
618 free_action_item (c);
623 * Try to (re)connect to the statistics service.
625 * @param h statistics handle to reconnect
626 * @return #GNUNET_YES on success, #GNUNET_NO on failure.
629 try_connect (struct GNUNET_STATISTICS_Handle *h)
631 GNUNET_MQ_hd_fixed_size (test,
632 GNUNET_MESSAGE_TYPE_TEST,
633 struct GNUNET_MessageHeader);
634 GNUNET_MQ_hd_fixed_size (statistics_end,
635 GNUNET_MESSAGE_TYPE_STATISTICS_END,
636 struct GNUNET_MessageHeader);
637 GNUNET_MQ_hd_var_size (statistics_value,
638 GNUNET_MESSAGE_TYPE_STATISTICS_VALUE,
639 struct GNUNET_STATISTICS_ReplyMessage);
640 GNUNET_MQ_hd_fixed_size (statistics_watch_value,
641 GNUNET_MESSAGE_TYPE_STATISTICS_WATCH_VALUE,
642 struct GNUNET_STATISTICS_WatchValueMessage);
643 struct GNUNET_MQ_MessageHandler handlers[] = {
644 make_test_handler (h),
645 make_statistics_end_handler (h),
646 make_statistics_value_handler (h),
647 make_statistics_watch_value_handler (h),
648 GNUNET_MQ_handler_end ()
650 struct GNUNET_STATISTICS_GetHandle *gh;
651 struct GNUNET_STATISTICS_GetHandle *gn;
653 if (NULL != h->backoff_task)
657 h->mq = GNUNET_CLIENT_connecT (h->cfg,
664 LOG (GNUNET_ERROR_TYPE_DEBUG,
665 "Failed to connect to statistics service!\n");
669 while (NULL != (gh = gn))
672 if (gh->type == ACTION_WATCH)
674 GNUNET_CONTAINER_DLL_remove (h->action_head,
677 free_action_item (gh);
680 for (unsigned int i = 0; i < h->watches_size; i++)
681 if (NULL != h->watches[i])
682 schedule_watch_request (h,
689 * We've waited long enough, reconnect now.
691 * @param cls the `struct GNUNET_STATISTICS_Handle` to reconnect
694 reconnect_task (void *cls)
696 struct GNUNET_STATISTICS_Handle *h = cls;
698 h->backoff_task = NULL;
704 * Task used by #reconnect_later() to shutdown the handle
706 * @param cls the statistics handle
709 do_destroy (void *cls)
711 struct GNUNET_STATISTICS_Handle *h = cls;
713 GNUNET_STATISTICS_destroy (h,
719 * Reconnect at a later time, respecting back-off.
721 * @param h statistics handle
724 reconnect_later (struct GNUNET_STATISTICS_Handle *h)
727 struct GNUNET_STATISTICS_GetHandle *gh;
729 GNUNET_assert (NULL == h->backoff_task);
730 if (GNUNET_YES == h->do_destroy)
732 /* So we are shutting down and the service is not reachable.
733 * Chances are that it's down for good and we are not going to connect to
735 * Give up and don't sync the rest of the data.
738 for (gh = h->action_head; NULL != gh; gh = gh->next)
739 if ( (gh->make_persistent) && (ACTION_SET == gh->type) )
741 if (GNUNET_YES == loss)
742 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
743 _("Could not save some persistent statistics\n"));
744 h->do_destroy = GNUNET_NO;
745 GNUNET_SCHEDULER_add_now (&do_destroy,
750 = GNUNET_SCHEDULER_add_delayed (h->backoff,
753 h->backoff = GNUNET_TIME_STD_BACKOFF (h->backoff);
759 * Transmit a GET request (and if successful, start to receive
762 * @param handle statistics handle
765 transmit_get (struct GNUNET_STATISTICS_Handle *handle)
767 struct GNUNET_STATISTICS_GetHandle *c;
768 struct GNUNET_MessageHeader *hdr;
769 struct GNUNET_MQ_Envelope *env;
773 GNUNET_assert (NULL != (c = handle->current));
774 slen1 = strlen (c->subsystem) + 1;
775 slen2 = strlen (c->name) + 1;
776 env = GNUNET_MQ_msg_extra (hdr,
778 GNUNET_MESSAGE_TYPE_STATISTICS_GET);
779 GNUNET_assert (slen1 + slen2 ==
780 GNUNET_STRINGS_buffer_fill ((char *) &hdr[1],
785 GNUNET_MQ_send (handle->mq,
791 * Transmit a WATCH request (and if successful, start to receive
794 * @param handle statistics handle
797 transmit_watch (struct GNUNET_STATISTICS_Handle *handle)
799 struct GNUNET_MessageHeader *hdr;
800 struct GNUNET_MQ_Envelope *env;
804 LOG (GNUNET_ERROR_TYPE_DEBUG,
805 "Transmitting watch request for `%s'\n",
806 handle->current->name);
807 slen1 = strlen (handle->current->subsystem) + 1;
808 slen2 = strlen (handle->current->name) + 1;
809 env = GNUNET_MQ_msg_extra (hdr,
811 GNUNET_MESSAGE_TYPE_STATISTICS_WATCH);
812 GNUNET_assert (slen1 + slen2 ==
813 GNUNET_STRINGS_buffer_fill ((char *) &hdr[1],
816 handle->current->subsystem,
817 handle->current->name));
818 GNUNET_MQ_send (handle->mq,
820 GNUNET_assert (NULL == handle->current->cont);
821 free_action_item (handle->current);
822 handle->current = NULL;
827 * Transmit a SET/UPDATE request.
829 * @param handle statistics handle
832 transmit_set (struct GNUNET_STATISTICS_Handle *handle)
834 struct GNUNET_STATISTICS_SetMessage *r;
835 struct GNUNET_MQ_Envelope *env;
839 slen = strlen (handle->current->subsystem) + 1;
840 nlen = strlen (handle->current->name) + 1;
841 env = GNUNET_MQ_msg_extra (r,
843 GNUNET_MESSAGE_TYPE_STATISTICS_SET);
845 r->value = GNUNET_htonll (handle->current->value);
846 if (handle->current->make_persistent)
847 r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_PERSISTENT);
848 if (handle->current->type == ACTION_UPDATE)
849 r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_RELATIVE);
850 GNUNET_assert (slen + nlen ==
851 GNUNET_STRINGS_buffer_fill ((char *) &r[1],
854 handle->current->subsystem,
855 handle->current->name));
856 GNUNET_assert (NULL == handle->current->cont);
857 free_action_item (handle->current);
858 handle->current = NULL;
859 update_memory_statistics (handle);
860 GNUNET_MQ_send (handle->mq,
866 * Get handle for the statistics service.
868 * @param subsystem name of subsystem using the service
869 * @param cfg services configuration in use
870 * @return handle to use
872 struct GNUNET_STATISTICS_Handle *
873 GNUNET_STATISTICS_create (const char *subsystem,
874 const struct GNUNET_CONFIGURATION_Handle *cfg)
876 struct GNUNET_STATISTICS_Handle *ret;
879 GNUNET_CONFIGURATION_get_value_yesno (cfg,
883 ret = GNUNET_new (struct GNUNET_STATISTICS_Handle);
885 ret->subsystem = GNUNET_strdup (subsystem);
886 ret->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
892 * Destroy a handle (free all state associated with
895 * @param h statistics handle to destroy
896 * @param sync_first set to #GNUNET_YES if pending SET requests should
900 GNUNET_STATISTICS_destroy (struct GNUNET_STATISTICS_Handle *h,
903 struct GNUNET_STATISTICS_GetHandle *pos;
904 struct GNUNET_STATISTICS_GetHandle *next;
908 GNUNET_assert (GNUNET_NO == h->do_destroy); // Don't call twice.
909 if (NULL != h->backoff_task)
911 GNUNET_SCHEDULER_cancel (h->backoff_task);
912 h->backoff_task = NULL;
915 (GNUNET_YES == try_connect (h)) )
917 if ( (NULL != h->current) &&
918 (ACTION_GET == h->current->type) )
919 h->current->aborted = GNUNET_YES;
920 next = h->action_head;
921 while (NULL != (pos = next))
924 if ( (ACTION_GET == pos->type) ||
925 (ACTION_WATCH == pos->type) ||
926 (GNUNET_NO == pos->make_persistent) )
928 GNUNET_CONTAINER_DLL_remove (h->action_head,
931 free_action_item (pos);
934 h->do_destroy = GNUNET_YES;
936 return; /* do not finish destruction just yet */
938 /* do clean up all */
939 while (NULL != (pos = h->action_head))
941 GNUNET_CONTAINER_DLL_remove (h->action_head,
944 free_action_item (pos);
947 for (unsigned int i = 0; i < h->watches_size; i++)
949 if (NULL == h->watches[i])
951 GNUNET_free (h->watches[i]->subsystem);
952 GNUNET_free (h->watches[i]->name);
953 GNUNET_free (h->watches[i]);
955 GNUNET_array_grow (h->watches,
958 GNUNET_free (h->subsystem);
964 * Schedule the next action to be performed.
966 * @param h statistics handle
969 schedule_action (struct GNUNET_STATISTICS_Handle *h)
971 if (NULL != h->backoff_task)
972 return; /* action already pending */
973 if (GNUNET_YES != try_connect (h))
978 /* schedule next action */
979 while (NULL == h->current)
981 h->current = h->action_head;
982 if (NULL == h->current)
984 struct GNUNET_MessageHeader *hdr;
985 struct GNUNET_MQ_Envelope *env;
987 if (GNUNET_YES != h->do_destroy)
988 return; /* nothing to do */
989 /* let service know that we're done */
990 h->do_destroy = GNUNET_SYSERR; /* in 'TEST' mode */
991 env = GNUNET_MQ_msg (hdr,
992 GNUNET_MESSAGE_TYPE_TEST);
993 GNUNET_MQ_send (h->mq,
997 GNUNET_CONTAINER_DLL_remove (h->action_head,
1000 switch (h->current->type)
1021 * Get statistic from the peer.
1023 * @param handle identification of the statistics service
1024 * @param subsystem limit to the specified subsystem, NULL for our subsystem
1025 * @param name name of the statistic value, NULL for all values
1026 * @param cont continuation to call when done (can be NULL)
1027 * This callback CANNOT destroy the statistics handle in the same call.
1028 * @param proc function to call on each value
1029 * @param cls closure for @a cont and @a proc
1030 * @return NULL on error
1032 struct GNUNET_STATISTICS_GetHandle *
1033 GNUNET_STATISTICS_get (struct GNUNET_STATISTICS_Handle *handle,
1034 const char *subsystem,
1036 GNUNET_STATISTICS_Callback cont,
1037 GNUNET_STATISTICS_Iterator proc,
1042 struct GNUNET_STATISTICS_GetHandle *ai;
1046 GNUNET_assert (NULL != proc);
1047 GNUNET_assert (GNUNET_NO == handle->do_destroy);
1048 if (NULL == subsystem)
1052 slen1 = strlen (subsystem) + 1;
1053 slen2 = strlen (name) + 1;
1054 GNUNET_assert (slen1 + slen2 + sizeof (struct GNUNET_MessageHeader) <
1055 GNUNET_SERVER_MAX_MESSAGE_SIZE);
1056 ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle);
1058 ai->subsystem = GNUNET_strdup (subsystem);
1059 ai->name = GNUNET_strdup (name);
1063 ai->type = ACTION_GET;
1064 ai->msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
1065 GNUNET_CONTAINER_DLL_insert_tail (handle->action_head,
1066 handle->action_tail,
1068 schedule_action (handle);
1074 * Cancel a 'get' request. Must be called before the 'cont'
1075 * function is called.
1077 * @param gh handle of the request to cancel
1080 GNUNET_STATISTICS_get_cancel (struct GNUNET_STATISTICS_GetHandle *gh)
1085 if (gh->sh->current == gh)
1087 gh->aborted = GNUNET_YES;
1090 GNUNET_CONTAINER_DLL_remove (gh->sh->action_head,
1091 gh->sh->action_tail,
1093 GNUNET_free (gh->name);
1094 GNUNET_free (gh->subsystem);
1100 * Watch statistics from the peer (be notified whenever they change).
1102 * @param handle identification of the statistics service
1103 * @param subsystem limit to the specified subsystem, never NULL
1104 * @param name name of the statistic value, never NULL
1105 * @param proc function to call on each value
1106 * @param proc_cls closure for @a proc
1107 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1110 GNUNET_STATISTICS_watch (struct GNUNET_STATISTICS_Handle *handle,
1111 const char *subsystem,
1113 GNUNET_STATISTICS_Iterator proc,
1116 struct GNUNET_STATISTICS_WatchEntry *w;
1119 return GNUNET_SYSERR;
1120 w = GNUNET_new (struct GNUNET_STATISTICS_WatchEntry);
1121 w->subsystem = GNUNET_strdup (subsystem);
1122 w->name = GNUNET_strdup (name);
1124 w->proc_cls = proc_cls;
1125 GNUNET_array_append (handle->watches,
1126 handle->watches_size,
1128 schedule_watch_request (handle,
1135 * Stop watching statistics from the peer.
1137 * @param handle identification of the statistics service
1138 * @param subsystem limit to the specified subsystem, never NULL
1139 * @param name name of the statistic value, never NULL
1140 * @param proc function to call on each value
1141 * @param proc_cls closure for @a proc
1142 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error (no such watch)
1145 GNUNET_STATISTICS_watch_cancel (struct GNUNET_STATISTICS_Handle *handle,
1146 const char *subsystem,
1148 GNUNET_STATISTICS_Iterator proc,
1151 struct GNUNET_STATISTICS_WatchEntry *w;
1154 return GNUNET_SYSERR;
1155 for (unsigned int i=0;i<handle->watches_size;i++)
1157 w = handle->watches[i];
1160 if ( (w->proc == proc) &&
1161 (w->proc_cls == proc_cls) &&
1162 (0 == strcmp (w->name, name)) &&
1163 (0 == strcmp (w->subsystem, subsystem)) )
1165 GNUNET_free (w->name);
1166 GNUNET_free (w->subsystem);
1168 handle->watches[i] = NULL;
1172 return GNUNET_SYSERR;
1177 * Queue a request to change a statistic.
1179 * @param h statistics handle
1180 * @param name name of the value
1181 * @param make_persistent should the value be kept across restarts?
1182 * @param value new value or change
1183 * @param type type of the action (#ACTION_SET or #ACTION_UPDATE)
1186 add_setter_action (struct GNUNET_STATISTICS_Handle *h,
1188 int make_persistent,
1190 enum ActionType type)
1192 struct GNUNET_STATISTICS_GetHandle *ai;
1198 slen = strlen (h->subsystem) + 1;
1199 nlen = strlen (name) + 1;
1200 nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
1201 if (nsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
1206 for (ai = h->action_head; NULL != ai; ai = ai->next)
1208 if (! ( (0 == strcmp (ai->subsystem, h->subsystem)) &&
1209 (0 == strcmp (ai->name, name)) &&
1210 ( (ACTION_UPDATE == ai->type) ||
1211 (ACTION_SET == ai->type) ) ) )
1213 if (ACTION_SET == ai->type)
1215 if (ACTION_UPDATE == type)
1217 delta = (int64_t) value;
1220 /* update old set by new delta */
1225 /* update old set by new delta, but never go negative */
1226 if (ai->value < -delta)
1234 /* new set overrides old set */
1240 if (ACTION_UPDATE == type)
1242 /* make delta cummulative */
1243 delta = (int64_t) value;
1248 /* drop old 'update', use new 'set' instead */
1253 ai->timeout = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT);
1254 ai->make_persistent = make_persistent;
1257 /* no existing entry matches, create a fresh one */
1258 ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle);
1260 ai->subsystem = GNUNET_strdup (h->subsystem);
1261 ai->name = GNUNET_strdup (name);
1262 ai->timeout = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT);
1263 ai->make_persistent = make_persistent;
1267 GNUNET_CONTAINER_DLL_insert_tail (h->action_head,
1270 schedule_action (h);
1275 * Set statistic value for the peer. Will always use our
1276 * subsystem (the argument used when "handle" was created).
1278 * @param handle identification of the statistics service
1279 * @param name name of the statistic value
1280 * @param value new value to set
1281 * @param make_persistent should the value be kept across restarts?
1284 GNUNET_STATISTICS_set (struct GNUNET_STATISTICS_Handle *handle,
1287 int make_persistent)
1291 GNUNET_assert (GNUNET_NO == handle->do_destroy);
1292 add_setter_action (handle,
1301 * Set statistic value for the peer. Will always use our
1302 * subsystem (the argument used when "handle" was created).
1304 * @param handle identification of the statistics service
1305 * @param name name of the statistic value
1306 * @param delta change in value (added to existing value)
1307 * @param make_persistent should the value be kept across restarts?
1310 GNUNET_STATISTICS_update (struct GNUNET_STATISTICS_Handle *handle,
1313 int make_persistent)
1319 GNUNET_assert (GNUNET_NO == handle->do_destroy);
1320 add_setter_action (handle,
1328 /* end of statistics_api.c */