added ATS addresstype information to unix
[oweals/gnunet.git] / src / statistics / statistics_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010 Christian Grothoff (and other contributing authors)
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 2, 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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, 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_client_lib.h"
28 #include "gnunet_constants.h"
29 #include "gnunet_container_lib.h"
30 #include "gnunet_protocols.h"
31 #include "gnunet_server_lib.h"
32 #include "gnunet_statistics_service.h"
33 #include "gnunet_strings_lib.h"
34 #include "statistics.h"
35
36 /**
37  * How long do we wait until a statistics request for setting
38  * a value times out?  (The update will be lost if the
39  * service does not react within this timeframe).
40  */
41 #define SET_TRANSMIT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 2)
42
43 #define LOG(kind,...) GNUNET_log_from (kind, "statistics-api",__VA_ARGS__)
44
45 /**
46  * Types of actions.
47  */
48 enum ActionType
49 {
50   ACTION_GET,
51   ACTION_SET,
52   ACTION_UPDATE,
53   ACTION_WATCH
54 };
55
56
57 /**
58  * Entry kept for each value we are watching.
59  */
60 struct GNUNET_STATISTICS_WatchEntry
61 {
62
63   /**
64    * What subsystem is this action about? (never NULL)
65    */
66   char *subsystem;
67
68   /**
69    * What value is this action about? (never NULL)
70    */
71   char *name;
72
73   /**
74    * Function to call
75    */
76   GNUNET_STATISTICS_Iterator proc;
77
78   /**
79    * Closure for proc
80    */
81   void *proc_cls;
82
83 };
84
85
86 /**
87  * Linked list of things we still need to do.
88  */
89 struct GNUNET_STATISTICS_GetHandle
90 {
91
92   /**
93    * This is a doubly linked list.
94    */
95   struct GNUNET_STATISTICS_GetHandle *next;
96
97   /**
98    * This is a doubly linked list.
99    */
100   struct GNUNET_STATISTICS_GetHandle *prev;
101
102   /**
103    * Main statistics handle.
104    */
105   struct GNUNET_STATISTICS_Handle *sh;
106
107   /**
108    * What subsystem is this action about? (can be NULL)
109    */
110   char *subsystem;
111
112   /**
113    * What value is this action about? (can be NULL)
114    */
115   char *name;
116
117   /**
118    * Continuation to call once action is complete.
119    */
120   GNUNET_STATISTICS_Callback cont;
121
122   /**
123    * Function to call (for GET actions only).
124    */
125   GNUNET_STATISTICS_Iterator proc;
126
127   /**
128    * Closure for proc and cont.
129    */
130   void *cls;
131
132   /**
133    * Timeout for this action.
134    */
135   struct GNUNET_TIME_Absolute timeout;
136
137   /**
138    * Associated value.
139    */
140   uint64_t value;
141
142   /**
143    * Flag for SET/UPDATE actions.
144    */
145   int make_persistent;
146
147   /**
148    * Has the current iteration been aborted; for GET actions.
149    */
150   int aborted;
151
152   /**
153    * Is this a GET, SET, UPDATE or WATCH?
154    */
155   enum ActionType type;
156
157   /**
158    * Size of the message that we will be transmitting.
159    */
160   uint16_t msize;
161
162 };
163
164
165 /**
166  * Handle for the service.
167  */
168 struct GNUNET_STATISTICS_Handle
169 {
170   /**
171    * Name of our subsystem.
172    */
173   char *subsystem;
174
175   /**
176    * Configuration to use.
177    */
178   const struct GNUNET_CONFIGURATION_Handle *cfg;
179
180   /**
181    * Socket (if available).
182    */
183   struct GNUNET_CLIENT_Connection *client;
184
185   /**
186    * Currently pending transmission request.
187    */
188   struct GNUNET_CLIENT_TransmitHandle *th;
189
190   /**
191    * Head of the linked list of pending actions (first action
192    * to be performed).
193    */
194   struct GNUNET_STATISTICS_GetHandle *action_head;
195
196   /**
197    * Tail of the linked list of actions (for fast append).
198    */
199   struct GNUNET_STATISTICS_GetHandle *action_tail;
200
201   /**
202    * Action we are currently busy with (action request has been
203    * transmitted, we're now receiving the response from the
204    * service).
205    */
206   struct GNUNET_STATISTICS_GetHandle *current;
207
208   /**
209    * Array of watch entries.
210    */
211   struct GNUNET_STATISTICS_WatchEntry **watches;
212
213   /**
214    * Task doing exponential back-off trying to reconnect.
215    */
216   GNUNET_SCHEDULER_TaskIdentifier backoff_task;
217
218   /**
219    * Time for next connect retry.
220    */
221   struct GNUNET_TIME_Relative backoff;
222
223   /**
224    * Size of the 'watches' array.
225    */
226   unsigned int watches_size;
227
228   /**
229    * Should this handle auto-destruct once all actions have
230    * been processed?
231    */
232   int do_destroy;
233
234   /**
235    * Are we currently receiving from the service?
236    */
237   int receiving;
238
239 };
240
241
242
243 /**
244  * Schedule the next action to be performed.
245  */
246 static void
247 schedule_action (struct GNUNET_STATISTICS_Handle *h);
248
249 /**
250  * Try to (re)connect to the statistics service.
251  *
252  * @return GNUNET_YES on success, GNUNET_NO on failure.
253  */
254 static int
255 try_connect (struct GNUNET_STATISTICS_Handle *ret);
256
257
258 static void
259 insert_ai (struct GNUNET_STATISTICS_Handle *h,
260            struct GNUNET_STATISTICS_GetHandle *ai)
261 {
262   GNUNET_CONTAINER_DLL_insert_after (h->action_head, h->action_tail,
263                                      h->action_tail, ai);
264   if (h->action_head == ai)
265     schedule_action (h);
266 }
267
268
269 static void
270 schedule_watch_request (struct GNUNET_STATISTICS_Handle *h,
271                         struct GNUNET_STATISTICS_WatchEntry *watch)
272 {
273
274   struct GNUNET_STATISTICS_GetHandle *ai;
275   size_t slen;
276   size_t nlen;
277   size_t nsize;
278
279   GNUNET_assert (h != NULL);
280   if (GNUNET_YES != try_connect (h))
281   {
282     schedule_action (h);
283     return;
284   }
285   slen = strlen (watch->subsystem) + 1;
286   nlen = strlen (watch->name) + 1;
287   nsize = sizeof (struct GNUNET_MessageHeader) + slen + nlen;
288   if (nsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
289   {
290     GNUNET_break (0);
291     return;
292   }
293   ai = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_GetHandle));
294   ai->sh = h;
295   ai->subsystem = GNUNET_strdup (watch->subsystem);
296   ai->name = GNUNET_strdup (watch->name);
297   ai->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
298   ai->msize = nsize;
299   ai->type = ACTION_WATCH;
300   ai->proc = watch->proc;
301   ai->cls = watch->proc_cls;
302   insert_ai (h, ai);
303 }
304
305
306 /**
307  * Try to (re)connect to the statistics service.
308  *
309  * @return GNUNET_YES on success, GNUNET_NO on failure.
310  */
311 static int
312 try_connect (struct GNUNET_STATISTICS_Handle *ret)
313 {
314   unsigned int i;
315
316   if (ret->client != NULL)
317     return GNUNET_YES;
318   ret->client = GNUNET_CLIENT_connect ("statistics", ret->cfg);
319   if (ret->client != NULL)
320   {
321     for (i = 0; i < ret->watches_size; i++)
322       schedule_watch_request (ret, ret->watches[i]);
323     return GNUNET_YES;
324   }
325 #if DEBUG_STATISTICS
326   LOG (GNUNET_ERROR_TYPE_DEBUG,
327        _("Failed to connect to statistics service!\n"));
328 #endif
329   return GNUNET_NO;
330 }
331
332
333 /**
334  * Free memory associated with the given action item.
335  */
336 static void
337 free_action_item (struct GNUNET_STATISTICS_GetHandle *ai)
338 {
339   GNUNET_free_non_null (ai->subsystem);
340   GNUNET_free_non_null (ai->name);
341   GNUNET_free (ai);
342 }
343
344
345 /**
346  * GET processing is complete, tell client about it.
347  */
348 static void
349 finish (struct GNUNET_STATISTICS_Handle *h, int code)
350 {
351   struct GNUNET_STATISTICS_GetHandle *pos = h->current;
352
353   h->current = NULL;
354   schedule_action (h);
355   if (pos != NULL)
356   {
357     if (pos->cont != NULL)
358       pos->cont (pos->cls, code);
359     free_action_item (pos);
360   }
361 }
362
363
364 /**
365  * Process the message.
366  *
367  * @return GNUNET_OK if the message was well-formed
368  */
369 static int
370 process_message (struct GNUNET_STATISTICS_Handle *h,
371                  const struct GNUNET_MessageHeader *msg)
372 {
373   char *service;
374   char *name;
375   const struct GNUNET_STATISTICS_ReplyMessage *smsg;
376   uint16_t size;
377
378   if (h->current->aborted)
379   {
380 #if DEBUG_STATISTICS
381     LOG (GNUNET_ERROR_TYPE_DEBUG, "Iteration was aborted, ignoring VALUE\n");
382 #endif
383     return GNUNET_OK;           /* don't bother */
384   }
385   size = ntohs (msg->size);
386   if (size < sizeof (struct GNUNET_STATISTICS_ReplyMessage))
387   {
388     GNUNET_break (0);
389     return GNUNET_SYSERR;
390   }
391   smsg = (const struct GNUNET_STATISTICS_ReplyMessage *) msg;
392   size -= sizeof (struct GNUNET_STATISTICS_ReplyMessage);
393   if (size !=
394       GNUNET_STRINGS_buffer_tokenize ((const char *) &smsg[1], size, 2,
395                                       &service, &name))
396   {
397     GNUNET_break (0);
398     return GNUNET_SYSERR;
399   }
400 #if DEBUG_STATISTICS
401   LOG (GNUNET_ERROR_TYPE_DEBUG, "Received valid statistic on `%s:%s': %llu\n",
402        service, name, GNUNET_ntohll (smsg->value));
403 #endif
404   if (GNUNET_OK !=
405       h->current->proc (h->current->cls, service, name,
406                         GNUNET_ntohll (smsg->value),
407                         0 !=
408                         (ntohl (smsg->uid) & GNUNET_STATISTICS_PERSIST_BIT)))
409   {
410 #if DEBUG_STATISTICS
411     LOG (GNUNET_ERROR_TYPE_DEBUG,
412          "Processing of remaining statistics aborted by client.\n");
413 #endif
414     h->current->aborted = GNUNET_YES;
415   }
416 #if DEBUG_STATISTICS
417   LOG (GNUNET_ERROR_TYPE_DEBUG, "VALUE processed successfully\n");
418 #endif
419   return GNUNET_OK;
420 }
421
422
423 static int
424 process_watch_value (struct GNUNET_STATISTICS_Handle *h,
425                      const struct GNUNET_MessageHeader *msg)
426 {
427   const struct GNUNET_STATISTICS_WatchValueMessage *wvm;
428   struct GNUNET_STATISTICS_WatchEntry *w;
429   uint32_t wid;
430
431   if (sizeof (struct GNUNET_STATISTICS_WatchValueMessage) != ntohs (msg->size))
432   {
433     GNUNET_break (0);
434     return GNUNET_SYSERR;
435   }
436   wvm = (const struct GNUNET_STATISTICS_WatchValueMessage *) msg;
437   GNUNET_break (0 == ntohl (wvm->reserved));
438   wid = ntohl (wvm->wid);
439   if (wid >= h->watches_size)
440   {
441     GNUNET_break (0);
442     return GNUNET_SYSERR;
443   }
444   w = h->watches[wid];
445   (void) w->proc (w->proc_cls, w->subsystem, w->name,
446                   GNUNET_ntohll (wvm->value),
447                   0 != (ntohl (wvm->flags) & GNUNET_STATISTICS_PERSIST_BIT));
448   return GNUNET_OK;
449 }
450
451
452 /**
453  * Function called with messages from stats service.
454  *
455  * @param cls closure
456  * @param msg message received, NULL on timeout or fatal error
457  */
458 static void
459 receive_stats (void *cls, const struct GNUNET_MessageHeader *msg)
460 {
461   struct GNUNET_STATISTICS_Handle *h = cls;
462
463   if (msg == NULL)
464   {
465     if (NULL != h->client)
466     {
467       GNUNET_CLIENT_disconnect (h->client, GNUNET_NO);
468       h->client = NULL;
469     }
470 #if DEBUG_STATISTICS
471     LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
472          "Error receiving statistics from service, is the service running?\n");
473 #endif
474     finish (h, GNUNET_SYSERR);
475     return;
476   }
477   switch (ntohs (msg->type))
478   {
479   case GNUNET_MESSAGE_TYPE_STATISTICS_END:
480 #if DEBUG_STATISTICS
481     LOG (GNUNET_ERROR_TYPE_DEBUG, "Received end of statistics marker\n");
482 #endif
483     h->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
484     if (h->watches_size > 0)
485     {
486       GNUNET_CLIENT_receive (h->client, &receive_stats, h,
487                              GNUNET_TIME_UNIT_FOREVER_REL);
488     }
489     else
490     {
491       h->receiving = GNUNET_NO;
492     }
493     finish (h, GNUNET_OK);
494     return;
495   case GNUNET_MESSAGE_TYPE_STATISTICS_VALUE:
496     if (GNUNET_OK == process_message (h, msg))
497     {
498       /* finally, look for more! */
499 #if DEBUG_STATISTICS
500       LOG (GNUNET_ERROR_TYPE_DEBUG,
501            "Processing VALUE done, now reading more\n");
502 #endif
503       GNUNET_CLIENT_receive (h->client, &receive_stats, h,
504                              GNUNET_TIME_absolute_get_remaining (h->
505                                                                  current->timeout));
506       h->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
507       return;
508     }
509     GNUNET_break (0);
510     break;
511   case GNUNET_MESSAGE_TYPE_STATISTICS_WATCH_VALUE:
512     if (GNUNET_OK == process_watch_value (h, msg))
513     {
514       h->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
515       GNUNET_assert (h->watches_size > 0);
516       GNUNET_CLIENT_receive (h->client, &receive_stats, h,
517                              GNUNET_TIME_UNIT_FOREVER_REL);
518       return;
519     }
520     GNUNET_break (0);
521     break;
522   default:
523     GNUNET_break (0);
524     break;
525   }
526   if (NULL != h->client)
527   {
528     GNUNET_CLIENT_disconnect (h->client, GNUNET_NO);
529     h->client = NULL;
530   }
531   finish (h, GNUNET_SYSERR);
532 }
533
534
535 /**
536  * Transmit a GET request (and if successful, start to receive
537  * the response).
538  */
539 static size_t
540 transmit_get (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf)
541 {
542   struct GNUNET_MessageHeader *hdr;
543   size_t slen1;
544   size_t slen2;
545   uint16_t msize;
546
547   if (buf == NULL)
548   {
549     /* timeout / error */
550 #if DEBUG_STATISTICS
551     LOG (GNUNET_ERROR_TYPE_DEBUG,
552          "Transmission of request for statistics failed!\n");
553 #endif
554     finish (handle, GNUNET_SYSERR);
555     return 0;
556   }
557   slen1 = strlen (handle->current->subsystem) + 1;
558   slen2 = strlen (handle->current->name) + 1;
559   msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
560   GNUNET_assert (msize <= size);
561   hdr = (struct GNUNET_MessageHeader *) buf;
562   hdr->size = htons (msize);
563   hdr->type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_GET);
564   GNUNET_assert (slen1 + slen2 ==
565                  GNUNET_STRINGS_buffer_fill ((char *) &hdr[1], slen1 + slen2, 2,
566                                              handle->current->subsystem,
567                                              handle->current->name));
568   if (!handle->receiving)
569   {
570 #if DEBUG_STATISTICS
571     LOG (GNUNET_ERROR_TYPE_DEBUG,
572          "Transmission of GET done, now reading response\n");
573 #endif
574     handle->receiving = GNUNET_YES;
575     GNUNET_CLIENT_receive (handle->client, &receive_stats, handle,
576                            GNUNET_TIME_absolute_get_remaining (handle->
577                                                                current->timeout));
578   }
579   return msize;
580 }
581
582
583 /**
584  * Transmit a WATCH request (and if successful, start to receive
585  * the response).
586  */
587 static size_t
588 transmit_watch (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf)
589 {
590   struct GNUNET_MessageHeader *hdr;
591   size_t slen1;
592   size_t slen2;
593   uint16_t msize;
594
595   if (buf == NULL)
596   {
597     /* timeout / error */
598 #if DEBUG_STATISTICS
599     LOG (GNUNET_ERROR_TYPE_DEBUG,
600          "Transmission of request for statistics failed!\n");
601 #endif
602     finish (handle, GNUNET_SYSERR);
603     return 0;
604   }
605 #if DEBUG_STATISTICS
606   LOG (GNUNET_ERROR_TYPE_DEBUG, "Transmitting watch request for `%s'\n",
607        handle->current->name);
608 #endif
609   slen1 = strlen (handle->current->subsystem) + 1;
610   slen2 = strlen (handle->current->name) + 1;
611   msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
612   GNUNET_assert (msize <= size);
613   hdr = (struct GNUNET_MessageHeader *) buf;
614   hdr->size = htons (msize);
615   hdr->type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_WATCH);
616   GNUNET_assert (slen1 + slen2 ==
617                  GNUNET_STRINGS_buffer_fill ((char *) &hdr[1], slen1 + slen2, 2,
618                                              handle->current->subsystem,
619                                              handle->current->name));
620   if (GNUNET_YES != handle->receiving)
621   {
622     handle->receiving = GNUNET_YES;
623     GNUNET_CLIENT_receive (handle->client, &receive_stats, handle,
624                            GNUNET_TIME_UNIT_FOREVER_REL);
625   }
626   finish (handle, GNUNET_OK);
627   return msize;
628 }
629
630
631 /**
632  * Transmit a SET/UPDATE request.
633  */
634 static size_t
635 transmit_set (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf)
636 {
637   struct GNUNET_STATISTICS_SetMessage *r;
638   size_t slen;
639   size_t nlen;
640   size_t nsize;
641
642   if (NULL == buf)
643   {
644     finish (handle, GNUNET_SYSERR);
645     return 0;
646   }
647
648   slen = strlen (handle->current->subsystem) + 1;
649   nlen = strlen (handle->current->name) + 1;
650   nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
651   if (size < nsize)
652   {
653     GNUNET_break (0);
654     finish (handle, GNUNET_SYSERR);
655     return 0;
656   }
657   r = buf;
658   r->header.size = htons (nsize);
659   r->header.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_SET);
660   r->flags = 0;
661   r->value = GNUNET_htonll (handle->current->value);
662   if (handle->current->make_persistent)
663     r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_PERSISTENT);
664   if (handle->current->type == ACTION_UPDATE)
665     r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_RELATIVE);
666   GNUNET_assert (slen + nlen ==
667                  GNUNET_STRINGS_buffer_fill ((char *) &r[1], slen + nlen, 2,
668                                              handle->current->subsystem,
669                                              handle->current->name));
670   finish (handle, GNUNET_OK);
671   return nsize;
672 }
673
674
675 static size_t
676 transmit_action (void *cls, size_t size, void *buf)
677 {
678   struct GNUNET_STATISTICS_Handle *handle = cls;
679   size_t ret;
680
681   handle->th = NULL;
682   if (NULL == handle->current)
683     return 0;
684   switch (handle->current->type)
685   {
686   case ACTION_GET:
687     ret = transmit_get (handle, size, buf);
688     break;
689   case ACTION_SET:
690   case ACTION_UPDATE:
691     ret = transmit_set (handle, size, buf);
692     break;
693   case ACTION_WATCH:
694     ret = transmit_watch (handle, size, buf);
695     break;
696   default:
697     ret = 0;
698     GNUNET_break (0);
699     break;
700   }
701   return ret;
702 }
703
704
705 /**
706  * Get handle for the statistics service.
707  *
708  * @param subsystem name of subsystem using the service
709  * @param cfg services configuration in use
710  * @return handle to use
711  */
712 struct GNUNET_STATISTICS_Handle *
713 GNUNET_STATISTICS_create (const char *subsystem,
714                           const struct GNUNET_CONFIGURATION_Handle *cfg)
715 {
716   struct GNUNET_STATISTICS_Handle *ret;
717
718   GNUNET_assert (subsystem != NULL);
719   GNUNET_assert (cfg != NULL);
720   ret = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_Handle));
721   ret->cfg = cfg;
722   ret->subsystem = GNUNET_strdup (subsystem);
723   ret->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
724   if (GNUNET_YES != try_connect (ret))
725   {
726     GNUNET_free (ret->subsystem);
727     GNUNET_free (ret);
728     return NULL;
729   }
730   return ret;
731 }
732
733
734 /**
735  * Destroy a handle (free all state associated with
736  * it).
737  *
738  * @param h statistics handle to destroy
739  * @param sync_first set to GNUNET_YES if pending SET requests should
740  *        be completed
741  */
742 void
743 GNUNET_STATISTICS_destroy (struct GNUNET_STATISTICS_Handle *h, int sync_first)
744 {
745   struct GNUNET_STATISTICS_GetHandle *pos;
746   struct GNUNET_STATISTICS_GetHandle *next;
747   struct GNUNET_STATISTICS_GetHandle *prev;
748   struct GNUNET_TIME_Relative timeout;
749   int i;
750
751   if (h == NULL)
752     return;
753   if (GNUNET_SCHEDULER_NO_TASK != h->backoff_task)
754     GNUNET_SCHEDULER_cancel (h->backoff_task);
755   if (sync_first)
756   {
757     if (h->current != NULL)
758     {
759       if (h->current->type == ACTION_GET)
760       {
761         GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
762         h->th = NULL;
763         free_action_item (h->current);
764         h->current = NULL;
765       }
766     }
767     pos = h->action_head;
768     prev = NULL;
769     while (pos != NULL)
770     {
771       next = pos->next;
772       if (pos->type == ACTION_GET)
773       {
774         if (prev == NULL)
775           h->action_head = next;
776         else
777           prev->next = next;
778         free_action_item (pos);
779       }
780       else
781       {
782         prev = pos;
783       }
784       pos = next;
785     }
786     h->action_tail = prev;
787     if (h->current == NULL)
788     {
789       h->current = h->action_head;
790       if (h->action_head != NULL)
791       {
792         h->action_head = h->action_head->next;
793         if (h->action_head == NULL)
794           h->action_tail = NULL;
795       }
796     }
797     h->do_destroy = GNUNET_YES;
798     if ((h->current != NULL) && (h->th == NULL))
799     {
800       timeout = GNUNET_TIME_absolute_get_remaining (h->current->timeout);
801       h->th =
802           GNUNET_CLIENT_notify_transmit_ready (h->client, h->current->msize,
803                                                timeout, GNUNET_YES,
804                                                &transmit_action, h);
805       GNUNET_assert (NULL != h->th);
806     }
807     if (h->th != NULL)
808       return;
809   }
810   if (NULL != h->th)
811   {
812     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
813     h->th = NULL;
814   }
815   if (h->current != NULL)
816     free_action_item (h->current);
817   while (NULL != (pos = h->action_head))
818   {
819     h->action_head = pos->next;
820     free_action_item (pos);
821   }
822   if (h->client != NULL)
823   {
824     GNUNET_CLIENT_disconnect (h->client, GNUNET_YES);
825     h->client = NULL;
826   }
827   for (i = 0; i < h->watches_size; i++)
828   {
829     GNUNET_free (h->watches[i]->subsystem);
830     GNUNET_free (h->watches[i]->name);
831     GNUNET_free (h->watches[i]);
832   }
833   GNUNET_array_grow (h->watches, h->watches_size, 0);
834   GNUNET_free (h->subsystem);
835   GNUNET_free (h);
836 }
837
838
839 static void
840 finish_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
841 {
842   struct GNUNET_STATISTICS_Handle *h = cls;
843
844   h->backoff_task = GNUNET_SCHEDULER_NO_TASK;
845   finish (h, GNUNET_SYSERR);
846 }
847
848
849 /**
850  * Schedule the next action to be performed.
851  */
852 static void
853 schedule_action (struct GNUNET_STATISTICS_Handle *h)
854 {
855   struct GNUNET_TIME_Relative timeout;
856
857   if (h->current != NULL)
858     return;                     /* action already pending */
859   if (GNUNET_YES != try_connect (h))
860   {
861     h->backoff_task =
862         GNUNET_SCHEDULER_add_delayed (h->backoff, &finish_task, h);
863     h->backoff = GNUNET_TIME_relative_multiply (h->backoff, 2);
864     h->backoff =
865         GNUNET_TIME_relative_min (h->backoff, GNUNET_CONSTANTS_SERVICE_TIMEOUT);
866     return;
867   }
868
869   /* schedule next action */
870   h->current = h->action_head;
871   if (NULL == h->current)
872   {
873     if (h->do_destroy)
874     {
875       h->do_destroy = GNUNET_NO;
876       GNUNET_STATISTICS_destroy (h, GNUNET_YES);
877     }
878     return;
879   }
880   GNUNET_CONTAINER_DLL_remove (h->action_head, h->action_tail, h->current);
881   timeout = GNUNET_TIME_absolute_get_remaining (h->current->timeout);
882   GNUNET_assert (NULL == h->th);
883   if (NULL ==
884       (h->th =
885        GNUNET_CLIENT_notify_transmit_ready (h->client, h->current->msize,
886                                             timeout, GNUNET_YES,
887                                             &transmit_action, h)))
888   {
889 #if DEBUG_STATISTICS
890     LOG (GNUNET_ERROR_TYPE_DEBUG,
891          "Failed to transmit request to statistics service.\n");
892 #endif
893     finish (h, GNUNET_SYSERR);
894   }
895 }
896
897
898 /**
899  * Get statistic from the peer.
900  *
901  * @param handle identification of the statistics service
902  * @param subsystem limit to the specified subsystem, NULL for our subsystem
903  * @param name name of the statistic value, NULL for all values
904  * @param timeout after how long should we give up (and call
905  *        cont with an error code)?
906  * @param cont continuation to call when done (can be NULL)
907  * @param proc function to call on each value
908  * @param cls closure for cont and proc
909  * @return NULL on error
910  */
911 struct GNUNET_STATISTICS_GetHandle *
912 GNUNET_STATISTICS_get (struct GNUNET_STATISTICS_Handle *handle,
913                        const char *subsystem, const char *name,
914                        struct GNUNET_TIME_Relative timeout,
915                        GNUNET_STATISTICS_Callback cont,
916                        GNUNET_STATISTICS_Iterator proc, void *cls)
917 {
918   size_t slen1;
919   size_t slen2;
920   struct GNUNET_STATISTICS_GetHandle *ai;
921
922   GNUNET_assert (handle != NULL);
923   GNUNET_assert (proc != NULL);
924   GNUNET_assert (GNUNET_NO == handle->do_destroy);
925   if (GNUNET_YES != try_connect (handle))
926   {
927 #if DEBUG_STATISTICS
928     LOG (GNUNET_ERROR_TYPE_DEBUG,
929          "Failed to connect to statistics service, can not get value `%s:%s'.\n",
930          strlen (subsystem) ? subsystem : "*", strlen (name) ? name : "*");
931 #endif
932     return NULL;
933   }
934   if (subsystem == NULL)
935     subsystem = "";
936   if (name == NULL)
937     name = "";
938   slen1 = strlen (subsystem) + 1;
939   slen2 = strlen (name) + 1;
940   GNUNET_assert (slen1 + slen2 + sizeof (struct GNUNET_MessageHeader) <
941                  GNUNET_SERVER_MAX_MESSAGE_SIZE);
942   ai = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_GetHandle));
943   ai->sh = handle;
944   ai->subsystem = GNUNET_strdup (subsystem);
945   ai->name = GNUNET_strdup (name);
946   ai->cont = cont;
947   ai->proc = proc;
948   ai->cls = cls;
949   ai->timeout = GNUNET_TIME_relative_to_absolute (timeout);
950   ai->type = ACTION_GET;
951   ai->msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
952   insert_ai (handle, ai);
953   return ai;
954 }
955
956
957 /**
958  * Cancel a 'get' request.  Must be called before the 'cont'
959  * function is called.
960  *
961  * @param gh handle of the request to cancel
962  */
963 void
964 GNUNET_STATISTICS_get_cancel (struct GNUNET_STATISTICS_GetHandle *gh)
965 {
966   if (gh->sh->current == gh)
967   {
968     gh->aborted = GNUNET_YES;
969   }
970   else
971   {
972     GNUNET_CONTAINER_DLL_remove (gh->sh->action_head, gh->sh->action_tail, gh);
973     GNUNET_free (gh->name);
974     GNUNET_free (gh->subsystem);
975     GNUNET_free (gh);
976   }
977 }
978
979
980 /**
981  * Watch statistics from the peer (be notified whenever they change).
982  * Note that the only way to cancel a "watch" request is to destroy
983  * the statistics handle given as the first argument to this call.
984  *
985  * @param handle identification of the statistics service
986  * @param subsystem limit to the specified subsystem, never NULL
987  * @param name name of the statistic value, never NULL
988  * @param proc function to call on each value
989  * @param proc_cls closure for proc
990  * @return GNUNET_OK on success, GNUNET_SYSERR on error
991  */
992 int
993 GNUNET_STATISTICS_watch (struct GNUNET_STATISTICS_Handle *handle,
994                          const char *subsystem, const char *name,
995                          GNUNET_STATISTICS_Iterator proc, void *proc_cls)
996 {
997   struct GNUNET_STATISTICS_WatchEntry *w;
998
999   if (handle == NULL)
1000     return GNUNET_SYSERR;
1001   w = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_WatchEntry));
1002   w->subsystem = GNUNET_strdup (subsystem);
1003   w->name = GNUNET_strdup (name);
1004   w->proc = proc;
1005   w->proc_cls = proc_cls;
1006   GNUNET_array_append (handle->watches, handle->watches_size, w);
1007   schedule_watch_request (handle, w);
1008   return GNUNET_OK;
1009 }
1010
1011
1012 static void
1013 add_setter_action (struct GNUNET_STATISTICS_Handle *h, const char *name,
1014                    int make_persistent, uint64_t value, enum ActionType type)
1015 {
1016   struct GNUNET_STATISTICS_GetHandle *ai;
1017   size_t slen;
1018   size_t nlen;
1019   size_t nsize;
1020   int64_t delta;
1021
1022   GNUNET_assert (h != NULL);
1023   GNUNET_assert (name != NULL);
1024   if (GNUNET_YES != try_connect (h))
1025     return;
1026   slen = strlen (h->subsystem) + 1;
1027   nlen = strlen (name) + 1;
1028   nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
1029   if (nsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
1030   {
1031     GNUNET_break (0);
1032     return;
1033   }
1034   ai = h->action_head;
1035   while (ai != NULL)
1036   {
1037     if ((0 == strcmp (ai->subsystem, h->subsystem)) &&
1038         (0 == strcmp (ai->name, name)) && ((ai->type == ACTION_UPDATE) ||
1039                                            (ai->type == ACTION_SET)))
1040     {
1041       if (ai->type == ACTION_SET)
1042       {
1043         if (type == ACTION_UPDATE)
1044         {
1045           delta = (int64_t) value;
1046           if (delta > 0)
1047           {
1048             ai->value += delta;
1049           }
1050           else
1051           {
1052             if (ai->value < -delta)
1053               ai->value = 0;
1054             else
1055               ai->value += delta;
1056           }
1057         }
1058         else
1059         {
1060           ai->value = value;
1061         }
1062       }
1063       else
1064       {
1065         if (type == ACTION_UPDATE)
1066         {
1067           delta = (int64_t) value;
1068           ai->value += delta;
1069         }
1070         else
1071         {
1072           ai->value = value;
1073           ai->type = type;
1074         }
1075       }
1076       ai->timeout = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT);
1077       ai->make_persistent = make_persistent;
1078       return;
1079     }
1080     ai = ai->next;
1081   }
1082   ai = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_GetHandle));
1083   ai->sh = h;
1084   ai->subsystem = GNUNET_strdup (h->subsystem);
1085   ai->name = GNUNET_strdup (name);
1086   ai->timeout = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT);
1087   ai->make_persistent = make_persistent;
1088   ai->msize = nsize;
1089   ai->value = value;
1090   ai->type = type;
1091   insert_ai (h, ai);
1092 }
1093
1094
1095 /**
1096  * Set statistic value for the peer.  Will always use our
1097  * subsystem (the argument used when "handle" was created).
1098  *
1099  * @param handle identification of the statistics service
1100  * @param name name of the statistic value
1101  * @param value new value to set
1102  * @param make_persistent should the value be kept across restarts?
1103  */
1104 void
1105 GNUNET_STATISTICS_set (struct GNUNET_STATISTICS_Handle *handle,
1106                        const char *name, uint64_t value, int make_persistent)
1107 {
1108   if (handle == NULL)
1109     return;
1110   GNUNET_assert (GNUNET_NO == handle->do_destroy);
1111   add_setter_action (handle, name, make_persistent, value, ACTION_SET);
1112 }
1113
1114
1115 /**
1116  * Set statistic value for the peer.  Will always use our
1117  * subsystem (the argument used when "handle" was created).
1118  *
1119  * @param handle identification of the statistics service
1120  * @param name name of the statistic value
1121  * @param delta change in value (added to existing value)
1122  * @param make_persistent should the value be kept across restarts?
1123  */
1124 void
1125 GNUNET_STATISTICS_update (struct GNUNET_STATISTICS_Handle *handle,
1126                           const char *name, int64_t delta, int make_persistent)
1127 {
1128   if (handle == NULL)
1129     return;
1130   if (delta == 0)
1131     return;
1132   GNUNET_assert (GNUNET_NO == handle->do_destroy);
1133   add_setter_action (handle, name, make_persistent, (uint64_t) delta,
1134                      ACTION_UPDATE);
1135 }
1136
1137
1138 /* end of statistics_api.c */