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