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