2 This file is part of GNUnet.
3 (C) 2012 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 2, 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 testbed/gnunet-service-testbed.c
23 * @brief implementation of the TESTBED service
24 * @author Sree Harsha Totakura
28 #include "gnunet_service_lib.h"
29 #include "gnunet_server_lib.h"
32 #include "gnunet_testbed_service.h"
33 #include "testbed_api_hosts.h"
38 #define LOG(kind,...) \
39 GNUNET_log (kind, __VA_ARGS__)
44 #define LOG_DEBUG(...) \
45 LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
50 * The client handle associated with this context
52 struct GNUNET_SERVER_Client *client;
55 * Event mask of event to be responded in this context
60 * Our host id according to this context
67 * The message queue for sending messages to clients
72 * The message to be sent
74 struct GNUNET_MessageHeader *msg;
77 * The client to send the message to
79 struct GNUNET_SERVER_Client *client;
82 * next pointer for DLL
84 struct MessageQueue *next;
87 * prev pointer for DLL
89 struct MessageQueue *prev;
94 * The structure for identifying a shared service
99 * The name of the shared service
104 * Number of shared peers per instance of the shared service
109 * Number of peers currently sharing the service
111 uint32_t num_sharing;
118 static struct GNUNET_DISK_FileHandle *fh;
121 * The master context; generated with the first INIT message
123 static struct Context *master_context;
126 * The shutdown task handle
128 static GNUNET_SCHEDULER_TaskIdentifier shutdown_task_id;
133 static struct GNUNET_TESTBED_Host **host_list;
136 * The size of the host list
138 static uint32_t host_list_size;
141 * The message queue head
143 static struct MessageQueue *mq_head;
146 * The message queue tail
148 static struct MessageQueue *mq_tail;
151 * Current Transmit Handle; NULL if no notify transmit exists currently
153 struct GNUNET_SERVER_TransmitHandle *transmit_handle;
156 * The hashmap of shared services
158 struct GNUNET_CONTAINER_MultiHashMap *ss_map;
162 * Function called to notify a client about the connection begin ready to queue
163 * more data. "buf" will be NULL and "size" zero if the connection was closed
164 * for writing in the meantime.
167 * @param size number of bytes available in buf
168 * @param buf where the callee should write the message
169 * @return number of bytes written to buf
172 transmit_ready_notify (void *cls, size_t size, void *buf)
174 struct MessageQueue *mq_entry;
176 transmit_handle = NULL;
178 GNUNET_assert (NULL != mq_entry);
181 GNUNET_assert (ntohs (mq_entry->msg->size) <= size);
182 size = ntohs (mq_entry->msg->size);
183 memcpy (buf, mq_entry->msg, size);
184 GNUNET_free (mq_entry->msg);
185 GNUNET_CONTAINER_DLL_remove (mq_head, mq_tail, mq_entry);
186 GNUNET_free (mq_entry);
188 if (NULL != mq_entry)
190 GNUNET_SERVER_notify_transmit_ready (mq_entry->client,
191 ntohs (mq_entry->msg->size),
192 GNUNET_TIME_UNIT_FOREVER_REL,
193 &transmit_ready_notify, NULL);
199 * Queues a message in send queue for sending to the service
201 * @param client the client to whom the queued message has to be sent
202 * @param msg the message to queue
205 queue_message (struct GNUNET_SERVER_Client *client,
206 struct GNUNET_MessageHeader *msg)
208 struct MessageQueue *mq_entry;
212 type = ntohs (msg->type);
213 size = ntohs (msg->size);
214 GNUNET_assert ((GNUNET_MESSAGE_TYPE_TESTBED_INIT <= type) &&
215 (GNUNET_MESSAGE_TYPE_TESTBED_MAX > type));
216 mq_entry = GNUNET_malloc (sizeof (struct MessageQueue));
218 mq_entry->client = client;
219 LOG_DEBUG ( "Queueing message of type %u, size %u for sending\n", type,
221 GNUNET_CONTAINER_DLL_insert_tail (mq_head, mq_tail, mq_entry);
222 if (NULL == transmit_handle)
224 GNUNET_SERVER_notify_transmit_ready (client, size,
225 GNUNET_TIME_UNIT_FOREVER_REL,
226 &transmit_ready_notify, NULL);
231 * Function to add a host to the current list of known hosts
233 * @param host the host to add
234 * @return GNUNET_OK on success; GNUNET_SYSERR on failure due to host-id
238 host_list_add (struct GNUNET_TESTBED_Host *host)
242 host_id = GNUNET_TESTBED_host_get_id_ (host);
243 if (host_list_size <= host_id)
245 host_list = GNUNET_realloc (host_list,
246 sizeof (struct GNUNET_TESTBED_Host *)
248 host_list_size += (host_id + 10);
250 if (NULL != host_list[host_id])
252 LOG_DEBUG ("A host with id: %u already exists\n", host_id);
253 return GNUNET_SYSERR;
255 host_list[host_id] = host;
261 * Routes message to a host given its host_id
263 * @param host_id the id of the destination host
264 * @param msg the message to be routed
267 route_message (uint32_t host_id, const struct GNUNET_MessageHeader *msg)
279 * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_INIT messages
282 * @param client identification of the client
283 * @param message the actual message
286 handle_init (void *cls,
287 struct GNUNET_SERVER_Client *client,
288 const struct GNUNET_MessageHeader *message)
290 const struct GNUNET_TESTBED_InitMessage *msg;
291 struct GNUNET_TESTBED_Host *host;
293 if (NULL != master_context)
296 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
299 msg = (const struct GNUNET_TESTBED_InitMessage *) message;
300 master_context = GNUNET_malloc (sizeof (struct Context));
301 master_context->client = client;
302 master_context->host_id = ntohl (msg->host_id);
303 host = GNUNET_TESTBED_host_create_with_id (master_context->host_id,
305 host_list_add (host);
306 master_context->event_mask = GNUNET_ntohll (msg->event_mask);
307 GNUNET_SERVER_client_keep (client);
308 LOG_DEBUG ("Created master context with host ID: %u\n",
309 master_context->host_id);
310 GNUNET_SERVER_receive_done (client, GNUNET_OK);
315 * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST messages
318 * @param client identification of the client
319 * @param message the actual message
322 handle_add_host (void *cls,
323 struct GNUNET_SERVER_Client *client,
324 const struct GNUNET_MessageHeader *message)
326 struct GNUNET_TESTBED_Host *host;
327 const struct GNUNET_TESTBED_AddHostMessage *msg;
328 struct GNUNET_TESTBED_HostConfirmedMessage *reply;
333 uint16_t username_length;
334 uint16_t hostname_length;
337 msg = (const struct GNUNET_TESTBED_AddHostMessage *) message;
338 username_length = ntohs (msg->user_name_length);
339 username_length = (0 == username_length) ? 0 : username_length + 1;
340 username = (char *) &(msg[1]);
341 hostname = username + username_length;
342 if (ntohs (message->size) <=
343 (sizeof (struct GNUNET_TESTBED_AddHostMessage) + username_length))
346 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
349 hostname_length = ntohs (message->size)
350 - (sizeof (struct GNUNET_TESTBED_AddHostMessage) + username_length);
351 if (strlen (hostname) != hostname_length)
354 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
357 host_id = ntohl (msg->host_id);
358 LOG_DEBUG ("Received ADDHOST message\n");
359 LOG_DEBUG ("-------host id: %u\n", host_id);
360 if (NULL != hostname) LOG_DEBUG ("-------hostname: %s\n", hostname);
361 if (NULL != username) LOG_DEBUG ("-------username: %s\n", username);
362 LOG_DEBUG ("-------ssh port: %u\n", ntohs (msg->ssh_port));
363 host = GNUNET_TESTBED_host_create_with_id (host_id, hostname, username,
364 ntohs (msg->ssh_port));
365 GNUNET_SERVER_receive_done (client, GNUNET_OK);
366 if (GNUNET_OK == host_list_add (host))
368 /* We are unable to add a host */
369 emsg = "A host exists with given host-id";
370 GNUNET_TESTBED_host_destroy (host);
371 reply_size = sizeof (struct GNUNET_TESTBED_HostConfirmedMessage)
373 reply = GNUNET_malloc (reply_size);
374 reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_ADDHOSTSUCCESS);
375 reply->header.size = htons (reply_size);
376 reply->host_id = htonl (host_id);
377 memcpy (&reply[1], emsg, strlen (emsg) + 1);
378 queue_message (client, (struct GNUNET_MessageHeader *) reply);
383 * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST messages
386 * @param client identification of the client
387 * @param message the actual message
390 handle_configure_shared_service (void *cls,
391 struct GNUNET_SERVER_Client *client,
392 const struct GNUNET_MessageHeader *message)
394 struct GNUNET_TESTBED_ConfigureSharedServiceMessage *msg;
395 struct SharedService *ss;
397 struct GNUNET_HashCode hash;
399 uint16_t service_name_size;
401 msg = (struct GNUNET_TESTBED_ConfigureSharedServiceMessage *) message;
402 msg_size = ntohs (message->size);
403 if (msg_size <= sizeof (struct GNUNET_TESTBED_ConfigureSharedServiceMessage))
406 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
409 service_name_size = msg_size -
410 sizeof (struct GNUNET_TESTBED_ConfigureSharedServiceMessage);
411 service_name = (char *) &msg[1];
412 if ('\0' != service_name[service_name_size - 1])
415 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
418 LOG_DEBUG ("Received service sharing request for %s, with %d peers\n",
419 service_name, ntohl (msg->num_peers));
420 if (ntohl (msg->host_id) != master_context->host_id)
422 route_message (ntohl (msg->host_id), message);
423 GNUNET_SERVER_receive_done (client, GNUNET_OK);
426 ss = GNUNET_malloc (sizeof (struct SharedService));
427 ss->name = strdup (service_name);
428 ss->num_shared = ntohl (msg->num_peers);
429 GNUNET_CRYPTO_hash (ss->name, service_name_size, &hash);
430 GNUNET_CONTAINER_multihashmap_put (ss_map, &hash, ss,
431 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
432 GNUNET_SERVER_receive_done (client, GNUNET_OK);
437 * Iterator over hash map entries.
440 * @param key current key code
441 * @param value value in the hash map
442 * @return GNUNET_YES if we should continue to
447 ss_map_free_iterator (void *cls,
448 const struct GNUNET_HashCode * key, void *value)
450 struct SharedService *ss = value;
452 GNUNET_CONTAINER_multihashmap_remove (ss_map, key, value);
453 GNUNET_free (ss->name);
460 * Task to clean up and shutdown nicely
463 * @param tc the TaskContext from scheduler
466 shutdown_task (void *cls,
467 const struct GNUNET_SCHEDULER_TaskContext *tc)
471 shutdown_task_id = GNUNET_SCHEDULER_NO_TASK;
472 GNUNET_SCHEDULER_shutdown ();
473 LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down testbed service\n");
474 (void) GNUNET_CONTAINER_multihashmap_iterate (ss_map, &ss_map_free_iterator,
476 GNUNET_CONTAINER_multihashmap_destroy (ss_map);
479 GNUNET_DISK_file_close (fh);
482 for (host_id = 0; host_id < host_list_size; host_id++)
483 if (NULL != host_list[host_id])
484 GNUNET_TESTBED_host_destroy (host_list[host_id]);
485 GNUNET_free_non_null (master_context);
486 master_context = NULL;
491 * Callback for client disconnect
494 * @param client the client which has disconnected
497 client_disconnect_cb (void *cls, struct GNUNET_SERVER_Client *client)
499 if (NULL == master_context)
501 if (client == master_context->client)
503 LOG (GNUNET_ERROR_TYPE_DEBUG, "Master client disconnected\n");
504 GNUNET_SERVER_client_drop (client);
505 /* should not be needed as we're terminated by failure to read
506 from stdin, but if stdin fails for some reason, this shouldn't
507 hurt for now --- might need to revise this later if we ever
508 decide that master connections might be temporarily down
510 GNUNET_SCHEDULER_shutdown ();
519 * @param server the initialized server
520 * @param cfg configuration to use
523 testbed_run (void *cls,
524 struct GNUNET_SERVER_Handle *server,
525 const struct GNUNET_CONFIGURATION_Handle *cfg)
527 static const struct GNUNET_SERVER_MessageHandler message_handlers[] =
529 {&handle_init, NULL, GNUNET_MESSAGE_TYPE_TESTBED_INIT,
530 sizeof (struct GNUNET_TESTBED_InitMessage)},
531 {&handle_add_host, NULL, GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST, 0},
532 {&handle_configure_shared_service, NULL,
533 GNUNET_MESSAGE_TYPE_TESTBED_SERVICESHARE, 0},
537 GNUNET_SERVER_add_handlers (server,
539 GNUNET_SERVER_disconnect_notify (server,
540 &client_disconnect_cb,
542 ss_map = GNUNET_CONTAINER_multihashmap_create (5);
543 fh = GNUNET_DISK_get_handle_from_native (stdin);
546 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
551 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
559 * The starting point of execution
561 int main (int argc, char *const *argv)
565 GNUNET_SERVICE_run (argc,
568 GNUNET_SERVICE_OPTION_NONE,