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;
97 static struct GNUNET_DISK_FileHandle *fh;
100 * The master context; generated with the first INIT message
102 static struct Context *master_context;
105 * The shutdown task handle
107 static GNUNET_SCHEDULER_TaskIdentifier shutdown_task_id;
112 static struct GNUNET_TESTBED_Host **host_list;
115 * The size of the host list
117 static uint32_t host_list_size;
120 * The message queue head
122 static struct MessageQueue *mq_head;
125 * The message queue tail
127 static struct MessageQueue *mq_tail;
130 * Current Transmit Handle; NULL if no notify transmit exists currently
132 struct GNUNET_SERVER_TransmitHandle *transmit_handle;
136 * Function called to notify a client about the connection begin ready to queue
137 * more data. "buf" will be NULL and "size" zero if the connection was closed
138 * for writing in the meantime.
141 * @param size number of bytes available in buf
142 * @param buf where the callee should write the message
143 * @return number of bytes written to buf
146 transmit_ready_notify (void *cls, size_t size, void *buf)
148 struct MessageQueue *mq_entry;
150 transmit_handle = NULL;
152 GNUNET_assert (NULL != mq_entry);
155 GNUNET_assert (ntohs (mq_entry->msg->size) <= size);
156 size = ntohs (mq_entry->msg->size);
157 memcpy (buf, mq_entry->msg, size);
158 GNUNET_free (mq_entry->msg);
159 GNUNET_CONTAINER_DLL_remove (mq_head, mq_tail, mq_entry);
160 GNUNET_free (mq_entry);
162 if (NULL != mq_entry)
164 GNUNET_SERVER_notify_transmit_ready (mq_entry->client,
165 ntohs (mq_entry->msg->size),
166 GNUNET_TIME_UNIT_FOREVER_REL,
167 &transmit_ready_notify, NULL);
173 * Queues a message in send queue for sending to the service
175 * @param controller the handle to the controller
176 * @param msg the message to queue
179 queue_message (struct GNUNET_SERVER_Client *client,
180 struct GNUNET_MessageHeader *msg)
182 struct MessageQueue *mq_entry;
186 type = ntohs (msg->type);
187 size = ntohs (msg->size);
188 GNUNET_assert ((GNUNET_MESSAGE_TYPE_TESTBED_INIT <= type) &&
189 (GNUNET_MESSAGE_TYPE_TESTBED_MAX > type));
190 mq_entry = GNUNET_malloc (sizeof (struct MessageQueue));
192 mq_entry->client = client;
193 LOG_DEBUG ( "Queueing message of type %u, size %u for sending\n", type,
195 GNUNET_CONTAINER_DLL_insert_tail (mq_head, mq_tail, mq_entry);
196 if (NULL == transmit_handle)
198 GNUNET_SERVER_notify_transmit_ready (client, size,
199 GNUNET_TIME_UNIT_FOREVER_REL,
200 &transmit_ready_notify, NULL);
205 * Function to add a host to the current list of known hosts
207 * @param host the host to add
208 * @return GNUNET_OK on success; GNUNET_SYSERR on failure due to host-id
212 host_list_add (struct GNUNET_TESTBED_Host *host)
216 host_id = GNUNET_TESTBED_host_get_id_ (host);
217 if (host_list_size <= host_id)
219 host_list = GNUNET_realloc (host_list,
220 sizeof (struct GNUNET_TESTBED_Host *)
222 host_list_size += (host_id + 10);
224 if (NULL != host_list[host_id])
226 LOG_DEBUG ("A host with id: %u already exists\n", host_id);
227 return GNUNET_SYSERR;
229 host_list[host_id] = host;
235 * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_INIT messages
238 * @param client identification of the client
239 * @param message the actual message
242 handle_init (void *cls,
243 struct GNUNET_SERVER_Client *client,
244 const struct GNUNET_MessageHeader *message)
246 const struct GNUNET_TESTBED_InitMessage *msg;
247 struct GNUNET_TESTBED_Host *host;
249 if (NULL != master_context)
252 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
255 msg = (const struct GNUNET_TESTBED_InitMessage *) message;
256 master_context = GNUNET_malloc (sizeof (struct Context));
257 master_context->client = client;
258 master_context->host_id = ntohl (msg->host_id);
259 host = GNUNET_TESTBED_host_create_with_id (master_context->host_id,
261 host_list_add (host);
262 master_context->event_mask = GNUNET_ntohll (msg->event_mask);
263 GNUNET_SERVER_client_keep (client);
264 LOG_DEBUG ("Created master context with host ID: %u\n",
265 master_context->host_id);
266 GNUNET_SERVER_receive_done (client, GNUNET_OK);
271 * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST messages
274 * @param client identification of the client
275 * @param message the actual message
278 handle_addhost (void *cls,
279 struct GNUNET_SERVER_Client *client,
280 const struct GNUNET_MessageHeader *message)
282 struct GNUNET_TESTBED_Host *host;
283 const struct GNUNET_TESTBED_AddHostMessage *msg;
284 struct GNUNET_TESTBED_HostConfirmedMessage *reply;
289 uint16_t username_length;
290 uint16_t hostname_length;
293 msg = (const struct GNUNET_TESTBED_AddHostMessage *) message;
294 username_length = ntohs (msg->user_name_length);
295 username_length = (0 == username_length) ? 0 : username_length + 1;
296 username = (char *) &(msg[1]);
297 hostname = username + username_length;
298 if (ntohs (message->size) <=
299 (sizeof (struct GNUNET_TESTBED_AddHostMessage) + username_length))
302 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
305 hostname_length = ntohs (message->size)
306 - (sizeof (struct GNUNET_TESTBED_AddHostMessage) + username_length);
307 if (strlen (hostname) != hostname_length)
310 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
313 host_id = ntohl (msg->host_id);
314 LOG_DEBUG ("Received ADDHOST message\n");
315 LOG_DEBUG ("-------host id: %u\n", host_id);
316 if (NULL != hostname) LOG_DEBUG ("-------hostname: %s\n", hostname);
317 if (NULL != username) LOG_DEBUG ("-------username: %s\n", username);
318 LOG_DEBUG ("-------ssh port: %u\n", ntohs (msg->ssh_port));
319 host = GNUNET_TESTBED_host_create_with_id (host_id, hostname, username,
320 ntohs (msg->ssh_port));
321 GNUNET_SERVER_receive_done (client, GNUNET_OK);
322 if (GNUNET_OK == host_list_add (host))
324 /* We are unable to add a host */
325 emsg = "A host exists with given host-id";
326 GNUNET_TESTBED_host_destroy (host);
327 reply_size = sizeof (struct GNUNET_TESTBED_HostConfirmedMessage)
329 reply = GNUNET_malloc (reply_size);
330 reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_ADDHOSTSUCCESS);
331 reply->header.size = htons (reply_size);
332 reply->host_id = htonl (host_id);
333 memcpy (&reply[1], emsg, strlen (emsg) + 1);
334 queue_message (client, (struct GNUNET_MessageHeader *) reply);
339 * Task to clean up and shutdown nicely
342 * @param tc the TaskContext from scheduler
345 shutdown_task (void *cls,
346 const struct GNUNET_SCHEDULER_TaskContext *tc)
350 shutdown_task_id = GNUNET_SCHEDULER_NO_TASK;
351 GNUNET_SCHEDULER_shutdown ();
352 LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down testbed service\n");
355 GNUNET_DISK_file_close (fh);
358 for (host_id = 0; host_id < host_list_size; host_id++)
359 if (NULL != host_list[host_id])
360 GNUNET_TESTBED_host_destroy (host_list[host_id]);
361 GNUNET_free_non_null (master_context);
362 master_context = NULL;
367 * Callback for client disconnect
370 * @param client the client which has disconnected
373 client_disconnect_cb (void *cls, struct GNUNET_SERVER_Client *client)
375 if (NULL == master_context)
377 if (client == master_context->client)
379 LOG (GNUNET_ERROR_TYPE_DEBUG, "Master client disconnected\n");
380 GNUNET_SERVER_client_drop (client);
381 /* should not be needed as we're terminated by failure to read
382 from stdin, but if stdin fails for some reason, this shouldn't
383 hurt for now --- might need to revise this later if we ever
384 decide that master connections might be temporarily down
386 GNUNET_SCHEDULER_shutdown ();
395 * @param server the initialized server
396 * @param cfg configuration to use
399 testbed_run (void *cls,
400 struct GNUNET_SERVER_Handle *server,
401 const struct GNUNET_CONFIGURATION_Handle *cfg)
403 static const struct GNUNET_SERVER_MessageHandler message_handlers[] =
405 {&handle_init, NULL, GNUNET_MESSAGE_TYPE_TESTBED_INIT,
406 sizeof (struct GNUNET_TESTBED_InitMessage)},
407 {&handle_addhost, NULL, GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST, 0},
411 GNUNET_SERVER_add_handlers (server,
413 GNUNET_SERVER_disconnect_notify (server,
414 &client_disconnect_cb,
416 fh = GNUNET_DISK_get_handle_from_native (stdin);
419 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
424 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
432 * The starting point of execution
434 int main (int argc, char *const *argv)
438 GNUNET_SERVICE_run (argc,
441 GNUNET_SERVICE_OPTION_NONE,