c8a341b0c8a8d6b69e4d9e8662bad0a781f48b1e
[oweals/gnunet.git] / src / statistics / statistics_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009 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_container_lib.h"
29 #include "gnunet_protocols.h"
30 #include "gnunet_server_lib.h"
31 #include "gnunet_statistics_service.h"
32 #include "gnunet_strings_lib.h"
33 #include "statistics.h"
34
35 #define SET_TRANSMIT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
36
37
38 /**
39  * Types of actions.
40  */
41 enum ActionType
42 {
43   ACTION_GET,
44   ACTION_SET,
45   ACTION_UPDATE
46 };
47
48
49 /**
50  * Linked list of things we still need to do.
51  */
52 struct GNUNET_STATISTICS_GetHandle
53 {
54
55   /**
56    * This is a doubly linked list.
57    */
58   struct GNUNET_STATISTICS_GetHandle *next;
59
60   /**
61    * This is a doubly linked list.
62    */
63   struct GNUNET_STATISTICS_GetHandle *prev;
64
65   /**
66    * Main statistics handle.
67    */
68   struct GNUNET_STATISTICS_Handle *sh;
69  
70   /**
71    * What subsystem is this action about? (can be NULL)
72    */
73   char *subsystem;
74
75   /**
76    * What value is this action about? (can be NULL)
77    */
78   char *name;
79
80   /**
81    * Continuation to call once action is complete.
82    */
83   GNUNET_STATISTICS_Callback cont;
84
85   /**
86    * Function to call (for GET actions only).
87    */
88   GNUNET_STATISTICS_Iterator proc;
89
90   /**
91    * Closure for proc and cont.
92    */
93   void *cls;
94
95   /**
96    * Timeout for this action.
97    */
98   struct GNUNET_TIME_Absolute timeout;
99
100   /**
101    * Associated value.
102    */
103   uint64_t value;
104
105   /**
106    * Flag for SET/UPDATE actions.
107    */
108   int make_persistent;
109
110   /**
111    * Has the current iteration been aborted; for GET actions.
112    */
113   int aborted;
114
115   /**
116    * Is this a GET, SET or UPDATE?
117    */
118   enum ActionType type;
119
120   /**
121    * Size of the message that we will be transmitting.
122    */
123   uint16_t msize;
124
125 };
126
127
128 /**
129  * Handle for the service.
130  */
131 struct GNUNET_STATISTICS_Handle
132 {
133   /**
134    * Our scheduler.
135    */
136   struct GNUNET_SCHEDULER_Handle *sched;
137
138   /**
139    * Name of our subsystem.
140    */
141   char *subsystem;
142
143   /**
144    * Configuration to use.
145    */
146   const struct GNUNET_CONFIGURATION_Handle *cfg;
147
148   /**
149    * Socket (if available).
150    */
151   struct GNUNET_CLIENT_Connection *client;
152
153   /**
154    * Currently pending transmission request.
155    */
156   struct GNUNET_CLIENT_TransmitHandle *th;
157
158   /**
159    * Head of the linked list of pending actions (first action
160    * to be performed).
161    */
162   struct GNUNET_STATISTICS_GetHandle *action_head;
163
164   /**
165    * Tail of the linked list of actions (for fast append).
166    */
167   struct GNUNET_STATISTICS_GetHandle *action_tail;
168
169   /**
170    * Action we are currently busy with (action request has been
171    * transmitted, we're now receiving the response from the
172    * service).
173    */
174   struct GNUNET_STATISTICS_GetHandle *current;
175
176   /**
177    * Should this handle auto-destruct once all actions have
178    * been processed?
179    */
180   int do_destroy;
181
182 };
183
184
185 /**
186  * Try to (re)connect to the statistics service.
187  *
188  * @return GNUNET_YES on success, GNUNET_NO on failure.
189  */
190 static int
191 try_connect (struct GNUNET_STATISTICS_Handle *ret)
192 {
193   if (ret->client != NULL)
194     return GNUNET_OK;
195   ret->client = GNUNET_CLIENT_connect (ret->sched, "statistics", ret->cfg);
196   if (ret->client != NULL)
197     return GNUNET_YES;
198 #if DEBUG_STATISTICS
199   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
200               _("Failed to connect to statistics service!\n"));
201 #endif
202   return GNUNET_NO;
203 }
204
205
206 /**
207  * Free memory associated with the given action item.
208  */
209 static void
210 free_action_item (struct GNUNET_STATISTICS_GetHandle *ai)
211 {
212   GNUNET_free_non_null (ai->subsystem);
213   GNUNET_free_non_null (ai->name);
214   GNUNET_free (ai);
215 }
216
217
218 /**
219  * Schedule the next action to be performed.
220  */
221 static void schedule_action (struct GNUNET_STATISTICS_Handle *h);
222
223
224 /**
225  * GET processing is complete, tell client about it.
226  */
227 static void
228 finish (struct GNUNET_STATISTICS_Handle *h, int code)
229 {
230   struct GNUNET_STATISTICS_GetHandle *pos = h->current;
231   h->current = NULL;
232   schedule_action (h);
233   if (pos->cont != NULL)
234     pos->cont (pos->cls, code);
235   free_action_item (pos);
236 }
237
238
239 /**
240  * Process the message.
241  *
242  * @return GNUNET_OK if the message was well-formed
243  */
244 static int
245 process_message (struct GNUNET_STATISTICS_Handle *h,
246                  const struct GNUNET_MessageHeader *msg)
247 {
248   char *service;
249   char *name;
250   const struct GNUNET_STATISTICS_ReplyMessage *smsg;
251   uint16_t size;
252
253   if (h->current->aborted)
254     return GNUNET_OK;           /* don't bother */
255   size = ntohs (msg->size);
256   if (size < sizeof (struct GNUNET_STATISTICS_ReplyMessage))
257     {
258       GNUNET_break (0);
259       return GNUNET_SYSERR;
260     }
261   smsg = (const struct GNUNET_STATISTICS_ReplyMessage *) msg;
262   size -= sizeof (struct GNUNET_STATISTICS_ReplyMessage);
263   if (size != GNUNET_STRINGS_buffer_tokenize ((const char *) &smsg[1],
264                                               size, 2, &service, &name))
265     {
266       GNUNET_break (0);
267       return GNUNET_SYSERR;
268     }
269 #if DEBUG_STATISTICS
270   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
271               "Received valid statistic on `%s:%s': %llu\n",
272               service, name, GNUNET_ntohll (smsg->value));
273 #endif
274   if (GNUNET_OK !=
275       h->current->proc (h->current->cls,
276                         service,
277                         name,
278                         GNUNET_ntohll (smsg->value),
279                         0 !=
280                         (ntohl (smsg->uid) & GNUNET_STATISTICS_PERSIST_BIT)))
281     {
282 #if DEBUG_STATISTICS
283       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
284                   "Processing of remaining statistics aborted by client.\n");
285 #endif
286       h->current->aborted = GNUNET_YES;    
287     }
288   return GNUNET_OK;
289 }
290
291
292 /**
293  * Function called with messages from stats service.
294  *
295  * @param cls closure
296  * @param msg message received, NULL on timeout or fatal error
297  */
298 static void
299 receive_stats (void *cls, const struct GNUNET_MessageHeader *msg)
300 {
301   struct GNUNET_STATISTICS_Handle *h = cls;
302
303   if (msg == NULL)
304     {
305       if (NULL != h->client)
306         {
307           GNUNET_CLIENT_disconnect (h->client, GNUNET_NO);
308           h->client = NULL;
309         }
310 #if DEBUG_STATISTICS
311       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
312                   "Error receiving statistics from service, is the service running?\n" );
313 #endif
314       finish (h, GNUNET_SYSERR);
315       return;
316     }
317   switch (ntohs (msg->type))
318     {
319     case GNUNET_MESSAGE_TYPE_STATISTICS_END:
320 #if DEBUG_STATISTICS
321       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
322                   "Received end of statistics marker\n");
323 #endif
324       finish (h, GNUNET_OK);
325       return;
326     case GNUNET_MESSAGE_TYPE_STATISTICS_VALUE:
327       if (GNUNET_OK == process_message (h, msg))
328         {
329           /* finally, look for more! */
330           GNUNET_CLIENT_receive (h->client,
331                                  &receive_stats,
332                                  h,
333                                  GNUNET_TIME_absolute_get_remaining
334                                  (h->current->timeout));
335           return;
336         }
337       GNUNET_break (0);
338       break;
339     default:
340       GNUNET_break (0);
341       break;
342     }
343   if (NULL != h->client)
344     {
345       GNUNET_CLIENT_disconnect (h->client, GNUNET_NO);
346       h->client = NULL;
347     }
348   finish (h, GNUNET_SYSERR);
349 }
350
351
352 /**
353  * Transmit a GET request (and if successful, start to receive
354  * the response).
355  */
356 static size_t
357 transmit_get (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf)
358 {
359   struct GNUNET_MessageHeader *hdr;
360   size_t slen1;
361   size_t slen2;
362   uint16_t msize;
363
364   if (buf == NULL)
365     {
366       /* timeout / error */
367 #if DEBUG_STATISTICS
368       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
369                   "Transmission of request for statistics failed!\n");
370 #endif
371       finish (handle, GNUNET_SYSERR);
372       return 0;
373     }
374   slen1 = strlen (handle->current->subsystem) + 1;
375   slen2 = strlen (handle->current->name) + 1;
376   msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
377   GNUNET_assert (msize <= size);
378   hdr = (struct GNUNET_MessageHeader *) buf;
379   hdr->size = htons (msize);
380   hdr->type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_GET);
381   GNUNET_assert (slen1 + slen2 ==
382                  GNUNET_STRINGS_buffer_fill ((char *) &hdr[1],
383                                              slen1 + slen2,
384                                              2,
385                                              handle->current->subsystem,
386                                              handle->current->name));
387   GNUNET_CLIENT_receive (handle->client,
388                          &receive_stats,
389                          handle,
390                          GNUNET_TIME_absolute_get_remaining (handle->
391                                                              current->timeout));
392   return msize;
393 }
394
395
396
397 /**
398  * Transmit a SET/UPDATE request.
399  */
400 static size_t
401 transmit_set (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf)
402 {
403   struct GNUNET_STATISTICS_SetMessage *r;
404   size_t slen;
405   size_t nlen;
406   size_t nsize;
407
408   if (NULL == buf)
409     {
410       finish (handle, GNUNET_SYSERR);
411       return 0;
412     }
413
414   slen = strlen (handle->current->subsystem) + 1;
415   nlen = strlen (handle->current->name) + 1;
416   nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
417   if (size < nsize)
418     {
419       GNUNET_break (0);
420       finish (handle, GNUNET_SYSERR);
421       return 0;
422     }
423   r = buf;
424   r->header.size = htons (nsize);
425   r->header.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_SET);
426   r->flags = 0;
427   r->value = GNUNET_htonll (handle->current->value);
428   if (handle->current->make_persistent)
429     r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_PERSISTENT);
430   if (handle->current->type == ACTION_UPDATE)
431     r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_RELATIVE);
432   GNUNET_assert (slen + nlen ==
433                  GNUNET_STRINGS_buffer_fill ((char *) &r[1],
434                                              slen + nlen,
435                                              2,
436                                              handle->current->subsystem,
437                                              handle->current->name));
438   finish (handle, GNUNET_OK);
439   return nsize;
440 }
441
442
443 static size_t
444 transmit_action (void *cls, size_t size, void *buf)
445 {
446   struct GNUNET_STATISTICS_Handle *handle = cls;
447   size_t ret;
448
449   handle->th = NULL;
450   switch (handle->current->type)
451     {
452     case ACTION_GET:
453       ret = transmit_get (handle, size, buf);
454       break;
455     case ACTION_SET:
456     case ACTION_UPDATE:
457       ret = transmit_set (handle, size, buf);
458       break;
459     default:
460       ret = 0;
461       GNUNET_break (0);
462       break; 
463     }
464   return ret;
465 }
466
467
468 /**
469  * Get handle for the statistics service.
470  *
471  * @param sched scheduler to use
472  * @param subsystem name of subsystem using the service
473  * @param cfg services configuration in use
474  * @return handle to use
475  */
476 struct GNUNET_STATISTICS_Handle *
477 GNUNET_STATISTICS_create (struct GNUNET_SCHEDULER_Handle *sched,
478                           const char *subsystem,
479                           const struct GNUNET_CONFIGURATION_Handle *cfg)
480 {
481   struct GNUNET_STATISTICS_Handle *ret;
482
483   GNUNET_assert (subsystem != NULL);
484   GNUNET_assert (sched != NULL);
485   GNUNET_assert (cfg != NULL);
486   ret = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_Handle));
487   ret->sched = sched;
488   ret->cfg = cfg;
489   ret->subsystem = GNUNET_strdup (subsystem);
490   try_connect (ret);
491   return ret;
492 }
493
494
495 /**
496  * Destroy a handle (free all state associated with
497  * it).
498  *
499  * @param h statistics handle to destroy
500  * @param sync_first set to GNUNET_YES if pending SET requests should
501  *        be completed
502  */
503 void
504 GNUNET_STATISTICS_destroy (struct GNUNET_STATISTICS_Handle *h,
505                            int sync_first)
506 {
507   struct GNUNET_STATISTICS_GetHandle *pos;
508   struct GNUNET_STATISTICS_GetHandle *next;
509   struct GNUNET_STATISTICS_GetHandle *prev;
510   struct GNUNET_TIME_Relative timeout;
511
512   if (sync_first)
513     {
514       if (h->current != NULL)
515         {
516           if (h->current->type == ACTION_GET)
517             {
518               GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
519               h->th = NULL;
520               free_action_item (h->current);
521               h->current = NULL;
522             }
523         }
524       pos = h->action_head;
525       prev = NULL;
526       while (pos != NULL)
527         {
528           next = pos->next;
529           if (pos->type == ACTION_GET)
530             {
531               if (prev == NULL)
532                 h->action_head = next;
533               else
534                 prev->next = next;
535               free_action_item (pos);
536             }
537           else
538             {
539               prev = pos;
540             }
541           pos = next;
542         }
543       h->action_tail = prev;
544       if (h->current == NULL)
545         {
546           h->current = h->action_head;
547           if (h->action_head != NULL)
548             {
549               h->action_head = h->action_head->next;
550               if (h->action_head == NULL)
551                 h->action_tail = NULL;
552             }
553         }
554       if ( (h->current != NULL) &&
555            (h->th == NULL) )
556         {                                       
557           timeout = GNUNET_TIME_absolute_get_remaining (h->current->timeout);
558           h->th = GNUNET_CLIENT_notify_transmit_ready (h->client,
559                                                        h->current->msize,
560                                                        timeout,
561                                                        GNUNET_YES,
562                                                        &transmit_action, h);
563           GNUNET_assert (NULL != h->th);
564         }
565       h->do_destroy = GNUNET_YES;
566       return;
567     }
568   if (NULL != h->th)
569     {
570       GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
571       h->th = NULL;
572     }
573   if (h->current != NULL)
574     free_action_item (h->current);
575   while (NULL != (pos = h->action_head))
576     {
577       h->action_head = pos->next;
578       free_action_item (pos);
579     }
580   if (h->client != NULL)
581     {
582       GNUNET_CLIENT_disconnect (h->client, GNUNET_YES);
583       h->client = NULL;
584     }
585   GNUNET_free (h->subsystem);
586   GNUNET_free (h);
587 }
588
589
590
591 /**
592  * Schedule the next action to be performed.
593  */
594 static void
595 schedule_action (struct GNUNET_STATISTICS_Handle *h)
596 {
597   struct GNUNET_TIME_Relative timeout;
598
599   if (h->current != NULL)
600     return;                     /* action already pending */
601   if (GNUNET_YES != try_connect (h))
602     {
603       finish (h, GNUNET_SYSERR);
604       return;
605     }
606
607   /* schedule next action */
608   h->current = h->action_head;
609   if (NULL == h->current)
610     {
611       if (h->do_destroy)
612         {
613           h->do_destroy = GNUNET_NO;
614           GNUNET_STATISTICS_destroy (h, GNUNET_YES);
615         }
616       return;
617     }
618   GNUNET_CONTAINER_DLL_remove (h->action_head,
619                                h->action_tail,
620                                h->current);
621   timeout = GNUNET_TIME_absolute_get_remaining (h->current->timeout);
622   if (NULL ==
623       (h->th = GNUNET_CLIENT_notify_transmit_ready (h->client,
624                                                     h->current->msize,
625                                                     timeout,
626                                                     GNUNET_YES,
627                                                     &transmit_action, h)))
628     {
629 #if DEBUG_STATISTICS
630       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
631                   "Failed to transmit request to statistics service.\n");
632 #endif
633       finish (h, GNUNET_SYSERR);
634     }
635 }
636
637
638 static void
639 insert_ai (struct GNUNET_STATISTICS_Handle *h, struct GNUNET_STATISTICS_GetHandle *ai)
640 {
641   GNUNET_CONTAINER_DLL_insert_after (h->action_head,
642                                      h->action_tail,
643                                      h->action_tail,
644                                      ai);                                    
645   if (h->action_head == ai)
646     schedule_action (h);
647 }
648
649
650 /**
651  * Get statistic from the peer.
652  *
653  * @param handle identification of the statistics service
654  * @param subsystem limit to the specified subsystem, NULL for our subsystem
655  * @param name name of the statistic value, NULL for all values
656  * @param timeout after how long should we give up (and call
657  *        cont with an error code)?
658  * @param cont continuation to call when done (can be NULL)
659  * @param proc function to call on each value
660  * @param cls closure for cont and proc
661  * @return NULL on error
662  */
663 struct GNUNET_STATISTICS_GetHandle *
664 GNUNET_STATISTICS_get (struct GNUNET_STATISTICS_Handle *handle,
665                        const char *subsystem,
666                        const char *name,
667                        struct GNUNET_TIME_Relative timeout,
668                        GNUNET_STATISTICS_Callback cont,
669                        GNUNET_STATISTICS_Iterator proc, void *cls)
670 {
671   size_t slen1;
672   size_t slen2;
673   struct GNUNET_STATISTICS_GetHandle *ai;
674
675   GNUNET_assert (handle != NULL);
676   GNUNET_assert (proc != NULL);
677   GNUNET_assert (GNUNET_NO == handle->do_destroy);
678   if (GNUNET_YES != try_connect (handle))
679     {
680 #if DEBUG_STATISTICS
681       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
682                   "Failed to connect to statistics service, can not get value `%s:%s'.\n",
683                   strlen (subsystem) ? subsystem : "*",
684                   strlen (name) ? name : "*");
685 #endif
686       return NULL;
687     }
688   if (subsystem == NULL)
689     subsystem = "";
690   if (name == NULL)
691     name = "";
692   slen1 = strlen (subsystem);
693   slen2 = strlen (name);
694   GNUNET_assert (slen1 + slen2 + sizeof (struct GNUNET_MessageHeader) <
695                  GNUNET_SERVER_MAX_MESSAGE_SIZE);
696   ai = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_GetHandle));
697   ai->sh = handle;
698   ai->subsystem = GNUNET_strdup (subsystem);
699   ai->name = GNUNET_strdup (name);
700   ai->cont = cont;
701   ai->proc = proc;
702   ai->cls = cls;
703   ai->timeout = GNUNET_TIME_relative_to_absolute (timeout);
704   ai->type = ACTION_GET;
705   ai->msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
706   insert_ai (handle, ai);
707   return ai;
708 }
709
710
711 /**
712  * Cancel a 'get' request.  Must be called before the 'cont' 
713  * function is called.
714  *
715  * @param gh handle of the request to cancel
716  */
717 void
718 GNUNET_STATISTICS_get_cancel (struct GNUNET_STATISTICS_GetHandle *gh)
719 {
720   if (gh->sh->current == gh)
721     {
722       gh->aborted = GNUNET_YES;
723     }
724   else
725     {
726       GNUNET_CONTAINER_DLL_remove (gh->sh->action_head,
727                                    gh->sh->action_tail,
728                                    gh);
729       GNUNET_free (gh->name);
730       GNUNET_free (gh->subsystem);
731       GNUNET_free (gh);
732     }
733 }
734
735
736 static void
737 add_setter_action (struct GNUNET_STATISTICS_Handle *h,
738                    const char *name,
739                    int make_persistent,
740                    uint64_t value, enum ActionType type)
741 {
742   struct GNUNET_STATISTICS_GetHandle *ai;
743   size_t slen;
744   size_t nlen;
745   size_t nsize;
746
747   GNUNET_assert (h != NULL);
748   GNUNET_assert (name != NULL);
749   if (GNUNET_YES != try_connect (h))
750     return;
751   slen = strlen (h->subsystem) + 1;
752   nlen = strlen (name) + 1;
753   nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
754   if (nsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
755     {
756       GNUNET_break (0);
757       return;
758     }
759   ai = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_GetHandle));
760   ai->sh = h;
761   ai->subsystem = GNUNET_strdup (h->subsystem);
762   ai->name = GNUNET_strdup (name);
763   ai->timeout = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT);
764   ai->make_persistent = make_persistent;
765   ai->msize = nsize;
766   ai->value = value;
767   ai->type = type;
768   insert_ai (h, ai);
769   schedule_action (h);
770 }
771
772
773 /**
774  * Set statistic value for the peer.  Will always use our
775  * subsystem (the argument used when "handle" was created).
776  *
777  * @param handle identification of the statistics service
778  * @param name name of the statistic value
779  * @param value new value to set
780  * @param make_persistent should the value be kept across restarts?
781  */
782 void
783 GNUNET_STATISTICS_set (struct GNUNET_STATISTICS_Handle *handle,
784                        const char *name,
785                        uint64_t value, int make_persistent)
786 {
787   GNUNET_assert (GNUNET_NO == handle->do_destroy);
788   add_setter_action (handle, name, make_persistent, value, ACTION_SET);
789 }
790
791
792 /**
793  * Set statistic value for the peer.  Will always use our
794  * subsystem (the argument used when "handle" was created).
795  *
796  * @param handle identification of the statistics service
797  * @param name name of the statistic value
798  * @param delta change in value (added to existing value)
799  * @param make_persistent should the value be kept across restarts?
800  */
801 void
802 GNUNET_STATISTICS_update (struct GNUNET_STATISTICS_Handle *handle,
803                           const char *name,
804                           int64_t delta, int make_persistent)
805 {
806   GNUNET_assert (GNUNET_NO == handle->do_destroy);
807   add_setter_action (handle, name, make_persistent,
808                      (unsigned long long) delta, ACTION_UPDATE);
809 }
810
811
812 /* end of statistics_api.c */