/*
This file is part of GNUnet.
Copyright (C) 2008--2013, 2016 GNUnet e.V.
GNUnet is free software: you can redistribute it and/or modify it
under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License,
or (at your option) any later version.
GNUnet is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
SPDX-License-Identifier: AGPL3.0-or-later
*/
/**
* @file testbed/testbed_api_barriers.c
* @brief API implementation for testbed barriers
* @author Sree Harsha Totakura
*/
#include "platform.h"
#include "gnunet_testbed_service.h"
#include "testbed_api.h"
/**
* Logging shorthand
*/
#define LOG(type, ...) \
GNUNET_log_from (type, "testbed-api-barriers", __VA_ARGS__);
/**
* Debug logging shorthand
*/
#define LOG_DEBUG(...) \
LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__);
/**
* Barrier wait handle
*/
struct GNUNET_TESTBED_BarrierWaitHandle
{
/**
* The name of the barrier
*/
char *name;
/**
* Then configuration used for the client connection
*/
struct GNUNET_CONFIGURATION_Handle *cfg;
/**
* The testbed-barrier service message queue.
*/
struct GNUNET_MQ_Handle *mq;
/**
* The barrier wait callback
*/
GNUNET_TESTBED_barrier_wait_cb cb;
/**
* The closure for @e cb.
*/
void *cb_cls;
};
/**
* Check if barrier status message is well-formed.
*
* @param cls closure
* @param msg received message
* @return #GNUNET_OK if the message is well-formed.
*/
static int
check_status (void *cls,
const struct GNUNET_TESTBED_BarrierStatusMsg *msg)
{
/* FIXME: this fails to actually check that the message
follows the protocol spec (0-terminations!). However,
not critical as #handle_status() doesn't interpret the
variable-size part anyway right now. */
return GNUNET_OK;
}
/**
* Type of a function to call when we receive a message
* from the service.
*
* @param cls closure
* @param msg received message
*/
static void
handle_status (void *cls,
const struct GNUNET_TESTBED_BarrierStatusMsg *msg)
{
struct GNUNET_TESTBED_BarrierWaitHandle *h = cls;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Got barrier status %d\n",
(int) ntohs (msg->status));
switch (ntohs (msg->status))
{
case GNUNET_TESTBED_BARRIERSTATUS_ERROR:
h->cb (h->cb_cls,
h->name,
GNUNET_SYSERR);
break;
case GNUNET_TESTBED_BARRIERSTATUS_INITIALISED:
h->cb (h->cb_cls,
h->name,
GNUNET_SYSERR);
GNUNET_break (0);
break;
case GNUNET_TESTBED_BARRIERSTATUS_CROSSED:
h->cb (h->cb_cls,
h->name,
GNUNET_OK);
break;
default:
GNUNET_break_op (0);
h->cb (h->cb_cls,
h->name,
GNUNET_SYSERR);
break;
}
GNUNET_TESTBED_barrier_wait_cancel (h);
}
/**
* Generic error handler, called with the appropriate error code and
* the same closure specified at the creation of the message queue.
* Not every message queue implementation supports an error handler.
*
* @param cls closure with the `struct GNUNET_TESTBED_BarrierWaitHandle *`
* @param error error code
*/
static void
mq_error_handler (void *cls,
enum GNUNET_MQ_Error error)
{
struct GNUNET_TESTBED_BarrierWaitHandle *h = cls;
h->cb (h->cb_cls,
h->name,
GNUNET_SYSERR);
GNUNET_TESTBED_barrier_wait_cancel (h);
}
/**
* Wait for a barrier to be crossed. This function should be called by the
* peers which have been started by the testbed. If the peer is not started by
* testbed this function may return error
*
* @param name the name of the barrier
* @param cb the barrier wait callback
* @param cb_cls the closure for @a cb
* @return barrier wait handle which can be used to cancel the waiting at
* anytime before the callback is called. NULL upon error.
*/
struct GNUNET_TESTBED_BarrierWaitHandle *
GNUNET_TESTBED_barrier_wait (const char *name,
GNUNET_TESTBED_barrier_wait_cb cb,
void *cb_cls)
{
struct GNUNET_TESTBED_BarrierWaitHandle *h
= GNUNET_new (struct GNUNET_TESTBED_BarrierWaitHandle);
struct GNUNET_MQ_MessageHandler handlers[] = {
GNUNET_MQ_hd_var_size (status,
GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_STATUS,
struct GNUNET_TESTBED_BarrierStatusMsg,
h),
GNUNET_MQ_handler_end ()
};
struct GNUNET_MQ_Envelope *env;
struct GNUNET_TESTBED_BarrierWait *msg;
const char *cfg_filename;
size_t name_len;
GNUNET_assert (NULL != cb);
cfg_filename = getenv (ENV_TESTBED_CONFIG);
if (NULL == cfg_filename)
{
LOG (GNUNET_ERROR_TYPE_ERROR,
"Are you running under testbed?\n");
GNUNET_free (h);
return NULL;
}
h->cfg = GNUNET_CONFIGURATION_create ();
if (GNUNET_OK !=
GNUNET_CONFIGURATION_load (h->cfg,
cfg_filename))
{
LOG (GNUNET_ERROR_TYPE_ERROR,
"Unable to load configuration from file `%s'\n",
cfg_filename);
GNUNET_CONFIGURATION_destroy (h->cfg);
GNUNET_free (h);
return NULL;
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Waiting on barrier `%s'\n",
name);
h->name = GNUNET_strdup (name);
h->cb = cb;
h->cb_cls = cb_cls;
h->mq = GNUNET_CLIENT_connect (h->cfg,
"testbed-barrier",
handlers,
&mq_error_handler,
h);
if (NULL == h->mq)
{
LOG (GNUNET_ERROR_TYPE_ERROR,
"Unable to connect to local testbed-barrier service\n");
GNUNET_TESTBED_barrier_wait_cancel (h);
return NULL;
}
name_len = strlen (name); /* NOTE: unusual to not have 0-termination, change? */
env = GNUNET_MQ_msg_extra (msg,
name_len,
GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_WAIT);
GNUNET_memcpy (msg->name,
name,
name_len);
GNUNET_MQ_send (h->mq,
env);
return h;
}
/**
* Cancel a barrier wait handle
*
* @param h the barrier wait handle
*/
void
GNUNET_TESTBED_barrier_wait_cancel (struct GNUNET_TESTBED_BarrierWaitHandle *h)
{
if (NULL != h->mq)
{
GNUNET_MQ_destroy (h->mq);
h->mq = NULL;
}
GNUNET_free (h->name);
GNUNET_CONFIGURATION_destroy (h->cfg);
GNUNET_free (h);
}
/* end of testbed_api_barriers.c */