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)
274 * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_INIT messages
277 * @param client identification of the client
278 * @param message the actual message
281 handle_init (void *cls,
282 struct GNUNET_SERVER_Client *client,
283 const struct GNUNET_MessageHeader *message)
285 const struct GNUNET_TESTBED_InitMessage *msg;
286 struct GNUNET_TESTBED_Host *host;
288 if (NULL != master_context)
291 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
294 msg = (const struct GNUNET_TESTBED_InitMessage *) message;
295 master_context = GNUNET_malloc (sizeof (struct Context));
296 master_context->client = client;
297 master_context->host_id = ntohl (msg->host_id);
298 host = GNUNET_TESTBED_host_create_with_id (master_context->host_id,
300 host_list_add (host);
301 master_context->event_mask = GNUNET_ntohll (msg->event_mask);
302 GNUNET_SERVER_client_keep (client);
303 LOG_DEBUG ("Created master context with host ID: %u\n",
304 master_context->host_id);
305 GNUNET_SERVER_receive_done (client, GNUNET_OK);
310 * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST messages
313 * @param client identification of the client
314 * @param message the actual message
317 handle_add_host (void *cls,
318 struct GNUNET_SERVER_Client *client,
319 const struct GNUNET_MessageHeader *message)
321 struct GNUNET_TESTBED_Host *host;
322 const struct GNUNET_TESTBED_AddHostMessage *msg;
323 struct GNUNET_TESTBED_HostConfirmedMessage *reply;
328 uint16_t username_length;
329 uint16_t hostname_length;
332 msg = (const struct GNUNET_TESTBED_AddHostMessage *) message;
333 username_length = ntohs (msg->user_name_length);
334 username_length = (0 == username_length) ? 0 : username_length + 1;
335 username = (char *) &(msg[1]);
336 hostname = username + username_length;
337 if (ntohs (message->size) <=
338 (sizeof (struct GNUNET_TESTBED_AddHostMessage) + username_length))
341 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
344 hostname_length = ntohs (message->size)
345 - (sizeof (struct GNUNET_TESTBED_AddHostMessage) + username_length);
346 if (strlen (hostname) != hostname_length)
349 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
352 host_id = ntohl (msg->host_id);
353 LOG_DEBUG ("Received ADDHOST message\n");
354 LOG_DEBUG ("-------host id: %u\n", host_id);
355 if (NULL != hostname) LOG_DEBUG ("-------hostname: %s\n", hostname);
356 if (NULL != username) LOG_DEBUG ("-------username: %s\n", username);
357 LOG_DEBUG ("-------ssh port: %u\n", ntohs (msg->ssh_port));
358 host = GNUNET_TESTBED_host_create_with_id (host_id, hostname, username,
359 ntohs (msg->ssh_port));
360 GNUNET_SERVER_receive_done (client, GNUNET_OK);
361 reply_size = sizeof (struct GNUNET_TESTBED_HostConfirmedMessage);
362 if (GNUNET_OK != host_list_add (host))
364 /* We are unable to add a host */
365 emsg = "A host exists with given host-id";
366 LOG_DEBUG ("%s: %u", emsg, host_id);
367 GNUNET_TESTBED_host_destroy (host);
368 reply_size += strlen (emsg) + 1;
369 reply = GNUNET_malloc (reply_size);
370 memcpy (&reply[1], emsg, strlen (emsg) + 1);
373 reply = GNUNET_malloc (reply_size);
374 reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_ADDHOSTCONFIRM);
375 reply->header.size = htons (reply_size);
376 reply->host_id = htonl (host_id);
377 queue_message (client, (struct GNUNET_MessageHeader *) reply);
382 * Iterator over hash map entries.
385 * @param key current key code
386 * @param value value in the hash map
387 * @return GNUNET_YES if we should continue to
391 int ss_exists_iterator (void *cls,
392 const struct GNUNET_HashCode * key,
395 struct SharedService *queried_ss = cls;
396 struct SharedService *ss = value;
398 if (0 == strcmp (ss->name, queried_ss->name))
405 * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST messages
408 * @param client identification of the client
409 * @param message the actual message
412 handle_configure_shared_service (void *cls,
413 struct GNUNET_SERVER_Client *client,
414 const struct GNUNET_MessageHeader *message)
416 struct GNUNET_TESTBED_ConfigureSharedServiceMessage *msg;
417 struct SharedService *ss;
419 struct GNUNET_HashCode hash;
421 uint16_t service_name_size;
423 msg = (struct GNUNET_TESTBED_ConfigureSharedServiceMessage *) message;
424 msg_size = ntohs (message->size);
425 if (msg_size <= sizeof (struct GNUNET_TESTBED_ConfigureSharedServiceMessage))
428 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
431 service_name_size = msg_size -
432 sizeof (struct GNUNET_TESTBED_ConfigureSharedServiceMessage);
433 service_name = (char *) &msg[1];
434 if ('\0' != service_name[service_name_size - 1])
437 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
440 LOG_DEBUG ("Received service sharing request for %s, with %d peers\n",
441 service_name, ntohl (msg->num_peers));
442 if (ntohl (msg->host_id) != master_context->host_id)
444 route_message (ntohl (msg->host_id), message);
445 GNUNET_SERVER_receive_done (client, GNUNET_OK);
448 GNUNET_SERVER_receive_done (client, GNUNET_OK);
449 ss = GNUNET_malloc (sizeof (struct SharedService));
450 ss->name = strdup (service_name);
451 ss->num_shared = ntohl (msg->num_peers);
452 GNUNET_CRYPTO_hash (ss->name, service_name_size, &hash);
454 GNUNET_CONTAINER_multihashmap_get_multiple (ss_map, &hash,
455 &ss_exists_iterator, ss))
457 LOG (GNUNET_ERROR_TYPE_WARNING,
458 "Service %s already configured as a shared service. "
459 "Ignoring service sharing request \n", ss->name);
460 GNUNET_free (ss->name);
464 GNUNET_CONTAINER_multihashmap_put (ss_map, &hash, ss,
465 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
470 * Iterator over hash map entries.
473 * @param key current key code
474 * @param value value in the hash map
475 * @return GNUNET_YES if we should continue to
480 ss_map_free_iterator (void *cls,
481 const struct GNUNET_HashCode * key, void *value)
483 struct SharedService *ss = value;
485 GNUNET_CONTAINER_multihashmap_remove (ss_map, key, value);
486 GNUNET_free (ss->name);
493 * Task to clean up and shutdown nicely
496 * @param tc the TaskContext from scheduler
499 shutdown_task (void *cls,
500 const struct GNUNET_SCHEDULER_TaskContext *tc)
504 shutdown_task_id = GNUNET_SCHEDULER_NO_TASK;
505 GNUNET_SCHEDULER_shutdown ();
506 LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down testbed service\n");
507 (void) GNUNET_CONTAINER_multihashmap_iterate (ss_map, &ss_map_free_iterator,
509 GNUNET_CONTAINER_multihashmap_destroy (ss_map);
510 /* Clear host array */
513 GNUNET_DISK_file_close (fh);
516 for (host_id = 0; host_id < host_list_size; host_id++)
517 if (NULL != host_list[host_id])
518 GNUNET_TESTBED_host_destroy (host_list[host_id]);
519 GNUNET_free_non_null (host_list);
520 GNUNET_free_non_null (master_context);
521 master_context = NULL;
526 * Callback for client disconnect
529 * @param client the client which has disconnected
532 client_disconnect_cb (void *cls, struct GNUNET_SERVER_Client *client)
534 if (NULL == master_context)
536 if (client == master_context->client)
538 LOG (GNUNET_ERROR_TYPE_DEBUG, "Master client disconnected\n");
539 GNUNET_SERVER_client_drop (client);
540 /* should not be needed as we're terminated by failure to read
541 from stdin, but if stdin fails for some reason, this shouldn't
542 hurt for now --- might need to revise this later if we ever
543 decide that master connections might be temporarily down
545 GNUNET_SCHEDULER_shutdown ();
554 * @param server the initialized server
555 * @param cfg configuration to use
558 testbed_run (void *cls,
559 struct GNUNET_SERVER_Handle *server,
560 const struct GNUNET_CONFIGURATION_Handle *cfg)
562 static const struct GNUNET_SERVER_MessageHandler message_handlers[] =
564 {&handle_init, NULL, GNUNET_MESSAGE_TYPE_TESTBED_INIT,
565 sizeof (struct GNUNET_TESTBED_InitMessage)},
566 {&handle_add_host, NULL, GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST, 0},
567 {&handle_configure_shared_service, NULL,
568 GNUNET_MESSAGE_TYPE_TESTBED_SERVICESHARE, 0},
572 GNUNET_SERVER_add_handlers (server,
574 GNUNET_SERVER_disconnect_notify (server,
575 &client_disconnect_cb,
577 ss_map = GNUNET_CONTAINER_multihashmap_create (5);
578 fh = GNUNET_DISK_get_handle_from_native (stdin);
581 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
586 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
594 * The starting point of execution
596 int main (int argc, char *const *argv)
600 GNUNET_SERVICE_run (argc,
603 GNUNET_SERVICE_OPTION_NONE,