towards 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_AddContext *rc_head;
56
57   /**
58    * Tail of transmission queue.
59    */
60   struct GNUNET_PEERSTORE_AddContext *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    * 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  * Connect to the PEERSTORE service.
144  *
145  * @return NULL on error
146  */
147 struct GNUNET_PEERSTORE_Handle *
148 GNUNET_PEERSTORE_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
149 {
150   struct GNUNET_CLIENT_Connection *client;
151   struct GNUNET_PEERSTORE_Handle *h;
152
153   client = GNUNET_CLIENT_connect ("peerstore", cfg);
154   if(NULL == client)
155     return NULL;
156   h = GNUNET_new (struct GNUNET_PEERSTORE_Handle);
157   h->client = client;
158   h->cfg = cfg;
159   return h;
160 }
161
162 /**
163  * Disconnect from the PEERSTORE service
164  *
165  * @param h handle to disconnect
166  */
167 void
168 GNUNET_PEERSTORE_disconnect(struct GNUNET_PEERSTORE_Handle *h)
169 {
170   if (NULL != h->client)
171   {
172     GNUNET_CLIENT_disconnect (h->client);
173     h->client = NULL;
174   }
175   GNUNET_free (h);
176 }
177
178 /**
179  * Task scheduled to re-try connecting to the PEERSTORE service.
180  *
181  * @param cls the 'struct GNUNET_PEERSTORE_Handle'
182  * @param tc scheduler context
183  */
184 static void
185 reconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
186 {
187   struct GNUNET_PEERSTORE_Handle *h = cls;
188
189   h->r_task = GNUNET_SCHEDULER_NO_TASK;
190   reconnect (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 *rc = h->rc_head;
242   size_t ret;
243
244   h->th = NULL;
245   if (NULL == rc)
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->rc_head, h->rc_tail, rc);
253     reconnect (h);
254     if (NULL != rc->cont)
255       rc->cont (rc->cont_cls, _("failed to transmit request (service down?)"));
256     GNUNET_free (rc);
257     return 0;
258   }
259   ret = rc->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, &rc[1], ret);
269   GNUNET_CONTAINER_DLL_remove (h->rc_head, h->rc_tail, rc);
270   trigger_transmit (h);
271   if (NULL != rc->cont)
272     rc->cont (rc->cont_cls, NULL);
273   GNUNET_free (rc);
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 *rc;
287
288   if (NULL == (rc = h->rc_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, rc->size,
300            GNUNET_TIME_UNIT_FOREVER_REL,
301            GNUNET_YES,
302            &do_transmit, h);
303 }
304
305 /******************************************************************************/
306 /*******************           STORE FUNCTIONS            *********************/
307 /******************************************************************************/
308
309
310
311 /* end of peerstore_api.c */