/**
 * Try to open a connection to the given neigbour.  If the connection is open
 * already, then it is re-used.  If not, the request is queued in the operation
 * queues responsible for bounding the total number of file descriptors.  The
 * actual connection will happen when the operation queue marks the
 * corresponding operation as active.
 *
 * @param n the neighbour to open a connection to
 * @param cb the notification callback to call when the connection is opened
 * @param cb_cls the closure for the above callback
 */
struct NeighbourConnectNotification *
GST_neighbour_get_connection (struct Neighbour *n,
                              GST_NeigbourConnectNotifyCallback cb,
                              void *cb_cls)
{
  struct NeighbourConnectNotification *h;

  GNUNET_assert (NULL != cb);
  LOG_DEBUG ("Attempting to get connection to controller on host %u\n",
             n->host_id);
  h = GNUNET_new (struct NeighbourConnectNotification);
  h->n = n;
  h->cb  = cb;
  h->cb_cls = cb_cls;
  GNUNET_CONTAINER_DLL_insert_tail (n->nl_head, n->nl_tail, h);
  if (NULL == n->conn_op)
  {
    GNUNET_assert (NULL == n->controller);
    n->conn_op = GNUNET_TESTBED_operation_create_ (n, &opstart_neighbour_conn,
                                                   &oprelease_neighbour_conn);
    GNUNET_TESTBED_operation_queue_insert_ (GST_opq_openfds, n->conn_op);
    GNUNET_TESTBED_operation_begin_wait_ (n->conn_op);
    return h;
  }
  trigger_notifications (n);
  return h;
}
Example #2
0
/**
 * Convenience method that iterates over all (running) peers
 * and retrieves all statistics from each peer.
 *
 * @param num_peers number of peers to iterate over
 * @param peers array of peers to iterate over
 * @param subsystem limit to the specified subsystem, NULL for all subsystems
 * @param name name of the statistic value, NULL for all values
 * @param proc processing function for each statistic retrieved
 * @param cont continuation to call once call is completed(?)
 * @param cls closure to pass to proc and cont
 * @return operation handle to cancel the operation
 */
struct GNUNET_TESTBED_Operation *
GNUNET_TESTBED_get_statistics (unsigned int num_peers,
                               struct GNUNET_TESTBED_Peer **peers,
                               const char *subsystem, const char *name,
                               GNUNET_TESTBED_StatisticsIterator proc,
                               GNUNET_TESTBED_OperationCompletionCallback cont,
                               void *cls)
{
  struct GetStatsContext *sc;

  GNUNET_assert (NULL != proc);
  GNUNET_assert (NULL != cont);
  if (NULL == no_wait_queue)
    no_wait_queue = GNUNET_TESTBED_operation_queue_create_
        (OPERATION_QUEUE_TYPE_FIXED, UINT_MAX);
  sc = GNUNET_new (struct GetStatsContext);
  sc->peers = peers;
  sc->subsystem = (NULL == subsystem) ? NULL : GNUNET_strdup (subsystem);
  sc->name = (NULL == name) ? NULL : GNUNET_strdup (name);
  sc->proc = proc;
  sc->cont = cont;
  sc->cb_cls = cls;
  sc->num_peers = num_peers;
  sc->main_op =
      GNUNET_TESTBED_operation_create_ (sc, &opstart_get_stats,
                                        &oprelease_get_stats);
  GNUNET_TESTBED_operation_queue_insert_ (no_wait_queue, sc->main_op);
  GNUNET_TESTBED_operation_begin_wait_ (sc->main_op);
  return sc->main_op;
}
Example #3
0
/**
 * Connect to a service offered by the given peer.  Will ensure that
 * the request is queued to not overwhelm our ability to create and
 * maintain connections with other systems.  The actual service
 * handle is then returned via the 'op_result' member in the event
 * callback.  The 'ca' callback is used to create the connection
 * when the time is right; the 'da' callback will be used to
 * destroy the connection (upon 'GNUNET_TESTBED_operation_done').
 * 'GNUNET_TESTBED_operation_done' can be used to abort this
 * operation until the event callback has been called.
 *
 * @param op_cls closure to pass in operation event
 * @param peer peer that runs the service
 * @param service_name name of the service to connect to
 * @param cb the callback to call when this operation finishes
 * @param cb_cls closure for the above callback
 * @param ca helper function to establish the connection
 * @param da helper function to close the connection
 * @param cada_cls closure for ca and da
 * @return handle for the operation
 */
struct GNUNET_TESTBED_Operation *
GNUNET_TESTBED_service_connect (void *op_cls, struct GNUNET_TESTBED_Peer *peer,
                                const char *service_name,
                                GNUNET_TESTBED_ServiceConnectCompletionCallback
                                cb, void *cb_cls,
                                GNUNET_TESTBED_ConnectAdapter ca,
                                GNUNET_TESTBED_DisconnectAdapter da,
                                void *cada_cls)
{
  struct ServiceConnectData *data;

  data = GNUNET_new (struct ServiceConnectData);
  data->ca = ca;
  data->da = da;
  data->cada_cls = cada_cls;
  data->op_cls = op_cls;
  data->peer = peer;
  data->state = INIT;
  data->cb = cb;
  data->cb_cls = cb_cls;
  data->operation =
      GNUNET_TESTBED_operation_create_ (data, &opstart_service_connect,
                                        &oprelease_service_connect);
  GNUNET_TESTBED_operation_queue_insert_ (peer->
                                          controller->opq_parallel_service_connections,
                                          data->operation);
  GNUNET_TESTBED_operation_queue_insert_ (peer->
                                          controller->opq_parallel_operations,
                                          data->operation);
  GNUNET_TESTBED_operation_begin_wait_ (data->operation);
  return data->operation;
}
/**
 * Both peers must have been started before calling this function.
 * This function then obtains a HELLO from 'p1', gives it to 'p2'
 * and asks 'p2' to connect to 'p1'.
 *
 * @param op_cls closure argument to give with the operation event
 * @param cb the callback to call when this operation has finished
 * @param cb_cls the closure for the above callback
 * @param p1 first peer
 * @param p2 second peer
 * @return handle to the operation, NULL if connecting these two
 *         peers is fundamentally not possible at this time (peers
 *         not running or underlay disallows)
 */
struct GNUNET_TESTBED_Operation *
GNUNET_TESTBED_overlay_connect (void *op_cls,
                                GNUNET_TESTBED_OperationCompletionCallback cb,
                                void *cb_cls,
				struct GNUNET_TESTBED_Peer *p1,
				struct GNUNET_TESTBED_Peer *p2)
{
  struct OperationContext *opc;
  struct OverlayConnectData *data;

  GNUNET_assert ((PS_STARTED == p1->state) && (PS_STARTED == p2->state));
  data = GNUNET_malloc (sizeof (struct OverlayConnectData));
  data->p1 = p1;
  data->p2 = p2;
  data->cb = cb;
  data->cb_cls = cb_cls;
  data->state = OCD_INIT;
  opc = GNUNET_malloc (sizeof (struct OperationContext));
  opc->data = data;
  opc->c = p1->controller;
  opc->id = GNUNET_TESTBED_get_next_op_id (opc->c);
  opc->type = OP_OVERLAY_CONNECT;
  opc->op_cls = op_cls;
  opc->op =
      GNUNET_TESTBED_operation_create_ (opc, &opstart_overlay_connect,
                                        &oprelease_overlay_connect);
  GNUNET_TESTBED_operation_queue_insert_
      (opc->c->opq_parallel_overlay_connect_operations, opc->op);
  GNUNET_TESTBED_operation_begin_wait_ (opc->op);
  return opc->op;
}
/**
 * Request information about a peer. The controller callback will not be called
 * with event type GNUNET_TESTBED_ET_OPERATION_FINISHED when result for this
 * operation is available. Instead, the GNUNET_TESTBED_PeerInfoCallback() will
 * be called.
 *
 * @param peer peer to request information about
 * @param pit desired information
 * @param cb the convenience callback to be called when results for this
 *          operation are available
 * @param cb_cls the closure for the above callback
 * @return handle to the operation
 */
struct GNUNET_TESTBED_Operation *
GNUNET_TESTBED_peer_get_information (struct GNUNET_TESTBED_Peer *peer,
				     enum GNUNET_TESTBED_PeerInformationType
				     pit,
				     GNUNET_TESTBED_PeerInfoCallback cb,
				     void *cb_cls)
{
  struct OperationContext *opc;
  struct PeerInfoData *data;

  GNUNET_assert (GNUNET_TESTBED_PIT_GENERIC != pit);
  data = GNUNET_malloc (sizeof (struct PeerInfoData));
  data->peer = peer;
  data->pit = pit;
  data->cb = cb;
  data->cb_cls = cb_cls;
  opc = GNUNET_malloc (sizeof (struct OperationContext));
  opc->c = peer->controller;
  opc->data = data;
  opc->type = OP_PEER_INFO;
  opc->id = GNUNET_TESTBED_get_next_op_id (opc->c);
  opc->op =
      GNUNET_TESTBED_operation_create_ (opc, &opstart_peer_getinfo,
                                        &oprelease_peer_getinfo);
  GNUNET_TESTBED_operation_queue_insert_ (opc->c->opq_parallel_operations,
                                          opc->op);
  GNUNET_TESTBED_operation_begin_wait_ (opc->op);
  return opc->op;
}
/**
 * Stop the given peer.  The handle remains valid (use
 * "GNUNET_TESTBED_peer_destroy" to fully clean up the
 * state of the peer).
 *
 * @param peer peer to stop
 * @param pcc function to call upon completion
 * @param pcc_cls closure for 'pcc'
 * @return handle to the operation
 */
struct GNUNET_TESTBED_Operation *
GNUNET_TESTBED_peer_stop (struct GNUNET_TESTBED_Peer *peer,
			  GNUNET_TESTBED_PeerChurnCallback pcc,
			  void *pcc_cls)
{
  struct OperationContext *opc;
  struct PeerEventData *data;
 
  data = GNUNET_malloc (sizeof (struct PeerEventData));
  data->peer = peer;
  data->pcc = pcc;
  data->pcc_cls = pcc_cls;
  opc = GNUNET_malloc (sizeof (struct OperationContext));
  opc->c = peer->controller;
  opc->data = data;
  opc->id = GNUNET_TESTBED_get_next_op_id (opc->c);
  opc->type = OP_PEER_STOP;
  opc->op =
      GNUNET_TESTBED_operation_create_ (opc, &opstart_peer_stop,
                                        &oprelease_peer_stop);
  GNUNET_TESTBED_operation_queue_insert_ (opc->c->opq_parallel_operations,
                                          opc->op);
  GNUNET_TESTBED_operation_begin_wait_ (opc->op);
  return opc->op;
}
/**
 * Main run function.
 *
 * @param cls NULL
 * @param args arguments passed to GNUNET_PROGRAM_run
 * @param cfgfile the path to configuration file
 * @param cfg the configuration file handle
 */
static void
run (void *cls, char *const *args, const char *cfgfile,
     const struct GNUNET_CONFIGURATION_Handle *config)
{
  q1 = GNUNET_TESTBED_operation_queue_create_ (OPERATION_QUEUE_TYPE_FIXED, 1);
  GNUNET_assert (NULL != q1);
  q2 = GNUNET_TESTBED_operation_queue_create_ (OPERATION_QUEUE_TYPE_FIXED, 2);
  GNUNET_assert (NULL != q2);
  op1 = GNUNET_TESTBED_operation_create_ (&op1, start_cb, release_cb);
  GNUNET_assert (NULL != op1);
  op2 = GNUNET_TESTBED_operation_create_ (&op2, start_cb, release_cb);
  GNUNET_TESTBED_operation_queue_insert_ (q1, op1);
  GNUNET_TESTBED_operation_queue_insert_ (q2, op1);
  GNUNET_TESTBED_operation_begin_wait_ (op1);
  GNUNET_TESTBED_operation_queue_insert_ (q1, op2);
  GNUNET_TESTBED_operation_queue_insert_ (q2, op2);
  GNUNET_TESTBED_operation_begin_wait_ (op2);
  result = TEST_INIT;
}
/**
 * Destroy the given peer; the peer should have been
 * stopped first (if it was started).
 *
 * @param peer peer to stop
 * @return handle to the operation
 */
struct GNUNET_TESTBED_Operation *
GNUNET_TESTBED_peer_destroy (struct GNUNET_TESTBED_Peer *peer)
{
  struct OperationContext *opc;

  opc = GNUNET_malloc (sizeof (struct OperationContext));
  opc->data = peer;
  opc->c = peer->controller;
  opc->id = GNUNET_TESTBED_get_next_op_id (peer->controller);
  opc->type = OP_PEER_DESTROY;
  opc->op =
      GNUNET_TESTBED_operation_create_ (opc, &opstart_peer_destroy,
                                        &oprelease_peer_destroy);
  GNUNET_TESTBED_operation_queue_insert_ (opc->c->opq_parallel_operations,
                                          opc->op);
  GNUNET_TESTBED_operation_begin_wait_ (opc->op);
  return opc->op;
}
/**
 * Create the given peer at the specified host using the given
 * controller.  If the given controller is not running on the target
 * host, it should find or create a controller at the target host and
 * delegate creating the peer.  Explicit delegation paths can be setup
 * using 'GNUNET_TESTBED_controller_link'.  If no explicit delegation
 * path exists, a direct link with a subordinate controller is setup
 * for the first delegated peer to a particular host; the subordinate
 * controller is then destroyed once the last peer that was delegated
 * to the remote host is stopped.  This function is used in particular
 * if some other controller has already assigned a unique ID to the
 * peer.
 *
 * Creating the peer only creates the handle to manipulate and further
 * configure the peer; use "GNUNET_TESTBED_peer_start" and
 * "GNUNET_TESTBED_peer_stop" to actually start/stop the peer's
 * processes.
 *
 * Note that the given configuration will be adjusted by the
 * controller to avoid port/path conflicts with other peers.
 * The "final" configuration can be obtained using
 * 'GNUNET_TESTBED_peer_get_information'.
 *
 * @param unique_id unique ID for this peer
 * @param controller controller process to use
 * @param host host to run the peer on
 * @param cfg Template configuration to use for the peer. Should exist until
 *          operation is cancelled or GNUNET_TESTBED_operation_done() is called
 * @param cb the callback to call when the peer has been created
 * @param cls the closure to the above callback
 * @return the operation handle
 */
struct GNUNET_TESTBED_Operation *
GNUNET_TESTBED_peer_create_with_id_ (uint32_t unique_id,
                                     struct GNUNET_TESTBED_Controller
                                     *controller,
                                     struct GNUNET_TESTBED_Host *host,
                                     const struct GNUNET_CONFIGURATION_Handle
                                     *cfg, GNUNET_TESTBED_PeerCreateCallback cb,
                                     void *cls)
{
  struct GNUNET_TESTBED_Peer *peer;
  struct PeerCreateData *data;
  struct OperationContext *opc;

  peer = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Peer));
  peer->controller = controller;
  peer->host = host;
  peer->unique_id = unique_id;
  peer->state = PS_INVALID;
  data = GNUNET_malloc (sizeof (struct PeerCreateData));
  data->host = host;
  data->cfg = cfg;
  data->cb = cb;
  data->cls = cls;
  data->peer = peer;
  opc = GNUNET_malloc (sizeof (struct OperationContext));
  opc->c = controller;
  opc->data = data;
  opc->id = GNUNET_TESTBED_get_next_op_id (controller);
  opc->type = OP_PEER_CREATE;
  opc->op =
      GNUNET_TESTBED_operation_create_ (opc, &opstart_peer_create,
                                        &oprelease_peer_create);
  GNUNET_TESTBED_operation_queue_insert_ (controller->opq_parallel_operations,
                                          opc->op);
  GNUNET_TESTBED_operation_begin_wait_ (opc->op);
  return opc->op;
}
/**
 * Function to cancel an operation (release all associated resources).  This can
 * be because of a call to "GNUNET_TESTBED_operation_cancel" (before the
 * operation generated an event) or AFTER the operation generated an event due
 * to a call to "GNUNET_TESTBED_operation_done".  Thus it is not guaranteed that
 * a callback to the 'OperationStart' preceeds the call to 'OperationRelease'.
 * Implementations of this function are expected to clean up whatever state is
 * in 'cls' and release all resources associated with the operation.
 */
static void
release_cb (void *cls)
{
  switch (result)
  {
  case TEST_OP1_STARTED:
    GNUNET_assert (&op1 == cls);
    result = TEST_OP1_RELEASED;
    op1 = NULL;
    step_task =
        GNUNET_SCHEDULER_add_delayed (STEP_DELAY, &step, NULL);
    break;
  case TEST_OP2_STARTED:
    GNUNET_assert (&op2 == cls);
    result = TEST_OP2_RELEASED;
    GNUNET_assert (NULL == step_task);
    break;
  case TEST_OP3_STARTED:
    GNUNET_assert (&op3 == cls);
    result = TEST_OP3_RELEASED;
    GNUNET_assert (NULL == step_task);
    break;
  case TEST_OP4_STARTED:
    GNUNET_assert (&op4 == cls);
    result = TEST_OP4_RELEASED;
    GNUNET_assert (NULL == step_task);
    op5 = GNUNET_TESTBED_operation_create_ (&op5, &start_cb, &release_cb);
    GNUNET_TESTBED_operation_queue_insert2_ (q1, op5, 1);
    GNUNET_TESTBED_operation_begin_wait_ (op5);
    op6 = GNUNET_TESTBED_operation_create_ (&op6, &start_cb, &release_cb);
    GNUNET_TESTBED_operation_queue_insert2_ (q2, op6, 1);
    GNUNET_TESTBED_operation_begin_wait_ (op6);
    op7 = GNUNET_TESTBED_operation_create_ (&op7, &start_cb, &release_cb);
    GNUNET_TESTBED_operation_queue_insert2_ (q1, op7, 1);
    GNUNET_TESTBED_operation_queue_insert2_ (q2, op7, 1);
    GNUNET_TESTBED_operation_begin_wait_ (op7);
    break;
  case TEST_OP5_6_7_STARTED:
    result = TEST_OP5_RELEASED;
    op5 = NULL;
    GNUNET_TESTBED_operation_release_ (op6);
    break;
  case TEST_OP5_RELEASED:
    op6 = NULL;
    result = TEST_OP6_RELEASED;
    GNUNET_TESTBED_operation_inactivate_ (op7);
    step_task = GNUNET_SCHEDULER_add_now (&step, NULL);
    break;
  case TEST_OP8_WAITING:
    GNUNET_assert (&op7 == cls);
    op7 = NULL;
    result = TEST_OP7_RELEASED;
    break;
  case TEST_OP8_ACTIVE:
    result = TEST_OP8_RELEASED;
    op8 = NULL;
    break;
  case TEST_OP9_STARTED:
    GNUNET_assert (&op9 == cls);
    result = TEST_OP9_RELEASED;
    GNUNET_TESTBED_operation_queue_destroy_ (q1);
    GNUNET_TESTBED_operation_queue_destroy_ (q2);
    q1 = NULL;
    q2 = NULL;
    break;
  default:
    GNUNET_assert (0);
  }
}
/**
 * Task to simulate artificial delay and change the test stage
 *
 * @param cls NULL
 */
static void
step (void *cls)
{
  GNUNET_assert (NULL != step_task);
  step_task = NULL;
  switch (result)
  {
  case TEST_OP1_STARTED:
    GNUNET_TESTBED_operation_release_ (op1);
    GNUNET_TESTBED_operation_queue_reset_max_active_ (q1, 0);
    op3 = GNUNET_TESTBED_operation_create_ (&op3, &start_cb, &release_cb);
    GNUNET_TESTBED_operation_queue_insert2_ (q1, op3, 2);
    GNUNET_TESTBED_operation_queue_insert2_ (q2, op3, 2);
    GNUNET_TESTBED_operation_begin_wait_ (op3);
    op4 = GNUNET_TESTBED_operation_create_ (&op4, &start_cb, &release_cb);
    GNUNET_TESTBED_operation_queue_insert2_ (q1, op4, 2);
    GNUNET_TESTBED_operation_queue_insert2_ (q2, op4, 2);
    GNUNET_TESTBED_operation_begin_wait_ (op4);
    break;
  case TEST_OP1_RELEASED:
    result = TEST_PAUSE;
    GNUNET_TESTBED_operation_queue_reset_max_active_ (q1, 2);
    break;
  case TEST_OP2_STARTED:
    GNUNET_TESTBED_operation_release_ (op2);
    break;
  case TEST_OP3_STARTED:
    GNUNET_TESTBED_operation_release_ (op3);
    break;
  case TEST_OP4_STARTED:
    GNUNET_TESTBED_operation_release_ (op4);
    break;
  case TEST_OP6_RELEASED:
    op8 = GNUNET_TESTBED_operation_create_ (&op8, &start_cb, &release_cb);
    GNUNET_TESTBED_operation_queue_insert2_ (q1, op8, 2);
    GNUNET_TESTBED_operation_queue_insert2_ (q2, op8, 2);
    result = TEST_OP8_WAITING;
    GNUNET_TESTBED_operation_begin_wait_ (op8);
    break;
  case TEST_OP8_STARTED:
    GNUNET_TESTBED_operation_inactivate_ (op8);
    result = TEST_OP8_INACTIVE_1;
    step_task = GNUNET_SCHEDULER_add_delayed (STEP_DELAY, &step, NULL);
    break;
  case TEST_OP8_INACTIVE_1:
    GNUNET_TESTBED_operation_activate_ (op8);
    result = TEST_OP8_ACTIVE;
    op9 = GNUNET_TESTBED_operation_create_ (&op9, &start_cb, &release_cb);
    GNUNET_TESTBED_operation_queue_insert2_ (q1, op9, 1);
    GNUNET_TESTBED_operation_queue_insert2_ (q2, op9, 1);
    GNUNET_TESTBED_operation_begin_wait_ (op9);
    step_task = GNUNET_SCHEDULER_add_delayed (STEP_DELAY, &step, NULL);
    break;
  case TEST_OP8_ACTIVE:
    GNUNET_TESTBED_operation_inactivate_ (op8);
    /* op8 should be released by now due to above call */
    GNUNET_assert (TEST_OP8_RELEASED == result);
    break;
  case TEST_OP9_STARTED:
    GNUNET_TESTBED_operation_release_ (op9);
    break;
  default:
    GNUNET_assert (0);
  }
}