- restructure
[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 2, 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 /**
40  * Context information to manage peers' services
41  */
42 struct ManageServiceContext
43 {
44   /**
45    * DLL next ptr
46    */
47   struct ManageServiceContext *next;
48
49   /**
50    * DLL prev ptr
51    */
52   struct ManageServiceContext *prev;
53
54   /**
55    * The ARM handle of the peer
56    */
57   struct GNUNET_ARM_Handle *ah;
58
59   /**
60    * peer whose service has to be managed
61    */
62   struct Peer *peer;
63
64   /**
65    * The client which requested to manage the peer's service
66    */
67   struct GNUNET_SERVER_Client *client;
68   
69   /**
70    * The operation id of the associated request
71    */
72   uint64_t op_id;
73   
74   /**
75    * 1 if the service at the peer has to be started; 0 if it has to be stopped
76    */
77   uint8_t start;
78
79   /**
80    * Is this context expired?  Do not work on this context if it is set to
81    * GNUNET_YES
82    */
83   uint8_t expired;  
84 };
85
86
87 /**
88  * DLL head for queue of manage service requests
89  */
90 static struct ManageServiceContext *mctx_head;
91
92 /**
93  * DLL tail for queue of manage service requests
94  */
95 static struct ManageServiceContext *mctx_tail;
96
97
98 /**
99  * Adds a peer to the peer array
100  *
101  * @param peer the peer to add
102  */
103 static void
104 peer_list_add (struct Peer *peer)
105 {
106   if (peer->id >= GST_peer_list_size)
107     GST_array_grow_large_enough (GST_peer_list, GST_peer_list_size, peer->id);
108   GNUNET_assert (NULL == GST_peer_list[peer->id]);
109   GST_peer_list[peer->id] = peer;
110 }
111
112
113 /**
114  * Removes a the give peer from the peer array
115  *
116  * @param peer the peer to be removed
117  */
118 static void
119 peer_list_remove (struct Peer *peer)
120 {
121   unsigned int orig_size;
122   uint32_t id;
123
124   GST_peer_list[peer->id] = NULL;
125   orig_size = GST_peer_list_size;
126   while (GST_peer_list_size >= LIST_GROW_STEP)
127   {
128     for (id = GST_peer_list_size - 1;
129          (id >= GST_peer_list_size - LIST_GROW_STEP) && (id != UINT32_MAX);
130          id--)
131       if (NULL != GST_peer_list[id])
132         break;
133     if (id != ((GST_peer_list_size - LIST_GROW_STEP) - 1))
134       break;
135     GST_peer_list_size -= LIST_GROW_STEP;
136   }
137   if (orig_size == GST_peer_list_size)
138     return;
139   GST_peer_list =
140       GNUNET_realloc (GST_peer_list,
141                       sizeof (struct Peer *) * GST_peer_list_size);
142 }
143
144
145 /**
146  * The task to be executed if the forwarded peer create operation has been
147  * timed out
148  *
149  * @param cls the FowardedOperationContext
150  * @param tc the TaskContext from the scheduler
151  */
152 static void
153 peer_create_forward_timeout (void *cls,
154                              const struct GNUNET_SCHEDULER_TaskContext *tc)
155 {
156   struct ForwardedOperationContext *fopc = cls;
157
158   GNUNET_free (fopc->cls);
159   GST_forwarded_operation_timeout (fopc, tc);
160 }
161
162
163 /**
164  * Callback to be called when forwarded peer create operation is successfull. We
165  * have to relay the reply msg back to the client
166  *
167  * @param cls ForwardedOperationContext
168  * @param msg the peer create success message
169  */
170 static void
171 peer_create_success_cb (void *cls, const struct GNUNET_MessageHeader *msg)
172 {
173   struct ForwardedOperationContext *fopc = cls;
174   struct Peer *remote_peer;
175
176   if (ntohs (msg->type) == GNUNET_MESSAGE_TYPE_TESTBED_CREATE_PEER_SUCCESS)
177   {
178     GNUNET_assert (NULL != fopc->cls);
179     remote_peer = fopc->cls;
180     peer_list_add (remote_peer);
181   }
182   GST_forwarded_operation_reply_relay (fopc, msg);
183 }
184
185
186 /**
187  * Function to destroy a peer
188  *
189  * @param peer the peer structure to destroy
190  */
191 void
192 GST_destroy_peer (struct Peer *peer)
193 {
194   GNUNET_break (0 == peer->reference_cnt);
195   if (GNUNET_YES == peer->is_remote)
196   {
197     peer_list_remove (peer);
198     GNUNET_free (peer);
199     return;
200   }
201   if (GNUNET_YES == peer->details.local.is_running)
202   {
203     GNUNET_TESTING_peer_stop (peer->details.local.peer);
204     peer->details.local.is_running = GNUNET_NO;
205   }
206   GNUNET_TESTING_peer_destroy (peer->details.local.peer);
207   GNUNET_CONFIGURATION_destroy (peer->details.local.cfg);
208   peer_list_remove (peer);
209   GNUNET_free (peer);
210 }
211
212
213 /**
214  * Callback to be called when forwarded peer destroy operation is successfull. We
215  * have to relay the reply msg back to the client
216  *
217  * @param cls ForwardedOperationContext
218  * @param msg the peer create success message
219  */
220 static void
221 peer_destroy_success_cb (void *cls, const struct GNUNET_MessageHeader *msg)
222 {
223   struct ForwardedOperationContext *fopc = cls;
224   struct Peer *remote_peer;
225
226   if (GNUNET_MESSAGE_TYPE_TESTBED_GENERIC_OPERATION_SUCCESS ==
227       ntohs (msg->type))
228   {
229     remote_peer = fopc->cls;
230     GNUNET_assert (NULL != remote_peer);
231     remote_peer->destroy_flag = GNUNET_YES;
232     if (0 == remote_peer->reference_cnt)
233       GST_destroy_peer (remote_peer);
234   }
235   GST_forwarded_operation_reply_relay (fopc, msg);
236 }
237
238
239 /**
240  * Handler for GNUNET_MESSAGE_TYPE_TESTBED_CREATEPEER messages
241  *
242  * @param cls NULL
243  * @param client identification of the client
244  * @param message the actual message
245  */
246 void
247 GST_handle_peer_create (void *cls, struct GNUNET_SERVER_Client *client,
248                         const struct GNUNET_MessageHeader *message)
249 {
250   const struct GNUNET_TESTBED_PeerCreateMessage *msg;
251   struct GNUNET_TESTBED_PeerCreateSuccessEventMessage *reply;
252   struct GNUNET_CONFIGURATION_Handle *cfg;
253   struct ForwardedOperationContext *fo_ctxt;
254   struct Route *route;
255   struct Peer *peer;
256   char *config;
257   size_t dest_size;
258   int ret;
259   uint32_t config_size;
260   uint32_t host_id;
261   uint32_t peer_id;
262   uint16_t msize;
263
264
265   msize = ntohs (message->size);
266   if (msize <= sizeof (struct GNUNET_TESTBED_PeerCreateMessage))
267   {
268     GNUNET_break (0);           /* We need configuration */
269     GNUNET_SERVER_receive_done (client, GNUNET_OK);
270     return;
271   }
272   msg = (const struct GNUNET_TESTBED_PeerCreateMessage *) message;
273   host_id = ntohl (msg->host_id);
274   peer_id = ntohl (msg->peer_id);
275   if (UINT32_MAX == peer_id)
276   {
277     GST_send_operation_fail_msg (client, GNUNET_ntohll (msg->operation_id),
278                                  "Cannot create peer with given ID");
279     GNUNET_SERVER_receive_done (client, GNUNET_OK);
280     return;
281   }
282   if (host_id == GST_context->host_id)
283   {
284     char *emsg;
285
286     /* We are responsible for this peer */
287     msize -= sizeof (struct GNUNET_TESTBED_PeerCreateMessage);
288     config_size = ntohl (msg->config_size);
289     config = GNUNET_malloc (config_size);
290     dest_size = config_size;
291     if (Z_OK !=
292         (ret =
293          uncompress ((Bytef *) config, (uLongf *) & dest_size,
294                      (const Bytef *) &msg[1], (uLong) msize)))
295     {
296       GNUNET_break (0);         /* uncompression error */
297       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
298       return;
299     }
300     if (config_size != dest_size)
301     {
302       GNUNET_break (0);         /* Uncompressed config size mismatch */
303       GNUNET_free (config);
304       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
305       return;
306     }
307     cfg = GNUNET_CONFIGURATION_create ();
308     if (GNUNET_OK !=
309         GNUNET_CONFIGURATION_deserialize (cfg, config, config_size, GNUNET_NO))
310     {
311       GNUNET_break (0);         /* Configuration parsing error */
312       GNUNET_free (config);
313       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
314       return;
315     }
316     GNUNET_free (config);
317     GNUNET_CONFIGURATION_set_value_number (cfg, "TESTBED", "PEERID",
318                                            (unsigned long long) peer_id);
319     peer = GNUNET_malloc (sizeof (struct Peer));
320     peer->is_remote = GNUNET_NO;
321     peer->details.local.cfg = cfg;
322     peer->id = peer_id;
323     LOG_DEBUG ("Creating peer with id: %u\n", (unsigned int) peer->id);
324     peer->details.local.peer =
325         GNUNET_TESTING_peer_configure (GST_context->system,
326                                        peer->details.local.cfg, peer->id,
327                                        NULL /* Peer id */ ,
328                                        &emsg);
329     if (NULL == peer->details.local.peer)
330     {
331       LOG (GNUNET_ERROR_TYPE_WARNING, "Configuring peer failed: %s\n", emsg);
332       GNUNET_free (emsg);
333       GNUNET_free (peer);
334       GNUNET_break (0);
335       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
336       return;
337     }
338     peer->details.local.is_running = GNUNET_NO;
339     peer_list_add (peer);
340     reply =
341         GNUNET_malloc (sizeof
342                        (struct GNUNET_TESTBED_PeerCreateSuccessEventMessage));
343     reply->header.size =
344         htons (sizeof (struct GNUNET_TESTBED_PeerCreateSuccessEventMessage));
345     reply->header.type =
346         htons (GNUNET_MESSAGE_TYPE_TESTBED_CREATE_PEER_SUCCESS);
347     reply->peer_id = msg->peer_id;
348     reply->operation_id = msg->operation_id;
349     GST_queue_message (client, &reply->header);
350     GNUNET_SERVER_receive_done (client, GNUNET_OK);
351     return;
352   }
353
354   /* Forward peer create request */
355   route = GST_find_dest_route (host_id);
356   if (NULL == route)
357   {
358     GNUNET_break (0);
359     GNUNET_SERVER_receive_done (client, GNUNET_OK);
360     return;
361   }
362
363   peer = GNUNET_malloc (sizeof (struct Peer));
364   peer->is_remote = GNUNET_YES;
365   peer->id = peer_id;
366   peer->details.remote.slave = GST_slave_list[route->dest];
367   peer->details.remote.remote_host_id = host_id;
368   fo_ctxt = GNUNET_malloc (sizeof (struct ForwardedOperationContext));
369   GNUNET_SERVER_client_keep (client);
370   fo_ctxt->client = client;
371   fo_ctxt->operation_id = GNUNET_ntohll (msg->operation_id);
372   fo_ctxt->cls = peer;          //GST_slave_list[route->dest]->controller;
373   fo_ctxt->type = OP_PEER_CREATE;
374   fo_ctxt->opc =
375       GNUNET_TESTBED_forward_operation_msg_ (GST_slave_list
376                                              [route->dest]->controller,
377                                              fo_ctxt->operation_id,
378                                              &msg->header,
379                                              peer_create_success_cb, fo_ctxt);
380   fo_ctxt->timeout_task =
381       GNUNET_SCHEDULER_add_delayed (GST_timeout, &peer_create_forward_timeout,
382                                     fo_ctxt);
383   GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fo_ctxt);
384   GNUNET_SERVER_receive_done (client, GNUNET_OK);
385 }
386
387
388 /**
389  * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_DESTROYPEER messages
390  *
391  * @param cls NULL
392  * @param client identification of the client
393  * @param message the actual message
394  */
395 void
396 GST_handle_peer_destroy (void *cls, struct GNUNET_SERVER_Client *client,
397                          const struct GNUNET_MessageHeader *message)
398 {
399   const struct GNUNET_TESTBED_PeerDestroyMessage *msg;
400   struct ForwardedOperationContext *fopc;
401   struct Peer *peer;
402   uint32_t peer_id;
403
404   msg = (const struct GNUNET_TESTBED_PeerDestroyMessage *) message;
405   peer_id = ntohl (msg->peer_id);
406   LOG_DEBUG ("Received peer destory on peer: %u and operation id: %ul\n",
407              peer_id, GNUNET_ntohll (msg->operation_id));
408   if ((GST_peer_list_size <= peer_id) || (NULL == GST_peer_list[peer_id]))
409   {
410     LOG (GNUNET_ERROR_TYPE_ERROR,
411          "Asked to destroy a non existent peer with id: %u\n", peer_id);
412     GST_send_operation_fail_msg (client, GNUNET_ntohll (msg->operation_id),
413                                  "Peer doesn't exist");
414     GNUNET_SERVER_receive_done (client, GNUNET_OK);
415     return;
416   }
417   peer = GST_peer_list[peer_id];
418   if (GNUNET_YES == peer->is_remote)
419   {
420     /* Forward the destory message to sub controller */
421     fopc = GNUNET_malloc (sizeof (struct ForwardedOperationContext));
422     GNUNET_SERVER_client_keep (client);
423     fopc->client = client;
424     fopc->cls = peer;
425     fopc->type = OP_PEER_DESTROY;
426     fopc->operation_id = GNUNET_ntohll (msg->operation_id);
427     fopc->opc =
428         GNUNET_TESTBED_forward_operation_msg_ (peer->details.remote.
429                                                slave->controller,
430                                                fopc->operation_id, &msg->header,
431                                                &peer_destroy_success_cb, fopc);
432     fopc->timeout_task =
433         GNUNET_SCHEDULER_add_delayed (GST_timeout, &GST_forwarded_operation_timeout,
434                                       fopc);
435     GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fopc);
436     GNUNET_SERVER_receive_done (client, GNUNET_OK);
437     return;
438   }
439   peer->destroy_flag = GNUNET_YES;
440   if (0 == peer->reference_cnt)
441     GST_destroy_peer (peer);
442   else
443     LOG (GNUNET_ERROR_TYPE_DEBUG,
444          "Delaying peer destroy as peer is currently in use\n");
445   GST_send_operation_success_msg (client, GNUNET_ntohll (msg->operation_id));
446   GNUNET_SERVER_receive_done (client, GNUNET_OK);
447 }
448
449
450 /**
451  * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_DESTROYPEER messages
452  *
453  * @param cls NULL
454  * @param client identification of the client
455  * @param message the actual message
456  */
457 void
458 GST_handle_peer_start (void *cls, struct GNUNET_SERVER_Client *client,
459                        const struct GNUNET_MessageHeader *message)
460 {
461   const struct GNUNET_TESTBED_PeerStartMessage *msg;
462   struct GNUNET_TESTBED_PeerEventMessage *reply;
463   struct ForwardedOperationContext *fopc;
464   struct Peer *peer;
465   uint32_t peer_id;
466
467   msg = (const struct GNUNET_TESTBED_PeerStartMessage *) message;
468   peer_id = ntohl (msg->peer_id);
469   if ((peer_id >= GST_peer_list_size) || (NULL == GST_peer_list[peer_id]))
470   {
471     GNUNET_break (0);
472     LOG (GNUNET_ERROR_TYPE_ERROR,
473          "Asked to start a non existent peer with id: %u\n", peer_id);
474     GNUNET_SERVER_receive_done (client, GNUNET_OK);
475     return;
476   }
477   peer = GST_peer_list[peer_id];
478   if (GNUNET_YES == peer->is_remote)
479   {
480     fopc = GNUNET_malloc (sizeof (struct ForwardedOperationContext));
481     GNUNET_SERVER_client_keep (client);
482     fopc->client = client;
483     fopc->operation_id = GNUNET_ntohll (msg->operation_id);
484     fopc->type = OP_PEER_START;
485     fopc->opc =
486         GNUNET_TESTBED_forward_operation_msg_ (peer->details.remote.
487                                                slave->controller,
488                                                fopc->operation_id, &msg->header,
489                                                &GST_forwarded_operation_reply_relay,
490                                                fopc);
491     fopc->timeout_task =
492         GNUNET_SCHEDULER_add_delayed (GST_timeout, &GST_forwarded_operation_timeout,
493                                       fopc);
494     GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fopc);
495     GNUNET_SERVER_receive_done (client, GNUNET_OK);
496     return;
497   }
498   if (GNUNET_OK != GNUNET_TESTING_peer_start (peer->details.local.peer))
499   {
500     GST_send_operation_fail_msg (client, GNUNET_ntohll (msg->operation_id),
501                                  "Failed to start");
502     GNUNET_SERVER_receive_done (client, GNUNET_OK);
503     return;
504   }
505   peer->details.local.is_running = GNUNET_YES;
506   reply = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_PeerEventMessage));
507   reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_PEER_EVENT);
508   reply->header.size = htons (sizeof (struct GNUNET_TESTBED_PeerEventMessage));
509   reply->event_type = htonl (GNUNET_TESTBED_ET_PEER_START);
510   reply->host_id = htonl (GST_context->host_id);
511   reply->peer_id = msg->peer_id;
512   reply->operation_id = msg->operation_id;
513   GST_queue_message (client, &reply->header);
514   GNUNET_SERVER_receive_done (client, GNUNET_OK);
515 }
516
517
518 /**
519  * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_DESTROYPEER messages
520  *
521  * @param cls NULL
522  * @param client identification of the client
523  * @param message the actual message
524  */
525 void
526 GST_handle_peer_stop (void *cls, struct GNUNET_SERVER_Client *client,
527                       const struct GNUNET_MessageHeader *message)
528 {
529   const struct GNUNET_TESTBED_PeerStopMessage *msg;
530   struct GNUNET_TESTBED_PeerEventMessage *reply;
531   struct ForwardedOperationContext *fopc;
532   struct Peer *peer;
533   uint32_t peer_id;
534
535   msg = (const struct GNUNET_TESTBED_PeerStopMessage *) message;
536   peer_id = ntohl (msg->peer_id);
537   LOG (GNUNET_ERROR_TYPE_DEBUG, "Received PEER_STOP for peer %u\n", peer_id);
538   if ((peer_id >= GST_peer_list_size) || (NULL == GST_peer_list[peer_id]))
539   {
540     GST_send_operation_fail_msg (client, GNUNET_ntohll (msg->operation_id),
541                                  "Peer not found");
542     GNUNET_SERVER_receive_done (client, GNUNET_OK);
543     return;
544   }
545   peer = GST_peer_list[peer_id];
546   if (GNUNET_YES == peer->is_remote)
547   {
548     LOG (GNUNET_ERROR_TYPE_DEBUG, "Forwarding PEER_STOP for peer %u\n",
549          peer_id);
550     fopc = GNUNET_malloc (sizeof (struct ForwardedOperationContext));
551     GNUNET_SERVER_client_keep (client);
552     fopc->client = client;
553     fopc->operation_id = GNUNET_ntohll (msg->operation_id);
554     fopc->type = OP_PEER_STOP;
555     fopc->opc =
556         GNUNET_TESTBED_forward_operation_msg_ (peer->details.remote.
557                                                slave->controller,
558                                                fopc->operation_id, &msg->header,
559                                                &GST_forwarded_operation_reply_relay,
560                                                fopc);
561     fopc->timeout_task =
562         GNUNET_SCHEDULER_add_delayed (GST_timeout, &GST_forwarded_operation_timeout,
563                                       fopc);
564     GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fopc);
565     GNUNET_SERVER_receive_done (client, GNUNET_OK);
566     return;
567   }
568   if (GNUNET_OK != GNUNET_TESTING_peer_kill (peer->details.local.peer))
569   {
570     LOG (GNUNET_ERROR_TYPE_WARNING, "Stopping peer %u failed\n", peer_id);
571     GST_send_operation_fail_msg (client, GNUNET_ntohll (msg->operation_id),
572                                  "Peer not running");
573     GNUNET_SERVER_receive_done (client, GNUNET_OK);
574     return;
575   }
576   LOG (GNUNET_ERROR_TYPE_DEBUG, "Peer %u successfully stopped\n", peer_id);
577   peer->details.local.is_running = GNUNET_NO;
578   reply = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_PeerEventMessage));
579   reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_PEER_EVENT);
580   reply->header.size = htons (sizeof (struct GNUNET_TESTBED_PeerEventMessage));
581   reply->event_type = htonl (GNUNET_TESTBED_ET_PEER_STOP);
582   reply->host_id = htonl (GST_context->host_id);
583   reply->peer_id = msg->peer_id;
584   reply->operation_id = msg->operation_id;
585   GST_queue_message (client, &reply->header);
586   GNUNET_SERVER_receive_done (client, GNUNET_OK);
587   GNUNET_TESTING_peer_wait (peer->details.local.peer);
588 }
589
590
591 /**
592  * Handler for GNUNET_MESSAGE_TYPE_TESTBED_GETPEERCONFIG messages
593  *
594  * @param cls NULL
595  * @param client identification of the client
596  * @param message the actual message
597  */
598 void
599 GST_handle_peer_get_config (void *cls, struct GNUNET_SERVER_Client *client,
600                             const struct GNUNET_MessageHeader *message)
601 {
602   const struct GNUNET_TESTBED_PeerGetConfigurationMessage *msg;
603   struct GNUNET_TESTBED_PeerConfigurationInformationMessage *reply;
604   struct Peer *peer;
605   char *config;
606   char *xconfig;
607   size_t c_size;
608   size_t xc_size;
609   uint32_t peer_id;
610   uint16_t msize;
611
612   msg = (const struct GNUNET_TESTBED_PeerGetConfigurationMessage *) message;
613   peer_id = ntohl (msg->peer_id);
614   if ((peer_id >= GST_peer_list_size) || (NULL == GST_peer_list[peer_id]))
615   {
616     GST_send_operation_fail_msg (client, GNUNET_ntohll (msg->operation_id),
617                                  "Peer not found");
618     GNUNET_SERVER_receive_done (client, GNUNET_OK);
619     return;
620   }
621   peer = GST_peer_list[peer_id];
622   if (GNUNET_YES == peer->is_remote)
623   {
624     struct ForwardedOperationContext *fopc;
625
626     LOG_DEBUG ("Forwarding PEER_GET_CONFIG for peer: %u\n", 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_INFO;
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   LOG_DEBUG ("Received PEER_GET_CONFIG for peer: %u\n", peer_id);
646   config =
647       GNUNET_CONFIGURATION_serialize (GST_peer_list[peer_id]->details.local.cfg,
648                                       &c_size);
649   xc_size = GNUNET_TESTBED_compress_config_ (config, c_size, &xconfig);
650   GNUNET_free (config);
651   msize =
652       xc_size +
653       sizeof (struct GNUNET_TESTBED_PeerConfigurationInformationMessage);
654   reply = GNUNET_realloc (xconfig, msize);
655   (void) memmove (&reply[1], reply, xc_size);
656   reply->header.size = htons (msize);
657   reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_PEER_CONFIGURATION);
658   reply->peer_id = msg->peer_id;
659   reply->operation_id = msg->operation_id;
660   GNUNET_TESTING_peer_get_identity (GST_peer_list[peer_id]->details.local.peer,
661                                     &reply->peer_identity);
662   reply->config_size = htons ((uint16_t) c_size);
663   GST_queue_message (client, &reply->header);
664   GNUNET_SERVER_receive_done (client, GNUNET_OK);
665 }
666
667
668 /**
669  * Cleanup the context information created for managing a peer's service
670  *
671  * @param mctx the ManageServiceContext
672  */
673 static void
674 cleanup_mctx (struct ManageServiceContext *mctx)
675 {
676   mctx->expired = GNUNET_YES;
677   GNUNET_CONTAINER_DLL_remove (mctx_head, mctx_tail, mctx);
678   GNUNET_SERVER_client_drop (mctx->client);
679   GNUNET_ARM_disconnect_and_free (mctx->ah);
680   GNUNET_assert (0 < mctx->peer->reference_cnt);
681   mctx->peer->reference_cnt--;
682   if ( (GNUNET_YES == mctx->peer->destroy_flag)
683        && (0 == mctx->peer->reference_cnt) )
684     GST_destroy_peer (mctx->peer);
685   GNUNET_free (mctx);
686 }
687
688
689 /**
690  * Frees the ManageServiceContext queue
691  */
692 void
693 GST_free_mctxq ()
694 {
695   while (NULL != mctx_head)
696     cleanup_mctx (mctx_head);
697 }
698
699
700 /**
701  * Returns a string interpretation of 'rs'
702  *
703  * @param rs the request status from ARM
704  * @return a string interpretation of the request status
705  */
706 static const char *
707 arm_req_string (enum GNUNET_ARM_RequestStatus rs)
708 {
709   switch (rs)
710   {
711   case GNUNET_ARM_REQUEST_SENT_OK:
712     return _("Message was sent successfully");
713   case GNUNET_ARM_REQUEST_CONFIGURATION_ERROR:
714     return _("Misconfiguration (can't connect to the ARM service)");
715   case GNUNET_ARM_REQUEST_DISCONNECTED:
716     return _("We disconnected from ARM before we could send a request");
717   case GNUNET_ARM_REQUEST_BUSY:
718     return _("ARM API is busy");
719   case GNUNET_ARM_REQUEST_TOO_LONG:
720     return _("Request doesn't fit into a message");
721   case GNUNET_ARM_REQUEST_TIMEOUT:
722     return _("Request timed out");
723   }
724   return _("Unknown request status");
725 }
726
727
728 /**
729  * Returns a string interpretation of the 'result'
730  *
731  * @param result the arm result
732  * @return a string interpretation
733  */
734 static const char *
735 arm_ret_string (enum GNUNET_ARM_Result result)
736 {
737   switch (result)
738   {
739   case GNUNET_ARM_RESULT_STOPPED:
740     return _("%s is stopped");
741   case GNUNET_ARM_RESULT_STARTING:
742     return _("%s is starting");
743   case GNUNET_ARM_RESULT_STOPPING:
744     return _("%s is stopping");
745   case GNUNET_ARM_RESULT_IS_STARTING_ALREADY:
746     return _("%s is starting already");
747   case GNUNET_ARM_RESULT_IS_STOPPING_ALREADY:
748     return _("%s is stopping already");
749   case GNUNET_ARM_RESULT_IS_STARTED_ALREADY:
750     return _("%s is started already");
751   case GNUNET_ARM_RESULT_IS_STOPPED_ALREADY:
752     return _("%s is stopped already");
753   case GNUNET_ARM_RESULT_IS_NOT_KNOWN:
754     return _("%s service is not known to ARM");
755   case GNUNET_ARM_RESULT_START_FAILED:
756     return _("%s service failed to start");
757   case GNUNET_ARM_RESULT_IN_SHUTDOWN:
758     return _("%s service can't be started because ARM is shutting down");
759   }
760   return _("%.s Unknown result code.");
761 }
762
763
764 /**
765  * Function called in response to a start/stop request.
766  * Will be called when request was not sent successfully,
767  * or when a reply comes. If the request was not sent successfully,
768  * 'rs' will indicate that, and 'service' and 'result' will be undefined.
769  *
770  * @param cls ManageServiceContext
771  * @param arm handle to the arm connection
772  * @param rs status of the request
773  * @param service service name
774  * @param result result of the operation
775  */
776 static void
777 service_manage_result_cb (void *cls, struct GNUNET_ARM_Handle *arm,
778                           enum GNUNET_ARM_RequestStatus rs, 
779                           const char *service, enum GNUNET_ARM_Result result)
780 {
781   struct ManageServiceContext *mctx = cls;
782   char *emsg;
783
784   emsg = NULL;
785   if (GNUNET_YES == mctx->expired)
786     return;
787   if (GNUNET_ARM_REQUEST_SENT_OK != rs)
788   {
789     GNUNET_asprintf (&emsg, "Error communicating with Peer %u's ARM: %s",
790                      mctx->peer->id, arm_req_string (rs));
791     goto ret;
792   }
793   if (1 == mctx->start)
794     goto service_start_check;
795   if (! ((GNUNET_ARM_RESULT_STOPPED == result)
796             || (GNUNET_ARM_RESULT_STOPPING == result)
797             || (GNUNET_ARM_RESULT_IS_STOPPING_ALREADY == result)
798             || (GNUNET_ARM_RESULT_IS_STOPPED_ALREADY == result)) )
799   {
800     /* stopping a service failed */
801     GNUNET_asprintf (&emsg, arm_ret_string (result), service);
802     goto ret;
803   }
804   /* service stopped successfully */
805   goto ret;
806
807  service_start_check:
808   if (! ((GNUNET_ARM_RESULT_STARTING == result)
809             || (GNUNET_ARM_RESULT_IS_STARTING_ALREADY == result)
810             || (GNUNET_ARM_RESULT_IS_STARTED_ALREADY == result)) )
811   {
812     /* starting a service failed */
813     GNUNET_asprintf (&emsg, arm_ret_string (result), service);
814     goto ret;
815   }
816   /* service started successfully */
817   
818  ret:
819   if (NULL != emsg)
820   {
821     LOG_DEBUG ("%s\n", emsg);
822     GST_send_operation_fail_msg (mctx->client, mctx->op_id, emsg);
823   }
824   else
825     GST_send_operation_success_msg (mctx->client, mctx->op_id);
826   GNUNET_free_non_null (emsg);
827   cleanup_mctx (mctx);
828 }
829
830
831 /**
832  * Handler for GNUNET_TESTBED_ManagePeerServiceMessage message
833  *
834  * @param cls NULL
835  * @param client identification of client
836  * @param message the actual message
837  */
838 void
839 GST_handle_manage_peer_service (void *cls, struct GNUNET_SERVER_Client *client,
840                                 const struct GNUNET_MessageHeader *message)
841 {
842   const struct GNUNET_TESTBED_ManagePeerServiceMessage *msg;
843   const char* service;
844   struct Peer *peer;
845   char *emsg;
846   struct GNUNET_ARM_Handle *ah;
847   struct ManageServiceContext *mctx;
848   struct ForwardedOperationContext *fopc;
849   uint64_t op_id;
850   uint32_t peer_id;
851   uint16_t msize;
852   
853
854   msize = ntohs (message->size);
855   if (msize <= sizeof (struct GNUNET_TESTBED_ManagePeerServiceMessage))
856   {
857     GNUNET_break_op (0);  
858     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
859     return;
860   }
861   msg = (const struct GNUNET_TESTBED_ManagePeerServiceMessage *) message;
862   service = (const char *) &msg[1];  
863   if ('\0' != service[msize - sizeof
864                       (struct GNUNET_TESTBED_ManagePeerServiceMessage) - 1])
865   {
866     GNUNET_break_op (0);
867     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
868     return;
869   }
870   if (1 < msg->start)
871   {
872     GNUNET_break_op (0);
873     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
874     return;    
875   }
876   peer_id = ntohl (msg->peer_id);
877   op_id = GNUNET_ntohll (msg->operation_id);
878   LOG_DEBUG ("Received request to manage service %s on peer %u\n",
879              service, (unsigned int) peer_id);
880   if ((GST_peer_list_size <= peer_id)
881       || (NULL == (peer = GST_peer_list[peer_id])))
882   {
883     GNUNET_asprintf (&emsg, "Asked to manage service of a non existent peer "
884                      "with id: %u", peer_id);
885     goto err_ret;
886   }
887   if (0 == strcasecmp ("arm", service))
888   {
889     emsg = GNUNET_strdup ("Cannot start/stop peer's ARM service.  "
890                           "Use peer start/stop for that");
891     goto err_ret;
892   }
893   if (GNUNET_YES == peer->is_remote)
894   {
895     /* Forward the destory message to sub controller */
896     fopc = GNUNET_malloc (sizeof (struct ForwardedOperationContext));
897     GNUNET_SERVER_client_keep (client);
898     fopc->client = client;
899     fopc->cls = peer;
900     fopc->type = OP_MANAGE_SERVICE;
901     fopc->operation_id = op_id;
902     fopc->opc =
903         GNUNET_TESTBED_forward_operation_msg_ (peer->details.remote.
904                                                slave->controller,
905                                                fopc->operation_id, &msg->header,
906                                                &GST_forwarded_operation_reply_relay,
907                                                fopc);
908     fopc->timeout_task =
909         GNUNET_SCHEDULER_add_delayed (GST_timeout, &GST_forwarded_operation_timeout,
910                                       fopc);
911     GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fopc);
912     GNUNET_SERVER_receive_done (client, GNUNET_OK);
913     return;
914   }
915   if ((0 != peer->reference_cnt)
916       && ( (0 == strcasecmp ("core", service))
917            || (0 == strcasecmp ("transport", service)) )  )
918   {
919     GNUNET_asprintf (&emsg, "Cannot stop %s service of peer with id: %u "
920                      "since it is required by existing operations",
921                      service, peer_id);
922     goto err_ret;
923   }
924   ah = GNUNET_ARM_connect (peer->details.local.cfg, NULL, NULL);
925   if (NULL == ah)
926   {
927     GNUNET_asprintf (&emsg,
928                      "Cannot connect to ARM service of peer with id: %u",
929                      peer_id);
930     goto err_ret;
931   }
932   mctx = GNUNET_malloc (sizeof (struct ManageServiceContext));
933   mctx->peer = peer;
934   peer->reference_cnt++;
935   mctx->op_id = op_id;
936   mctx->ah = ah;
937   GNUNET_SERVER_client_keep (client);
938   mctx->client = client;
939   mctx->start = msg->start;
940   GNUNET_CONTAINER_DLL_insert_tail (mctx_head, mctx_tail, mctx);
941   if (1 == mctx->start)
942     GNUNET_ARM_request_service_start (mctx->ah, service,
943                                       GNUNET_OS_INHERIT_STD_ERR,
944                                       GST_timeout,
945                                       service_manage_result_cb,
946                                       mctx);
947   else
948     GNUNET_ARM_request_service_stop (mctx->ah, service,
949                                      GST_timeout,
950                                      service_manage_result_cb,
951                                      mctx);
952   GNUNET_SERVER_receive_done (client, GNUNET_OK);
953   return;
954
955  err_ret:
956   LOG (GNUNET_ERROR_TYPE_ERROR, "%s\n", emsg);
957   GST_send_operation_fail_msg (client, op_id, emsg);
958   GNUNET_free (emsg);
959   GNUNET_SERVER_receive_done (client, GNUNET_OK);
960 }
961
962
963 /**
964  * Stops and destroys all peers
965  */
966 void
967 GST_destroy_peers ()
968 {
969   struct Peer *peer;
970   unsigned int id;
971
972   if (NULL == GST_peer_list)
973     return;
974   for (id = 0; id < GST_peer_list_size; id++)
975   {
976     peer = GST_peer_list[id];
977     if (NULL == peer)
978       continue;
979     /* If destroy flag is set it means that this peer should have been
980      * destroyed by a context which we destroy before */
981     GNUNET_break (GNUNET_NO == peer->destroy_flag);
982     /* counter should be zero as we free all contexts before */
983     GNUNET_break (0 == peer->reference_cnt);
984     if ((GNUNET_NO == peer->is_remote) &&
985         (GNUNET_YES == peer->details.local.is_running))
986       GNUNET_TESTING_peer_kill (peer->details.local.peer);
987   }
988   for (id = 0; id < GST_peer_list_size; id++)
989   {
990     peer = GST_peer_list[id];
991     if (NULL == peer)
992       continue;    
993     if (GNUNET_NO == peer->is_remote)
994     {
995       if (GNUNET_YES == peer->details.local.is_running)
996         GNUNET_TESTING_peer_wait (peer->details.local.peer);
997       GNUNET_TESTING_peer_destroy (peer->details.local.peer);
998       GNUNET_CONFIGURATION_destroy (peer->details.local.cfg);
999     }
1000     GNUNET_free (peer);
1001   }
1002   GNUNET_free_non_null (GST_peer_list);
1003   GST_peer_list = NULL;
1004   GST_peer_list_size = 0;
1005 }
1006
1007
1008 /**
1009  * Task run upon timeout of forwarded SHUTDOWN_PEERS operation
1010  *
1011  * @param cls the ForwardedOperationContext
1012  * @param tc the scheduler task context
1013  */
1014 static void
1015 shutdown_peers_timeout_cb (void *cls,
1016                            const struct GNUNET_SCHEDULER_TaskContext *tc)
1017 {
1018   struct ForwardedOperationContext *fo_ctxt = cls;
1019   struct HandlerContext_ShutdownPeers *hc;
1020
1021   fo_ctxt->timeout_task = GNUNET_SCHEDULER_NO_TASK;
1022   hc = fo_ctxt->cls;
1023   hc->timeout = GNUNET_YES;
1024   GNUNET_assert (0 < hc->nslaves);
1025   hc->nslaves--;
1026   if (0 == hc->nslaves)
1027     GST_send_operation_fail_msg (fo_ctxt->client, fo_ctxt->operation_id,
1028                                  "Timeout at a slave controller");
1029   GNUNET_TESTBED_forward_operation_msg_cancel_ (fo_ctxt->opc);  
1030   GNUNET_SERVER_client_drop (fo_ctxt->client);
1031   GNUNET_CONTAINER_DLL_remove (fopcq_head, fopcq_tail, fo_ctxt);
1032   GNUNET_free (fo_ctxt);
1033 }
1034
1035
1036 /**
1037  * The reply msg handler forwarded SHUTDOWN_PEERS operation.  Checks if a
1038  * success reply is received from all clients and then sends the success message
1039  * to the client
1040  *
1041  * @param cls ForwardedOperationContext
1042  * @param msg the message to relay
1043  */
1044 static void
1045 shutdown_peers_reply_cb (void *cls,
1046                          const struct GNUNET_MessageHeader *msg)
1047 {
1048   struct ForwardedOperationContext *fo_ctxt = cls;
1049   struct HandlerContext_ShutdownPeers *hc;
1050   
1051   hc = fo_ctxt->cls;
1052   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != fo_ctxt->timeout_task);
1053   GNUNET_SCHEDULER_cancel (fo_ctxt->timeout_task);
1054   fo_ctxt->timeout_task = GNUNET_SCHEDULER_NO_TASK;
1055   GNUNET_assert (0 < hc->nslaves);
1056   hc->nslaves--;
1057   if (GNUNET_MESSAGE_TYPE_TESTBED_GENERIC_OPERATION_SUCCESS != 
1058       ntohs (msg->type))
1059     hc->timeout = GNUNET_YES;
1060   if (0 == hc->nslaves)
1061   {
1062     if (GNUNET_YES == hc->timeout)
1063       GST_send_operation_fail_msg (fo_ctxt->client, fo_ctxt->operation_id,
1064                                    "Timeout at a slave controller");
1065     else
1066       GST_send_operation_success_msg (fo_ctxt->client, fo_ctxt->operation_id);
1067   }
1068   GNUNET_SERVER_client_drop (fo_ctxt->client);
1069   GNUNET_CONTAINER_DLL_remove (fopcq_head, fopcq_tail, fo_ctxt);
1070   GNUNET_free (fo_ctxt);
1071 }
1072
1073
1074 /**
1075  * Handler for GNUNET_MESSAGE_TYPE_TESTBED_SHUTDOWN_PEERS messages
1076  *
1077  * @param cls NULL
1078  * @param client identification of the client
1079  * @param message the actual message
1080  */
1081 void
1082 GST_handle_shutdown_peers (void *cls, struct GNUNET_SERVER_Client *client,
1083                            const struct GNUNET_MessageHeader *message)
1084 {
1085   const struct GNUNET_TESTBED_ShutdownPeersMessage *msg;
1086   struct HandlerContext_ShutdownPeers *hc;
1087   struct Slave *slave;
1088   struct ForwardedOperationContext *fo_ctxt;
1089   uint64_t op_id;
1090   unsigned int cnt;
1091
1092   msg = (const struct GNUNET_TESTBED_ShutdownPeersMessage *) message;
1093   LOG_DEBUG ("Received SHUTDOWN_PEERS\n");
1094     /* Stop and destroy all peers */
1095   GST_free_mctxq ();
1096   GST_free_occq ();
1097   GST_free_roccq ();
1098   GST_clear_fopcq ();
1099   /* Forward to all slaves which we have started */
1100   op_id = GNUNET_ntohll (msg->operation_id);
1101   hc = GNUNET_malloc (sizeof (struct HandlerContext_ShutdownPeers));
1102   /* FIXME: have a better implementation where we track which slaves are
1103      started by this controller */
1104   for (cnt = 0; cnt < GST_slave_list_size; cnt++)
1105   {
1106     slave = GST_slave_list[cnt];
1107     if (NULL == slave)
1108       continue;
1109     if (NULL == slave->controller_proc) /* We didn't start the slave */
1110       continue;
1111     LOG_DEBUG ("Forwarding SHUTDOWN_PEERS\n");
1112     hc->nslaves++;
1113     fo_ctxt = GNUNET_malloc (sizeof (struct ForwardedOperationContext));
1114     GNUNET_SERVER_client_keep (client);
1115     fo_ctxt->client = client;
1116     fo_ctxt->operation_id = op_id;
1117     fo_ctxt->cls = hc;
1118     fo_ctxt->type = OP_SHUTDOWN_PEERS;
1119     fo_ctxt->opc =
1120         GNUNET_TESTBED_forward_operation_msg_ (slave->controller,
1121                                                fo_ctxt->operation_id,
1122                                                &msg->header,
1123                                                shutdown_peers_reply_cb,
1124                                                fo_ctxt);
1125     fo_ctxt->timeout_task =
1126         GNUNET_SCHEDULER_add_delayed (GST_timeout, &shutdown_peers_timeout_cb,
1127                                       fo_ctxt);
1128     GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fo_ctxt);
1129   }
1130   LOG_DEBUG ("Shutting down peers\n");
1131   GST_destroy_peers ();
1132   if (0 == hc->nslaves)
1133   {
1134     GST_send_operation_success_msg (client, op_id);
1135     GNUNET_free (hc);
1136   }
1137   GNUNET_SERVER_receive_done (client, GNUNET_OK);  
1138 }