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