aboutsummaryrefslogtreecommitdiff
path: root/src/transport/transport_api_monitor_plugins.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/transport/transport_api_monitor_plugins.c')
-rw-r--r--src/transport/transport_api_monitor_plugins.c434
1 files changed, 434 insertions, 0 deletions
diff --git a/src/transport/transport_api_monitor_plugins.c b/src/transport/transport_api_monitor_plugins.c
new file mode 100644
index 000000000..290092c22
--- /dev/null
+++ b/src/transport/transport_api_monitor_plugins.c
@@ -0,0 +1,434 @@
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 */
37struct 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 */
83struct 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 */
104static void
105response_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 */
114static void
115send_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 */
137static void
138do_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 */
158static int
159free_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 */
186static void
187clear_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 */
200static void
201reconnect_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 */
219static uint32_t
220wrap_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 */
229struct 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 */
253static int
254locate_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 */
277static void
278response_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 */
386struct GNUNET_TRANSPORT_PluginMonitor *
387GNUNET_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", cfg);
395 if (NULL == client)
396 return NULL;
397 pm = GNUNET_new (struct GNUNET_TRANSPORT_PluginMonitor);
398 pm->cb = cb;
399 pm->cb_cls = cb_cls;
400 pm->cfg = cfg;
401 pm->client = client;
402 send_plugin_mon_request (pm);
403 return pm;
404}
405
406
407/**
408 * Cancel monitoring the plugin session state. The callback will
409 * be called once for each session that is up with the information
410 * #GNUNET_TRANSPORT_SS_FINI (even though the session may stay up;
411 * this is just to enable client-side cleanup).
412 *
413 * @param pm handle of the request that is to be cancelled
414 */
415void
416GNUNET_TRANSPORT_monitor_plugins_cancel (struct GNUNET_TRANSPORT_PluginMonitor *pm)
417{
418 if (NULL != pm->client)
419 {
420 GNUNET_CLIENT_disconnect (pm->client);
421 pm->client = NULL;
422 }
423 if (GNUNET_SCHEDULER_NO_TASK != pm->reconnect_task)
424 {
425 GNUNET_SCHEDULER_cancel (pm->reconnect_task);
426 pm->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
427 }
428 clear_map (pm);
429 GNUNET_CONTAINER_multihashmap32_destroy (pm->sessions);
430 GNUNET_free (pm);
431}
432
433
434/* end of transport_api_monitor_plugins.c */