959d2488a733379fa8c3c9459e8cd3241465c83f
[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  *
236  * @param h handle to disconnect
237  */
238 void
239 GNUNET_PEERSTORE_disconnect(struct GNUNET_PEERSTORE_Handle *h)
240 {
241   struct GNUNET_PEERSTORE_StoreContext *sc;
242   struct GNUNET_PEERSTORE_RequestContext *rc;
243
244   while (NULL != (sc = h->sc_head))
245   {
246     GNUNET_break (GNUNET_YES == sc->request_transmitted);
247     sc->request_transmitted = GNUNET_NO;
248     GNUNET_PEERSTORE_store_cancel(sc);
249   }
250   while (NULL != (rc = h->rc_head))
251   {
252     GNUNET_CONTAINER_DLL_remove (h->rc_head, h->rc_tail, rc);
253     if (NULL != rc->cont)
254       rc->cont (rc->cont_cls, _("aborted due to explicit disconnect request"));
255     GNUNET_free (rc);
256   }
257   if (NULL != h->th)
258   {
259     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
260     h->th = NULL;
261   }
262   if (NULL != h->client)
263   {
264     GNUNET_CLIENT_disconnect (h->client);
265     h->client = NULL;
266   }
267   if (GNUNET_SCHEDULER_NO_TASK != h->r_task)
268   {
269     GNUNET_SCHEDULER_cancel (h->r_task);
270     h->r_task = GNUNET_SCHEDULER_NO_TASK;
271   }
272   GNUNET_free (h);
273   LOG(GNUNET_ERROR_TYPE_DEBUG, "Disconnected, BYE!\n");
274 }
275
276 /**
277  * Close the existing connection to PEERSTORE and reconnect.
278  *
279  * @param h handle to the service
280  */
281 static void
282 reconnect (struct GNUNET_PEERSTORE_Handle *h)
283 {
284   LOG(GNUNET_ERROR_TYPE_DEBUG, "Reconnecting...\n");
285   if (GNUNET_SCHEDULER_NO_TASK != h->r_task)
286   {
287     GNUNET_SCHEDULER_cancel (h->r_task);
288     h->r_task = GNUNET_SCHEDULER_NO_TASK;
289   }
290   if (NULL != h->th)
291   {
292     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
293     h->th = NULL;
294   }
295   if (NULL != h->client)
296   {
297     GNUNET_CLIENT_disconnect (h->client);
298     h->client = NULL;
299   }
300   h->in_receive = GNUNET_NO;
301   h->client = GNUNET_CLIENT_connect ("peerstore", h->cfg);
302   if (NULL == h->client)
303   {
304     h->r_task =
305         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &reconnect_task,
306                                       h);
307     return;
308   }
309   trigger_transmit (h);
310 }
311
312 /**
313  * Transmit the request at the head of the transmission queue
314  * and trigger continuation (if any).
315  *
316  * @param cls the 'struct GNUNET_PEERSTORE_Handle' (with the queue)
317  * @param size size of the buffer (0 on error)
318  * @param buf where to copy the message
319  * @return number of bytes copied to buf
320  */
321 static size_t
322 do_transmit (void *cls, size_t size, void *buf)
323 {
324   struct GNUNET_PEERSTORE_Handle *h = cls;
325   struct GNUNET_PEERSTORE_RequestContext *rc = h->rc_head;
326   size_t ret;
327
328   h->th = NULL;
329   if (NULL == rc)
330     return 0; /* request was canceled in the meantime */
331   if (NULL == buf)
332   {
333     /* peerstore service died */
334     LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
335          "Failed to transmit message to `%s' service.\n", "PEERSTORE");
336     GNUNET_CONTAINER_DLL_remove (h->rc_head, h->rc_tail, rc);
337     reconnect (h);
338     if (NULL != rc->cont)
339       rc->cont (rc->cont_cls, _("failed to transmit request (service down?)"));
340     GNUNET_free (rc);
341     return 0;
342   }
343   ret = rc->size;
344   if (size < ret)
345   {
346     /* change in head of queue (i.e. cancel + add), try again */
347     trigger_transmit (h);
348     return 0;
349   }
350   LOG (GNUNET_ERROR_TYPE_DEBUG,
351        "Transmitting request of size %u to `%s' service.\n", ret, "PEERSTORE");
352   memcpy (buf, &rc[1], ret);
353   GNUNET_CONTAINER_DLL_remove (h->rc_head, h->rc_tail, rc);
354   trigger_transmit (h);
355   if (NULL != rc->cont)
356     rc->cont (rc->cont_cls, NULL);
357   GNUNET_free (rc);
358   return ret;
359 }
360
361 /**
362  * Check if we have a request pending in the transmission queue and are
363  * able to transmit it right now.  If so, schedule transmission.
364  *
365  * @param h handle to the service
366  */
367 static void
368 trigger_transmit (struct GNUNET_PEERSTORE_Handle *h)
369 {
370   struct GNUNET_PEERSTORE_RequestContext *rc;
371
372   if (NULL == (rc = h->rc_head))
373     return; /* no requests queued */
374   if (NULL != h->th)
375     return; /* request already pending */
376   if (NULL == h->client)
377   {
378     /* disconnected, try to reconnect */
379     reconnect (h);
380     return;
381   }
382   h->th =
383     GNUNET_CLIENT_notify_transmit_ready (h->client, rc->size,
384            GNUNET_TIME_UNIT_FOREVER_REL,
385            GNUNET_YES,
386            &do_transmit, h);
387 }
388
389 /******************************************************************************/
390 /*******************             ADD FUNCTIONS            *********************/
391 /******************************************************************************/
392
393 /**
394  * Cancel a store request
395  *
396  * @param sc Store request context
397  */
398 void
399 GNUNET_PEERSTORE_store_cancel (struct GNUNET_PEERSTORE_StoreContext *sc)
400 {
401   struct GNUNET_PEERSTORE_Handle *h;
402
403   h = sc->h;
404   sc->cont = NULL;
405   if (GNUNET_YES == sc->request_transmitted)
406     return;                     /* need to finish processing */
407   GNUNET_CONTAINER_DLL_remove (h->sc_head,
408              h->sc_tail,
409              sc);
410   if (NULL != sc->rc)
411   {
412     GNUNET_CONTAINER_DLL_remove (h->rc_head, h->rc_tail, sc->rc);
413     GNUNET_free (sc->rc);
414   }
415   GNUNET_free (sc);
416 }
417
418 /**
419  * Function called with server response message
420  * after a store operation is requested
421  *
422  * @param cls a 'struct GNUNET_PEERSTORE_Handle'
423  * @param msg message received, NULL on timeout or fatal error
424  */
425 static void
426 store_receive(void *cls, const struct GNUNET_MessageHeader *msg)
427 {
428   struct GNUNET_PEERSTORE_Handle *h = cls;
429   struct GNUNET_PEERSTORE_StoreContext *sc = h->sc_head;
430   GNUNET_PEERSTORE_Continuation cont;
431   void *cont_cls;
432   uint16_t response_type;
433   uint16_t response_size;
434   struct StoreResponseMessage *srm;
435   int malformed = GNUNET_NO;
436   char *emsg;
437
438   h->in_receive = GNUNET_NO;
439   if (NULL == sc)
440   {
441     /* didn't expect a response, reconnect */
442     reconnect (h);
443     return;
444   }
445   cont = sc->cont;
446   cont_cls = sc->cont_cls;
447   sc->request_transmitted = GNUNET_NO;
448   //cancel the request since we only need one response
449   GNUNET_PEERSTORE_store_cancel(sc);
450   if(NULL == msg)
451   {
452     LOG(GNUNET_ERROR_TYPE_ERROR, "`PEERSTORE' service died\n");
453     reconnect (h);
454     if (NULL != cont)
455       cont (cont_cls,
456           _("Failed to receive response from `PEERSTORE' service."));
457     return;
458   }
459   response_type = ntohs(msg->type);
460   response_size = ntohs(msg->size);
461   if(GNUNET_MESSAGE_TYPE_PEERSTORE_STORE_RESULT != response_type)
462   {
463     LOG(GNUNET_ERROR_TYPE_ERROR, "Received an unexpected response type: %lu to store request\n", response_type);
464     reconnect(h);
465     if (NULL != cont)
466         cont (cont_cls,
467             _("Received an unexpected response from `PEERSTORE' service."));
468     return;
469   }
470   if(response_size < sizeof(struct StoreResponseMessage))
471   {
472     malformed = GNUNET_YES;
473   }
474   else
475   {
476     srm = (struct StoreResponseMessage *)msg;
477     if(sizeof(struct StoreResponseMessage) + ntohs(srm->emsg_size) != response_size)
478       malformed = GNUNET_YES;
479   }
480   if(GNUNET_YES == malformed)
481   {
482     LOG(GNUNET_ERROR_TYPE_ERROR, "Received a malformed response from `PEERSTORE' service.\n");
483     reconnect(h);
484     if (NULL != cont)
485         cont (cont_cls,
486             _("Received a malformed response from `PEERSTORE' service."));
487     return;
488   }
489   LOG(GNUNET_ERROR_TYPE_DEBUG, "Received a response of type %lu from server\n", response_type);
490   trigger_transmit(h);
491   if ( (GNUNET_NO == h->in_receive) && (NULL != h->sc_head) )
492   {
493     LOG(GNUNET_ERROR_TYPE_DEBUG,
494         "A store request was sent but response not received, receiving now.\n");
495     h->in_receive = GNUNET_YES;
496     GNUNET_CLIENT_receive (h->client,
497         &store_receive,
498         h,
499         GNUNET_TIME_UNIT_FOREVER_REL);
500   }
501   if(NULL != cont)
502   {
503     LOG(GNUNET_ERROR_TYPE_DEBUG, "Calling continuation of store request\n");
504     srm = (struct StoreResponseMessage *)msg;
505     emsg = NULL;
506     if(GNUNET_NO == ntohs(srm->success))
507     {
508       emsg = GNUNET_malloc(ntohs(srm->emsg_size));
509       memcpy(emsg, &srm[1], ntohs(srm->emsg_size));
510     }
511     cont(cont_cls, emsg);
512   }
513 }
514
515 /**
516  * Called after store request is sent
517  * Waits for response from service
518  *
519  * @param cls a 'struct GNUNET_PEERSTORE_StoreContext'
520  * @parma emsg error message (or NULL)
521  */
522 void store_trigger_receive(void *cls, const char *emsg)
523 {
524   struct GNUNET_PEERSTORE_StoreContext *sc = cls;
525   struct GNUNET_PEERSTORE_Handle *h = sc->h;
526   GNUNET_PEERSTORE_Continuation cont;
527   void *cont_cls;
528
529   sc->rc = NULL;
530   if(NULL != emsg)
531   {
532     cont = sc->cont;
533     cont_cls = sc->cont_cls;
534     GNUNET_PEERSTORE_store_cancel (sc);
535     reconnect (h);
536     if (NULL != sc->cont)
537       sc->cont (sc->cont_cls, emsg);
538     return;
539   }
540   LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for response from `%s' service.\n",
541          "PEERSTORE");
542   sc->request_transmitted = GNUNET_YES;
543   if (GNUNET_NO == h->in_receive)
544   {
545     h->in_receive = GNUNET_YES;
546     GNUNET_CLIENT_receive (h->client,
547         &store_receive,
548         h,
549         GNUNET_TIME_UNIT_FOREVER_REL);
550   }
551 }
552
553 /**
554  * Store a new entry in the PEERSTORE
555  *
556  * @param h Handle to the PEERSTORE service
557  * @param peer Peer Identity
558  * @param sub_system name of the sub system
559  * @param value entry value BLOB
560  * @param size size of 'value'
561  * @param lifetime relative time after which the entry is (possibly) deleted
562  * @param cont Continuation function after the store request is processed
563  * @param cont_cls Closure for 'cont'
564  */
565 struct GNUNET_PEERSTORE_StoreContext *
566 GNUNET_PEERSTORE_store (struct GNUNET_PEERSTORE_Handle *h,
567     const struct GNUNET_PeerIdentity *peer,
568     const char *sub_system,
569     const void *value,
570     size_t size,
571     struct GNUNET_TIME_Relative lifetime,
572     GNUNET_PEERSTORE_Continuation cont,
573     void *cont_cls)
574 {
575   struct GNUNET_PEERSTORE_RequestContext *rc;
576   struct StoreRequestMessage *entry;
577   struct GNUNET_PEERSTORE_StoreContext *sc;
578   char *ss;
579   void *val;
580   size_t sub_system_size;
581   size_t request_size;
582
583   LOG (GNUNET_ERROR_TYPE_DEBUG,
584       "Storing value (size: %lu) for subsytem `%s' and peer `%s'\n",
585       size, sub_system, GNUNET_i2s (peer));
586   sub_system_size = strlen(sub_system);
587   request_size = sizeof(struct StoreRequestMessage) + sub_system_size + size;
588   rc = GNUNET_malloc(sizeof(struct GNUNET_PEERSTORE_RequestContext) + request_size);
589   rc->h = h;
590   rc->size = request_size;
591   entry = (struct StoreRequestMessage *)&rc[1];
592   entry->header.size = htons(request_size);
593   entry->header.type = htons(GNUNET_MESSAGE_TYPE_PEERSTORE_STORE);
594   entry->peer = *peer;
595   entry->sub_system_size = htons(sub_system_size);
596   entry->value_size = htons(size);
597   entry->lifetime = lifetime;
598   ss = (char *)&entry[1];
599   memcpy(ss, sub_system, sub_system_size);
600   val = ss + sub_system_size;
601   memcpy(val, value, size);
602   sc = GNUNET_new(struct GNUNET_PEERSTORE_StoreContext);
603   sc->cont = cont;
604   sc->cont_cls = cont_cls;
605   sc->h = h;
606   sc->rc = rc;
607   sc->request_transmitted = GNUNET_NO;
608   rc->cont = &store_trigger_receive;
609   rc->cont_cls = sc;
610   GNUNET_CONTAINER_DLL_insert_tail(h->rc_head, h->rc_tail, rc);
611   GNUNET_CONTAINER_DLL_insert_tail(h->sc_head, h->sc_tail, sc);
612   trigger_transmit (h);
613   return sc;
614
615 }
616
617
618 /* end of peerstore_api.c */