d8d5134b607f19b9b4542af7d968483c80c1ae90
[oweals/gnunet.git] / src / psycstore / psycstore_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2013 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public Liceidentity 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 Liceidentity for more details.
14
15      You should have received a copy of the GNU General Public Liceidentity
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 psycstore/psycstore_api.c
23  * @brief API to interact with the PSYCstore service
24  * @author Gabor X Toth
25  * @author Christian Grothoff
26  */
27
28 #include "platform.h"
29 #include "gnunet_util_lib.h"
30 #include "gnunet_constants.h"
31 #include "gnunet_protocols.h"
32 #include "gnunet_psycstore_service.h"
33 #include "psycstore.h"
34
35 #define LOG(kind,...) GNUNET_log_from (kind, "psycstore-api",__VA_ARGS__)
36
37
38 /** 
39  * Handle for an operation with the PSYCstore service.
40  */
41 struct GNUNET_PSYCSTORE_OperationHandle
42 {
43
44   /**
45    * Main PSYCstore handle.
46    */
47   struct GNUNET_PSYCSTORE_Handle *h;
48   
49   /**
50    * We keep operations in a DLL.
51    */
52   struct GNUNET_PSYCSTORE_OperationHandle *next;
53
54   /**
55    * We keep operations in a DLL.
56    */
57   struct GNUNET_PSYCSTORE_OperationHandle *prev;
58
59   /**
60    * Message to send to the PSYCstore service.
61    * Allocated at the end of this struct.
62    */
63   const struct GNUNET_MessageHeader *msg;
64
65   /**
66    * Continuation to invoke with the result of an operation.
67    */
68   GNUNET_PSYCSTORE_ResultCallback res_cb;
69
70   /**
71    * Continuation to invoke with the result of an operation returning a fragment.
72    */
73   GNUNET_PSYCSTORE_FragmentCallback frag_cb;
74
75   /**
76    * Continuation to invoke with the result of an operation returning a state variable.
77    */
78   GNUNET_PSYCSTORE_StateCallback state_cb;
79
80   /**
81    * Closure for the callbacks.
82    */
83   void *cls;
84
85 };
86
87
88 /**
89  * Handle for the service.
90  */
91 struct GNUNET_PSYCSTORE_Handle
92 {
93   /**
94    * Configuration to use.
95    */
96   const struct GNUNET_CONFIGURATION_Handle *cfg;
97
98   /**
99    * Socket (if available).
100    */
101   struct GNUNET_CLIENT_Connection *client;
102
103   /**
104    * Head of active operations.
105    */ 
106   struct GNUNET_PSYCSTORE_OperationHandle *op_head;
107
108   /**
109    * Tail of active operations.
110    */ 
111   struct GNUNET_PSYCSTORE_OperationHandle *op_tail;
112
113   /**
114    * Currently pending transmission request, or NULL for none.
115    */
116   struct GNUNET_CLIENT_TransmitHandle *th;
117
118   /**
119    * Task doing exponential back-off trying to reconnect.
120    */
121   GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
122
123   /**
124    * Time for next connect retry.
125    */
126   struct GNUNET_TIME_Relative reconnect_delay;
127
128   /**
129    * Are we polling for incoming messages right now?
130    */
131   int in_receive;
132
133 };
134
135
136 /**
137  * Try again to connect to the PSYCstore service.
138  *
139  * @param cls handle to the PSYCstore service.
140  * @param tc scheduler context
141  */
142 static void
143 reconnect (void *cls,
144            const struct GNUNET_SCHEDULER_TaskContext *tc);
145
146
147 /**
148  * Reschedule a connect attempt to the service.
149  *
150  * @param h transport service to reconnect
151  */
152 static void
153 reschedule_connect (struct GNUNET_PSYCSTORE_Handle *h)
154 {
155   GNUNET_assert (h->reconnect_task == GNUNET_SCHEDULER_NO_TASK);
156
157   if (NULL != h->th)
158   {
159     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
160     h->th = NULL;
161   }
162   if (NULL != h->client)
163   {
164     GNUNET_CLIENT_disconnect (h->client);
165     h->client = NULL;
166   }
167   h->in_receive = GNUNET_NO;
168   LOG (GNUNET_ERROR_TYPE_DEBUG,
169        "Scheduling task to reconnect to PSYCstore service in %s.\n",
170        GNUNET_STRINGS_relative_time_to_string (h->reconnect_delay, GNUNET_YES));
171   h->reconnect_task =
172       GNUNET_SCHEDULER_add_delayed (h->reconnect_delay, &reconnect, h);
173   h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay);
174 }
175
176
177 /**
178  * Type of a function to call when we receive a message
179  * from the service.
180  *
181  * @param cls closure
182  * @param msg message received, NULL on timeout or fatal error
183  */
184 static void
185 message_handler (void *cls, 
186                  const struct GNUNET_MessageHeader *msg)
187 {
188   struct GNUNET_PSYCSTORE_Handle *h = cls;
189   struct GNUNET_PSYCSTORE_OperationHandle *op;
190   const struct GNUNET_PSYCSTORE_ResultCodeMessage *rcm;
191   const char *str;
192   uint16_t size;
193
194   if (NULL == msg)
195   {
196     reschedule_connect (h);
197     return;
198   }
199   LOG (GNUNET_ERROR_TYPE_DEBUG,
200        "Received message of type %d from PSYCstore service\n",
201        ntohs (msg->type));
202   size = ntohs (msg->size);
203   switch (ntohs (msg->type))
204   {
205   case GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_CODE:
206     if (size < sizeof (struct GNUNET_PSYCSTORE_ResultCodeMessage))
207     {
208       GNUNET_break (0);
209       reschedule_connect (h);
210       return;
211     }
212     rcm = (const struct GNUNET_PSYCSTORE_ResultCodeMessage *) msg;
213     str = (const char *) &rcm[1];
214     if ( (size > sizeof (struct GNUNET_PSYCSTORE_ResultCodeMessage)) &&
215          ('\0' != str[size - sizeof (struct GNUNET_PSYCSTORE_ResultCodeMessage) - 1]) )
216     {
217       GNUNET_break (0);
218       reschedule_connect (h);
219       return;
220     }
221     if (size == sizeof (struct GNUNET_PSYCSTORE_ResultCodeMessage))
222       str = NULL;
223
224     op = h->op_head;
225     GNUNET_CONTAINER_DLL_remove (h->op_head,
226                                  h->op_tail,
227                                  op);
228     GNUNET_CLIENT_receive (h->client, &message_handler, h,
229                            GNUNET_TIME_UNIT_FOREVER_REL);
230     if (NULL != op->res_cb)
231       op->res_cb (op->cls, rcm->result_code , str);
232     GNUNET_free (op);
233     break;
234   default:
235     GNUNET_break (0);
236     reschedule_connect (h);
237     return;
238   }
239 }
240
241
242 /**
243  * Schedule transmission of the next message from our queue.
244  *
245  * @param h PSYCstore handle
246  */
247 static void
248 transmit_next (struct GNUNET_PSYCSTORE_Handle *h);
249
250
251 /**
252  * Transmit next message to service.
253  *
254  * @param cls the 'struct GNUNET_PSYCSTORE_Handle'.
255  * @param size number of bytes available in buf
256  * @param buf where to copy the message
257  * @return number of bytes copied to buf
258  */
259 static size_t
260 send_next_message (void *cls, 
261                    size_t size, 
262                    void *buf)
263 {
264   struct GNUNET_PSYCSTORE_Handle *h = cls;
265   struct GNUNET_PSYCSTORE_OperationHandle *op = h->op_head;
266   size_t ret;
267   
268   h->th = NULL;
269   if (NULL == op)
270     return 0;
271   ret = ntohs (op->msg->size);
272   if (ret > size)
273   {
274     reschedule_connect (h);
275     return 0;
276   }  
277   LOG (GNUNET_ERROR_TYPE_DEBUG,
278        "Sending message of type %d to PSYCstore service\n",
279        ntohs (op->msg->type));
280   memcpy (buf, op->msg, ret);
281   if ( (NULL == op->res_cb) &&
282        (NULL == op->frag_cb) &&
283        (NULL == op->state_cb))
284   {
285     GNUNET_CONTAINER_DLL_remove (h->op_head,
286                                  h->op_tail,
287                                  op);
288     GNUNET_free (op);
289     transmit_next (h);
290   }
291   if (GNUNET_NO == h->in_receive)
292   {
293     h->in_receive = GNUNET_YES;
294     GNUNET_CLIENT_receive (h->client,
295                            &message_handler, h,
296                            GNUNET_TIME_UNIT_FOREVER_REL);
297   }
298   return ret;
299 }
300
301
302 /**
303  * Schedule transmission of the next message from our queue.
304  *
305  * @param h PSYCstore handle
306  */
307 static void
308 transmit_next (struct GNUNET_PSYCSTORE_Handle *h)
309 {
310   struct GNUNET_PSYCSTORE_OperationHandle *op = h->op_head;
311
312   GNUNET_assert (NULL == h->th);
313   if (NULL == op)
314     return;
315   if (NULL == h->client)
316     return;
317   h->th = GNUNET_CLIENT_notify_transmit_ready (h->client,
318                                                ntohs (op->msg->size),
319                                                GNUNET_TIME_UNIT_FOREVER_REL,
320                                                GNUNET_NO,
321                                                &send_next_message,
322                                                h);
323 }
324
325
326 /**
327  * Try again to connect to the PSYCstore service.
328  *
329  * @param cls the handle to the PSYCstore service
330  * @param tc scheduler context
331  */
332 static void
333 reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
334 {
335   struct GNUNET_PSYCSTORE_Handle *h = cls;
336
337   h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
338   LOG (GNUNET_ERROR_TYPE_DEBUG,
339        "Connecting to PSYCstore service.\n");
340   GNUNET_assert (NULL == h->client);
341   h->client = GNUNET_CLIENT_connect ("psycstore", h->cfg);
342   GNUNET_assert (NULL != h->client);
343   transmit_next (h);
344   GNUNET_assert (NULL != h->th);
345 }
346
347
348 /**
349  * Connect to the PSYCstore service.
350  *
351  * @param cfg the configuration to use
352  * @return handle to use
353  */
354 struct GNUNET_PSYCSTORE_Handle *
355 GNUNET_PSYCSTORE_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
356 {
357   struct GNUNET_PSYCSTORE_Handle *h;
358
359   h = GNUNET_new (struct GNUNET_PSYCSTORE_Handle);
360   h->cfg = cfg;
361   h->reconnect_delay = GNUNET_TIME_UNIT_ZERO;
362   h->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, h);
363   return h;
364 }
365
366
367 /**
368  * Disconnect from PSYCstore service
369  *
370  * @param h handle to destroy
371  */
372 void
373 GNUNET_PSYCSTORE_disconnect (struct GNUNET_PSYCSTORE_Handle *h)
374 {
375   GNUNET_assert (NULL != h);
376   GNUNET_assert (h->op_head == h->op_tail);
377   if (h->reconnect_task != GNUNET_SCHEDULER_NO_TASK)
378   {
379     GNUNET_SCHEDULER_cancel (h->reconnect_task);
380     h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
381   }
382   if (NULL != h->th)
383   {
384     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
385     h->th = NULL;
386   }
387   if (NULL != h->client)
388   {
389     GNUNET_CLIENT_disconnect (h->client);
390     h->client = NULL;
391   }
392   GNUNET_free (h);
393 }
394
395
396 /**
397  * Cancel a PSYCstore operation. Note that the operation MAY still
398  * be executed; this merely cancels the continuation; if the request
399  * was already transmitted, the service may still choose to complete
400  * the operation.
401  *
402  * @param op operation to cancel
403  */
404 void
405 GNUNET_PSYCSTORE_operation_cancel (struct GNUNET_PSYCSTORE_OperationHandle *op)
406 {
407   struct GNUNET_PSYCSTORE_Handle *h = op->h;
408
409   if ( (h->op_head != op) ||
410        (NULL == h->client) )
411   {
412     /* request not active, can simply remove */
413     GNUNET_CONTAINER_DLL_remove (h->op_head,
414                                  h->op_tail,
415                                  op);
416     GNUNET_free (op);
417     return;
418   }
419   if (NULL != h->th)
420   {
421     /* request active but not yet with service, can still abort */
422     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
423     h->th = NULL;
424     GNUNET_CONTAINER_DLL_remove (h->op_head,
425                                  h->op_tail,
426                                  op);
427     GNUNET_free (op);
428     transmit_next (h);
429     return;
430   }
431   /* request active with service, simply ensure continuations are not called */
432   op->res_cb = NULL;
433   op->frag_cb = NULL;
434   op->state_cb = NULL;
435 }
436
437
438 /* end of psycstore_api.c */