/* 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 */