Added documenting comments
[oweals/gnunet.git] / src / util / client_manager.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 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 util/client_manager.c
23  * @brief Client manager; higher level client API with transmission queue
24  * and message handler registration.
25  * @author Gabor X Toth
26  */
27
28 #include <inttypes.h>
29
30 #include "platform.h"
31 #include "gnunet_util_lib.h"
32
33 #define LOG(kind,...) GNUNET_log_from (kind, "util-client-mgr", __VA_ARGS__)
34
35
36 /**
37  * List of arrays of message handlers.
38  */
39 struct HandlersListItem
40 {
41   struct HandlersListItem *prev;
42   struct HandlersListItem *next;
43
44   /**
45    * NULL-terminated array of handlers.
46    */
47   const struct GNUNET_CLIENT_MANAGER_MessageHandler *handlers;
48 };
49
50
51 struct MessageQueueItem
52 {
53   struct MessageQueueItem *prev;
54   struct MessageQueueItem *next;
55   struct GNUNET_MessageHeader *msg;
56 };
57
58
59 struct GNUNET_CLIENT_MANAGER_Connection
60 {
61   /**
62    * Configuration to use.
63    */
64   const struct GNUNET_CONFIGURATION_Handle *cfg;
65
66   /**
67    * Client connection to service.
68    */
69   struct GNUNET_CLIENT_Connection *client;
70
71   /**
72    * Currently pending transmission request, or NULL for none.
73    */
74   struct GNUNET_CLIENT_TransmitHandle *client_tmit;
75
76   /**
77    * Service name to connect to.
78    */
79   const char *service_name;
80
81   /**
82    * Head of messages to transmit to the service.
83    */
84   struct MessageQueueItem *tmit_head;
85
86   /**
87    * Tail of messages to transmit to the service.
88    */
89   struct MessageQueueItem *tmit_tail;
90
91   /**
92    * Message handlers.
93    */
94   const struct GNUNET_CLIENT_MANAGER_MessageHandler *handlers;
95
96   /**
97    * Disconnect callback.
98    */
99   void (*disconnect_cb)(void *);
100
101   /**
102    * Disconnect closure.
103    */
104   void *disconnect_cls;
105
106   /**
107    * User context value.
108    * @see GNUNET_CLIENT_MANAGER_set_user_context()
109    * @see GNUNET_CLIENT_MANAGER_get_user_context()
110    */
111   void *user_ctx;
112
113   /**
114    * Last size given when user context was initialized.
115    * Used for sanity check.
116    */
117   size_t user_ctx_size;
118
119   /**
120    * Task doing exponential back-off trying to reconnect.
121    */
122   struct GNUNET_SCHEDULER_Task * reconnect_task;
123
124   /**
125    * Time for next connect retry.
126    */
127   struct GNUNET_TIME_Relative reconnect_delay;
128
129   /**
130    * Are we currently polling for incoming messages?
131    */
132   uint8_t in_receive;
133
134   /**
135    * #GNUNET_YES if GNUNET_CLIENT_MANAGER_disconnect() was called
136    * and we're transmitting the last messages from the queue.
137    */
138   uint8_t is_disconnecting;
139 };
140
141
142 /**
143  * Handle received messages from the service.
144  */
145 static void
146 recv_message (void *cls, const struct GNUNET_MessageHeader *msg)
147 {
148   struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
149   uint16_t type = 0, size = 0;
150
151   if (NULL != msg)
152   {
153     type = ntohs (msg->type);
154     size = ntohs (msg->size);
155     /* FIXME: decrease reconnect_delay gradually after a successful reconnection */
156   }
157
158   size_t i = 0;
159   while (NULL != mgr->handlers[i].callback)
160   {
161     const struct GNUNET_CLIENT_MANAGER_MessageHandler *mh = &mgr->handlers[i];
162     if ((mh->type == type) || (mh->type == GNUNET_MESSAGE_TYPE_ALL))
163     {
164       if (0 != mh->expected_size
165           && ((GNUNET_NO == mh->is_variable_size && size != mh->expected_size)
166               || (GNUNET_YES == mh->is_variable_size && size < mh->expected_size)))
167       {
168         LOG (GNUNET_ERROR_TYPE_ERROR,
169              "Expected %u bytes for message of type %u, got %u.\n",
170              mh->expected_size, type, size);
171         GNUNET_break_op (0);
172         GNUNET_CLIENT_disconnect (mgr->client);
173         mgr->client = NULL;
174         recv_message (mgr, NULL);
175         break;
176       }
177       mh->callback (mh->callback_cls, mgr, msg);
178     }
179     i++;
180   }
181   if (NULL != mgr->client)
182   {
183     GNUNET_CLIENT_receive (mgr->client, &recv_message, mgr,
184                            GNUNET_TIME_UNIT_FOREVER_REL);
185   }
186 }
187
188
189 /**
190  * Schedule transmission of the next message from our queue.
191  *
192  * @param mgr  Client manager connection.
193  */
194 static void
195 transmit_next (struct GNUNET_CLIENT_MANAGER_Connection *mgr);
196
197
198 static void
199 schedule_disconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
200 {
201   struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
202   GNUNET_CLIENT_MANAGER_disconnect (mgr, GNUNET_NO,
203                                     mgr->disconnect_cb, mgr->disconnect_cls);
204 }
205
206
207 /**
208  * Transmit next message to service.
209  *
210  * @param cls
211  *        struct GNUNET_CLIENT_MANAGER_Connection
212  * @param size
213  *        Number of bytes available in @a buf.
214  * @param buf
215  *        Where to copy the message.
216  *
217  * @return Number of bytes copied to @a buf.
218  */
219 static size_t
220 send_next_message (void *cls, size_t buf_size, void *buf)
221 {
222   LOG (GNUNET_ERROR_TYPE_DEBUG, "send_next_message()\n");
223   struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
224
225   if (NULL == buf)
226   {
227     /* disconnected */
228     recv_message (mgr, NULL);
229     return 0;
230   }
231
232   struct MessageQueueItem *mqi = mgr->tmit_head;
233   if (NULL == mqi)
234     return 0;
235
236   uint16_t size = ntohs (mqi->msg->size);
237   mgr->client_tmit = NULL;
238   GNUNET_assert (size <= buf_size);
239   memcpy (buf, mqi->msg, size);
240
241   GNUNET_CONTAINER_DLL_remove (mgr->tmit_head, mgr->tmit_tail, mqi);
242   GNUNET_free (mqi->msg);
243   GNUNET_free (mqi);
244
245   if (NULL != mgr->tmit_head)
246   {
247     transmit_next (mgr);
248   }
249   else if (GNUNET_YES == mgr->is_disconnecting)
250   {
251     GNUNET_SCHEDULER_add_now (&schedule_disconnect, mgr);
252     return size;
253   }
254
255   if (GNUNET_NO == mgr->in_receive)
256   {
257     mgr->in_receive = GNUNET_YES;
258     GNUNET_CLIENT_receive (mgr->client, &recv_message, mgr,
259                            GNUNET_TIME_UNIT_FOREVER_REL);
260   }
261   return size;
262 }
263
264
265 /**
266  * Schedule transmission of the next message from our queue.
267  *
268  * @param mgr  Client manager connection.
269  */
270 static void
271 transmit_next (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
272 {
273   LOG (GNUNET_ERROR_TYPE_DEBUG, "transmit_next()\n");
274   if (NULL != mgr->client_tmit || NULL == mgr->client)
275     return;
276
277   if (NULL == mgr->tmit_head)
278   {
279     if (GNUNET_YES == mgr->is_disconnecting)
280       GNUNET_CLIENT_MANAGER_disconnect (mgr, GNUNET_NO,
281                                         mgr->disconnect_cb, mgr->disconnect_cls);
282     return;
283   }
284
285   mgr->client_tmit
286     = GNUNET_CLIENT_notify_transmit_ready (mgr->client,
287                                            ntohs (mgr->tmit_head->msg->size),
288                                            GNUNET_TIME_UNIT_FOREVER_REL,
289                                            GNUNET_NO,
290                                            &send_next_message,
291                                            mgr);
292 }
293
294
295 /**
296  * Try again to connect to the service.
297  *
298  * @param cls
299  *        Channel handle.
300  * @param tc
301  *        Scheduler context.
302  */
303 static void
304 schedule_reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
305 {
306   struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
307   mgr->reconnect_task = NULL;
308
309   LOG (GNUNET_ERROR_TYPE_DEBUG,
310        "Connecting to %s service.\n", mgr->service_name);
311   GNUNET_assert (NULL == mgr->client);
312   mgr->client = GNUNET_CLIENT_connect (mgr->service_name, mgr->cfg);
313   GNUNET_assert (NULL != mgr->client);
314
315   transmit_next (mgr);
316 }
317
318
319 /**
320  * Connect to service.
321  *
322  * @param cfg
323  *        Configuration to use.
324  * @param service_name
325  *        Service name to connect to.
326  * @param handlers
327  *        Message handlers.
328  *
329  * @return Client manager connection handle.
330  */
331 struct GNUNET_CLIENT_MANAGER_Connection *
332 GNUNET_CLIENT_MANAGER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
333                                const char *service_name,
334                                const struct
335                                GNUNET_CLIENT_MANAGER_MessageHandler *handlers)
336 {
337   struct GNUNET_CLIENT_MANAGER_Connection *
338     mgr = GNUNET_malloc (sizeof (*mgr));
339   mgr->cfg = cfg;
340   mgr->service_name = service_name;
341   mgr->handlers = handlers;
342   mgr->reconnect_delay = GNUNET_TIME_UNIT_ZERO;
343   mgr->reconnect_task = GNUNET_SCHEDULER_add_now (&schedule_reconnect, mgr);
344   return mgr;
345 }
346
347
348 /**
349  * Disconnect from the service.
350  *
351  * @param mgr
352  *        Client manager connection.
353  * @param transmit_queue
354  *        Transmit pending messages in queue before disconnecting.
355  * @param disconnect_cb
356  *        Function called after disconnected from the service.
357  * @param cls
358  *        Closure for @a disconnect_cb.
359  */
360 void
361 GNUNET_CLIENT_MANAGER_disconnect (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
362                                   int transmit_queue,
363                                   GNUNET_ContinuationCallback disconnect_cb,
364                                   void *cls)
365 {
366   LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting (%d)\n", transmit_queue);
367   mgr->disconnect_cb = disconnect_cb;
368   mgr->disconnect_cls = cls;
369   if (NULL != mgr->tmit_head)
370   {
371     if (GNUNET_YES == transmit_queue)
372     {
373       mgr->is_disconnecting = GNUNET_YES;
374       transmit_next (mgr);
375       return;
376     }
377     else
378     {
379       LOG (GNUNET_ERROR_TYPE_DEBUG,
380            "Disconnecting while there are still messages "
381            "in the transmission queue.\n");
382       GNUNET_CLIENT_MANAGER_drop_queue (mgr);
383     }
384   }
385   if (mgr->reconnect_task != NULL)
386   {
387     GNUNET_SCHEDULER_cancel (mgr->reconnect_task);
388     mgr->reconnect_task = NULL;
389   }
390   if (NULL != mgr->client_tmit)
391   {
392     GNUNET_CLIENT_notify_transmit_ready_cancel (mgr->client_tmit);
393     mgr->client_tmit = NULL;
394   }
395   if (NULL != mgr->client)
396   {
397     GNUNET_CLIENT_disconnect (mgr->client);
398     mgr->client = NULL;
399   }
400   if (NULL != mgr->disconnect_cb)
401     mgr->disconnect_cb (mgr->disconnect_cls);
402   GNUNET_free (mgr);
403   LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnected.\n");
404 }
405
406
407 /**
408  * Reschedule connect to the service using exponential back-off.
409  *
410  * @param mgr
411  *        Client manager connection.
412  */
413 void
414 GNUNET_CLIENT_MANAGER_reconnect (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
415 {
416   if (NULL != mgr->reconnect_task)
417     return;
418
419   if (NULL != mgr->client_tmit)
420   {
421     GNUNET_CLIENT_notify_transmit_ready_cancel (mgr->client_tmit);
422     mgr->client_tmit = NULL;
423   }
424   if (NULL != mgr->client)
425   {
426     GNUNET_CLIENT_disconnect (mgr->client);
427     mgr->client = NULL;
428   }
429   mgr->in_receive = GNUNET_NO;
430   LOG (GNUNET_ERROR_TYPE_DEBUG,
431        "Scheduling task to reconnect to service in %s.\n",
432        GNUNET_STRINGS_relative_time_to_string (mgr->reconnect_delay, GNUNET_YES));
433   mgr->reconnect_task =
434     GNUNET_SCHEDULER_add_delayed (mgr->reconnect_delay, &schedule_reconnect, mgr);
435   mgr->reconnect_delay = GNUNET_TIME_STD_BACKOFF (mgr->reconnect_delay);
436 }
437
438
439 /**
440  * Add a message to the end of the transmission queue.
441  *
442  * @param mgr
443  *        Client manager connection.
444  * @param msg
445  *        Message to transmit, should be allocated with GNUNET_malloc() or
446  *        GNUNET_new(), as it is freed with GNUNET_free() after transmission.
447  */
448 void
449 GNUNET_CLIENT_MANAGER_transmit (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
450                                 struct GNUNET_MessageHeader *msg)
451 {
452   struct MessageQueueItem *mqi = GNUNET_malloc (sizeof (*mqi));
453   mqi->msg = msg;
454   GNUNET_CONTAINER_DLL_insert_tail (mgr->tmit_head, mgr->tmit_tail, mqi);
455   transmit_next (mgr);
456 }
457
458
459 /**
460  * Add a message to the beginning of the transmission queue.
461  *
462  * @param mgr
463  *        Client manager connection.
464  * @param msg
465  *        Message to transmit, should be allocated with GNUNET_malloc() or
466  *        GNUNET_new(), as it is freed with GNUNET_free() after transmission.
467  */
468 void
469 GNUNET_CLIENT_MANAGER_transmit_now (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
470                                     struct GNUNET_MessageHeader *msg)
471 {
472   struct MessageQueueItem *mqi = GNUNET_malloc (sizeof (*mqi));
473   mqi->msg = msg;
474   GNUNET_CONTAINER_DLL_insert (mgr->tmit_head, mgr->tmit_tail, mqi);
475   transmit_next (mgr);
476 }
477
478
479 /**
480  * Drop all queued messages.
481  *
482  * @param mgr
483  *        Client manager connection.
484  */
485 void
486 GNUNET_CLIENT_MANAGER_drop_queue (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
487 {
488   struct MessageQueueItem *cur, *next = mgr->tmit_head;
489   while (NULL != next)
490   {
491     cur = next;
492     next = cur->next;
493     GNUNET_free (cur->msg);
494     GNUNET_free (cur);
495   }
496 }
497
498
499 /**
500  * Obtain client connection handle.
501  *
502  * @param mgr
503  *        Client manager connection.
504  *
505  * @return Client connection handle.
506  */
507 struct GNUNET_CLIENT_Connection *
508 GNUNET_CLIENT_MANAGER_get_client (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
509 {
510   return mgr->client;
511 }
512
513
514 /**
515  * Return user context associated with the given client.
516  * Note: you should probably use the macro (call without the underscore).
517  *
518  * @param mgr
519  *        Client manager connection.
520  * @param size
521  *        Number of bytes in user context struct (for verification only).
522  *
523  * @return User context.
524  */
525 void *
526 GNUNET_CLIENT_MANAGER_get_user_context_ (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
527                                          size_t size)
528 {
529   if ((0 == mgr->user_ctx_size) &&
530       (NULL == mgr->user_ctx))
531     return NULL; /* never set */
532   GNUNET_assert (size == mgr->user_ctx_size);
533   return mgr->user_ctx;
534 }
535
536
537 /**
538  * Set user context to be associated with the given client.
539  * Note: you should probably use the macro (call without the underscore).
540  *
541  * @param mgr
542  *        Client manager connection.
543  * @param ctx
544  *        User context.
545  * @param size
546  *        Number of bytes in user context struct (for verification only).
547  */
548 void
549 GNUNET_CLIENT_MANAGER_set_user_context_ (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
550                                          void *ctx,
551                                          size_t size)
552 {
553   if (NULL == ctx)
554   {
555     mgr->user_ctx_size = 0;
556     mgr->user_ctx = ctx;
557     return;
558   }
559   mgr->user_ctx_size = size;
560   mgr->user_ctx = ctx;
561 }