5d419f637a8b1acdb65b0d29014129c17e035f03
[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 schedule_action (struct GNUNET_STATISTICS_Handle *h);
247
248 /**
249  * Try to (re)connect to the statistics service.
250  *
251  * @return GNUNET_YES on success, GNUNET_NO on failure.
252  */
253 static int try_connect (struct GNUNET_STATISTICS_Handle *ret);
254
255
256 static void
257 insert_ai (struct GNUNET_STATISTICS_Handle *h,
258            struct GNUNET_STATISTICS_GetHandle *ai)
259 {
260   GNUNET_CONTAINER_DLL_insert_after (h->action_head, h->action_tail,
261                                      h->action_tail, ai);
262   if (h->action_head == ai)
263     schedule_action (h);
264 }
265
266
267 static void
268 schedule_watch_request (struct GNUNET_STATISTICS_Handle *h,
269                         struct GNUNET_STATISTICS_WatchEntry *watch)
270 {
271
272   struct GNUNET_STATISTICS_GetHandle *ai;
273   size_t slen;
274   size_t nlen;
275   size_t nsize;
276
277   GNUNET_assert (h != NULL);
278   if (GNUNET_YES != try_connect (h))
279     {
280       schedule_action (h);
281       return;
282     }
283   slen = strlen (watch->subsystem) + 1;
284   nlen = strlen (watch->name) + 1;
285   nsize = sizeof (struct GNUNET_MessageHeader) + slen + nlen;
286   if (nsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
287     {
288       GNUNET_break (0);
289       return;
290     }
291   ai = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_GetHandle));
292   ai->sh = h;
293   ai->subsystem = GNUNET_strdup (watch->subsystem);
294   ai->name = GNUNET_strdup (watch->name);
295   ai->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
296   ai->msize = nsize;
297   ai->type = ACTION_WATCH;
298   ai->proc = watch->proc;
299   ai->cls = watch->proc_cls;
300   insert_ai (h, ai);
301 }
302
303
304 /**
305  * Try to (re)connect to the statistics service.
306  *
307  * @return GNUNET_YES on success, GNUNET_NO on failure.
308  */
309 static int
310 try_connect (struct GNUNET_STATISTICS_Handle *ret)
311 {
312   unsigned int i;
313
314   if (ret->client != NULL)
315     return GNUNET_YES;
316   ret->client = GNUNET_CLIENT_connect ("statistics", ret->cfg);
317   if (ret->client != NULL)
318     {
319       for (i = 0; i < ret->watches_size; i++)
320         schedule_watch_request (ret, ret->watches[i]);
321       return GNUNET_YES;
322     }
323 #if DEBUG_STATISTICS
324   LOG (GNUNET_ERROR_TYPE_DEBUG,
325        _("Failed to connect to statistics service!\n"));
326 #endif
327   return GNUNET_NO;
328 }
329
330
331 /**
332  * Free memory associated with the given action item.
333  */
334 static void
335 free_action_item (struct GNUNET_STATISTICS_GetHandle *ai)
336 {
337   GNUNET_free_non_null (ai->subsystem);
338   GNUNET_free_non_null (ai->name);
339   GNUNET_free (ai);
340 }
341
342
343 /**
344  * GET processing is complete, tell client about it.
345  */
346 static void
347 finish (struct GNUNET_STATISTICS_Handle *h, int code)
348 {
349   struct GNUNET_STATISTICS_GetHandle *pos = h->current;
350
351   h->current = NULL;
352   schedule_action (h);
353   if (pos != NULL)
354     {
355       if (pos->cont != NULL)
356         pos->cont (pos->cls, code);
357       free_action_item (pos);
358     }
359 }
360
361
362 /**
363  * Process the message.
364  *
365  * @return GNUNET_OK if the message was well-formed
366  */
367 static int
368 process_message (struct GNUNET_STATISTICS_Handle *h,
369                  const struct GNUNET_MessageHeader *msg)
370 {
371   char *service;
372   char *name;
373   const struct GNUNET_STATISTICS_ReplyMessage *smsg;
374   uint16_t size;
375
376   if (h->current->aborted)
377     {
378 #if DEBUG_STATISTICS
379       LOG (GNUNET_ERROR_TYPE_DEBUG,
380            "Iteration was aborted, ignoring VALUE\n");
381 #endif
382       return GNUNET_OK;         /* don't bother */
383     }
384   size = ntohs (msg->size);
385   if (size < sizeof (struct GNUNET_STATISTICS_ReplyMessage))
386     {
387       GNUNET_break (0);
388       return GNUNET_SYSERR;
389     }
390   smsg = (const struct GNUNET_STATISTICS_ReplyMessage *) msg;
391   size -= sizeof (struct GNUNET_STATISTICS_ReplyMessage);
392   if (size !=
393       GNUNET_STRINGS_buffer_tokenize ((const char *) &smsg[1], size, 2,
394                                       &service, &name))
395     {
396       GNUNET_break (0);
397       return GNUNET_SYSERR;
398     }
399 #if DEBUG_STATISTICS
400   LOG (GNUNET_ERROR_TYPE_DEBUG,
401        "Received valid statistic on `%s:%s': %llu\n", service, name,
402        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) !=
432       ntohs (msg->size))
433     {
434       GNUNET_break (0);
435       return GNUNET_SYSERR;
436     }
437   wvm = (const struct GNUNET_STATISTICS_WatchValueMessage *) msg;
438   GNUNET_break (0 == ntohl (wvm->reserved));
439   wid = ntohl (wvm->wid);
440   if (wid >= h->watches_size)
441     {
442       GNUNET_break (0);
443       return GNUNET_SYSERR;
444     }
445   w = h->watches[wid];
446   (void) w->proc (w->proc_cls, w->subsystem, w->name,
447                   GNUNET_ntohll (wvm->value),
448                   0 != (ntohl (wvm->flags) & GNUNET_STATISTICS_PERSIST_BIT));
449   return GNUNET_OK;
450 }
451
452
453 /**
454  * Function called with messages from stats service.
455  *
456  * @param cls closure
457  * @param msg message received, NULL on timeout or fatal error
458  */
459 static void
460 receive_stats (void *cls, const struct GNUNET_MessageHeader *msg)
461 {
462   struct GNUNET_STATISTICS_Handle *h = cls;
463
464   if (msg == NULL)
465     {
466       if (NULL != h->client)
467         {
468           GNUNET_CLIENT_disconnect (h->client, GNUNET_NO);
469           h->client = NULL;
470         }
471 #if DEBUG_STATISTICS
472       LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
473            "Error receiving statistics from service, is the service running?\n");
474 #endif
475       finish (h, GNUNET_SYSERR);
476       return;
477     }
478   switch (ntohs (msg->type))
479     {
480     case GNUNET_MESSAGE_TYPE_STATISTICS_END:
481 #if DEBUG_STATISTICS
482       LOG (GNUNET_ERROR_TYPE_DEBUG, "Received end of statistics marker\n");
483 #endif
484       h->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
485       if (h->watches_size > 0)
486         {
487           GNUNET_CLIENT_receive (h->client, &receive_stats, h,
488                                  GNUNET_TIME_UNIT_FOREVER_REL);
489         }
490       else
491         {
492           h->receiving = GNUNET_NO;
493         }
494       finish (h, GNUNET_OK);
495       return;
496     case GNUNET_MESSAGE_TYPE_STATISTICS_VALUE:
497       if (GNUNET_OK == process_message (h, msg))
498         {
499           /* finally, look for more! */
500 #if DEBUG_STATISTICS
501           LOG (GNUNET_ERROR_TYPE_DEBUG,
502                "Processing VALUE done, now reading more\n");
503 #endif
504           GNUNET_CLIENT_receive (h->client, &receive_stats, h,
505                                  GNUNET_TIME_absolute_get_remaining
506                                  (h->current->timeout));
507           h->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
508           return;
509         }
510       GNUNET_break (0);
511       break;
512     case GNUNET_MESSAGE_TYPE_STATISTICS_WATCH_VALUE:
513       if (GNUNET_OK == process_watch_value (h, msg))
514         {
515           h->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
516           GNUNET_assert (h->watches_size > 0);
517           GNUNET_CLIENT_receive (h->client, &receive_stats, h,
518                                  GNUNET_TIME_UNIT_FOREVER_REL);
519           return;
520         }
521       GNUNET_break (0);
522       break;
523     default:
524       GNUNET_break (0);
525       break;
526     }
527   if (NULL != h->client)
528     {
529       GNUNET_CLIENT_disconnect (h->client, GNUNET_NO);
530       h->client = NULL;
531     }
532   finish (h, GNUNET_SYSERR);
533 }
534
535
536 /**
537  * Transmit a GET request (and if successful, start to receive
538  * the response).
539  */
540 static size_t
541 transmit_get (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf)
542 {
543   struct GNUNET_MessageHeader *hdr;
544   size_t slen1;
545   size_t slen2;
546   uint16_t msize;
547
548   if (buf == NULL)
549     {
550       /* timeout / error */
551 #if DEBUG_STATISTICS
552       LOG (GNUNET_ERROR_TYPE_DEBUG,
553            "Transmission of request for statistics failed!\n");
554 #endif
555       finish (handle, GNUNET_SYSERR);
556       return 0;
557     }
558   slen1 = strlen (handle->current->subsystem) + 1;
559   slen2 = strlen (handle->current->name) + 1;
560   msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
561   GNUNET_assert (msize <= size);
562   hdr = (struct GNUNET_MessageHeader *) buf;
563   hdr->size = htons (msize);
564   hdr->type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_GET);
565   GNUNET_assert (slen1 + slen2 ==
566                  GNUNET_STRINGS_buffer_fill ((char *) &hdr[1], slen1 + slen2,
567                                              2, handle->current->subsystem,
568                                              handle->current->name));
569   if (!handle->receiving)
570     {
571 #if DEBUG_STATISTICS
572       LOG (GNUNET_ERROR_TYPE_DEBUG,
573            "Transmission of GET done, now reading response\n");
574 #endif
575       handle->receiving = GNUNET_YES;
576       GNUNET_CLIENT_receive (handle->client, &receive_stats, handle,
577                              GNUNET_TIME_absolute_get_remaining
578                              (handle->current->timeout));
579     }
580   return msize;
581 }
582
583
584 /**
585  * Transmit a WATCH request (and if successful, start to receive
586  * the response).
587  */
588 static size_t
589 transmit_watch (struct GNUNET_STATISTICS_Handle *handle, size_t size,
590                 void *buf)
591 {
592   struct GNUNET_MessageHeader *hdr;
593   size_t slen1;
594   size_t slen2;
595   uint16_t msize;
596
597   if (buf == NULL)
598     {
599       /* timeout / error */
600 #if DEBUG_STATISTICS
601       LOG (GNUNET_ERROR_TYPE_DEBUG,
602            "Transmission of request for statistics failed!\n");
603 #endif
604       finish (handle, GNUNET_SYSERR);
605       return 0;
606     }
607 #if DEBUG_STATISTICS
608   LOG (GNUNET_ERROR_TYPE_DEBUG, "Transmitting watch request for `%s'\n",
609        handle->current->name);
610 #endif
611   slen1 = strlen (handle->current->subsystem) + 1;
612   slen2 = strlen (handle->current->name) + 1;
613   msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
614   GNUNET_assert (msize <= size);
615   hdr = (struct GNUNET_MessageHeader *) buf;
616   hdr->size = htons (msize);
617   hdr->type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_WATCH);
618   GNUNET_assert (slen1 + slen2 ==
619                  GNUNET_STRINGS_buffer_fill ((char *) &hdr[1], slen1 + slen2,
620                                              2, handle->current->subsystem,
621                                              handle->current->name));
622   if (GNUNET_YES != handle->receiving)
623     {
624       handle->receiving = GNUNET_YES;
625       GNUNET_CLIENT_receive (handle->client, &receive_stats, handle,
626                              GNUNET_TIME_UNIT_FOREVER_REL);
627     }
628   finish (handle, GNUNET_OK);
629   return msize;
630 }
631
632
633 /**
634  * Transmit a SET/UPDATE request.
635  */
636 static size_t
637 transmit_set (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf)
638 {
639   struct GNUNET_STATISTICS_SetMessage *r;
640   size_t slen;
641   size_t nlen;
642   size_t nsize;
643
644   if (NULL == buf)
645     {
646       finish (handle, GNUNET_SYSERR);
647       return 0;
648     }
649
650   slen = strlen (handle->current->subsystem) + 1;
651   nlen = strlen (handle->current->name) + 1;
652   nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
653   if (size < nsize)
654     {
655       GNUNET_break (0);
656       finish (handle, GNUNET_SYSERR);
657       return 0;
658     }
659   r = buf;
660   r->header.size = htons (nsize);
661   r->header.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_SET);
662   r->flags = 0;
663   r->value = GNUNET_htonll (handle->current->value);
664   if (handle->current->make_persistent)
665     r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_PERSISTENT);
666   if (handle->current->type == ACTION_UPDATE)
667     r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_RELATIVE);
668   GNUNET_assert (slen + nlen ==
669                  GNUNET_STRINGS_buffer_fill ((char *) &r[1], slen + nlen, 2,
670                                              handle->current->subsystem,
671                                              handle->current->name));
672   finish (handle, GNUNET_OK);
673   return nsize;
674 }
675
676
677 static size_t
678 transmit_action (void *cls, size_t size, void *buf)
679 {
680   struct GNUNET_STATISTICS_Handle *handle = cls;
681   size_t ret;
682
683   handle->th = NULL;
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,
866                                   GNUNET_CONSTANTS_SERVICE_TIMEOUT);
867       return;
868     }
869
870   /* schedule next action */
871   h->current = h->action_head;
872   if (NULL == h->current)
873     {
874       if (h->do_destroy)
875         {
876           h->do_destroy = GNUNET_NO;
877           GNUNET_STATISTICS_destroy (h, GNUNET_YES);
878         }
879       return;
880     }
881   GNUNET_CONTAINER_DLL_remove (h->action_head, h->action_tail, h->current);
882   timeout = GNUNET_TIME_absolute_get_remaining (h->current->timeout);
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,
973                                    gh);
974       GNUNET_free (gh->name);
975       GNUNET_free (gh->subsystem);
976       GNUNET_free (gh);
977     }
978 }
979
980
981 /**
982  * Watch statistics from the peer (be notified whenever they change).
983  * Note that the only way to cancel a "watch" request is to destroy
984  * the statistics handle given as the first argument to this call.
985  *
986  * @param handle identification of the statistics service
987  * @param subsystem limit to the specified subsystem, never NULL
988  * @param name name of the statistic value, never NULL
989  * @param proc function to call on each value
990  * @param proc_cls closure for proc
991  * @return GNUNET_OK on success, GNUNET_SYSERR on error
992  */
993 int
994 GNUNET_STATISTICS_watch (struct GNUNET_STATISTICS_Handle *handle,
995                          const char *subsystem, const char *name,
996                          GNUNET_STATISTICS_Iterator proc, void *proc_cls)
997 {
998   struct GNUNET_STATISTICS_WatchEntry *w;
999
1000   if (handle == NULL)
1001     return GNUNET_SYSERR;
1002   w = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_WatchEntry));
1003   w->subsystem = GNUNET_strdup (subsystem);
1004   w->name = GNUNET_strdup (name);
1005   w->proc = proc;
1006   w->proc_cls = proc_cls;
1007   GNUNET_array_append (handle->watches, handle->watches_size, w);
1008   schedule_watch_request (handle, w);
1009   return GNUNET_OK;
1010 }
1011
1012
1013 static void
1014 add_setter_action (struct GNUNET_STATISTICS_Handle *h, const char *name,
1015                    int make_persistent, uint64_t value, enum ActionType type)
1016 {
1017   struct GNUNET_STATISTICS_GetHandle *ai;
1018   size_t slen;
1019   size_t nlen;
1020   size_t nsize;
1021   int64_t delta;
1022
1023   GNUNET_assert (h != NULL);
1024   GNUNET_assert (name != NULL);
1025   if (GNUNET_YES != try_connect (h))
1026     return;
1027   slen = strlen (h->subsystem) + 1;
1028   nlen = strlen (name) + 1;
1029   nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
1030   if (nsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
1031     {
1032       GNUNET_break (0);
1033       return;
1034     }
1035   ai = h->action_head;
1036   while (ai != NULL)
1037     {
1038       if ((0 == strcmp (ai->subsystem, h->subsystem)) &&
1039           (0 == strcmp (ai->name, name)) && ((ai->type == ACTION_UPDATE) ||
1040                                              (ai->type == ACTION_SET)))
1041         {
1042           if (ai->type == ACTION_SET)
1043             {
1044               if (type == ACTION_UPDATE)
1045                 {
1046                   delta = (int64_t) value;
1047                   if (delta > 0)
1048                     {
1049                       ai->value += delta;
1050                     }
1051                   else
1052                     {
1053                       if (ai->value < -delta)
1054                         ai->value = 0;
1055                       else
1056                         ai->value += delta;
1057                     }
1058                 }
1059               else
1060                 {
1061                   ai->value = value;
1062                 }
1063             }
1064           else
1065             {
1066               if (type == ACTION_UPDATE)
1067                 {
1068                   delta = (int64_t) value;
1069                   ai->value += delta;
1070                 }
1071               else
1072                 {
1073                   ai->value = value;
1074                   ai->type = type;
1075                 }
1076             }
1077           ai->timeout =
1078             GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT);
1079           ai->make_persistent = make_persistent;
1080           return;
1081         }
1082       ai = ai->next;
1083     }
1084   ai = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_GetHandle));
1085   ai->sh = h;
1086   ai->subsystem = GNUNET_strdup (h->subsystem);
1087   ai->name = GNUNET_strdup (name);
1088   ai->timeout = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT);
1089   ai->make_persistent = make_persistent;
1090   ai->msize = nsize;
1091   ai->value = value;
1092   ai->type = type;
1093   insert_ai (h, ai);
1094 }
1095
1096
1097 /**
1098  * Set statistic value for the peer.  Will always use our
1099  * subsystem (the argument used when "handle" was created).
1100  *
1101  * @param handle identification of the statistics service
1102  * @param name name of the statistic value
1103  * @param value new value to set
1104  * @param make_persistent should the value be kept across restarts?
1105  */
1106 void
1107 GNUNET_STATISTICS_set (struct GNUNET_STATISTICS_Handle *handle,
1108                        const char *name, uint64_t value, int make_persistent)
1109 {
1110   if (handle == NULL)
1111     return;
1112   GNUNET_assert (GNUNET_NO == handle->do_destroy);
1113   add_setter_action (handle, name, make_persistent, value, ACTION_SET);
1114 }
1115
1116
1117 /**
1118  * Set statistic value for the peer.  Will always use our
1119  * subsystem (the argument used when "handle" was created).
1120  *
1121  * @param handle identification of the statistics service
1122  * @param name name of the statistic value
1123  * @param delta change in value (added to existing value)
1124  * @param make_persistent should the value be kept across restarts?
1125  */
1126 void
1127 GNUNET_STATISTICS_update (struct GNUNET_STATISTICS_Handle *handle,
1128                           const char *name, int64_t delta,
1129                           int make_persistent)
1130 {
1131   if (handle == NULL)
1132     return;
1133   if (delta == 0)
1134     return;
1135   GNUNET_assert (GNUNET_NO == handle->do_destroy);
1136   add_setter_action (handle, name, make_persistent, (uint64_t) delta,
1137                      ACTION_UPDATE);
1138 }
1139
1140
1141 /* end of statistics_api.c */