+/**
+ * Cleans up the given PeerReconfigureContext
+ *
+ * @param prc the PeerReconfigureContext
+ */
+static void
+cleanup_prc (struct PeerReconfigureContext *prc)
+{
+ struct Peer *peer;
+
+ if (VALID_PEER_ID (prc->peer_id))
+ {
+ peer = GST_peer_list [prc->peer_id];
+ if (1 != prc->stopped)
+ {
+ GNUNET_TESTING_peer_stop_async_cancel (peer->details.local.peer);
+ stop_peer (peer); /* Stop the peer synchronously */
+ }
+ }
+ if (NULL != prc->cfg)
+ GNUNET_CONFIGURATION_destroy (prc->cfg);
+ GNUNET_SERVER_client_drop (prc->client);
+ GNUNET_CONTAINER_DLL_remove (prc_head, prc_tail, prc);
+ GNUNET_free (prc);
+}
+
+
+/**
+ * Cleans up the Peer reconfigure context list
+ */
+void
+GST_free_prcq ()
+{
+ while (NULL != prc_head)
+ cleanup_prc (prc_head);
+}
+
+
+/**
+ * Update peer configuration
+ *
+ * @param peer the peer to update
+ * @param cfg the new configuration
+ * @return error message (freshly allocated); NULL upon success
+ */
+static char *
+update_peer_config (struct Peer *peer,
+ struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ char *emsg;
+
+ GNUNET_TESTING_peer_destroy (peer->details.local.peer);
+ GNUNET_CONFIGURATION_destroy (peer->details.local.cfg);
+ peer->details.local.cfg = cfg;
+ emsg = NULL;
+ peer->details.local.peer
+ = GNUNET_TESTING_peer_configure (GST_context->system,
+ peer->details.local.cfg, peer->id,
+ NULL /* Peer id */ ,
+ &emsg);
+ return emsg;
+}
+
+
+/**
+ * Callback to inform whether the peer is running or stopped.
+ *
+ * @param cls the closure given to GNUNET_TESTING_peer_stop_async()
+ * @param p the respective peer whose status is being reported
+ * @param success GNUNET_YES if the peer is stopped; GNUNET_SYSERR upon any
+ * error
+ */
+static void
+prc_stop_cb (void *cls, struct GNUNET_TESTING_Peer *p, int success)
+{
+ struct PeerReconfigureContext *prc = cls;
+ struct Peer *peer;
+ char *emsg;
+
+ GNUNET_assert (VALID_PEER_ID (prc->peer_id));
+ peer = GST_peer_list [prc->peer_id];
+ GNUNET_assert (GNUNET_NO == peer->is_remote);
+ emsg = update_peer_config (peer, prc->cfg);
+ prc->cfg = NULL;
+ prc->stopped = 1;
+ if (NULL != emsg)
+ {
+ GST_send_operation_fail_msg (prc->client, prc->op_id, emsg);
+ goto cleanup;
+ }
+ if (GNUNET_OK != start_peer (peer))
+ {
+ GST_send_operation_fail_msg (prc->client, prc->op_id,
+ "Failed to start reconfigured peer");
+ goto cleanup;
+ }
+ GST_send_operation_success_msg (prc->client, prc->op_id);
+
+ cleanup:
+ cleanup_prc (prc);
+ return;
+}
+
+
+/**
+ * Handler for GNUNET_MESSAGE_TYPDE_TESTBED_RECONFIGURE_PEER type messages.
+ * Should stop the peer asyncronously, destroy it and create it again with the
+ * new configuration.
+ *
+ * @param cls NULL
+ * @param client identification of the client
+ * @param message the actual message
+ */
+void
+GST_handle_peer_reconfigure (void *cls, struct GNUNET_SERVER_Client *client,
+ const struct GNUNET_MessageHeader *message)
+{
+ const struct GNUNET_TESTBED_PeerReconfigureMessage *msg;
+ struct Peer *peer;
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+ struct ForwardedOperationContext *fopc;
+ struct PeerReconfigureContext *prc;
+ char *emsg;
+ uint64_t op_id;
+ uint32_t peer_id;
+ uint16_t msize;
+
+ msize = ntohs (message->size);
+ if (msize <= sizeof (struct GNUNET_TESTBED_PeerReconfigureMessage))
+ {
+ GNUNET_break_op (0);
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+ msg = (const struct GNUNET_TESTBED_PeerReconfigureMessage *) message;
+ peer_id = ntohl (msg->peer_id);
+ op_id = GNUNET_ntohll (msg->operation_id);
+ if (!VALID_PEER_ID (peer_id))
+ {
+ GNUNET_break (0);
+ GST_send_operation_fail_msg (client, op_id, "Peer not found");
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+ return;
+ }
+ peer = GST_peer_list[peer_id];
+ if (GNUNET_YES == peer->is_remote)
+ {
+ LOG_DEBUG ("Forwarding PEER_RECONFIGURE for peer: %u\n", peer_id);
+ fopc = GNUNET_new (struct ForwardedOperationContext);
+ GNUNET_SERVER_client_keep (client);
+ fopc->client = client;
+ fopc->operation_id = op_id;
+ fopc->type = OP_PEER_RECONFIGURE;
+ fopc->opc =
+ GNUNET_TESTBED_forward_operation_msg_ (peer->details.remote.
+ slave->controller,
+ fopc->operation_id, &msg->header,
+ &GST_forwarded_operation_reply_relay,
+ fopc);
+ fopc->timeout_task =
+ GNUNET_SCHEDULER_add_delayed (GST_timeout, &GST_forwarded_operation_timeout,
+ fopc);
+ GNUNET_CONTAINER_DLL_insert_tail (fopcq_head, fopcq_tail, fopc);
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+ return;
+ }
+ LOG_DEBUG ("Received PEER_RECONFIGURE for peer %u\n", peer_id);
+ if (0 < peer->reference_cnt)
+ {
+ GNUNET_break (0);
+ GST_send_operation_fail_msg (client, op_id, "Peer in use");
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+ return;
+ }
+ if (GNUNET_YES == peer->destroy_flag)
+ {
+ GNUNET_break (0);
+ GST_send_operation_fail_msg (client, op_id, "Peer is being destroyed");
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+ return;
+ }
+ cfg = GNUNET_TESTBED_extract_config_ (message);
+ if (NULL == cfg)
+ {
+ GNUNET_break (0);
+ GST_send_operation_fail_msg (client, op_id, "Compression error");
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+ return;
+ }
+ if (GNUNET_NO == peer->details.local.is_running)
+ {
+ emsg = update_peer_config (peer, cfg);
+ if (NULL != emsg)
+ GST_send_operation_fail_msg (client, op_id, emsg);
+ GST_send_operation_success_msg (client, op_id);
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+ GNUNET_free_non_null (emsg);
+ return;
+ }
+ prc = GNUNET_new (struct PeerReconfigureContext);
+ if (GNUNET_OK !=
+ GNUNET_TESTING_peer_stop_async (peer->details.local.peer, &prc_stop_cb,
+ prc))
+ {
+ GNUNET_assert (0 < GNUNET_asprintf (&emsg,
+ "Error trying to stop peer %u asynchronously\n",
+ peer_id));
+ LOG (GNUNET_ERROR_TYPE_ERROR, "%s\n", emsg);
+ GST_send_operation_fail_msg (client, op_id, emsg);
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+ GNUNET_free (prc);
+ GNUNET_free (emsg);
+ return;
+ }
+ prc->cfg = cfg;
+ prc->peer_id = peer_id;
+ prc->op_id = op_id;
+ prc->client = client;
+ GNUNET_SERVER_client_keep (client);
+ GNUNET_CONTAINER_DLL_insert_tail (prc_head, prc_tail, prc);
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+}
+
+