2 This file is part of GNUnet.
3 Copyright (C) 2008--2013 GNUnet e.V.
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
22 * @file testbed/testbed_api_barriers.c
23 * @brief API implementation for testbed barriers
24 * @author Sree Harsha Totakura <sreeharsha@totakura.in>
28 #include "gnunet_testbed_service.h"
29 #include "testbed_api.h"
30 #include "testbed_api_barriers.h"
35 #define LOG(type, ...) \
36 GNUNET_log_from (type, "testbed-api-barriers", __VA_ARGS__);
39 * Debug logging shorthand
41 #define LOG_DEBUG(...) \
42 LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__);
47 struct GNUNET_TESTBED_Barrier
50 * hashcode identifying this barrier in the hashmap
52 struct GNUNET_HashCode key;
55 * The controller handle given while initiliasing this barrier
57 struct GNUNET_TESTBED_Controller *c;
60 * The name of the barrier
65 * The continuation callback to call when we have a status update on this
67 GNUNET_TESTBED_barrier_status_cb cb;
70 * the closure for the above callback
75 * Should the barrier crossed status message be echoed back to the controller?
82 * handle for hashtable of barrier handles
84 static struct GNUNET_CONTAINER_MultiHashMap *barrier_map;
88 * Remove a barrier and it was the last one in the barrier hash map, destroy the
91 * @param barrier the barrier to remove
94 barrier_remove (struct GNUNET_TESTBED_Barrier *barrier)
96 GNUNET_assert (NULL != barrier_map); /* No barriers present */
97 GNUNET_assert (GNUNET_OK ==
98 GNUNET_CONTAINER_multihashmap_remove (barrier_map,
101 GNUNET_free (barrier->name);
102 GNUNET_free (barrier);
103 if (0 == GNUNET_CONTAINER_multihashmap_size (barrier_map))
105 GNUNET_CONTAINER_multihashmap_destroy (barrier_map);
112 * Validate #GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_STATUS message.
114 * @param cls the controller handle to determine the connection this message
116 * @param msg the barrier status message
117 * @return #GNUNET_OK if the message is valid; #GNUNET_SYSERR to tear it
118 * down signalling an error (message malformed)
121 check_barrier_status_ (struct GNUNET_TESTBED_Controller *c,
122 const struct GNUNET_TESTBED_BarrierStatusMsg *msg)
130 msize = ntohs (msg->header.size);
132 name_len = ntohs (msg->name_len);
134 if (sizeof (struct GNUNET_TESTBED_BarrierStatusMsg) + name_len + 1 > msize)
137 return GNUNET_SYSERR;
139 if ('\0' != name[name_len])
142 return GNUNET_SYSERR;
144 status = ntohs (msg->status);
145 if (GNUNET_TESTBED_BARRIERSTATUS_ERROR == status)
147 emsg_len = msize - (sizeof (struct GNUNET_TESTBED_BarrierStatusMsg) + name_len
152 return GNUNET_SYSERR;
160 * Handler for #GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_STATUS messages
162 * @param c the controller handle to determine the connection this message
164 * @param msg the barrier status message
167 handle_barrier_status_ (struct GNUNET_TESTBED_Controller *c,
168 const struct GNUNET_TESTBED_BarrierStatusMsg *msg)
170 struct GNUNET_TESTBED_Barrier *barrier;
173 struct GNUNET_HashCode key;
181 msize = ntohs (msg->header.size);
183 name_len = ntohs (msg->name_len);
184 LOG_DEBUG ("Received BARRIER_STATUS msg\n");
185 status = ntohs (msg->status);
186 if (GNUNET_TESTBED_BARRIERSTATUS_ERROR == status)
189 emsg_len = msize - (sizeof (struct GNUNET_TESTBED_BarrierStatusMsg) + name_len
191 emsg = GNUNET_malloc (emsg_len + 1);
193 msg->data + name_len + 1,
196 if (NULL == barrier_map)
201 GNUNET_CRYPTO_hash (name, name_len, &key);
202 barrier = GNUNET_CONTAINER_multihashmap_get (barrier_map, &key);
208 GNUNET_assert (NULL != barrier->cb);
209 if ((GNUNET_YES == barrier->echo) && (GNUNET_TESTBED_BARRIERSTATUS_CROSSED == status))
210 GNUNET_TESTBED_queue_message_ (c, GNUNET_copy_message (&msg->header));
211 barrier->cb (barrier->cls, name, barrier, status, emsg);
212 if (GNUNET_TESTBED_BARRIERSTATUS_INITIALISED == status)
213 return; /* just initialised; skip cleanup */
216 GNUNET_free_non_null (emsg);
218 barrier_remove (barrier);
223 * Initialise a barrier and call the given callback when the required percentage
224 * of peers (quorum) reach the barrier OR upon error.
226 * @param controller the handle to the controller
227 * @param name identification name of the barrier
228 * @param quorum the percentage of peers that is required to reach the barrier.
229 * Peers signal reaching a barrier by calling
230 * GNUNET_TESTBED_barrier_reached().
231 * @param cb the callback to call when the barrier is reached or upon error.
233 * @param cls closure for the above callback
234 * @param echo GNUNET_YES to echo the barrier crossed status message back to the
236 * @return barrier handle; NULL upon error
238 struct GNUNET_TESTBED_Barrier *
239 GNUNET_TESTBED_barrier_init_ (struct GNUNET_TESTBED_Controller *controller,
242 GNUNET_TESTBED_barrier_status_cb cb, void *cls,
245 struct GNUNET_TESTBED_BarrierInit *msg;
246 struct GNUNET_TESTBED_Barrier *barrier;
247 struct GNUNET_HashCode key;
251 GNUNET_assert (quorum <= 100);
252 GNUNET_assert (NULL != cb);
253 name_len = strlen (name);
254 GNUNET_assert (0 < name_len);
255 GNUNET_CRYPTO_hash (name, name_len, &key);
256 if (NULL == barrier_map)
257 barrier_map = GNUNET_CONTAINER_multihashmap_create (3, GNUNET_YES);
259 GNUNET_CONTAINER_multihashmap_contains (barrier_map, &key))
264 LOG_DEBUG ("Initialising barrier `%s'\n", name);
265 barrier = GNUNET_new (struct GNUNET_TESTBED_Barrier);
266 barrier->c = controller;
267 barrier->name = GNUNET_strdup (name);
270 barrier->echo = echo;
271 (void) memcpy (&barrier->key, &key, sizeof (struct GNUNET_HashCode));
272 GNUNET_assert (GNUNET_OK ==
273 GNUNET_CONTAINER_multihashmap_put (barrier_map, &barrier->key,
275 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
276 msize = name_len + sizeof (struct GNUNET_TESTBED_BarrierInit);
277 msg = GNUNET_malloc (msize);
278 msg->header.size = htons (msize);
279 msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_INIT);
280 msg->quorum = (uint8_t) quorum;
281 (void) memcpy (msg->name, barrier->name, name_len);
282 GNUNET_TESTBED_queue_message_ (barrier->c, &msg->header);
288 * Initialise a barrier and call the given callback when the required percentage
289 * of peers (quorum) reach the barrier OR upon error.
291 * @param controller the handle to the controller
292 * @param name identification name of the barrier
293 * @param quorum the percentage of peers that is required to reach the barrier.
294 * Peers signal reaching a barrier by calling
295 * GNUNET_TESTBED_barrier_reached().
296 * @param cb the callback to call when the barrier is reached or upon error.
298 * @param cls closure for the above callback
299 * @return barrier handle; NULL upon error
301 struct GNUNET_TESTBED_Barrier *
302 GNUNET_TESTBED_barrier_init (struct GNUNET_TESTBED_Controller *controller,
305 GNUNET_TESTBED_barrier_status_cb cb, void *cls)
307 return GNUNET_TESTBED_barrier_init_ (controller, name, quorum, cb, cls, GNUNET_YES);
314 * @param barrier the barrier handle
317 GNUNET_TESTBED_barrier_cancel (struct GNUNET_TESTBED_Barrier *barrier)
319 struct GNUNET_TESTBED_BarrierCancel *msg;
322 msize = sizeof (struct GNUNET_TESTBED_BarrierCancel) + strlen (barrier->name);
323 msg = GNUNET_malloc (msize);
324 msg->header.size = htons (msize);
325 msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_CANCEL);
326 (void) memcpy (msg->name, barrier->name, strlen (barrier->name));
327 GNUNET_TESTBED_queue_message_ (barrier->c, &msg->header);
328 barrier_remove (barrier);
333 * Barrier wait handle
335 struct GNUNET_TESTBED_BarrierWaitHandle
338 * The name of the barrier
343 * Then configuration used for the client connection
345 struct GNUNET_CONFIGURATION_Handle *cfg;
348 * The client connection
350 struct GNUNET_CLIENT_Connection *conn;
355 struct GNUNET_CLIENT_TransmitHandle *tx;
358 * The message to transmit with tx
360 struct GNUNET_MessageHeader *msg;
363 * The barrier wait callback
365 GNUNET_TESTBED_barrier_wait_cb cb;
368 * The closure for the above callback
375 * Function to destroy barrier wait handle
377 * @param h the handle to destroy
380 destroy_handle (struct GNUNET_TESTBED_BarrierWaitHandle *h)
382 GNUNET_free (h->name);
384 GNUNET_CLIENT_notify_transmit_ready_cancel (h->tx);
386 GNUNET_CLIENT_disconnect (h->conn);
388 GNUNET_free (h->msg);
389 GNUNET_CONFIGURATION_destroy (h->cfg);
395 * Type of a function to call when we receive a message
399 * @param message received message; NULL on timeout or fatal error
402 receive_handler (void *cls, const struct GNUNET_MessageHeader *message)
404 struct GNUNET_TESTBED_BarrierWaitHandle *h = cls;
405 const struct GNUNET_TESTBED_BarrierStatusMsg *msg;
413 if (GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_STATUS != ntohs (message->type))
418 msize = ntohs (message->size);
419 if (msize <= sizeof (struct GNUNET_TESTBED_BarrierStatusMsg))
424 msg = (const struct GNUNET_TESTBED_BarrierStatusMsg *) message;
425 switch (ntohs (msg->status))
427 case GNUNET_TESTBED_BARRIERSTATUS_ERROR:
429 case GNUNET_TESTBED_BARRIERSTATUS_INITIALISED:
430 GNUNET_break (0); /* FIXME */
432 case GNUNET_TESTBED_BARRIERSTATUS_CROSSED:
433 h->cb (h->cls, h->name, GNUNET_OK);
440 h->cb (h->cls, h->name, GNUNET_SYSERR);
448 * Function called to notify a client about the connection
449 * begin ready to queue more data. "buf" will be
450 * NULL and "size" zero if the connection was closed for
451 * writing in the meantime.
454 * @param size number of bytes available in buf
455 * @param buf where the callee should write the message
456 * @return number of bytes written to buf
459 transmit_notify (void *cls, size_t size, void *buf)
461 struct GNUNET_TESTBED_BarrierWaitHandle *h = cls;
465 if ((0 == size) || (NULL == buf))
470 msize = htons (h->msg->size);
471 GNUNET_assert (msize <= size);
472 (void) memcpy (buf, h->msg, msize);
473 GNUNET_free (h->msg);
475 GNUNET_CLIENT_receive (h->conn, &receive_handler, h, GNUNET_TIME_UNIT_FOREVER_REL);
481 * Wait for a barrier to be crossed. This function should be called by the
482 * peers which have been started by the testbed. If the peer is not started by
483 * testbed this function may return error
485 * @param name the name of the barrier
486 * @param cb the barrier wait callback
487 * @param cls the closure for the above callback
488 * @return barrier wait handle which can be used to cancel the waiting at
489 * anytime before the callback is called. NULL upon error.
491 struct GNUNET_TESTBED_BarrierWaitHandle *
492 GNUNET_TESTBED_barrier_wait (const char *name,
493 GNUNET_TESTBED_barrier_wait_cb cb,
496 struct GNUNET_TESTBED_BarrierWait *msg;
497 struct GNUNET_CONFIGURATION_Handle *cfg;
498 struct GNUNET_TESTBED_BarrierWaitHandle *h;
503 GNUNET_assert (NULL != cb);
504 GNUNET_assert (NULL != name);
505 cfg_filename = getenv (ENV_TESTBED_CONFIG);
506 if (NULL == cfg_filename)
508 LOG (GNUNET_ERROR_TYPE_ERROR, "Are you running under testbed?\n");
511 cfg = GNUNET_CONFIGURATION_create ();
512 if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, cfg_filename))
514 LOG (GNUNET_ERROR_TYPE_ERROR, "Unable to load configuration from file `%s'\n",
516 GNUNET_CONFIGURATION_destroy (cfg);
519 h = GNUNET_new (struct GNUNET_TESTBED_BarrierWaitHandle);
520 h->name = GNUNET_strdup (name);
522 h->conn = GNUNET_CLIENT_connect ("testbed-barrier", h->cfg);
527 LOG (GNUNET_ERROR_TYPE_ERROR, "Unable to connect to local testbed-barrier service\n");
531 name_len = strlen (name);
532 msize = sizeof (struct GNUNET_TESTBED_BarrierWait) + name_len;
533 msg = GNUNET_malloc (msize);
534 msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_WAIT);
535 msg->header.size = htons (msize);
536 (void) memcpy (msg->name, name, name_len);
537 h->msg = &msg->header;
539 GNUNET_CLIENT_notify_transmit_ready (h->conn, msize,
540 GNUNET_TIME_UNIT_FOREVER_REL,
549 * Cancel a barrier wait handle
551 * @param h the barrier wait handle
554 GNUNET_TESTBED_barrier_wait_cancel (struct GNUNET_TESTBED_BarrierWaitHandle *h)
559 /* end of testbed_api_barriers.c */