- revert 27104
[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   ptr += hostname_length;
620   if (NULL == (host_cfg = GNUNET_TESTBED_extract_config_ (message)))
621   {
622     GNUNET_free_non_null (username);
623     GNUNET_free_non_null (hostname);
624     GNUNET_break_op (0);
625     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
626     return;
627   }
628   host_id = ntohl (msg->host_id);
629   LOG_DEBUG ("Received ADDHOST %u message\n", host_id);
630   LOG_DEBUG ("-------host id: %u\n", host_id);
631   LOG_DEBUG ("-------hostname: %s\n", hostname);
632   if (NULL != username)
633     LOG_DEBUG ("-------username: %s\n", username);
634   else
635     LOG_DEBUG ("-------username: <not given>\n");
636   LOG_DEBUG ("-------ssh port: %u\n", ntohs (msg->ssh_port));
637   host =
638       GNUNET_TESTBED_host_create_with_id (host_id, hostname, username,
639                                           host_cfg, ntohs (msg->ssh_port));
640   GNUNET_free_non_null (username);
641   GNUNET_free (hostname);
642   GNUNET_CONFIGURATION_destroy (host_cfg);
643   if (NULL == host)
644   {
645     GNUNET_break_op (0);
646     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
647     return;
648   }
649   reply_size = sizeof (struct GNUNET_TESTBED_HostConfirmedMessage);
650   if (GNUNET_OK != host_list_add (host))
651   {
652     /* We are unable to add a host */
653     emsg = "A host exists with given host-id";
654     LOG_DEBUG ("%s: %u", emsg, host_id);
655     GNUNET_TESTBED_host_destroy (host);
656     reply_size += strlen (emsg) + 1;
657     reply = GNUNET_malloc (reply_size);
658     memcpy (&reply[1], emsg, strlen (emsg) + 1);
659   }
660   else
661   {
662     LOG_DEBUG ("Added host %u at %u\n", host_id, GST_context->host_id);
663     reply = GNUNET_malloc (reply_size);
664   }
665   reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_ADD_HOST_SUCCESS);
666   reply->header.size = htons (reply_size);
667   reply->host_id = htonl (host_id);
668   GST_queue_message (client, &reply->header);
669   GNUNET_SERVER_receive_done (client, GNUNET_OK);
670 }
671
672
673 /**
674  * Handler for GNUNET_MESSAGE_TYPE_TESTBED_GETSLAVECONFIG messages
675  *
676  * @param cls NULL
677  * @param client identification of the client
678  * @param message the actual message
679  */
680 static void
681 handle_slave_get_config (void *cls, struct GNUNET_SERVER_Client *client,
682                          const struct GNUNET_MessageHeader *message)
683 {
684   struct GNUNET_TESTBED_SlaveGetConfigurationMessage *msg;
685   struct Slave *slave;
686   struct GNUNET_TESTBED_SlaveConfiguration *reply;
687   const struct GNUNET_CONFIGURATION_Handle *cfg;
688   char *config;
689   char *xconfig;
690   size_t config_size;
691   size_t xconfig_size;
692   size_t reply_size;
693   uint64_t op_id;
694   uint32_t slave_id;
695
696   msg = (struct GNUNET_TESTBED_SlaveGetConfigurationMessage *) message;
697   slave_id = ntohl (msg->slave_id);
698   op_id = GNUNET_ntohll (msg->operation_id);
699   if ((GST_slave_list_size <= slave_id) || (NULL == GST_slave_list[slave_id]))
700   {
701     /* FIXME: Add forwardings for this type of message here.. */
702     GST_send_operation_fail_msg (client, op_id, "Slave not found");
703     GNUNET_SERVER_receive_done (client, GNUNET_OK);
704     return;
705   }
706   slave = GST_slave_list[slave_id];
707   GNUNET_assert (NULL != (cfg = GNUNET_TESTBED_host_get_cfg_ (GST_host_list[slave->host_id])));
708   config = GNUNET_CONFIGURATION_serialize (cfg, &config_size);
709   xconfig_size =
710       GNUNET_TESTBED_compress_config_ (config, config_size, &xconfig);
711   GNUNET_free (config);
712   reply_size = xconfig_size + sizeof (struct GNUNET_TESTBED_SlaveConfiguration);
713   GNUNET_break (reply_size <= UINT16_MAX);
714   GNUNET_break (config_size <= UINT16_MAX);
715   reply = GNUNET_realloc (xconfig, reply_size);
716   (void) memmove (&reply[1], reply, xconfig_size);
717   reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_SLAVE_CONFIGURATION);
718   reply->header.size = htons ((uint16_t) reply_size);
719   reply->slave_id = msg->slave_id;
720   reply->operation_id = msg->operation_id;
721   reply->config_size = htons ((uint16_t) config_size);
722   GST_queue_message (client, &reply->header);
723   GNUNET_SERVER_receive_done (client, GNUNET_OK);
724 }
725
726
727 /**
728  * Clears the forwarded operations queue
729  */
730 void
731 GST_clear_fopcq ()
732 {
733   struct ForwardedOperationContext *fopc;
734   
735   while (NULL != (fopc = fopcq_head))
736   {
737     GNUNET_CONTAINER_DLL_remove (fopcq_head, fopcq_tail, fopc);
738     GNUNET_TESTBED_forward_operation_msg_cancel_ (fopc->opc);
739     if (GNUNET_SCHEDULER_NO_TASK != fopc->timeout_task)
740       GNUNET_SCHEDULER_cancel (fopc->timeout_task);
741     GNUNET_SERVER_client_drop (fopc->client);
742     switch (fopc->type)
743     {
744     case OP_PEER_CREATE:
745       GNUNET_free (fopc->cls);
746       break;
747     case OP_SHUTDOWN_PEERS:
748       {
749         struct HandlerContext_ShutdownPeers *hc = fopc->cls;
750         
751         GNUNET_assert (0 < hc->nslaves);
752         hc->nslaves--;
753         if (0 == hc->nslaves)
754           GNUNET_free (hc);
755       }
756       break;
757     case OP_PEER_START:
758     case OP_PEER_STOP:
759     case OP_PEER_DESTROY:
760     case OP_PEER_INFO:
761     case OP_OVERLAY_CONNECT:
762     case OP_LINK_CONTROLLERS:
763     case OP_GET_SLAVE_CONFIG:
764     case OP_MANAGE_SERVICE:
765     case OP_PEER_RECONFIGURE:
766       break;
767     case OP_FORWARDED:
768       GNUNET_assert (0);
769     };
770     GNUNET_free (fopc);
771   }
772 }
773
774
775 /**
776  * Task to clean up and shutdown nicely
777  *
778  * @param cls NULL
779  * @param tc the TaskContext from scheduler
780  */
781 static void
782 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
783 {
784   struct MessageQueue *mq_entry;
785   uint32_t id;
786
787   shutdown_task_id = GNUNET_SCHEDULER_NO_TASK;
788   LOG_DEBUG ("Shutting down testbed service\n");
789   /* cleanup any remaining forwarded operations */
790   GST_clear_fopcq ();
791   GST_free_lcfq ();
792   GST_free_mctxq ();
793   GST_free_occq ();
794   GST_free_roccq ();
795   GST_free_nccq ();
796   GST_neighbour_list_clean();
797   GST_free_prcq ();
798   /* Clear peer list */
799   GST_destroy_peers ();
800   /* Clear route list */
801   GST_route_list_clear ();
802   /* Clear GST_slave_list */
803   GST_slave_list_clear ();
804   /* Clear host list */
805   for (id = 0; id < GST_host_list_size; id++)
806     if (NULL != GST_host_list[id])
807       GNUNET_TESTBED_host_destroy (GST_host_list[id]);
808   GNUNET_free_non_null (GST_host_list);
809   if (NULL != GST_context)
810   {
811     GNUNET_free_non_null (GST_context->master_ip);
812     if (NULL != GST_context->system)
813       GNUNET_TESTING_system_destroy (GST_context->system, GNUNET_YES);
814     GNUNET_SERVER_client_drop (GST_context->client);
815     GNUNET_free (GST_context);
816     GST_context = NULL;
817   }
818   if (NULL != transmit_handle)
819     GNUNET_SERVER_notify_transmit_ready_cancel (transmit_handle);
820   while (NULL != (mq_entry = mq_head))
821   {
822     GNUNET_free (mq_entry->msg);
823     GNUNET_SERVER_client_drop (mq_entry->client);
824     GNUNET_CONTAINER_DLL_remove (mq_head, mq_tail, mq_entry);
825     GNUNET_free (mq_entry);
826   }
827   GNUNET_free_non_null (hostname);
828   GNUNET_CONFIGURATION_destroy (our_config);
829   /* Free hello cache */
830   GST_cache_clear ();
831   GNUNET_TESTBED_operation_queue_destroy_ (GST_opq_openfds);
832   GST_opq_openfds = NULL;
833   GST_stats_destroy ();
834 }
835
836
837 /**
838  * Callback for client disconnect
839  *
840  * @param cls NULL
841  * @param client the client which has disconnected
842  */
843 static void
844 client_disconnect_cb (void *cls, struct GNUNET_SERVER_Client *client)
845 {
846   if (NULL == GST_context)
847     return;
848   if (client == GST_context->client)
849   {
850     LOG (GNUNET_ERROR_TYPE_DEBUG, "Master client disconnected\n");
851     /* should not be needed as we're terminated by failure to read
852      * from stdin, but if stdin fails for some reason, this shouldn't
853      * hurt for now --- might need to revise this later if we ever
854      * decide that master connections might be temporarily down
855      * for some reason */
856     //GNUNET_SCHEDULER_shutdown ();
857   }
858 }
859
860
861 /**
862  * Testbed setup
863  *
864  * @param cls closure
865  * @param server the initialized server
866  * @param cfg configuration to use
867  */
868 static void
869 testbed_run (void *cls, struct GNUNET_SERVER_Handle *server,
870              const struct GNUNET_CONFIGURATION_Handle *cfg)
871 {
872   static const struct GNUNET_SERVER_MessageHandler message_handlers[] = {
873     {&handle_init, NULL, GNUNET_MESSAGE_TYPE_TESTBED_INIT, 0},
874     {&handle_add_host, NULL, GNUNET_MESSAGE_TYPE_TESTBED_ADD_HOST, 0},
875     {&GST_handle_link_controllers, NULL,
876      GNUNET_MESSAGE_TYPE_TESTBED_LINK_CONTROLLERS,
877      sizeof (struct GNUNET_TESTBED_ControllerLinkRequest)},
878     {&GST_handle_peer_create, NULL, GNUNET_MESSAGE_TYPE_TESTBED_CREATE_PEER, 0},
879     {&GST_handle_peer_destroy, NULL, GNUNET_MESSAGE_TYPE_TESTBED_DESTROY_PEER,
880      sizeof (struct GNUNET_TESTBED_PeerDestroyMessage)},
881     {&GST_handle_peer_start, NULL, GNUNET_MESSAGE_TYPE_TESTBED_START_PEER,
882      sizeof (struct GNUNET_TESTBED_PeerStartMessage)},
883     {&GST_handle_peer_stop, NULL, GNUNET_MESSAGE_TYPE_TESTBED_STOP_PEER,
884      sizeof (struct GNUNET_TESTBED_PeerStopMessage)},
885     {&GST_handle_peer_get_config, NULL,
886      GNUNET_MESSAGE_TYPE_TESTBED_GET_PEER_CONFIGURATION,
887      sizeof (struct GNUNET_TESTBED_PeerGetConfigurationMessage)},
888     {&GST_handle_overlay_connect, NULL,
889      GNUNET_MESSAGE_TYPE_TESTBED_OVERLAY_CONNECT,
890      sizeof (struct GNUNET_TESTBED_OverlayConnectMessage)},
891     {&GST_handle_remote_overlay_connect, NULL,
892      GNUNET_MESSAGE_TYPE_TESTBED_REMOTE_OVERLAY_CONNECT, 0},
893     {&GST_handle_manage_peer_service, NULL,
894      GNUNET_MESSAGE_TYPE_TESTBED_MANAGE_PEER_SERVICE, 0},
895     {&handle_slave_get_config, NULL,
896      GNUNET_MESSAGE_TYPE_TESTBED_GET_SLAVE_CONFIGURATION,
897      sizeof (struct GNUNET_TESTBED_SlaveGetConfigurationMessage)},
898     {&GST_handle_shutdown_peers, NULL, GNUNET_MESSAGE_TYPE_TESTBED_SHUTDOWN_PEERS,
899      sizeof (struct GNUNET_TESTBED_ShutdownPeersMessage)},
900     {&GST_handle_peer_reconfigure, NULL, 
901      GNUNET_MESSAGE_TYPE_TESTBED_RECONFIGURE_PEER, 0},
902     {NULL, NULL, 0, 0}
903   };
904   char *logfile;
905   unsigned long long num;
906
907   LOG_DEBUG ("Starting testbed\n");
908   if (GNUNET_OK ==
909       GNUNET_CONFIGURATION_get_value_filename (cfg, "TESTBED", "LOG_FILE",
910                                                &logfile))
911   {
912     GNUNET_break (GNUNET_OK == GNUNET_log_setup ("testbed", "DEBUG", logfile));
913     GNUNET_free (logfile);
914   }
915   GNUNET_assert (GNUNET_OK ==
916                  GNUNET_CONFIGURATION_get_value_number (cfg, "TESTBED",
917                                                         "CACHE_SIZE", &num));
918   GST_cache_init ((unsigned int) num);
919   GNUNET_assert (GNUNET_OK ==
920                  GNUNET_CONFIGURATION_get_value_number (cfg, "TESTBED",
921                                                         "MAX_OPEN_FDS", &num));
922   GST_opq_openfds = GNUNET_TESTBED_operation_queue_create_ ((unsigned int) num);
923   GNUNET_assert (GNUNET_OK ==
924                  GNUNET_CONFIGURATION_get_value_time (cfg, "TESTBED",
925                                                       "OPERATION_TIMEOUT",
926                                                       (struct
927                                                        GNUNET_TIME_Relative *)
928                                                       &GST_timeout));
929   GNUNET_assert (GNUNET_OK ==
930                  GNUNET_CONFIGURATION_get_value_string (cfg, "testbed",
931                                                         "HOSTNAME", &hostname));
932   our_config = GNUNET_CONFIGURATION_dup (cfg);
933   GNUNET_SERVER_add_handlers (server, message_handlers);
934   GNUNET_SERVER_disconnect_notify (server, &client_disconnect_cb, NULL);
935   shutdown_task_id =
936       GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_UNIT_FOREVER_REL,
937                                                   GNUNET_SCHEDULER_PRIORITY_IDLE,
938                                                   &shutdown_task, NULL);
939   LOG_DEBUG ("Testbed startup complete\n");
940   GST_stats_init (our_config);
941 }
942
943
944 /**
945  * The starting point of execution
946  */
947 int
948 main (int argc, char *const *argv)
949 {
950   //sleep (15);                 /* Debugging */
951   return (GNUNET_OK ==
952           GNUNET_SERVICE_run (argc, argv, "testbed", GNUNET_SERVICE_OPTION_NONE,
953                               &testbed_run, NULL)) ? 0 : 1;
954 }
955
956 /* end of gnunet-service-testbed.c */