make it easy to start service by hand
[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_YES;
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 != NULL)
239     {
240       if (pos->cont != NULL)
241         pos->cont (pos->cls, code);
242       free_action_item (pos);
243     }
244 }
245
246
247 /**
248  * Process the message.
249  *
250  * @return GNUNET_OK if the message was well-formed
251  */
252 static int
253 process_message (struct GNUNET_STATISTICS_Handle *h,
254                  const struct GNUNET_MessageHeader *msg)
255 {
256   char *service;
257   char *name;
258   const struct GNUNET_STATISTICS_ReplyMessage *smsg;
259   uint16_t size;
260
261   if (h->current->aborted)
262     return GNUNET_OK;           /* don't bother */
263   size = ntohs (msg->size);
264   if (size < sizeof (struct GNUNET_STATISTICS_ReplyMessage))
265     {
266       GNUNET_break (0);
267       return GNUNET_SYSERR;
268     }
269   smsg = (const struct GNUNET_STATISTICS_ReplyMessage *) msg;
270   size -= sizeof (struct GNUNET_STATISTICS_ReplyMessage);
271   if (size != GNUNET_STRINGS_buffer_tokenize ((const char *) &smsg[1],
272                                               size, 2, &service, &name))
273     {
274       GNUNET_break (0);
275       return GNUNET_SYSERR;
276     }
277 #if DEBUG_STATISTICS
278   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
279               "Received valid statistic on `%s:%s': %llu\n",
280               service, name, GNUNET_ntohll (smsg->value));
281 #endif
282   if (GNUNET_OK !=
283       h->current->proc (h->current->cls,
284                         service,
285                         name,
286                         GNUNET_ntohll (smsg->value),
287                         0 !=
288                         (ntohl (smsg->uid) & GNUNET_STATISTICS_PERSIST_BIT)))
289     {
290 #if DEBUG_STATISTICS
291       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
292                   "Processing of remaining statistics aborted by client.\n");
293 #endif
294       h->current->aborted = GNUNET_YES;    
295     }
296   return GNUNET_OK;
297 }
298
299
300 /**
301  * Function called with messages from stats service.
302  *
303  * @param cls closure
304  * @param msg message received, NULL on timeout or fatal error
305  */
306 static void
307 receive_stats (void *cls, const struct GNUNET_MessageHeader *msg)
308 {
309   struct GNUNET_STATISTICS_Handle *h = cls;
310
311   if (msg == NULL)
312     {
313       if (NULL != h->client)
314         {
315           GNUNET_CLIENT_disconnect (h->client, GNUNET_NO);
316           h->client = NULL;
317         }
318 #if DEBUG_STATISTICS
319       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
320                   "Error receiving statistics from service, is the service running?\n" );
321 #endif
322       finish (h, GNUNET_SYSERR);
323       return;
324     }
325   switch (ntohs (msg->type))
326     {
327     case GNUNET_MESSAGE_TYPE_STATISTICS_END:
328 #if DEBUG_STATISTICS
329       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
330                   "Received end of statistics marker\n");
331 #endif
332       finish (h, GNUNET_OK);
333       return;
334     case GNUNET_MESSAGE_TYPE_STATISTICS_VALUE:
335       if (GNUNET_OK == process_message (h, msg))
336         {
337           /* finally, look for more! */
338           GNUNET_CLIENT_receive (h->client,
339                                  &receive_stats,
340                                  h,
341                                  GNUNET_TIME_absolute_get_remaining
342                                  (h->current->timeout));
343           return;
344         }
345       GNUNET_break (0);
346       break;
347     default:
348       GNUNET_break (0);
349       break;
350     }
351   if (NULL != h->client)
352     {
353       GNUNET_CLIENT_disconnect (h->client, GNUNET_NO);
354       h->client = NULL;
355     }
356   finish (h, GNUNET_SYSERR);
357 }
358
359
360 /**
361  * Transmit a GET request (and if successful, start to receive
362  * the response).
363  */
364 static size_t
365 transmit_get (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf)
366 {
367   struct GNUNET_MessageHeader *hdr;
368   size_t slen1;
369   size_t slen2;
370   uint16_t msize;
371
372   if (buf == NULL)
373     {
374       /* timeout / error */
375 #if DEBUG_STATISTICS
376       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
377                   "Transmission of request for statistics failed!\n");
378 #endif
379       finish (handle, GNUNET_SYSERR);
380       return 0;
381     }
382   slen1 = strlen (handle->current->subsystem) + 1;
383   slen2 = strlen (handle->current->name) + 1;
384   msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
385   GNUNET_assert (msize <= size);
386   hdr = (struct GNUNET_MessageHeader *) buf;
387   hdr->size = htons (msize);
388   hdr->type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_GET);
389   GNUNET_assert (slen1 + slen2 ==
390                  GNUNET_STRINGS_buffer_fill ((char *) &hdr[1],
391                                              slen1 + slen2,
392                                              2,
393                                              handle->current->subsystem,
394                                              handle->current->name));
395   GNUNET_CLIENT_receive (handle->client,
396                          &receive_stats,
397                          handle,
398                          GNUNET_TIME_absolute_get_remaining (handle->
399                                                              current->timeout));
400   return msize;
401 }
402
403
404
405 /**
406  * Transmit a SET/UPDATE request.
407  */
408 static size_t
409 transmit_set (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf)
410 {
411   struct GNUNET_STATISTICS_SetMessage *r;
412   size_t slen;
413   size_t nlen;
414   size_t nsize;
415
416   if (NULL == buf)
417     {
418       finish (handle, GNUNET_SYSERR);
419       return 0;
420     }
421
422   slen = strlen (handle->current->subsystem) + 1;
423   nlen = strlen (handle->current->name) + 1;
424   nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
425   if (size < nsize)
426     {
427       GNUNET_break (0);
428       finish (handle, GNUNET_SYSERR);
429       return 0;
430     }
431   r = buf;
432   r->header.size = htons (nsize);
433   r->header.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_SET);
434   r->flags = 0;
435   r->value = GNUNET_htonll (handle->current->value);
436   if (handle->current->make_persistent)
437     r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_PERSISTENT);
438   if (handle->current->type == ACTION_UPDATE)
439     r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_RELATIVE);
440   GNUNET_assert (slen + nlen ==
441                  GNUNET_STRINGS_buffer_fill ((char *) &r[1],
442                                              slen + nlen,
443                                              2,
444                                              handle->current->subsystem,
445                                              handle->current->name));
446   finish (handle, GNUNET_OK);
447   return nsize;
448 }
449
450
451 static size_t
452 transmit_action (void *cls, size_t size, void *buf)
453 {
454   struct GNUNET_STATISTICS_Handle *handle = cls;
455   size_t ret;
456
457   handle->th = NULL;
458   switch (handle->current->type)
459     {
460     case ACTION_GET:
461       ret = transmit_get (handle, size, buf);
462       break;
463     case ACTION_SET:
464     case ACTION_UPDATE:
465       ret = transmit_set (handle, size, buf);
466       break;
467     default:
468       ret = 0;
469       GNUNET_break (0);
470       break; 
471     }
472   return ret;
473 }
474
475
476 /**
477  * Get handle for the statistics service.
478  *
479  * @param sched scheduler to use
480  * @param subsystem name of subsystem using the service
481  * @param cfg services configuration in use
482  * @return handle to use
483  */
484 struct GNUNET_STATISTICS_Handle *
485 GNUNET_STATISTICS_create (struct GNUNET_SCHEDULER_Handle *sched,
486                           const char *subsystem,
487                           const struct GNUNET_CONFIGURATION_Handle *cfg)
488 {
489   struct GNUNET_STATISTICS_Handle *ret;
490
491   GNUNET_assert (subsystem != NULL);
492   GNUNET_assert (sched != NULL);
493   GNUNET_assert (cfg != NULL);
494   ret = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_Handle));
495   ret->sched = sched;
496   ret->cfg = cfg;
497   ret->subsystem = GNUNET_strdup (subsystem);
498   try_connect (ret);
499   return ret;
500 }
501
502
503 /**
504  * Destroy a handle (free all state associated with
505  * it).
506  *
507  * @param h statistics handle to destroy
508  * @param sync_first set to GNUNET_YES if pending SET requests should
509  *        be completed
510  */
511 void
512 GNUNET_STATISTICS_destroy (struct GNUNET_STATISTICS_Handle *h,
513                            int sync_first)
514 {
515   struct GNUNET_STATISTICS_GetHandle *pos;
516   struct GNUNET_STATISTICS_GetHandle *next;
517   struct GNUNET_STATISTICS_GetHandle *prev;
518   struct GNUNET_TIME_Relative timeout;
519
520   if (sync_first)
521     {
522       if (h->current != NULL)
523         {
524           if (h->current->type == ACTION_GET)
525             {
526               GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
527               h->th = NULL;
528               free_action_item (h->current);
529               h->current = NULL;
530             }
531         }
532       pos = h->action_head;
533       prev = NULL;
534       while (pos != NULL)
535         {
536           next = pos->next;
537           if (pos->type == ACTION_GET)
538             {
539               if (prev == NULL)
540                 h->action_head = next;
541               else
542                 prev->next = next;
543               free_action_item (pos);
544             }
545           else
546             {
547               prev = pos;
548             }
549           pos = next;
550         }
551       h->action_tail = prev;
552       if (h->current == NULL)
553         {
554           h->current = h->action_head;
555           if (h->action_head != NULL)
556             {
557               h->action_head = h->action_head->next;
558               if (h->action_head == NULL)
559                 h->action_tail = NULL;
560             }
561         }
562       h->do_destroy = GNUNET_YES;
563       if ( (h->current != NULL) &&
564            (h->th == NULL) )
565         {                                       
566           timeout = GNUNET_TIME_absolute_get_remaining (h->current->timeout);
567           h->th = GNUNET_CLIENT_notify_transmit_ready (h->client,
568                                                        h->current->msize,
569                                                        timeout,
570                                                        GNUNET_YES,
571                                                        &transmit_action, h);
572           GNUNET_assert (NULL != h->th);
573         }
574       if (h->th != NULL)
575         return;
576     }
577   if (NULL != h->th)
578     {
579       GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
580       h->th = NULL;
581     }
582   if (h->current != NULL)
583     free_action_item (h->current);
584   while (NULL != (pos = h->action_head))
585     {
586       h->action_head = pos->next;
587       free_action_item (pos);
588     }
589   if (h->client != NULL)
590     {
591       GNUNET_CLIENT_disconnect (h->client, GNUNET_YES);
592       h->client = NULL;
593     }
594   GNUNET_free (h->subsystem);
595   GNUNET_free (h);
596 }
597
598
599
600 /**
601  * Schedule the next action to be performed.
602  */
603 static void
604 schedule_action (struct GNUNET_STATISTICS_Handle *h)
605 {
606   struct GNUNET_TIME_Relative timeout;
607
608   if (h->current != NULL)
609     return;                     /* action already pending */
610   if (GNUNET_YES != try_connect (h))
611     {
612       finish (h, GNUNET_SYSERR);
613       return;
614     }
615
616   /* schedule next action */
617   h->current = h->action_head;
618   if (NULL == h->current)
619     {
620       if (h->do_destroy)
621         {
622           h->do_destroy = GNUNET_NO;
623           GNUNET_STATISTICS_destroy (h, GNUNET_YES);
624         }
625       return;
626     }
627   GNUNET_CONTAINER_DLL_remove (h->action_head,
628                                h->action_tail,
629                                h->current);
630   timeout = GNUNET_TIME_absolute_get_remaining (h->current->timeout);
631   if (NULL ==
632       (h->th = GNUNET_CLIENT_notify_transmit_ready (h->client,
633                                                     h->current->msize,
634                                                     timeout,
635                                                     GNUNET_YES,
636                                                     &transmit_action, h)))
637     {
638 #if DEBUG_STATISTICS
639       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
640                   "Failed to transmit request to statistics service.\n");
641 #endif
642       finish (h, GNUNET_SYSERR);
643     }
644 }
645
646
647 static void
648 insert_ai (struct GNUNET_STATISTICS_Handle *h, struct GNUNET_STATISTICS_GetHandle *ai)
649 {
650   GNUNET_CONTAINER_DLL_insert_after (h->action_head,
651                                      h->action_tail,
652                                      h->action_tail,
653                                      ai);                                    
654   if (h->action_head == ai)
655     schedule_action (h);
656 }
657
658
659 /**
660  * Get statistic from the peer.
661  *
662  * @param handle identification of the statistics service
663  * @param subsystem limit to the specified subsystem, NULL for our subsystem
664  * @param name name of the statistic value, NULL for all values
665  * @param timeout after how long should we give up (and call
666  *        cont with an error code)?
667  * @param cont continuation to call when done (can be NULL)
668  * @param proc function to call on each value
669  * @param cls closure for cont and proc
670  * @return NULL on error
671  */
672 struct GNUNET_STATISTICS_GetHandle *
673 GNUNET_STATISTICS_get (struct GNUNET_STATISTICS_Handle *handle,
674                        const char *subsystem,
675                        const char *name,
676                        struct GNUNET_TIME_Relative timeout,
677                        GNUNET_STATISTICS_Callback cont,
678                        GNUNET_STATISTICS_Iterator proc, void *cls)
679 {
680   size_t slen1;
681   size_t slen2;
682   struct GNUNET_STATISTICS_GetHandle *ai;
683
684   GNUNET_assert (handle != NULL);
685   GNUNET_assert (proc != NULL);
686   GNUNET_assert (GNUNET_NO == handle->do_destroy);
687   if (GNUNET_YES != try_connect (handle))
688     {
689 #if DEBUG_STATISTICS
690       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
691                   "Failed to connect to statistics service, can not get value `%s:%s'.\n",
692                   strlen (subsystem) ? subsystem : "*",
693                   strlen (name) ? name : "*");
694 #endif
695       return NULL;
696     }
697   if (subsystem == NULL)
698     subsystem = "";
699   if (name == NULL)
700     name = "";
701   slen1 = strlen (subsystem);
702   slen2 = strlen (name);
703   GNUNET_assert (slen1 + slen2 + sizeof (struct GNUNET_MessageHeader) <
704                  GNUNET_SERVER_MAX_MESSAGE_SIZE);
705   ai = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_GetHandle));
706   ai->sh = handle;
707   ai->subsystem = GNUNET_strdup (subsystem);
708   ai->name = GNUNET_strdup (name);
709   ai->cont = cont;
710   ai->proc = proc;
711   ai->cls = cls;
712   ai->timeout = GNUNET_TIME_relative_to_absolute (timeout);
713   ai->type = ACTION_GET;
714   ai->msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
715   insert_ai (handle, ai);
716   return ai;
717 }
718
719
720 /**
721  * Cancel a 'get' request.  Must be called before the 'cont' 
722  * function is called.
723  *
724  * @param gh handle of the request to cancel
725  */
726 void
727 GNUNET_STATISTICS_get_cancel (struct GNUNET_STATISTICS_GetHandle *gh)
728 {
729   if (gh->sh->current == gh)
730     {
731       gh->aborted = GNUNET_YES;
732     }
733   else
734     {
735       GNUNET_CONTAINER_DLL_remove (gh->sh->action_head,
736                                    gh->sh->action_tail,
737                                    gh);
738       GNUNET_free (gh->name);
739       GNUNET_free (gh->subsystem);
740       GNUNET_free (gh);
741     }
742 }
743
744
745 static void
746 add_setter_action (struct GNUNET_STATISTICS_Handle *h,
747                    const char *name,
748                    int make_persistent,
749                    uint64_t value, enum ActionType type)
750 {
751   struct GNUNET_STATISTICS_GetHandle *ai;
752   size_t slen;
753   size_t nlen;
754   size_t nsize;
755
756   GNUNET_assert (h != NULL);
757   GNUNET_assert (name != NULL);
758   if (GNUNET_YES != try_connect (h))
759     return;
760   slen = strlen (h->subsystem) + 1;
761   nlen = strlen (name) + 1;
762   nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
763   if (nsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
764     {
765       GNUNET_break (0);
766       return;
767     }
768   ai = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_GetHandle));
769   ai->sh = h;
770   ai->subsystem = GNUNET_strdup (h->subsystem);
771   ai->name = GNUNET_strdup (name);
772   ai->timeout = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT);
773   ai->make_persistent = make_persistent;
774   ai->msize = nsize;
775   ai->value = value;
776   ai->type = type;
777   insert_ai (h, ai);
778   schedule_action (h);
779 }
780
781
782 /**
783  * Set statistic value for the peer.  Will always use our
784  * subsystem (the argument used when "handle" was created).
785  *
786  * @param handle identification of the statistics service
787  * @param name name of the statistic value
788  * @param value new value to set
789  * @param make_persistent should the value be kept across restarts?
790  */
791 void
792 GNUNET_STATISTICS_set (struct GNUNET_STATISTICS_Handle *handle,
793                        const char *name,
794                        uint64_t value, int make_persistent)
795 {
796   if (handle == NULL)
797     return;
798   GNUNET_assert (GNUNET_NO == handle->do_destroy);
799   add_setter_action (handle, name, make_persistent, value, ACTION_SET);
800 }
801
802
803 /**
804  * Set statistic value for the peer.  Will always use our
805  * subsystem (the argument used when "handle" was created).
806  *
807  * @param handle identification of the statistics service
808  * @param name name of the statistic value
809  * @param delta change in value (added to existing value)
810  * @param make_persistent should the value be kept across restarts?
811  */
812 void
813 GNUNET_STATISTICS_update (struct GNUNET_STATISTICS_Handle *handle,
814                           const char *name,
815                           int64_t delta, int make_persistent)
816 {
817   if (handle == NULL)
818     return;
819   if (delta == 0)
820     return;
821   GNUNET_assert (GNUNET_NO == handle->do_destroy);
822   add_setter_action (handle, name, make_persistent,
823                      (uint64_t) delta, ACTION_UPDATE);
824 }
825
826
827 /* end of statistics_api.c */