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