+/**
+ * Iterator over all peers to send a monitoring client info about a tunnel.
+ *
+ * @param cls Closure (message being built).
+ * @param key Key (hashed tunnel ID, unused).
+ * @param value Peer info.
+ *
+ * @return GNUNET_YES, to keep iterating.
+ */
+static int
+monitor_peers_iterator (void *cls,
+ const struct GNUNET_HashCode * key,
+ void *value)
+{
+ struct GNUNET_MESH_LocalMonitor *msg = cls;
+ struct GNUNET_PeerIdentity *id;
+ struct MeshPeerInfo *info = value;
+
+ id = (struct GNUNET_PeerIdentity *) &msg[1];
+ GNUNET_PEER_resolve (info->id, &id[msg->npeers]);
+ msg->npeers++;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "* sending info about peer %s [%u]\n",
+ GNUNET_i2s (&id[msg->npeers - 1]), msg->npeers);
+
+ return GNUNET_YES;
+}
+
+
+
+/**
+ * Iterator over all tunnels to send a monitoring client info about each tunnel.
+ *
+ * @param cls Closure (client handle).
+ * @param key Key (hashed tunnel ID, unused).
+ * @param value Tunnel info.
+ *
+ * @return GNUNET_YES, to keep iterating.
+ */
+static int
+monitor_all_tunnels_iterator (void *cls,
+ const struct GNUNET_HashCode * key,
+ void *value)
+{
+ struct GNUNET_SERVER_Client *client = cls;
+ struct MeshTunnel *t = value;
+ struct GNUNET_MESH_LocalMonitor *msg;
+ uint32_t npeers;
+
+ npeers = GNUNET_CONTAINER_multihashmap_size (t->peers);
+ msg = GNUNET_malloc (sizeof(struct GNUNET_MESH_LocalMonitor) +
+ npeers * sizeof (struct GNUNET_PeerIdentity));
+ GNUNET_PEER_resolve(t->id.oid, &msg->owner);
+ msg->tunnel_id = htonl (t->id.tid);
+ msg->header.size = htons (sizeof (struct GNUNET_MESH_LocalMonitor) +
+ npeers * sizeof (struct GNUNET_PeerIdentity));
+ msg->header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_INFO_TUNNELS);
+ msg->npeers = 0;
+ (void) GNUNET_CONTAINER_multihashmap_iterate (t->peers,
+ monitor_peers_iterator,
+ msg);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "* sending info about tunnel %s [%u] (%u peers)\n",
+ GNUNET_i2s (&msg->owner), t->id.tid, npeers);
+
+ if (msg->npeers != npeers)
+ {
+ GNUNET_break (0);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Get tunnels fail: size %u - iter %u\n",
+ npeers, msg->npeers);
+ }
+
+ msg->npeers = htonl (npeers);
+ GNUNET_SERVER_notification_context_unicast (nc, client,
+ &msg->header, GNUNET_NO);
+ return GNUNET_YES;
+}
+
+
+/**
+ * Handler for client's MONITOR request.
+ *
+ * @param cls Closure (unused).
+ * @param client Identification of the client.
+ * @param message The actual message.
+ */
+static void
+handle_local_get_tunnels (void *cls, struct GNUNET_SERVER_Client *client,
+ const struct GNUNET_MessageHeader *message)
+{
+ struct MeshClient *c;
+
+ /* Sanity check for client registration */
+ if (NULL == (c = client_get (client)))
+ {
+ GNUNET_break (0);
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Received get tunnels request from client %u\n",
+ c->id);
+ GNUNET_CONTAINER_multihashmap_iterate (tunnels,
+ monitor_all_tunnels_iterator,
+ client);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Get tunnels request from client %u completed\n",
+ c->id);
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+}
+
+
+/**
+ * Data needed to build a Monitor_Tunnel message.
+ */
+struct MeshMonitorTunnelContext
+{
+ /**
+ * Partial message, including peer count.
+ */
+ struct GNUNET_MESH_LocalMonitor *msg;
+
+ /**
+ * Hashmap with positions: peer->position.
+ */
+ struct GNUNET_CONTAINER_MultiHashMap *lookup;
+
+ /**
+ * Index of the parent of each peer in the message, realtive to the absolute
+ * order in the array (can be in a previous message).
+ */
+ uint32_t parents[1024];
+
+ /**
+ * Peers visited so far in the tree, aka position of the current peer.
+ */
+ unsigned int npeers;
+
+ /**
+ * Client requesting the info.
+ */
+ struct MeshClient *c;
+};
+
+
+/**
+ * Send a client a message about the structure of a tunnel.
+ *
+ * @param ctx Context of the tunnel iteration, with info regarding the state
+ * of the execution and the number of peers visited for this message.
+ */
+static void
+send_client_tunnel_info (struct MeshMonitorTunnelContext *ctx)
+{
+ struct GNUNET_MESH_LocalMonitor *resp = ctx->msg;
+ struct GNUNET_PeerIdentity *pid;
+ unsigned int *parent;
+ size_t size;
+
+ size = sizeof (struct GNUNET_MESH_LocalMonitor);
+ size += (sizeof (struct GNUNET_PeerIdentity) + sizeof (int)) * resp->npeers;
+ resp->header.size = htons (size);
+ pid = (struct GNUNET_PeerIdentity *) &resp[1];
+ parent = (unsigned int *) &pid[resp->npeers];
+ memcpy (parent, ctx->parents, sizeof(uint32_t) * resp->npeers);
+ GNUNET_SERVER_notification_context_unicast (nc, ctx->c->handle,
+ &resp->header, GNUNET_NO);
+}
+
+/**
+ * Iterator over a tunnel tree to build a message containing all peers
+ * the in the tunnel, including relay nodes.
+ *
+ * @param cls Closure (pointer to pointer of message being built).
+ * @param peer Short ID of a peer.
+ * @param parent Short ID of the @c peer 's parent.
+ */
+static void
+tunnel_tree_iterator (void *cls,
+ GNUNET_PEER_Id peer,
+ GNUNET_PEER_Id parent)
+{
+ struct MeshMonitorTunnelContext *ctx = cls;
+ struct GNUNET_MESH_LocalMonitor *msg;
+ struct GNUNET_PeerIdentity *pid;
+ struct GNUNET_PeerIdentity ppid;
+
+ msg = ctx->msg;
+ pid = (struct GNUNET_PeerIdentity *) &msg[1];
+ GNUNET_PEER_resolve (peer, &pid[msg->npeers]);
+ GNUNET_CONTAINER_multihashmap_put (ctx->lookup,
+ &pid[msg->npeers].hashPubKey,
+ (void *) (long) ctx->npeers,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+ GNUNET_PEER_resolve (parent, &ppid);
+ ctx->parents[msg->npeers] =
+ htonl ((long) GNUNET_CONTAINER_multihashmap_get (ctx->lookup,
+ &ppid.hashPubKey));
+
+ ctx->npeers++;
+ msg->npeers++;
+
+ if (sizeof (struct GNUNET_MESH_LocalMonitor) +
+ (msg->npeers + 1) *
+ (sizeof (struct GNUNET_PeerIdentity) + sizeof (uint32_t))
+ > USHRT_MAX)
+ {
+ send_client_tunnel_info (ctx);
+ msg->npeers = 0;
+ }
+}
+
+
+/**
+ * Handler for client's MONITOR_TUNNEL request.
+ *
+ * @param cls Closure (unused).
+ * @param client Identification of the client.
+ * @param message The actual message.
+ */
+static void
+handle_local_show_tunnel (void *cls, struct GNUNET_SERVER_Client *client,
+ const struct GNUNET_MessageHeader *message)
+{
+ const struct GNUNET_MESH_LocalMonitor *msg;
+ struct GNUNET_MESH_LocalMonitor *resp;
+ struct MeshMonitorTunnelContext ctx;
+ struct MeshClient *c;
+ struct MeshTunnel *t;
+
+ /* Sanity check for client registration */
+ if (NULL == (c = client_get (client)))
+ {
+ GNUNET_break (0);
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+
+ msg = (struct GNUNET_MESH_LocalMonitor *) message;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Received tunnel info request from client %u for tunnel %s[%X]\n",
+ c->id,
+ &msg->owner,
+ ntohl (msg->tunnel_id));
+ t = tunnel_get (&msg->owner, ntohl (msg->tunnel_id));
+ if (NULL == t)
+ {
+ /* We don't know the tunnel */
+ struct GNUNET_MESH_LocalMonitor warn;
+
+ warn = *msg;
+ warn.npeers = htonl (UINT_MAX);
+ GNUNET_SERVER_notification_context_unicast (nc, client,
+ &warn.header,
+ GNUNET_NO);
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+ return;
+ }
+
+ /* Initialize context */
+ resp = GNUNET_malloc (USHRT_MAX); /* avoid realloc'ing on each step */
+ *resp = *msg;
+ resp->npeers = 0;
+ ctx.msg = resp;
+ ctx.lookup = GNUNET_CONTAINER_multihashmap_create (4 * t->peers_total,
+ GNUNET_YES);
+ ctx.c = c;
+
+ /* Collect and send information */
+ tree_iterate_all (t->tree, &tunnel_tree_iterator, &ctx);
+ send_client_tunnel_info (&ctx);
+
+ /* Free context */
+ GNUNET_CONTAINER_multihashmap_destroy (ctx.lookup);
+ GNUNET_free (resp);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Monitor tunnel request from client %u completed\n",
+ c->id);
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+}
+
+