2cdf0acbc7d67d238290c8632166499703cd26de
[oweals/gnunet.git] / src / testbed / gnunet-service-testbed.c
1 /*
2   This file is part of GNUnet.
3   Copyright (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 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., 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 #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  * The shutdown task handle
134  */
135 static struct GNUNET_SCHEDULER_Task * shutdown_task_id;
136
137
138 /**
139  * Function called to notify a client about the connection begin ready to queue
140  * more data.  "buf" will be NULL and "size" zero if the connection was closed
141  * for writing in the meantime.
142  *
143  * @param cls NULL
144  * @param size number of bytes available in buf
145  * @param buf where the callee should write the message
146  * @return number of bytes written to buf
147  */
148 static size_t
149 transmit_ready_notify (void *cls, size_t size, void *buf)
150 {
151   struct MessageQueue *mq_entry;
152
153   transmit_handle = NULL;
154   mq_entry = mq_head;
155   GNUNET_assert (NULL != mq_entry);
156   if (0 == size)
157     return 0;
158   GNUNET_assert (ntohs (mq_entry->msg->size) <= size);
159   size = ntohs (mq_entry->msg->size);
160   memcpy (buf, mq_entry->msg, size);
161   GNUNET_free (mq_entry->msg);
162   GNUNET_SERVER_client_drop (mq_entry->client);
163   GNUNET_CONTAINER_DLL_remove (mq_head, mq_tail, mq_entry);
164   GNUNET_free (mq_entry);
165   mq_entry = mq_head;
166   if (NULL != mq_entry)
167     transmit_handle =
168         GNUNET_SERVER_notify_transmit_ready (mq_entry->client,
169                                              ntohs (mq_entry->msg->size),
170                                              GNUNET_TIME_UNIT_FOREVER_REL,
171                                              &transmit_ready_notify, NULL);
172   return size;
173 }
174
175
176 /**
177  * Queues a message in send queue for sending to the service
178  *
179  * @param client the client to whom the queued message has to be sent
180  * @param msg the message to queue
181  */
182 void
183 GST_queue_message (struct GNUNET_SERVER_Client *client,
184                    struct GNUNET_MessageHeader *msg)
185 {
186   struct MessageQueue *mq_entry;
187   uint16_t type;
188   uint16_t size;
189
190   type = ntohs (msg->type);
191   size = ntohs (msg->size);
192   GNUNET_assert ((GNUNET_MESSAGE_TYPE_TESTBED_INIT <= type) &&
193                  (GNUNET_MESSAGE_TYPE_TESTBED_MAX > type));
194   mq_entry = GNUNET_new (struct MessageQueue);
195   mq_entry->msg = msg;
196   mq_entry->client = client;
197   GNUNET_SERVER_client_keep (client);
198   LOG_DEBUG ("Queueing message of type %u, size %u for sending\n", type,
199              ntohs (msg->size));
200   GNUNET_CONTAINER_DLL_insert_tail (mq_head, mq_tail, mq_entry);
201   if (NULL == transmit_handle)
202     transmit_handle =
203         GNUNET_SERVER_notify_transmit_ready (client, size,
204                                              GNUNET_TIME_UNIT_FOREVER_REL,
205                                              &transmit_ready_notify, NULL);
206 }
207
208
209 /**
210  * Function to add a host to the current list of known hosts
211  *
212  * @param host the host to add
213  * @return GNUNET_OK on success; GNUNET_SYSERR on failure due to host-id
214  *           already in use
215  */
216 static int
217 host_list_add (struct GNUNET_TESTBED_Host *host)
218 {
219   uint32_t host_id;
220
221   host_id = GNUNET_TESTBED_host_get_id_ (host);
222   if (GST_host_list_size <= host_id)
223     GST_array_grow_large_enough (GST_host_list, GST_host_list_size, host_id);
224   if (NULL != GST_host_list[host_id])
225   {
226     LOG_DEBUG ("A host with id: %u already exists\n", host_id);
227     return GNUNET_SYSERR;
228   }
229   GST_host_list[host_id] = host;
230   return GNUNET_OK;
231 }
232
233
234 /**
235  * Send operation failure message to client
236  *
237  * @param client the client to which the failure message has to be sent to
238  * @param operation_id the id of the failed operation
239  * @param emsg the error message; can be NULL
240  */
241 void
242 GST_send_operation_fail_msg (struct GNUNET_SERVER_Client *client,
243                              uint64_t operation_id, const char *emsg)
244 {
245   struct GNUNET_TESTBED_OperationFailureEventMessage *msg;
246   uint16_t msize;
247   uint16_t emsg_len;
248
249   msize = sizeof (struct GNUNET_TESTBED_OperationFailureEventMessage);
250   emsg_len = (NULL == emsg) ? 0 : strlen (emsg) + 1;
251   msize += emsg_len;
252   msg = GNUNET_malloc (msize);
253   msg->header.size = htons (msize);
254   msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_OPERATION_FAIL_EVENT);
255   msg->event_type = htonl (GNUNET_TESTBED_ET_OPERATION_FINISHED);
256   msg->operation_id = GNUNET_htonll (operation_id);
257   if (0 != emsg_len)
258     memcpy (&msg[1], emsg, emsg_len);
259   GST_queue_message (client, &msg->header);
260 }
261
262
263 /**
264  * Function to send generic operation success message to given client
265  *
266  * @param client the client to send the message to
267  * @param operation_id the id of the operation which was successful
268  */
269 void
270 GST_send_operation_success_msg (struct GNUNET_SERVER_Client *client,
271                                 uint64_t operation_id)
272 {
273   struct GNUNET_TESTBED_GenericOperationSuccessEventMessage *msg;
274   uint16_t msize;
275
276   msize = sizeof (struct GNUNET_TESTBED_GenericOperationSuccessEventMessage);
277   msg = GNUNET_malloc (msize);
278   msg->header.size = htons (msize);
279   msg->header.type =
280       htons (GNUNET_MESSAGE_TYPE_TESTBED_GENERIC_OPERATION_SUCCESS);
281   msg->operation_id = GNUNET_htonll (operation_id);
282   msg->event_type = htonl (GNUNET_TESTBED_ET_OPERATION_FINISHED);
283   GST_queue_message (client, &msg->header);
284 }
285
286 /**
287  * Callback which will be called after a host registration succeeded or failed
288  *
289  * @param cls the handle to the slave at which the registration is completed
290  * @param emsg the error message; NULL if host registration is successful
291  */
292 static void
293 hr_completion (void *cls, const char *emsg);
294
295
296 /**
297  * Attempts to register the next host in the host registration queue
298  *
299  * @param slave the slave controller whose host registration queue is checked
300  *          for host registrations
301  */
302 static void
303 register_next_host (struct Slave *slave)
304 {
305   struct HostRegistration *hr;
306
307   hr = slave->hr_dll_head;
308   GNUNET_assert (NULL != hr);
309   GNUNET_assert (NULL == slave->rhandle);
310   LOG (GNUNET_ERROR_TYPE_DEBUG, "Registering host %u at %u\n",
311        GNUNET_TESTBED_host_get_id_ (hr->host),
312        GNUNET_TESTBED_host_get_id_ (GST_host_list[slave->host_id]));
313   slave->rhandle =
314       GNUNET_TESTBED_register_host (slave->controller, hr->host, hr_completion,
315                                     slave);
316 }
317
318
319 /**
320  * Callback which will be called to after a host registration succeeded or failed
321  *
322  * @param cls the handle to the slave at which the registration is completed
323  * @param emsg the error message; NULL if host registration is successful
324  */
325 static void
326 hr_completion (void *cls, const char *emsg)
327 {
328   struct Slave *slave = cls;
329   struct HostRegistration *hr;
330
331   slave->rhandle = NULL;
332   hr = slave->hr_dll_head;
333   GNUNET_assert (NULL != hr);
334   LOG (GNUNET_ERROR_TYPE_DEBUG, "Registering host %u at %u successful\n",
335        GNUNET_TESTBED_host_get_id_ (hr->host),
336        GNUNET_TESTBED_host_get_id_ (GST_host_list[slave->host_id]));
337   GNUNET_CONTAINER_DLL_remove (slave->hr_dll_head, slave->hr_dll_tail, hr);
338   if (NULL != hr->cb)
339     hr->cb (hr->cb_cls, emsg);
340   GNUNET_free (hr);
341   if (NULL != slave->hr_dll_head)
342     register_next_host (slave);
343 }
344
345
346 /**
347  * Adds a host registration's request to a slave's registration queue
348  *
349  * @param slave the slave controller at which the given host has to be
350  *          registered
351  * @param cb the host registration completion callback
352  * @param cb_cls the closure for the host registration completion callback
353  * @param host the host which has to be registered
354  */
355 void
356 GST_queue_host_registration (struct Slave *slave,
357                              GNUNET_TESTBED_HostRegistrationCompletion cb,
358                              void *cb_cls, struct GNUNET_TESTBED_Host *host)
359 {
360   struct HostRegistration *hr;
361   int call_register;
362
363   LOG (GNUNET_ERROR_TYPE_DEBUG,
364        "Queueing host registration for host %u at %u\n",
365        GNUNET_TESTBED_host_get_id_ (host),
366        GNUNET_TESTBED_host_get_id_ (GST_host_list[slave->host_id]));
367   hr = GNUNET_new (struct HostRegistration);
368   hr->cb = cb;
369   hr->cb_cls = cb_cls;
370   hr->host = host;
371   call_register = (NULL == slave->hr_dll_head) ? GNUNET_YES : GNUNET_NO;
372   GNUNET_CONTAINER_DLL_insert_tail (slave->hr_dll_head, slave->hr_dll_tail, hr);
373   if (GNUNET_YES == call_register)
374     register_next_host (slave);
375 }
376
377
378 /**
379  * Callback to relay the reply msg of a forwarded operation back to the client
380  *
381  * @param cls ForwardedOperationContext
382  * @param msg the message to relay
383  */
384 void
385 GST_forwarded_operation_reply_relay (void *cls,
386                                      const struct GNUNET_MessageHeader *msg)
387 {
388   struct ForwardedOperationContext *fopc = cls;
389   struct GNUNET_MessageHeader *dup_msg;
390   uint16_t msize;
391
392   msize = ntohs (msg->size);
393   LOG_DEBUG ("Relaying message with type: %u, size: %u\n", ntohs (msg->type),
394              msize);
395   dup_msg = GNUNET_copy_message (msg);
396   GST_queue_message (fopc->client, dup_msg);
397   GNUNET_SERVER_client_drop (fopc->client);
398   GNUNET_SCHEDULER_cancel (fopc->timeout_task);
399   GNUNET_CONTAINER_DLL_remove (fopcq_head, fopcq_tail, fopc);
400   GNUNET_free (fopc);
401 }
402
403
404 /**
405  * Task to free resources when forwarded operation has been timedout
406  *
407  * @param cls the ForwardedOperationContext
408  * @param tc the task context from scheduler
409  */
410 void
411 GST_forwarded_operation_timeout (void *cls,
412                                  const struct GNUNET_SCHEDULER_TaskContext *tc)
413 {
414   struct ForwardedOperationContext *fopc = cls;
415
416   GNUNET_TESTBED_forward_operation_msg_cancel_ (fopc->opc);
417   LOG (GNUNET_ERROR_TYPE_DEBUG, "A forwarded operation has timed out\n");
418   GST_send_operation_fail_msg (fopc->client, fopc->operation_id,
419                                "A forwarded operation has timed out");
420   GNUNET_SERVER_client_drop (fopc->client);
421   GNUNET_CONTAINER_DLL_remove (fopcq_head, fopcq_tail, fopc);
422   GNUNET_free (fopc);
423 }
424
425
426 /**
427  * Parse service sharing specification line.
428  * Format is "[<service:share>] [<service:share>] ..."
429  *
430  * @param ss_str the spec string to be parsed
431  * @param cfg the configuration to use for shared services
432  * @return an array suitable to pass to GNUNET_TESTING_system_create().  NULL
433  *           upon empty service sharing specification.
434  */
435 static struct GNUNET_TESTING_SharedService *
436 parse_shared_services (char *ss_str, struct GNUNET_CONFIGURATION_Handle *cfg)
437 {
438   struct GNUNET_TESTING_SharedService ss;
439   struct GNUNET_TESTING_SharedService *slist;
440   char service[256];
441   char *arg;
442   unsigned int n;
443 #define GROW_SS                                 \
444   do {                                          \
445     GNUNET_array_grow (slist, n, n+1);                                  \
446     (void) memcpy (&slist[n - 1], &ss,                                  \
447                    sizeof (struct GNUNET_TESTING_SharedService));       \
448   } while (0)
449
450   slist = NULL;
451   n = 0;
452   ss.cfg = cfg;
453   for (; NULL != (arg = strtok (ss_str, " ")); ss_str = NULL)
454   {
455     ss.service = NULL;
456     ss.share = 0;
457     if (2 != sscanf (arg, "%255[^:]:%u", service, &ss.share))
458     {
459       LOG (GNUNET_ERROR_TYPE_WARNING, "Ignoring shared service spec: %s", arg);
460       continue;
461     }
462     LOG_DEBUG ("Will be sharing %s service among %u peers\n", service, ss.share);
463     ss.service = GNUNET_strdup (service);
464     GROW_SS;
465   }
466   if (NULL != slist)
467   {
468     /* Add trailing NULL block */
469     (void) memset (&ss, 0, sizeof (struct GNUNET_TESTING_SharedService));
470     GROW_SS;
471   }
472   return slist;
473 #undef GROW_SS
474 }
475
476
477 /**
478  * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_INIT messages
479  *
480  * @param cls NULL
481  * @param client identification of the client
482  * @param message the actual message
483  */
484 static void
485 handle_init (void *cls, struct GNUNET_SERVER_Client *client,
486              const struct GNUNET_MessageHeader *message)
487 {
488   const struct GNUNET_TESTBED_InitMessage *msg;
489   struct GNUNET_TESTBED_Host *host;
490   const char *controller_hostname;
491   char *ss_str;
492   struct GNUNET_TESTING_SharedService *ss;
493   unsigned int cnt;
494   uint16_t msize;
495
496   if (NULL != GST_context)
497   {
498     LOG_DEBUG ("We are being connected to laterally\n");
499     GNUNET_SERVER_receive_done (client, GNUNET_OK);
500     return;
501   }
502   msg = (const struct GNUNET_TESTBED_InitMessage *) message;
503   msize = ntohs (message->size);
504   if (msize <= sizeof (struct GNUNET_TESTBED_InitMessage))
505   {
506     GNUNET_break (0);
507     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
508     return;
509   }
510   msize -= sizeof (struct GNUNET_TESTBED_InitMessage);
511   controller_hostname = (const char *) &msg[1];
512   if ('\0' != controller_hostname[msize - 1])
513   {
514     GNUNET_break (0);
515     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
516     return;
517   }
518   ss_str = NULL;
519   ss = NULL;
520   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (GST_config, "TESTBED",
521                                                           "SHARED_SERVICES",
522                                                           &ss_str))
523   {
524     ss = parse_shared_services (ss_str, GST_config);
525     GNUNET_free (ss_str);
526     ss_str = NULL;
527   }
528   GST_context = GNUNET_new (struct Context);
529   GNUNET_SERVER_client_keep (client);
530   GST_context->client = client;
531   GST_context->host_id = ntohl (msg->host_id);
532   GST_context->master_ip = GNUNET_strdup (controller_hostname);
533   LOG_DEBUG ("Our IP: %s\n", GST_context->master_ip);
534   GST_context->system =
535       GNUNET_TESTING_system_create ("testbed", GST_context->master_ip,
536                                     hostname, ss);
537   if (NULL != ss)
538   {
539     for (cnt = 0; NULL != ss[cnt].service; cnt++)
540     {
541       ss_str = (char *) ss[cnt].service;
542       GNUNET_free (ss_str);
543     }
544     GNUNET_free (ss);
545     ss = NULL;
546   }
547   host =
548       GNUNET_TESTBED_host_create_with_id (GST_context->host_id,
549                                           GST_context->master_ip, NULL,
550                                           GST_config, 0);
551   host_list_add (host);
552   LOG_DEBUG ("Created master context with host ID: %u\n", GST_context->host_id);
553   GNUNET_SERVER_receive_done (client, GNUNET_OK);
554 }
555
556
557 /**
558  * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST messages
559  *
560  * @param cls NULL
561  * @param client identification of the client
562  * @param message the actual message
563  */
564 static void
565 handle_add_host (void *cls, struct GNUNET_SERVER_Client *client,
566                  const struct GNUNET_MessageHeader *message)
567 {
568   struct GNUNET_TESTBED_Host *host;
569   const struct GNUNET_TESTBED_AddHostMessage *msg;
570   struct GNUNET_TESTBED_HostConfirmedMessage *reply;
571   struct GNUNET_CONFIGURATION_Handle *host_cfg;
572   char *username;
573   char *hostname;
574   char *emsg;
575   const void *ptr;
576   uint32_t host_id;
577   uint16_t username_length;
578   uint16_t hostname_length;
579   uint16_t reply_size;
580   uint16_t msize;
581
582   msg = (const struct GNUNET_TESTBED_AddHostMessage *) message;
583   msize = ntohs (msg->header.size);
584   if (msize <= sizeof (struct GNUNET_TESTBED_AddHostMessage))
585   {
586     GNUNET_break_op (0);
587     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
588     return;
589   }
590   username_length = ntohs (msg->username_length);
591   hostname_length = ntohs (msg->hostname_length);
592   /* msg must contain hostname */
593   if ((msize <= (sizeof (struct GNUNET_TESTBED_AddHostMessage) +
594                  username_length))
595       || (0 == hostname_length))
596   {
597     GNUNET_break_op (0);
598     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
599     return;
600   }
601   /* msg must contain configuration */
602   if (msize <= (sizeof (struct GNUNET_TESTBED_AddHostMessage) +
603                 username_length + hostname_length))
604   {
605     GNUNET_break_op (0);
606     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
607     return;
608   }
609   username = NULL;
610   hostname = NULL;
611   ptr = &msg[1];
612   if (0 != username_length)
613   {
614     username = GNUNET_malloc (username_length + 1);
615     strncpy (username, ptr, username_length);
616     ptr += username_length;
617   }
618   hostname = GNUNET_malloc (hostname_length + 1);
619   strncpy (hostname, 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 (NULL != 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 = NULL;
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   /* Free hello cache */
829   GST_cache_clear ();
830   GST_connection_pool_destroy ();
831   GNUNET_TESTBED_operation_queue_destroy_ (GST_opq_openfds);
832   GST_opq_openfds = NULL;
833   GST_stats_destroy ();
834   GST_barriers_destroy ();
835   GNUNET_CONFIGURATION_destroy (GST_config);
836 }
837
838
839 /**
840  * Callback for client disconnect
841  *
842  * @param cls NULL
843  * @param client the client which has disconnected
844  */
845 static void
846 client_disconnect_cb (void *cls, struct GNUNET_SERVER_Client *client)
847 {
848   if (NULL == GST_context)
849     return;
850   if (client == GST_context->client)
851   {
852     LOG (GNUNET_ERROR_TYPE_DEBUG, "Master client disconnected\n");
853     /* should not be needed as we're terminated by failure to read
854      * from stdin, but if stdin fails for some reason, this shouldn't
855      * hurt for now --- might need to revise this later if we ever
856      * decide that master connections might be temporarily down
857      * for some reason */
858     //GNUNET_SCHEDULER_shutdown ();
859   }
860 }
861
862
863 /**
864  * Testbed setup
865  *
866  * @param cls closure
867  * @param server the initialized server
868  * @param cfg configuration to use
869  */
870 static void
871 testbed_run (void *cls, struct GNUNET_SERVER_Handle *server,
872              const struct GNUNET_CONFIGURATION_Handle *cfg)
873 {
874   static const struct GNUNET_SERVER_MessageHandler message_handlers[] = {
875     {&handle_init, NULL, GNUNET_MESSAGE_TYPE_TESTBED_INIT, 0},
876     {&handle_add_host, NULL, GNUNET_MESSAGE_TYPE_TESTBED_ADD_HOST, 0},
877     {&GST_handle_link_controllers, NULL,
878      GNUNET_MESSAGE_TYPE_TESTBED_LINK_CONTROLLERS,
879      sizeof (struct GNUNET_TESTBED_ControllerLinkRequest)},
880     {&GST_handle_peer_create, NULL, GNUNET_MESSAGE_TYPE_TESTBED_CREATE_PEER, 0},
881     {&GST_handle_peer_destroy, NULL, GNUNET_MESSAGE_TYPE_TESTBED_DESTROY_PEER,
882      sizeof (struct GNUNET_TESTBED_PeerDestroyMessage)},
883     {&GST_handle_peer_start, NULL, GNUNET_MESSAGE_TYPE_TESTBED_START_PEER,
884      sizeof (struct GNUNET_TESTBED_PeerStartMessage)},
885     {&GST_handle_peer_stop, NULL, GNUNET_MESSAGE_TYPE_TESTBED_STOP_PEER,
886      sizeof (struct GNUNET_TESTBED_PeerStopMessage)},
887     {&GST_handle_peer_get_config, NULL,
888      GNUNET_MESSAGE_TYPE_TESTBED_GET_PEER_INFORMATION,
889      sizeof (struct GNUNET_TESTBED_PeerGetConfigurationMessage)},
890     {&GST_handle_overlay_connect, NULL,
891      GNUNET_MESSAGE_TYPE_TESTBED_OVERLAY_CONNECT,
892      sizeof (struct GNUNET_TESTBED_OverlayConnectMessage)},
893     {&GST_handle_remote_overlay_connect, NULL,
894      GNUNET_MESSAGE_TYPE_TESTBED_REMOTE_OVERLAY_CONNECT, 0},
895     {&GST_handle_manage_peer_service, NULL,
896      GNUNET_MESSAGE_TYPE_TESTBED_MANAGE_PEER_SERVICE, 0},
897     {&handle_slave_get_config, NULL,
898      GNUNET_MESSAGE_TYPE_TESTBED_GET_SLAVE_CONFIGURATION,
899      sizeof (struct GNUNET_TESTBED_SlaveGetConfigurationMessage)},
900     {&GST_handle_shutdown_peers, NULL, GNUNET_MESSAGE_TYPE_TESTBED_SHUTDOWN_PEERS,
901      sizeof (struct GNUNET_TESTBED_ShutdownPeersMessage)},
902     {&GST_handle_peer_reconfigure, NULL,
903      GNUNET_MESSAGE_TYPE_TESTBED_RECONFIGURE_PEER, 0},
904     {&GST_handle_barrier_init, NULL,
905      GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_INIT, 0},
906     {&GST_handle_barrier_cancel, NULL,
907      GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_CANCEL, 0},
908     {&GST_handle_barrier_status, NULL,
909      GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_STATUS, 0},
910     {NULL, NULL, 0, 0}
911   };
912   char *logfile;
913   unsigned long long num;
914
915   LOG_DEBUG ("Starting testbed\n");
916   if (GNUNET_OK ==
917       GNUNET_CONFIGURATION_get_value_filename (cfg, "TESTBED", "LOG_FILE",
918                                                &logfile))
919   {
920     GNUNET_break (GNUNET_OK == GNUNET_log_setup ("testbed", "DEBUG", logfile));
921     GNUNET_free (logfile);
922   }
923   GNUNET_assert (GNUNET_OK ==
924                  GNUNET_CONFIGURATION_get_value_number (cfg, "TESTBED",
925                                                         "CACHE_SIZE", &num));
926   GST_cache_init ((unsigned int) num);
927   GST_connection_pool_init ((unsigned int) num);
928   GNUNET_assert (GNUNET_OK ==
929                  GNUNET_CONFIGURATION_get_value_number (cfg, "TESTBED",
930                                                         "MAX_OPEN_FDS", &num));
931   GST_opq_openfds = GNUNET_TESTBED_operation_queue_create_
932       (OPERATION_QUEUE_TYPE_FIXED, (unsigned int) num);
933   GNUNET_assert (GNUNET_OK ==
934                  GNUNET_CONFIGURATION_get_value_time (cfg, "TESTBED",
935                                                       "OPERATION_TIMEOUT",
936                                                       (struct
937                                                        GNUNET_TIME_Relative *)
938                                                       &GST_timeout));
939   GNUNET_assert (GNUNET_OK ==
940                  GNUNET_CONFIGURATION_get_value_string (cfg, "testbed",
941                                                         "HOSTNAME", &hostname));
942   GST_config = GNUNET_CONFIGURATION_dup (cfg);
943   GNUNET_SERVER_add_handlers (server, message_handlers);
944   GNUNET_SERVER_disconnect_notify (server, &client_disconnect_cb, NULL);
945   shutdown_task_id =
946       GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_UNIT_FOREVER_REL,
947                                                   GNUNET_SCHEDULER_PRIORITY_IDLE,
948                                                   &shutdown_task, NULL);
949   LOG_DEBUG ("Testbed startup complete\n");
950   GST_stats_init (GST_config);
951   GST_barriers_init (GST_config);
952 }
953
954
955 /**
956  * The starting point of execution
957  */
958 int
959 main (int argc, char *const *argv)
960 {
961   //sleep (15);                 /* Debugging */
962   return (GNUNET_OK ==
963           GNUNET_SERVICE_run (argc, argv, "testbed", GNUNET_SERVICE_OPTION_NONE,
964                               &testbed_run, NULL)) ? 0 : 1;
965 }
966
967 /* end of gnunet-service-testbed.c */