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 * Task for running #do_destroy().
228 struct GNUNET_SCHEDULER_Task *destroy_task;
231 * Time for next connect retry.
233 struct GNUNET_TIME_Relative backoff;
236 * Maximum heap size observed so far (if available).
238 uint64_t peak_heap_size;
241 * Maximum resident set side observed so far (if available).
246 * Size of the @e watches array.
248 unsigned int watches_size;
251 * Should this handle auto-destruct once all actions have
257 * Are we currently receiving from the service?
265 * Obtain statistics about this process's memory consumption and
266 * report those as well (if they changed).
269 update_memory_statistics (struct GNUNET_STATISTICS_Handle *h)
271 #if ENABLE_HEAP_STATISTICS
272 uint64_t current_heap_size = 0;
273 uint64_t current_rss = 0;
275 if (GNUNET_NO != h->do_destroy)
282 current_heap_size = mi.uordblks + mi.fordblks;
289 if (0 == getrusage (RUSAGE_SELF, &ru))
291 current_rss = 1024LL * ru.ru_maxrss;
295 if (current_heap_size > h->peak_heap_size)
297 h->peak_heap_size = current_heap_size;
298 GNUNET_STATISTICS_set (h, "# peak heap size", current_heap_size, GNUNET_NO);
300 if (current_rss > h->peak_rss)
302 h->peak_rss = current_rss;
303 GNUNET_STATISTICS_set (h, "# peak resident set size", current_rss, GNUNET_NO);
310 * Reconnect at a later time, respecting back-off.
312 * @param h statistics handle
315 reconnect_later (struct GNUNET_STATISTICS_Handle *h);
319 * Schedule the next action to be performed.
321 * @param cls statistics handle to reconnect
324 schedule_action (void *cls);
328 * Transmit request to service that we want to watch
329 * the development of a particular value.
331 * @param h statistics handle
332 * @param watch watch entry of the value to watch
335 schedule_watch_request (struct GNUNET_STATISTICS_Handle *h,
336 struct GNUNET_STATISTICS_WatchEntry *watch)
338 struct GNUNET_STATISTICS_GetHandle *ai;
343 slen = strlen (watch->subsystem) + 1;
344 nlen = strlen (watch->name) + 1;
345 nsize = sizeof (struct GNUNET_MessageHeader) + slen + nlen;
346 if (nsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
351 ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle);
353 ai->subsystem = GNUNET_strdup (watch->subsystem);
354 ai->name = GNUNET_strdup (watch->name);
355 ai->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
357 ai->type = ACTION_WATCH;
358 ai->proc = watch->proc;
359 ai->cls = watch->proc_cls;
360 GNUNET_CONTAINER_DLL_insert_tail (h->action_head,
368 * Free memory associated with the given action item.
370 * @param gh action item to free
373 free_action_item (struct GNUNET_STATISTICS_GetHandle *gh)
375 GNUNET_free_non_null (gh->subsystem);
376 GNUNET_free_non_null (gh->name);
382 * Disconnect from the statistics service.
384 * @param h statistics handle to disconnect from
387 do_disconnect (struct GNUNET_STATISTICS_Handle *h)
389 struct GNUNET_STATISTICS_GetHandle *c;
391 h->receiving = GNUNET_NO;
392 if (NULL != (c = h->current))
395 if ( (NULL != c->cont) &&
396 (GNUNET_YES != c->aborted) )
402 free_action_item (c);
406 GNUNET_MQ_destroy (h->mq);
413 * Process a #GNUNET_MESSAGE_TYPE_STATISTICS_VALUE message.
415 * @param cls statistics handle
416 * @param smsg message received from the service, never NULL
417 * @return #GNUNET_OK if the message was well-formed
420 check_statistics_value (void *cls,
421 const struct GNUNET_STATISTICS_ReplyMessage *smsg)
427 size = ntohs (smsg->header.size);
428 size -= sizeof (struct GNUNET_STATISTICS_ReplyMessage);
430 GNUNET_STRINGS_buffer_tokenize ((const char *) &smsg[1],
437 return GNUNET_SYSERR;
444 * Process a #GNUNET_MESSAGE_TYPE_STATISTICS_VALUE message.
446 * @param cls statistics handle
447 * @param msg message received from the service, never NULL
448 * @return #GNUNET_OK if the message was well-formed
451 handle_statistics_value (void *cls,
452 const struct GNUNET_STATISTICS_ReplyMessage *smsg)
454 struct GNUNET_STATISTICS_Handle *h = cls;
459 if (h->current->aborted)
460 return; /* iteration aborted, don't bother */
462 size = ntohs (smsg->header.size);
463 size -= sizeof (struct GNUNET_STATISTICS_ReplyMessage);
464 GNUNET_assert (size ==
465 GNUNET_STRINGS_buffer_tokenize ((const char *) &smsg[1],
470 LOG (GNUNET_ERROR_TYPE_DEBUG,
471 "Received valid statistic on `%s:%s': %llu\n",
473 GNUNET_ntohll (smsg->value));
475 h->current->proc (h->current->cls,
478 GNUNET_ntohll (smsg->value),
480 (ntohl (smsg->uid) & GNUNET_STATISTICS_PERSIST_BIT)))
482 LOG (GNUNET_ERROR_TYPE_DEBUG,
483 "Processing of remaining statistics aborted by client.\n");
484 h->current->aborted = GNUNET_YES;
490 * We have received a watch value from the service. Process it.
492 * @param cls statistics handle
493 * @param msg the watch value message
496 handle_statistics_watch_value (void *cls,
497 const struct GNUNET_STATISTICS_WatchValueMessage *wvm)
499 struct GNUNET_STATISTICS_Handle *h = cls;
500 struct GNUNET_STATISTICS_WatchEntry *w;
503 GNUNET_break (0 == ntohl (wvm->reserved));
504 wid = ntohl (wvm->wid);
505 if (wid >= h->watches_size)
514 (void) w->proc (w->proc_cls,
517 GNUNET_ntohll (wvm->value),
518 0 != (ntohl (wvm->flags) & GNUNET_STATISTICS_PERSIST_BIT));
523 * Generic error handler, called with the appropriate error code and
524 * the same closure specified at the creation of the message queue.
525 * Not every message queue implementation supports an error handler.
527 * @param cls closure with the `struct GNUNET_STATISTICS_Handle *`
528 * @param error error code
531 mq_error_handler (void *cls,
532 enum GNUNET_MQ_Error error)
534 struct GNUNET_STATISTICS_Handle *h = cls;
536 if (GNUNET_NO != h->do_destroy)
538 h->do_destroy = GNUNET_NO;
539 if (NULL != h->destroy_task)
541 GNUNET_SCHEDULER_cancel (h->destroy_task);
542 h->destroy_task = NULL;
544 GNUNET_STATISTICS_destroy (h,
554 * Task used to destroy the statistics handle.
556 * @param cls the `struct GNUNET_STATISTICS_Handle`
559 do_destroy (void *cls)
561 struct GNUNET_STATISTICS_Handle *h = cls;
563 h->destroy_task = NULL;
564 h->do_destroy = GNUNET_NO;
565 LOG (GNUNET_ERROR_TYPE_DEBUG,
566 "Running final destruction\n");
567 GNUNET_STATISTICS_destroy (h,
573 * Handle a #GNUNET_MESSAGE_TYPE_TEST (sic) message. We receive this
574 * message at the end of the shutdown when the service confirms that
575 * all data has been written to disk.
577 * @param cls our `struct GNUNET_STATISTICS_Handle *`
578 * @param msg the message
581 handle_test (void *cls,
582 const struct GNUNET_MessageHeader *msg)
584 struct GNUNET_STATISTICS_Handle *h = cls;
586 if (GNUNET_SYSERR != h->do_destroy)
588 /* not in shutdown, why do we get 'TEST'? */
594 LOG (GNUNET_ERROR_TYPE_DEBUG,
595 "Received TEST message from statistics, can complete disconnect\n");
596 if (NULL != h->destroy_task)
597 GNUNET_SCHEDULER_cancel (h->destroy_task);
598 h->destroy_task = GNUNET_SCHEDULER_add_now (&do_destroy,
604 * Handle a #GNUNET_MESSAGE_TYPE_STATISTICS_END message. We receive
605 * this message in response to a query to indicate that there are no
606 * further matching results.
608 * @param cls our `struct GNUNET_STATISTICS_Handle *`
609 * @param msg the message
612 handle_statistics_end (void *cls,
613 const struct GNUNET_MessageHeader *msg)
615 struct GNUNET_STATISTICS_Handle *h = cls;
616 struct GNUNET_STATISTICS_GetHandle *c;
618 LOG (GNUNET_ERROR_TYPE_DEBUG,
619 "Received end of statistics marker\n");
620 if (NULL == (c = h->current))
627 h->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
636 free_action_item (c);
641 * Try to (re)connect to the statistics service.
643 * @param h statistics handle to reconnect
644 * @return #GNUNET_YES on success, #GNUNET_NO on failure.
647 try_connect (struct GNUNET_STATISTICS_Handle *h)
649 GNUNET_MQ_hd_fixed_size (test,
650 GNUNET_MESSAGE_TYPE_TEST,
651 struct GNUNET_MessageHeader);
652 GNUNET_MQ_hd_fixed_size (statistics_end,
653 GNUNET_MESSAGE_TYPE_STATISTICS_END,
654 struct GNUNET_MessageHeader);
655 GNUNET_MQ_hd_var_size (statistics_value,
656 GNUNET_MESSAGE_TYPE_STATISTICS_VALUE,
657 struct GNUNET_STATISTICS_ReplyMessage);
658 GNUNET_MQ_hd_fixed_size (statistics_watch_value,
659 GNUNET_MESSAGE_TYPE_STATISTICS_WATCH_VALUE,
660 struct GNUNET_STATISTICS_WatchValueMessage);
661 struct GNUNET_MQ_MessageHandler handlers[] = {
662 make_test_handler (h),
663 make_statistics_end_handler (h),
664 make_statistics_value_handler (h),
665 make_statistics_watch_value_handler (h),
666 GNUNET_MQ_handler_end ()
668 struct GNUNET_STATISTICS_GetHandle *gh;
669 struct GNUNET_STATISTICS_GetHandle *gn;
671 if (NULL != h->backoff_task)
675 h->mq = GNUNET_CLIENT_connecT (h->cfg,
682 LOG (GNUNET_ERROR_TYPE_DEBUG,
683 "Failed to connect to statistics service!\n");
687 while (NULL != (gh = gn))
690 if (gh->type == ACTION_WATCH)
692 GNUNET_CONTAINER_DLL_remove (h->action_head,
695 free_action_item (gh);
698 for (unsigned int i = 0; i < h->watches_size; i++)
699 if (NULL != h->watches[i])
700 schedule_watch_request (h,
707 * We've waited long enough, reconnect now.
709 * @param cls the `struct GNUNET_STATISTICS_Handle` to reconnect
712 reconnect_task (void *cls)
714 struct GNUNET_STATISTICS_Handle *h = cls;
716 h->backoff_task = NULL;
722 * Reconnect at a later time, respecting back-off.
724 * @param h statistics handle
727 reconnect_later (struct GNUNET_STATISTICS_Handle *h)
730 struct GNUNET_STATISTICS_GetHandle *gh;
732 GNUNET_assert (NULL == h->backoff_task);
733 if (GNUNET_YES == h->do_destroy)
735 /* So we are shutting down and the service is not reachable.
736 * Chances are that it's down for good and we are not going to connect to
738 * Give up and don't sync the rest of the data.
741 for (gh = h->action_head; NULL != gh; gh = gh->next)
742 if ( (gh->make_persistent) && (ACTION_SET == gh->type) )
744 if (GNUNET_YES == loss)
745 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
746 _("Could not save some persistent statistics\n"));
747 if (NULL != h->destroy_task)
748 GNUNET_SCHEDULER_cancel (h->destroy_task);
749 h->destroy_task = GNUNET_SCHEDULER_add_now (&do_destroy,
754 = GNUNET_SCHEDULER_add_delayed (h->backoff,
757 h->backoff = GNUNET_TIME_STD_BACKOFF (h->backoff);
763 * Transmit a GET request (and if successful, start to receive
766 * @param handle statistics handle
769 transmit_get (struct GNUNET_STATISTICS_Handle *handle)
771 struct GNUNET_STATISTICS_GetHandle *c;
772 struct GNUNET_MessageHeader *hdr;
773 struct GNUNET_MQ_Envelope *env;
777 GNUNET_assert (NULL != (c = handle->current));
778 slen1 = strlen (c->subsystem) + 1;
779 slen2 = strlen (c->name) + 1;
780 env = GNUNET_MQ_msg_extra (hdr,
782 GNUNET_MESSAGE_TYPE_STATISTICS_GET);
783 GNUNET_assert (slen1 + slen2 ==
784 GNUNET_STRINGS_buffer_fill ((char *) &hdr[1],
789 GNUNET_MQ_notify_sent (env,
792 GNUNET_MQ_send (handle->mq,
798 * Transmit a WATCH request (and if successful, start to receive
801 * @param handle statistics handle
804 transmit_watch (struct GNUNET_STATISTICS_Handle *handle)
806 struct GNUNET_MessageHeader *hdr;
807 struct GNUNET_MQ_Envelope *env;
811 LOG (GNUNET_ERROR_TYPE_DEBUG,
812 "Transmitting watch request for `%s'\n",
813 handle->current->name);
814 slen1 = strlen (handle->current->subsystem) + 1;
815 slen2 = strlen (handle->current->name) + 1;
816 env = GNUNET_MQ_msg_extra (hdr,
818 GNUNET_MESSAGE_TYPE_STATISTICS_WATCH);
819 GNUNET_assert (slen1 + slen2 ==
820 GNUNET_STRINGS_buffer_fill ((char *) &hdr[1],
823 handle->current->subsystem,
824 handle->current->name));
825 GNUNET_MQ_notify_sent (env,
828 GNUNET_MQ_send (handle->mq,
830 GNUNET_assert (NULL == handle->current->cont);
831 free_action_item (handle->current);
832 handle->current = NULL;
833 schedule_action (handle);
838 * Transmit a SET/UPDATE request.
840 * @param handle statistics handle
843 transmit_set (struct GNUNET_STATISTICS_Handle *handle)
845 struct GNUNET_STATISTICS_SetMessage *r;
846 struct GNUNET_MQ_Envelope *env;
850 slen = strlen (handle->current->subsystem) + 1;
851 nlen = strlen (handle->current->name) + 1;
852 env = GNUNET_MQ_msg_extra (r,
854 GNUNET_MESSAGE_TYPE_STATISTICS_SET);
856 r->value = GNUNET_htonll (handle->current->value);
857 if (handle->current->make_persistent)
858 r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_PERSISTENT);
859 if (handle->current->type == ACTION_UPDATE)
860 r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_RELATIVE);
861 GNUNET_assert (slen + nlen ==
862 GNUNET_STRINGS_buffer_fill ((char *) &r[1],
865 handle->current->subsystem,
866 handle->current->name));
867 GNUNET_assert (NULL == handle->current->cont);
868 free_action_item (handle->current);
869 handle->current = NULL;
870 update_memory_statistics (handle);
871 GNUNET_MQ_notify_sent (env,
874 GNUNET_MQ_send (handle->mq,
880 * Get handle for the statistics service.
882 * @param subsystem name of subsystem using the service
883 * @param cfg services configuration in use
884 * @return handle to use
886 struct GNUNET_STATISTICS_Handle *
887 GNUNET_STATISTICS_create (const char *subsystem,
888 const struct GNUNET_CONFIGURATION_Handle *cfg)
890 struct GNUNET_STATISTICS_Handle *h;
893 GNUNET_CONFIGURATION_get_value_yesno (cfg,
897 h = GNUNET_new (struct GNUNET_STATISTICS_Handle);
899 h->subsystem = GNUNET_strdup (subsystem);
900 h->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
906 * Destroy a handle (free all state associated with
909 * @param h statistics handle to destroy
910 * @param sync_first set to #GNUNET_YES if pending SET requests should
914 GNUNET_STATISTICS_destroy (struct GNUNET_STATISTICS_Handle *h,
917 struct GNUNET_STATISTICS_GetHandle *pos;
918 struct GNUNET_STATISTICS_GetHandle *next;
922 GNUNET_assert (GNUNET_NO == h->do_destroy); /* Don't call twice. */
924 (GNUNET_YES == try_connect (h)) )
926 if ( (NULL != h->current) &&
927 (ACTION_GET == h->current->type) )
928 h->current->aborted = GNUNET_YES;
929 next = h->action_head;
930 while (NULL != (pos = next))
933 if ( (ACTION_GET == pos->type) ||
934 (ACTION_WATCH == pos->type) ||
935 (GNUNET_NO == pos->make_persistent) )
937 GNUNET_CONTAINER_DLL_remove (h->action_head,
940 free_action_item (pos);
943 h->do_destroy = GNUNET_YES;
945 GNUNET_assert (NULL == h->destroy_task);
947 = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (h->backoff,
951 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
952 "Deferring destruction\n");
953 return; /* do not finish destruction just yet */
955 /* do clean up all */
956 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
957 "Cleaning all up\n");
958 while (NULL != (pos = h->action_head))
960 GNUNET_CONTAINER_DLL_remove (h->action_head,
963 free_action_item (pos);
966 if (NULL != h->backoff_task)
968 GNUNET_SCHEDULER_cancel (h->backoff_task);
969 h->backoff_task = NULL;
971 if (NULL != h->destroy_task)
974 GNUNET_SCHEDULER_cancel (h->destroy_task);
975 h->destroy_task = NULL;
977 for (unsigned int i = 0; i < h->watches_size; i++)
979 if (NULL == h->watches[i])
981 GNUNET_free (h->watches[i]->subsystem);
982 GNUNET_free (h->watches[i]->name);
983 GNUNET_free (h->watches[i]);
985 GNUNET_array_grow (h->watches,
988 GNUNET_free (h->subsystem);
994 * Schedule the next action to be performed.
996 * @param cls statistics handle
999 schedule_action (void *cls)
1001 struct GNUNET_STATISTICS_Handle *h = cls;
1003 if (NULL != h->backoff_task)
1004 return; /* action already pending */
1005 if (GNUNET_YES != try_connect (h))
1007 reconnect_later (h);
1010 if (0 < GNUNET_MQ_get_length (h->mq) )
1011 return; /* Wait for queue to be reduced more */
1012 /* schedule next action */
1013 while (NULL == h->current)
1015 h->current = h->action_head;
1016 if (NULL == h->current)
1018 struct GNUNET_MessageHeader *hdr;
1019 struct GNUNET_MQ_Envelope *env;
1021 if (GNUNET_YES != h->do_destroy)
1022 return; /* nothing to do */
1023 /* let service know that we're done */
1024 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1025 "Notifying service that we are done\n");
1026 h->do_destroy = GNUNET_SYSERR; /* in 'TEST' mode */
1027 env = GNUNET_MQ_msg (hdr,
1028 GNUNET_MESSAGE_TYPE_TEST);
1029 GNUNET_MQ_notify_sent (env,
1032 GNUNET_MQ_send (h->mq,
1036 GNUNET_CONTAINER_DLL_remove (h->action_head,
1039 switch (h->current->type)
1060 * Get statistic from the peer.
1062 * @param handle identification of the statistics service
1063 * @param subsystem limit to the specified subsystem, NULL for our subsystem
1064 * @param name name of the statistic value, NULL for all values
1065 * @param cont continuation to call when done (can be NULL)
1066 * This callback CANNOT destroy the statistics handle in the same call.
1067 * @param proc function to call on each value
1068 * @param cls closure for @a cont and @a proc
1069 * @return NULL on error
1071 struct GNUNET_STATISTICS_GetHandle *
1072 GNUNET_STATISTICS_get (struct GNUNET_STATISTICS_Handle *handle,
1073 const char *subsystem,
1075 GNUNET_STATISTICS_Callback cont,
1076 GNUNET_STATISTICS_Iterator proc,
1081 struct GNUNET_STATISTICS_GetHandle *ai;
1085 GNUNET_assert (NULL != proc);
1086 GNUNET_assert (GNUNET_NO == handle->do_destroy);
1087 if (NULL == subsystem)
1091 slen1 = strlen (subsystem) + 1;
1092 slen2 = strlen (name) + 1;
1093 GNUNET_assert (slen1 + slen2 + sizeof (struct GNUNET_MessageHeader) <
1094 GNUNET_SERVER_MAX_MESSAGE_SIZE);
1095 ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle);
1097 ai->subsystem = GNUNET_strdup (subsystem);
1098 ai->name = GNUNET_strdup (name);
1102 ai->type = ACTION_GET;
1103 ai->msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
1104 GNUNET_CONTAINER_DLL_insert_tail (handle->action_head,
1105 handle->action_tail,
1107 schedule_action (handle);
1113 * Cancel a 'get' request. Must be called before the 'cont'
1114 * function is called.
1116 * @param gh handle of the request to cancel
1119 GNUNET_STATISTICS_get_cancel (struct GNUNET_STATISTICS_GetHandle *gh)
1124 if (gh->sh->current == gh)
1126 gh->aborted = GNUNET_YES;
1129 GNUNET_CONTAINER_DLL_remove (gh->sh->action_head,
1130 gh->sh->action_tail,
1132 GNUNET_free (gh->name);
1133 GNUNET_free (gh->subsystem);
1139 * Watch statistics from the peer (be notified whenever they change).
1141 * @param handle identification of the statistics service
1142 * @param subsystem limit to the specified subsystem, never NULL
1143 * @param name name of the statistic value, never NULL
1144 * @param proc function to call on each value
1145 * @param proc_cls closure for @a proc
1146 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1149 GNUNET_STATISTICS_watch (struct GNUNET_STATISTICS_Handle *handle,
1150 const char *subsystem,
1152 GNUNET_STATISTICS_Iterator proc,
1155 struct GNUNET_STATISTICS_WatchEntry *w;
1158 return GNUNET_SYSERR;
1159 w = GNUNET_new (struct GNUNET_STATISTICS_WatchEntry);
1160 w->subsystem = GNUNET_strdup (subsystem);
1161 w->name = GNUNET_strdup (name);
1163 w->proc_cls = proc_cls;
1164 GNUNET_array_append (handle->watches,
1165 handle->watches_size,
1167 schedule_watch_request (handle,
1174 * Stop watching statistics from the peer.
1176 * @param handle identification of the statistics service
1177 * @param subsystem limit to the specified subsystem, never NULL
1178 * @param name name of the statistic value, never NULL
1179 * @param proc function to call on each value
1180 * @param proc_cls closure for @a proc
1181 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error (no such watch)
1184 GNUNET_STATISTICS_watch_cancel (struct GNUNET_STATISTICS_Handle *handle,
1185 const char *subsystem,
1187 GNUNET_STATISTICS_Iterator proc,
1190 struct GNUNET_STATISTICS_WatchEntry *w;
1193 return GNUNET_SYSERR;
1194 for (unsigned int i=0;i<handle->watches_size;i++)
1196 w = handle->watches[i];
1199 if ( (w->proc == proc) &&
1200 (w->proc_cls == proc_cls) &&
1201 (0 == strcmp (w->name, name)) &&
1202 (0 == strcmp (w->subsystem, subsystem)) )
1204 GNUNET_free (w->name);
1205 GNUNET_free (w->subsystem);
1207 handle->watches[i] = NULL;
1211 return GNUNET_SYSERR;
1216 * Queue a request to change a statistic.
1218 * @param h statistics handle
1219 * @param name name of the value
1220 * @param make_persistent should the value be kept across restarts?
1221 * @param value new value or change
1222 * @param type type of the action (#ACTION_SET or #ACTION_UPDATE)
1225 add_setter_action (struct GNUNET_STATISTICS_Handle *h,
1227 int make_persistent,
1229 enum ActionType type)
1231 struct GNUNET_STATISTICS_GetHandle *ai;
1237 slen = strlen (h->subsystem) + 1;
1238 nlen = strlen (name) + 1;
1239 nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
1240 if (nsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
1245 for (ai = h->action_head; NULL != ai; ai = ai->next)
1247 if (! ( (0 == strcmp (ai->subsystem, h->subsystem)) &&
1248 (0 == strcmp (ai->name, name)) &&
1249 ( (ACTION_UPDATE == ai->type) ||
1250 (ACTION_SET == ai->type) ) ) )
1252 if (ACTION_SET == ai->type)
1254 if (ACTION_UPDATE == type)
1256 delta = (int64_t) value;
1259 /* update old set by new delta */
1264 /* update old set by new delta, but never go negative */
1265 if (ai->value < -delta)
1273 /* new set overrides old set */
1279 if (ACTION_UPDATE == type)
1281 /* make delta cummulative */
1282 delta = (int64_t) value;
1287 /* drop old 'update', use new 'set' instead */
1292 ai->timeout = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT);
1293 ai->make_persistent = make_persistent;
1296 /* no existing entry matches, create a fresh one */
1297 ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle);
1299 ai->subsystem = GNUNET_strdup (h->subsystem);
1300 ai->name = GNUNET_strdup (name);
1301 ai->timeout = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT);
1302 ai->make_persistent = make_persistent;
1306 GNUNET_CONTAINER_DLL_insert_tail (h->action_head,
1309 schedule_action (h);
1314 * Set statistic value for the peer. Will always use our
1315 * subsystem (the argument used when "handle" was created).
1317 * @param handle identification of the statistics service
1318 * @param name name of the statistic value
1319 * @param value new value to set
1320 * @param make_persistent should the value be kept across restarts?
1323 GNUNET_STATISTICS_set (struct GNUNET_STATISTICS_Handle *handle,
1326 int make_persistent)
1330 GNUNET_assert (GNUNET_NO == handle->do_destroy);
1331 add_setter_action (handle,
1340 * Set statistic value for the peer. Will always use our
1341 * subsystem (the argument used when "handle" was created).
1343 * @param handle identification of the statistics service
1344 * @param name name of the statistic value
1345 * @param delta change in value (added to existing value)
1346 * @param make_persistent should the value be kept across restarts?
1349 GNUNET_STATISTICS_update (struct GNUNET_STATISTICS_Handle *handle,
1352 int make_persistent)
1358 GNUNET_assert (GNUNET_NO == handle->do_destroy);
1359 add_setter_action (handle,
1367 /* end of statistics_api.c */