/**
* Restart the given peer
* @param tth testing handle
* @param p the peer
* @param cfgname the cfg file used to restart
* @param restart_cb callback to call when restarted
* @param cb_cls callback closure
* @return GNUNET_OK in success otherwise GNUNET_SYSERR
*/
int
GNUNET_TRANSPORT_TESTING_restart_peer (struct GNUNET_TRANSPORT_TESTING_handle
                                       *tth, struct PeerContext *p,
                                       const char *cfgname,
                                       GNUNET_TRANSPORT_TESTING_start_cb
                                       restart_cb, void *cb_cls)
{
  GNUNET_assert (tth != NULL);
  GNUNET_assert (p != NULL);
  GNUNET_assert (NULL != p->peer);

  GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-testing",
                   "Restarting peer %u (`%s')\n", p->no, GNUNET_i2s (&p->id));

  /* shutdown */
  GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-testing",
                   "Stopping peer %u (`%s')\n", p->no, GNUNET_i2s (&p->id));
  if (NULL != p->ghh)
    GNUNET_TRANSPORT_get_hello_cancel (p->ghh);
  p->ghh = NULL;

  if (NULL != p->th)
    GNUNET_TRANSPORT_disconnect (p->th);

  if (GNUNET_SYSERR == GNUNET_TESTING_peer_stop(p->peer))
  {
    GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "transport-testing",
                     "Failed to stop peer %u (`%s')\n", p->no, GNUNET_i2s (&p->id));
    return GNUNET_SYSERR;
  }

  sleep (5);

  /* restart */
  if (GNUNET_SYSERR == GNUNET_TESTING_peer_start(p->peer))
  {
    GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "transport-testing",
                     "Failed to restart peer %u (`%s')\n",
                     p->no, GNUNET_i2s (&p->id));
    return GNUNET_SYSERR;
  }

  GNUNET_assert (p->th != NULL);
  GNUNET_assert (p->start_cb == NULL);
  p->start_cb = restart_cb;
  p->cb_cls = cb_cls;

  p->th = GNUNET_TRANSPORT_connect (p->cfg, NULL, p,
                                    &notify_receive,
                                    &notify_connect,
                                    &notify_disconnect);
  GNUNET_assert (NULL != p->th);

  p->ghh = GNUNET_TRANSPORT_get_hello (p->th, &get_hello, p);
  GNUNET_assert (p->ghh != NULL);
  return GNUNET_OK;
}
/**
 * shutdown the given peer
 * @param tth testing handle
 * @param p the peer
 */
void
GNUNET_TRANSPORT_TESTING_stop_peer (struct GNUNET_TRANSPORT_TESTING_handle *tth,
                                    struct PeerContext *p)
{
  GNUNET_assert (p != NULL);
  if (p->ghh != NULL)
  {
    GNUNET_TRANSPORT_get_hello_cancel (p->ghh);
    p->ghh = NULL;
  }
  if (p->th != NULL)
  {
    GNUNET_TRANSPORT_disconnect (p->th);
    p->th = NULL;
  }

  if (p->peer != NULL)
  {
    if (GNUNET_OK != GNUNET_TESTING_peer_stop (p->peer))
    {
      GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-testing",
                       "Testing lib failed to stop peer %u (`%s') \n", p->no,
                       GNUNET_i2s (&p->id));
    }
    GNUNET_TESTING_peer_destroy (p->peer);
    p->peer = NULL;
  }

  if (p->hello != NULL)
  {
    GNUNET_free (p->hello);
    p->hello = NULL;
  }
  if (p->cfg != NULL)
  {
    GNUNET_CONFIGURATION_destroy (p->cfg);
    p->cfg = NULL;
  }
  GNUNET_CONTAINER_DLL_remove (tth->p_head, tth->p_tail, p);
  GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-testing",
                   "Peer %u (`%s') stopped \n", p->no,
                   GNUNET_i2s (&p->id));
  GNUNET_free (p);
}
/**
 * Function to destroy a peer
 *
 * @param peer the peer structure to destroy
 */
void
GST_destroy_peer (struct Peer *peer)
{
  GNUNET_break (0 == peer->reference_cnt);
  if (GNUNET_YES == peer->is_remote)
  {
    peer_list_remove (peer);
    GNUNET_free (peer);
    return;
  }
  if (GNUNET_YES == peer->details.local.is_running)
  {
    GNUNET_TESTING_peer_stop (peer->details.local.peer);
    peer->details.local.is_running = GNUNET_NO;
  }
  GNUNET_TESTING_peer_destroy (peer->details.local.peer);
  GNUNET_CONFIGURATION_destroy (peer->details.local.cfg);
  peer_list_remove (peer);
  GNUNET_free (peer);
}
/**
 * Callback for lock status changes
 *
 * @param cls the handle
 *
 * @param domain_name the locking domain of the lock
 *
 * @param lock the lock for which this status is relevant
 *
 * @param status GNUNET_LOCKMANAGER_SUCCESS if the lock has been successfully
 *          acquired; GNUNET_LOCKMANAGER_RELEASE when the acquired lock is lost
 */
static void
status_cb (void *cls, const char *domain_name, uint32_t lock,
           enum GNUNET_LOCKMANAGER_Status status)
{
  LOG (GNUNET_ERROR_TYPE_DEBUG,
       "Status change callback called on lock: %d of domain: %s\n", lock,
       domain_name);
  switch (result)
  {
  case TEST_INIT:
    GNUNET_assert (handle == cls);
    GNUNET_assert (GNUNET_LOCKMANAGER_SUCCESS == status);
    result = TEST_CLIENT1_LOCK_SUCCESS;
    request2 =
        GNUNET_LOCKMANAGER_acquire_lock (handle2, "GNUNET_LOCKMANAGER_TESTING",
                                         99, &status_cb, handle2);
    GNUNET_assert (NULL != request2);
    GNUNET_LOCKMANAGER_cancel_request (request);
    request = NULL;
    break;
  case TEST_CLIENT1_LOCK_SUCCESS:
    GNUNET_assert (handle2 == cls);
    GNUNET_assert (GNUNET_LOCKMANAGER_SUCCESS == status);
    result = TEST_CLIENT2_LOCK_SUCCESS;
    /* We should stop our peer to simulate crash in lockmanager service */
    GNUNET_TESTING_peer_stop (self);
    break;
  case TEST_CLIENT2_LOCK_SUCCESS:
    GNUNET_assert (handle2 == cls);
    GNUNET_assert (GNUNET_LOCKMANAGER_RELEASE == status);
    GNUNET_assert (99 == lock);
    GNUNET_assert (0 == strcmp (domain_name, "GNUNET_LOCKMANAGER_TESTING"));
    result = TEST_CLIENT2_SERVER_CRASH_SUCCESS;
    GNUNET_LOCKMANAGER_cancel_request (request2);
    request2 = NULL;
    GNUNET_SCHEDULER_add_delayed (TIME_REL_SECONDS (1), &do_shutdown, NULL);
    break;
  default:
    GNUNET_assert (0);          /* We should never reach here */
  }
}
/**
 * Task for shutdown
 *
 * @param cls the testing context
 * @param tc the tast context
 */
static void
do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
{
  struct TestingContext *test_ctx = cls;
  struct GNUNET_TESTING_Peer *peer;
  unsigned int cnt;

  GNUNET_assert (NULL != test_ctx);
  for (cnt = 0; cnt < NUM_PEERS; cnt++)
  {
    peer = test_ctx->peers[cnt];
    if (NULL == peer)
      continue;
    (void) GNUNET_TESTING_peer_stop (peer);
    GNUNET_TESTING_peer_destroy (peer);
  }
  if (NULL != test_ctx->cfg)
    GNUNET_CONFIGURATION_destroy (test_ctx->cfg);
  if (NULL != test_ctx->system)
    GNUNET_TESTING_system_destroy (test_ctx->system, GNUNET_YES);
  GNUNET_free (test_ctx);
}