#include "gnunet_service_lib.h"
#include "gnunet_core_service.h"
#include "gnunet_signal_lib.h"
+#include "gnunet_util_lib.h"
#include "dv.h"
/**
/**
* The identity of our peer.
*/
-static struct GNUNET_PeerIdentity *my_identity;
+const struct GNUNET_PeerIdentity *my_identity;
/**
* The configuration for this service.
*/
static struct GNUNET_SCHEDULER_Handle *sched;
+/**
+ * How often do we check about sending out more peer information (if
+ * we are connected to no peers previously).
+ */
+#define GNUNET_DV_DEFAULT_SEND_INTERVAL (500 * GNUNET_CRON_MILLISECONDS)
+
+/**
+ * How long do we wait at most between sending out information?
+ */
+#define GNUNET_DV_MAX_SEND_INTERVAL (5000 * GNUNET_CRON_MILLISECONDS)
+
+/**
+ * How long can we have not heard from a peer and
+ * still have it in our tables?
+ */
+#define GNUNET_DV_PEER_EXPIRATION_TIME (3000 * GNUNET_CRON_SECONDS)
+
+/**
+ * Priority for gossip.
+ */
+#define GNUNET_DV_DHT_GOSSIP_PRIORITY (GNUNET_EXTREME_PRIORITY / 10)
+
+/**
+ * How often should we check if expiration time has elapsed for
+ * some peer?
+ */
+#define GNUNET_DV_MAINTAIN_FREQUENCY (5 * GNUNET_CRON_SECONDS)
+
+/**
+ * How long to allow a message to be delayed?
+ */
+#define DV_DELAY (5000 * GNUNET_CRON_MILLISECONDS)
+
+/**
+ * Priority to use for DV data messages.
+ */
+#define DV_PRIORITY 0
+
/**
* The client, should be the DV plugin connected to us. Hopefully
* this client will never change, although if the plugin dies
GNUNET_SCHEDULER_TaskIdentifier cleanup_task;
+/**
+ * Struct where neighbor information is stored.
+ */
+struct DistantNeighbor *referees;
+
+/**
+ * Struct to hold information for updating existing neighbors
+ */
+struct NeighborUpdateInfo
+{
+ /**
+ * Cost
+ */
+ unsigned int cost;
+
+ /**
+ * The existing neighbor
+ */
+ struct DistantNeighbor *neighbor;
+
+ /**
+ * The referrer of the possibly existing peer
+ */
+ struct DirectNeighbor *referrer;
+
+ /**
+ * The time we heard about this peer
+ */
+ struct GNUNET_TIME_Absolute now;
+};
+
+/**
+ * Struct where actual neighbor information is stored,
+ * referenced by min_heap and max_heap. Freeing dealt
+ * with when items removed from hashmap.
+ */
+struct DirectNeighbor
+{
+ /**
+ * Identity of neighbor.
+ */
+ struct GNUNET_PeerIdentity identity;
+
+ /**
+ * Head of DLL of nodes that this direct neighbor referred to us.
+ */
+ struct DistantNeighbor *referee_head;
+
+ /**
+ * Tail of DLL of nodes that this direct neighbor referred to us.
+ */
+ struct DistantNeighbor *referee_tail;
+
+ /**
+ * Is this one of the direct neighbors that we are "hiding"
+ * from DV?
+ */
+ int hidden;
+};
+
+
+/**
+ * Struct where actual neighbor information is stored,
+ * referenced by min_heap and max_heap. Freeing dealt
+ * with when items removed from hashmap.
+ */
+struct DistantNeighbor
+{
+ /**
+ * We keep distant neighbor's of the same referrer in a DLL.
+ */
+ struct DistantNeighbor *next;
+
+ /**
+ * We keep distant neighbor's of the same referrer in a DLL.
+ */
+ struct DistantNeighbor *prev;
+
+ /**
+ * Node in min heap
+ */
+ struct GNUNET_CONTAINER_HeapNode *min_loc;
+
+ /**
+ * Node in max heap
+ */
+ struct GNUNET_CONTAINER_HeapNode *max_loc;
+
+ /**
+ * Identity of referrer (next hop towards 'neighbor').
+ */
+ struct DirectNeighbor *referrer;
+
+ /**
+ * Identity of neighbor.
+ */
+ struct GNUNET_PeerIdentity identity;
+
+ /**
+ * Last time we received routing information from this peer
+ */
+ struct GNUNET_TIME_Absolute last_activity;
+
+ /**
+ * Cost to neighbor, used for actual distance vector computations
+ */
+ unsigned int cost;
+
+ /**
+ * Random identifier *we* use for this peer, to be used as shortcut
+ * instead of sending full peer id for each message
+ */
+ unsigned int our_id;
+
+ /**
+ * Random identifier the *referrer* uses for this peer.
+ */
+ unsigned int referrer_id;
+
+ /**
+ * Is this one of the direct neighbors that we are "hiding"
+ * from DV?
+ */
+ int hidden;
+};
+
+
+/**
+ * Global construct
+ */
+struct GNUNET_DV_Context
+{
+ /**
+ * Map of PeerIdentifiers to 'struct GNUNET_dv_neighbor*'s for all
+ * directly connected peers.
+ */
+ struct GNUNET_CONTAINER_MultiHashMap *direct_neighbors;
+
+ /**
+ * Map of PeerIdentifiers to 'struct GNUNET_dv_neighbor*'s for
+ * peers connected via DV (extended neighborhood). Does ALSO
+ * include any peers that are in 'direct_neighbors'; for those
+ * peers, the cost will be zero and the referrer all zeros.
+ */
+ struct GNUNET_CONTAINER_MultiHashMap *extended_neighbors;
+
+ /**
+ * We use the min heap (min refers to cost) to prefer
+ * gossipping about peers with small costs.
+ */
+ struct GNUNET_CONTAINER_Heap *neighbor_min_heap;
+
+ /**
+ * We use the max heap (max refers to cost) for general
+ * iterations over all peers and to remove the most costly
+ * connection if we have too many.
+ */
+ struct GNUNET_CONTAINER_Heap *neighbor_max_heap;
+
+ unsigned long long fisheye_depth;
+
+ unsigned long long max_table_size;
+
+ unsigned int send_interval;
+
+ unsigned int neighbor_id_loc;
+
+ int closing;
+};
+
+static char shortID[5];
+
+static struct GNUNET_DV_Context ctx;
+
+struct FindDestinationContext
+{
+ unsigned int tid;
+ struct DistantNeighbor *dest;
+};
+
+
+/**
+ * We've been given a target ID based on the random numbers that
+ * we assigned to our DV-neighborhood. Find the entry for the
+ * respective neighbor.
+ */
+static int
+find_destination (void *cls,
+ struct GNUNET_CONTAINER_HeapNode *node,
+ void *element, GNUNET_CONTAINER_HeapCostType cost)
+{
+ struct FindDestinationContext *fdc = cls;
+ struct DistantNeighbor *dn = element;
+
+ if (fdc->tid != dn->our_id)
+ return GNUNET_YES;
+ fdc->dest = dn;
+ return GNUNET_NO;
+}
+
+/**
+ * Function called to notify a client about the socket
+ * begin ready to queue more data. "buf" will be
+ * NULL and "size" zero if the socket was closed for
+ * writing in the meantime.
+ *
+ * @param cls closure
+ * @param size number of bytes available in buf
+ * @param buf where the callee should write the message
+ * @return number of bytes written to buf
+ */
+size_t transmit_to_plugin (void *cls,
+ size_t size, void *buf)
+{
+ struct GNUNET_DV_MessageReceived *msg = cls;
+
+ if (buf == NULL)
+ return 0;
+
+ GNUNET_assert(size >= ntohs(msg->header.size));
+
+ memcpy(buf, msg, size);
+ GNUNET_free(msg);
+ return size;
+}
+
+
+void send_to_plugin(const struct GNUNET_PeerIdentity * sender, const struct GNUNET_MessageHeader *message, size_t message_size, struct DistantNeighbor *distant_neighbor)
+{
+ struct GNUNET_DV_MessageReceived *received_msg;
+ int size;
+
+ if (ntohs(msg->size) < sizeof(struct GNUNET_DV_MessageReceived))
+ return;
+
+ size = sizeof(struct GNUNET_DV_MessageReceived) + message_size + sizeof(struct GNUNET_PeerIdentity);
+ received_msg = GNUNET_malloc(size);
+ received_msg->header.size = htons(size);
+ received_msg->header.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_DV_RECEIVE);
+ received_msg->sender_address_len = sizeof(struct GNUNET_PeerIdentity);
+ received_msg->distance = htonl(distant_neighbor->cost);
+ /* Set the sender in this message to be the original sender! */
+ memcpy(&received_msg->sender, &distant_neighbor->identity, sizeof(struct GNUNET_PeerIdentity));
+ /* Copy the intermediate sender to the end of the message, this is how the transport identifies this peer */
+ memcpy(&received_msg[1], sender, sizeof(struct GNUNET_PeerIdentity));
+
+ /* FIXME: Send to the client please */
+ GNUNET_SERVER_notify_transmit_ready (client_handle,
+ size, CLIENT_TRANSMIT_TIMEOUT,
+ &transmit_to_plugin, &received_msg);
+
+}
+
/**
* Core handler for dv data messages. Whatever this message
* contains all we really have to do is rip it out of its
* in with.
*
* @param cls closure
- * @param client identification of the client
- * @param message the actual message
+ * @param peer peer which sent the message (immediate sender)
+ * @param message the message
+ * @param latency the latency of the connection we received the message from
+ * @param distance the distance to the immediate peer
*/
-static void handle_dv_data_message (void *cls,
- struct GNUNET_PeerIdentity *
- peer,
- const struct
- GNUNET_MessageHeader *
- message,
+static int handle_dv_data_message (void *cls,
+ const struct GNUNET_PeerIdentity * peer,
+ const struct GNUNET_MessageHeader * message,
struct GNUNET_TIME_Relative latency,
uint32_t distance)
{
"%s: Receives %s message!\n", "dv", "DV DATA");
#endif
+ const p2p_dv_MESSAGE_Data *incoming = (const p2p_dv_MESSAGE_Data *) message;
+ const struct GNUNET_MessageHeader *packed_message = (const struct GNUNET_MessageHeader *) &incoming[1];
+ struct DirectNeighbor *dn;
+ struct DistantNeighbor *pos;
+ unsigned int sid; /* Sender id */
+ unsigned int tid; /* Target id */
+ struct GNUNET_PeerIdentity original_sender;
+ struct GNUNET_PeerIdentity destination;
+ struct FindDestinationContext fdc;
+ int ret;
+
+ if ((ntohs (incoming->header.size) <
+ sizeof (p2p_dv_MESSAGE_Data) + sizeof (struct GNUNET_MessageHeader))
+ || (ntohs (incoming->header.size) !=
+ (sizeof (p2p_dv_MESSAGE_Data) + ntohs (packed_message->size))))
+ {
+ return GNUNET_SYSERR;
+ }
+
+ dn = GNUNET_CONTAINER_multihashmap_get (ctx.direct_neighbors,
+ &peer->hashPubKey);
+ if (dn == NULL)
+ {
+ return GNUNET_OK;
+ }
+ sid = ntohl (incoming->sender);
+ pos = dn->referee_head;
+ while ((NULL != pos) && (pos->referrer_id != sid))
+ pos = pos->next;
+ if (pos == NULL)
+ {
+ /* unknown sender */
+ return GNUNET_OK;
+ }
+ original_sender = pos->identity;
+ tid = ntohl (incoming->recipient);
+ if (tid == 0)
+ {
+ /* 0 == us */
+
+ /* FIXME: Will we support wrapped messages being these types? Probably not, they should
+ * be encrypted messages that need decrypting and junk like that.
+ */
+ GNUNET_break_op (ntohs (packed_message->type) != GNUNET_MESSAGE_TYPE_DV_GOSSIP);
+ GNUNET_break_op (ntohs (packed_message->type) != GNUNET_MESSAGE_TYPE_DV_DATA);
+ if ( (ntohs (packed_message->type) != GNUNET_MESSAGE_TYPE_DV_GOSSIP) &&
+ (ntohs (packed_message->type) != GNUNET_MESSAGE_TYPE_DV_DATA) )
+ {
+ /* FIXME: send the message, wrap it up and return it to the DV plugin */
+ /*coreAPI->loopback_send (&original_sender, (const char *) packed_message,
+ ntohs (packed_message->size), GNUNET_YES, NULL);*/
+ send_to_plugin(peer, packed_message, ntohs(packed_message->size), pos);
+ }
+
+ return GNUNET_OK;
+ }
+
+ /* FIXME: this is the *only* per-request operation we have in DV
+ that is O(n) in relation to the number of connected peers; a
+ hash-table lookup could easily solve this (minor performance
+ issue) */
+ fdc.tid = tid;
+ fdc.dest = NULL;
+ GNUNET_CONTAINER_heap_iterate (ctx.neighbor_max_heap,
+ &find_destination, &fdc);
+ if (fdc.dest == NULL)
+ {
+ return GNUNET_OK;
+ }
+ destination = fdc.dest->identity;
+
+ if (0 == memcmp (&destination, peer, sizeof (struct GNUNET_PeerIdentity)))
+ {
+ /* FIXME: create stat: routing loop-discard! */
+ return GNUNET_OK;
+ }
+
+ /* FIXME: Can't send message on, we have to behave.
+ * We have to tell core we have a message for the next peer, and let
+ * transport do transport selection on how to get this message to 'em */
+ /*ret = send_message (&destination,
+ &original_sender,
+ packed_message, DV_PRIORITY, DV_DELAY);*/
+ send_to_core(&destination, &original_sender, packed_message, DV_PRIORITY, DV_DELAY);
+
+ return GNUNET_OK;
}
/**
* transport service so that it can be used by all others.
*
* @param cls closure
- * @param client identification of the client
- * @param message the actual message
+ * @param peer peer which sent the message (immediate sender)
+ * @param message the message
+ * @param latency the latency of the connection we received the message from
+ * @param distance the distance to the immediate peer
*/
-static void handle_dv_gossip_message (void *cls,
- struct GNUNET_PeerIdentity * peer,
- const struct GNUNET_MessageHeader * message,
- struct GNUNET_TIME_Relative latency,
- uint32_t distance)
+static int handle_dv_gossip_message (void *cls,
+ const struct GNUNET_PeerIdentity *peer,
+ const struct GNUNET_MessageHeader *message,
+ struct GNUNET_TIME_Relative latency,
+ uint32_t distance)
{
#if DEBUG_DV
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"%s: Receives %s message!\n", "dv", "DV GOSSIP");
#endif
+ return 0;
}
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"%s: Setting client handle (was a different client!)!\n", "dv");
}
+
+ GNUNET_SERVER_receive_done(client, GNUNET_OK);
}
+
/**
* List of handlers for the messages understood by this
* service.
{NULL, 0, 0}
};
+
static struct GNUNET_SERVER_MessageHandler plugin_handlers[] = {
{&send_dv_message, NULL, GNUNET_MESSAGE_TYPE_TRANSPORT_DV_SEND, 0},
{NULL, NULL, 0, 0}
*/
void core_init (void *cls,
struct GNUNET_CORE_Handle * server,
- const struct GNUNET_PeerIdentity *my_identity,
+ const struct GNUNET_PeerIdentity *identity,
const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded * publicKey)
{
}
#if DEBUG_DV
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "%s: Core connection initialized, I am peer: %s\n", "dv", GNUNET_i2s(my_identity));
+ "%s: Core connection initialized, I am peer: %s\n", "dv", GNUNET_i2s(identity));
#endif
+ my_identity = identity;
coreAPI = server;
}
+
+/**
+ * Iterator over hash map entries.
+ *
+ * @param cls closure
+ * @param key current key code
+ * @param value value in the hash map
+ * @return GNUNET_YES if we should continue to
+ * iterate,
+ * GNUNET_NO if not.
+ */
+static int update_matching_neighbors (void *cls,
+ const GNUNET_HashCode * key,
+ void *value)
+{
+ struct NeighborUpdateInfo * update_info = cls;
+ struct DirectNeighbor *direct_neighbor = value;
+
+ if (update_info->referrer == direct_neighbor) /* Direct neighbor matches, update it's info and return GNUNET_NO */
+ {
+ /* same referrer, cost change! */
+ GNUNET_CONTAINER_heap_update_cost (ctx.neighbor_max_heap,
+ update_info->neighbor->max_loc, update_info->cost);
+ GNUNET_CONTAINER_heap_update_cost (ctx.neighbor_min_heap,
+ update_info->neighbor->min_loc, update_info->cost);
+ update_info->neighbor->last_activity = update_info->now;
+ update_info->neighbor->cost = update_info->cost;
+ return GNUNET_NO;
+ }
+
+ return GNUNET_YES;
+}
+
+
+/**
+ * Free a DistantNeighbor node, including removing it
+ * from the referer's list.
+ */
+static void
+distant_neighbor_free (struct DistantNeighbor *referee)
+{
+ struct DirectNeighbor *referrer;
+
+ referrer = referee->referrer;
+ if (referrer != NULL)
+ {
+ GNUNET_CONTAINER_DLL_remove (referrer->referee_head,
+ referrer->referee_tail, referee);
+ }
+ GNUNET_CONTAINER_heap_remove_node (ctx.neighbor_max_heap, referee->max_loc);
+ GNUNET_CONTAINER_heap_remove_node (ctx.neighbor_min_heap, referee->min_loc);
+ GNUNET_CONTAINER_multihashmap_remove_all (ctx.extended_neighbors,
+ &referee->identity.hashPubKey);
+ GNUNET_free (referee);
+}
+
+
+/**
+ * Handles when a peer is either added due to being newly connected
+ * or having been gossiped about, also called when a cost for a neighbor
+ * needs to be updated.
+ *
+ * @param peer identity of the peer whose info is being added/updated
+ * @param peer_id id to use when sending to 'peer'
+ * @param referrer if this is a gossiped peer, who did we hear it from?
+ * @param cost the cost of communicating with this peer via 'referrer'
+ */
+static void
+addUpdateNeighbor (const struct GNUNET_PeerIdentity * peer,
+ unsigned int referrer_peer_id,
+ struct DirectNeighbor *referrer, unsigned int cost)
+{
+ struct DistantNeighbor *neighbor;
+ struct DistantNeighbor *max;
+ struct GNUNET_TIME_Absolute now;
+ struct NeighborUpdateInfo *neighbor_update;
+ unsigned int our_id;
+
+ now = GNUNET_TIME_absolute_get ();
+ our_id = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, RAND_MAX - 1) + 1;
+
+ neighbor = GNUNET_CONTAINER_multihashmap_get (ctx.extended_neighbors,
+ &peer->hashPubKey);
+ neighbor_update = GNUNET_malloc(sizeof(struct NeighborUpdateInfo));
+ neighbor_update->neighbor = neighbor;
+ neighbor_update->cost = cost;
+ neighbor_update->now = now;
+ neighbor_update->referrer = referrer;
+
+ /* Either we do not know this peer, or we already do but via a different immediate peer */
+ if ((neighbor == NULL) ||
+ (GNUNET_CONTAINER_multihashmap_get_multiple(ctx.extended_neighbors,
+ &peer->hashPubKey,
+ &update_matching_neighbors,
+ neighbor_update) != GNUNET_SYSERR))
+ {
+ /* new neighbor! */
+ if (cost > ctx.fisheye_depth)
+ {
+ /* too costly */
+ return;
+ }
+ if (ctx.max_table_size <=
+ GNUNET_CONTAINER_multihashmap_size (ctx.extended_neighbors))
+ {
+ /* remove most expensive entry */
+ max = GNUNET_CONTAINER_heap_peek (ctx.neighbor_max_heap);
+ if (cost > max->cost)
+ {
+ /* new entry most expensive, don't create */
+ return;
+ }
+ if (max->cost > 0)
+ {
+ /* only free if this is not a direct connection;
+ we could theoretically have more direct
+ connections than DV entries allowed total! */
+ distant_neighbor_free (max);
+ }
+ }
+
+ neighbor = GNUNET_malloc (sizeof (struct DistantNeighbor));
+ GNUNET_CONTAINER_DLL_insert (referrer->referee_head,
+ referrer->referee_tail, neighbor);
+ neighbor->max_loc = GNUNET_CONTAINER_heap_insert (ctx.neighbor_max_heap,
+ neighbor, cost);
+ neighbor->min_loc = GNUNET_CONTAINER_heap_insert (ctx.neighbor_min_heap,
+ neighbor, cost);
+ neighbor->referrer = referrer;
+ memcpy (&neighbor->identity, peer, sizeof (struct GNUNET_PeerIdentity));
+ neighbor->last_activity = now;
+ neighbor->cost = cost;
+ neighbor->referrer_id = referrer_peer_id;
+ neighbor->our_id = our_id;
+ neighbor->hidden =
+ (cost == 0) ? (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 4) ==
+ 0) : GNUNET_NO;
+ GNUNET_CONTAINER_multihashmap_put (ctx.extended_neighbors, &peer->hashPubKey,
+ neighbor,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+
+ return;
+ }
+
+ /* Old logic to remove entry and replace, not needed now as we only want to remove when full
+ * or when the referring peer disconnects from us.
+ */
+ /*
+ GNUNET_DLL_remove (neighbor->referrer->referee_head,
+ neighbor->referrer->referee_tail, neighbor);
+ neighbor->referrer = referrer;
+ GNUNET_DLL_insert (referrer->referee_head,
+ referrer->referee_tail, neighbor);
+ GNUNET_CONTAINER_heap_update_cost (ctx.neighbor_max_heap,
+ neighbor->max_loc, cost);
+ GNUNET_CONTAINER_heap_update_cost (ctx.neighbor_min_heap,
+ neighbor->min_loc, cost);
+ neighbor->referrer_id = referrer_peer_id;
+ neighbor->last_activity = now;
+ neighbor->cost = cost;
+ */
+}
+
+
/**
* Method called whenever a given peer either connects.
*
* @param cls closure
* @param peer peer identity this notification is about
- * @param latency reported latency of the connection with 'other'
- * @param distance reported distance (DV) to 'other'
+ * @param latency reported latency of the connection with peer
+ * @param distance reported distance (DV) to peer
*/
void handle_core_connect (void *cls,
const struct GNUNET_PeerIdentity * peer,
struct GNUNET_TIME_Relative latency,
uint32_t distance)
{
-
+ struct DirectNeighbor *neighbor;
#if DEBUG_DV
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "%s: Receives core connect message!\n", "dv");
+ "%s: Receives core connect message for peer %s distance %d!\n", "dv", GNUNET_i2s(peer), distance);
#endif
+
+ neighbor = GNUNET_malloc (sizeof (struct DirectNeighbor));
+ memcpy (&neighbor->identity, peer, sizeof (struct GNUNET_PeerIdentity));
+ GNUNET_CONTAINER_multihashmap_put (ctx.direct_neighbors,
+ &peer->hashPubKey,
+ neighbor, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
+ addUpdateNeighbor (peer, 0, neighbor, 0);
}
/**
*
* @param cls closure
* @param peer peer identity this notification is about
- * @param latency reported latency of the connection with 'other'
- * @param distance reported distance (DV) to 'other'
*/
void handle_core_disconnect (void *cls,
const struct GNUNET_PeerIdentity * peer)
{
+ struct DirectNeighbor *neighbor;
+ struct DistantNeighbor *referee;
+
#if DEBUG_DV
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"%s: Receives core peer disconnect message!\n", "dv");
#endif
+
+ neighbor =
+ GNUNET_CONTAINER_multihashmap_get (ctx.direct_neighbors, &peer->hashPubKey);
+ if (neighbor == NULL)
+ {
+ return;
+ }
+ while (NULL != (referee = neighbor->referee_head))
+ distant_neighbor_free (referee);
+ GNUNET_assert (neighbor->referee_tail == NULL);
+ GNUNET_CONTAINER_multihashmap_remove (ctx.direct_neighbors,
+ &peer->hashPubKey, neighbor);
+ GNUNET_free (neighbor);
}
* Process dv requests.
*
* @param cls closure
- * @param sched scheduler to use
+ * @param scheduler scheduler to use
* @param server the initialized server
* @param c configuration to use
*/