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