- rename PEER_GET_CONFIGURATION to PEER_GET_INFORMATION
[oweals/gnunet.git] / src / testbed / gnunet-service-testbed.c
1 /*
2   This file is part of GNUnet.
3   (C) 2008--2013 Christian Grothoff (and other contributing authors)
4
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.
9
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.
14
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.
19 */
20
21 /**
22  * @file testbed/gnunet-service-testbed.c
23  * @brief implementation of the TESTBED service
24  * @author Sree Harsha Totakura
25  */
26
27 #include "gnunet-service-testbed.h"
28
29
30 /***********/
31 /* Globals */
32 /***********/
33
34 /**
35  * Our configuration
36  */
37 struct GNUNET_CONFIGURATION_Handle *our_config;
38
39 /**
40  * The master context; generated with the first INIT message
41  */
42 struct Context *GST_context;
43
44 /**
45  * Array of hosts
46  */
47 struct GNUNET_TESTBED_Host **GST_host_list;
48
49 /**
50  * DLL head for forwarded operation contexts
51  */
52 struct ForwardedOperationContext *fopcq_head;
53
54 /**
55  * DLL tail for forwarded operation contexts
56  */
57 struct ForwardedOperationContext *fopcq_tail;
58
59 /**
60  * Operation queue for open file descriptors
61  */
62 struct OperationQueue *GST_opq_openfds;
63
64 /**
65  * Timeout for operations which may take some time
66  */
67 const struct GNUNET_TIME_Relative GST_timeout;
68
69 /**
70  * The size of the host list
71  */
72 unsigned int GST_host_list_size;
73
74 /**
75  * The size of the peer list
76  */
77 unsigned int GST_peer_list_size;
78
79
80 /***********************************/
81 /* Local definitions and variables */
82 /***********************************/
83
84 /**
85  * The message queue for sending messages to clients
86  */
87 struct MessageQueue
88 {
89   /**
90    * The message to be sent
91    */
92   struct GNUNET_MessageHeader *msg;
93
94   /**
95    * The client to send the message to
96    */
97   struct GNUNET_SERVER_Client *client;
98
99   /**
100    * next pointer for DLL
101    */
102   struct MessageQueue *next;
103
104   /**
105    * prev pointer for DLL
106    */
107   struct MessageQueue *prev;
108 };
109
110 /**
111  * Our hostname; we give this to all the peers we start
112  */
113 static char *hostname;
114
115 /**
116  * Current Transmit Handle; NULL if no notify transmit exists currently
117  */
118 static struct GNUNET_SERVER_TransmitHandle *transmit_handle;
119
120 /**
121  * The message queue head
122  */
123 static struct MessageQueue *mq_head;
124
125 /**
126  * The message queue tail
127  */
128 static struct MessageQueue *mq_tail;
129
130
131 /**
132  * The shutdown task handle
133  */
134 static GNUNET_SCHEDULER_TaskIdentifier shutdown_task_id;
135
136
137 /**
138  * Function called to notify a client about the connection begin ready to queue
139  * more data.  "buf" will be NULL and "size" zero if the connection was closed
140  * for writing in the meantime.
141  *
142  * @param cls NULL
143  * @param size number of bytes available in buf
144  * @param buf where the callee should write the message
145  * @return number of bytes written to buf
146  */
147 static size_t
148 transmit_ready_notify (void *cls, size_t size, void *buf)
149 {
150   struct MessageQueue *mq_entry;
151
152   transmit_handle = NULL;
153   mq_entry = mq_head;
154   GNUNET_assert (NULL != mq_entry);
155   if (0 == size)
156     return 0;
157   GNUNET_assert (ntohs (mq_entry->msg->size) <= size);
158   size = ntohs (mq_entry->msg->size);
159   memcpy (buf, mq_entry->msg, size);
160   GNUNET_free (mq_entry->msg);
161   GNUNET_SERVER_client_drop (mq_entry->client);
162   GNUNET_CONTAINER_DLL_remove (mq_head, mq_tail, mq_entry);
163   GNUNET_free (mq_entry);
164   mq_entry = mq_head;
165   if (NULL != mq_entry)
166     transmit_handle =
167         GNUNET_SERVER_notify_transmit_ready (mq_entry->client,
168                                              ntohs (mq_entry->msg->size),
169                                              GNUNET_TIME_UNIT_FOREVER_REL,
170                                              &transmit_ready_notify, NULL);
171   return size;
172 }
173
174
175 /**
176  * Queues a message in send queue for sending to the service
177  *
178  * @param client the client to whom the queued message has to be sent
179  * @param msg the message to queue
180  */
181 void
182 GST_queue_message (struct GNUNET_SERVER_Client *client,
183                    struct GNUNET_MessageHeader *msg)
184 {
185   struct MessageQueue *mq_entry;
186   uint16_t type;
187   uint16_t size;
188
189   type = ntohs (msg->type);
190   size = ntohs (msg->size);
191   GNUNET_assert ((GNUNET_MESSAGE_TYPE_TESTBED_INIT <= type) &&
192                  (GNUNET_MESSAGE_TYPE_TESTBED_MAX > type));
193   mq_entry = GNUNET_malloc (sizeof (struct MessageQueue));
194   mq_entry->msg = msg;
195   mq_entry->client = client;
196   GNUNET_SERVER_client_keep (client);
197   LOG_DEBUG ("Queueing message of type %u, size %u for sending\n", type,
198              ntohs (msg->size));
199   GNUNET_CONTAINER_DLL_insert_tail (mq_head, mq_tail, mq_entry);
200   if (NULL == transmit_handle)
201     transmit_handle =
202         GNUNET_SERVER_notify_transmit_ready (client, size,
203                                              GNUNET_TIME_UNIT_FOREVER_REL,
204                                              &transmit_ready_notify, NULL);
205 }
206
207
208 /**
209  * Function to add a host to the current list of known hosts
210  *
211  * @param host the host to add
212  * @return GNUNET_OK on success; GNUNET_SYSERR on failure due to host-id
213  *           already in use
214  */
215 static int
216 host_list_add (struct GNUNET_TESTBED_Host *host)
217 {
218   uint32_t host_id;
219
220   host_id = GNUNET_TESTBED_host_get_id_ (host);
221   if (GST_host_list_size <= host_id)
222     GST_array_grow_large_enough (GST_host_list, GST_host_list_size, host_id);
223   if (NULL != GST_host_list[host_id])
224   {
225     LOG_DEBUG ("A host with id: %u already exists\n", host_id);
226     return GNUNET_SYSERR;
227   }
228   GST_host_list[host_id] = host;
229   return GNUNET_OK;
230 }
231
232
233 /**
234  * Send operation failure message to client
235  *
236  * @param client the client to which the failure message has to be sent to
237  * @param operation_id the id of the failed operation
238  * @param emsg the error message; can be NULL
239  */
240 void
241 GST_send_operation_fail_msg (struct GNUNET_SERVER_Client *client,
242                              uint64_t operation_id, const char *emsg)
243 {
244   struct GNUNET_TESTBED_OperationFailureEventMessage *msg;
245   uint16_t msize;
246   uint16_t emsg_len;
247
248   msize = sizeof (struct GNUNET_TESTBED_OperationFailureEventMessage);
249   emsg_len = (NULL == emsg) ? 0 : strlen (emsg) + 1;
250   msize += emsg_len;
251   msg = GNUNET_malloc (msize);
252   msg->header.size = htons (msize);
253   msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_OPERATION_FAIL_EVENT);
254   msg->event_type = htonl (GNUNET_TESTBED_ET_OPERATION_FINISHED);
255   msg->operation_id = GNUNET_htonll (operation_id);
256   if (0 != emsg_len)
257     memcpy (&msg[1], emsg, emsg_len);
258   GST_queue_message (client, &msg->header);
259 }
260
261
262 /**
263  * Function to send generic operation success message to given client
264  *
265  * @param client the client to send the message to
266  * @param operation_id the id of the operation which was successful
267  */
268 void
269 GST_send_operation_success_msg (struct GNUNET_SERVER_Client *client,
270                                 uint64_t operation_id)
271 {
272   struct GNUNET_TESTBED_GenericOperationSuccessEventMessage *msg;
273   uint16_t msize;
274
275   msize = sizeof (struct GNUNET_TESTBED_GenericOperationSuccessEventMessage);
276   msg = GNUNET_malloc (msize);
277   msg->header.size = htons (msize);
278   msg->header.type =
279       htons (GNUNET_MESSAGE_TYPE_TESTBED_GENERIC_OPERATION_SUCCESS);
280   msg->operation_id = GNUNET_htonll (operation_id);
281   msg->event_type = htonl (GNUNET_TESTBED_ET_OPERATION_FINISHED);
282   GST_queue_message (client, &msg->header);
283 }
284
285 /**
286  * Callback which will be called after a host registration succeeded or failed
287  *
288  * @param cls the handle to the slave at which the registration is completed
289  * @param emsg the error message; NULL if host registration is successful
290  */
291 static void
292 hr_completion (void *cls, const char *emsg);
293
294
295 /**
296  * Attempts to register the next host in the host registration queue
297  *
298  * @param slave the slave controller whose host registration queue is checked
299  *          for host registrations
300  */
301 static void
302 register_next_host (struct Slave *slave)
303 {
304   struct HostRegistration *hr;
305
306   hr = slave->hr_dll_head;
307   GNUNET_assert (NULL != hr);
308   GNUNET_assert (NULL == slave->rhandle);
309   LOG (GNUNET_ERROR_TYPE_DEBUG, "Registering host %u at %u\n",
310        GNUNET_TESTBED_host_get_id_ (hr->host),
311        GNUNET_TESTBED_host_get_id_ (GST_host_list[slave->host_id]));
312   slave->rhandle =
313       GNUNET_TESTBED_register_host (slave->controller, hr->host, hr_completion,
314                                     slave);
315 }
316
317
318 /**
319  * Callback which will be called to after a host registration succeeded or failed
320  *
321  * @param cls the handle to the slave at which the registration is completed
322  * @param emsg the error message; NULL if host registration is successful
323  */
324 static void
325 hr_completion (void *cls, const char *emsg)
326 {
327   struct Slave *slave = cls;
328   struct HostRegistration *hr;
329
330   slave->rhandle = NULL;
331   hr = slave->hr_dll_head;
332   GNUNET_assert (NULL != hr);
333   LOG (GNUNET_ERROR_TYPE_DEBUG, "Registering host %u at %u successful\n",
334        GNUNET_TESTBED_host_get_id_ (hr->host),
335        GNUNET_TESTBED_host_get_id_ (GST_host_list[slave->host_id]));
336   GNUNET_CONTAINER_DLL_remove (slave->hr_dll_head, slave->hr_dll_tail, hr);
337   if (NULL != hr->cb)
338     hr->cb (hr->cb_cls, emsg);
339   GNUNET_free (hr);
340   if (NULL != slave->hr_dll_head)
341     register_next_host (slave);
342 }
343
344
345 /**
346  * Adds a host registration's request to a slave's registration queue
347  *
348  * @param slave the slave controller at which the given host has to be
349  *          registered
350  * @param cb the host registration completion callback
351  * @param cb_cls the closure for the host registration completion callback
352  * @param host the host which has to be registered
353  */
354 void
355 GST_queue_host_registration (struct Slave *slave,
356                              GNUNET_TESTBED_HostRegistrationCompletion cb,
357                              void *cb_cls, struct GNUNET_TESTBED_Host *host)
358 {
359   struct HostRegistration *hr;
360   int call_register;
361
362   LOG (GNUNET_ERROR_TYPE_DEBUG,
363        "Queueing host registration for host %u at %u\n",
364        GNUNET_TESTBED_host_get_id_ (host),
365        GNUNET_TESTBED_host_get_id_ (GST_host_list[slave->host_id]));
366   hr = GNUNET_malloc (sizeof (struct HostRegistration));
367   hr->cb = cb;
368   hr->cb_cls = cb_cls;
369   hr->host = host;
370   call_register = (NULL == slave->hr_dll_head) ? GNUNET_YES : GNUNET_NO;
371   GNUNET_CONTAINER_DLL_insert_tail (slave->hr_dll_head, slave->hr_dll_tail, hr);
372   if (GNUNET_YES == call_register)
373     register_next_host (slave);
374 }
375
376
377 /**
378  * Callback to relay the reply msg of a forwarded operation back to the client
379  *
380  * @param cls ForwardedOperationContext
381  * @param msg the message to relay
382  */
383 void
384 GST_forwarded_operation_reply_relay (void *cls,
385                                      const struct GNUNET_MessageHeader *msg)
386 {
387   struct ForwardedOperationContext *fopc = cls;
388   struct GNUNET_MessageHeader *dup_msg;
389   uint16_t msize;
390
391   msize = ntohs (msg->size);
392   LOG_DEBUG ("Relaying message with type: %u, size: %u\n", ntohs (msg->type),
393              msize);
394   dup_msg = GNUNET_copy_message (msg);
395   GST_queue_message (fopc->client, dup_msg);
396   GNUNET_SERVER_client_drop (fopc->client);
397   GNUNET_SCHEDULER_cancel (fopc->timeout_task);
398   GNUNET_CONTAINER_DLL_remove (fopcq_head, fopcq_tail, fopc);
399   GNUNET_free (fopc);
400 }
401
402
403 /**
404  * Task to free resources when forwarded operation has been timedout
405  *
406  * @param cls the ForwardedOperationContext
407  * @param tc the task context from scheduler
408  */
409 void
410 GST_forwarded_operation_timeout (void *cls,
411                                  const struct GNUNET_SCHEDULER_TaskContext *tc)
412 {
413   struct ForwardedOperationContext *fopc = cls;
414
415   GNUNET_TESTBED_forward_operation_msg_cancel_ (fopc->opc);
416   LOG (GNUNET_ERROR_TYPE_DEBUG, "A forwarded operation has timed out\n");
417   GST_send_operation_fail_msg (fopc->client, fopc->operation_id,
418                                "A forwarded operation has timed out");
419   GNUNET_SERVER_client_drop (fopc->client);
420   GNUNET_CONTAINER_DLL_remove (fopcq_head, fopcq_tail, fopc);
421   GNUNET_free (fopc);
422 }
423
424
425 /**
426  * Parse service sharing specification line.
427  * Format is "[<service:share>] [<service:share>] ..."
428  *
429  * @param ss_str the spec string to be parsed
430  * @param cfg the configuration to use for shared services
431  * @return an array suitable to pass to GNUNET_TESTING_system_create().  NULL
432  *           upon empty service sharing specification.
433  */
434 static struct GNUNET_TESTING_SharedService *
435 parse_shared_services (char *ss_str, struct GNUNET_CONFIGURATION_Handle *cfg)
436 {
437   struct GNUNET_TESTING_SharedService ss;
438   struct GNUNET_TESTING_SharedService *slist;
439   char service[256];
440   char *arg;
441   unsigned int n;
442 #define GROW_SS                                 \
443   do {                                          \
444     GNUNET_array_grow (slist, n, n+1);                                  \
445     (void) memcpy (&slist[n - 1], &ss,                                  \
446                    sizeof (struct GNUNET_TESTING_SharedService));       \
447   } while (0)
448   
449   slist = NULL;
450   n = 0;
451   ss.cfg = cfg;
452   for (; NULL != (arg = strtok (ss_str, " ")); ss_str = NULL)
453   {
454     ss.service = NULL;
455     ss.share = 0;
456     if (2 != sscanf (arg, "%255[^:]:%u", service, &ss.share))
457     {
458       LOG (GNUNET_ERROR_TYPE_WARNING, "Ignoring shared service spec: %s", arg);
459       continue;
460     }
461     LOG_DEBUG ("Will be sharing %s service among %u peers\n", service, ss.share);
462     ss.service = GNUNET_strdup (service);
463     GROW_SS;
464   }
465   if (NULL != slist)
466   {
467     /* Add trailing NULL block */
468     (void) memset (&ss, 0, sizeof (struct GNUNET_TESTING_SharedService));
469     GROW_SS;
470   }
471   return slist;
472 #undef GROW_SS
473 }
474
475
476 /**
477  * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_INIT messages
478  *
479  * @param cls NULL
480  * @param client identification of the client
481  * @param message the actual message
482  */
483 static void
484 handle_init (void *cls, struct GNUNET_SERVER_Client *client,
485              const struct GNUNET_MessageHeader *message)
486 {
487   const struct GNUNET_TESTBED_InitMessage *msg;
488   struct GNUNET_TESTBED_Host *host;
489   const char *controller_hostname;
490   char *ss_str;
491   struct GNUNET_TESTING_SharedService *ss;
492   unsigned int cnt;
493   uint16_t msize;
494
495   if (NULL != GST_context)
496   {
497     LOG_DEBUG ("We are being connected to laterally\n");
498     GNUNET_SERVER_receive_done (client, GNUNET_OK);
499     return;
500   }
501   msg = (const struct GNUNET_TESTBED_InitMessage *) message;
502   msize = ntohs (message->size);
503   if (msize <= sizeof (struct GNUNET_TESTBED_InitMessage))
504   {
505     GNUNET_break (0);
506     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
507     return;
508   }
509   msize -= sizeof (struct GNUNET_TESTBED_InitMessage);
510   controller_hostname = (const char *) &msg[1];
511   if ('\0' != controller_hostname[msize - 1])
512   {
513     GNUNET_break (0);
514     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
515     return;
516   }
517   ss_str = NULL;
518   ss = NULL;
519   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (our_config, "TESTBED",
520                                                           "SHARED_SERVICES",
521                                                           &ss_str))
522   {
523     ss = parse_shared_services (ss_str, our_config);
524     GNUNET_free (ss_str);
525     ss_str = NULL;
526   }
527   GST_context = GNUNET_malloc (sizeof (struct Context));
528   GNUNET_SERVER_client_keep (client);
529   GST_context->client = client;
530   GST_context->host_id = ntohl (msg->host_id);
531   GST_context->master_ip = GNUNET_strdup (controller_hostname);
532   LOG_DEBUG ("Our IP: %s\n", GST_context->master_ip);
533   GST_context->system =
534       GNUNET_TESTING_system_create ("testbed", GST_context->master_ip,
535                                     hostname, ss);
536   if (NULL != ss)
537   {
538     for (cnt = 0; NULL != ss[cnt].service; cnt++)
539     {
540       ss_str = (char *) ss[cnt].service;
541       GNUNET_free (ss_str);
542     }
543     GNUNET_free (ss);
544     ss = NULL;
545   }
546   host =
547       GNUNET_TESTBED_host_create_with_id (GST_context->host_id,
548                                           GST_context->master_ip, NULL,
549                                           our_config, 0);
550   host_list_add (host);
551   LOG_DEBUG ("Created master context with host ID: %u\n", GST_context->host_id);
552   GNUNET_SERVER_receive_done (client, GNUNET_OK);
553 }
554
555
556 /**
557  * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST messages
558  *
559  * @param cls NULL
560  * @param client identification of the client
561  * @param message the actual message
562  */
563 static void
564 handle_add_host (void *cls, struct GNUNET_SERVER_Client *client,
565                  const struct GNUNET_MessageHeader *message)
566 {
567   struct GNUNET_TESTBED_Host *host;
568   const struct GNUNET_TESTBED_AddHostMessage *msg;
569   struct GNUNET_TESTBED_HostConfirmedMessage *reply;
570   struct GNUNET_CONFIGURATION_Handle *host_cfg;
571   char *username;
572   char *hostname;
573   char *emsg;
574   const void *ptr;
575   uint32_t host_id;
576   uint16_t username_length;
577   uint16_t hostname_length;
578   uint16_t reply_size;
579   uint16_t msize;
580
581   msg = (const struct GNUNET_TESTBED_AddHostMessage *) message;
582   msize = ntohs (msg->header.size);
583   if (msize <= sizeof (struct GNUNET_TESTBED_AddHostMessage))
584   {
585     GNUNET_break_op (0);
586     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
587     return;
588   }
589   username_length = ntohs (msg->username_length);
590   hostname_length = ntohs (msg->hostname_length);
591   /* msg must contain hostname */
592   if ((msize <= (sizeof (struct GNUNET_TESTBED_AddHostMessage) + 
593                  username_length))
594       || (0 == hostname_length))
595   {
596     GNUNET_break_op (0);
597     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
598     return;
599   }
600   /* msg must contain configuration */
601   if (msize <= (sizeof (struct GNUNET_TESTBED_AddHostMessage) +
602                 username_length + hostname_length))
603   {
604     GNUNET_break_op (0);
605     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
606     return;
607   }
608   username = NULL;
609   hostname = NULL;
610   ptr = &msg[1];
611   if (0 != username_length)
612   {
613     username = GNUNET_malloc (username_length + 1);
614     strncpy (username, ptr, username_length);
615     ptr += username_length;
616   }
617   hostname = GNUNET_malloc (hostname_length + 1);
618   strncpy (hostname, ptr, hostname_length);
619   if (NULL == (host_cfg = GNUNET_TESTBED_extract_config_ (message)))
620   {
621     GNUNET_free_non_null (username);
622     GNUNET_free_non_null (hostname);
623     GNUNET_break_op (0);
624     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
625     return;
626   }
627   host_id = ntohl (msg->host_id);
628   LOG_DEBUG ("Received ADDHOST %u message\n", host_id);
629   LOG_DEBUG ("-------host id: %u\n", host_id);
630   LOG_DEBUG ("-------hostname: %s\n", hostname);
631   if (NULL != username)
632     LOG_DEBUG ("-------username: %s\n", username);
633   else
634     LOG_DEBUG ("-------username: <not given>\n");
635   LOG_DEBUG ("-------ssh port: %u\n", ntohs (msg->ssh_port));
636   host =
637       GNUNET_TESTBED_host_create_with_id (host_id, hostname, username,
638                                           host_cfg, ntohs (msg->ssh_port));
639   GNUNET_free_non_null (username);
640   GNUNET_free (hostname);
641   GNUNET_CONFIGURATION_destroy (host_cfg);
642   if (NULL == host)
643   {
644     GNUNET_break_op (0);
645     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
646     return;
647   }
648   reply_size = sizeof (struct GNUNET_TESTBED_HostConfirmedMessage);
649   if (GNUNET_OK != host_list_add (host))
650   {
651     /* We are unable to add a host */
652     emsg = "A host exists with given host-id";
653     LOG_DEBUG ("%s: %u", emsg, host_id);
654     GNUNET_TESTBED_host_destroy (host);
655     reply_size += strlen (emsg) + 1;
656     reply = GNUNET_malloc (reply_size);
657     memcpy (&reply[1], emsg, strlen (emsg) + 1);
658   }
659   else
660   {
661     LOG_DEBUG ("Added host %u at %u\n", host_id, GST_context->host_id);
662     reply = GNUNET_malloc (reply_size);
663   }
664   reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_ADD_HOST_SUCCESS);
665   reply->header.size = htons (reply_size);
666   reply->host_id = htonl (host_id);
667   GST_queue_message (client, &reply->header);
668   GNUNET_SERVER_receive_done (client, GNUNET_OK);
669 }
670
671
672 /**
673  * Handler for GNUNET_MESSAGE_TYPE_TESTBED_GETSLAVECONFIG messages
674  *
675  * @param cls NULL
676  * @param client identification of the client
677  * @param message the actual message
678  */
679 static void
680 handle_slave_get_config (void *cls, struct GNUNET_SERVER_Client *client,
681                          const struct GNUNET_MessageHeader *message)
682 {
683   struct GNUNET_TESTBED_SlaveGetConfigurationMessage *msg;
684   struct Slave *slave;
685   struct GNUNET_TESTBED_SlaveConfiguration *reply;
686   const struct GNUNET_CONFIGURATION_Handle *cfg;
687   char *config;
688   char *xconfig;
689   size_t config_size;
690   size_t xconfig_size;
691   size_t reply_size;
692   uint64_t op_id;
693   uint32_t slave_id;
694
695   msg = (struct GNUNET_TESTBED_SlaveGetConfigurationMessage *) message;
696   slave_id = ntohl (msg->slave_id);
697   op_id = GNUNET_ntohll (msg->operation_id);
698   if ((GST_slave_list_size <= slave_id) || (NULL == GST_slave_list[slave_id]))
699   {
700     /* FIXME: Add forwardings for this type of message here.. */
701     GST_send_operation_fail_msg (client, op_id, "Slave not found");
702     GNUNET_SERVER_receive_done (client, GNUNET_OK);
703     return;
704   }
705   slave = GST_slave_list[slave_id];
706   GNUNET_assert (NULL != (cfg = GNUNET_TESTBED_host_get_cfg_ (GST_host_list[slave->host_id])));
707   config = GNUNET_CONFIGURATION_serialize (cfg, &config_size);
708   xconfig_size =
709       GNUNET_TESTBED_compress_config_ (config, config_size, &xconfig);
710   GNUNET_free (config);
711   reply_size = xconfig_size + sizeof (struct GNUNET_TESTBED_SlaveConfiguration);
712   GNUNET_break (reply_size <= UINT16_MAX);
713   GNUNET_break (config_size <= UINT16_MAX);
714   reply = GNUNET_realloc (xconfig, reply_size);
715   (void) memmove (&reply[1], reply, xconfig_size);
716   reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_SLAVE_CONFIGURATION);
717   reply->header.size = htons ((uint16_t) reply_size);
718   reply->slave_id = msg->slave_id;
719   reply->operation_id = msg->operation_id;
720   reply->config_size = htons ((uint16_t) config_size);
721   GST_queue_message (client, &reply->header);
722   GNUNET_SERVER_receive_done (client, GNUNET_OK);
723 }
724
725
726 /**
727  * Clears the forwarded operations queue
728  */
729 void
730 GST_clear_fopcq ()
731 {
732   struct ForwardedOperationContext *fopc;
733   
734   while (NULL != (fopc = fopcq_head))
735   {
736     GNUNET_CONTAINER_DLL_remove (fopcq_head, fopcq_tail, fopc);
737     GNUNET_TESTBED_forward_operation_msg_cancel_ (fopc->opc);
738     if (GNUNET_SCHEDULER_NO_TASK != fopc->timeout_task)
739       GNUNET_SCHEDULER_cancel (fopc->timeout_task);
740     GNUNET_SERVER_client_drop (fopc->client);
741     switch (fopc->type)
742     {
743     case OP_PEER_CREATE:
744       GNUNET_free (fopc->cls);
745       break;
746     case OP_SHUTDOWN_PEERS:
747       {
748         struct HandlerContext_ShutdownPeers *hc = fopc->cls;
749         
750         GNUNET_assert (0 < hc->nslaves);
751         hc->nslaves--;
752         if (0 == hc->nslaves)
753           GNUNET_free (hc);
754       }
755       break;
756     case OP_PEER_START:
757     case OP_PEER_STOP:
758     case OP_PEER_DESTROY:
759     case OP_PEER_INFO:
760     case OP_OVERLAY_CONNECT:
761     case OP_LINK_CONTROLLERS:
762     case OP_GET_SLAVE_CONFIG:
763     case OP_MANAGE_SERVICE:
764     case OP_PEER_RECONFIGURE:
765       break;
766     case OP_FORWARDED:
767       GNUNET_assert (0);
768     };
769     GNUNET_free (fopc);
770   }
771 }
772
773
774 /**
775  * Task to clean up and shutdown nicely
776  *
777  * @param cls NULL
778  * @param tc the TaskContext from scheduler
779  */
780 static void
781 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
782 {
783   struct MessageQueue *mq_entry;
784   uint32_t id;
785
786   shutdown_task_id = GNUNET_SCHEDULER_NO_TASK;
787   LOG_DEBUG ("Shutting down testbed service\n");
788   /* cleanup any remaining forwarded operations */
789   GST_clear_fopcq ();
790   GST_free_lcfq ();
791   GST_free_mctxq ();
792   GST_free_occq ();
793   GST_free_roccq ();
794   GST_free_nccq ();
795   GST_neighbour_list_clean();
796   GST_free_prcq ();
797   /* Clear peer list */
798   GST_destroy_peers ();
799   /* Clear route list */
800   GST_route_list_clear ();
801   /* Clear GST_slave_list */
802   GST_slave_list_clear ();
803   /* Clear host list */
804   for (id = 0; id < GST_host_list_size; id++)
805     if (NULL != GST_host_list[id])
806       GNUNET_TESTBED_host_destroy (GST_host_list[id]);
807   GNUNET_free_non_null (GST_host_list);
808   if (NULL != GST_context)
809   {
810     GNUNET_free_non_null (GST_context->master_ip);
811     if (NULL != GST_context->system)
812       GNUNET_TESTING_system_destroy (GST_context->system, GNUNET_YES);
813     GNUNET_SERVER_client_drop (GST_context->client);
814     GNUNET_free (GST_context);
815     GST_context = NULL;
816   }
817   if (NULL != transmit_handle)
818     GNUNET_SERVER_notify_transmit_ready_cancel (transmit_handle);
819   while (NULL != (mq_entry = mq_head))
820   {
821     GNUNET_free (mq_entry->msg);
822     GNUNET_SERVER_client_drop (mq_entry->client);
823     GNUNET_CONTAINER_DLL_remove (mq_head, mq_tail, mq_entry);
824     GNUNET_free (mq_entry);
825   }
826   GNUNET_free_non_null (hostname);
827   GNUNET_CONFIGURATION_destroy (our_config);
828   /* Free hello cache */
829   GST_cache_clear ();
830   GNUNET_TESTBED_operation_queue_destroy_ (GST_opq_openfds);
831   GST_opq_openfds = NULL;
832   GST_stats_destroy ();
833 }
834
835
836 /**
837  * Callback for client disconnect
838  *
839  * @param cls NULL
840  * @param client the client which has disconnected
841  */
842 static void
843 client_disconnect_cb (void *cls, struct GNUNET_SERVER_Client *client)
844 {
845   if (NULL == GST_context)
846     return;
847   if (client == GST_context->client)
848   {
849     LOG (GNUNET_ERROR_TYPE_DEBUG, "Master client disconnected\n");
850     /* should not be needed as we're terminated by failure to read
851      * from stdin, but if stdin fails for some reason, this shouldn't
852      * hurt for now --- might need to revise this later if we ever
853      * decide that master connections might be temporarily down
854      * for some reason */
855     //GNUNET_SCHEDULER_shutdown ();
856   }
857 }
858
859
860 /**
861  * Testbed setup
862  *
863  * @param cls closure
864  * @param server the initialized server
865  * @param cfg configuration to use
866  */
867 static void
868 testbed_run (void *cls, struct GNUNET_SERVER_Handle *server,
869              const struct GNUNET_CONFIGURATION_Handle *cfg)
870 {
871   static const struct GNUNET_SERVER_MessageHandler message_handlers[] = {
872     {&handle_init, NULL, GNUNET_MESSAGE_TYPE_TESTBED_INIT, 0},
873     {&handle_add_host, NULL, GNUNET_MESSAGE_TYPE_TESTBED_ADD_HOST, 0},
874     {&GST_handle_link_controllers, NULL,
875      GNUNET_MESSAGE_TYPE_TESTBED_LINK_CONTROLLERS,
876      sizeof (struct GNUNET_TESTBED_ControllerLinkRequest)},
877     {&GST_handle_peer_create, NULL, GNUNET_MESSAGE_TYPE_TESTBED_CREATE_PEER, 0},
878     {&GST_handle_peer_destroy, NULL, GNUNET_MESSAGE_TYPE_TESTBED_DESTROY_PEER,
879      sizeof (struct GNUNET_TESTBED_PeerDestroyMessage)},
880     {&GST_handle_peer_start, NULL, GNUNET_MESSAGE_TYPE_TESTBED_START_PEER,
881      sizeof (struct GNUNET_TESTBED_PeerStartMessage)},
882     {&GST_handle_peer_stop, NULL, GNUNET_MESSAGE_TYPE_TESTBED_STOP_PEER,
883      sizeof (struct GNUNET_TESTBED_PeerStopMessage)},
884     {&GST_handle_peer_get_config, NULL,
885      GNUNET_MESSAGE_TYPE_TESTBED_GET_PEER_INFORMATION,
886      sizeof (struct GNUNET_TESTBED_PeerGetConfigurationMessage)},
887     {&GST_handle_overlay_connect, NULL,
888      GNUNET_MESSAGE_TYPE_TESTBED_OVERLAY_CONNECT,
889      sizeof (struct GNUNET_TESTBED_OverlayConnectMessage)},
890     {&GST_handle_remote_overlay_connect, NULL,
891      GNUNET_MESSAGE_TYPE_TESTBED_REMOTE_OVERLAY_CONNECT, 0},
892     {&GST_handle_manage_peer_service, NULL,
893      GNUNET_MESSAGE_TYPE_TESTBED_MANAGE_PEER_SERVICE, 0},
894     {&handle_slave_get_config, NULL,
895      GNUNET_MESSAGE_TYPE_TESTBED_GET_SLAVE_CONFIGURATION,
896      sizeof (struct GNUNET_TESTBED_SlaveGetConfigurationMessage)},
897     {&GST_handle_shutdown_peers, NULL, GNUNET_MESSAGE_TYPE_TESTBED_SHUTDOWN_PEERS,
898      sizeof (struct GNUNET_TESTBED_ShutdownPeersMessage)},
899     {&GST_handle_peer_reconfigure, NULL, 
900      GNUNET_MESSAGE_TYPE_TESTBED_RECONFIGURE_PEER, 0},
901     {NULL, NULL, 0, 0}
902   };
903   char *logfile;
904   unsigned long long num;
905
906   LOG_DEBUG ("Starting testbed\n");
907   if (GNUNET_OK ==
908       GNUNET_CONFIGURATION_get_value_filename (cfg, "TESTBED", "LOG_FILE",
909                                                &logfile))
910   {
911     GNUNET_break (GNUNET_OK == GNUNET_log_setup ("testbed", "DEBUG", logfile));
912     GNUNET_free (logfile);
913   }
914   GNUNET_assert (GNUNET_OK ==
915                  GNUNET_CONFIGURATION_get_value_number (cfg, "TESTBED",
916                                                         "CACHE_SIZE", &num));
917   GST_cache_init ((unsigned int) num);
918   GNUNET_assert (GNUNET_OK ==
919                  GNUNET_CONFIGURATION_get_value_number (cfg, "TESTBED",
920                                                         "MAX_OPEN_FDS", &num));
921   GST_opq_openfds = GNUNET_TESTBED_operation_queue_create_ ((unsigned int) num);
922   GNUNET_assert (GNUNET_OK ==
923                  GNUNET_CONFIGURATION_get_value_time (cfg, "TESTBED",
924                                                       "OPERATION_TIMEOUT",
925                                                       (struct
926                                                        GNUNET_TIME_Relative *)
927                                                       &GST_timeout));
928   GNUNET_assert (GNUNET_OK ==
929                  GNUNET_CONFIGURATION_get_value_string (cfg, "testbed",
930                                                         "HOSTNAME", &hostname));
931   our_config = GNUNET_CONFIGURATION_dup (cfg);
932   GNUNET_SERVER_add_handlers (server, message_handlers);
933   GNUNET_SERVER_disconnect_notify (server, &client_disconnect_cb, NULL);
934   shutdown_task_id =
935       GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_UNIT_FOREVER_REL,
936                                                   GNUNET_SCHEDULER_PRIORITY_IDLE,
937                                                   &shutdown_task, NULL);
938   LOG_DEBUG ("Testbed startup complete\n");
939   GST_stats_init (our_config);
940 }
941
942
943 /**
944  * The starting point of execution
945  */
946 int
947 main (int argc, char *const *argv)
948 {
949   //sleep (15);                 /* Debugging */
950   return (GNUNET_OK ==
951           GNUNET_SERVICE_run (argc, argv, "testbed", GNUNET_SERVICE_OPTION_NONE,
952                               &testbed_run, NULL)) ? 0 : 1;
953 }
954
955 /* end of gnunet-service-testbed.c */