-need to do mkdir before testing UNIXPATH, not sure how this worked before
[oweals/gnunet.git] / src / testbed / gnunet-service-testbed_peers.c
1 /*
2   This file is part of GNUnet.
3   (C) 2008--2013 Christian Grothoff (and other contributing authors)
4
5   GNUnet is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published
7   by the Free Software Foundation; either version 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 /**
23  * @file testbed/gnunet-service-testbed_peers.c
24  * @brief implementation of TESTBED service that deals with peer management
25  * @author Sree Harsha Totakura <sreeharsha@totakura.in>
26  */
27
28 #include "gnunet-service-testbed.h"
29 #include "gnunet_arm_service.h"
30 #include <zlib.h>
31
32
33 /**
34  * A list of peers we know about
35  */
36 struct Peer **GST_peer_list;
37
38 /**
39  * The current number of peers running locally under this controller
40  */
41 unsigned int GST_num_local_peers;
42
43
44 /**
45  * Context information to manage peers' services
46  */
47 struct ManageServiceContext
48 {
49   /**
50    * DLL next ptr
51    */
52   struct ManageServiceContext *next;
53
54   /**
55    * DLL prev ptr
56    */
57   struct ManageServiceContext *prev;
58
59   /**
60    * The ARM handle of the peer
61    */
62   struct GNUNET_ARM_Handle *ah;
63
64   /**
65    * peer whose service has to be managed
66    */
67   struct Peer *peer;
68
69   /**
70    * The client which requested to manage the peer's service
71    */
72   struct GNUNET_SERVER_Client *client;
73
74   /**
75    * The operation id of the associated request
76    */
77   uint64_t op_id;
78
79   /**
80    * 1 if the service at the peer has to be started; 0 if it has to be stopped
81    */
82   uint8_t start;
83
84   /**
85    * Is this context expired?  Do not work on this context if it is set to
86    * GNUNET_YES
87    */
88   uint8_t expired;
89 };
90
91
92 /**
93  * Context information for peer re-configure operations
94  */
95 struct PeerReconfigureContext
96 {
97   /**
98    * DLL next for inclusoin in peer reconfigure operations list
99    */
100   struct PeerReconfigureContext *next;
101
102   /**
103    * DLL prev
104    */
105   struct PeerReconfigureContext *prev;
106
107   /**
108    * The client which gave this operation to us
109    */
110   struct GNUNET_SERVER_Client *client;
111
112   /**
113    * The configuration handle to use as the new template
114    */
115   struct GNUNET_CONFIGURATION_Handle *cfg;
116
117   /**
118    * The id of the operation
119    */
120   uint64_t op_id;
121
122   /**
123    * The id of the peer which has to be reconfigured
124    */
125   uint32_t peer_id;
126
127   /**
128    * The the peer stopped?  Used while cleaning up this context to decide
129    * whether the asynchronous stop request through Testing/ARM API has to be
130    * cancelled
131    */
132   uint8_t stopped;
133 };
134
135 /**
136  * The DLL head for the peer reconfigure list
137  */
138 static struct PeerReconfigureContext *prc_head;
139
140 /**
141  * The DLL tail for the peer reconfigure list
142  */
143 static struct PeerReconfigureContext *prc_tail;
144
145
146
147 /**
148  * DLL head for queue of manage service requests
149  */
150 static struct ManageServiceContext *mctx_head;
151
152 /**
153  * DLL tail for queue of manage service requests
154  */
155 static struct ManageServiceContext *mctx_tail;
156
157
158 /**
159  * Adds a peer to the peer array
160  *
161  * @param peer the peer to add
162  */
163 static void
164 peer_list_add (struct Peer *peer)
165 {
166   if (peer->id >= GST_peer_list_size)
167     GST_array_grow_large_enough (GST_peer_list, GST_peer_list_size, peer->id);
168   GNUNET_assert (NULL == GST_peer_list[peer->id]);
169   GST_peer_list[peer->id] = peer;
170   if (GNUNET_NO == peer->is_remote)
171     GST_num_local_peers++;
172 }
173
174
175 /**
176  * Removes a the give peer from the peer array
177  *
178  * @param peer the peer to be removed
179  */
180 static void
181 peer_list_remove (struct Peer *peer)
182 {
183   unsigned int orig_size;
184   uint32_t id;
185
186   if (GNUNET_NO == peer->is_remote)
187     GST_num_local_peers--;
188   GST_peer_list[peer->id] = NULL;
189   orig_size = GST_peer_list_size;
190   while (GST_peer_list_size >= LIST_GROW_STEP)
191   {
192     for (id = GST_peer_list_size - 1;
193          (id >= GST_peer_list_size - LIST_GROW_STEP) && (id != UINT32_MAX);
194          id--)
195       if (NULL != GST_peer_list[id])
196         break;
197     if (id != ((GST_peer_list_size - LIST_GROW_STEP) - 1))
198       break;
199     GST_peer_list_size -= LIST_GROW_STEP;
200   }
201   if (orig_size == GST_peer_list_size)
202     return;
203   GST_peer_list =
204       GNUNET_realloc (GST_peer_list,
205                       sizeof (struct Peer *) * GST_peer_list_size);
206 }
207
208
209 /**
210  * The task to be executed if the forwarded peer create operation has been
211  * timed out
212  *
213  * @param cls the FowardedOperationContext
214  * @param tc the TaskContext from the scheduler
215  */
216 static void
217 peer_create_forward_timeout (void *cls,
218                              const struct GNUNET_SCHEDULER_TaskContext *tc)
219 {
220   struct ForwardedOperationContext *fopc = cls;
221
222   GNUNET_free (fopc->cls);
223   GST_forwarded_operation_timeout (fopc, tc);
224 }
225
226
227 /**
228  * Callback to be called when forwarded peer create operation is successfull. We
229  * have to relay the reply msg back to the client
230  *
231  * @param cls ForwardedOperationContext
232  * @param msg the peer create success message
233  */
234 static void
235 peer_create_success_cb (void *cls, const struct GNUNET_MessageHeader *msg)
236 {
237   struct ForwardedOperationContext *fopc = cls;
238   struct Peer *remote_peer;
239
240   if (ntohs (msg->type) == GNUNET_MESSAGE_TYPE_TESTBED_CREATE_PEER_SUCCESS)
241   {
242     GNUNET_assert (NULL != fopc->cls);
243     remote_peer = fopc->cls;
244     peer_list_add (remote_peer);
245   }
246   GST_forwarded_operation_reply_relay (fopc, msg);
247 }
248
249
250 /**
251  * Function to destroy a peer
252  *
253  * @param peer the peer structure to destroy
254  */
255 void
256 GST_destroy_peer (struct Peer *peer)
257 {
258   GNUNET_break (0 == peer->reference_cnt);
259   if (GNUNET_YES == peer->is_remote)
260   {
261     peer_list_remove (peer);
262     GNUNET_free (peer);
263     return;
264   }
265   if (GNUNET_YES == peer->details.local.is_running)
266   {
267     GNUNET_TESTING_peer_stop (peer->details.local.peer);
268     peer->details.local.is_running = GNUNET_NO;
269   }
270   GNUNET_TESTING_peer_destroy (peer->details.local.peer);
271   GNUNET_CONFIGURATION_destroy (peer->details.local.cfg);
272   peer_list_remove (peer);
273   GNUNET_free (peer);
274 }
275
276
277 /**
278  * Callback to be called when forwarded peer destroy operation is successfull. We
279  * have to relay the reply msg back to the client
280  *
281  * @param cls ForwardedOperationContext
282  * @param msg the peer create success message
283  */
284 static void
285 peer_destroy_success_cb (void *cls, const struct GNUNET_MessageHeader *msg)
286 {
287   struct ForwardedOperationContext *fopc = cls;
288   struct Peer *remote_peer;
289
290   if (GNUNET_MESSAGE_TYPE_TESTBED_GENERIC_OPERATION_SUCCESS ==
291       ntohs (msg->type))
292   {
293     remote_peer = fopc->cls;
294     GNUNET_assert (NULL != remote_peer);
295     remote_peer->destroy_flag = GNUNET_YES;
296     if (0 == remote_peer->reference_cnt)
297       GST_destroy_peer (remote_peer);
298   }
299   GST_forwarded_operation_reply_relay (fopc, msg);
300 }
301
302
303 /**
304  * Handler for GNUNET_MESSAGE_TYPE_TESTBED_CREATEPEER messages
305  *
306  * @param cls NULL
307  * @param client identification of the client
308  * @param message the actual message
309  */
310 void
311 GST_handle_peer_create (void *cls, struct GNUNET_SERVER_Client *client,
312                         const struct GNUNET_MessageHeader *message)
313 {
314   const struct GNUNET_TESTBED_PeerCreateMessage *msg;
315   struct GNUNET_TESTBED_PeerCreateSuccessEventMessage *reply;
316   struct GNUNET_CONFIGURATION_Handle *cfg;
317   struct ForwardedOperationContext *fo_ctxt;
318   struct Route *route;
319   struct Peer *peer;
320   char *emsg;
321   uint32_t host_id;
322   uint32_t peer_id;
323   uint16_t msize;
324
325
326   msize = ntohs (message->size);
327   if (msize <= sizeof (struct GNUNET_TESTBED_PeerCreateMessage))
328   {
329     GNUNET_break (0);           /* We need configuration */
330     GNUNET_SERVER_receive_done (client, GNUNET_OK);
331     return;
332   }
333   msg = (const struct GNUNET_TESTBED_PeerCreateMessage *) message;
334   host_id = ntohl (msg->host_id);
335   peer_id = ntohl (msg->peer_id);
336   if (VALID_PEER_ID (peer_id))
337   {
338     (void) GNUNET_asprintf (&emsg, "Peer with ID %u already exists", peer_id);
339     GST_send_operation_fail_msg (client, GNUNET_ntohll (msg->operation_id),
340                                  emsg);
341     GNUNET_free (emsg);
342     GNUNET_SERVER_receive_done (client, GNUNET_OK);
343     return;
344   }
345   if (UINT32_MAX == peer_id)
346   {
347     GST_send_operation_fail_msg (client, GNUNET_ntohll (msg->operation_id),
348                                  "Cannot create peer with given ID");
349     GNUNET_SERVER_receive_done (client, GNUNET_OK);
350     return;
351   }
352   if (host_id == GST_context->host_id)
353   {
354     /* We are responsible for this peer */
355     cfg = GNUNET_TESTBED_extract_config_ (message);
356     if (NULL == cfg)
357     {
358       GNUNET_break (0);
359       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
360       return;
361     }
362     GNUNET_CONFIGURATION_set_value_number (cfg, "TESTBED", "PEERID",
363                                            (unsigned long long) peer_id);
364     peer = GNUNET_malloc (sizeof (struct Peer));
365     peer->is_remote = GNUNET_NO;
366     peer->details.local.cfg = cfg;
367     peer->id = peer_id;
368     LOG_DEBUG ("Creating peer with id: %u\n", (unsigned int) peer->id);
369     peer->details.local.peer =
370         GNUNET_TESTING_peer_configure (GST_context->system,
371                                        peer->details.local.cfg, peer->id,
372                                        NULL /* Peer id */ ,
373                                        &emsg);
374     if (NULL == peer->details.local.peer)
375     {
376       LOG (GNUNET_ERROR_TYPE_WARNING, "Configuring peer failed: %s\n", emsg);
377       GNUNET_free (emsg);
378       GNUNET_free (peer);
379       GNUNET_break (0);
380       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
381       return;
382     }
383     peer->details.local.is_running = GNUNET_NO;
384     peer_list_add (peer);
385     reply =
386         GNUNET_malloc (sizeof
387                        (struct GNUNET_TESTBED_PeerCreateSuccessEventMessage));
388     reply->header.size =
389         htons (sizeof (struct GNUNET_TESTBED_PeerCreateSuccessEventMessage));
390     reply->header.type =
391         htons (GNUNET_MESSAGE_TYPE_TESTBED_CREATE_PEER_SUCCESS);
392     reply->peer_id = msg->peer_id;
393     reply->operation_id = msg->operation_id;
394     GST_queue_message (client, &reply->header);
395     GNUNET_SERVER_receive_done (client, GNUNET_OK);
396     return;
397   }
398
399   /* Forward peer create request */
400   route = GST_find_dest_route (host_id);
401   if (NULL == route)
402   {
403     GNUNET_break (0);
404     GNUNET_SERVER_receive_done (client, GNUNET_OK);
405     return;
406   }
407   peer = GNUNET_malloc (sizeof (struct Peer));
408   peer->is_remote = GNUNET_YES;
409   peer->id = peer_id;
410   peer->details.remote.slave = GST_slave_list[route->dest];
411   peer->details.remote.remote_host_id = host_id;
412   fo_ctxt = GNUNET_malloc (sizeof (struct ForwardedOperationContext));
413   GNUNET_SERVER_client_keep (client);
414   fo_ctxt->client = client;
415   fo_ctxt->operation_id = GNUNET_ntohll (msg->operation_id);
416   fo_ctxt->cls = peer;
417   fo_ctxt->type = OP_PEER_CREATE;
418   fo_ctxt->opc =
419       GNUNET_TESTBED_forward_operation_msg_ (GST_slave_list
420                                              [route->dest]->controller,
421                                              fo_ctxt->operation_id,
422                                              &msg->header,
423                                              peer_create_success_cb, fo_ctxt);
424   fo_ctxt->timeout_task =
425       GNUNET_SCHEDULER_add_delayed (GST_timeout, &peer_create_forward_timeout,
426                                     fo_ctxt);
427   GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fo_ctxt);
428   GNUNET_SERVER_receive_done (client, GNUNET_OK);
429 }
430
431
432 /**
433  * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_DESTROYPEER messages
434  *
435  * @param cls NULL
436  * @param client identification of the client
437  * @param message the actual message
438  */
439 void
440 GST_handle_peer_destroy (void *cls, struct GNUNET_SERVER_Client *client,
441                          const struct GNUNET_MessageHeader *message)
442 {
443   const struct GNUNET_TESTBED_PeerDestroyMessage *msg;
444   struct ForwardedOperationContext *fopc;
445   struct Peer *peer;
446   uint32_t peer_id;
447
448   msg = (const struct GNUNET_TESTBED_PeerDestroyMessage *) message;
449   peer_id = ntohl (msg->peer_id);
450   LOG_DEBUG ("Received peer destory on peer: %u and operation id: %ul\n",
451              peer_id, GNUNET_ntohll (msg->operation_id));
452   if (!VALID_PEER_ID (peer_id))
453   {
454     LOG (GNUNET_ERROR_TYPE_ERROR,
455          "Asked to destroy a non existent peer with id: %u\n", peer_id);
456     GST_send_operation_fail_msg (client, GNUNET_ntohll (msg->operation_id),
457                                  "Peer doesn't exist");
458     GNUNET_SERVER_receive_done (client, GNUNET_OK);
459     return;
460   }
461   peer = GST_peer_list[peer_id];
462   if (GNUNET_YES == peer->is_remote)
463   {
464     /* Forward the destory message to sub controller */
465     fopc = GNUNET_malloc (sizeof (struct ForwardedOperationContext));
466     GNUNET_SERVER_client_keep (client);
467     fopc->client = client;
468     fopc->cls = peer;
469     fopc->type = OP_PEER_DESTROY;
470     fopc->operation_id = GNUNET_ntohll (msg->operation_id);
471     fopc->opc =
472         GNUNET_TESTBED_forward_operation_msg_ (peer->details.remote.
473                                                slave->controller,
474                                                fopc->operation_id, &msg->header,
475                                                &peer_destroy_success_cb, fopc);
476     fopc->timeout_task =
477         GNUNET_SCHEDULER_add_delayed (GST_timeout, &GST_forwarded_operation_timeout,
478                                       fopc);
479     GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fopc);
480     GNUNET_SERVER_receive_done (client, GNUNET_OK);
481     return;
482   }
483   peer->destroy_flag = GNUNET_YES;
484   if (0 == peer->reference_cnt)
485     GST_destroy_peer (peer);
486   else
487     LOG (GNUNET_ERROR_TYPE_DEBUG,
488          "Delaying peer destroy as peer is currently in use\n");
489   GST_send_operation_success_msg (client, GNUNET_ntohll (msg->operation_id));
490   GNUNET_SERVER_receive_done (client, GNUNET_OK);
491 }
492
493
494 /**
495  * Stats a peer
496  *
497  * @param peer the peer to start
498  * @return GNUNET_OK upon success; GNUNET_SYSERR upon failure
499  */
500 static int
501 start_peer (struct Peer *peer)
502 {
503   GNUNET_assert (GNUNET_NO == peer->is_remote);
504   if (GNUNET_OK != GNUNET_TESTING_peer_start (peer->details.local.peer))
505     return GNUNET_SYSERR;
506   peer->details.local.is_running = GNUNET_YES;
507   return GNUNET_OK;
508 }
509
510
511 /**
512  * Stops a peer
513  *
514  * @param peer the peer to stop
515  * @return GNUNET_OK upon success; GNUNET_SYSERR upon failure
516  */
517 static int
518 stop_peer (struct Peer *peer)
519 {
520   GNUNET_assert (GNUNET_NO == peer->is_remote);
521   if (GNUNET_OK != GNUNET_TESTING_peer_kill (peer->details.local.peer))
522     return GNUNET_SYSERR;
523   peer->details.local.is_running = GNUNET_NO;
524   return GNUNET_OK;
525 }
526
527
528 /**
529  * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_DESTROYPEER messages
530  *
531  * @param cls NULL
532  * @param client identification of the client
533  * @param message the actual message
534  */
535 void
536 GST_handle_peer_start (void *cls, struct GNUNET_SERVER_Client *client,
537                        const struct GNUNET_MessageHeader *message)
538 {
539   const struct GNUNET_TESTBED_PeerStartMessage *msg;
540   struct GNUNET_TESTBED_PeerEventMessage *reply;
541   struct ForwardedOperationContext *fopc;
542   struct Peer *peer;
543   uint32_t peer_id;
544
545   msg = (const struct GNUNET_TESTBED_PeerStartMessage *) message;
546   peer_id = ntohl (msg->peer_id);
547   if (!VALID_PEER_ID (peer_id))
548   {
549     GNUNET_break (0);
550     LOG (GNUNET_ERROR_TYPE_ERROR,
551          "Asked to start a non existent peer with id: %u\n", peer_id);
552     GNUNET_SERVER_receive_done (client, GNUNET_OK);
553     return;
554   }
555   peer = GST_peer_list[peer_id];
556   if (GNUNET_YES == peer->is_remote)
557   {
558     fopc = GNUNET_malloc (sizeof (struct ForwardedOperationContext));
559     GNUNET_SERVER_client_keep (client);
560     fopc->client = client;
561     fopc->operation_id = GNUNET_ntohll (msg->operation_id);
562     fopc->type = OP_PEER_START;
563     fopc->opc =
564         GNUNET_TESTBED_forward_operation_msg_ (peer->details.remote.
565                                                slave->controller,
566                                                fopc->operation_id, &msg->header,
567                                                &GST_forwarded_operation_reply_relay,
568                                                fopc);
569     fopc->timeout_task =
570         GNUNET_SCHEDULER_add_delayed (GST_timeout, &GST_forwarded_operation_timeout,
571                                       fopc);
572     GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fopc);
573     GNUNET_SERVER_receive_done (client, GNUNET_OK);
574     return;
575   }
576   if (GNUNET_OK != start_peer (peer))
577   {
578     GST_send_operation_fail_msg (client, GNUNET_ntohll (msg->operation_id),
579                                  "Failed to start");
580     GNUNET_SERVER_receive_done (client, GNUNET_OK);
581     return;
582   }
583   reply = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_PeerEventMessage));
584   reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_PEER_EVENT);
585   reply->header.size = htons (sizeof (struct GNUNET_TESTBED_PeerEventMessage));
586   reply->event_type = htonl (GNUNET_TESTBED_ET_PEER_START);
587   reply->host_id = htonl (GST_context->host_id);
588   reply->peer_id = msg->peer_id;
589   reply->operation_id = msg->operation_id;
590   GST_queue_message (client, &reply->header);
591   GNUNET_SERVER_receive_done (client, GNUNET_OK);
592 }
593
594
595 /**
596  * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_DESTROYPEER messages
597  *
598  * @param cls NULL
599  * @param client identification of the client
600  * @param message the actual message
601  */
602 void
603 GST_handle_peer_stop (void *cls, struct GNUNET_SERVER_Client *client,
604                       const struct GNUNET_MessageHeader *message)
605 {
606   const struct GNUNET_TESTBED_PeerStopMessage *msg;
607   struct GNUNET_TESTBED_PeerEventMessage *reply;
608   struct ForwardedOperationContext *fopc;
609   struct Peer *peer;
610   uint32_t peer_id;
611
612   msg = (const struct GNUNET_TESTBED_PeerStopMessage *) message;
613   peer_id = ntohl (msg->peer_id);
614   LOG (GNUNET_ERROR_TYPE_DEBUG, "Received PEER_STOP for peer %u\n", peer_id);
615   if (!VALID_PEER_ID (peer_id))
616   {
617     GST_send_operation_fail_msg (client, GNUNET_ntohll (msg->operation_id),
618                                  "Peer not found");
619     GNUNET_SERVER_receive_done (client, GNUNET_OK);
620     return;
621   }
622   peer = GST_peer_list[peer_id];
623   if (GNUNET_YES == peer->is_remote)
624   {
625     LOG (GNUNET_ERROR_TYPE_DEBUG, "Forwarding PEER_STOP for peer %u\n",
626          peer_id);
627     fopc = GNUNET_malloc (sizeof (struct ForwardedOperationContext));
628     GNUNET_SERVER_client_keep (client);
629     fopc->client = client;
630     fopc->operation_id = GNUNET_ntohll (msg->operation_id);
631     fopc->type = OP_PEER_STOP;
632     fopc->opc =
633         GNUNET_TESTBED_forward_operation_msg_ (peer->details.remote.
634                                                slave->controller,
635                                                fopc->operation_id, &msg->header,
636                                                &GST_forwarded_operation_reply_relay,
637                                                fopc);
638     fopc->timeout_task =
639         GNUNET_SCHEDULER_add_delayed (GST_timeout, &GST_forwarded_operation_timeout,
640                                       fopc);
641     GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fopc);
642     GNUNET_SERVER_receive_done (client, GNUNET_OK);
643     return;
644   }
645   if (GNUNET_OK != stop_peer (peer))
646   {
647     LOG (GNUNET_ERROR_TYPE_WARNING, "Stopping peer %u failed\n", peer_id);
648     GST_send_operation_fail_msg (client, GNUNET_ntohll (msg->operation_id),
649                                  "Peer not running");
650     GNUNET_SERVER_receive_done (client, GNUNET_OK);
651     return;
652   }
653   LOG (GNUNET_ERROR_TYPE_DEBUG, "Peer %u successfully stopped\n", peer_id);
654   reply = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_PeerEventMessage));
655   reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_PEER_EVENT);
656   reply->header.size = htons (sizeof (struct GNUNET_TESTBED_PeerEventMessage));
657   reply->event_type = htonl (GNUNET_TESTBED_ET_PEER_STOP);
658   reply->host_id = htonl (GST_context->host_id);
659   reply->peer_id = msg->peer_id;
660   reply->operation_id = msg->operation_id;
661   GST_queue_message (client, &reply->header);
662   GNUNET_SERVER_receive_done (client, GNUNET_OK);
663   GNUNET_TESTING_peer_wait (peer->details.local.peer);
664 }
665
666
667 /**
668  * Handler for GNUNET_MESSAGE_TYPE_TESTBED_GETPEERCONFIG messages
669  *
670  * @param cls NULL
671  * @param client identification of the client
672  * @param message the actual message
673  */
674 void
675 GST_handle_peer_get_config (void *cls, struct GNUNET_SERVER_Client *client,
676                             const struct GNUNET_MessageHeader *message)
677 {
678   const struct GNUNET_TESTBED_PeerGetConfigurationMessage *msg;
679   struct GNUNET_TESTBED_PeerConfigurationInformationMessage *reply;
680   struct ForwardedOperationContext *fopc;
681   struct Peer *peer;
682   char *config;
683   char *xconfig;
684   size_t c_size;
685   size_t xc_size;
686   uint32_t peer_id;
687   uint16_t msize;
688
689   msg = (const struct GNUNET_TESTBED_PeerGetConfigurationMessage *) message;
690   peer_id = ntohl (msg->peer_id);
691   LOG_DEBUG ("Received GET_CONFIG for peer %u\n", peer_id);
692   if (!VALID_PEER_ID (peer_id))
693   {
694     GST_send_operation_fail_msg (client, GNUNET_ntohll (msg->operation_id),
695                                  "Peer not found");
696     GNUNET_SERVER_receive_done (client, GNUNET_OK);
697     return;
698   }
699   peer = GST_peer_list[peer_id];
700   if (GNUNET_YES == peer->is_remote)
701   {
702     LOG_DEBUG ("Forwarding PEER_GET_CONFIG for peer: %u\n", peer_id);
703     fopc = GNUNET_malloc (sizeof (struct ForwardedOperationContext));
704     GNUNET_SERVER_client_keep (client);
705     fopc->client = client;
706     fopc->operation_id = GNUNET_ntohll (msg->operation_id);
707     fopc->type = OP_PEER_INFO;
708     fopc->opc =
709         GNUNET_TESTBED_forward_operation_msg_ (peer->details.remote.
710                                                slave->controller,
711                                                fopc->operation_id, &msg->header,
712                                                &GST_forwarded_operation_reply_relay,
713                                                fopc);
714     fopc->timeout_task =
715         GNUNET_SCHEDULER_add_delayed (GST_timeout, &GST_forwarded_operation_timeout,
716                                       fopc);
717     GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fopc);
718     GNUNET_SERVER_receive_done (client, GNUNET_OK);
719     return;
720   }
721   LOG_DEBUG ("Received PEER_GET_CONFIG for peer: %u\n", peer_id);
722   config =
723       GNUNET_CONFIGURATION_serialize (GST_peer_list[peer_id]->details.local.cfg,
724                                       &c_size);
725   xc_size = GNUNET_TESTBED_compress_config_ (config, c_size, &xconfig);
726   GNUNET_free (config);
727   msize =
728       xc_size +
729       sizeof (struct GNUNET_TESTBED_PeerConfigurationInformationMessage);
730   reply = GNUNET_realloc (xconfig, msize);
731   (void) memmove (&reply[1], reply, xc_size);
732   reply->header.size = htons (msize);
733   reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_PEER_INFORMATION);
734   reply->peer_id = msg->peer_id;
735   reply->operation_id = msg->operation_id;
736   GNUNET_TESTING_peer_get_identity (GST_peer_list[peer_id]->details.local.peer,
737                                     &reply->peer_identity);
738   reply->config_size = htons ((uint16_t) c_size);
739   GST_queue_message (client, &reply->header);
740   GNUNET_SERVER_receive_done (client, GNUNET_OK);
741 }
742
743
744 /**
745  * Cleans up the given PeerReconfigureContext
746  *
747  * @param prc the PeerReconfigureContext
748  */
749 static void
750 cleanup_prc (struct PeerReconfigureContext *prc)
751 {
752   struct Peer *peer;
753
754   if (VALID_PEER_ID (prc->peer_id))
755   {
756     peer = GST_peer_list [prc->peer_id];
757     if (1 != prc->stopped)
758     {
759       GNUNET_TESTING_peer_stop_async_cancel (peer->details.local.peer);
760       stop_peer (peer);         /* Stop the peer synchronously */
761     }
762   }
763   if (NULL != prc->cfg)
764     GNUNET_CONFIGURATION_destroy (prc->cfg);
765   GNUNET_SERVER_client_drop (prc->client);
766   GNUNET_CONTAINER_DLL_remove (prc_head, prc_tail, prc);
767   GNUNET_free (prc);
768 }
769
770
771 /**
772  * Cleans up the Peer reconfigure context list
773  */
774 void
775 GST_free_prcq ()
776 {
777   while (NULL != prc_head)
778     cleanup_prc (prc_head);
779 }
780
781
782 /**
783  * Update peer configuration
784  *
785  * @param peer the peer to update
786  * @param cfg the new configuration
787  * @return error message (freshly allocated); NULL upon success
788  */
789 static char *
790 update_peer_config (struct Peer *peer,
791                     struct GNUNET_CONFIGURATION_Handle *cfg)
792 {
793   char *emsg;
794
795   GNUNET_TESTING_peer_destroy (peer->details.local.peer);
796   GNUNET_CONFIGURATION_destroy (peer->details.local.cfg);
797   peer->details.local.cfg = cfg;
798   emsg = NULL;  
799   peer->details.local.peer
800       = GNUNET_TESTING_peer_configure (GST_context->system,
801                                        peer->details.local.cfg, peer->id,
802                                        NULL /* Peer id */ ,
803                                        &emsg);
804   return emsg;
805 }
806
807
808 /**
809  * Callback to inform whether the peer is running or stopped.
810  *
811  * @param cls the closure given to GNUNET_TESTING_peer_stop_async()
812  * @param p the respective peer whose status is being reported
813  * @param success GNUNET_YES if the peer is stopped; GNUNET_SYSERR upon any
814  *          error
815  */
816 static void
817 prc_stop_cb (void *cls, struct GNUNET_TESTING_Peer *p, int success)
818 {
819   struct PeerReconfigureContext *prc = cls;
820   struct Peer *peer;
821   char *emsg;
822
823   GNUNET_assert (VALID_PEER_ID (prc->peer_id));
824   peer = GST_peer_list [prc->peer_id];
825   GNUNET_assert (GNUNET_NO == peer->is_remote);
826   emsg = update_peer_config (peer, prc->cfg);
827   prc->cfg = NULL;
828   prc->stopped = 1;
829   if (NULL != emsg)
830   {
831     GST_send_operation_fail_msg (prc->client, prc->op_id, emsg);
832     goto cleanup;
833   }
834   if (GNUNET_OK != start_peer (peer))
835   {
836     GST_send_operation_fail_msg (prc->client, prc->op_id,
837                                  "Failed to start reconfigured peer");
838     goto cleanup;
839   }
840   GST_send_operation_success_msg (prc->client, prc->op_id);
841
842  cleanup:
843   cleanup_prc (prc);
844   return;
845 }
846
847
848 /**
849  * Handler for GNUNET_MESSAGE_TYPDE_TESTBED_RECONFIGURE_PEER type messages.
850  * Should stop the peer asyncronously, destroy it and create it again with the
851  * new configuration.
852  *
853  * @param cls NULL
854  * @param client identification of the client
855  * @param message the actual message
856  */
857 void
858 GST_handle_peer_reconfigure (void *cls, struct GNUNET_SERVER_Client *client,
859                              const struct GNUNET_MessageHeader *message)
860 {
861   const struct GNUNET_TESTBED_PeerReconfigureMessage *msg;
862   struct Peer *peer;
863   struct GNUNET_CONFIGURATION_Handle *cfg;
864   struct ForwardedOperationContext *fopc;
865   struct PeerReconfigureContext *prc;
866   char *emsg;
867   uint64_t op_id;
868   uint32_t peer_id;
869   uint16_t msize;
870
871   msize = ntohs (message->size);
872   if (msize <= sizeof (struct GNUNET_TESTBED_PeerReconfigureMessage))
873   {
874     GNUNET_break_op (0);
875     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
876     return;
877   }
878   msg = (const struct GNUNET_TESTBED_PeerReconfigureMessage *) message;
879   peer_id = ntohl (msg->peer_id);
880   op_id = GNUNET_ntohll (msg->operation_id);
881   if (!VALID_PEER_ID (peer_id))
882   {
883     GNUNET_break (0);
884     GST_send_operation_fail_msg (client, op_id, "Peer not found");
885     GNUNET_SERVER_receive_done (client, GNUNET_OK);
886     return;
887   }
888   peer = GST_peer_list[peer_id];
889   if (GNUNET_YES == peer->is_remote)
890   {
891     LOG_DEBUG ("Forwarding PEER_RECONFIGURE for peer: %u\n", peer_id);
892     fopc = GNUNET_malloc (sizeof (struct ForwardedOperationContext));
893     GNUNET_SERVER_client_keep (client);
894     fopc->client = client;
895     fopc->operation_id = op_id;
896     fopc->type = OP_PEER_RECONFIGURE;
897     fopc->opc =
898         GNUNET_TESTBED_forward_operation_msg_ (peer->details.remote.
899                                                slave->controller,
900                                                fopc->operation_id, &msg->header,
901                                                &GST_forwarded_operation_reply_relay,
902                                                fopc);
903     fopc->timeout_task =
904         GNUNET_SCHEDULER_add_delayed (GST_timeout, &GST_forwarded_operation_timeout,
905                                       fopc);
906     GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fopc);
907     GNUNET_SERVER_receive_done (client, GNUNET_OK);
908     return;
909   }
910   LOG_DEBUG ("Received PEER_RECONFIGURE for peer %u\n", peer_id);
911   if (0 < peer->reference_cnt)
912   {
913     GNUNET_break (0);
914     GST_send_operation_fail_msg (client, op_id, "Peer in use");
915     GNUNET_SERVER_receive_done (client, GNUNET_OK);
916     return;
917   }
918   if (GNUNET_YES == peer->destroy_flag)
919   {
920     GNUNET_break (0);
921     GST_send_operation_fail_msg (client, op_id, "Peer is being destroyed");
922     GNUNET_SERVER_receive_done (client, GNUNET_OK);
923     return;
924   }
925   cfg = GNUNET_TESTBED_extract_config_ (message);
926   if (NULL == cfg)
927   {
928     GNUNET_break (0);
929     GST_send_operation_fail_msg (client, op_id, "Compression error");
930     GNUNET_SERVER_receive_done (client, GNUNET_OK);
931     return;
932   }
933   if (GNUNET_NO == peer->details.local.is_running)
934   {
935     emsg = update_peer_config (peer, cfg);
936     if (NULL != emsg)
937       GST_send_operation_fail_msg (client, op_id, emsg);
938     GST_send_operation_success_msg (client, op_id);
939     GNUNET_SERVER_receive_done (client, GNUNET_OK);
940     return;
941   }
942   prc = GNUNET_malloc (sizeof (struct PeerReconfigureContext));
943   if (GNUNET_OK != 
944       GNUNET_TESTING_peer_stop_async (peer->details.local.peer, &prc_stop_cb,
945                                       prc))
946   {
947     GNUNET_assert (0 < GNUNET_asprintf (&emsg,
948                                         "Error trying to stop peer %u asynchronously\n", 
949                                         peer_id));
950     LOG (GNUNET_ERROR_TYPE_ERROR, "%s\n", emsg);
951     GST_send_operation_fail_msg (client, op_id, emsg);
952     GNUNET_SERVER_receive_done (client, GNUNET_OK);
953     GNUNET_free (prc);
954     return;
955   }
956   prc->cfg = cfg;
957   prc->peer_id = peer_id;
958   prc->op_id = op_id;
959   prc->client = client;
960   GNUNET_SERVER_client_keep (client);
961   GNUNET_CONTAINER_DLL_insert_tail (prc_head, prc_tail, prc);
962   GNUNET_SERVER_receive_done (client, GNUNET_OK);
963 }
964
965
966 /**
967  * Cleanup the context information created for managing a peer's service
968  *
969  * @param mctx the ManageServiceContext
970  */
971 static void
972 cleanup_mctx (struct ManageServiceContext *mctx)
973 {
974   mctx->expired = GNUNET_YES;
975   GNUNET_CONTAINER_DLL_remove (mctx_head, mctx_tail, mctx);
976   GNUNET_SERVER_client_drop (mctx->client);
977   GNUNET_ARM_disconnect_and_free (mctx->ah);
978   GNUNET_assert (0 < mctx->peer->reference_cnt);
979   mctx->peer->reference_cnt--;
980   if ( (GNUNET_YES == mctx->peer->destroy_flag)
981        && (0 == mctx->peer->reference_cnt) )
982     GST_destroy_peer (mctx->peer);
983   GNUNET_free (mctx);
984 }
985
986
987 /**
988  * Frees the ManageServiceContext queue
989  */
990 void
991 GST_free_mctxq ()
992 {
993   while (NULL != mctx_head)
994     cleanup_mctx (mctx_head);
995 }
996
997
998 /**
999  * Returns a string interpretation of 'rs'
1000  *
1001  * @param rs the request status from ARM
1002  * @return a string interpretation of the request status
1003  */
1004 static const char *
1005 arm_req_string (enum GNUNET_ARM_RequestStatus rs)
1006 {
1007   switch (rs)
1008   {
1009   case GNUNET_ARM_REQUEST_SENT_OK:
1010     return _("Message was sent successfully");
1011   case GNUNET_ARM_REQUEST_CONFIGURATION_ERROR:
1012     return _("Misconfiguration (can't connect to the ARM service)");
1013   case GNUNET_ARM_REQUEST_DISCONNECTED:
1014     return _("We disconnected from ARM before we could send a request");
1015   case GNUNET_ARM_REQUEST_BUSY:
1016     return _("ARM API is busy");
1017   case GNUNET_ARM_REQUEST_TOO_LONG:
1018     return _("Request doesn't fit into a message");
1019   case GNUNET_ARM_REQUEST_TIMEOUT:
1020     return _("Request timed out");
1021   }
1022   return _("Unknown request status");
1023 }
1024
1025
1026 /**
1027  * Returns a string interpretation of the 'result'
1028  *
1029  * @param result the arm result
1030  * @return a string interpretation
1031  */
1032 static const char *
1033 arm_ret_string (enum GNUNET_ARM_Result result)
1034 {
1035   switch (result)
1036   {
1037   case GNUNET_ARM_RESULT_STOPPED:
1038     return _("%s is stopped");
1039   case GNUNET_ARM_RESULT_STARTING:
1040     return _("%s is starting");
1041   case GNUNET_ARM_RESULT_STOPPING:
1042     return _("%s is stopping");
1043   case GNUNET_ARM_RESULT_IS_STARTING_ALREADY:
1044     return _("%s is starting already");
1045   case GNUNET_ARM_RESULT_IS_STOPPING_ALREADY:
1046     return _("%s is stopping already");
1047   case GNUNET_ARM_RESULT_IS_STARTED_ALREADY:
1048     return _("%s is started already");
1049   case GNUNET_ARM_RESULT_IS_STOPPED_ALREADY:
1050     return _("%s is stopped already");
1051   case GNUNET_ARM_RESULT_IS_NOT_KNOWN:
1052     return _("%s service is not known to ARM");
1053   case GNUNET_ARM_RESULT_START_FAILED:
1054     return _("%s service failed to start");
1055   case GNUNET_ARM_RESULT_IN_SHUTDOWN:
1056     return _("%s service can't be started because ARM is shutting down");
1057   }
1058   return _("%.s Unknown result code.");
1059 }
1060
1061
1062 /**
1063  * Function called in response to a start/stop request.
1064  * Will be called when request was not sent successfully,
1065  * or when a reply comes. If the request was not sent successfully,
1066  * 'rs' will indicate that, and 'service' and 'result' will be undefined.
1067  *
1068  * @param cls ManageServiceContext
1069  * @param rs status of the request
1070  * @param service service name
1071  * @param result result of the operation
1072  */
1073 static void
1074 service_manage_result_cb (void *cls,
1075                           enum GNUNET_ARM_RequestStatus rs,
1076                           const char *service, enum GNUNET_ARM_Result result)
1077 {
1078   struct ManageServiceContext *mctx = cls;
1079   char *emsg;
1080
1081   emsg = NULL;
1082   if (GNUNET_YES == mctx->expired)
1083     return;
1084   if (GNUNET_ARM_REQUEST_SENT_OK != rs)
1085   {
1086     GNUNET_asprintf (&emsg, "Error communicating with Peer %u's ARM: %s",
1087                      mctx->peer->id, arm_req_string (rs));
1088     goto ret;
1089   }
1090   if (1 == mctx->start)
1091     goto service_start_check;
1092   if (! ((GNUNET_ARM_RESULT_STOPPED == result)
1093             || (GNUNET_ARM_RESULT_STOPPING == result)
1094             || (GNUNET_ARM_RESULT_IS_STOPPING_ALREADY == result)
1095             || (GNUNET_ARM_RESULT_IS_STOPPED_ALREADY == result)) )
1096   {
1097     /* stopping a service failed */
1098     GNUNET_asprintf (&emsg, arm_ret_string (result), service);
1099     goto ret;
1100   }
1101   /* service stopped successfully */
1102   goto ret;
1103
1104  service_start_check:
1105   if (! ((GNUNET_ARM_RESULT_STARTING == result)
1106             || (GNUNET_ARM_RESULT_IS_STARTING_ALREADY == result)
1107             || (GNUNET_ARM_RESULT_IS_STARTED_ALREADY == result)) )
1108   {
1109     /* starting a service failed */
1110     GNUNET_asprintf (&emsg, arm_ret_string (result), service);
1111     goto ret;
1112   }
1113   /* service started successfully */
1114
1115  ret:
1116   if (NULL != emsg)
1117   {
1118     LOG_DEBUG ("%s\n", emsg);
1119     GST_send_operation_fail_msg (mctx->client, mctx->op_id, emsg);
1120   }
1121   else
1122     GST_send_operation_success_msg (mctx->client, mctx->op_id);
1123   GNUNET_free_non_null (emsg);
1124   cleanup_mctx (mctx);
1125 }
1126
1127
1128 /**
1129  * Handler for GNUNET_TESTBED_ManagePeerServiceMessage message
1130  *
1131  * @param cls NULL
1132  * @param client identification of client
1133  * @param message the actual message
1134  */
1135 void
1136 GST_handle_manage_peer_service (void *cls, struct GNUNET_SERVER_Client *client,
1137                                 const struct GNUNET_MessageHeader *message)
1138 {
1139   const struct GNUNET_TESTBED_ManagePeerServiceMessage *msg;
1140   const char* service;
1141   struct Peer *peer;
1142   char *emsg;
1143   struct GNUNET_ARM_Handle *ah;
1144   struct ManageServiceContext *mctx;
1145   struct ForwardedOperationContext *fopc;
1146   uint64_t op_id;
1147   uint32_t peer_id;
1148   uint16_t msize;
1149
1150
1151   msize = ntohs (message->size);
1152   if (msize <= sizeof (struct GNUNET_TESTBED_ManagePeerServiceMessage))
1153   {
1154     GNUNET_break_op (0);
1155     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1156     return;
1157   }
1158   msg = (const struct GNUNET_TESTBED_ManagePeerServiceMessage *) message;
1159   service = (const char *) &msg[1];
1160   if ('\0' != service[msize - sizeof
1161                       (struct GNUNET_TESTBED_ManagePeerServiceMessage) - 1])
1162   {
1163     GNUNET_break_op (0);
1164     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1165     return;
1166   }
1167   if (1 < msg->start)
1168   {
1169     GNUNET_break_op (0);
1170     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1171     return;
1172   }
1173   peer_id = ntohl (msg->peer_id);
1174   op_id = GNUNET_ntohll (msg->operation_id);
1175   LOG_DEBUG ("Received request to manage service %s on peer %u\n",
1176              service, (unsigned int) peer_id);
1177   if ((GST_peer_list_size <= peer_id)
1178       || (NULL == (peer = GST_peer_list[peer_id])))
1179   {
1180     GNUNET_asprintf (&emsg, "Asked to manage service of a non existent peer "
1181                      "with id: %u", peer_id);
1182     goto err_ret;
1183   }
1184   if (0 == strcasecmp ("arm", service))
1185   {
1186     emsg = GNUNET_strdup ("Cannot start/stop peer's ARM service.  "
1187                           "Use peer start/stop for that");
1188     goto err_ret;
1189   }
1190   if (GNUNET_YES == peer->is_remote)
1191   {
1192     /* Forward the destory message to sub controller */
1193     fopc = GNUNET_malloc (sizeof (struct ForwardedOperationContext));
1194     GNUNET_SERVER_client_keep (client);
1195     fopc->client = client;
1196     fopc->cls = peer;
1197     fopc->type = OP_MANAGE_SERVICE;
1198     fopc->operation_id = op_id;
1199     fopc->opc =
1200         GNUNET_TESTBED_forward_operation_msg_ (peer->details.remote.
1201                                                slave->controller,
1202                                                fopc->operation_id, &msg->header,
1203                                                &GST_forwarded_operation_reply_relay,
1204                                                fopc);
1205     fopc->timeout_task =
1206         GNUNET_SCHEDULER_add_delayed (GST_timeout, &GST_forwarded_operation_timeout,
1207                                       fopc);
1208     GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fopc);
1209     GNUNET_SERVER_receive_done (client, GNUNET_OK);
1210     return;
1211   }
1212   if (GNUNET_NO == peer->details.local.is_running)
1213   {
1214     emsg = GNUNET_strdup ("Peer not running\n");
1215     goto err_ret;
1216   }
1217   if ((0 != peer->reference_cnt)
1218       && ( (0 == strcasecmp ("core", service))
1219            || (0 == strcasecmp ("transport", service)) )  )
1220   {
1221     GNUNET_asprintf (&emsg, "Cannot stop %s service of peer with id: %u "
1222                      "since it is required by existing operations",
1223                      service, peer_id);
1224     goto err_ret;
1225   }
1226   ah = GNUNET_ARM_connect (peer->details.local.cfg, NULL, NULL);
1227   if (NULL == ah)
1228   {
1229     GNUNET_asprintf (&emsg,
1230                      "Cannot connect to ARM service of peer with id: %u",
1231                      peer_id);
1232     goto err_ret;
1233   }
1234   mctx = GNUNET_malloc (sizeof (struct ManageServiceContext));
1235   mctx->peer = peer;
1236   peer->reference_cnt++;
1237   mctx->op_id = op_id;
1238   mctx->ah = ah;
1239   GNUNET_SERVER_client_keep (client);
1240   mctx->client = client;
1241   mctx->start = msg->start;
1242   GNUNET_CONTAINER_DLL_insert_tail (mctx_head, mctx_tail, mctx);
1243   if (1 == mctx->start)
1244     GNUNET_ARM_request_service_start (mctx->ah, service,
1245                                       GNUNET_OS_INHERIT_STD_ERR,
1246                                       GST_timeout,
1247                                       service_manage_result_cb,
1248                                       mctx);
1249   else
1250     GNUNET_ARM_request_service_stop (mctx->ah, service,
1251                                      GST_timeout,
1252                                      service_manage_result_cb,
1253                                      mctx);
1254   GNUNET_SERVER_receive_done (client, GNUNET_OK);
1255   return;
1256
1257  err_ret:
1258   LOG (GNUNET_ERROR_TYPE_ERROR, "%s\n", emsg);
1259   GST_send_operation_fail_msg (client, op_id, emsg);
1260   GNUNET_free (emsg);
1261   GNUNET_SERVER_receive_done (client, GNUNET_OK);
1262 }
1263
1264
1265 /**
1266  * Stops and destroys all peers
1267  */
1268 void
1269 GST_destroy_peers ()
1270 {
1271   struct Peer *peer;
1272   unsigned int id;
1273
1274   if (NULL == GST_peer_list)
1275     return;
1276   for (id = 0; id < GST_peer_list_size; id++)
1277   {
1278     peer = GST_peer_list[id];
1279     if (NULL == peer)
1280       continue;
1281     /* If destroy flag is set it means that this peer should have been
1282      * destroyed by a context which we destroy before */
1283     GNUNET_break (GNUNET_NO == peer->destroy_flag);
1284     /* counter should be zero as we free all contexts before */
1285     GNUNET_break (0 == peer->reference_cnt);
1286     if ((GNUNET_NO == peer->is_remote) &&
1287         (GNUNET_YES == peer->details.local.is_running))
1288       GNUNET_TESTING_peer_kill (peer->details.local.peer);
1289   }
1290   for (id = 0; id < GST_peer_list_size; id++)
1291   {
1292     peer = GST_peer_list[id];
1293     if (NULL == peer)
1294       continue;
1295     if (GNUNET_NO == peer->is_remote)
1296     {
1297       if (GNUNET_YES == peer->details.local.is_running)
1298         GNUNET_TESTING_peer_wait (peer->details.local.peer);
1299       GNUNET_TESTING_peer_destroy (peer->details.local.peer);
1300       GNUNET_CONFIGURATION_destroy (peer->details.local.cfg);
1301     }
1302     GNUNET_free (peer);
1303   }
1304   GNUNET_free_non_null (GST_peer_list);
1305   GST_peer_list = NULL;
1306   GST_peer_list_size = 0;
1307 }
1308
1309
1310 /**
1311  * Task run upon timeout of forwarded SHUTDOWN_PEERS operation
1312  *
1313  * @param cls the ForwardedOperationContext
1314  * @param tc the scheduler task context
1315  */
1316 static void
1317 shutdown_peers_timeout_cb (void *cls,
1318                            const struct GNUNET_SCHEDULER_TaskContext *tc)
1319 {
1320   struct ForwardedOperationContext *fo_ctxt = cls;
1321   struct HandlerContext_ShutdownPeers *hc;
1322
1323   fo_ctxt->timeout_task = GNUNET_SCHEDULER_NO_TASK;
1324   hc = fo_ctxt->cls;
1325   hc->timeout = GNUNET_YES;
1326   GNUNET_assert (0 < hc->nslaves);
1327   hc->nslaves--;
1328   if (0 == hc->nslaves)
1329   {
1330     GST_send_operation_fail_msg (fo_ctxt->client, fo_ctxt->operation_id,
1331                                  "Timeout at a slave controller");
1332     GNUNET_free (hc);
1333     hc = NULL;
1334   }
1335   GNUNET_TESTBED_forward_operation_msg_cancel_ (fo_ctxt->opc);
1336   GNUNET_SERVER_client_drop (fo_ctxt->client);
1337   GNUNET_CONTAINER_DLL_remove (fopcq_head, fopcq_tail, fo_ctxt);
1338   GNUNET_free (fo_ctxt);
1339 }
1340
1341
1342 /**
1343  * The reply msg handler forwarded SHUTDOWN_PEERS operation.  Checks if a
1344  * success reply is received from all clients and then sends the success message
1345  * to the client
1346  *
1347  * @param cls ForwardedOperationContext
1348  * @param msg the message to relay
1349  */
1350 static void
1351 shutdown_peers_reply_cb (void *cls,
1352                          const struct GNUNET_MessageHeader *msg)
1353 {
1354   struct ForwardedOperationContext *fo_ctxt = cls;
1355   struct HandlerContext_ShutdownPeers *hc;
1356
1357   hc = fo_ctxt->cls;
1358   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != fo_ctxt->timeout_task);
1359   GNUNET_SCHEDULER_cancel (fo_ctxt->timeout_task);
1360   fo_ctxt->timeout_task = GNUNET_SCHEDULER_NO_TASK;
1361   GNUNET_assert (0 < hc->nslaves);
1362   hc->nslaves--;
1363   if (GNUNET_MESSAGE_TYPE_TESTBED_GENERIC_OPERATION_SUCCESS !=
1364       ntohs (msg->type))
1365     hc->timeout = GNUNET_YES;
1366   if (0 == hc->nslaves)
1367   {
1368     if (GNUNET_YES == hc->timeout)
1369       GST_send_operation_fail_msg (fo_ctxt->client, fo_ctxt->operation_id,
1370                                    "Timeout at a slave controller");
1371     else
1372       GST_send_operation_success_msg (fo_ctxt->client, fo_ctxt->operation_id);
1373     GNUNET_free (hc);
1374     hc = NULL;
1375   }
1376   GNUNET_SERVER_client_drop (fo_ctxt->client);
1377   GNUNET_CONTAINER_DLL_remove (fopcq_head, fopcq_tail, fo_ctxt);
1378   GNUNET_free (fo_ctxt);
1379 }
1380
1381
1382 /**
1383  * Handler for GNUNET_MESSAGE_TYPE_TESTBED_SHUTDOWN_PEERS messages
1384  *
1385  * @param cls NULL
1386  * @param client identification of the client
1387  * @param message the actual message
1388  */
1389 void
1390 GST_handle_shutdown_peers (void *cls, struct GNUNET_SERVER_Client *client,
1391                            const struct GNUNET_MessageHeader *message)
1392 {
1393   const struct GNUNET_TESTBED_ShutdownPeersMessage *msg;
1394   struct HandlerContext_ShutdownPeers *hc;
1395   struct Slave *slave;
1396   struct ForwardedOperationContext *fo_ctxt;
1397   uint64_t op_id;
1398   unsigned int cnt;
1399
1400   msg = (const struct GNUNET_TESTBED_ShutdownPeersMessage *) message;
1401   LOG_DEBUG ("Received SHUTDOWN_PEERS\n");
1402     /* Stop and destroy all peers */
1403   GST_free_mctxq ();
1404   GST_free_occq ();
1405   GST_free_roccq ();
1406   GST_clear_fopcq ();
1407   /* Forward to all slaves which we have started */
1408   op_id = GNUNET_ntohll (msg->operation_id);
1409   hc = GNUNET_malloc (sizeof (struct HandlerContext_ShutdownPeers));
1410   /* FIXME: have a better implementation where we track which slaves are
1411      started by this controller */
1412   for (cnt = 0; cnt < GST_slave_list_size; cnt++)
1413   {
1414     slave = GST_slave_list[cnt];
1415     if (NULL == slave)
1416       continue;
1417     if (NULL == slave->controller_proc) /* We didn't start the slave */
1418       continue;
1419     LOG_DEBUG ("Forwarding SHUTDOWN_PEERS\n");
1420     hc->nslaves++;
1421     fo_ctxt = GNUNET_malloc (sizeof (struct ForwardedOperationContext));
1422     GNUNET_SERVER_client_keep (client);
1423     fo_ctxt->client = client;
1424     fo_ctxt->operation_id = op_id;
1425     fo_ctxt->cls = hc;
1426     fo_ctxt->type = OP_SHUTDOWN_PEERS;
1427     fo_ctxt->opc =
1428         GNUNET_TESTBED_forward_operation_msg_ (slave->controller,
1429                                                fo_ctxt->operation_id,
1430                                                &msg->header,
1431                                                shutdown_peers_reply_cb,
1432                                                fo_ctxt);
1433     fo_ctxt->timeout_task =
1434         GNUNET_SCHEDULER_add_delayed (GST_timeout, &shutdown_peers_timeout_cb,
1435                                       fo_ctxt);
1436     GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fo_ctxt);
1437   }
1438   LOG_DEBUG ("Shutting down peers\n");
1439   GST_destroy_peers ();
1440   if (0 == hc->nslaves)
1441   {
1442     GST_send_operation_success_msg (client, op_id);
1443     GNUNET_free (hc);
1444   }
1445   GNUNET_SERVER_receive_done (client, GNUNET_OK);
1446 }