warning fix
[oweals/gnunet.git] / src / peerstore / peerstore_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 
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 3, 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 peerstore/peerstore_api.c
23  * @brief API for peerstore
24  * @author Omar Tarabai
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "peerstore.h"
29
30 #define LOG(kind,...) GNUNET_log_from (kind, "peerstore-api",__VA_ARGS__)
31
32 /******************************************************************************/
33 /************************      DATA STRUCTURES     ****************************/
34 /******************************************************************************/
35
36 /**
37  * Handle to the PEERSTORE service.
38  */
39 struct GNUNET_PEERSTORE_Handle
40 {
41
42   /**
43    * Our configuration.
44    */
45     const struct GNUNET_CONFIGURATION_Handle *cfg;
46
47   /**
48    * Connection to the service.
49    */
50   struct GNUNET_CLIENT_Connection *client;
51
52   /**
53    * Head of transmission queue.
54    */
55   struct GNUNET_PEERSTORE_RequestContext *rc_head;
56
57   /**
58    * Tail of transmission queue.
59    */
60   struct GNUNET_PEERSTORE_RequestContext *rc_tail;
61
62   /**
63    * Handle for the current transmission request, or NULL if none is pending.
64    */
65   struct GNUNET_CLIENT_TransmitHandle *th;
66
67   /**
68    * Head of store requests DLL.
69    */
70   struct GNUNET_PEERSTORE_StoreContext *sc_head;
71
72   /**
73    * Tail of store requests DLL.
74    */
75   struct GNUNET_PEERSTORE_StoreContext *sc_tail;
76
77   /**
78    * ID for a reconnect task.
79    */
80   GNUNET_SCHEDULER_TaskIdentifier r_task;
81
82   /**
83    * Are we now receiving?
84    */
85   int in_receive;
86
87 };
88
89 /**
90  * Entry in the transmission queue to PEERSTORE service.
91  *
92  */
93 struct GNUNET_PEERSTORE_RequestContext
94 {
95   /**
96    * This is a linked list.
97    */
98   struct GNUNET_PEERSTORE_RequestContext *next;
99
100   /**
101    * This is a linked list.
102    */
103   struct GNUNET_PEERSTORE_RequestContext *prev;
104
105   /**
106    * Handle to the PEERSTORE service.
107    */
108   struct GNUNET_PEERSTORE_Handle *h;
109
110   /**
111    * Function to call after request has been transmitted, or NULL.
112    */
113   GNUNET_PEERSTORE_Continuation cont;
114
115   /**
116    * Closure for 'cont'.
117    */
118   void *cont_cls;
119
120   /**
121    * Number of bytes of the request message (follows after this struct).
122    */
123   size_t size;
124
125 };
126
127 /**
128  * Context for a store request
129  *
130  */
131 struct GNUNET_PEERSTORE_StoreContext
132 {
133   /**
134    * Kept in a DLL.
135    */
136   struct GNUNET_PEERSTORE_StoreContext *next;
137
138   /**
139    * Kept in a DLL.
140    */
141   struct GNUNET_PEERSTORE_StoreContext *prev;
142
143   /**
144    * Handle to the PEERSTORE service.
145    */
146   struct GNUNET_PEERSTORE_Handle *h;
147
148   /**
149    * Our entry in the transmission queue.
150    */
151   struct GNUNET_PEERSTORE_RequestContext *rc;
152
153   /**
154    * Function to call with store operation result
155    */
156   GNUNET_PEERSTORE_Continuation cont;
157
158   /**
159    * Closure for 'cont'.
160    */
161   void *cont_cls;
162
163   /**
164    * Set to GNUNET_YES if we are currently receiving replies from the
165    * service.
166    */
167   int request_transmitted;
168
169 };
170
171 /******************************************************************************/
172 /***********************         DECLARATIONS         *************************/
173 /******************************************************************************/
174
175 /**
176  * Close the existing connection to PEERSTORE and reconnect.
177  *
178  * @param h handle to the service
179  */
180 static void
181 reconnect (struct GNUNET_PEERSTORE_Handle *h);
182
183 /**
184  * Check if we have a request pending in the transmission queue and are
185  * able to transmit it right now.  If so, schedule transmission.
186  *
187  * @param h handle to the service
188  */
189 static void
190 trigger_transmit (struct GNUNET_PEERSTORE_Handle *h);
191
192 /******************************************************************************/
193 /*******************         CONNECTION FUNCTIONS         *********************/
194 /******************************************************************************/
195
196 /**
197  * Task scheduled to re-try connecting to the peerstore service.
198  *
199  * @param cls the 'struct GNUNET_PEERSTORE_Handle'
200  * @param tc scheduler context
201  */
202 static void
203 reconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
204 {
205   struct GNUNET_PEERSTORE_Handle *h = cls;
206
207   LOG(GNUNET_ERROR_TYPE_DEBUG, "Reconnect task executed\n");
208   h->r_task = GNUNET_SCHEDULER_NO_TASK;
209   reconnect (h);
210 }
211
212 /**
213  * Connect to the PEERSTORE service.
214  *
215  * @return NULL on error
216  */
217 struct GNUNET_PEERSTORE_Handle *
218 GNUNET_PEERSTORE_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
219 {
220   struct GNUNET_CLIENT_Connection *client;
221   struct GNUNET_PEERSTORE_Handle *h;
222
223   client = GNUNET_CLIENT_connect ("peerstore", cfg);
224   if(NULL == client)
225     return NULL;
226   h = GNUNET_new (struct GNUNET_PEERSTORE_Handle);
227   h->client = client;
228   h->cfg = cfg;
229   LOG(GNUNET_ERROR_TYPE_DEBUG, "New connection created\n");
230   return h;
231 }
232
233 /**
234  * Disconnect from the PEERSTORE service
235  * Do not call in case of pending requests
236  *
237  * @param h handle to disconnect
238  */
239 void
240 GNUNET_PEERSTORE_disconnect(struct GNUNET_PEERSTORE_Handle *h)
241 {
242   struct GNUNET_PEERSTORE_StoreContext *sc;
243   struct GNUNET_PEERSTORE_RequestContext *rc;
244
245   while (NULL != (sc = h->sc_head))
246   {
247     GNUNET_break (GNUNET_YES == sc->request_transmitted);
248     sc->request_transmitted = GNUNET_NO;
249     GNUNET_PEERSTORE_store_cancel(sc);
250   }
251   while (NULL != (rc = h->rc_head))
252   {
253     GNUNET_CONTAINER_DLL_remove (h->rc_head, h->rc_tail, rc);
254     if (NULL != rc->cont)
255       rc->cont (rc->cont_cls, _("aborted due to explicit disconnect request"));
256     GNUNET_free (rc);
257   }
258   if (NULL != h->th)
259   {
260     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
261     h->th = NULL;
262   }
263   if (NULL != h->client)
264   {
265     GNUNET_CLIENT_disconnect (h->client);
266     h->client = NULL;
267   }
268   if (GNUNET_SCHEDULER_NO_TASK != h->r_task)
269   {
270     GNUNET_SCHEDULER_cancel (h->r_task);
271     h->r_task = GNUNET_SCHEDULER_NO_TASK;
272   }
273   GNUNET_free (h);
274   LOG(GNUNET_ERROR_TYPE_DEBUG, "Disconnected, BYE!\n");
275 }
276
277 /**
278  * Close the existing connection to PEERSTORE and reconnect.
279  *
280  * @param h handle to the service
281  */
282 static void
283 reconnect (struct GNUNET_PEERSTORE_Handle *h)
284 {
285   LOG(GNUNET_ERROR_TYPE_DEBUG, "Reconnecting...\n");
286   if (GNUNET_SCHEDULER_NO_TASK != h->r_task)
287   {
288     GNUNET_SCHEDULER_cancel (h->r_task);
289     h->r_task = GNUNET_SCHEDULER_NO_TASK;
290   }
291   if (NULL != h->th)
292   {
293     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
294     h->th = NULL;
295   }
296   if (NULL != h->client)
297   {
298     GNUNET_CLIENT_disconnect (h->client);
299     h->client = NULL;
300   }
301   h->in_receive = GNUNET_NO;
302   h->client = GNUNET_CLIENT_connect ("peerstore", h->cfg);
303   if (NULL == h->client)
304   {
305     h->r_task =
306         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &reconnect_task,
307                                       h);
308     return;
309   }
310   trigger_transmit (h);
311 }
312
313 /**
314  * Transmit the request at the head of the transmission queue
315  * and trigger continuation (if any).
316  *
317  * @param cls the 'struct GNUNET_PEERSTORE_Handle' (with the queue)
318  * @param size size of the buffer (0 on error)
319  * @param buf where to copy the message
320  * @return number of bytes copied to buf
321  */
322 static size_t
323 do_transmit (void *cls, size_t size, void *buf)
324 {
325   struct GNUNET_PEERSTORE_Handle *h = cls;
326   struct GNUNET_PEERSTORE_RequestContext *rc = h->rc_head;
327   size_t ret;
328
329   h->th = NULL;
330   if (NULL == rc)
331     return 0; /* request was canceled in the meantime */
332   if (NULL == buf)
333   {
334     /* peerstore service died */
335     LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
336          "Failed to transmit message to `%s' service.\n", "PEERSTORE");
337     GNUNET_CONTAINER_DLL_remove (h->rc_head, h->rc_tail, rc);
338     reconnect (h);
339     if (NULL != rc->cont)
340       rc->cont (rc->cont_cls, _("failed to transmit request (service down?)"));
341     GNUNET_free (rc);
342     return 0;
343   }
344   ret = rc->size;
345   if (size < ret)
346   {
347     /* change in head of queue (i.e. cancel + add), try again */
348     trigger_transmit (h);
349     return 0;
350   }
351   LOG (GNUNET_ERROR_TYPE_DEBUG,
352        "Transmitting request of size %u to `%s' service.\n", ret, "PEERSTORE");
353   memcpy (buf, &rc[1], ret);
354   GNUNET_CONTAINER_DLL_remove (h->rc_head, h->rc_tail, rc);
355   trigger_transmit (h);
356   if (NULL != rc->cont)
357     rc->cont (rc->cont_cls, NULL);
358   GNUNET_free (rc);
359   return ret;
360 }
361
362 /**
363  * Check if we have a request pending in the transmission queue and are
364  * able to transmit it right now.  If so, schedule transmission.
365  *
366  * @param h handle to the service
367  */
368 static void
369 trigger_transmit (struct GNUNET_PEERSTORE_Handle *h)
370 {
371   struct GNUNET_PEERSTORE_RequestContext *rc;
372
373   if (NULL == (rc = h->rc_head))
374     return; /* no requests queued */
375   if (NULL != h->th)
376     return; /* request already pending */
377   if (NULL == h->client)
378   {
379     /* disconnected, try to reconnect */
380     reconnect (h);
381     return;
382   }
383   h->th =
384     GNUNET_CLIENT_notify_transmit_ready (h->client, rc->size,
385            GNUNET_TIME_UNIT_FOREVER_REL,
386            GNUNET_YES,
387            &do_transmit, h);
388 }
389
390 /******************************************************************************/
391 /*******************             ADD FUNCTIONS            *********************/
392 /******************************************************************************/
393
394 /**
395  * Cancel a store request
396  *
397  * @param sc Store request context
398  */
399 void
400 GNUNET_PEERSTORE_store_cancel (struct GNUNET_PEERSTORE_StoreContext *sc)
401 {
402   struct GNUNET_PEERSTORE_Handle *h;
403
404   h = sc->h;
405   sc->cont = NULL;
406   if (GNUNET_YES == sc->request_transmitted)
407     return;                     /* need to finish processing */
408   GNUNET_CONTAINER_DLL_remove (h->sc_head,
409              h->sc_tail,
410              sc);
411   if (NULL != sc->rc)
412   {
413     GNUNET_CONTAINER_DLL_remove (h->rc_head, h->rc_tail, sc->rc);
414     GNUNET_free (sc->rc);
415   }
416   GNUNET_free (sc);
417 }
418
419 /**
420  * Function called with server response message
421  * after a store operation is requested
422  *
423  * @param cls a 'struct GNUNET_PEERSTORE_Handle'
424  * @param msg message received, NULL on timeout or fatal error
425  */
426 static void
427 store_receive(void *cls, const struct GNUNET_MessageHeader *msg)
428 {
429   struct GNUNET_PEERSTORE_Handle *h = cls;
430   struct GNUNET_PEERSTORE_StoreContext *sc = h->sc_head;
431   GNUNET_PEERSTORE_Continuation cont;
432   void *cont_cls;
433   uint16_t response_type;
434   uint16_t response_size;
435   struct StoreResponseMessage *srm;
436   int malformed = GNUNET_NO;
437   char *emsg;
438
439   h->in_receive = GNUNET_NO;
440   if (NULL == sc)
441   {
442     /* didn't expect a response, reconnect */
443     reconnect (h);
444     return;
445   }
446   cont = sc->cont;
447   cont_cls = sc->cont_cls;
448   sc->request_transmitted = GNUNET_NO;
449   //cancel the request since we only need one response
450   GNUNET_PEERSTORE_store_cancel(sc);
451   if(NULL == msg)
452   {
453     LOG(GNUNET_ERROR_TYPE_ERROR, "`PEERSTORE' service died\n");
454     reconnect (h);
455     if (NULL != cont)
456       cont (cont_cls,
457           _("Failed to receive response from `PEERSTORE' service."));
458     return;
459   }
460   response_type = ntohs(msg->type);
461   response_size = ntohs(msg->size);
462   if(GNUNET_MESSAGE_TYPE_PEERSTORE_STORE_RESULT != response_type)
463   {
464     LOG(GNUNET_ERROR_TYPE_ERROR, "Received an unexpected response type: %lu to store request\n", response_type);
465     reconnect(h);
466     if (NULL != cont)
467         cont (cont_cls,
468             _("Received an unexpected response from `PEERSTORE' service."));
469     return;
470   }
471   if(response_size < sizeof(struct StoreResponseMessage))
472   {
473     malformed = GNUNET_YES;
474   }
475   else
476   {
477     srm = (struct StoreResponseMessage *)msg;
478     if(sizeof(struct StoreResponseMessage) + ntohs(srm->emsg_size) != response_size)
479       malformed = GNUNET_YES;
480   }
481   if(GNUNET_YES == malformed)
482   {
483     LOG(GNUNET_ERROR_TYPE_ERROR, "Received a malformed response from `PEERSTORE' service.\n");
484     reconnect(h);
485     if (NULL != cont)
486         cont (cont_cls,
487             _("Received a malformed response from `PEERSTORE' service."));
488     return;
489   }
490   LOG(GNUNET_ERROR_TYPE_DEBUG, "Received a response of type %lu from server\n", response_type);
491   trigger_transmit(h);
492   if ( (GNUNET_NO == h->in_receive) && (NULL != h->sc_head) )
493   {
494     LOG(GNUNET_ERROR_TYPE_DEBUG,
495         "A store request was sent but response not received, receiving now.\n");
496     h->in_receive = GNUNET_YES;
497     GNUNET_CLIENT_receive (h->client,
498         &store_receive,
499         h,
500         GNUNET_TIME_UNIT_FOREVER_REL);
501   }
502   if(NULL != cont)
503   {
504     LOG(GNUNET_ERROR_TYPE_DEBUG, "Calling continuation of store request\n");
505     srm = (struct StoreResponseMessage *)msg;
506     emsg = NULL;
507     if(GNUNET_NO == ntohs(srm->success))
508     {
509       emsg = GNUNET_malloc(ntohs(srm->emsg_size));
510       memcpy(emsg, &srm[1], ntohs(srm->emsg_size));
511     }
512     cont(cont_cls, emsg);
513   }
514 }
515
516 /**
517  * Called after store request is sent
518  * Waits for response from service
519  *
520  * @param cls a 'struct GNUNET_PEERSTORE_StoreContext'
521  * @parma emsg error message (or NULL)
522  */
523 void store_trigger_receive(void *cls, const char *emsg)
524 {
525   struct GNUNET_PEERSTORE_StoreContext *sc = cls;
526   struct GNUNET_PEERSTORE_Handle *h = sc->h;
527   GNUNET_PEERSTORE_Continuation cont;
528   void *cont_cls;
529
530   sc->rc = NULL;
531   if(NULL != emsg)
532   {
533     cont = sc->cont;
534     cont_cls = sc->cont_cls;
535     GNUNET_PEERSTORE_store_cancel (sc);
536     reconnect (h);
537     if (NULL != cont)
538       cont (cont_cls, emsg);
539     return;
540   }
541   LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for response from `%s' service.\n",
542          "PEERSTORE");
543   sc->request_transmitted = GNUNET_YES;
544   if (GNUNET_NO == h->in_receive)
545   {
546     h->in_receive = GNUNET_YES;
547     GNUNET_CLIENT_receive (h->client,
548         &store_receive,
549         h,
550         GNUNET_TIME_UNIT_FOREVER_REL);
551   }
552 }
553
554 /**
555  * Store a new entry in the PEERSTORE
556  *
557  * @param h Handle to the PEERSTORE service
558  * @param peer Peer Identity
559  * @param sub_system name of the sub system
560  * @param value entry value BLOB
561  * @param size size of 'value'
562  * @param lifetime relative time after which the entry is (possibly) deleted
563  * @param cont Continuation function after the store request is processed
564  * @param cont_cls Closure for 'cont'
565  */
566 struct GNUNET_PEERSTORE_StoreContext *
567 GNUNET_PEERSTORE_store (struct GNUNET_PEERSTORE_Handle *h,
568     const struct GNUNET_PeerIdentity *peer,
569     const char *sub_system,
570     const void *value,
571     size_t size,
572     struct GNUNET_TIME_Relative lifetime,
573     GNUNET_PEERSTORE_Continuation cont,
574     void *cont_cls)
575 {
576   struct GNUNET_PEERSTORE_RequestContext *rc;
577   struct StoreRequestMessage *entry;
578   struct GNUNET_PEERSTORE_StoreContext *sc;
579   char *ss;
580   void *val;
581   size_t sub_system_size;
582   size_t request_size;
583
584   LOG (GNUNET_ERROR_TYPE_DEBUG,
585       "Storing value (size: %lu) for subsytem `%s' and peer `%s'\n",
586       size, sub_system, GNUNET_i2s (peer));
587   sub_system_size = strlen(sub_system) + 1;
588   request_size = sizeof(struct StoreRequestMessage) + sub_system_size + size;
589   rc = GNUNET_malloc(sizeof(struct GNUNET_PEERSTORE_RequestContext) + request_size);
590   rc->h = h;
591   rc->size = request_size;
592   entry = (struct StoreRequestMessage *)&rc[1];
593   entry->header.size = htons(request_size);
594   entry->header.type = htons(GNUNET_MESSAGE_TYPE_PEERSTORE_STORE);
595   entry->peer = *peer;
596   entry->sub_system_size = htons(sub_system_size);
597   entry->value_size = htons(size);
598   entry->lifetime = lifetime;
599   ss = (char *)&entry[1];
600   memcpy(ss, sub_system, sub_system_size);
601   val = ss + sub_system_size;
602   memcpy(val, value, size);
603   sc = GNUNET_new(struct GNUNET_PEERSTORE_StoreContext);
604   sc->cont = cont;
605   sc->cont_cls = cont_cls;
606   sc->h = h;
607   sc->rc = rc;
608   sc->request_transmitted = GNUNET_NO;
609   rc->cont = &store_trigger_receive;
610   rc->cont_cls = sc;
611   GNUNET_CONTAINER_DLL_insert_tail(h->rc_head, h->rc_tail, rc);
612   GNUNET_CONTAINER_DLL_insert_tail(h->sc_head, h->sc_tail, sc);
613   trigger_transmit (h);
614   return sc;
615
616 }
617
618
619 /* end of peerstore_api.c */