no null byte needed in message payload, so GNUNET_memcpy fits better
[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      SPDX-License-Identifier: AGPL3.0-or-later
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  * Our hostname; we give this to all the peers we start
87  */
88 static char *hostname;
89
90
91 /**
92  * Function to add a host to the current list of known hosts
93  *
94  * @param host the host to add
95  * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure due to host-id
96  *           already in use
97  */
98 static int
99 host_list_add (struct GNUNET_TESTBED_Host *host)
100 {
101   uint32_t host_id;
102
103   host_id = GNUNET_TESTBED_host_get_id_ (host);
104   if (GST_host_list_size <= host_id)
105     GST_array_grow_large_enough (GST_host_list, GST_host_list_size, host_id);
106   if (NULL != GST_host_list[host_id])
107   {
108     LOG_DEBUG ("A host with id: %u already exists\n", host_id);
109     return GNUNET_SYSERR;
110   }
111   GST_host_list[host_id] = host;
112   return GNUNET_OK;
113 }
114
115
116 /**
117  * Send operation failure message to client
118  *
119  * @param client the client to which the failure message has to be sent to
120  * @param operation_id the id of the failed operation
121  * @param emsg the error message; can be NULL
122  */
123 void
124 GST_send_operation_fail_msg (struct GNUNET_SERVICE_Client *client,
125                              uint64_t operation_id,
126                              const char *emsg)
127 {
128   struct GNUNET_MQ_Envelope *env;
129   struct GNUNET_TESTBED_OperationFailureEventMessage *msg;
130   uint16_t emsg_len;
131
132   emsg_len = (NULL == emsg) ? 0 : strlen (emsg) + 1;
133   env = GNUNET_MQ_msg_extra (msg,
134                              emsg_len,
135                              GNUNET_MESSAGE_TYPE_TESTBED_OPERATION_FAIL_EVENT);
136   msg->event_type = htonl (GNUNET_TESTBED_ET_OPERATION_FINISHED);
137   msg->operation_id = GNUNET_htonll (operation_id);
138   GNUNET_memcpy (&msg[1],
139                  emsg,
140                  emsg_len);
141   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
142                   env);
143 }
144
145
146 /**
147  * Function to send generic operation success message to given client
148  *
149  * @param client the client to send the message to
150  * @param operation_id the id of the operation which was successful
151  */
152 void
153 GST_send_operation_success_msg (struct GNUNET_SERVICE_Client *client,
154                                 uint64_t operation_id)
155 {
156   struct GNUNET_MQ_Envelope *env;
157   struct GNUNET_TESTBED_GenericOperationSuccessEventMessage *msg;
158
159   env = GNUNET_MQ_msg (msg,
160                        GNUNET_MESSAGE_TYPE_TESTBED_GENERIC_OPERATION_SUCCESS);
161   msg->operation_id = GNUNET_htonll (operation_id);
162   msg->event_type = htonl (GNUNET_TESTBED_ET_OPERATION_FINISHED);
163   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
164                   env);
165 }
166
167
168 /**
169  * Callback which will be called after a host registration succeeded or failed
170  *
171  * @param cls the handle to the slave at which the registration is completed
172  * @param emsg the error message; NULL if host registration is successful
173  */
174 static void
175 hr_completion (void *cls,
176                const char *emsg);
177
178
179 /**
180  * Attempts to register the next host in the host registration queue
181  *
182  * @param slave the slave controller whose host registration queue is checked
183  *          for host registrations
184  */
185 static void
186 register_next_host (struct Slave *slave)
187 {
188   struct HostRegistration *hr;
189
190   hr = slave->hr_dll_head;
191   GNUNET_assert (NULL != hr);
192   GNUNET_assert (NULL == slave->rhandle);
193   LOG (GNUNET_ERROR_TYPE_DEBUG, "Registering host %u at %u\n",
194        GNUNET_TESTBED_host_get_id_ (hr->host),
195        GNUNET_TESTBED_host_get_id_ (GST_host_list[slave->host_id]));
196   slave->rhandle
197     = GNUNET_TESTBED_register_host (slave->controller,
198                                     hr->host,
199                                     hr_completion,
200                                     slave);
201 }
202
203
204 /**
205  * Callback which will be called to after a host registration succeeded or failed
206  *
207  * @param cls the handle to the slave at which the registration is completed
208  * @param emsg the error message; NULL if host registration is successful
209  */
210 static void
211 hr_completion (void *cls,
212                const char *emsg)
213 {
214   struct Slave *slave = cls;
215   struct HostRegistration *hr;
216
217   slave->rhandle = NULL;
218   hr = slave->hr_dll_head;
219   GNUNET_assert (NULL != hr);
220   LOG (GNUNET_ERROR_TYPE_DEBUG,
221        "Registering host %u at %u successful\n",
222        GNUNET_TESTBED_host_get_id_ (hr->host),
223        GNUNET_TESTBED_host_get_id_ (GST_host_list[slave->host_id]));
224   GNUNET_CONTAINER_DLL_remove (slave->hr_dll_head,
225                                slave->hr_dll_tail,
226                                hr);
227   if (NULL != hr->cb)
228     hr->cb (hr->cb_cls,
229             emsg);
230   GNUNET_free (hr);
231   if (NULL != slave->hr_dll_head)
232     register_next_host (slave);
233 }
234
235
236 /**
237  * Adds a host registration's request to a slave's registration queue
238  *
239  * @param slave the slave controller at which the given host has to be
240  *          registered
241  * @param cb the host registration completion callback
242  * @param cb_cls the closure for the host registration completion callback
243  * @param host the host which has to be registered
244  */
245 void
246 GST_queue_host_registration (struct Slave *slave,
247                              GNUNET_TESTBED_HostRegistrationCompletion cb,
248                              void *cb_cls,
249                              struct GNUNET_TESTBED_Host *host)
250 {
251   struct HostRegistration *hr;
252   int call_register;
253
254   LOG (GNUNET_ERROR_TYPE_DEBUG,
255        "Queueing host registration for host %u at %u\n",
256        GNUNET_TESTBED_host_get_id_ (host),
257        GNUNET_TESTBED_host_get_id_ (GST_host_list[slave->host_id]));
258   hr = GNUNET_new (struct HostRegistration);
259   hr->cb = cb;
260   hr->cb_cls = cb_cls;
261   hr->host = host;
262   call_register = (NULL == slave->hr_dll_head) ? GNUNET_YES : GNUNET_NO;
263   GNUNET_CONTAINER_DLL_insert_tail (slave->hr_dll_head,
264                                     slave->hr_dll_tail,
265                                     hr);
266   if (GNUNET_YES == call_register)
267     register_next_host (slave);
268 }
269
270
271 /**
272  * Callback to relay the reply msg of a forwarded operation back to the client
273  *
274  * @param cls ForwardedOperationContext
275  * @param msg the message to relay
276  */
277 void
278 GST_forwarded_operation_reply_relay (void *cls,
279                                      const struct GNUNET_MessageHeader *msg)
280 {
281   struct ForwardedOperationContext *fopc = cls;
282   struct GNUNET_MQ_Envelope *env;
283
284   LOG_DEBUG ("Relaying message with type: %u, size: %u\n",
285              ntohs (msg->type),
286              ntohs (msg->size));
287   env = GNUNET_MQ_msg_copy (msg);
288   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (fopc->client),
289                   env);
290   GNUNET_SCHEDULER_cancel (fopc->timeout_task);
291   GNUNET_CONTAINER_DLL_remove (fopcq_head,
292                                fopcq_tail,
293                                fopc);
294   GNUNET_free (fopc);
295 }
296
297
298 /**
299  * Task to free resources when forwarded operation has been timed out
300  *
301  * @param cls the ForwardedOperationContext
302  */
303 void
304 GST_forwarded_operation_timeout (void *cls)
305 {
306   struct ForwardedOperationContext *fopc = cls;
307
308   fopc->timeout_task = NULL;
309   GNUNET_TESTBED_forward_operation_msg_cancel_ (fopc->opc);
310   LOG (GNUNET_ERROR_TYPE_DEBUG,
311        "A forwarded operation has timed out\n");
312   GST_send_operation_fail_msg (fopc->client,
313                                fopc->operation_id,
314                                "A forwarded operation has timed out");
315   GNUNET_CONTAINER_DLL_remove (fopcq_head,
316                                fopcq_tail,
317                                fopc);
318   GNUNET_free (fopc);
319 }
320
321
322 /**
323  * Parse service sharing specification line.
324  * Format is "[<service:share>] [<service:share>] ..."
325  *
326  * @param ss_str the spec string to be parsed
327  * @param cfg the configuration to use for shared services
328  * @return an array suitable to pass to GNUNET_TESTING_system_create().  NULL
329  *           upon empty service sharing specification.
330  */
331 static struct GNUNET_TESTING_SharedService *
332 parse_shared_services (char *ss_str,
333                        struct GNUNET_CONFIGURATION_Handle *cfg)
334 {
335   struct GNUNET_TESTING_SharedService ss;
336   struct GNUNET_TESTING_SharedService *slist;
337   char service[256];
338   char *arg;
339   unsigned int n;
340 #define GROW_SS                                 \
341   do {                                          \
342     GNUNET_array_grow (slist, n, n+1);                                  \
343     GNUNET_memcpy (&slist[n - 1], &ss,                                  \
344                    sizeof (struct GNUNET_TESTING_SharedService));       \
345   } while (0)
346
347   slist = NULL;
348   n = 0;
349   ss.cfg = cfg;
350   for (; NULL != (arg = strtok (ss_str, " ")); ss_str = NULL)
351   {
352     ss.service = NULL;
353     ss.share = 0;
354     if (2 != sscanf (arg, "%255[^:]:%u",
355                      service,
356                      &ss.share))
357     {
358       LOG (GNUNET_ERROR_TYPE_WARNING,
359            "Ignoring shared service spec: %s",
360            arg);
361       continue;
362     }
363     LOG_DEBUG ("Will be sharing %s service among %u peers\n",
364                service,
365                ss.share);
366     ss.service = GNUNET_strdup (service);
367     GROW_SS;
368   }
369   if (NULL != slist)
370   {
371     /* Add trailing NULL block */
372     (void) memset (&ss,
373                    0,
374                    sizeof (struct GNUNET_TESTING_SharedService));
375     GROW_SS;
376   }
377   return slist;
378 #undef GROW_SS
379 }
380
381
382 /**
383  * Check #GNUNET_MESSAGE_TYPE_TESTBED_INIT messages
384  *
385  * @param cls identification of the client
386  * @param message the actual message
387  * @return #GNUNET_OK if @a message is well-formed
388  */
389 static int
390 check_init (void *cls,
391             const struct GNUNET_TESTBED_InitMessage *msg)
392 {
393   const char *controller_hostname;
394   uint16_t msize;
395
396   msize = ntohs (msg->header.size) - sizeof (struct GNUNET_TESTBED_InitMessage);
397   controller_hostname = (const char *) &msg[1];
398   if ('\0' != controller_hostname[msize - 1])
399   {
400     GNUNET_break (0);
401     return GNUNET_SYSERR;
402   }
403   return GNUNET_OK;
404 }
405
406
407 /**
408  * Message handler for #GNUNET_MESSAGE_TYPE_TESTBED_INIT messages
409  *
410  * @param cls identification of the client
411  * @param message the actual message
412  */
413 static void
414 handle_init (void *cls,
415              const struct GNUNET_TESTBED_InitMessage *msg)
416 {
417   struct GNUNET_SERVICE_Client *client = cls;
418   struct GNUNET_TESTBED_Host *host;
419   const char *controller_hostname;
420   char *ss_str;
421   struct GNUNET_TESTING_SharedService *ss;
422   unsigned int cnt;
423
424   if (NULL != GST_context)
425   {
426     LOG_DEBUG ("We are being connected to laterally\n");
427     GNUNET_SERVICE_client_continue (client);
428     return;
429   }
430   controller_hostname = (const char *) &msg[1];
431   ss_str = NULL;
432   ss = NULL;
433   if (GNUNET_OK ==
434       GNUNET_CONFIGURATION_get_value_string (GST_config,
435                                              "TESTBED",
436                                              "SHARED_SERVICES",
437                                              &ss_str))
438   {
439     ss = parse_shared_services (ss_str,
440                                 GST_config);
441     GNUNET_free (ss_str);
442     ss_str = NULL;
443   }
444   GST_context = GNUNET_new (struct Context);
445   GST_context->client = client;
446   GST_context->host_id = ntohl (msg->host_id);
447   GST_context->master_ip = GNUNET_strdup (controller_hostname);
448   LOG_DEBUG ("Our IP: %s\n",
449              GST_context->master_ip);
450   GST_context->system
451     = GNUNET_TESTING_system_create ("testbed",
452                                     GST_context->master_ip,
453                                     hostname,
454                                     ss);
455   if (NULL != ss)
456   {
457     for (cnt = 0; NULL != ss[cnt].service; cnt++)
458     {
459       ss_str = (char *) ss[cnt].service;
460       GNUNET_free (ss_str);
461     }
462     GNUNET_free (ss);
463     ss = NULL;
464   }
465   host =
466       GNUNET_TESTBED_host_create_with_id (GST_context->host_id,
467                                           GST_context->master_ip,
468                                           NULL,
469                                           GST_config,
470                                           0);
471   host_list_add (host);
472   LOG_DEBUG ("Created master context with host ID: %u\n",
473              GST_context->host_id);
474   GNUNET_SERVICE_client_continue (client);
475 }
476
477
478 /**
479  * Check #GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST messages
480  *
481  * @param cls identification of the client
482  * @param msg the actual message
483  * @return #GNUNET_OK if @a message is well-formed
484  */
485 static int
486 check_add_host (void *cls,
487                  const struct GNUNET_TESTBED_AddHostMessage *msg)
488 {
489   uint16_t username_length;
490   uint16_t hostname_length;
491   uint16_t msize;
492
493   msize = ntohs (msg->header.size) - sizeof (struct GNUNET_TESTBED_AddHostMessage);
494   username_length = ntohs (msg->username_length);
495   hostname_length = ntohs (msg->hostname_length);
496   /* msg must contain hostname */
497   if ( (msize <= username_length) ||
498        (0 == hostname_length) )
499   {
500     GNUNET_break (0);
501     return GNUNET_SYSERR;
502   }
503   /* msg must contain configuration */
504   if (msize <= username_length + hostname_length)
505   {
506     GNUNET_break (0);
507     return GNUNET_SYSERR;
508   }
509   return GNUNET_OK;
510 }
511
512
513 /**
514  * Message handler for #GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST messages
515  *
516  * @param cls identification of the client
517  * @param msg the actual message
518  */
519 static void
520 handle_add_host (void *cls,
521                  const struct GNUNET_TESTBED_AddHostMessage *msg)
522 {
523   struct GNUNET_SERVICE_Client *client = cls;
524   struct GNUNET_TESTBED_Host *host;
525   struct GNUNET_TESTBED_HostConfirmedMessage *reply;
526   struct GNUNET_CONFIGURATION_Handle *host_cfg;
527   char *username;
528   char *hostname;
529   char *emsg;
530   const void *ptr;
531   uint32_t host_id;
532   uint16_t username_length;
533   uint16_t hostname_length;
534   struct GNUNET_MQ_Envelope *env;
535
536   username_length = ntohs (msg->username_length);
537   hostname_length = ntohs (msg->hostname_length);
538   username = NULL;
539   hostname = NULL;
540   ptr = &msg[1];
541   if (0 != username_length)
542   {
543     username = GNUNET_malloc (username_length + 1);
544     GNUNET_strlcpy (username, ptr, username_length + 1);
545     ptr += username_length;
546   }
547   hostname = GNUNET_malloc (hostname_length + 1);
548   GNUNET_strlcpy (hostname, ptr, hostname_length + 1);
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 */