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