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