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) &&
210 (GNUNET_TESTBED_BARRIERSTATUS_CROSSED == status))
211 GNUNET_TESTBED_queue_message_ (c, GNUNET_copy_message (&msg->header));
212 barrier->cb (barrier->cls, name, barrier, status, emsg);
213 if (GNUNET_TESTBED_BARRIERSTATUS_INITIALISED == status)
214 return; /* just initialised; skip cleanup */
217 GNUNET_free_non_null (emsg);
219 barrier_remove (barrier);
224 * Initialise a barrier and call the given callback when the required percentage
225 * of peers (quorum) reach the barrier OR upon error.
227 * @param controller the handle to the controller
228 * @param name identification name of the barrier
229 * @param quorum the percentage of peers that is required to reach the barrier.
230 * Peers signal reaching a barrier by calling
231 * GNUNET_TESTBED_barrier_reached().
232 * @param cb the callback to call when the barrier is reached or upon error.
234 * @param cls closure for the above callback
235 * @param echo GNUNET_YES to echo the barrier crossed status message back to the
237 * @return barrier handle; NULL upon error
239 struct GNUNET_TESTBED_Barrier *
240 GNUNET_TESTBED_barrier_init_ (struct GNUNET_TESTBED_Controller *controller,
243 GNUNET_TESTBED_barrier_status_cb cb, void *cls,
246 struct GNUNET_TESTBED_BarrierInit *msg;
247 struct GNUNET_TESTBED_Barrier *barrier;
248 struct GNUNET_HashCode key;
252 GNUNET_assert (quorum <= 100);
253 GNUNET_assert (NULL != cb);
254 name_len = strlen (name);
255 GNUNET_assert (0 < name_len);
256 GNUNET_CRYPTO_hash (name, name_len, &key);
257 if (NULL == barrier_map)
258 barrier_map = GNUNET_CONTAINER_multihashmap_create (3, GNUNET_YES);
260 GNUNET_CONTAINER_multihashmap_contains (barrier_map, &key))
265 LOG_DEBUG ("Initialising barrier `%s'\n", name);
266 barrier = GNUNET_new (struct GNUNET_TESTBED_Barrier);
267 barrier->c = controller;
268 barrier->name = GNUNET_strdup (name);
271 barrier->echo = echo;
272 (void) memcpy (&barrier->key, &key, sizeof (struct GNUNET_HashCode));
273 GNUNET_assert (GNUNET_OK ==
274 GNUNET_CONTAINER_multihashmap_put (barrier_map,
277 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
278 msize = name_len + sizeof (struct GNUNET_TESTBED_BarrierInit);
279 msg = GNUNET_malloc (msize);
280 msg->header.size = htons (msize);
281 msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_INIT);
282 msg->quorum = (uint8_t) quorum;
283 (void) memcpy (msg->name, barrier->name, name_len);
284 GNUNET_TESTBED_queue_message_ (barrier->c, &msg->header);
290 * Initialise a barrier and call the given callback when the required percentage
291 * of peers (quorum) reach the barrier OR upon error.
293 * @param controller the handle to the controller
294 * @param name identification name of the barrier
295 * @param quorum the percentage of peers that is required to reach the barrier.
296 * Peers signal reaching a barrier by calling
297 * GNUNET_TESTBED_barrier_reached().
298 * @param cb the callback to call when the barrier is reached or upon error.
300 * @param cls closure for the above callback
301 * @return barrier handle; NULL upon error
303 struct GNUNET_TESTBED_Barrier *
304 GNUNET_TESTBED_barrier_init (struct GNUNET_TESTBED_Controller *controller,
307 GNUNET_TESTBED_barrier_status_cb cb, void *cls)
309 return GNUNET_TESTBED_barrier_init_ (controller,
310 name, quorum, cb, cls, GNUNET_YES);
317 * @param barrier the barrier handle
320 GNUNET_TESTBED_barrier_cancel (struct GNUNET_TESTBED_Barrier *barrier)
322 struct GNUNET_TESTBED_BarrierCancel *msg;
325 msize = sizeof (struct GNUNET_TESTBED_BarrierCancel) + strlen (barrier->name);
326 msg = GNUNET_malloc (msize);
327 msg->header.size = htons (msize);
328 msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_CANCEL);
329 (void) memcpy (msg->name, barrier->name, strlen (barrier->name));
330 GNUNET_TESTBED_queue_message_ (barrier->c, &msg->header);
331 barrier_remove (barrier);
336 * Barrier wait handle
338 struct GNUNET_TESTBED_BarrierWaitHandle
341 * The name of the barrier
346 * Then configuration used for the client connection
348 struct GNUNET_CONFIGURATION_Handle *cfg;
351 * The client connection
353 struct GNUNET_CLIENT_Connection *conn;
358 struct GNUNET_CLIENT_TransmitHandle *tx;
361 * The message to transmit with tx
363 struct GNUNET_MessageHeader *msg;
366 * The barrier wait callback
368 GNUNET_TESTBED_barrier_wait_cb cb;
371 * The closure for the above callback
378 * Function to destroy barrier wait handle
380 * @param h the handle to destroy
383 destroy_handle (struct GNUNET_TESTBED_BarrierWaitHandle *h)
385 GNUNET_free (h->name);
387 GNUNET_CLIENT_notify_transmit_ready_cancel (h->tx);
389 GNUNET_CLIENT_disconnect (h->conn);
391 GNUNET_free (h->msg);
392 GNUNET_CONFIGURATION_destroy (h->cfg);
398 * Type of a function to call when we receive a message
402 * @param message received message; NULL on timeout or fatal error
405 receive_handler (void *cls,
406 const struct GNUNET_MessageHeader *message)
408 struct GNUNET_TESTBED_BarrierWaitHandle *h = cls;
409 const struct GNUNET_TESTBED_BarrierStatusMsg *msg;
417 if (GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_STATUS != ntohs (message->type))
422 msize = ntohs (message->size);
423 if (msize <= sizeof (struct GNUNET_TESTBED_BarrierStatusMsg))
428 msg = (const struct GNUNET_TESTBED_BarrierStatusMsg *) message;
429 switch (ntohs (msg->status))
431 case GNUNET_TESTBED_BARRIERSTATUS_ERROR:
433 case GNUNET_TESTBED_BARRIERSTATUS_INITIALISED:
434 GNUNET_break (0); /* FIXME */
436 case GNUNET_TESTBED_BARRIERSTATUS_CROSSED:
437 h->cb (h->cls, h->name, GNUNET_OK);
444 h->cb (h->cls, h->name, GNUNET_SYSERR);
452 * Function called to notify a client about the connection
453 * begin ready to queue more data. "buf" will be
454 * NULL and "size" zero if the connection was closed for
455 * writing in the meantime.
458 * @param size number of bytes available in buf
459 * @param buf where the callee should write the message
460 * @return number of bytes written to buf
463 transmit_notify (void *cls, size_t size, void *buf)
465 struct GNUNET_TESTBED_BarrierWaitHandle *h = cls;
469 if ((0 == size) || (NULL == buf))
474 msize = htons (h->msg->size);
475 GNUNET_assert (msize <= size);
476 (void) memcpy (buf, h->msg, msize);
477 GNUNET_free (h->msg);
479 GNUNET_CLIENT_receive (h->conn, &receive_handler, h, GNUNET_TIME_UNIT_FOREVER_REL);
485 * Wait for a barrier to be crossed. This function should be called by the
486 * peers which have been started by the testbed. If the peer is not started by
487 * testbed this function may return error
489 * @param name the name of the barrier
490 * @param cb the barrier wait callback
491 * @param cls the closure for the above callback
492 * @return barrier wait handle which can be used to cancel the waiting at
493 * anytime before the callback is called. NULL upon error.
495 struct GNUNET_TESTBED_BarrierWaitHandle *
496 GNUNET_TESTBED_barrier_wait (const char *name,
497 GNUNET_TESTBED_barrier_wait_cb cb,
500 struct GNUNET_TESTBED_BarrierWait *msg;
501 struct GNUNET_CONFIGURATION_Handle *cfg;
502 struct GNUNET_TESTBED_BarrierWaitHandle *h;
507 GNUNET_assert (NULL != cb);
508 GNUNET_assert (NULL != name);
509 cfg_filename = getenv (ENV_TESTBED_CONFIG);
510 if (NULL == cfg_filename)
512 LOG (GNUNET_ERROR_TYPE_ERROR, "Are you running under testbed?\n");
515 cfg = GNUNET_CONFIGURATION_create ();
516 if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, cfg_filename))
518 LOG (GNUNET_ERROR_TYPE_ERROR, "Unable to load configuration from file `%s'\n",
520 GNUNET_CONFIGURATION_destroy (cfg);
523 h = GNUNET_new (struct GNUNET_TESTBED_BarrierWaitHandle);
524 h->name = GNUNET_strdup (name);
526 h->conn = GNUNET_CLIENT_connect ("testbed-barrier", h->cfg);
531 LOG (GNUNET_ERROR_TYPE_ERROR,
532 "Unable to connect to local testbed-barrier service\n");
536 name_len = strlen (name);
537 msize = sizeof (struct GNUNET_TESTBED_BarrierWait) + name_len;
538 msg = GNUNET_malloc (msize);
539 msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_WAIT);
540 msg->header.size = htons (msize);
541 (void) memcpy (msg->name, name, name_len);
542 h->msg = &msg->header;
544 GNUNET_CLIENT_notify_transmit_ready (h->conn, msize,
545 GNUNET_TIME_UNIT_FOREVER_REL,
554 * Cancel a barrier wait handle
556 * @param h the barrier wait handle
559 GNUNET_TESTBED_barrier_wait_cancel (struct GNUNET_TESTBED_BarrierWaitHandle *h)
564 /* end of testbed_api_barriers.c */