1792373a321cf2da08e45b46161c8f36d9ab1289
[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  * Callback to inform whether the peer is running or stopped.
784  *
785  * @param cls the closure given to GNUNET_TESTING_peer_stop_async()
786  * @param p the respective peer whose status is being reported
787  * @param success GNUNET_YES if the peer is stopped; GNUNET_SYSERR upon any
788  *          error
789  */
790 static void
791 prc_stop_cb (void *cls, struct GNUNET_TESTING_Peer *p, int success)
792 {
793   struct PeerReconfigureContext *prc = cls;
794   struct Peer *peer;
795   char *emsg;
796   
797   GNUNET_assert (VALID_PEER_ID (prc->peer_id));
798   peer = GST_peer_list [prc->peer_id];
799   GNUNET_assert (GNUNET_NO == peer->is_remote);
800   GNUNET_TESTING_peer_destroy (peer->details.local.peer);
801   GNUNET_CONFIGURATION_destroy (peer->details.local.cfg);
802   peer->details.local.cfg = prc->cfg;
803   prc->cfg = NULL;
804   prc->stopped = 1;
805   emsg = NULL;
806   peer->details.local.peer
807       = GNUNET_TESTING_peer_configure (GST_context->system,
808                                        peer->details.local.cfg, peer->id,
809                                        NULL /* Peer id */ ,
810                                        &emsg);
811   if (NULL == peer->details.local.peer)
812   {
813     GST_send_operation_fail_msg (prc->client, prc->op_id, emsg);
814     goto cleanup;
815   }
816   if (GNUNET_OK != start_peer (peer))
817   {
818     
819     GST_send_operation_fail_msg (prc->client, prc->op_id,
820                                  "Failed to start reconfigured peer");
821     goto cleanup;
822   }
823   GST_send_operation_success_msg (prc->client, prc->op_id);
824   
825  cleanup:
826   cleanup_prc (prc);
827   return;
828 }
829
830
831 /**
832  * Handler for GNUNET_MESSAGE_TYPDE_TESTBED_RECONFIGURE_PEER type messages.
833  * Should stop the peer asyncronously, destroy it and create it again with the
834  * new configuration.
835  *
836  * @param cls NULL
837  * @param client identification of the client
838  * @param message the actual message
839  */
840 void
841 GST_handle_peer_reconfigure (void *cls, struct GNUNET_SERVER_Client *client,
842                              const struct GNUNET_MessageHeader *message)
843 {
844   const struct GNUNET_TESTBED_PeerReconfigureMessage *msg;
845   struct Peer *peer;
846   struct GNUNET_CONFIGURATION_Handle *cfg;
847   struct ForwardedOperationContext *fopc;
848   struct PeerReconfigureContext *prc;
849   uint64_t op_id;
850   uint32_t peer_id;
851   uint16_t msize;
852   
853   msize = ntohs (message->size);
854   if (msize <= sizeof (struct GNUNET_TESTBED_PeerReconfigureMessage))
855   {
856     GNUNET_break_op (0);
857     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
858     return;
859   }
860   msg = (const struct GNUNET_TESTBED_PeerReconfigureMessage *) message;
861   peer_id = ntohl (msg->peer_id);
862   op_id = GNUNET_ntohll (msg->operation_id);
863   if (!VALID_PEER_ID (peer_id))
864   {
865     GNUNET_break (0);
866     GST_send_operation_fail_msg (client, op_id, "Peer not found");
867     GNUNET_SERVER_receive_done (client, GNUNET_OK);
868     return;
869   }
870   peer = GST_peer_list[peer_id];
871   if (GNUNET_YES == peer->is_remote)
872   {
873     LOG_DEBUG ("Forwarding PEER_RECONFIGURE for peer: %u\n", peer_id);
874     fopc = GNUNET_malloc (sizeof (struct ForwardedOperationContext));
875     GNUNET_SERVER_client_keep (client);
876     fopc->client = client;
877     fopc->operation_id = op_id;
878     fopc->type = OP_PEER_RECONFIGURE;
879     fopc->opc =
880         GNUNET_TESTBED_forward_operation_msg_ (peer->details.remote.
881                                                slave->controller,
882                                                fopc->operation_id, &msg->header,
883                                                &GST_forwarded_operation_reply_relay,
884                                                fopc);
885     fopc->timeout_task =
886         GNUNET_SCHEDULER_add_delayed (GST_timeout, &GST_forwarded_operation_timeout,
887                                       fopc);
888     GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fopc);
889     GNUNET_SERVER_receive_done (client, GNUNET_OK);
890     return;
891   }
892   LOG_DEBUG ("Received PEER_RECONFIGURE for peer %u\n", peer_id);
893   if (0 < peer->reference_cnt)
894   {
895     GNUNET_break (0);
896     GST_send_operation_fail_msg (client, op_id, "Peer in use");
897     GNUNET_SERVER_receive_done (client, GNUNET_OK);
898     return;
899   }
900   if (GNUNET_YES == peer->destroy_flag)
901   {
902     GNUNET_break (0);
903     GST_send_operation_fail_msg (client, op_id, "Peer is being destroyed");
904     GNUNET_SERVER_receive_done (client, GNUNET_OK);
905     return;
906   }
907   cfg = GNUNET_TESTBED_extract_config_ (message);
908   if (NULL == cfg)
909   {
910     GNUNET_break (0);    
911     GST_send_operation_fail_msg (client, op_id, "Compression error");
912     GNUNET_SERVER_receive_done (client, GNUNET_OK);
913     return;
914   }
915   prc = GNUNET_malloc (sizeof (struct PeerReconfigureContext));
916   prc->cfg = cfg;
917   prc->peer_id = peer_id;
918   prc->op_id = op_id;
919   prc->client = client;  
920   GNUNET_SERVER_client_keep (client);
921   GNUNET_CONTAINER_DLL_insert_tail (prc_head, prc_tail, prc);
922   GNUNET_TESTING_peer_stop_async (peer->details.local.peer, prc_stop_cb, prc);
923   GNUNET_SERVER_receive_done (client, GNUNET_OK);
924 }
925
926
927 /**
928  * Cleanup the context information created for managing a peer's service
929  *
930  * @param mctx the ManageServiceContext
931  */
932 static void
933 cleanup_mctx (struct ManageServiceContext *mctx)
934 {
935   mctx->expired = GNUNET_YES;
936   GNUNET_CONTAINER_DLL_remove (mctx_head, mctx_tail, mctx);
937   GNUNET_SERVER_client_drop (mctx->client);
938   GNUNET_ARM_disconnect_and_free (mctx->ah);
939   GNUNET_assert (0 < mctx->peer->reference_cnt);
940   mctx->peer->reference_cnt--;
941   if ( (GNUNET_YES == mctx->peer->destroy_flag)
942        && (0 == mctx->peer->reference_cnt) )
943     GST_destroy_peer (mctx->peer);
944   GNUNET_free (mctx);
945 }
946
947
948 /**
949  * Frees the ManageServiceContext queue
950  */
951 void
952 GST_free_mctxq ()
953 {
954   while (NULL != mctx_head)
955     cleanup_mctx (mctx_head);
956 }
957
958
959 /**
960  * Returns a string interpretation of 'rs'
961  *
962  * @param rs the request status from ARM
963  * @return a string interpretation of the request status
964  */
965 static const char *
966 arm_req_string (enum GNUNET_ARM_RequestStatus rs)
967 {
968   switch (rs)
969   {
970   case GNUNET_ARM_REQUEST_SENT_OK:
971     return _("Message was sent successfully");
972   case GNUNET_ARM_REQUEST_CONFIGURATION_ERROR:
973     return _("Misconfiguration (can't connect to the ARM service)");
974   case GNUNET_ARM_REQUEST_DISCONNECTED:
975     return _("We disconnected from ARM before we could send a request");
976   case GNUNET_ARM_REQUEST_BUSY:
977     return _("ARM API is busy");
978   case GNUNET_ARM_REQUEST_TOO_LONG:
979     return _("Request doesn't fit into a message");
980   case GNUNET_ARM_REQUEST_TIMEOUT:
981     return _("Request timed out");
982   }
983   return _("Unknown request status");
984 }
985
986
987 /**
988  * Returns a string interpretation of the 'result'
989  *
990  * @param result the arm result
991  * @return a string interpretation
992  */
993 static const char *
994 arm_ret_string (enum GNUNET_ARM_Result result)
995 {
996   switch (result)
997   {
998   case GNUNET_ARM_RESULT_STOPPED:
999     return _("%s is stopped");
1000   case GNUNET_ARM_RESULT_STARTING:
1001     return _("%s is starting");
1002   case GNUNET_ARM_RESULT_STOPPING:
1003     return _("%s is stopping");
1004   case GNUNET_ARM_RESULT_IS_STARTING_ALREADY:
1005     return _("%s is starting already");
1006   case GNUNET_ARM_RESULT_IS_STOPPING_ALREADY:
1007     return _("%s is stopping already");
1008   case GNUNET_ARM_RESULT_IS_STARTED_ALREADY:
1009     return _("%s is started already");
1010   case GNUNET_ARM_RESULT_IS_STOPPED_ALREADY:
1011     return _("%s is stopped already");
1012   case GNUNET_ARM_RESULT_IS_NOT_KNOWN:
1013     return _("%s service is not known to ARM");
1014   case GNUNET_ARM_RESULT_START_FAILED:
1015     return _("%s service failed to start");
1016   case GNUNET_ARM_RESULT_IN_SHUTDOWN:
1017     return _("%s service can't be started because ARM is shutting down");
1018   }
1019   return _("%.s Unknown result code.");
1020 }
1021
1022
1023 /**
1024  * Function called in response to a start/stop request.
1025  * Will be called when request was not sent successfully,
1026  * or when a reply comes. If the request was not sent successfully,
1027  * 'rs' will indicate that, and 'service' and 'result' will be undefined.
1028  *
1029  * @param cls ManageServiceContext
1030  * @param rs status of the request
1031  * @param service service name
1032  * @param result result of the operation
1033  */
1034 static void
1035 service_manage_result_cb (void *cls, 
1036                           enum GNUNET_ARM_RequestStatus rs, 
1037                           const char *service, enum GNUNET_ARM_Result result)
1038 {
1039   struct ManageServiceContext *mctx = cls;
1040   char *emsg;
1041
1042   emsg = NULL;
1043   if (GNUNET_YES == mctx->expired)
1044     return;
1045   if (GNUNET_ARM_REQUEST_SENT_OK != rs)
1046   {
1047     GNUNET_asprintf (&emsg, "Error communicating with Peer %u's ARM: %s",
1048                      mctx->peer->id, arm_req_string (rs));
1049     goto ret;
1050   }
1051   if (1 == mctx->start)
1052     goto service_start_check;
1053   if (! ((GNUNET_ARM_RESULT_STOPPED == result)
1054             || (GNUNET_ARM_RESULT_STOPPING == result)
1055             || (GNUNET_ARM_RESULT_IS_STOPPING_ALREADY == result)
1056             || (GNUNET_ARM_RESULT_IS_STOPPED_ALREADY == result)) )
1057   {
1058     /* stopping a service failed */
1059     GNUNET_asprintf (&emsg, arm_ret_string (result), service);
1060     goto ret;
1061   }
1062   /* service stopped successfully */
1063   goto ret;
1064
1065  service_start_check:
1066   if (! ((GNUNET_ARM_RESULT_STARTING == result)
1067             || (GNUNET_ARM_RESULT_IS_STARTING_ALREADY == result)
1068             || (GNUNET_ARM_RESULT_IS_STARTED_ALREADY == result)) )
1069   {
1070     /* starting a service failed */
1071     GNUNET_asprintf (&emsg, arm_ret_string (result), service);
1072     goto ret;
1073   }
1074   /* service started successfully */
1075   
1076  ret:
1077   if (NULL != emsg)
1078   {
1079     LOG_DEBUG ("%s\n", emsg);
1080     GST_send_operation_fail_msg (mctx->client, mctx->op_id, emsg);
1081   }
1082   else
1083     GST_send_operation_success_msg (mctx->client, mctx->op_id);
1084   GNUNET_free_non_null (emsg);
1085   cleanup_mctx (mctx);
1086 }
1087
1088
1089 /**
1090  * Handler for GNUNET_TESTBED_ManagePeerServiceMessage message
1091  *
1092  * @param cls NULL
1093  * @param client identification of client
1094  * @param message the actual message
1095  */
1096 void
1097 GST_handle_manage_peer_service (void *cls, struct GNUNET_SERVER_Client *client,
1098                                 const struct GNUNET_MessageHeader *message)
1099 {
1100   const struct GNUNET_TESTBED_ManagePeerServiceMessage *msg;
1101   const char* service;
1102   struct Peer *peer;
1103   char *emsg;
1104   struct GNUNET_ARM_Handle *ah;
1105   struct ManageServiceContext *mctx;
1106   struct ForwardedOperationContext *fopc;
1107   uint64_t op_id;
1108   uint32_t peer_id;
1109   uint16_t msize;
1110   
1111
1112   msize = ntohs (message->size);
1113   if (msize <= sizeof (struct GNUNET_TESTBED_ManagePeerServiceMessage))
1114   {
1115     GNUNET_break_op (0);  
1116     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1117     return;
1118   }
1119   msg = (const struct GNUNET_TESTBED_ManagePeerServiceMessage *) message;
1120   service = (const char *) &msg[1];  
1121   if ('\0' != service[msize - sizeof
1122                       (struct GNUNET_TESTBED_ManagePeerServiceMessage) - 1])
1123   {
1124     GNUNET_break_op (0);
1125     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1126     return;
1127   }
1128   if (1 < msg->start)
1129   {
1130     GNUNET_break_op (0);
1131     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1132     return;    
1133   }
1134   peer_id = ntohl (msg->peer_id);
1135   op_id = GNUNET_ntohll (msg->operation_id);
1136   LOG_DEBUG ("Received request to manage service %s on peer %u\n",
1137              service, (unsigned int) peer_id);
1138   if ((GST_peer_list_size <= peer_id)
1139       || (NULL == (peer = GST_peer_list[peer_id])))
1140   {
1141     GNUNET_asprintf (&emsg, "Asked to manage service of a non existent peer "
1142                      "with id: %u", peer_id);
1143     goto err_ret;
1144   }
1145   if (0 == strcasecmp ("arm", service))
1146   {
1147     emsg = GNUNET_strdup ("Cannot start/stop peer's ARM service.  "
1148                           "Use peer start/stop for that");
1149     goto err_ret;
1150   }
1151   if (GNUNET_YES == peer->is_remote)
1152   {
1153     /* Forward the destory message to sub controller */
1154     fopc = GNUNET_malloc (sizeof (struct ForwardedOperationContext));
1155     GNUNET_SERVER_client_keep (client);
1156     fopc->client = client;
1157     fopc->cls = peer;
1158     fopc->type = OP_MANAGE_SERVICE;
1159     fopc->operation_id = op_id;
1160     fopc->opc =
1161         GNUNET_TESTBED_forward_operation_msg_ (peer->details.remote.
1162                                                slave->controller,
1163                                                fopc->operation_id, &msg->header,
1164                                                &GST_forwarded_operation_reply_relay,
1165                                                fopc);
1166     fopc->timeout_task =
1167         GNUNET_SCHEDULER_add_delayed (GST_timeout, &GST_forwarded_operation_timeout,
1168                                       fopc);
1169     GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fopc);
1170     GNUNET_SERVER_receive_done (client, GNUNET_OK);
1171     return;
1172   }
1173   if ((0 != peer->reference_cnt)
1174       && ( (0 == strcasecmp ("core", service))
1175            || (0 == strcasecmp ("transport", service)) )  )
1176   {
1177     GNUNET_asprintf (&emsg, "Cannot stop %s service of peer with id: %u "
1178                      "since it is required by existing operations",
1179                      service, peer_id);
1180     goto err_ret;
1181   }
1182   ah = GNUNET_ARM_connect (peer->details.local.cfg, NULL, NULL);
1183   if (NULL == ah)
1184   {
1185     GNUNET_asprintf (&emsg,
1186                      "Cannot connect to ARM service of peer with id: %u",
1187                      peer_id);
1188     goto err_ret;
1189   }
1190   mctx = GNUNET_malloc (sizeof (struct ManageServiceContext));
1191   mctx->peer = peer;
1192   peer->reference_cnt++;
1193   mctx->op_id = op_id;
1194   mctx->ah = ah;
1195   GNUNET_SERVER_client_keep (client);
1196   mctx->client = client;
1197   mctx->start = msg->start;
1198   GNUNET_CONTAINER_DLL_insert_tail (mctx_head, mctx_tail, mctx);
1199   if (1 == mctx->start)
1200     GNUNET_ARM_request_service_start (mctx->ah, service,
1201                                       GNUNET_OS_INHERIT_STD_ERR,
1202                                       GST_timeout,
1203                                       service_manage_result_cb,
1204                                       mctx);
1205   else
1206     GNUNET_ARM_request_service_stop (mctx->ah, service,
1207                                      GST_timeout,
1208                                      service_manage_result_cb,
1209                                      mctx);
1210   GNUNET_SERVER_receive_done (client, GNUNET_OK);
1211   return;
1212
1213  err_ret:
1214   LOG (GNUNET_ERROR_TYPE_ERROR, "%s\n", emsg);
1215   GST_send_operation_fail_msg (client, op_id, emsg);
1216   GNUNET_free (emsg);
1217   GNUNET_SERVER_receive_done (client, GNUNET_OK);
1218 }
1219
1220
1221 /**
1222  * Stops and destroys all peers
1223  */
1224 void
1225 GST_destroy_peers ()
1226 {
1227   struct Peer *peer;
1228   unsigned int id;
1229
1230   if (NULL == GST_peer_list)
1231     return;
1232   for (id = 0; id < GST_peer_list_size; id++)
1233   {
1234     peer = GST_peer_list[id];
1235     if (NULL == peer)
1236       continue;
1237     /* If destroy flag is set it means that this peer should have been
1238      * destroyed by a context which we destroy before */
1239     GNUNET_break (GNUNET_NO == peer->destroy_flag);
1240     /* counter should be zero as we free all contexts before */
1241     GNUNET_break (0 == peer->reference_cnt);
1242     if ((GNUNET_NO == peer->is_remote) &&
1243         (GNUNET_YES == peer->details.local.is_running))
1244       GNUNET_TESTING_peer_kill (peer->details.local.peer);
1245   }
1246   for (id = 0; id < GST_peer_list_size; id++)
1247   {
1248     peer = GST_peer_list[id];
1249     if (NULL == peer)
1250       continue;    
1251     if (GNUNET_NO == peer->is_remote)
1252     {
1253       if (GNUNET_YES == peer->details.local.is_running)
1254         GNUNET_TESTING_peer_wait (peer->details.local.peer);
1255       GNUNET_TESTING_peer_destroy (peer->details.local.peer);
1256       GNUNET_CONFIGURATION_destroy (peer->details.local.cfg);
1257     }
1258     GNUNET_free (peer);
1259   }
1260   GNUNET_free_non_null (GST_peer_list);
1261   GST_peer_list = NULL;
1262   GST_peer_list_size = 0;
1263 }
1264
1265
1266 /**
1267  * Task run upon timeout of forwarded SHUTDOWN_PEERS operation
1268  *
1269  * @param cls the ForwardedOperationContext
1270  * @param tc the scheduler task context
1271  */
1272 static void
1273 shutdown_peers_timeout_cb (void *cls,
1274                            const struct GNUNET_SCHEDULER_TaskContext *tc)
1275 {
1276   struct ForwardedOperationContext *fo_ctxt = cls;
1277   struct HandlerContext_ShutdownPeers *hc;
1278
1279   fo_ctxt->timeout_task = GNUNET_SCHEDULER_NO_TASK;
1280   hc = fo_ctxt->cls;
1281   hc->timeout = GNUNET_YES;
1282   GNUNET_assert (0 < hc->nslaves);
1283   hc->nslaves--;
1284   if (0 == hc->nslaves)
1285   {
1286     GST_send_operation_fail_msg (fo_ctxt->client, fo_ctxt->operation_id,
1287                                  "Timeout at a slave controller");
1288     GNUNET_free (hc);
1289     hc = NULL;
1290   }
1291   GNUNET_TESTBED_forward_operation_msg_cancel_ (fo_ctxt->opc);  
1292   GNUNET_SERVER_client_drop (fo_ctxt->client);
1293   GNUNET_CONTAINER_DLL_remove (fopcq_head, fopcq_tail, fo_ctxt);
1294   GNUNET_free (fo_ctxt);
1295 }
1296
1297
1298 /**
1299  * The reply msg handler forwarded SHUTDOWN_PEERS operation.  Checks if a
1300  * success reply is received from all clients and then sends the success message
1301  * to the client
1302  *
1303  * @param cls ForwardedOperationContext
1304  * @param msg the message to relay
1305  */
1306 static void
1307 shutdown_peers_reply_cb (void *cls,
1308                          const struct GNUNET_MessageHeader *msg)
1309 {
1310   struct ForwardedOperationContext *fo_ctxt = cls;
1311   struct HandlerContext_ShutdownPeers *hc;
1312   
1313   hc = fo_ctxt->cls;
1314   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != fo_ctxt->timeout_task);
1315   GNUNET_SCHEDULER_cancel (fo_ctxt->timeout_task);
1316   fo_ctxt->timeout_task = GNUNET_SCHEDULER_NO_TASK;
1317   GNUNET_assert (0 < hc->nslaves);
1318   hc->nslaves--;
1319   if (GNUNET_MESSAGE_TYPE_TESTBED_GENERIC_OPERATION_SUCCESS != 
1320       ntohs (msg->type))
1321     hc->timeout = GNUNET_YES;
1322   if (0 == hc->nslaves)
1323   {
1324     if (GNUNET_YES == hc->timeout)
1325       GST_send_operation_fail_msg (fo_ctxt->client, fo_ctxt->operation_id,
1326                                    "Timeout at a slave controller");
1327     else
1328       GST_send_operation_success_msg (fo_ctxt->client, fo_ctxt->operation_id);
1329     GNUNET_free (hc);
1330     hc = NULL;
1331   }
1332   GNUNET_SERVER_client_drop (fo_ctxt->client);
1333   GNUNET_CONTAINER_DLL_remove (fopcq_head, fopcq_tail, fo_ctxt);
1334   GNUNET_free (fo_ctxt);
1335 }
1336
1337
1338 /**
1339  * Handler for GNUNET_MESSAGE_TYPE_TESTBED_SHUTDOWN_PEERS messages
1340  *
1341  * @param cls NULL
1342  * @param client identification of the client
1343  * @param message the actual message
1344  */
1345 void
1346 GST_handle_shutdown_peers (void *cls, struct GNUNET_SERVER_Client *client,
1347                            const struct GNUNET_MessageHeader *message)
1348 {
1349   const struct GNUNET_TESTBED_ShutdownPeersMessage *msg;
1350   struct HandlerContext_ShutdownPeers *hc;
1351   struct Slave *slave;
1352   struct ForwardedOperationContext *fo_ctxt;
1353   uint64_t op_id;
1354   unsigned int cnt;
1355
1356   msg = (const struct GNUNET_TESTBED_ShutdownPeersMessage *) message;
1357   LOG_DEBUG ("Received SHUTDOWN_PEERS\n");
1358     /* Stop and destroy all peers */
1359   GST_free_mctxq ();
1360   GST_free_occq ();
1361   GST_free_roccq ();
1362   GST_clear_fopcq ();
1363   /* Forward to all slaves which we have started */
1364   op_id = GNUNET_ntohll (msg->operation_id);
1365   hc = GNUNET_malloc (sizeof (struct HandlerContext_ShutdownPeers));
1366   /* FIXME: have a better implementation where we track which slaves are
1367      started by this controller */
1368   for (cnt = 0; cnt < GST_slave_list_size; cnt++)
1369   {
1370     slave = GST_slave_list[cnt];
1371     if (NULL == slave)
1372       continue;
1373     if (NULL == slave->controller_proc) /* We didn't start the slave */
1374       continue;
1375     LOG_DEBUG ("Forwarding SHUTDOWN_PEERS\n");
1376     hc->nslaves++;
1377     fo_ctxt = GNUNET_malloc (sizeof (struct ForwardedOperationContext));
1378     GNUNET_SERVER_client_keep (client);
1379     fo_ctxt->client = client;
1380     fo_ctxt->operation_id = op_id;
1381     fo_ctxt->cls = hc;
1382     fo_ctxt->type = OP_SHUTDOWN_PEERS;
1383     fo_ctxt->opc =
1384         GNUNET_TESTBED_forward_operation_msg_ (slave->controller,
1385                                                fo_ctxt->operation_id,
1386                                                &msg->header,
1387                                                shutdown_peers_reply_cb,
1388                                                fo_ctxt);
1389     fo_ctxt->timeout_task =
1390         GNUNET_SCHEDULER_add_delayed (GST_timeout, &shutdown_peers_timeout_cb,
1391                                       fo_ctxt);
1392     GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fo_ctxt);
1393   }
1394   LOG_DEBUG ("Shutting down peers\n");
1395   GST_destroy_peers ();
1396   if (0 == hc->nslaves)
1397   {
1398     GST_send_operation_success_msg (client, op_id);
1399     GNUNET_free (hc);
1400   }
1401   GNUNET_SERVER_receive_done (client, GNUNET_OK);  
1402 }