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