- unify testbed operation handlers, avoid pointer arithmetic
[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 rs status of the request
772  * @param service service name
773  * @param result result of the operation
774  */
775 static void
776 service_manage_result_cb (void *cls, 
777                           enum GNUNET_ARM_RequestStatus rs, 
778                           const char *service, enum GNUNET_ARM_Result result)
779 {
780   struct ManageServiceContext *mctx = cls;
781   char *emsg;
782
783   emsg = NULL;
784   if (GNUNET_YES == mctx->expired)
785     return;
786   if (GNUNET_ARM_REQUEST_SENT_OK != rs)
787   {
788     GNUNET_asprintf (&emsg, "Error communicating with Peer %u's ARM: %s",
789                      mctx->peer->id, arm_req_string (rs));
790     goto ret;
791   }
792   if (1 == mctx->start)
793     goto service_start_check;
794   if (! ((GNUNET_ARM_RESULT_STOPPED == result)
795             || (GNUNET_ARM_RESULT_STOPPING == result)
796             || (GNUNET_ARM_RESULT_IS_STOPPING_ALREADY == result)
797             || (GNUNET_ARM_RESULT_IS_STOPPED_ALREADY == result)) )
798   {
799     /* stopping a service failed */
800     GNUNET_asprintf (&emsg, arm_ret_string (result), service);
801     goto ret;
802   }
803   /* service stopped successfully */
804   goto ret;
805
806  service_start_check:
807   if (! ((GNUNET_ARM_RESULT_STARTING == result)
808             || (GNUNET_ARM_RESULT_IS_STARTING_ALREADY == result)
809             || (GNUNET_ARM_RESULT_IS_STARTED_ALREADY == result)) )
810   {
811     /* starting a service failed */
812     GNUNET_asprintf (&emsg, arm_ret_string (result), service);
813     goto ret;
814   }
815   /* service started successfully */
816   
817  ret:
818   if (NULL != emsg)
819   {
820     LOG_DEBUG ("%s\n", emsg);
821     GST_send_operation_fail_msg (mctx->client, mctx->op_id, emsg);
822   }
823   else
824     GST_send_operation_success_msg (mctx->client, mctx->op_id);
825   GNUNET_free_non_null (emsg);
826   cleanup_mctx (mctx);
827 }
828
829
830 /**
831  * Handler for GNUNET_TESTBED_ManagePeerServiceMessage message
832  *
833  * @param cls NULL
834  * @param client identification of client
835  * @param message the actual message
836  */
837 void
838 GST_handle_manage_peer_service (void *cls, struct GNUNET_SERVER_Client *client,
839                                 const struct GNUNET_MessageHeader *message)
840 {
841   const struct GNUNET_TESTBED_ManagePeerServiceMessage *msg;
842   const char* service;
843   struct Peer *peer;
844   char *emsg;
845   struct GNUNET_ARM_Handle *ah;
846   struct ManageServiceContext *mctx;
847   struct ForwardedOperationContext *fopc;
848   uint64_t op_id;
849   uint32_t peer_id;
850   uint16_t msize;
851   
852
853   msize = ntohs (message->size);
854   if (msize <= sizeof (struct GNUNET_TESTBED_ManagePeerServiceMessage))
855   {
856     GNUNET_break_op (0);  
857     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
858     return;
859   }
860   msg = (const struct GNUNET_TESTBED_ManagePeerServiceMessage *) message;
861   service = (const char *) &msg[1];  
862   if ('\0' != service[msize - sizeof
863                       (struct GNUNET_TESTBED_ManagePeerServiceMessage) - 1])
864   {
865     GNUNET_break_op (0);
866     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
867     return;
868   }
869   if (1 < msg->start)
870   {
871     GNUNET_break_op (0);
872     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
873     return;    
874   }
875   peer_id = ntohl (msg->peer_id);
876   op_id = GNUNET_ntohll (msg->operation_id);
877   LOG_DEBUG ("Received request to manage service %s on peer %u\n",
878              service, (unsigned int) peer_id);
879   if ((GST_peer_list_size <= peer_id)
880       || (NULL == (peer = GST_peer_list[peer_id])))
881   {
882     GNUNET_asprintf (&emsg, "Asked to manage service of a non existent peer "
883                      "with id: %u", peer_id);
884     goto err_ret;
885   }
886   if (0 == strcasecmp ("arm", service))
887   {
888     emsg = GNUNET_strdup ("Cannot start/stop peer's ARM service.  "
889                           "Use peer start/stop for that");
890     goto err_ret;
891   }
892   if (GNUNET_YES == peer->is_remote)
893   {
894     /* Forward the destory message to sub controller */
895     fopc = GNUNET_malloc (sizeof (struct ForwardedOperationContext));
896     GNUNET_SERVER_client_keep (client);
897     fopc->client = client;
898     fopc->cls = peer;
899     fopc->type = OP_MANAGE_SERVICE;
900     fopc->operation_id = op_id;
901     fopc->opc =
902         GNUNET_TESTBED_forward_operation_msg_ (peer->details.remote.
903                                                slave->controller,
904                                                fopc->operation_id, &msg->header,
905                                                &GST_forwarded_operation_reply_relay,
906                                                fopc);
907     fopc->timeout_task =
908         GNUNET_SCHEDULER_add_delayed (GST_timeout, &GST_forwarded_operation_timeout,
909                                       fopc);
910     GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fopc);
911     GNUNET_SERVER_receive_done (client, GNUNET_OK);
912     return;
913   }
914   if ((0 != peer->reference_cnt)
915       && ( (0 == strcasecmp ("core", service))
916            || (0 == strcasecmp ("transport", service)) )  )
917   {
918     GNUNET_asprintf (&emsg, "Cannot stop %s service of peer with id: %u "
919                      "since it is required by existing operations",
920                      service, peer_id);
921     goto err_ret;
922   }
923   ah = GNUNET_ARM_connect (peer->details.local.cfg, NULL, NULL);
924   if (NULL == ah)
925   {
926     GNUNET_asprintf (&emsg,
927                      "Cannot connect to ARM service of peer with id: %u",
928                      peer_id);
929     goto err_ret;
930   }
931   mctx = GNUNET_malloc (sizeof (struct ManageServiceContext));
932   mctx->peer = peer;
933   peer->reference_cnt++;
934   mctx->op_id = op_id;
935   mctx->ah = ah;
936   GNUNET_SERVER_client_keep (client);
937   mctx->client = client;
938   mctx->start = msg->start;
939   GNUNET_CONTAINER_DLL_insert_tail (mctx_head, mctx_tail, mctx);
940   if (1 == mctx->start)
941     GNUNET_ARM_request_service_start (mctx->ah, service,
942                                       GNUNET_OS_INHERIT_STD_ERR,
943                                       GST_timeout,
944                                       service_manage_result_cb,
945                                       mctx);
946   else
947     GNUNET_ARM_request_service_stop (mctx->ah, service,
948                                      GST_timeout,
949                                      service_manage_result_cb,
950                                      mctx);
951   GNUNET_SERVER_receive_done (client, GNUNET_OK);
952   return;
953
954  err_ret:
955   LOG (GNUNET_ERROR_TYPE_ERROR, "%s\n", emsg);
956   GST_send_operation_fail_msg (client, op_id, emsg);
957   GNUNET_free (emsg);
958   GNUNET_SERVER_receive_done (client, GNUNET_OK);
959 }
960
961
962 /**
963  * Stops and destroys all peers
964  */
965 void
966 GST_destroy_peers ()
967 {
968   struct Peer *peer;
969   unsigned int id;
970
971   if (NULL == GST_peer_list)
972     return;
973   for (id = 0; id < GST_peer_list_size; id++)
974   {
975     peer = GST_peer_list[id];
976     if (NULL == peer)
977       continue;
978     /* If destroy flag is set it means that this peer should have been
979      * destroyed by a context which we destroy before */
980     GNUNET_break (GNUNET_NO == peer->destroy_flag);
981     /* counter should be zero as we free all contexts before */
982     GNUNET_break (0 == peer->reference_cnt);
983     if ((GNUNET_NO == peer->is_remote) &&
984         (GNUNET_YES == peer->details.local.is_running))
985       GNUNET_TESTING_peer_kill (peer->details.local.peer);
986   }
987   for (id = 0; id < GST_peer_list_size; id++)
988   {
989     peer = GST_peer_list[id];
990     if (NULL == peer)
991       continue;    
992     if (GNUNET_NO == peer->is_remote)
993     {
994       if (GNUNET_YES == peer->details.local.is_running)
995         GNUNET_TESTING_peer_wait (peer->details.local.peer);
996       GNUNET_TESTING_peer_destroy (peer->details.local.peer);
997       GNUNET_CONFIGURATION_destroy (peer->details.local.cfg);
998     }
999     GNUNET_free (peer);
1000   }
1001   GNUNET_free_non_null (GST_peer_list);
1002   GST_peer_list = NULL;
1003   GST_peer_list_size = 0;
1004 }
1005
1006
1007 /**
1008  * Task run upon timeout of forwarded SHUTDOWN_PEERS operation
1009  *
1010  * @param cls the ForwardedOperationContext
1011  * @param tc the scheduler task context
1012  */
1013 static void
1014 shutdown_peers_timeout_cb (void *cls,
1015                            const struct GNUNET_SCHEDULER_TaskContext *tc)
1016 {
1017   struct ForwardedOperationContext *fo_ctxt = cls;
1018   struct HandlerContext_ShutdownPeers *hc;
1019
1020   fo_ctxt->timeout_task = GNUNET_SCHEDULER_NO_TASK;
1021   hc = fo_ctxt->cls;
1022   hc->timeout = GNUNET_YES;
1023   GNUNET_assert (0 < hc->nslaves);
1024   hc->nslaves--;
1025   if (0 == hc->nslaves)
1026     GST_send_operation_fail_msg (fo_ctxt->client, fo_ctxt->operation_id,
1027                                  "Timeout at a slave controller");
1028   GNUNET_TESTBED_forward_operation_msg_cancel_ (fo_ctxt->opc);  
1029   GNUNET_SERVER_client_drop (fo_ctxt->client);
1030   GNUNET_CONTAINER_DLL_remove (fopcq_head, fopcq_tail, fo_ctxt);
1031   GNUNET_free (fo_ctxt);
1032 }
1033
1034
1035 /**
1036  * The reply msg handler forwarded SHUTDOWN_PEERS operation.  Checks if a
1037  * success reply is received from all clients and then sends the success message
1038  * to the client
1039  *
1040  * @param cls ForwardedOperationContext
1041  * @param msg the message to relay
1042  */
1043 static void
1044 shutdown_peers_reply_cb (void *cls,
1045                          const struct GNUNET_MessageHeader *msg)
1046 {
1047   struct ForwardedOperationContext *fo_ctxt = cls;
1048   struct HandlerContext_ShutdownPeers *hc;
1049   
1050   hc = fo_ctxt->cls;
1051   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != fo_ctxt->timeout_task);
1052   GNUNET_SCHEDULER_cancel (fo_ctxt->timeout_task);
1053   fo_ctxt->timeout_task = GNUNET_SCHEDULER_NO_TASK;
1054   GNUNET_assert (0 < hc->nslaves);
1055   hc->nslaves--;
1056   if (GNUNET_MESSAGE_TYPE_TESTBED_GENERIC_OPERATION_SUCCESS != 
1057       ntohs (msg->type))
1058     hc->timeout = GNUNET_YES;
1059   if (0 == hc->nslaves)
1060   {
1061     if (GNUNET_YES == hc->timeout)
1062       GST_send_operation_fail_msg (fo_ctxt->client, fo_ctxt->operation_id,
1063                                    "Timeout at a slave controller");
1064     else
1065       GST_send_operation_success_msg (fo_ctxt->client, fo_ctxt->operation_id);
1066   }
1067   GNUNET_SERVER_client_drop (fo_ctxt->client);
1068   GNUNET_CONTAINER_DLL_remove (fopcq_head, fopcq_tail, fo_ctxt);
1069   GNUNET_free (fo_ctxt);
1070 }
1071
1072
1073 /**
1074  * Handler for GNUNET_MESSAGE_TYPE_TESTBED_SHUTDOWN_PEERS messages
1075  *
1076  * @param cls NULL
1077  * @param client identification of the client
1078  * @param message the actual message
1079  */
1080 void
1081 GST_handle_shutdown_peers (void *cls, struct GNUNET_SERVER_Client *client,
1082                            const struct GNUNET_MessageHeader *message)
1083 {
1084   const struct GNUNET_TESTBED_ShutdownPeersMessage *msg;
1085   struct HandlerContext_ShutdownPeers *hc;
1086   struct Slave *slave;
1087   struct ForwardedOperationContext *fo_ctxt;
1088   uint64_t op_id;
1089   unsigned int cnt;
1090
1091   msg = (const struct GNUNET_TESTBED_ShutdownPeersMessage *) message;
1092   LOG_DEBUG ("Received SHUTDOWN_PEERS\n");
1093     /* Stop and destroy all peers */
1094   GST_free_mctxq ();
1095   GST_free_occq ();
1096   GST_free_roccq ();
1097   GST_clear_fopcq ();
1098   /* Forward to all slaves which we have started */
1099   op_id = GNUNET_ntohll (msg->operation_id);
1100   hc = GNUNET_malloc (sizeof (struct HandlerContext_ShutdownPeers));
1101   /* FIXME: have a better implementation where we track which slaves are
1102      started by this controller */
1103   for (cnt = 0; cnt < GST_slave_list_size; cnt++)
1104   {
1105     slave = GST_slave_list[cnt];
1106     if (NULL == slave)
1107       continue;
1108     if (NULL == slave->controller_proc) /* We didn't start the slave */
1109       continue;
1110     LOG_DEBUG ("Forwarding SHUTDOWN_PEERS\n");
1111     hc->nslaves++;
1112     fo_ctxt = GNUNET_malloc (sizeof (struct ForwardedOperationContext));
1113     GNUNET_SERVER_client_keep (client);
1114     fo_ctxt->client = client;
1115     fo_ctxt->operation_id = op_id;
1116     fo_ctxt->cls = hc;
1117     fo_ctxt->type = OP_SHUTDOWN_PEERS;
1118     fo_ctxt->opc =
1119         GNUNET_TESTBED_forward_operation_msg_ (slave->controller,
1120                                                fo_ctxt->operation_id,
1121                                                &msg->header,
1122                                                shutdown_peers_reply_cb,
1123                                                fo_ctxt);
1124     fo_ctxt->timeout_task =
1125         GNUNET_SCHEDULER_add_delayed (GST_timeout, &shutdown_peers_timeout_cb,
1126                                       fo_ctxt);
1127     GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fo_ctxt);
1128   }
1129   LOG_DEBUG ("Shutting down peers\n");
1130   GST_destroy_peers ();
1131   if (0 == hc->nslaves)
1132   {
1133     GST_send_operation_success_msg (client, op_id);
1134     GNUNET_free (hc);
1135   }
1136   GNUNET_SERVER_receive_done (client, GNUNET_OK);  
1137 }