- doxygen
[oweals/gnunet.git] / src / testbed / testbed_api_barriers.c
1 /*
2   This file is part of GNUnet.
3   (C) 2008--2013 Christian Grothoff (and other contributing authors)
4
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.
9
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.
14
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., 59 Temple Place - Suite 330,
18   Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file testbed/testbed_api_barriers.c
23  * @brief API implementation for testbed barriers
24  * @author Sree Harsha Totakura <sreeharsha@totakura.in> 
25  */
26
27 #include "platform.h"
28 #include "gnunet_testbed_service.h"
29 #include "testbed_api.h"
30
31 /**
32  * Handle for barrier
33  */
34 struct GNUNET_TESTBED_Barrier
35 {
36   /**
37    * hashcode identifying this barrier in the hashmap
38    */
39   struct GNUNET_HashCode key;
40
41   /**
42    * The controller handle given while initiliasing this barrier
43    */
44   struct GNUNET_TESTBED_Controller *c;
45   
46   /**
47    * The name of the barrier
48    */
49   char *name;
50
51   /**
52    * The continuation callback to call when we have a status update on this
53    */
54   GNUNET_TESTBED_barrier_status_cb cb;
55
56   /**
57    * the closure for the above callback
58    */
59   void *cls;
60  
61 };
62
63
64 /**
65  * handle for hashtable of barrier handles
66  */
67 static struct GNUNET_CONTAINER_MultiHashMap *barrier_map;
68
69
70 /**
71  * Remove a barrier and it was the last one in the barrier hash map, destroy the
72  * hash map
73  *
74  * @param barrier the barrier to remove
75  */
76 static void
77 barrier_remove (struct GNUNET_TESTBED_Barrier *barrier)
78 {
79   GNUNET_assert (NULL != barrier_map); /* No barriers present */
80   GNUNET_assert (GNUNET_OK ==
81                  GNUNET_CONTAINER_multihashmap_remove (barrier_map,
82                                                        &barrier->key,
83                                                        barrier));
84   GNUNET_free (barrier->name);
85   GNUNET_free (barrier);
86   if (0 == GNUNET_CONTAINER_multihashmap_size (barrier_map))
87   {
88     GNUNET_CONTAINER_multihashmap_destroy (barrier_map);
89     barrier_map = NULL;
90   }
91 }
92
93
94 /**
95  * Handler for GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_STATUS messages
96  *
97  * @param c the controller handle to determine the connection this message
98  *   belongs to
99  * @param msg the barrier status message
100  * @return GNUNET_OK to keep the connection active; GNUNET_SYSERR to tear it
101  *   down signalling an error
102  */
103 int
104 GNUNET_TESTBED_handle_barrier_status_ (struct GNUNET_TESTBED_Controller *c,
105                                        const struct GNUNET_TESTBED_BarrierStatusMsg
106                                        *msg)
107 {
108   struct GNUNET_TESTBED_Barrier *barrier;
109   char *emsg;
110   const char *name;
111   struct GNUNET_HashCode key;  
112   size_t emsg_len;
113   int status;
114   uint16_t msize;
115   uint16_t name_len;
116   
117   emsg = NULL;
118   barrier = NULL;
119   msize = ntohs (msg->header.size);  
120   name = msg->data;
121   name_len = ntohs (msg->name_len);
122   if (  (sizeof (struct GNUNET_TESTBED_BarrierStatusMsg) + name_len + 1 > msize)
123         || ('\0' != name[name_len])  )
124   {
125     GNUNET_break_op (0);
126     return GNUNET_SYSERR;
127   }
128   status = ntohs (msg->status);
129   if (BARRIER_STATUS_ERROR == status)
130   {
131     status = -1;
132     emsg_len = msize - (sizeof (struct GNUNET_TESTBED_BarrierStatusMsg) + name_len
133                         + 1);
134     if (0 == emsg_len)
135     {
136       GNUNET_break_op (0);
137       return GNUNET_SYSERR;
138     }
139     emsg_len++;
140     emsg = GNUNET_malloc (emsg_len);
141     emsg_len--;
142     emsg[emsg_len] = '\0';
143     (void) memcpy (emsg, msg->data + name_len + 1, emsg_len);
144   }
145   if (NULL == barrier_map)
146     goto cleanup;
147   GNUNET_CRYPTO_hash (name, name_len, &key);
148   barrier = GNUNET_CONTAINER_multihashmap_get (barrier_map, &key);
149   if (NULL == barrier)
150     goto cleanup;
151   GNUNET_assert (NULL != barrier->cb);
152   barrier->cb (barrier->cls, name, barrier, status, emsg);
153   if (BARRIER_STATUS_INITIALISED == status)
154     return GNUNET_OK;           /* just initialised; skip cleanup */
155
156  cleanup:
157   GNUNET_free_non_null (emsg);
158   if (NULL != barrier)
159     barrier_remove (barrier);
160   return GNUNET_OK;
161 }
162
163
164 /**
165  * Initialise a barrier and call the given callback when the required percentage
166  * of peers (quorum) reach the barrier OR upon error.
167  *
168  * @param controller the handle to the controller
169  * @param name identification name of the barrier
170  * @param quorum the percentage of peers that is required to reach the barrier.
171  *   Peers signal reaching a barrier by calling
172  *   GNUNET_TESTBED_barrier_reached().
173  * @param cb the callback to call when the barrier is reached or upon error.
174  *   Cannot be NULL.
175  * @param cls closure for the above callback
176  * @return barrier handle; NULL upon error
177  */
178 struct GNUNET_TESTBED_Barrier *
179 GNUNET_TESTBED_barrier_init (struct GNUNET_TESTBED_Controller *controller,
180                              const char *name,
181                              unsigned int quorum,
182                              GNUNET_TESTBED_barrier_status_cb cb, void *cls)
183 {
184   struct GNUNET_TESTBED_BarrierInit *msg;
185   struct GNUNET_TESTBED_Barrier *barrier;
186   struct GNUNET_HashCode key;
187   size_t name_len;
188   uint16_t msize;
189   
190   GNUNET_assert (quorum <= 100);
191   GNUNET_assert (NULL != cb);
192   name_len = strlen (name);
193   GNUNET_assert (0 < name_len);
194   GNUNET_CRYPTO_hash (name, name_len, &key);
195   if (NULL == barrier_map)
196     barrier_map = GNUNET_CONTAINER_multihashmap_create (3, GNUNET_YES);
197   if (GNUNET_YES ==
198       GNUNET_CONTAINER_multihashmap_contains (barrier_map, &key))
199   {
200     GNUNET_break (0);
201     return NULL;
202   }
203   barrier = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Barrier));
204   barrier->c = controller;
205   barrier->name = GNUNET_strdup (name);
206   barrier->cb = cb;
207   barrier->cls = cls;
208   (void) memcpy (&barrier->key, &key, sizeof (struct GNUNET_HashCode));
209   GNUNET_assert (GNUNET_OK ==
210                  GNUNET_CONTAINER_multihashmap_put (barrier_map, &barrier->key,
211                                                     barrier,
212                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
213   msize = name_len + sizeof (struct GNUNET_TESTBED_BarrierInit);
214   msg = GNUNET_malloc (msize);
215   msg->header.size = htons (msize);
216   msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_INIT);
217   msg->quorum = (uint8_t) quorum;
218   (void) memcpy (msg->name, barrier->name, name_len);
219   GNUNET_TESTBED_queue_message_ (barrier->c, &msg->header);
220   return barrier;
221 }
222
223
224 /**
225  * Cancel a barrier.
226  *
227  * @param barrier the barrier handle
228  */
229 void
230 GNUNET_TESTBED_barrier_cancel (struct GNUNET_TESTBED_Barrier *barrier)
231 {
232   struct GNUNET_TESTBED_BarrierCancel *msg;
233   uint16_t msize;
234
235   msize = sizeof (struct GNUNET_TESTBED_BarrierCancel) + strlen (barrier->name);
236   msg = GNUNET_malloc (msize);
237   msg->header.size = htons (msize);
238   msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_CANCEL);
239   (void) memcpy (msg->name, barrier->name, strlen (barrier->name));
240   GNUNET_TESTBED_queue_message_ (barrier->c, &msg->header);
241   barrier_remove (barrier);
242 }
243
244
245 /**
246  * Barrier wait handle
247  */
248 struct GNUNET_TESTBED_BarrierWaitHandle
249 {
250   /**
251    * The name of the barrier
252    */
253   char *name;
254   
255   /**
256    * Then configuration used for the client connection
257    */
258   struct GNUNET_CONFIGURATION_Handle *cfg;  
259
260   /**
261    * The client connection
262    */
263   struct GNUNET_CLIENT_Connection *conn;
264
265   /**
266    * Transmit handle
267    */
268   struct GNUNET_CLIENT_TransmitHandle *tx;
269
270   /**
271    * The message to transmit with tx
272    */
273   struct GNUNET_MessageHeader *msg;
274
275   /**
276    * The barrier wait callback
277    */
278   GNUNET_TESTBED_barrier_wait_cb cb;
279
280   /**
281    * The closure for the above callback
282    */
283   void *cls;
284 };
285
286
287 /**
288  * Function to destroy barrier wait handle
289  *
290  * @param h the handle to destroy
291  */
292 static void
293 destroy_handle (struct GNUNET_TESTBED_BarrierWaitHandle *h)
294 {
295   GNUNET_free (h->name);
296   if (NULL != h->tx)
297     GNUNET_CLIENT_notify_transmit_ready_cancel (h->tx);
298   if (NULL != h->conn)
299     GNUNET_CLIENT_disconnect (h->conn);
300   if (NULL != h->msg)
301     GNUNET_free (h->msg);
302   GNUNET_CONFIGURATION_destroy (h->cfg);
303   GNUNET_free (h);
304 }
305
306
307 /**
308  * Type of a function to call when we receive a message
309  * from the service.
310  *
311  * @param cls closure
312  * @param message received message; NULL on timeout or fatal error
313  */
314 static void
315 receive_handler (void *cls, const struct GNUNET_MessageHeader *message)
316 {
317   struct GNUNET_TESTBED_BarrierWaitHandle *h = cls;
318   const struct GNUNET_TESTBED_BarrierStatusMsg *msg;
319   uint16_t msize;
320   
321   if (GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_STATUS != ntohs (message->type))
322   {
323     GNUNET_break_op (0);
324     goto fail;
325   } 
326   msize = ntohs (message->size);
327   if (msize <= sizeof (struct GNUNET_TESTBED_BarrierStatusMsg))
328   {
329     GNUNET_break_op (0);
330     goto fail;
331   } 
332   msg = (const struct GNUNET_TESTBED_BarrierStatusMsg *) message;
333   switch (ntohs (msg->status))
334   {
335   case BARRIER_STATUS_ERROR:
336     goto fail;
337   case BARRIER_STATUS_INITIALISED:
338     GNUNET_break (0);           /* FIXME */
339     goto destroy;
340   case BARRIER_STATUS_CROSSED:
341     h->cb (h->cls, h->name, GNUNET_OK);
342     goto destroy;
343   }
344
345  fail:
346   h->cb (h->cls, h->name, GNUNET_SYSERR);
347
348  destroy:
349   destroy_handle (h);
350 }
351
352
353 /**
354  * Function called to notify a client about the connection
355  * begin ready to queue more data.  "buf" will be
356  * NULL and "size" zero if the connection was closed for
357  * writing in the meantime.
358  *
359  * @param cls closure
360  * @param size number of bytes available in buf
361  * @param buf where the callee should write the message
362  * @return number of bytes written to buf
363  */
364 static size_t
365 transmit_notify (void *cls, size_t size, void *buf)
366 {
367   struct GNUNET_TESTBED_BarrierWaitHandle *h = cls;
368   uint16_t msize;
369   
370   h->tx = NULL;
371   if ((0 == size) || (NULL == buf))
372   {
373     destroy_handle (h);
374     return 0;
375   }
376   msize = htons (h->msg->size);
377   GNUNET_assert (msize <= size);
378   (void) memcpy (buf, h->msg, msize);
379   GNUNET_free (h->msg);
380   h->msg = NULL;
381   GNUNET_CLIENT_receive (h->conn, &receive_handler, h, GNUNET_TIME_UNIT_FOREVER_REL);
382   return msize;
383 }
384
385
386 /**
387  * Wait for a barrier to be crossed.  This function should be called by the
388  * peers which have been started by the testbed.  If the peer is not started by
389  * testbed this function may return error
390  *
391  * @param name the name of the barrier
392  * @param cb the barrier wait callback
393  * @param cls the closure for the above callback
394  * @return barrier wait handle which can be used to cancel the waiting at
395  *   anytime before the callback is called.  NULL upon error.
396  */
397 struct GNUNET_TESTBED_BarrierWaitHandle *
398 GNUNET_TESTBED_barrier_wait (const char *name,
399                              GNUNET_TESTBED_barrier_wait_cb cb,
400                              void *cls)
401 {
402   struct GNUNET_TESTBED_BarrierWait *msg;
403   struct GNUNET_CONFIGURATION_Handle *cfg;
404   struct GNUNET_TESTBED_BarrierWaitHandle *h;
405   char *cfg_filename;
406   size_t name_len;
407   uint16_t msize;
408
409   GNUNET_assert (NULL != cb);
410   GNUNET_assert (NULL != name);
411   cfg_filename = getenv (ENV_TESTBED_CONFIG);
412   if (NULL == cfg_filename)
413     return NULL;
414   cfg = GNUNET_CONFIGURATION_create ();
415   if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, cfg_filename));
416   {
417     GNUNET_CONFIGURATION_destroy (cfg);
418     return NULL;
419   }
420   h = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_BarrierWaitHandle));
421   h->name = GNUNET_strdup (name);
422   h->cfg = cfg;
423   h->conn = GNUNET_CLIENT_connect ("testbed-barrier", h->cfg);
424   h->cb = cb;
425   h->cls = cls;
426   if (NULL == h->conn)
427   {
428     destroy_handle (h);
429     return NULL;
430   }
431   name_len = strlen (name);
432   msize = sizeof (struct GNUNET_TESTBED_BarrierWait) + name_len;
433   msg = GNUNET_malloc (msize);
434   msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_WAIT);
435   msg->header.size = htons (msize);
436   (void) memcpy (msg->name, name, name_len);
437   h->msg = &msg->header;
438   h->tx = 
439       GNUNET_CLIENT_notify_transmit_ready (h->conn, msize,
440                                            GNUNET_TIME_UNIT_FOREVER_REL,
441                                            GNUNET_NO,
442                                            &transmit_notify,
443                                            h);
444   return h;
445 }
446
447
448 /**
449  * Cancel a barrier wait handle
450  *
451  * @param h the barrier wait handle
452  */
453 void
454 GNUNET_TESTBED_barrier_wait_cancel (struct GNUNET_TESTBED_BarrierWaitHandle *h)
455 {
456   destroy_handle (h);
457 }
458
459 /* end of testbed_api_barriers.c */