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 Affero 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.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
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 ( \
39 GNUNET_TIME_UNIT_SECONDS, 2)
41 #define LOG(kind, ...) GNUNET_log_from (kind, "statistics-api", __VA_ARGS__)
71 * Entry kept for each value we are watching.
73 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;
98 * Linked list of things we still need to do.
100 struct GNUNET_STATISTICS_GetHandle
103 * This is a doubly linked list.
105 struct GNUNET_STATISTICS_GetHandle *next;
108 * This is a doubly linked list.
110 struct GNUNET_STATISTICS_GetHandle *prev;
113 * Main statistics handle.
115 struct GNUNET_STATISTICS_Handle *sh;
118 * What subsystem is this action about? (can be NULL)
123 * What value is this action about? (can be NULL)
128 * Continuation to call once action is complete.
130 GNUNET_STATISTICS_Callback cont;
133 * Function to call (for GET actions only).
135 GNUNET_STATISTICS_Iterator proc;
138 * Closure for @e proc and @e cont.
143 * Timeout for this action.
145 struct GNUNET_TIME_Absolute timeout;
153 * Flag for SET/UPDATE actions.
158 * Has the current iteration been aborted; for GET actions.
163 * Is this a #ACTION_GET, #ACTION_SET, #ACTION_UPDATE or #ACTION_WATCH?
165 enum ActionType type;
168 * Size of the message that we will be transmitting.
175 * Handle for the service.
177 struct GNUNET_STATISTICS_Handle
180 * Name of our subsystem.
185 * Configuration to use.
187 const struct GNUNET_CONFIGURATION_Handle *cfg;
190 * Message queue to the service.
192 struct GNUNET_MQ_Handle *mq;
195 * Head of the linked list of pending actions (first action
198 struct GNUNET_STATISTICS_GetHandle *action_head;
201 * Tail of the linked list of actions (for fast append).
203 struct GNUNET_STATISTICS_GetHandle *action_tail;
206 * Action we are currently busy with (action request has been
207 * transmitted, we're now receiving the response from the
210 struct GNUNET_STATISTICS_GetHandle *current;
213 * Array of watch entries.
215 struct GNUNET_STATISTICS_WatchEntry **watches;
218 * Task doing exponential back-off trying to reconnect.
220 struct GNUNET_SCHEDULER_Task *backoff_task;
223 * Task for running #do_destroy().
225 struct GNUNET_SCHEDULER_Task *destroy_task;
228 * Time for next connect retry.
230 struct GNUNET_TIME_Relative backoff;
233 * Maximum heap size observed so far (if available).
235 uint64_t peak_heap_size;
238 * Maximum resident set side observed so far (if available).
243 * Size of the @e watches array.
245 unsigned int watches_size;
248 * Should this handle auto-destruct once all actions have
254 * Are we currently receiving from the service?
261 * Obtain statistics about this process's memory consumption and
262 * report those as well (if they changed).
265 update_memory_statistics (struct GNUNET_STATISTICS_Handle *h)
267 #if ENABLE_HEAP_STATISTICS
268 uint64_t current_heap_size = 0;
269 uint64_t current_rss = 0;
271 if (GNUNET_NO != h->do_destroy)
278 current_heap_size = mi.uordblks + mi.fordblks;
285 if (0 == getrusage (RUSAGE_SELF, &ru))
287 current_rss = 1024LL * ru.ru_maxrss;
291 if (current_heap_size > h->peak_heap_size)
293 h->peak_heap_size = current_heap_size;
294 GNUNET_STATISTICS_set (h,
299 if (current_rss > h->peak_rss)
301 h->peak_rss = current_rss;
302 GNUNET_STATISTICS_set (h,
303 "# peak resident set size",
312 * Reconnect at a later time, respecting back-off.
314 * @param h statistics handle
317 reconnect_later (struct GNUNET_STATISTICS_Handle *h);
321 * Schedule the next action to be performed.
323 * @param cls statistics handle to reconnect
326 schedule_action (void *cls);
330 * Transmit request to service that we want to watch
331 * the development of a particular value.
333 * @param h statistics handle
334 * @param watch watch entry of the value to watch
337 schedule_watch_request (struct GNUNET_STATISTICS_Handle *h,
338 struct GNUNET_STATISTICS_WatchEntry *watch)
340 struct GNUNET_STATISTICS_GetHandle *ai;
345 slen = strlen (watch->subsystem) + 1;
346 nlen = strlen (watch->name) + 1;
347 nsize = sizeof(struct GNUNET_MessageHeader) + slen + nlen;
348 if (nsize >= GNUNET_MAX_MESSAGE_SIZE)
353 ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle);
355 ai->subsystem = GNUNET_strdup (watch->subsystem);
356 ai->name = GNUNET_strdup (watch->name);
357 ai->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
359 ai->type = ACTION_WATCH;
360 ai->proc = watch->proc;
361 ai->cls = watch->proc_cls;
362 GNUNET_CONTAINER_DLL_insert_tail (h->action_head,
370 * Free memory associated with the given action item.
372 * @param gh action item to free
375 free_action_item (struct GNUNET_STATISTICS_GetHandle *gh)
377 GNUNET_free_non_null (gh->subsystem);
378 GNUNET_free_non_null (gh->name);
384 * Disconnect from the statistics service.
386 * @param h statistics handle to disconnect from
389 do_disconnect (struct GNUNET_STATISTICS_Handle *h)
391 struct GNUNET_STATISTICS_GetHandle *c;
393 h->receiving = GNUNET_NO;
394 if (NULL != (c = h->current))
397 if ((NULL != c->cont) &&
398 (GNUNET_YES != c->aborted))
404 free_action_item (c);
408 GNUNET_MQ_destroy (h->mq);
415 * Process a #GNUNET_MESSAGE_TYPE_STATISTICS_VALUE message.
417 * @param cls statistics handle
418 * @param smsg message received from the service, never NULL
419 * @return #GNUNET_OK if the message was well-formed
422 check_statistics_value (void *cls,
423 const struct GNUNET_STATISTICS_ReplyMessage *smsg)
429 size = ntohs (smsg->header.size);
430 size -= sizeof(struct GNUNET_STATISTICS_ReplyMessage);
432 GNUNET_STRINGS_buffer_tokenize ((const char *) &smsg[1],
439 return GNUNET_SYSERR;
446 * Process a #GNUNET_MESSAGE_TYPE_STATISTICS_VALUE message.
448 * @param cls statistics handle
449 * @param msg message received from the service, never NULL
450 * @return #GNUNET_OK if the message was well-formed
453 handle_statistics_value (void *cls,
454 const struct GNUNET_STATISTICS_ReplyMessage *smsg)
456 struct GNUNET_STATISTICS_Handle *h = cls;
461 if (h->current->aborted)
462 return; /* iteration aborted, don't bother */
464 size = ntohs (smsg->header.size);
465 size -= sizeof(struct GNUNET_STATISTICS_ReplyMessage);
466 GNUNET_assert (size ==
467 GNUNET_STRINGS_buffer_tokenize ((const char *) &smsg[1],
472 LOG (GNUNET_ERROR_TYPE_DEBUG,
473 "Received valid statistic on `%s:%s': %llu\n",
475 GNUNET_ntohll (smsg->value));
477 h->current->proc (h->current->cls,
480 GNUNET_ntohll (smsg->value),
482 (ntohl (smsg->uid) & GNUNET_STATISTICS_PERSIST_BIT)) ))
484 LOG (GNUNET_ERROR_TYPE_DEBUG,
485 "Processing of remaining statistics aborted by client.\n");
486 h->current->aborted = GNUNET_YES;
492 * We have received a watch value from the service. Process it.
494 * @param cls statistics handle
495 * @param msg the watch value message
498 handle_statistics_watch_value (void *cls,
500 GNUNET_STATISTICS_WatchValueMessage *wvm)
502 struct GNUNET_STATISTICS_Handle *h = cls;
503 struct GNUNET_STATISTICS_WatchEntry *w;
506 GNUNET_break (0 == ntohl (wvm->reserved));
507 wid = ntohl (wvm->wid);
508 if (wid >= h->watches_size)
517 (void) w->proc (w->proc_cls,
520 GNUNET_ntohll (wvm->value),
521 0 != (ntohl (wvm->flags) & GNUNET_STATISTICS_PERSIST_BIT));
526 * Generic error handler, called with the appropriate error code and
527 * the same closure specified at the creation of the message queue.
528 * Not every message queue implementation supports an error handler.
530 * @param cls closure with the `struct GNUNET_STATISTICS_Handle *`
531 * @param error error code
534 mq_error_handler (void *cls,
535 enum GNUNET_MQ_Error error)
537 struct GNUNET_STATISTICS_Handle *h = cls;
539 if (GNUNET_NO != h->do_destroy)
541 h->do_destroy = GNUNET_NO;
542 if (NULL != h->destroy_task)
544 GNUNET_SCHEDULER_cancel (h->destroy_task);
545 h->destroy_task = NULL;
547 GNUNET_STATISTICS_destroy (h,
557 * Task used to destroy the statistics handle.
559 * @param cls the `struct GNUNET_STATISTICS_Handle`
562 do_destroy (void *cls)
564 struct GNUNET_STATISTICS_Handle *h = cls;
566 h->destroy_task = NULL;
567 h->do_destroy = GNUNET_NO;
568 LOG (GNUNET_ERROR_TYPE_DEBUG,
569 "Running final destruction\n");
570 GNUNET_STATISTICS_destroy (h,
576 * Handle a #GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT_CONFIRM
577 * message. We receive this message at the end of the shutdown when
578 * the service confirms that all data has been written to disk.
580 * @param cls our `struct GNUNET_STATISTICS_Handle *`
581 * @param msg the message
584 handle_disconnect_confirm (void *cls,
585 const struct GNUNET_MessageHeader *msg)
587 struct GNUNET_STATISTICS_Handle *h = cls;
589 if (GNUNET_SYSERR != h->do_destroy)
591 /* not in shutdown, why do we get 'TEST'? */
597 LOG (GNUNET_ERROR_TYPE_DEBUG,
598 "Received DISCONNNECT_CONFIRM message from statistics, can complete disconnect\n");
599 if (NULL != h->destroy_task)
600 GNUNET_SCHEDULER_cancel (h->destroy_task);
601 h->destroy_task = GNUNET_SCHEDULER_add_now (&do_destroy,
607 * Handle a #GNUNET_MESSAGE_TYPE_STATISTICS_END message. We receive
608 * this message in response to a query to indicate that there are no
609 * further matching results.
611 * @param cls our `struct GNUNET_STATISTICS_Handle *`
612 * @param msg the message
615 handle_statistics_end (void *cls,
616 const struct GNUNET_MessageHeader *msg)
618 struct GNUNET_STATISTICS_Handle *h = cls;
619 struct GNUNET_STATISTICS_GetHandle *c;
621 LOG (GNUNET_ERROR_TYPE_DEBUG,
622 "Received end of statistics marker\n");
623 if (NULL == (c = h->current))
630 h->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
639 free_action_item (c);
644 * Try to (re)connect to the statistics service.
646 * @param h statistics handle to reconnect
647 * @return #GNUNET_YES on success, #GNUNET_NO on failure.
650 try_connect (struct GNUNET_STATISTICS_Handle *h)
652 struct GNUNET_MQ_MessageHandler handlers[] = {
653 GNUNET_MQ_hd_fixed_size (disconnect_confirm,
654 GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT_CONFIRM,
655 struct GNUNET_MessageHeader,
657 GNUNET_MQ_hd_fixed_size (statistics_end,
658 GNUNET_MESSAGE_TYPE_STATISTICS_END,
659 struct GNUNET_MessageHeader,
661 GNUNET_MQ_hd_var_size (statistics_value,
662 GNUNET_MESSAGE_TYPE_STATISTICS_VALUE,
663 struct GNUNET_STATISTICS_ReplyMessage,
665 GNUNET_MQ_hd_fixed_size (statistics_watch_value,
666 GNUNET_MESSAGE_TYPE_STATISTICS_WATCH_VALUE,
667 struct GNUNET_STATISTICS_WatchValueMessage,
669 GNUNET_MQ_handler_end ()
671 struct GNUNET_STATISTICS_GetHandle *gh;
672 struct GNUNET_STATISTICS_GetHandle *gn;
674 if (NULL != h->backoff_task)
678 h->mq = GNUNET_CLIENT_connect (h->cfg,
685 LOG (GNUNET_ERROR_TYPE_DEBUG,
686 "Failed to connect to statistics service!\n");
690 while (NULL != (gh = gn))
693 if (gh->type == ACTION_WATCH)
695 GNUNET_CONTAINER_DLL_remove (h->action_head,
698 free_action_item (gh);
701 for (unsigned int i = 0; i < h->watches_size; i++)
702 if (NULL != h->watches[i])
703 schedule_watch_request (h,
710 * We've waited long enough, reconnect now.
712 * @param cls the `struct GNUNET_STATISTICS_Handle` to reconnect
715 reconnect_task (void *cls)
717 struct GNUNET_STATISTICS_Handle *h = cls;
719 h->backoff_task = NULL;
725 * Reconnect at a later time, respecting back-off.
727 * @param h statistics handle
730 reconnect_later (struct GNUNET_STATISTICS_Handle *h)
733 struct GNUNET_STATISTICS_GetHandle *gh;
735 GNUNET_assert (NULL == h->backoff_task);
736 if (GNUNET_YES == h->do_destroy)
738 /* So we are shutting down and the service is not reachable.
739 * Chances are that it's down for good and we are not going to connect to
741 * Give up and don't sync the rest of the data.
743 for (gh = h->action_head; NULL != gh; gh = gh->next)
744 if ((gh->make_persistent) &&
745 (ACTION_SET == gh->type))
747 if (GNUNET_YES == loss)
748 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
749 _ ("Could not save some persistent statistics\n"));
750 if (NULL != h->destroy_task)
751 GNUNET_SCHEDULER_cancel (h->destroy_task);
752 h->destroy_task = GNUNET_SCHEDULER_add_now (&do_destroy,
757 = GNUNET_SCHEDULER_add_delayed (h->backoff,
760 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)
1065 * Get statistic from the peer.
1067 * @param handle identification of the statistics service
1068 * @param subsystem limit to the specified subsystem, NULL for our subsystem
1069 * @param name name of the statistic value, NULL for all values
1070 * @param cont continuation to call when done (can be NULL)
1071 * This callback CANNOT destroy the statistics handle in the same call.
1072 * @param proc function to call on each value
1073 * @param cls closure for @a cont and @a proc
1074 * @return NULL on error
1076 struct GNUNET_STATISTICS_GetHandle *
1077 GNUNET_STATISTICS_get (struct GNUNET_STATISTICS_Handle *handle,
1078 const char *subsystem,
1080 GNUNET_STATISTICS_Callback cont,
1081 GNUNET_STATISTICS_Iterator proc,
1086 struct GNUNET_STATISTICS_GetHandle *ai;
1090 GNUNET_assert (NULL != proc);
1091 GNUNET_assert (GNUNET_NO == handle->do_destroy);
1092 if (NULL == subsystem)
1096 slen1 = strlen (subsystem) + 1;
1097 slen2 = strlen (name) + 1;
1098 GNUNET_assert (slen1 + slen2 + sizeof(struct GNUNET_MessageHeader) <
1099 GNUNET_MAX_MESSAGE_SIZE);
1100 ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle);
1102 ai->subsystem = GNUNET_strdup (subsystem);
1103 ai->name = GNUNET_strdup (name);
1107 ai->type = ACTION_GET;
1108 ai->msize = slen1 + slen2 + sizeof(struct GNUNET_MessageHeader);
1109 GNUNET_CONTAINER_DLL_insert_tail (handle->action_head,
1110 handle->action_tail,
1112 schedule_action (handle);
1118 * Cancel a 'get' request. Must be called before the 'cont'
1119 * function is called.
1121 * @param gh handle of the request to cancel
1124 GNUNET_STATISTICS_get_cancel (struct GNUNET_STATISTICS_GetHandle *gh)
1129 if (gh->sh->current == gh)
1131 gh->aborted = GNUNET_YES;
1134 GNUNET_CONTAINER_DLL_remove (gh->sh->action_head,
1135 gh->sh->action_tail,
1137 GNUNET_free (gh->name);
1138 GNUNET_free (gh->subsystem);
1144 * Watch statistics from the peer (be notified whenever they change).
1146 * @param handle identification of the statistics service
1147 * @param subsystem limit to the specified subsystem, never NULL
1148 * @param name name of the statistic value, never NULL
1149 * @param proc function to call on each value
1150 * @param proc_cls closure for @a proc
1151 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1154 GNUNET_STATISTICS_watch (struct GNUNET_STATISTICS_Handle *handle,
1155 const char *subsystem,
1157 GNUNET_STATISTICS_Iterator proc,
1160 struct GNUNET_STATISTICS_WatchEntry *w;
1163 return GNUNET_SYSERR;
1164 w = GNUNET_new (struct GNUNET_STATISTICS_WatchEntry);
1165 w->subsystem = GNUNET_strdup (subsystem);
1166 w->name = GNUNET_strdup (name);
1168 w->proc_cls = proc_cls;
1169 GNUNET_array_append (handle->watches,
1170 handle->watches_size,
1172 schedule_watch_request (handle,
1179 * Stop watching statistics from the peer.
1181 * @param handle identification of the statistics service
1182 * @param subsystem limit to the specified subsystem, never NULL
1183 * @param name name of the statistic value, never NULL
1184 * @param proc function to call on each value
1185 * @param proc_cls closure for @a proc
1186 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error (no such watch)
1189 GNUNET_STATISTICS_watch_cancel (struct GNUNET_STATISTICS_Handle *handle,
1190 const char *subsystem,
1192 GNUNET_STATISTICS_Iterator proc,
1195 struct GNUNET_STATISTICS_WatchEntry *w;
1198 return GNUNET_SYSERR;
1199 for (unsigned int i = 0; i < handle->watches_size; i++)
1201 w = handle->watches[i];
1204 if ((w->proc == proc) &&
1205 (w->proc_cls == proc_cls) &&
1206 (0 == strcmp (w->name,
1208 (0 == strcmp (w->subsystem,
1211 GNUNET_free (w->name);
1212 GNUNET_free (w->subsystem);
1214 handle->watches[i] = NULL;
1218 return GNUNET_SYSERR;
1223 * Queue a request to change a statistic.
1225 * @param h statistics handle
1226 * @param name name of the value
1227 * @param make_persistent should the value be kept across restarts?
1228 * @param value new value or change
1229 * @param type type of the action (#ACTION_SET or #ACTION_UPDATE)
1232 add_setter_action (struct GNUNET_STATISTICS_Handle *h,
1234 int make_persistent,
1236 enum ActionType type)
1238 struct GNUNET_STATISTICS_GetHandle *ai;
1244 slen = strlen (h->subsystem) + 1;
1245 nlen = strlen (name) + 1;
1246 nsize = sizeof(struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
1247 if (nsize >= GNUNET_MAX_MESSAGE_SIZE)
1252 for (ai = h->action_head; NULL != ai; ai = ai->next)
1254 if (! ((0 == strcmp (ai->subsystem,
1256 (0 == strcmp (ai->name,
1258 ((ACTION_UPDATE == ai->type) ||
1259 (ACTION_SET == ai->type))))
1261 if (ACTION_SET == ai->type)
1263 if (ACTION_UPDATE == type)
1265 delta = (int64_t) value;
1268 /* update old set by new delta */
1273 /* update old set by new delta, but never go negative */
1274 if (ai->value < -delta)
1282 /* new set overrides old set */
1288 if (ACTION_UPDATE == type)
1290 /* make delta cummulative */
1291 delta = (int64_t) value;
1296 /* drop old 'update', use new 'set' instead */
1302 = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT);
1307 /* no existing entry matches, create a fresh one */
1308 ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle);
1310 ai->subsystem = GNUNET_strdup (h->subsystem);
1311 ai->name = GNUNET_strdup (name);
1312 ai->timeout = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT);
1313 ai->make_persistent = make_persistent;
1317 GNUNET_CONTAINER_DLL_insert_tail (h->action_head,
1320 schedule_action (h);
1325 * Set statistic value for the peer. Will always use our
1326 * subsystem (the argument used when "handle" was created).
1328 * @param handle identification of the statistics service
1329 * @param name name of the statistic value
1330 * @param value new value to set
1331 * @param make_persistent should the value be kept across restarts?
1334 GNUNET_STATISTICS_set (struct GNUNET_STATISTICS_Handle *handle,
1337 int make_persistent)
1341 GNUNET_assert (GNUNET_NO == handle->do_destroy);
1342 add_setter_action (handle,
1351 * Set statistic value for the peer. Will always use our
1352 * subsystem (the argument used when "handle" was created).
1354 * @param handle identification of the statistics service
1355 * @param name name of the statistic value
1356 * @param delta change in value (added to existing value)
1357 * @param make_persistent should the value be kept across restarts?
1360 GNUNET_STATISTICS_update (struct GNUNET_STATISTICS_Handle *handle,
1363 int make_persistent)
1369 GNUNET_assert (GNUNET_NO == handle->do_destroy);
1370 add_setter_action (handle,
1378 /* end of statistics_api.c */