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