a49d2a72013880ee48f25c91605d411495830c8d
[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_AddContext *ac_head;
56
57   /**
58    * Tail of transmission queue.
59    */
60   struct GNUNET_PEERSTORE_AddContext *ac_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    * ID for a reconnect task.
69    */
70   GNUNET_SCHEDULER_TaskIdentifier r_task;
71
72   /**
73    * Are we now receiving?
74    */
75   int in_receive;
76
77 };
78
79 /**
80  * Entry in the transmission queue to PEERSTORE service.
81  *
82  */
83 struct GNUNET_PEERSTORE_AddContext
84 {
85   /**
86    * This is a linked list.
87    */
88   struct GNUNET_PEERSTORE_AddContext *next;
89
90   /**
91    * This is a linked list.
92    */
93   struct GNUNET_PEERSTORE_AddContext *prev;
94
95   /**
96    * Handle to the PEERSTORE service.
97    */
98   struct GNUNET_PEERSTORE_Handle *h;
99
100   /**
101    * Function to call after request has been transmitted, or NULL.
102    */
103   GNUNET_PEERSTORE_Continuation cont;
104
105   /**
106    * Closure for 'cont'.
107    */
108   void *cont_cls;
109
110   /**
111    * Number of bytes of the request message (follows after this struct).
112    */
113   size_t size;
114
115 };
116
117 /******************************************************************************/
118 /***********************         DECLARATIONS         *************************/
119 /******************************************************************************/
120
121 /**
122  * Close the existing connection to PEERSTORE and reconnect.
123  *
124  * @param h handle to the service
125  */
126 static void
127 reconnect (struct GNUNET_PEERSTORE_Handle *h);
128
129 /**
130  * Check if we have a request pending in the transmission queue and are
131  * able to transmit it right now.  If so, schedule transmission.
132  *
133  * @param h handle to the service
134  */
135 static void
136 trigger_transmit (struct GNUNET_PEERSTORE_Handle *h);
137
138 /******************************************************************************/
139 /*******************         CONNECTION FUNCTIONS         *********************/
140 /******************************************************************************/
141
142 /**
143  * Task scheduled to re-try connecting to the peerstore service.
144  *
145  * @param cls the 'struct GNUNET_PEERSTORE_Handle'
146  * @param tc scheduler context
147  */
148 static void
149 reconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
150 {
151   struct GNUNET_PEERSTORE_Handle *h = cls;
152
153   h->r_task = GNUNET_SCHEDULER_NO_TASK;
154   reconnect (h);
155 }
156
157 /**
158  * Connect to the PEERSTORE service.
159  *
160  * @return NULL on error
161  */
162 struct GNUNET_PEERSTORE_Handle *
163 GNUNET_PEERSTORE_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
164 {
165   struct GNUNET_CLIENT_Connection *client;
166   struct GNUNET_PEERSTORE_Handle *h;
167
168   client = GNUNET_CLIENT_connect ("peerstore", cfg);
169   if(NULL == client)
170     return NULL;
171   h = GNUNET_new (struct GNUNET_PEERSTORE_Handle);
172   h->client = client;
173   h->cfg = cfg;
174   return h;
175 }
176
177 /**
178  * Disconnect from the PEERSTORE service
179  *
180  * @param h handle to disconnect
181  */
182 void
183 GNUNET_PEERSTORE_disconnect(struct GNUNET_PEERSTORE_Handle *h)
184 {
185   if (NULL != h->client)
186   {
187     GNUNET_CLIENT_disconnect (h->client);
188     h->client = NULL;
189   }
190   GNUNET_free (h);
191 }
192
193 /**
194  * Close the existing connection to PEERSTORE and reconnect.
195  *
196  * @param h handle to the service
197  */
198 static void
199 reconnect (struct GNUNET_PEERSTORE_Handle *h)
200 {
201   if (GNUNET_SCHEDULER_NO_TASK != h->r_task)
202   {
203     GNUNET_SCHEDULER_cancel (h->r_task);
204     h->r_task = GNUNET_SCHEDULER_NO_TASK;
205   }
206   if (NULL != h->th)
207   {
208     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
209     h->th = NULL;
210   }
211   if (NULL != h->client)
212   {
213     GNUNET_CLIENT_disconnect (h->client);
214     h->client = NULL;
215   }
216   h->in_receive = GNUNET_NO;
217   h->client = GNUNET_CLIENT_connect ("peerstore", h->cfg);
218   if (NULL == h->client)
219   {
220     h->r_task =
221         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &reconnect_task,
222                                       h);
223     return;
224   }
225   trigger_transmit (h);
226 }
227
228 /**
229  * Transmit the request at the head of the transmission queue
230  * and trigger continuation (if any).
231  *
232  * @param cls the 'struct GNUNET_PEERSTORE_Handle' (with the queue)
233  * @param size size of the buffer (0 on error)
234  * @param buf where to copy the message
235  * @return number of bytes copied to buf
236  */
237 static size_t
238 do_transmit (void *cls, size_t size, void *buf)
239 {
240   struct GNUNET_PEERSTORE_Handle *h = cls;
241   struct GNUNET_PEERSTORE_AddContext *ac = h->ac_head;
242   size_t ret;
243
244   h->th = NULL;
245   if (NULL == ac)
246     return 0; /* request was cancelled in the meantime */
247   if (NULL == buf)
248   {
249     /* peerstore service died */
250     LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
251          "Failed to transmit message to `%s' service.\n", "PEERSTORE");
252     GNUNET_CONTAINER_DLL_remove (h->ac_head, h->ac_tail, ac);
253     reconnect (h);
254     if (NULL != ac->cont)
255       ac->cont (ac->cont_cls, _("failed to transmit request (service down?)"));
256     GNUNET_free (ac);
257     return 0;
258   }
259   ret = ac->size;
260   if (size < ret)
261   {
262     /* change in head of queue (i.e. cancel + add), try again */
263     trigger_transmit (h);
264     return 0;
265   }
266   LOG (GNUNET_ERROR_TYPE_DEBUG,
267        "Transmitting request of size %u to `%s' service.\n", ret, "PEERSTORE");
268   memcpy (buf, &ac[1], ret);
269   GNUNET_CONTAINER_DLL_remove (h->ac_head, h->ac_tail, ac);
270   trigger_transmit (h);
271   if (NULL != ac->cont)
272     ac->cont (ac->cont_cls, NULL);
273   GNUNET_free (ac);
274   return ret;
275 }
276
277 /**
278  * Check if we have a request pending in the transmission queue and are
279  * able to transmit it right now.  If so, schedule transmission.
280  *
281  * @param h handle to the service
282  */
283 static void
284 trigger_transmit (struct GNUNET_PEERSTORE_Handle *h)
285 {
286   struct GNUNET_PEERSTORE_AddContext *ac;
287
288   if (NULL == (ac = h->ac_head))
289     return; /* no requests queued */
290   if (NULL != h->th)
291     return; /* request already pending */
292   if (NULL == h->client)
293   {
294     /* disconnected, try to reconnect */
295     reconnect (h);
296     return;
297   }
298   h->th =
299     GNUNET_CLIENT_notify_transmit_ready (h->client, ac->size,
300            GNUNET_TIME_UNIT_FOREVER_REL,
301            GNUNET_YES,
302            &do_transmit, h);
303 }
304
305 /******************************************************************************/
306 /*******************             ADD FUNCTIONS            *********************/
307 /******************************************************************************/
308
309 struct GNUNET_PEERSTORE_AddContext *
310 GNUNET_PEERSTORE_add (struct GNUNET_PEERSTORE_Handle *h,
311     const struct GNUNET_PeerIdentity *peer,
312     const char *sub_system,
313     const void *value,
314     size_t size,
315     struct GNUNET_TIME_Relative lifetime,
316     GNUNET_PEERSTORE_Continuation cont,
317     void *cont_cls)
318 {
319   struct GNUNET_PEERSTORE_AddContext *ac;
320   struct AddEntryMessage *entry;
321   char *ss;
322   void *val;
323   size_t sub_system_size;
324   size_t request_size;
325
326   LOG (GNUNET_ERROR_TYPE_DEBUG,
327       "Storing value (size: %lu) for subsytem `%s' and peer `%s'",
328       size, sub_system, GNUNET_i2s (peer));
329   sub_system_size = strlen(sub_system);
330   request_size = sizeof(struct AddEntryMessage) + sub_system_size + size;
331   ac = GNUNET_malloc(sizeof(struct GNUNET_PEERSTORE_AddContext) + request_size);
332   ac->h = h;
333   ac->size = request_size;
334   entry = (struct AddEntryMessage *)&ac[1];
335   entry->header.size = htons(request_size);
336   entry->header.type = htons(GNUNET_MESSAGE_TYPE_PEERSTORE_ADD);
337   entry->peer = *peer;
338   entry->sub_system_size = sub_system_size;
339   entry->value_size = size;
340   entry->lifetime = lifetime;
341   ss = (char *)&entry[1];
342   memcpy(ss, sub_system, sub_system_size);
343   val = ss + sub_system_size;
344   memcpy(val, value, size);
345   GNUNET_CONTAINER_DLL_insert_tail(h->ac_head, h->ac_tail, ac);
346   trigger_transmit (h);
347   return ac;
348
349 }
350
351
352 /* end of peerstore_api.c */