2 This file is part of GNUnet.
3 (C) 2014 Christian Grothoff (and other contributing authors)
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.
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.
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.
22 * @file transport/transport_api_monitor_plugins.c
23 * @brief montoring api for transport plugin session status
24 * @author Christian Grothoff
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"
36 * Handle for a plugin session state monitor.
38 struct GNUNET_TRANSPORT_PluginMonitor
42 * Connection to the service.
44 struct GNUNET_CLIENT_Connection *client;
49 const struct GNUNET_CONFIGURATION_Handle *cfg;
54 GNUNET_TRANSPORT_SessionMonitorCallback cb;
62 * Map of session_ids (reduced to 32-bits) to
63 * `struct GNUNET_TRANSPORT_PluginSession` objects.
65 struct GNUNET_CONTAINER_MultiHashMap32 *sessions;
68 * Backoff for reconnect.
70 struct GNUNET_TIME_Relative backoff;
73 * Task ID for reconnect.
75 struct GNUNET_SCHEDULER_Task * reconnect_task;
81 * Abstract representation of a plugin's session.
82 * Corresponds to the `struct Session` within the TRANSPORT service.
84 struct GNUNET_TRANSPORT_PluginSession
87 * Unique session identifier.
92 * Location for the client to store "data".
99 * Function called with responses from the service.
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
106 response_processor (void *cls,
107 const struct GNUNET_MessageHeader *msg);
111 * Send our subscription request to the service.
113 * @param pal_ctx our context
116 send_plugin_mon_request (struct GNUNET_TRANSPORT_PluginMonitor *pm)
118 struct GNUNET_MessageHeader msg;
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,
125 GNUNET_TIME_UNIT_FOREVER_REL,
133 * Task run to re-establish the connection.
135 * @param cls our `struct GNUNET_TRANSPORT_PluginMonitor *`
136 * @param tc scheduler context, unused
139 do_plugin_connect (void *cls,
140 const struct GNUNET_SCHEDULER_TaskContext *tc)
142 struct GNUNET_TRANSPORT_PluginMonitor *pm = cls;
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);
152 * Free the session entry and notify the callback about its demise.
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)
160 free_entry (void *cls,
164 struct GNUNET_TRANSPORT_PluginMonitor *pm = cls;
165 struct GNUNET_TRANSPORT_PluginSession *ps = value;
171 GNUNET_break (GNUNET_YES ==
172 GNUNET_CONTAINER_multihashmap32_remove (pm->sessions,
175 GNUNET_break (NULL == ps->client_ctx);
182 * We got disconnected, remove all existing entries from
183 * the map and notify client.
185 * @param pm montitor that got disconnected
188 clear_map (struct GNUNET_TRANSPORT_PluginMonitor *pm)
190 GNUNET_CONTAINER_multihashmap32_iterate (pm->sessions,
197 * Cut the existing connection and reconnect.
199 * @param pm our context
202 reconnect_plugin_ctx (struct GNUNET_TRANSPORT_PluginMonitor *pm)
204 GNUNET_CLIENT_disconnect (pm->client);
207 pm->backoff = GNUNET_TIME_STD_BACKOFF (pm->backoff);
208 pm->reconnect_task = GNUNET_SCHEDULER_add_delayed (pm->backoff,
215 * Convert 64-bit session ID to 32-bit index for hash map.
217 * @param id 64-bit session ID
218 * @return 32-bit hash map index
221 wrap_id (uint64_t id)
223 return ((uint32_t) id) ^ ((uint32_t) (id >> 32));
228 * Context for #locate_by_id().
236 struct GNUNET_TRANSPORT_PluginSession *ps;
247 * Locate a session entry.
249 * @param cls our `struct SearchContext`
250 * @param key key of the session in the map
251 * @param value a session
252 * @return #GNUNET_OK (continue to iterate), or #GNUNET_SYSERR (match found)
255 locate_by_id (void *cls,
259 struct SearchContext *sc = cls;
260 struct GNUNET_TRANSPORT_PluginSession *ps = value;
262 if (sc->session_id == ps->session_id)
265 return GNUNET_SYSERR;
272 * Function called with responses from the service.
274 * @param cls our `struct GNUNET_TRANSPORT_PluginMonitor *`
275 * @param msg NULL on timeout or error, otherwise presumably a
276 * message with the human-readable address
279 response_processor (void *cls,
280 const struct GNUNET_MessageHeader *msg)
282 struct GNUNET_TRANSPORT_PluginMonitor *pm = cls;
283 const struct TransportPluginMonitorMessage *tpmm;
284 struct GNUNET_TRANSPORT_PluginSession *ps;
287 enum GNUNET_TRANSPORT_SessionState ss;
290 struct GNUNET_TRANSPORT_SessionInfo info;
291 struct GNUNET_HELLO_Address addr;
292 struct SearchContext rv;
296 reconnect_plugin_ctx (pm);
299 if ( (GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_PLUGIN_SYNC == ntohs (msg->type)) &&
300 (sizeof (struct GNUNET_MessageHeader) == ntohs (msg->size)) )
307 GNUNET_CLIENT_receive (pm->client,
310 GNUNET_TIME_UNIT_FOREVER_REL);
314 if ( (GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_PLUGIN_EVENT != ntohs (msg->type)) ||
315 (sizeof (struct TransportPluginMonitorMessage) > ntohs (msg->size)) )
318 reconnect_plugin_ctx (pm);
321 tpmm = (const struct TransportPluginMonitorMessage *) msg;
322 pname = (const char *) &tpmm[1];
323 pname_len = ntohs (tpmm->plugin_name_len);
324 paddr_len = ntohs (tpmm->plugin_address_len);
327 sizeof (struct TransportPluginMonitorMessage) != ntohs (msg->size)) ||
328 ( (0 != pname_len) &&
329 ('\0' != pname[pname_len - 1]) ) )
332 reconnect_plugin_ctx (pm);
335 paddr = &pname[pname_len];
337 ss = (enum GNUNET_TRANSPORT_SessionState) ntohs (tpmm->session_state);
338 if (GNUNET_TRANSPORT_SS_INIT == ss)
340 ps = GNUNET_new (struct GNUNET_TRANSPORT_PluginSession);
341 ps->session_id = tpmm->session_id;
342 (void) GNUNET_CONTAINER_multihashmap32_put (pm->sessions,
343 wrap_id (tpmm->session_id),
345 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
350 rv.session_id = tpmm->session_id;
352 (void) GNUNET_CONTAINER_multihashmap32_get_multiple (pm->sessions,
353 wrap_id (tpmm->session_id),
360 reconnect_plugin_ctx (pm);
365 info.is_inbound = (int16_t) ntohs (tpmm->is_inbound);
366 info.num_msg_pending = ntohl (tpmm->msgs_pending);
367 info.num_bytes_pending = ntohl (tpmm->bytes_pending);
368 info.receive_delay = GNUNET_TIME_absolute_ntoh (tpmm->delay);
369 info.session_timeout = GNUNET_TIME_absolute_ntoh (tpmm->timeout);
370 info.address = &addr;
371 addr.peer = tpmm->peer;
372 addr.address = (0 == paddr_len) ? NULL : paddr;
373 addr.address_length = paddr_len;
374 addr.transport_name = (0 == pname_len) ? NULL : pname;
375 addr.local_info = GNUNET_HELLO_ADDRESS_INFO_NONE;
381 if (GNUNET_TRANSPORT_SS_DONE == ss)
383 GNUNET_break (NULL == ps->client_ctx);
384 GNUNET_assert (GNUNET_YES ==
385 GNUNET_CONTAINER_multihashmap32_remove (pm->sessions,
386 wrap_id (tpmm->session_id),
390 GNUNET_CLIENT_receive (pm->client,
393 GNUNET_TIME_UNIT_FOREVER_REL);
398 * Install a plugin session state monitor callback. The callback
399 * will be notified whenever the session changes.
401 * @param cfg configuration to use
402 * @param cb callback to invoke on events
403 * @param cb_cls closure for @a cb
404 * @return NULL on error, otherwise handle for cancellation
406 struct GNUNET_TRANSPORT_PluginMonitor *
407 GNUNET_TRANSPORT_monitor_plugins (const struct GNUNET_CONFIGURATION_Handle *cfg,
408 GNUNET_TRANSPORT_SessionMonitorCallback cb,
411 struct GNUNET_TRANSPORT_PluginMonitor *pm;
412 struct GNUNET_CLIENT_Connection *client;
414 client = GNUNET_CLIENT_connect ("transport",
418 pm = GNUNET_new (struct GNUNET_TRANSPORT_PluginMonitor);
423 pm->sessions = GNUNET_CONTAINER_multihashmap32_create (128);
424 send_plugin_mon_request (pm);
430 * Cancel monitoring the plugin session state. The callback will
431 * be called once for each session that is up with the information
432 * #GNUNET_TRANSPORT_SS_FINI (even though the session may stay up;
433 * this is just to enable client-side cleanup).
435 * @param pm handle of the request that is to be cancelled
438 GNUNET_TRANSPORT_monitor_plugins_cancel (struct GNUNET_TRANSPORT_PluginMonitor *pm)
440 if (NULL != pm->client)
442 GNUNET_CLIENT_disconnect (pm->client);
445 if (NULL != pm->reconnect_task)
447 GNUNET_SCHEDULER_cancel (pm->reconnect_task);
448 pm->reconnect_task = NULL;
451 GNUNET_CONTAINER_multihashmap32_destroy (pm->sessions);
456 /* end of transport_api_monitor_plugins.c */