-only trigger check config if we actually need it
[oweals/gnunet.git] / src / statistics / statistics_api.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009, 2010, 2011, 2016 GNUnet e.V.
4
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.
9
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.
14
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.
19 */
20
21 /**
22  * @file statistics/statistics_api.c
23  * @brief API of the statistics service
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
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"
32
33 /**
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).
37  */
38 #define SET_TRANSMIT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 2)
39
40 #define LOG(kind,...) GNUNET_log_from (kind, "statistics-api",__VA_ARGS__)
41
42 /**
43  * Types of actions.
44  */
45 enum ActionType
46 {
47   /**
48    * Get a value.
49    */
50   ACTION_GET,
51
52   /**
53    * Set a value.
54    */
55   ACTION_SET,
56
57   /**
58    * Update a value.
59    */
60   ACTION_UPDATE,
61
62   /**
63    * Watch a value.
64    */
65   ACTION_WATCH
66 };
67
68
69 /**
70  * Entry kept for each value we are watching.
71  */
72 struct GNUNET_STATISTICS_WatchEntry
73 {
74
75   /**
76    * What subsystem is this action about? (never NULL)
77    */
78   char *subsystem;
79
80   /**
81    * What value is this action about? (never NULL)
82    */
83   char *name;
84
85   /**
86    * Function to call
87    */
88   GNUNET_STATISTICS_Iterator proc;
89
90   /**
91    * Closure for @e proc
92    */
93   void *proc_cls;
94
95 };
96
97
98 /**
99  * Linked list of things we still need to do.
100  */
101 struct GNUNET_STATISTICS_GetHandle
102 {
103
104   /**
105    * This is a doubly linked list.
106    */
107   struct GNUNET_STATISTICS_GetHandle *next;
108
109   /**
110    * This is a doubly linked list.
111    */
112   struct GNUNET_STATISTICS_GetHandle *prev;
113
114   /**
115    * Main statistics handle.
116    */
117   struct GNUNET_STATISTICS_Handle *sh;
118
119   /**
120    * What subsystem is this action about? (can be NULL)
121    */
122   char *subsystem;
123
124   /**
125    * What value is this action about? (can be NULL)
126    */
127   char *name;
128
129   /**
130    * Continuation to call once action is complete.
131    */
132   GNUNET_STATISTICS_Callback cont;
133
134   /**
135    * Function to call (for GET actions only).
136    */
137   GNUNET_STATISTICS_Iterator proc;
138
139   /**
140    * Closure for @e proc and @e cont.
141    */
142   void *cls;
143
144   /**
145    * Timeout for this action.
146    */
147   struct GNUNET_TIME_Absolute timeout;
148
149   /**
150    * Associated value.
151    */
152   uint64_t value;
153
154   /**
155    * Flag for SET/UPDATE actions.
156    */
157   int make_persistent;
158
159   /**
160    * Has the current iteration been aborted; for GET actions.
161    */
162   int aborted;
163
164   /**
165    * Is this a #ACTION_GET, #ACTION_SET, #ACTION_UPDATE or #ACTION_WATCH?
166    */
167   enum ActionType type;
168
169   /**
170    * Size of the message that we will be transmitting.
171    */
172   uint16_t msize;
173
174 };
175
176
177 /**
178  * Handle for the service.
179  */
180 struct GNUNET_STATISTICS_Handle
181 {
182   /**
183    * Name of our subsystem.
184    */
185   char *subsystem;
186
187   /**
188    * Configuration to use.
189    */
190   const struct GNUNET_CONFIGURATION_Handle *cfg;
191
192   /**
193    * Message queue to the service.
194    */
195   struct GNUNET_MQ_Handle *mq;
196
197   /**
198    * Head of the linked list of pending actions (first action
199    * to be performed).
200    */
201   struct GNUNET_STATISTICS_GetHandle *action_head;
202
203   /**
204    * Tail of the linked list of actions (for fast append).
205    */
206   struct GNUNET_STATISTICS_GetHandle *action_tail;
207
208   /**
209    * Action we are currently busy with (action request has been
210    * transmitted, we're now receiving the response from the
211    * service).
212    */
213   struct GNUNET_STATISTICS_GetHandle *current;
214
215   /**
216    * Array of watch entries.
217    */
218   struct GNUNET_STATISTICS_WatchEntry **watches;
219
220   /**
221    * Task doing exponential back-off trying to reconnect.
222    */
223   struct GNUNET_SCHEDULER_Task *backoff_task;
224
225   /**
226    * Task for running #do_destroy().
227    */
228   struct GNUNET_SCHEDULER_Task *destroy_task;
229
230   /**
231    * Time for next connect retry.
232    */
233   struct GNUNET_TIME_Relative backoff;
234
235   /**
236    * Maximum heap size observed so far (if available).
237    */
238   uint64_t peak_heap_size;
239
240   /**
241    * Maximum resident set side observed so far (if available).
242    */
243   uint64_t peak_rss;
244
245   /**
246    * Size of the @e watches array.
247    */
248   unsigned int watches_size;
249
250   /**
251    * Should this handle auto-destruct once all actions have
252    * been processed?
253    */
254   int do_destroy;
255
256   /**
257    * Are we currently receiving from the service?
258    */
259   int receiving;
260
261 };
262
263
264 /**
265  * Obtain statistics about this process's memory consumption and
266  * report those as well (if they changed).
267  */
268 static void
269 update_memory_statistics (struct GNUNET_STATISTICS_Handle *h)
270 {
271 #if ENABLE_HEAP_STATISTICS
272   uint64_t current_heap_size = 0;
273   uint64_t current_rss = 0;
274
275   if (GNUNET_NO != h->do_destroy)
276     return;
277 #if HAVE_MALLINFO
278   {
279     struct mallinfo mi;
280
281     mi = mallinfo();
282     current_heap_size = mi.uordblks + mi.fordblks;
283   }
284 #endif
285 #if HAVE_GETRUSAGE
286   {
287     struct rusage ru;
288
289     if (0 == getrusage (RUSAGE_SELF, &ru))
290     {
291       current_rss = 1024LL * ru.ru_maxrss;
292     }
293   }
294 #endif
295   if (current_heap_size > h->peak_heap_size)
296   {
297     h->peak_heap_size = current_heap_size;
298     GNUNET_STATISTICS_set (h, "# peak heap size", current_heap_size, GNUNET_NO);
299   }
300   if (current_rss > h->peak_rss)
301   {
302     h->peak_rss = current_rss;
303     GNUNET_STATISTICS_set (h, "# peak resident set size", current_rss, GNUNET_NO);
304   }
305 #endif
306 }
307
308
309 /**
310  * Schedule the next action to be performed.
311  *
312  * @param cls statistics handle to reconnect
313  */
314 static void
315 schedule_action (void *cls);
316
317
318 /**
319  * Reconnect at a later time, respecting back-off.
320  *
321  * @param h statistics handle
322  */
323 static void
324 reconnect_later (struct GNUNET_STATISTICS_Handle *h);
325
326
327 /**
328  * Transmit request to service that we want to watch
329  * the development of a particular value.
330  *
331  * @param h statistics handle
332  * @param watch watch entry of the value to watch
333  */
334 static void
335 schedule_watch_request (struct GNUNET_STATISTICS_Handle *h,
336                         struct GNUNET_STATISTICS_WatchEntry *watch)
337 {
338   struct GNUNET_STATISTICS_GetHandle *ai;
339   size_t slen;
340   size_t nlen;
341   size_t nsize;
342
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)
347   {
348     GNUNET_break (0);
349     return;
350   }
351   ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle);
352   ai->sh = h;
353   ai->subsystem = GNUNET_strdup (watch->subsystem);
354   ai->name = GNUNET_strdup (watch->name);
355   ai->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
356   ai->msize = nsize;
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,
361                                     h->action_tail,
362                                     ai);
363   schedule_action (h);
364 }
365
366
367 /**
368  * Free memory associated with the given action item.
369  *
370  * @param gh action item to free
371  */
372 static void
373 free_action_item (struct GNUNET_STATISTICS_GetHandle *gh)
374 {
375   GNUNET_free_non_null (gh->subsystem);
376   GNUNET_free_non_null (gh->name);
377   GNUNET_free (gh);
378 }
379
380
381 /**
382  * Disconnect from the statistics service.
383  *
384  * @param h statistics handle to disconnect from
385  */
386 static void
387 do_disconnect (struct GNUNET_STATISTICS_Handle *h)
388 {
389   struct GNUNET_STATISTICS_GetHandle *c;
390
391   h->receiving = GNUNET_NO;
392   if (NULL != (c = h->current))
393   {
394     h->current = NULL;
395     if ( (NULL != c->cont) &&
396          (GNUNET_YES != c->aborted) )
397     {
398       c->cont (c->cls,
399                GNUNET_SYSERR);
400       c->cont = NULL;
401     }
402     free_action_item (c);
403   }
404   if (NULL != h->mq)
405   {
406     GNUNET_MQ_destroy (h->mq);
407     h->mq = NULL;
408   }
409 }
410
411
412 /**
413  * Process a #GNUNET_MESSAGE_TYPE_STATISTICS_VALUE message.
414  *
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
418  */
419 static int
420 check_statistics_value (void *cls,
421                         const struct GNUNET_STATISTICS_ReplyMessage *smsg)
422 {
423   const char *service;
424   const char *name;
425   uint16_t size;
426
427   size = ntohs (smsg->header.size);
428   size -= sizeof (struct GNUNET_STATISTICS_ReplyMessage);
429   if (size !=
430       GNUNET_STRINGS_buffer_tokenize ((const char *) &smsg[1],
431                                       size,
432                                       2,
433                                       &service,
434                                       &name))
435   {
436     GNUNET_break (0);
437     return GNUNET_SYSERR;
438   }
439   return GNUNET_OK;
440 }
441
442
443 /**
444  * Process a #GNUNET_MESSAGE_TYPE_STATISTICS_VALUE message.
445  *
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
449  */
450 static void
451 handle_statistics_value (void *cls,
452                          const struct GNUNET_STATISTICS_ReplyMessage *smsg)
453 {
454   struct GNUNET_STATISTICS_Handle *h = cls;
455   const char *service;
456   const char *name;
457   uint16_t size;
458
459   if (h->current->aborted)
460     return;           /* iteration aborted, don't bother */
461
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],
466                                                  size,
467                                                  2,
468                                                  &service,
469                                                  &name));
470   LOG (GNUNET_ERROR_TYPE_DEBUG,
471        "Received valid statistic on `%s:%s': %llu\n",
472        service, name,
473        GNUNET_ntohll (smsg->value));
474   if (GNUNET_OK !=
475       h->current->proc (h->current->cls,
476                         service,
477                         name,
478                         GNUNET_ntohll (smsg->value),
479                         0 !=
480                         (ntohl (smsg->uid) & GNUNET_STATISTICS_PERSIST_BIT)))
481   {
482     LOG (GNUNET_ERROR_TYPE_DEBUG,
483          "Processing of remaining statistics aborted by client.\n");
484     h->current->aborted = GNUNET_YES;
485   }
486 }
487
488
489 /**
490  * We have received a watch value from the service.  Process it.
491  *
492  * @param cls statistics handle
493  * @param msg the watch value message
494  */
495 static void
496 handle_statistics_watch_value (void *cls,
497                                const struct GNUNET_STATISTICS_WatchValueMessage *wvm)
498 {
499   struct GNUNET_STATISTICS_Handle *h = cls;
500   struct GNUNET_STATISTICS_WatchEntry *w;
501   uint32_t wid;
502
503   GNUNET_break (0 == ntohl (wvm->reserved));
504   wid = ntohl (wvm->wid);
505   if (wid >= h->watches_size)
506   {
507     do_disconnect (h);
508     reconnect_later (h);
509     return;
510   }
511   w = h->watches[wid];
512   if (NULL == w)
513     return;
514   (void) w->proc (w->proc_cls,
515                   w->subsystem,
516                   w->name,
517                   GNUNET_ntohll (wvm->value),
518                   0 != (ntohl (wvm->flags) & GNUNET_STATISTICS_PERSIST_BIT));
519 }
520
521
522 /**
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.
526  *
527  * @param cls closure with the `struct GNUNET_STATISTICS_Handle *`
528  * @param error error code
529  */
530 static void
531 mq_error_handler (void *cls,
532                   enum GNUNET_MQ_Error error)
533 {
534   struct GNUNET_STATISTICS_Handle *h = cls;
535
536   if (GNUNET_NO != h->do_destroy)
537   {
538     h->do_destroy = GNUNET_NO;
539     if (NULL != h->destroy_task)
540     {
541       GNUNET_SCHEDULER_cancel (h->destroy_task);
542       h->destroy_task = NULL;
543     }
544     GNUNET_STATISTICS_destroy (h,
545                                GNUNET_NO);
546     return;
547   }
548   do_disconnect (h);
549   reconnect_later (h);
550 }
551
552
553 /**
554  * Task used to destroy the statistics handle.
555  *
556  * @param cls the `struct GNUNET_STATISTICS_Handle`
557  */
558 static void
559 do_destroy (void *cls)
560 {
561   struct GNUNET_STATISTICS_Handle *h = cls;
562
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,
568                              GNUNET_NO);
569 }
570
571
572 /**
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.
576  *
577  * @param cls our `struct GNUNET_STATISTICS_Handle *`
578  * @param msg the message
579  */
580 static void
581 handle_test (void *cls,
582              const struct GNUNET_MessageHeader *msg)
583 {
584   struct GNUNET_STATISTICS_Handle *h = cls;
585
586   if (GNUNET_SYSERR != h->do_destroy)
587   {
588     /* not in shutdown, why do we get 'TEST'? */
589     GNUNET_break (0);
590     do_disconnect (h);
591     reconnect_later (h);
592     return;
593   }
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,
599                                               h);
600 }
601
602
603 /**
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.
607  *
608  * @param cls our `struct GNUNET_STATISTICS_Handle *`
609  * @param msg the message
610  */
611 static void
612 handle_statistics_end (void *cls,
613                        const struct GNUNET_MessageHeader *msg)
614 {
615   struct GNUNET_STATISTICS_Handle *h = cls;
616   struct GNUNET_STATISTICS_GetHandle *c;
617
618   LOG (GNUNET_ERROR_TYPE_DEBUG,
619        "Received end of statistics marker\n");
620   if (NULL == (c = h->current))
621   {
622     GNUNET_break (0);
623     do_disconnect (h);
624     reconnect_later (h);
625     return;
626   }
627   h->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
628   h->current = NULL;
629   schedule_action (h);
630   if (NULL != c->cont)
631   {
632     c->cont (c->cls,
633              GNUNET_OK);
634     c->cont = NULL;
635   }
636   free_action_item (c);
637 }
638
639
640 /**
641  * Try to (re)connect to the statistics service.
642  *
643  * @param h statistics handle to reconnect
644  * @return #GNUNET_YES on success, #GNUNET_NO on failure.
645  */
646 static int
647 try_connect (struct GNUNET_STATISTICS_Handle *h)
648 {
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 ()
667   };
668   struct GNUNET_STATISTICS_GetHandle *gh;
669   struct GNUNET_STATISTICS_GetHandle *gn;
670
671   if (NULL != h->backoff_task)
672     return GNUNET_NO;
673   if (NULL != h->mq)
674     return GNUNET_YES;
675   h->mq = GNUNET_CLIENT_connecT (h->cfg,
676                                  "statistics",
677                                  handlers,
678                                  &mq_error_handler,
679                                  h);
680   if (NULL == h->mq)
681   {
682     LOG (GNUNET_ERROR_TYPE_DEBUG,
683          "Failed to connect to statistics service!\n");
684     return GNUNET_NO;
685   }
686   gn = h->action_head;
687   while (NULL != (gh = gn))
688   {
689     gn = gh->next;
690     if (gh->type == ACTION_WATCH)
691     {
692       GNUNET_CONTAINER_DLL_remove (h->action_head,
693                                    h->action_tail,
694                                    gh);
695       free_action_item (gh);
696     }
697   }
698   for (unsigned int i = 0; i < h->watches_size; i++)
699     if (NULL != h->watches[i])
700       schedule_watch_request (h,
701                               h->watches[i]);
702   return GNUNET_YES;
703 }
704
705
706 /**
707  * We've waited long enough, reconnect now.
708  *
709  * @param cls the `struct GNUNET_STATISTICS_Handle` to reconnect
710  */
711 static void
712 reconnect_task (void *cls)
713 {
714   struct GNUNET_STATISTICS_Handle *h = cls;
715
716   h->backoff_task = NULL;
717   schedule_action (h);
718 }
719
720
721 /**
722  * Reconnect at a later time, respecting back-off.
723  *
724  * @param h statistics handle
725  */
726 static void
727 reconnect_later (struct GNUNET_STATISTICS_Handle *h)
728 {
729   int loss;
730   struct GNUNET_STATISTICS_GetHandle *gh;
731
732   GNUNET_assert (NULL == h->backoff_task);
733   if (GNUNET_YES == h->do_destroy)
734   {
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
737      * it anymore.
738      * Give up and don't sync the rest of the data.
739      */
740     loss = GNUNET_NO;
741     for (gh = h->action_head; NULL != gh; gh = gh->next)
742       if ( (gh->make_persistent) && (ACTION_SET == gh->type) )
743         loss = GNUNET_YES;
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,
750                                                 h);
751     return;
752   }
753   h->backoff_task
754     = GNUNET_SCHEDULER_add_delayed (h->backoff,
755                                     &reconnect_task,
756                                     h);
757   h->backoff = GNUNET_TIME_STD_BACKOFF (h->backoff);
758 }
759
760
761
762 /**
763  * Transmit a GET request (and if successful, start to receive
764  * the response).
765  *
766  * @param handle statistics handle
767  */
768 static void
769 transmit_get (struct GNUNET_STATISTICS_Handle *handle)
770 {
771   struct GNUNET_STATISTICS_GetHandle *c;
772   struct GNUNET_MessageHeader *hdr;
773   struct GNUNET_MQ_Envelope *env;
774   size_t slen1;
775   size_t slen2;
776
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,
781                              slen1 + slen2,
782                              GNUNET_MESSAGE_TYPE_STATISTICS_GET);
783   GNUNET_assert (slen1 + slen2 ==
784                  GNUNET_STRINGS_buffer_fill ((char *) &hdr[1],
785                                              slen1 + slen2,
786                                              2,
787                                              c->subsystem,
788                                              c->name));
789   GNUNET_MQ_send (handle->mq,
790                   env);
791 }
792
793
794 /**
795  * Transmit a WATCH request (and if successful, start to receive
796  * the response).
797  *
798  * @param handle statistics handle
799  */
800 static void
801 transmit_watch (struct GNUNET_STATISTICS_Handle *handle)
802 {
803   struct GNUNET_MessageHeader *hdr;
804   struct GNUNET_MQ_Envelope *env;
805   size_t slen1;
806   size_t slen2;
807
808   LOG (GNUNET_ERROR_TYPE_DEBUG,
809        "Transmitting watch request for `%s'\n",
810        handle->current->name);
811   slen1 = strlen (handle->current->subsystem) + 1;
812   slen2 = strlen (handle->current->name) + 1;
813   env = GNUNET_MQ_msg_extra (hdr,
814                              slen1 + slen2,
815                              GNUNET_MESSAGE_TYPE_STATISTICS_WATCH);
816   GNUNET_assert (slen1 + slen2 ==
817                  GNUNET_STRINGS_buffer_fill ((char *) &hdr[1],
818                                              slen1 + slen2,
819                                              2,
820                                              handle->current->subsystem,
821                                              handle->current->name));
822   GNUNET_MQ_send (handle->mq,
823                   env);
824   GNUNET_assert (NULL == handle->current->cont);
825   free_action_item (handle->current);
826   handle->current = NULL;
827   schedule_action (handle);
828 }
829
830
831 /**
832  * Transmit a SET/UPDATE request.
833  *
834  * @param handle statistics handle
835  */
836 static void
837 transmit_set (struct GNUNET_STATISTICS_Handle *handle)
838 {
839   struct GNUNET_STATISTICS_SetMessage *r;
840   struct GNUNET_MQ_Envelope *env;
841   size_t slen;
842   size_t nlen;
843
844   slen = strlen (handle->current->subsystem) + 1;
845   nlen = strlen (handle->current->name) + 1;
846   env = GNUNET_MQ_msg_extra (r,
847                              slen + nlen,
848                              GNUNET_MESSAGE_TYPE_STATISTICS_SET);
849   r->flags = 0;
850   r->value = GNUNET_htonll (handle->current->value);
851   if (handle->current->make_persistent)
852     r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_PERSISTENT);
853   if (handle->current->type == ACTION_UPDATE)
854     r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_RELATIVE);
855   GNUNET_assert (slen + nlen ==
856                  GNUNET_STRINGS_buffer_fill ((char *) &r[1],
857                                              slen + nlen,
858                                              2,
859                                              handle->current->subsystem,
860                                              handle->current->name));
861   GNUNET_assert (NULL == handle->current->cont);
862   free_action_item (handle->current);
863   handle->current = NULL;
864   update_memory_statistics (handle);
865   GNUNET_MQ_notify_sent (env,
866                          &schedule_action,
867                          handle);
868   GNUNET_MQ_send (handle->mq,
869                   env);
870 }
871
872
873 /**
874  * Get handle for the statistics service.
875  *
876  * @param subsystem name of subsystem using the service
877  * @param cfg services configuration in use
878  * @return handle to use
879  */
880 struct GNUNET_STATISTICS_Handle *
881 GNUNET_STATISTICS_create (const char *subsystem,
882                           const struct GNUNET_CONFIGURATION_Handle *cfg)
883 {
884   struct GNUNET_STATISTICS_Handle *h;
885
886   if (GNUNET_YES ==
887       GNUNET_CONFIGURATION_get_value_yesno (cfg,
888                                             "statistics",
889                                             "DISABLE"))
890     return NULL;
891   h = GNUNET_new (struct GNUNET_STATISTICS_Handle);
892   h->cfg = cfg;
893   h->subsystem = GNUNET_strdup (subsystem);
894   h->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
895   return h;
896 }
897
898
899 /**
900  * Destroy a handle (free all state associated with
901  * it).
902  *
903  * @param h statistics handle to destroy
904  * @param sync_first set to #GNUNET_YES if pending SET requests should
905  *        be completed
906  */
907 void
908 GNUNET_STATISTICS_destroy (struct GNUNET_STATISTICS_Handle *h,
909                            int sync_first)
910 {
911   struct GNUNET_STATISTICS_GetHandle *pos;
912   struct GNUNET_STATISTICS_GetHandle *next;
913
914   if (NULL == h)
915     return;
916   GNUNET_assert (GNUNET_NO == h->do_destroy); /* Don't call twice. */
917   if ( (sync_first) &&
918        (GNUNET_YES == try_connect (h)) )
919   {
920     if ( (NULL != h->current) &&
921          (ACTION_GET == h->current->type) )
922       h->current->aborted = GNUNET_YES;
923     next = h->action_head;
924     while (NULL != (pos = next))
925     {
926       next = pos->next;
927       if ( (ACTION_GET == pos->type) ||
928            (ACTION_WATCH == pos->type) ||
929            (GNUNET_NO == pos->make_persistent) )
930       {
931         GNUNET_CONTAINER_DLL_remove (h->action_head,
932                                      h->action_tail,
933                                      pos);
934         free_action_item (pos);
935       }
936     }
937     h->do_destroy = GNUNET_YES;
938     schedule_action (h);
939     GNUNET_assert (NULL == h->destroy_task);
940     h->destroy_task
941       = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (h->backoff,
942                                                                      5),
943                                       &do_destroy,
944                                       h);
945     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
946                 "Deferring destruction\n");
947     return; /* do not finish destruction just yet */
948   }
949   /* do clean up all */
950   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
951               "Cleaning all up\n");
952   while (NULL != (pos = h->action_head))
953   {
954     GNUNET_CONTAINER_DLL_remove (h->action_head,
955                                  h->action_tail,
956                                  pos);
957     free_action_item (pos);
958   }
959   do_disconnect (h);
960   if (NULL != h->backoff_task)
961   {
962     GNUNET_SCHEDULER_cancel (h->backoff_task);
963     h->backoff_task = NULL;
964   }
965   if (NULL != h->destroy_task)
966   {
967     GNUNET_break (0);
968     GNUNET_SCHEDULER_cancel (h->destroy_task);
969     h->destroy_task = NULL;
970   }
971   for (unsigned int i = 0; i < h->watches_size; i++)
972   {
973     if (NULL == h->watches[i])
974       continue;
975     GNUNET_free (h->watches[i]->subsystem);
976     GNUNET_free (h->watches[i]->name);
977     GNUNET_free (h->watches[i]);
978   }
979   GNUNET_array_grow (h->watches,
980                      h->watches_size,
981                      0);
982   GNUNET_free (h->subsystem);
983   GNUNET_free (h);
984 }
985
986
987 /**
988  * Schedule the next action to be performed.
989  *
990  * @param cls statistics handle
991  */
992 static void
993 schedule_action (void *cls)
994 {
995   struct GNUNET_STATISTICS_Handle *h = cls;
996
997   if (NULL != h->backoff_task)
998     return;                     /* action already pending */
999   if (GNUNET_YES != try_connect (h))
1000   {
1001     reconnect_later (h);
1002     return;
1003   }
1004   /* schedule next action */
1005   while (NULL == h->current)
1006   {
1007     h->current = h->action_head;
1008     if (NULL == h->current)
1009     {
1010       struct GNUNET_MessageHeader *hdr;
1011       struct GNUNET_MQ_Envelope *env;
1012
1013       if (GNUNET_YES != h->do_destroy)
1014         return; /* nothing to do */
1015       /* let service know that we're done */
1016       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1017                   "Notifying service that we are done\n");
1018       h->do_destroy = GNUNET_SYSERR; /* in 'TEST' mode */
1019       env = GNUNET_MQ_msg (hdr,
1020                            GNUNET_MESSAGE_TYPE_TEST);
1021       GNUNET_MQ_send (h->mq,
1022                       env);
1023       return;
1024     }
1025     GNUNET_CONTAINER_DLL_remove (h->action_head,
1026                                  h->action_tail,
1027                                  h->current);
1028     switch (h->current->type)
1029     {
1030     case ACTION_GET:
1031       transmit_get (h);
1032       break;
1033     case ACTION_SET:
1034     case ACTION_UPDATE:
1035       transmit_set (h);
1036       break;
1037     case ACTION_WATCH:
1038       transmit_watch (h);
1039       break;
1040     default:
1041       GNUNET_assert (0);
1042       break;
1043     }
1044   }
1045 }
1046
1047
1048 /**
1049  * Get statistic from the peer.
1050  *
1051  * @param handle identification of the statistics service
1052  * @param subsystem limit to the specified subsystem, NULL for our subsystem
1053  * @param name name of the statistic value, NULL for all values
1054  * @param cont continuation to call when done (can be NULL)
1055  *        This callback CANNOT destroy the statistics handle in the same call.
1056  * @param proc function to call on each value
1057  * @param cls closure for @a cont and @a proc
1058  * @return NULL on error
1059  */
1060 struct GNUNET_STATISTICS_GetHandle *
1061 GNUNET_STATISTICS_get (struct GNUNET_STATISTICS_Handle *handle,
1062                        const char *subsystem,
1063                        const char *name,
1064                        GNUNET_STATISTICS_Callback cont,
1065                        GNUNET_STATISTICS_Iterator proc,
1066                        void *cls)
1067 {
1068   size_t slen1;
1069   size_t slen2;
1070   struct GNUNET_STATISTICS_GetHandle *ai;
1071
1072   if (NULL == handle)
1073     return NULL;
1074   GNUNET_assert (NULL != proc);
1075   GNUNET_assert (GNUNET_NO == handle->do_destroy);
1076   if (NULL == subsystem)
1077     subsystem = "";
1078   if (NULL == name)
1079     name = "";
1080   slen1 = strlen (subsystem) + 1;
1081   slen2 = strlen (name) + 1;
1082   GNUNET_assert (slen1 + slen2 + sizeof (struct GNUNET_MessageHeader) <
1083                  GNUNET_SERVER_MAX_MESSAGE_SIZE);
1084   ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle);
1085   ai->sh = handle;
1086   ai->subsystem = GNUNET_strdup (subsystem);
1087   ai->name = GNUNET_strdup (name);
1088   ai->cont = cont;
1089   ai->proc = proc;
1090   ai->cls = cls;
1091   ai->type = ACTION_GET;
1092   ai->msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
1093   GNUNET_CONTAINER_DLL_insert_tail (handle->action_head,
1094                                     handle->action_tail,
1095                                     ai);
1096   schedule_action (handle);
1097   return ai;
1098 }
1099
1100
1101 /**
1102  * Cancel a 'get' request.  Must be called before the 'cont'
1103  * function is called.
1104  *
1105  * @param gh handle of the request to cancel
1106  */
1107 void
1108 GNUNET_STATISTICS_get_cancel (struct GNUNET_STATISTICS_GetHandle *gh)
1109 {
1110   if (NULL == gh)
1111     return;
1112   gh->cont = NULL;
1113   if (gh->sh->current == gh)
1114   {
1115     gh->aborted = GNUNET_YES;
1116     return;
1117   }
1118   GNUNET_CONTAINER_DLL_remove (gh->sh->action_head,
1119                                gh->sh->action_tail,
1120                                gh);
1121   GNUNET_free (gh->name);
1122   GNUNET_free (gh->subsystem);
1123   GNUNET_free (gh);
1124 }
1125
1126
1127 /**
1128  * Watch statistics from the peer (be notified whenever they change).
1129  *
1130  * @param handle identification of the statistics service
1131  * @param subsystem limit to the specified subsystem, never NULL
1132  * @param name name of the statistic value, never NULL
1133  * @param proc function to call on each value
1134  * @param proc_cls closure for @a proc
1135  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1136  */
1137 int
1138 GNUNET_STATISTICS_watch (struct GNUNET_STATISTICS_Handle *handle,
1139                          const char *subsystem,
1140                          const char *name,
1141                          GNUNET_STATISTICS_Iterator proc,
1142                          void *proc_cls)
1143 {
1144   struct GNUNET_STATISTICS_WatchEntry *w;
1145
1146   if (NULL == handle)
1147     return GNUNET_SYSERR;
1148   w = GNUNET_new (struct GNUNET_STATISTICS_WatchEntry);
1149   w->subsystem = GNUNET_strdup (subsystem);
1150   w->name = GNUNET_strdup (name);
1151   w->proc = proc;
1152   w->proc_cls = proc_cls;
1153   GNUNET_array_append (handle->watches,
1154                        handle->watches_size,
1155                        w);
1156   schedule_watch_request (handle,
1157                           w);
1158   return GNUNET_OK;
1159 }
1160
1161
1162 /**
1163  * Stop watching statistics from the peer.
1164  *
1165  * @param handle identification of the statistics service
1166  * @param subsystem limit to the specified subsystem, never NULL
1167  * @param name name of the statistic value, never NULL
1168  * @param proc function to call on each value
1169  * @param proc_cls closure for @a proc
1170  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error (no such watch)
1171  */
1172 int
1173 GNUNET_STATISTICS_watch_cancel (struct GNUNET_STATISTICS_Handle *handle,
1174                                 const char *subsystem,
1175                                 const char *name,
1176                                 GNUNET_STATISTICS_Iterator proc,
1177                                 void *proc_cls)
1178 {
1179   struct GNUNET_STATISTICS_WatchEntry *w;
1180
1181   if (NULL == handle)
1182     return GNUNET_SYSERR;
1183   for (unsigned int i=0;i<handle->watches_size;i++)
1184   {
1185     w = handle->watches[i];
1186     if (NULL == w)
1187       continue;
1188     if ( (w->proc == proc) &&
1189          (w->proc_cls == proc_cls) &&
1190          (0 == strcmp (w->name, name)) &&
1191          (0 == strcmp (w->subsystem, subsystem)) )
1192     {
1193       GNUNET_free (w->name);
1194       GNUNET_free (w->subsystem);
1195       GNUNET_free (w);
1196       handle->watches[i] = NULL;
1197       return GNUNET_OK;
1198     }
1199   }
1200   return GNUNET_SYSERR;
1201 }
1202
1203
1204 /**
1205  * Queue a request to change a statistic.
1206  *
1207  * @param h statistics handle
1208  * @param name name of the value
1209  * @param make_persistent  should the value be kept across restarts?
1210  * @param value new value or change
1211  * @param type type of the action (#ACTION_SET or #ACTION_UPDATE)
1212  */
1213 static void
1214 add_setter_action (struct GNUNET_STATISTICS_Handle *h,
1215                    const char *name,
1216                    int make_persistent,
1217                    uint64_t value,
1218                    enum ActionType type)
1219 {
1220   struct GNUNET_STATISTICS_GetHandle *ai;
1221   size_t slen;
1222   size_t nlen;
1223   size_t nsize;
1224   int64_t delta;
1225
1226   slen = strlen (h->subsystem) + 1;
1227   nlen = strlen (name) + 1;
1228   nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
1229   if (nsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
1230   {
1231     GNUNET_break (0);
1232     return;
1233   }
1234   for (ai = h->action_head; NULL != ai; ai = ai->next)
1235   {
1236     if (! ( (0 == strcmp (ai->subsystem, h->subsystem)) &&
1237             (0 == strcmp (ai->name, name)) &&
1238             ( (ACTION_UPDATE == ai->type) ||
1239               (ACTION_SET == ai->type) ) ) )
1240       continue;
1241     if (ACTION_SET == ai->type)
1242     {
1243       if (ACTION_UPDATE == type)
1244       {
1245         delta = (int64_t) value;
1246         if (delta > 0)
1247         {
1248           /* update old set by new delta */
1249           ai->value += delta;
1250         }
1251         else
1252         {
1253           /* update old set by new delta, but never go negative */
1254           if (ai->value < -delta)
1255             ai->value = 0;
1256           else
1257             ai->value += delta;
1258         }
1259       }
1260       else
1261       {
1262         /* new set overrides old set */
1263         ai->value = value;
1264       }
1265     }
1266     else
1267     {
1268       if (ACTION_UPDATE == type)
1269       {
1270         /* make delta cummulative */
1271         delta = (int64_t) value;
1272         ai->value += delta;
1273       }
1274       else
1275       {
1276         /* drop old 'update', use new 'set' instead */
1277         ai->value = value;
1278         ai->type = type;
1279       }
1280     }
1281     ai->timeout = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT);
1282     ai->make_persistent = make_persistent;
1283     return;
1284   }
1285   /* no existing entry matches, create a fresh one */
1286   ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle);
1287   ai->sh = h;
1288   ai->subsystem = GNUNET_strdup (h->subsystem);
1289   ai->name = GNUNET_strdup (name);
1290   ai->timeout = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT);
1291   ai->make_persistent = make_persistent;
1292   ai->msize = nsize;
1293   ai->value = value;
1294   ai->type = type;
1295   GNUNET_CONTAINER_DLL_insert_tail (h->action_head,
1296                                     h->action_tail,
1297                                     ai);
1298   schedule_action (h);
1299 }
1300
1301
1302 /**
1303  * Set statistic value for the peer.  Will always use our
1304  * subsystem (the argument used when "handle" was created).
1305  *
1306  * @param handle identification of the statistics service
1307  * @param name name of the statistic value
1308  * @param value new value to set
1309  * @param make_persistent should the value be kept across restarts?
1310  */
1311 void
1312 GNUNET_STATISTICS_set (struct GNUNET_STATISTICS_Handle *handle,
1313                        const char *name,
1314                        uint64_t value,
1315                        int make_persistent)
1316 {
1317   if (NULL == handle)
1318     return;
1319   GNUNET_assert (GNUNET_NO == handle->do_destroy);
1320   add_setter_action (handle,
1321                      name,
1322                      make_persistent,
1323                      value,
1324                      ACTION_SET);
1325 }
1326
1327
1328 /**
1329  * Set statistic value for the peer.  Will always use our
1330  * subsystem (the argument used when "handle" was created).
1331  *
1332  * @param handle identification of the statistics service
1333  * @param name name of the statistic value
1334  * @param delta change in value (added to existing value)
1335  * @param make_persistent should the value be kept across restarts?
1336  */
1337 void
1338 GNUNET_STATISTICS_update (struct GNUNET_STATISTICS_Handle *handle,
1339                           const char *name,
1340                           int64_t delta,
1341                           int make_persistent)
1342 {
1343   if (NULL == handle)
1344     return;
1345   if (0 == delta)
1346     return;
1347   GNUNET_assert (GNUNET_NO == handle->do_destroy);
1348   add_setter_action (handle,
1349                      name,
1350                      make_persistent,
1351                      (uint64_t) delta,
1352                      ACTION_UPDATE);
1353 }
1354
1355
1356 /* end of statistics_api.c */