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