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"
33 #include "gnunet_testbed_service.h"
34 #include "testbed_api_hosts.h"
39 #define LOG(kind,...) \
40 GNUNET_log (kind, __VA_ARGS__)
45 #define LOG_DEBUG(...) \
46 LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
51 * The client handle associated with this context
53 struct GNUNET_SERVER_Client *client;
56 * Event mask of event to be responded in this context
61 * Our host id according to this context
68 * The message queue for sending messages to clients
73 * The message to be sent
75 struct GNUNET_MessageHeader *msg;
78 * The client to send the message to
80 struct GNUNET_SERVER_Client *client;
83 * next pointer for DLL
85 struct MessageQueue *next;
88 * prev pointer for DLL
90 struct MessageQueue *prev;
95 * The structure for identifying a shared service
100 * The name of the shared service
105 * Number of shared peers per instance of the shared service
110 * Number of peers currently sharing the service
112 uint32_t num_sharing;
119 static struct GNUNET_DISK_FileHandle *fh;
122 * The master context; generated with the first INIT message
124 static struct Context *master_context;
127 * The shutdown task handle
129 static GNUNET_SCHEDULER_TaskIdentifier shutdown_task_id;
134 static struct GNUNET_TESTBED_Host **host_list;
137 * The size of the host list
139 static uint32_t host_list_size;
142 * The message queue head
144 static struct MessageQueue *mq_head;
147 * The message queue tail
149 static struct MessageQueue *mq_tail;
152 * Current Transmit Handle; NULL if no notify transmit exists currently
154 struct GNUNET_SERVER_TransmitHandle *transmit_handle;
157 * The hashmap of shared services
159 struct GNUNET_CONTAINER_MultiHashMap *ss_map;
163 * Function called to notify a client about the connection begin ready to queue
164 * more data. "buf" will be NULL and "size" zero if the connection was closed
165 * for writing in the meantime.
168 * @param size number of bytes available in buf
169 * @param buf where the callee should write the message
170 * @return number of bytes written to buf
173 transmit_ready_notify (void *cls, size_t size, void *buf)
175 struct MessageQueue *mq_entry;
177 transmit_handle = NULL;
179 GNUNET_assert (NULL != mq_entry);
182 GNUNET_assert (ntohs (mq_entry->msg->size) <= size);
183 size = ntohs (mq_entry->msg->size);
184 memcpy (buf, mq_entry->msg, size);
185 GNUNET_free (mq_entry->msg);
186 GNUNET_CONTAINER_DLL_remove (mq_head, mq_tail, mq_entry);
187 GNUNET_free (mq_entry);
189 if (NULL != mq_entry)
191 GNUNET_SERVER_notify_transmit_ready (mq_entry->client,
192 ntohs (mq_entry->msg->size),
193 GNUNET_TIME_UNIT_FOREVER_REL,
194 &transmit_ready_notify, NULL);
200 * Queues a message in send queue for sending to the service
202 * @param client the client to whom the queued message has to be sent
203 * @param msg the message to queue
206 queue_message (struct GNUNET_SERVER_Client *client,
207 struct GNUNET_MessageHeader *msg)
209 struct MessageQueue *mq_entry;
213 type = ntohs (msg->type);
214 size = ntohs (msg->size);
215 GNUNET_assert ((GNUNET_MESSAGE_TYPE_TESTBED_INIT <= type) &&
216 (GNUNET_MESSAGE_TYPE_TESTBED_MAX > type));
217 mq_entry = GNUNET_malloc (sizeof (struct MessageQueue));
219 mq_entry->client = client;
220 LOG_DEBUG ( "Queueing message of type %u, size %u for sending\n", type,
222 GNUNET_CONTAINER_DLL_insert_tail (mq_head, mq_tail, mq_entry);
223 if (NULL == transmit_handle)
225 GNUNET_SERVER_notify_transmit_ready (client, size,
226 GNUNET_TIME_UNIT_FOREVER_REL,
227 &transmit_ready_notify, NULL);
232 * Function to add a host to the current list of known hosts
234 * @param host the host to add
235 * @return GNUNET_OK on success; GNUNET_SYSERR on failure due to host-id
239 host_list_add (struct GNUNET_TESTBED_Host *host)
243 host_id = GNUNET_TESTBED_host_get_id_ (host);
244 if (host_list_size <= host_id)
246 host_list = GNUNET_realloc (host_list,
247 sizeof (struct GNUNET_TESTBED_Host *)
249 host_list_size += (host_id + 10);
251 if (NULL != host_list[host_id])
253 LOG_DEBUG ("A host with id: %u already exists\n", host_id);
254 return GNUNET_SYSERR;
256 host_list[host_id] = host;
262 * Routes message to a host given its host_id
264 * @param host_id the id of the destination host
265 * @param msg the message to be routed
268 route_message (uint32_t host_id, const struct GNUNET_MessageHeader *msg)
275 * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_INIT messages
278 * @param client identification of the client
279 * @param message the actual message
282 handle_init (void *cls,
283 struct GNUNET_SERVER_Client *client,
284 const struct GNUNET_MessageHeader *message)
286 const struct GNUNET_TESTBED_InitMessage *msg;
287 struct GNUNET_TESTBED_Host *host;
289 if (NULL != master_context)
292 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
295 msg = (const struct GNUNET_TESTBED_InitMessage *) message;
296 master_context = GNUNET_malloc (sizeof (struct Context));
297 master_context->client = client;
298 master_context->host_id = ntohl (msg->host_id);
299 host = GNUNET_TESTBED_host_create_with_id (master_context->host_id,
301 host_list_add (host);
302 master_context->event_mask = GNUNET_ntohll (msg->event_mask);
303 GNUNET_SERVER_client_keep (client);
304 LOG_DEBUG ("Created master context with host ID: %u\n",
305 master_context->host_id);
306 GNUNET_SERVER_receive_done (client, GNUNET_OK);
311 * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST messages
314 * @param client identification of the client
315 * @param message the actual message
318 handle_add_host (void *cls,
319 struct GNUNET_SERVER_Client *client,
320 const struct GNUNET_MessageHeader *message)
322 struct GNUNET_TESTBED_Host *host;
323 const struct GNUNET_TESTBED_AddHostMessage *msg;
324 struct GNUNET_TESTBED_HostConfirmedMessage *reply;
329 uint16_t username_length;
330 uint16_t hostname_length;
333 msg = (const struct GNUNET_TESTBED_AddHostMessage *) message;
334 username_length = ntohs (msg->user_name_length);
335 username_length = (0 == username_length) ? 0 : username_length + 1;
336 username = (char *) &(msg[1]);
337 hostname = username + username_length;
338 if (ntohs (message->size) <=
339 (sizeof (struct GNUNET_TESTBED_AddHostMessage) + username_length))
342 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
345 hostname_length = ntohs (message->size)
346 - (sizeof (struct GNUNET_TESTBED_AddHostMessage) + username_length);
347 if (strlen (hostname) != hostname_length)
350 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
353 host_id = ntohl (msg->host_id);
354 LOG_DEBUG ("Received ADDHOST message\n");
355 LOG_DEBUG ("-------host id: %u\n", host_id);
356 if (NULL != hostname) LOG_DEBUG ("-------hostname: %s\n", hostname);
357 if (NULL != username) LOG_DEBUG ("-------username: %s\n", username);
358 LOG_DEBUG ("-------ssh port: %u\n", ntohs (msg->ssh_port));
359 host = GNUNET_TESTBED_host_create_with_id (host_id, hostname, username,
360 ntohs (msg->ssh_port));
361 GNUNET_SERVER_receive_done (client, GNUNET_OK);
362 reply_size = sizeof (struct GNUNET_TESTBED_HostConfirmedMessage);
363 if (GNUNET_OK != host_list_add (host))
365 /* We are unable to add a host */
366 emsg = "A host exists with given host-id";
367 LOG_DEBUG ("%s: %u", emsg, host_id);
368 GNUNET_TESTBED_host_destroy (host);
369 reply_size += strlen (emsg) + 1;
370 reply = GNUNET_malloc (reply_size);
371 memcpy (&reply[1], emsg, strlen (emsg) + 1);
374 reply = GNUNET_malloc (reply_size);
375 reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_ADDHOSTCONFIRM);
376 reply->header.size = htons (reply_size);
377 reply->host_id = htonl (host_id);
378 queue_message (client, (struct GNUNET_MessageHeader *) reply);
383 * Iterator over hash map entries.
386 * @param key current key code
387 * @param value value in the hash map
388 * @return GNUNET_YES if we should continue to
392 int ss_exists_iterator (void *cls,
393 const struct GNUNET_HashCode * key,
396 struct SharedService *queried_ss = cls;
397 struct SharedService *ss = value;
399 if (0 == strcmp (ss->name, queried_ss->name))
406 * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST messages
409 * @param client identification of the client
410 * @param message the actual message
413 handle_configure_shared_service (void *cls,
414 struct GNUNET_SERVER_Client *client,
415 const struct GNUNET_MessageHeader *message)
417 const struct GNUNET_TESTBED_ConfigureSharedServiceMessage *msg;
418 struct SharedService *ss;
420 struct GNUNET_HashCode hash;
422 uint16_t service_name_size;
424 msg = (const struct GNUNET_TESTBED_ConfigureSharedServiceMessage *) message;
425 msg_size = ntohs (message->size);
426 if (msg_size <= sizeof (struct GNUNET_TESTBED_ConfigureSharedServiceMessage))
429 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
432 service_name_size = msg_size -
433 sizeof (struct GNUNET_TESTBED_ConfigureSharedServiceMessage);
434 service_name = (char *) &msg[1];
435 if ('\0' != service_name[service_name_size - 1])
438 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
441 LOG_DEBUG ("Received service sharing request for %s, with %d peers\n",
442 service_name, ntohl (msg->num_peers));
443 if (ntohl (msg->host_id) != master_context->host_id)
445 route_message (ntohl (msg->host_id), message);
446 GNUNET_SERVER_receive_done (client, GNUNET_OK);
449 GNUNET_SERVER_receive_done (client, GNUNET_OK);
450 ss = GNUNET_malloc (sizeof (struct SharedService));
451 ss->name = strdup (service_name);
452 ss->num_shared = ntohl (msg->num_peers);
453 GNUNET_CRYPTO_hash (ss->name, service_name_size, &hash);
455 GNUNET_CONTAINER_multihashmap_get_multiple (ss_map, &hash,
456 &ss_exists_iterator, ss))
458 LOG (GNUNET_ERROR_TYPE_WARNING,
459 "Service %s already configured as a shared service. "
460 "Ignoring service sharing request \n", ss->name);
461 GNUNET_free (ss->name);
465 GNUNET_CONTAINER_multihashmap_put (ss_map, &hash, ss,
466 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
471 * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_LCONTROLLERS message
474 * @param client identification of the client
475 * @param message the actual message
478 handle_link_controllers (void *cls,
479 struct GNUNET_SERVER_Client *client,
480 const struct GNUNET_MessageHeader *message)
482 const struct GNUNET_TESTBED_ControllerLinkMessage *msg;
483 struct GNUNET_CONFIGURATION_Handle *cfg;
487 uint32_t delegated_host_id;
488 uint32_t slave_host_id;
491 msize = ntohs (message->size);
492 if (sizeof (struct GNUNET_TESTBED_ControllerLinkMessage) >= msize)
495 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
498 msg = (const struct GNUNET_TESTBED_ControllerLinkMessage *) message;
499 delegated_host_id = ntohl (msg->delegated_host_id);
500 if ((delegated_host_id >= host_list_size) ||
501 (NULL == host_list[delegated_host_id]))
503 GNUNET_break (0); /* Delegated host not registered with us */
504 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
507 slave_host_id = ntohl (msg->slave_host_id);
508 if ((slave_host_id >= host_list_size) || (NULL == host_list[slave_host_id]))
510 GNUNET_break (0); /* Slave host not registered with us */
511 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
514 config_size = ntohs (msg->config_size);
515 config = GNUNET_malloc (config_size);
516 dest_size = (uLongf) config_size;
517 msize -= sizeof (struct GNUNET_TESTBED_ControllerLinkMessage);
518 if (Z_OK != uncompress ((Bytef *) config, &dest_size,
519 (const Bytef *) &msg[1], (uLong) msize))
521 GNUNET_break (0); /* Compression error */
522 GNUNET_free (config);
523 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
526 GNUNET_assert (config_size == dest_size);
527 cfg = GNUNET_CONFIGURATION_create ();
528 if (GNUNET_OK != GNUNET_CONFIGURATION_deserialize (cfg, config, config_size,
531 GNUNET_break (0); /* Configuration parsing error */
532 GNUNET_break (config);
533 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
536 GNUNET_free (config);
537 /* FIXME: start the slave controller if is_subordinate is yes; else store it
540 GNUNET_CONFIGURATION_destroy (cfg);
545 * Iterator over hash map entries.
548 * @param key current key code
549 * @param value value in the hash map
550 * @return GNUNET_YES if we should continue to
555 ss_map_free_iterator (void *cls,
556 const struct GNUNET_HashCode * key, void *value)
558 struct SharedService *ss = value;
560 GNUNET_assert (GNUNET_YES ==
561 GNUNET_CONTAINER_multihashmap_remove (ss_map, key, value));
562 GNUNET_free (ss->name);
569 * Task to clean up and shutdown nicely
572 * @param tc the TaskContext from scheduler
575 shutdown_task (void *cls,
576 const struct GNUNET_SCHEDULER_TaskContext *tc)
580 shutdown_task_id = GNUNET_SCHEDULER_NO_TASK;
581 GNUNET_SCHEDULER_shutdown ();
582 LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down testbed service\n");
583 (void) GNUNET_CONTAINER_multihashmap_iterate (ss_map, &ss_map_free_iterator,
585 GNUNET_CONTAINER_multihashmap_destroy (ss_map);
586 /* Clear host array */
589 GNUNET_DISK_file_close (fh);
592 for (host_id = 0; host_id < host_list_size; host_id++)
593 if (NULL != host_list[host_id])
594 GNUNET_TESTBED_host_destroy (host_list[host_id]);
595 GNUNET_free_non_null (host_list);
596 GNUNET_free_non_null (master_context);
597 master_context = NULL;
602 * Callback for client disconnect
605 * @param client the client which has disconnected
608 client_disconnect_cb (void *cls, struct GNUNET_SERVER_Client *client)
610 if (NULL == master_context)
612 if (client == master_context->client)
614 LOG (GNUNET_ERROR_TYPE_DEBUG, "Master client disconnected\n");
615 GNUNET_SERVER_client_drop (client);
616 /* should not be needed as we're terminated by failure to read
617 from stdin, but if stdin fails for some reason, this shouldn't
618 hurt for now --- might need to revise this later if we ever
619 decide that master connections might be temporarily down
621 GNUNET_SCHEDULER_shutdown ();
630 * @param server the initialized server
631 * @param cfg configuration to use
634 testbed_run (void *cls,
635 struct GNUNET_SERVER_Handle *server,
636 const struct GNUNET_CONFIGURATION_Handle *cfg)
638 static const struct GNUNET_SERVER_MessageHandler message_handlers[] =
640 {&handle_init, NULL, GNUNET_MESSAGE_TYPE_TESTBED_INIT,
641 sizeof (struct GNUNET_TESTBED_InitMessage)},
642 {&handle_add_host, NULL, GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST, 0},
643 {&handle_configure_shared_service, NULL,
644 GNUNET_MESSAGE_TYPE_TESTBED_SERVICESHARE, 0},
645 {&handle_link_controllers, NULL,
646 GNUNET_MESSAGE_TYPE_TESTBED_LCONTROLLERS, 0},
650 GNUNET_SERVER_add_handlers (server,
652 GNUNET_SERVER_disconnect_notify (server,
653 &client_disconnect_cb,
655 ss_map = GNUNET_CONTAINER_multihashmap_create (5);
656 fh = GNUNET_DISK_get_handle_from_native (stdin);
659 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
664 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
672 * The starting point of execution
674 int main (int argc, char *const *argv)
678 GNUNET_SERVICE_run (argc,
681 GNUNET_SERVICE_OPTION_NONE,