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(GNUNET_TIME_UNIT_SECONDS, 2)
40 #define LOG(kind, ...) GNUNET_log_from(kind, "statistics-api", __VA_ARGS__)
69 * Entry kept for each value we are watching.
71 struct GNUNET_STATISTICS_WatchEntry {
73 * What subsystem is this action about? (never NULL)
78 * What value is this action about? (never NULL)
85 GNUNET_STATISTICS_Iterator proc;
95 * Linked list of things we still need to do.
97 struct GNUNET_STATISTICS_GetHandle {
99 * This is a doubly linked list.
101 struct GNUNET_STATISTICS_GetHandle *next;
104 * This is a doubly linked list.
106 struct GNUNET_STATISTICS_GetHandle *prev;
109 * Main statistics handle.
111 struct GNUNET_STATISTICS_Handle *sh;
114 * What subsystem is this action about? (can be NULL)
119 * What value is this action about? (can be NULL)
124 * Continuation to call once action is complete.
126 GNUNET_STATISTICS_Callback cont;
129 * Function to call (for GET actions only).
131 GNUNET_STATISTICS_Iterator proc;
134 * Closure for @e proc and @e cont.
139 * Timeout for this action.
141 struct GNUNET_TIME_Absolute timeout;
149 * Flag for SET/UPDATE actions.
154 * Has the current iteration been aborted; for GET actions.
159 * Is this a #ACTION_GET, #ACTION_SET, #ACTION_UPDATE or #ACTION_WATCH?
161 enum ActionType type;
164 * Size of the message that we will be transmitting.
171 * Handle for the service.
173 struct GNUNET_STATISTICS_Handle {
175 * Name of our subsystem.
180 * Configuration to use.
182 const struct GNUNET_CONFIGURATION_Handle *cfg;
185 * Message queue to the service.
187 struct GNUNET_MQ_Handle *mq;
190 * Head of the linked list of pending actions (first action
193 struct GNUNET_STATISTICS_GetHandle *action_head;
196 * Tail of the linked list of actions (for fast append).
198 struct GNUNET_STATISTICS_GetHandle *action_tail;
201 * Action we are currently busy with (action request has been
202 * transmitted, we're now receiving the response from the
205 struct GNUNET_STATISTICS_GetHandle *current;
208 * Array of watch entries.
210 struct GNUNET_STATISTICS_WatchEntry **watches;
213 * Task doing exponential back-off trying to reconnect.
215 struct GNUNET_SCHEDULER_Task *backoff_task;
218 * Task for running #do_destroy().
220 struct GNUNET_SCHEDULER_Task *destroy_task;
223 * Time for next connect retry.
225 struct GNUNET_TIME_Relative backoff;
228 * Maximum heap size observed so far (if available).
230 uint64_t peak_heap_size;
233 * Maximum resident set side observed so far (if available).
238 * Size of the @e watches array.
240 unsigned int watches_size;
243 * Should this handle auto-destruct once all actions have
249 * Are we currently receiving from the service?
256 * Obtain statistics about this process's memory consumption and
257 * report those as well (if they changed).
260 update_memory_statistics(struct GNUNET_STATISTICS_Handle *h)
262 #if ENABLE_HEAP_STATISTICS
263 uint64_t current_heap_size = 0;
264 uint64_t current_rss = 0;
266 if (GNUNET_NO != h->do_destroy)
273 current_heap_size = mi.uordblks + mi.fordblks;
280 if (0 == getrusage(RUSAGE_SELF, &ru))
282 current_rss = 1024LL * ru.ru_maxrss;
286 if (current_heap_size > h->peak_heap_size)
288 h->peak_heap_size = current_heap_size;
289 GNUNET_STATISTICS_set(h,
294 if (current_rss > h->peak_rss)
296 h->peak_rss = current_rss;
297 GNUNET_STATISTICS_set(h,
298 "# peak resident set size",
307 * Reconnect at a later time, respecting back-off.
309 * @param h statistics handle
312 reconnect_later(struct GNUNET_STATISTICS_Handle *h);
316 * Schedule the next action to be performed.
318 * @param cls statistics handle to reconnect
321 schedule_action(void *cls);
325 * Transmit request to service that we want to watch
326 * the development of a particular value.
328 * @param h statistics handle
329 * @param watch watch entry of the value to watch
332 schedule_watch_request(struct GNUNET_STATISTICS_Handle *h,
333 struct GNUNET_STATISTICS_WatchEntry *watch)
335 struct GNUNET_STATISTICS_GetHandle *ai;
340 slen = strlen(watch->subsystem) + 1;
341 nlen = strlen(watch->name) + 1;
342 nsize = sizeof(struct GNUNET_MessageHeader) + slen + nlen;
343 if (nsize >= GNUNET_MAX_MESSAGE_SIZE)
348 ai = GNUNET_new(struct GNUNET_STATISTICS_GetHandle);
350 ai->subsystem = GNUNET_strdup(watch->subsystem);
351 ai->name = GNUNET_strdup(watch->name);
352 ai->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
354 ai->type = ACTION_WATCH;
355 ai->proc = watch->proc;
356 ai->cls = watch->proc_cls;
357 GNUNET_CONTAINER_DLL_insert_tail(h->action_head,
365 * Free memory associated with the given action item.
367 * @param gh action item to free
370 free_action_item(struct GNUNET_STATISTICS_GetHandle *gh)
372 GNUNET_free_non_null(gh->subsystem);
373 GNUNET_free_non_null(gh->name);
379 * Disconnect from the statistics service.
381 * @param h statistics handle to disconnect from
384 do_disconnect(struct GNUNET_STATISTICS_Handle *h)
386 struct GNUNET_STATISTICS_GetHandle *c;
388 h->receiving = GNUNET_NO;
389 if (NULL != (c = h->current))
392 if ((NULL != c->cont) &&
393 (GNUNET_YES != c->aborted))
403 GNUNET_MQ_destroy(h->mq);
410 * Process a #GNUNET_MESSAGE_TYPE_STATISTICS_VALUE message.
412 * @param cls statistics handle
413 * @param smsg message received from the service, never NULL
414 * @return #GNUNET_OK if the message was well-formed
417 check_statistics_value(void *cls,
418 const struct GNUNET_STATISTICS_ReplyMessage *smsg)
424 size = ntohs(smsg->header.size);
425 size -= sizeof(struct GNUNET_STATISTICS_ReplyMessage);
427 GNUNET_STRINGS_buffer_tokenize((const char *)&smsg[1],
434 return GNUNET_SYSERR;
441 * Process a #GNUNET_MESSAGE_TYPE_STATISTICS_VALUE message.
443 * @param cls statistics handle
444 * @param msg message received from the service, never NULL
445 * @return #GNUNET_OK if the message was well-formed
448 handle_statistics_value(void *cls,
449 const struct GNUNET_STATISTICS_ReplyMessage *smsg)
451 struct GNUNET_STATISTICS_Handle *h = cls;
456 if (h->current->aborted)
457 return; /* iteration aborted, don't bother */
459 size = ntohs(smsg->header.size);
460 size -= sizeof(struct GNUNET_STATISTICS_ReplyMessage);
461 GNUNET_assert(size ==
462 GNUNET_STRINGS_buffer_tokenize((const char *)&smsg[1],
467 LOG(GNUNET_ERROR_TYPE_DEBUG,
468 "Received valid statistic on `%s:%s': %llu\n",
470 GNUNET_ntohll(smsg->value));
472 h->current->proc(h->current->cls,
475 GNUNET_ntohll(smsg->value),
477 (ntohl(smsg->uid) & GNUNET_STATISTICS_PERSIST_BIT)))
479 LOG(GNUNET_ERROR_TYPE_DEBUG,
480 "Processing of remaining statistics aborted by client.\n");
481 h->current->aborted = GNUNET_YES;
487 * We have received a watch value from the service. Process it.
489 * @param cls statistics handle
490 * @param msg the watch value message
493 handle_statistics_watch_value(void *cls,
494 const struct GNUNET_STATISTICS_WatchValueMessage *wvm)
496 struct GNUNET_STATISTICS_Handle *h = cls;
497 struct GNUNET_STATISTICS_WatchEntry *w;
500 GNUNET_break(0 == ntohl(wvm->reserved));
501 wid = ntohl(wvm->wid);
502 if (wid >= h->watches_size)
511 (void)w->proc(w->proc_cls,
514 GNUNET_ntohll(wvm->value),
515 0 != (ntohl(wvm->flags) & GNUNET_STATISTICS_PERSIST_BIT));
520 * Generic error handler, called with the appropriate error code and
521 * the same closure specified at the creation of the message queue.
522 * Not every message queue implementation supports an error handler.
524 * @param cls closure with the `struct GNUNET_STATISTICS_Handle *`
525 * @param error error code
528 mq_error_handler(void *cls,
529 enum GNUNET_MQ_Error error)
531 struct GNUNET_STATISTICS_Handle *h = cls;
533 if (GNUNET_NO != h->do_destroy)
535 h->do_destroy = GNUNET_NO;
536 if (NULL != h->destroy_task)
538 GNUNET_SCHEDULER_cancel(h->destroy_task);
539 h->destroy_task = NULL;
541 GNUNET_STATISTICS_destroy(h,
551 * Task used to destroy the statistics handle.
553 * @param cls the `struct GNUNET_STATISTICS_Handle`
556 do_destroy(void *cls)
558 struct GNUNET_STATISTICS_Handle *h = cls;
560 h->destroy_task = NULL;
561 h->do_destroy = GNUNET_NO;
562 LOG(GNUNET_ERROR_TYPE_DEBUG,
563 "Running final destruction\n");
564 GNUNET_STATISTICS_destroy(h,
570 * Handle a #GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT_CONFIRM
571 * message. We receive this message at the end of the shutdown when
572 * the service confirms that all data has been written to disk.
574 * @param cls our `struct GNUNET_STATISTICS_Handle *`
575 * @param msg the message
578 handle_disconnect_confirm(void *cls,
579 const struct GNUNET_MessageHeader *msg)
581 struct GNUNET_STATISTICS_Handle *h = cls;
583 if (GNUNET_SYSERR != h->do_destroy)
585 /* not in shutdown, why do we get 'TEST'? */
591 LOG(GNUNET_ERROR_TYPE_DEBUG,
592 "Received DISCONNNECT_CONFIRM message from statistics, can complete disconnect\n");
593 if (NULL != h->destroy_task)
594 GNUNET_SCHEDULER_cancel(h->destroy_task);
595 h->destroy_task = GNUNET_SCHEDULER_add_now(&do_destroy,
601 * Handle a #GNUNET_MESSAGE_TYPE_STATISTICS_END message. We receive
602 * this message in response to a query to indicate that there are no
603 * further matching results.
605 * @param cls our `struct GNUNET_STATISTICS_Handle *`
606 * @param msg the message
609 handle_statistics_end(void *cls,
610 const struct GNUNET_MessageHeader *msg)
612 struct GNUNET_STATISTICS_Handle *h = cls;
613 struct GNUNET_STATISTICS_GetHandle *c;
615 LOG(GNUNET_ERROR_TYPE_DEBUG,
616 "Received end of statistics marker\n");
617 if (NULL == (c = h->current))
624 h->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
638 * Try to (re)connect to the statistics service.
640 * @param h statistics handle to reconnect
641 * @return #GNUNET_YES on success, #GNUNET_NO on failure.
644 try_connect(struct GNUNET_STATISTICS_Handle *h)
646 struct GNUNET_MQ_MessageHandler handlers[] = {
647 GNUNET_MQ_hd_fixed_size(disconnect_confirm,
648 GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT_CONFIRM,
649 struct GNUNET_MessageHeader,
651 GNUNET_MQ_hd_fixed_size(statistics_end,
652 GNUNET_MESSAGE_TYPE_STATISTICS_END,
653 struct GNUNET_MessageHeader,
655 GNUNET_MQ_hd_var_size(statistics_value,
656 GNUNET_MESSAGE_TYPE_STATISTICS_VALUE,
657 struct GNUNET_STATISTICS_ReplyMessage,
659 GNUNET_MQ_hd_fixed_size(statistics_watch_value,
660 GNUNET_MESSAGE_TYPE_STATISTICS_WATCH_VALUE,
661 struct GNUNET_STATISTICS_WatchValueMessage,
663 GNUNET_MQ_handler_end()
665 struct GNUNET_STATISTICS_GetHandle *gh;
666 struct GNUNET_STATISTICS_GetHandle *gn;
668 if (NULL != h->backoff_task)
672 h->mq = GNUNET_CLIENT_connect(h->cfg,
679 LOG(GNUNET_ERROR_TYPE_DEBUG,
680 "Failed to connect to statistics service!\n");
684 while (NULL != (gh = gn))
687 if (gh->type == ACTION_WATCH)
689 GNUNET_CONTAINER_DLL_remove(h->action_head,
692 free_action_item(gh);
695 for (unsigned int i = 0; i < h->watches_size; i++)
696 if (NULL != h->watches[i])
697 schedule_watch_request(h,
704 * We've waited long enough, reconnect now.
706 * @param cls the `struct GNUNET_STATISTICS_Handle` to reconnect
709 reconnect_task(void *cls)
711 struct GNUNET_STATISTICS_Handle *h = cls;
713 h->backoff_task = NULL;
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) &&
740 (ACTION_SET == gh->type))
742 if (GNUNET_YES == loss)
743 GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
744 _("Could not save some persistent statistics\n"));
745 if (NULL != h->destroy_task)
746 GNUNET_SCHEDULER_cancel(h->destroy_task);
747 h->destroy_task = GNUNET_SCHEDULER_add_now(&do_destroy,
752 = GNUNET_SCHEDULER_add_delayed(h->backoff,
755 h->backoff = GNUNET_TIME_STD_BACKOFF(h->backoff);
761 * Transmit a GET request (and if successful, start to receive
764 * @param handle statistics handle
767 transmit_get(struct GNUNET_STATISTICS_Handle *handle)
769 struct GNUNET_STATISTICS_GetHandle *c;
770 struct GNUNET_MessageHeader *hdr;
771 struct GNUNET_MQ_Envelope *env;
775 GNUNET_assert(NULL != (c = handle->current));
776 slen1 = strlen(c->subsystem) + 1;
777 slen2 = strlen(c->name) + 1;
778 env = GNUNET_MQ_msg_extra(hdr,
780 GNUNET_MESSAGE_TYPE_STATISTICS_GET);
781 GNUNET_assert(slen1 + slen2 ==
782 GNUNET_STRINGS_buffer_fill((char *)&hdr[1],
787 GNUNET_MQ_notify_sent(env,
790 GNUNET_MQ_send(handle->mq,
796 * Transmit a WATCH request (and if successful, start to receive
799 * @param handle statistics handle
802 transmit_watch(struct GNUNET_STATISTICS_Handle *handle)
804 struct GNUNET_MessageHeader *hdr;
805 struct GNUNET_MQ_Envelope *env;
809 LOG(GNUNET_ERROR_TYPE_DEBUG,
810 "Transmitting watch request for `%s'\n",
811 handle->current->name);
812 slen1 = strlen(handle->current->subsystem) + 1;
813 slen2 = strlen(handle->current->name) + 1;
814 env = GNUNET_MQ_msg_extra(hdr,
816 GNUNET_MESSAGE_TYPE_STATISTICS_WATCH);
817 GNUNET_assert(slen1 + slen2 ==
818 GNUNET_STRINGS_buffer_fill((char *)&hdr[1],
821 handle->current->subsystem,
822 handle->current->name));
823 GNUNET_MQ_notify_sent(env,
826 GNUNET_MQ_send(handle->mq,
828 GNUNET_assert(NULL == handle->current->cont);
829 free_action_item(handle->current);
830 handle->current = NULL;
831 schedule_action(handle);
836 * Transmit a SET/UPDATE request.
838 * @param handle statistics handle
841 transmit_set(struct GNUNET_STATISTICS_Handle *handle)
843 struct GNUNET_STATISTICS_SetMessage *r;
844 struct GNUNET_MQ_Envelope *env;
848 slen = strlen(handle->current->subsystem) + 1;
849 nlen = strlen(handle->current->name) + 1;
850 env = GNUNET_MQ_msg_extra(r,
852 GNUNET_MESSAGE_TYPE_STATISTICS_SET);
854 r->value = GNUNET_htonll(handle->current->value);
855 if (handle->current->make_persistent)
856 r->flags |= htonl(GNUNET_STATISTICS_SETFLAG_PERSISTENT);
857 if (handle->current->type == ACTION_UPDATE)
858 r->flags |= htonl(GNUNET_STATISTICS_SETFLAG_RELATIVE);
859 GNUNET_assert(slen + nlen ==
860 GNUNET_STRINGS_buffer_fill((char *)&r[1],
863 handle->current->subsystem,
864 handle->current->name));
865 GNUNET_assert(NULL == handle->current->cont);
866 free_action_item(handle->current);
867 handle->current = NULL;
868 update_memory_statistics(handle);
869 GNUNET_MQ_notify_sent(env,
872 GNUNET_MQ_send(handle->mq,
878 * Get handle for the statistics service.
880 * @param subsystem name of subsystem using the service
881 * @param cfg services configuration in use
882 * @return handle to use
884 struct GNUNET_STATISTICS_Handle *
885 GNUNET_STATISTICS_create(const char *subsystem,
886 const struct GNUNET_CONFIGURATION_Handle *cfg)
888 struct GNUNET_STATISTICS_Handle *h;
891 GNUNET_CONFIGURATION_get_value_yesno(cfg,
895 h = GNUNET_new(struct GNUNET_STATISTICS_Handle);
897 h->subsystem = GNUNET_strdup(subsystem);
898 h->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
904 * Destroy a handle (free all state associated with
907 * @param h statistics handle to destroy
908 * @param sync_first set to #GNUNET_YES if pending SET requests should
912 GNUNET_STATISTICS_destroy(struct GNUNET_STATISTICS_Handle *h,
915 struct GNUNET_STATISTICS_GetHandle *pos;
916 struct GNUNET_STATISTICS_GetHandle *next;
920 GNUNET_assert(GNUNET_NO == h->do_destroy); /* Don't call twice. */
923 (0 != GNUNET_MQ_get_length(h->mq)))
925 if ((NULL != h->current) &&
926 (ACTION_GET == h->current->type))
927 h->current->aborted = GNUNET_YES;
928 next = h->action_head;
929 while (NULL != (pos = next))
932 if ((ACTION_GET == pos->type) ||
933 (ACTION_WATCH == pos->type))
935 GNUNET_CONTAINER_DLL_remove(h->action_head,
938 free_action_item(pos);
941 h->do_destroy = GNUNET_YES;
943 GNUNET_assert(NULL == h->destroy_task);
945 = GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_relative_multiply(h->backoff,
949 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
950 "Deferring destruction\n");
951 return; /* do not finish destruction just yet */
953 /* do clean up all */
954 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
955 "Cleaning all up\n");
956 while (NULL != (pos = h->action_head))
958 GNUNET_CONTAINER_DLL_remove(h->action_head,
961 free_action_item(pos);
964 if (NULL != h->backoff_task)
966 GNUNET_SCHEDULER_cancel(h->backoff_task);
967 h->backoff_task = NULL;
969 if (NULL != h->destroy_task)
972 GNUNET_SCHEDULER_cancel(h->destroy_task);
973 h->destroy_task = NULL;
975 for (unsigned int i = 0; i < h->watches_size; i++)
977 if (NULL == h->watches[i])
979 GNUNET_free(h->watches[i]->subsystem);
980 GNUNET_free(h->watches[i]->name);
981 GNUNET_free(h->watches[i]);
983 GNUNET_array_grow(h->watches,
986 GNUNET_free(h->subsystem);
992 * Schedule the next action to be performed.
994 * @param cls statistics handle
997 schedule_action(void *cls)
999 struct GNUNET_STATISTICS_Handle *h = cls;
1001 if (NULL != h->backoff_task)
1002 return; /* action already pending */
1003 if (GNUNET_YES != try_connect(h))
1008 if (0 < GNUNET_MQ_get_length(h->mq))
1009 return; /* Wait for queue to be reduced more */
1010 /* schedule next action */
1011 while (NULL == h->current)
1013 h->current = h->action_head;
1014 if (NULL == h->current)
1016 struct GNUNET_MessageHeader *hdr;
1017 struct GNUNET_MQ_Envelope *env;
1019 if (GNUNET_YES != h->do_destroy)
1020 return; /* nothing to do */
1021 /* let service know that we're done */
1022 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1023 "Notifying service that we are done\n");
1024 h->do_destroy = GNUNET_SYSERR; /* in 'TEST' mode */
1025 env = GNUNET_MQ_msg(hdr,
1026 GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT);
1027 GNUNET_MQ_notify_sent(env,
1030 GNUNET_MQ_send(h->mq,
1034 GNUNET_CONTAINER_DLL_remove(h->action_head,
1037 switch (h->current->type)
1061 * Get statistic from the peer.
1063 * @param handle identification of the statistics service
1064 * @param subsystem limit to the specified subsystem, NULL for our subsystem
1065 * @param name name of the statistic value, NULL for all values
1066 * @param cont continuation to call when done (can be NULL)
1067 * This callback CANNOT destroy the statistics handle in the same call.
1068 * @param proc function to call on each value
1069 * @param cls closure for @a cont and @a proc
1070 * @return NULL on error
1072 struct GNUNET_STATISTICS_GetHandle *
1073 GNUNET_STATISTICS_get(struct GNUNET_STATISTICS_Handle *handle,
1074 const char *subsystem,
1076 GNUNET_STATISTICS_Callback cont,
1077 GNUNET_STATISTICS_Iterator proc,
1082 struct GNUNET_STATISTICS_GetHandle *ai;
1086 GNUNET_assert(NULL != proc);
1087 GNUNET_assert(GNUNET_NO == handle->do_destroy);
1088 if (NULL == subsystem)
1092 slen1 = strlen(subsystem) + 1;
1093 slen2 = strlen(name) + 1;
1094 GNUNET_assert(slen1 + slen2 + sizeof(struct GNUNET_MessageHeader) <
1095 GNUNET_MAX_MESSAGE_SIZE);
1096 ai = GNUNET_new(struct GNUNET_STATISTICS_GetHandle);
1098 ai->subsystem = GNUNET_strdup(subsystem);
1099 ai->name = GNUNET_strdup(name);
1103 ai->type = ACTION_GET;
1104 ai->msize = slen1 + slen2 + sizeof(struct GNUNET_MessageHeader);
1105 GNUNET_CONTAINER_DLL_insert_tail(handle->action_head,
1106 handle->action_tail,
1108 schedule_action(handle);
1114 * Cancel a 'get' request. Must be called before the 'cont'
1115 * function is called.
1117 * @param gh handle of the request to cancel
1120 GNUNET_STATISTICS_get_cancel(struct GNUNET_STATISTICS_GetHandle *gh)
1125 if (gh->sh->current == gh)
1127 gh->aborted = GNUNET_YES;
1130 GNUNET_CONTAINER_DLL_remove(gh->sh->action_head,
1131 gh->sh->action_tail,
1133 GNUNET_free(gh->name);
1134 GNUNET_free(gh->subsystem);
1140 * Watch statistics from the peer (be notified whenever they change).
1142 * @param handle identification of the statistics service
1143 * @param subsystem limit to the specified subsystem, never NULL
1144 * @param name name of the statistic value, never NULL
1145 * @param proc function to call on each value
1146 * @param proc_cls closure for @a proc
1147 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1150 GNUNET_STATISTICS_watch(struct GNUNET_STATISTICS_Handle *handle,
1151 const char *subsystem,
1153 GNUNET_STATISTICS_Iterator proc,
1156 struct GNUNET_STATISTICS_WatchEntry *w;
1159 return GNUNET_SYSERR;
1160 w = GNUNET_new(struct GNUNET_STATISTICS_WatchEntry);
1161 w->subsystem = GNUNET_strdup(subsystem);
1162 w->name = GNUNET_strdup(name);
1164 w->proc_cls = proc_cls;
1165 GNUNET_array_append(handle->watches,
1166 handle->watches_size,
1168 schedule_watch_request(handle,
1175 * Stop watching statistics from the peer.
1177 * @param handle identification of the statistics service
1178 * @param subsystem limit to the specified subsystem, never NULL
1179 * @param name name of the statistic value, never NULL
1180 * @param proc function to call on each value
1181 * @param proc_cls closure for @a proc
1182 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error (no such watch)
1185 GNUNET_STATISTICS_watch_cancel(struct GNUNET_STATISTICS_Handle *handle,
1186 const char *subsystem,
1188 GNUNET_STATISTICS_Iterator proc,
1191 struct GNUNET_STATISTICS_WatchEntry *w;
1194 return GNUNET_SYSERR;
1195 for (unsigned int i = 0; i < handle->watches_size; i++)
1197 w = handle->watches[i];
1200 if ((w->proc == proc) &&
1201 (w->proc_cls == proc_cls) &&
1202 (0 == strcmp(w->name,
1204 (0 == strcmp(w->subsystem,
1207 GNUNET_free(w->name);
1208 GNUNET_free(w->subsystem);
1210 handle->watches[i] = NULL;
1214 return GNUNET_SYSERR;
1219 * Queue a request to change a statistic.
1221 * @param h statistics handle
1222 * @param name name of the value
1223 * @param make_persistent should the value be kept across restarts?
1224 * @param value new value or change
1225 * @param type type of the action (#ACTION_SET or #ACTION_UPDATE)
1228 add_setter_action(struct GNUNET_STATISTICS_Handle *h,
1230 int make_persistent,
1232 enum ActionType type)
1234 struct GNUNET_STATISTICS_GetHandle *ai;
1240 slen = strlen(h->subsystem) + 1;
1241 nlen = strlen(name) + 1;
1242 nsize = sizeof(struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
1243 if (nsize >= GNUNET_MAX_MESSAGE_SIZE)
1248 for (ai = h->action_head; NULL != ai; ai = ai->next)
1250 if (!((0 == strcmp(ai->subsystem,
1252 (0 == strcmp(ai->name,
1254 ((ACTION_UPDATE == ai->type) ||
1255 (ACTION_SET == ai->type))))
1257 if (ACTION_SET == ai->type)
1259 if (ACTION_UPDATE == type)
1261 delta = (int64_t)value;
1264 /* update old set by new delta */
1269 /* update old set by new delta, but never go negative */
1270 if (ai->value < -delta)
1278 /* new set overrides old set */
1284 if (ACTION_UPDATE == type)
1286 /* make delta cummulative */
1287 delta = (int64_t)value;
1292 /* drop old 'update', use new 'set' instead */
1298 = GNUNET_TIME_relative_to_absolute(SET_TRANSMIT_TIMEOUT);
1303 /* no existing entry matches, create a fresh one */
1304 ai = GNUNET_new(struct GNUNET_STATISTICS_GetHandle);
1306 ai->subsystem = GNUNET_strdup(h->subsystem);
1307 ai->name = GNUNET_strdup(name);
1308 ai->timeout = GNUNET_TIME_relative_to_absolute(SET_TRANSMIT_TIMEOUT);
1309 ai->make_persistent = make_persistent;
1313 GNUNET_CONTAINER_DLL_insert_tail(h->action_head,
1321 * Set statistic value for the peer. Will always use our
1322 * subsystem (the argument used when "handle" was created).
1324 * @param handle identification of the statistics service
1325 * @param name name of the statistic value
1326 * @param value new value to set
1327 * @param make_persistent should the value be kept across restarts?
1330 GNUNET_STATISTICS_set(struct GNUNET_STATISTICS_Handle *handle,
1333 int make_persistent)
1337 GNUNET_assert(GNUNET_NO == handle->do_destroy);
1338 add_setter_action(handle,
1347 * Set statistic value for the peer. Will always use our
1348 * subsystem (the argument used when "handle" was created).
1350 * @param handle identification of the statistics service
1351 * @param name name of the statistic value
1352 * @param delta change in value (added to existing value)
1353 * @param make_persistent should the value be kept across restarts?
1356 GNUNET_STATISTICS_update(struct GNUNET_STATISTICS_Handle *handle,
1359 int make_persistent)
1365 GNUNET_assert(GNUNET_NO == handle->do_destroy);
1366 add_setter_action(handle,
1374 /* end of statistics_api.c */