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