-move welcome creation to startup
[oweals/gnunet.git] / src / transport / transport_api_monitor_plugins.c
1 /*
2      This file is part of GNUnet.
3      (C) 2014 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 transport/transport_api_monitor_plugins.c
23  * @brief montoring api for transport plugin session status
24  */
25 #include "platform.h"
26 #include "gnunet_util_lib.h"
27 #include "gnunet_arm_service.h"
28 #include "gnunet_hello_lib.h"
29 #include "gnunet_protocols.h"
30 #include "gnunet_transport_service.h"
31 #include "transport.h"
32
33
34 /**
35  * Handle for a plugin session state monitor.
36  */
37 struct GNUNET_TRANSPORT_PluginMonitor
38 {
39
40   /**
41    * Connection to the service.
42    */
43   struct GNUNET_CLIENT_Connection *client;
44
45   /**
46    * Our configuration.
47    */
48   const struct GNUNET_CONFIGURATION_Handle *cfg;
49
50   /**
51    * Callback to call.
52    */
53   GNUNET_TRANSPORT_SessionMonitorCallback cb;
54
55   /**
56    * Closure for @e cb
57    */
58   void *cb_cls;
59
60   /**
61    * Map of session_ids (reduced to 32-bits) to
62    * `struct GNUNET_TRANSPORT_PluginSession` objects.
63    */
64   struct GNUNET_CONTAINER_MultiHashMap32 *sessions;
65
66   /**
67    * Backoff for reconnect.
68    */
69   struct GNUNET_TIME_Relative backoff;
70
71   /**
72    * Task ID for reconnect.
73    */
74   GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
75
76 };
77
78
79 /**
80  * Abstract representation of a plugin's session.
81  * Corresponds to the `struct Session` within the TRANSPORT service.
82  */
83 struct GNUNET_TRANSPORT_PluginSession
84 {
85   /**
86    * Unique session identifier.
87    */
88   uint64_t session_id;
89
90   /**
91    * Location for the client to store "data".
92    */
93   void *client_ctx;
94 };
95
96
97 /**
98  * Function called with responses from the service.
99  *
100  * @param cls our `struct GNUNET_TRANSPORT_PluginMonitor *`
101  * @param msg NULL on timeout or error, otherwise presumably a
102  *        message with the human-readable address
103  */
104 static void
105 response_processor (void *cls,
106                     const struct GNUNET_MessageHeader *msg);
107
108
109 /**
110  * Send our subscription request to the service.
111  *
112  * @param pal_ctx our context
113  */
114 static void
115 send_plugin_mon_request (struct GNUNET_TRANSPORT_PluginMonitor *pm)
116 {
117   struct GNUNET_MessageHeader msg;
118
119   msg.size = htons (sizeof (struct GNUNET_MessageHeader));
120   msg.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_PLUGIN_START);
121   GNUNET_assert (GNUNET_OK ==
122                  GNUNET_CLIENT_transmit_and_get_response (pm->client,
123                                                           &msg,
124                                                           GNUNET_TIME_UNIT_FOREVER_REL,
125                                                           GNUNET_YES,
126                                                           &response_processor,
127                                                           pm));
128 }
129
130
131 /**
132  * Task run to re-establish the connection.
133  *
134  * @param cls our `struct GNUNET_TRANSPORT_PluginMonitor *`
135  * @param tc scheduler context, unused
136  */
137 static void
138 do_plugin_connect (void *cls,
139                  const struct GNUNET_SCHEDULER_TaskContext *tc)
140 {
141   struct GNUNET_TRANSPORT_PluginMonitor *pm = cls;
142
143   pm->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
144   pm->client = GNUNET_CLIENT_connect ("transport", pm->cfg);
145   GNUNET_assert (NULL != pm->client);
146   send_plugin_mon_request (pm);
147 }
148
149
150 /**
151  * Free the session entry and notify the callback about its demise.
152  *
153  * @param cls our `struct GNUNET_TRANSPORT_PluginMonitor`
154  * @param key key of the session in the map
155  * @param value the session to free
156  * @return #GNUNET_OK (continue to iterate)
157  */
158 static int
159 free_entry (void *cls,
160             uint32_t key,
161             void *value)
162 {
163   struct GNUNET_TRANSPORT_PluginMonitor *pm = cls;
164   struct GNUNET_TRANSPORT_PluginSession *ps = value;
165
166   pm->cb (pm->cb_cls,
167           ps,
168           &ps->client_ctx,
169           NULL);
170   GNUNET_break (GNUNET_YES ==
171                 GNUNET_CONTAINER_multihashmap32_remove (pm->sessions,
172                                                         key,
173                                                         ps));
174   GNUNET_break (NULL == ps->client_ctx);
175   GNUNET_free (ps);
176   return GNUNET_OK;
177 }
178
179
180 /**
181  * We got disconnected, remove all existing entries from
182  * the map and notify client.
183  *
184  * @param pm montitor that got disconnected
185  */
186 static void
187 clear_map (struct GNUNET_TRANSPORT_PluginMonitor *pm)
188 {
189   GNUNET_CONTAINER_multihashmap32_iterate (pm->sessions,
190                                            &free_entry,
191                                            pm);
192 }
193
194
195 /**
196  * Cut the existing connection and reconnect.
197  *
198  * @param pm our context
199  */
200 static void
201 reconnect_plugin_ctx (struct GNUNET_TRANSPORT_PluginMonitor *pm)
202 {
203   GNUNET_CLIENT_disconnect (pm->client);
204   pm->client = NULL;
205   clear_map (pm);
206   pm->backoff = GNUNET_TIME_STD_BACKOFF (pm->backoff);
207   pm->reconnect_task = GNUNET_SCHEDULER_add_delayed (pm->backoff,
208                                                      &do_plugin_connect,
209                                                      pm);
210 }
211
212
213 /**
214  * Convert 64-bit session ID to 32-bit index for hash map.
215  *
216  * @param id 64-bit session ID
217  * @return 32-bit hash map index
218  */
219 static uint32_t
220 wrap_id (uint64_t id)
221 {
222   return ((uint32_t) id) ^ ((uint32_t) (id >> 32));
223 }
224
225
226 /**
227  * Context for #locate_by_id().
228  */
229 struct SearchContext
230 {
231
232   /**
233    * Result.
234    */
235   struct GNUNET_TRANSPORT_PluginSession *ps;
236
237   /**
238    * ID to locate.
239    */
240   uint64_t session_id;
241
242 };
243
244
245 /**
246  * Locate a session entry.
247  *
248  * @param cls our `struct SearchContext`
249  * @param key key of the session in the map
250  * @param value a session
251  * @return #GNUNET_OK (continue to iterate), or #GNUNET_SYSERR (match found)
252  */
253 static int
254 locate_by_id (void *cls,
255               uint32_t key,
256               void *value)
257 {
258   struct SearchContext *sc = cls;
259   struct GNUNET_TRANSPORT_PluginSession *ps = value;
260
261   if (sc->session_id == ps->session_id)
262   {
263     sc->ps = ps;
264     return GNUNET_SYSERR;
265   }
266   return GNUNET_OK;
267 }
268
269
270 /**
271  * Function called with responses from the service.
272  *
273  * @param cls our `struct GNUNET_TRANSPORT_PluginMonitor *`
274  * @param msg NULL on timeout or error, otherwise presumably a
275  *        message with the human-readable address
276  */
277 static void
278 response_processor (void *cls,
279                     const struct GNUNET_MessageHeader *msg)
280 {
281   struct GNUNET_TRANSPORT_PluginMonitor *pm = cls;
282   const struct TransportPluginMonitorMessage *tpmm;
283   struct GNUNET_TRANSPORT_PluginSession *ps;
284   const char *pname;
285   const void *paddr;
286   enum GNUNET_TRANSPORT_SessionState ss;
287   size_t pname_len;
288   size_t paddr_len;
289   struct GNUNET_TRANSPORT_SessionInfo info;
290   struct GNUNET_HELLO_Address addr;
291   struct SearchContext rv;
292
293   if (NULL == msg)
294   {
295     reconnect_plugin_ctx (pm);
296     return;
297   }
298   if ( (GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_PLUGIN_EVENT != ntohs (msg->type)) ||
299        (sizeof (struct TransportPluginMonitorMessage) > ntohs (msg->size)) )
300   {
301     GNUNET_break (0);
302     reconnect_plugin_ctx (pm);
303     return;
304   }
305   tpmm = (const struct TransportPluginMonitorMessage *) msg;
306   pname = (const char *) &tpmm[1];
307   pname_len = ntohs (tpmm->plugin_name_len);
308   paddr_len = ntohs (tpmm->plugin_address_len);
309   if ( (pname_len +
310         paddr_len +
311         sizeof (struct TransportPluginMonitorMessage) != ntohs (msg->size)) ||
312        ( (0 != pname_len) &&
313          ('\0' != pname[pname_len - 1]) ) )
314   {
315     GNUNET_break (0);
316     reconnect_plugin_ctx (pm);
317     return;
318   }
319   paddr = &pname[pname_len];
320   ps = NULL;
321   ss = (enum GNUNET_TRANSPORT_SessionState) ntohs (tpmm->session_state);
322   if (GNUNET_TRANSPORT_SS_INIT == ss)
323   {
324     ps = GNUNET_new (struct GNUNET_TRANSPORT_PluginSession);
325     ps->session_id = tpmm->session_id;
326     (void) GNUNET_CONTAINER_multihashmap32_put (pm->sessions,
327                                                 wrap_id (tpmm->session_id),
328                                                 ps,
329                                                 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
330
331   }
332   else
333   {
334     rv.session_id = tpmm->session_id;
335     rv.ps = NULL;
336     (void) GNUNET_CONTAINER_multihashmap32_get_multiple (pm->sessions,
337                                                          wrap_id (tpmm->session_id),
338                                                          &locate_by_id,
339                                                          &rv);
340     ps = rv.ps;
341     if (NULL == ps)
342     {
343       GNUNET_break (0);
344       reconnect_plugin_ctx (pm);
345       return;
346     }
347   }
348   info.state = ss;
349   info.is_inbound = (int16_t) ntohs (tpmm->is_inbound);
350   info.num_msg_pending = ntohl (tpmm->msgs_pending);
351   info.num_bytes_pending = ntohl (tpmm->bytes_pending);
352   info.receive_delay = GNUNET_TIME_absolute_ntoh (tpmm->delay);
353   info.session_timeout = GNUNET_TIME_absolute_ntoh (tpmm->timeout);
354   info.address = &addr;
355   addr.peer = tpmm->peer;
356   addr.address = (0 == paddr_len) ? NULL : paddr;
357   addr.address_length = paddr_len;
358   addr.transport_name = (0 == pname_len) ? NULL : pname;
359   addr.local_info = GNUNET_HELLO_ADDRESS_INFO_NONE;
360   pm->cb (pm->cb_cls,
361           ps,
362           &ps->client_ctx,
363           &info);
364
365   if (GNUNET_TRANSPORT_SS_DONE == ss)
366   {
367     GNUNET_break (NULL == ps->client_ctx);
368     GNUNET_assert (GNUNET_YES ==
369                    GNUNET_CONTAINER_multihashmap32_remove (pm->sessions,
370                                                            wrap_id (tpmm->session_id),
371                                                            ps));
372     GNUNET_free (ps);
373   }
374 }
375
376
377 /**
378  * Install a plugin session state monitor callback.  The callback
379  * will be notified whenever the session changes.
380  *
381  * @param cfg configuration to use
382  * @param cb callback to invoke on events
383  * @param cb_cls closure for @a cb
384  * @return NULL on error, otherwise handle for cancellation
385  */
386 struct GNUNET_TRANSPORT_PluginMonitor *
387 GNUNET_TRANSPORT_monitor_plugins (const struct GNUNET_CONFIGURATION_Handle *cfg,
388                                   GNUNET_TRANSPORT_SessionMonitorCallback cb,
389                                   void *cb_cls)
390 {
391   struct GNUNET_TRANSPORT_PluginMonitor *pm;
392   struct GNUNET_CLIENT_Connection *client;
393
394   client = GNUNET_CLIENT_connect ("transport",
395                                   cfg);
396   if (NULL == client)
397     return NULL;
398   pm = GNUNET_new (struct GNUNET_TRANSPORT_PluginMonitor);
399   pm->cb = cb;
400   pm->cb_cls = cb_cls;
401   pm->cfg = cfg;
402   pm->client = client;
403   pm->sessions = GNUNET_CONTAINER_multihashmap32_create (128);
404   send_plugin_mon_request (pm);
405   return pm;
406 }
407
408
409 /**
410  * Cancel monitoring the plugin session state.  The callback will
411  * be called once for each session that is up with the information
412  * #GNUNET_TRANSPORT_SS_FINI (even though the session may stay up;
413  * this is just to enable client-side cleanup).
414  *
415  * @param pm handle of the request that is to be cancelled
416  */
417 void
418 GNUNET_TRANSPORT_monitor_plugins_cancel (struct GNUNET_TRANSPORT_PluginMonitor *pm)
419 {
420   if (NULL != pm->client)
421   {
422     GNUNET_CLIENT_disconnect (pm->client);
423     pm->client = NULL;
424   }
425   if (GNUNET_SCHEDULER_NO_TASK != pm->reconnect_task)
426   {
427     GNUNET_SCHEDULER_cancel (pm->reconnect_task);
428     pm->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
429   }
430   clear_map (pm);
431   GNUNET_CONTAINER_multihashmap32_destroy (pm->sessions);
432   GNUNET_free (pm);
433 }
434
435
436 /* end of transport_api_monitor_plugins.c */