Use statement exprs instead of local function
[oweals/gnunet.git] / src / testbed / testbed_api_barriers.c
1 /*
2   This file is part of GNUnet.
3   Copyright (C) 2008--2013, 2016 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18   Boston, MA 02110-1301, 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 #include "platform.h"
27 #include "gnunet_testbed_service.h"
28 #include "testbed_api.h"
29
30 /**
31  * Logging shorthand
32  */
33 #define LOG(type, ...)                          \
34   GNUNET_log_from (type, "testbed-api-barriers", __VA_ARGS__);
35
36 /**
37  * Debug logging shorthand
38  */
39 #define LOG_DEBUG(...)                          \
40   LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__);
41
42
43 /**
44  * Barrier wait handle
45  */
46 struct GNUNET_TESTBED_BarrierWaitHandle
47 {
48   /**
49    * The name of the barrier
50    */
51   char *name;
52
53   /**
54    * Then configuration used for the client connection
55    */
56   struct GNUNET_CONFIGURATION_Handle *cfg;
57
58   /**
59    * The testbed-barrier service message queue.
60    */
61   struct GNUNET_MQ_Handle *mq;
62
63   /**
64    * The barrier wait callback
65    */
66   GNUNET_TESTBED_barrier_wait_cb cb;
67
68   /**
69    * The closure for @e cb.
70    */
71   void *cb_cls;
72 };
73
74
75
76 /**
77  * Check if barrier status message is well-formed.
78  *
79  * @param cls closure
80  * @param msg received message
81  * @return #GNUNET_OK if the message is well-formed.
82  */
83 static int
84 check_status (void *cls,
85               const struct GNUNET_TESTBED_BarrierStatusMsg *msg)
86 {
87   /* FIXME: this fails to actually check that the message
88      follows the protocol spec (0-terminations!).  However,
89      not critical as #handle_status() doesn't interpret the
90      variable-size part anyway right now. */
91   return GNUNET_OK;
92 }
93
94
95 /**
96  * Type of a function to call when we receive a message
97  * from the service.
98  *
99  * @param cls closure
100  * @param msg received message
101  */
102 static void
103 handle_status (void *cls,
104                const struct GNUNET_TESTBED_BarrierStatusMsg *msg)
105 {
106   struct GNUNET_TESTBED_BarrierWaitHandle *h = cls;
107
108   switch (ntohs (msg->status))
109   {
110   case GNUNET_TESTBED_BARRIERSTATUS_ERROR:
111     h->cb (h->cb_cls,
112            h->name,
113            GNUNET_SYSERR);
114     break;
115   case GNUNET_TESTBED_BARRIERSTATUS_INITIALISED:
116     h->cb (h->cb_cls,
117            h->name,
118            GNUNET_SYSERR);
119     GNUNET_break (0);
120     break;
121   case GNUNET_TESTBED_BARRIERSTATUS_CROSSED:
122     h->cb (h->cb_cls,
123            h->name,
124            GNUNET_OK);
125     break;
126   default:
127     GNUNET_break_op (0);
128     h->cb (h->cb_cls,
129            h->name,
130            GNUNET_SYSERR);
131     break;
132   }
133   GNUNET_TESTBED_barrier_wait_cancel (h);
134 }
135
136
137 /**
138  * Generic error handler, called with the appropriate error code and
139  * the same closure specified at the creation of the message queue.
140  * Not every message queue implementation supports an error handler.
141  *
142  * @param cls closure with the `struct GNUNET_TESTBED_BarrierWaitHandle *`
143  * @param error error code
144  */
145 static void
146 mq_error_handler (void *cls,
147                   enum GNUNET_MQ_Error error)
148 {
149   struct GNUNET_TESTBED_BarrierWaitHandle *h = cls;
150
151   h->cb (h->cb_cls,
152          h->name,
153          GNUNET_SYSERR);
154   GNUNET_TESTBED_barrier_wait_cancel (h);
155 }
156
157
158 /**
159  * Wait for a barrier to be crossed.  This function should be called by the
160  * peers which have been started by the testbed.  If the peer is not started by
161  * testbed this function may return error
162  *
163  * @param name the name of the barrier
164  * @param cb the barrier wait callback
165  * @param cb_cls the closure for @a cb
166  * @return barrier wait handle which can be used to cancel the waiting at
167  *   anytime before the callback is called.  NULL upon error.
168  */
169 struct GNUNET_TESTBED_BarrierWaitHandle *
170 GNUNET_TESTBED_barrier_wait (const char *name,
171                              GNUNET_TESTBED_barrier_wait_cb cb,
172                              void *cb_cls)
173 {
174   struct GNUNET_TESTBED_BarrierWaitHandle *h
175     = GNUNET_new (struct GNUNET_TESTBED_BarrierWaitHandle);
176   struct GNUNET_MQ_MessageHandler handlers[] = {
177     GNUNET_MQ_hd_var_size (status,
178                            GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_STATUS,
179                            struct GNUNET_TESTBED_BarrierStatusMsg,
180                            h),
181     GNUNET_MQ_handler_end ()
182   };
183   struct GNUNET_MQ_Envelope *env;
184   struct GNUNET_TESTBED_BarrierWait *msg;
185   const char *cfg_filename;
186   size_t name_len;
187
188   GNUNET_assert (NULL != cb);
189   cfg_filename = getenv (ENV_TESTBED_CONFIG);
190   if (NULL == cfg_filename)
191   {
192     LOG (GNUNET_ERROR_TYPE_ERROR,
193          "Are you running under testbed?\n");
194     GNUNET_free (h);
195     return NULL;
196   }
197   h->cfg = GNUNET_CONFIGURATION_create ();
198   if (GNUNET_OK !=
199       GNUNET_CONFIGURATION_load (h->cfg,
200                                  cfg_filename))
201   {
202     LOG (GNUNET_ERROR_TYPE_ERROR,
203          "Unable to load configuration from file `%s'\n",
204          cfg_filename);
205     GNUNET_CONFIGURATION_destroy (h->cfg);
206     GNUNET_free (h);
207     return NULL;
208   }
209   h->name = GNUNET_strdup (name);
210   h->cb = cb;
211   h->cb_cls = cb_cls;
212   h->mq = GNUNET_CLIENT_connecT (h->cfg,
213                                  "testbed-barrier",
214                                  handlers,
215                                  &mq_error_handler,
216                                  h);
217   if (NULL == h->mq)
218   {
219     LOG (GNUNET_ERROR_TYPE_ERROR,
220          "Unable to connect to local testbed-barrier service\n");
221     GNUNET_TESTBED_barrier_wait_cancel (h);
222     return NULL;
223   }
224   name_len = strlen (name); /* NOTE: unusual to not have 0-termination, change? */
225   env = GNUNET_MQ_msg_extra (msg,
226                              name_len,
227                              GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_WAIT);
228   GNUNET_memcpy (msg->name,
229           name,
230           name_len);
231   GNUNET_MQ_send (h->mq,
232                   env);
233   return h;
234 }
235
236
237 /**
238  * Cancel a barrier wait handle
239  *
240  * @param h the barrier wait handle
241  */
242 void
243 GNUNET_TESTBED_barrier_wait_cancel (struct GNUNET_TESTBED_BarrierWaitHandle *h)
244 {
245   if (NULL != h->mq)
246   {
247     GNUNET_MQ_destroy (h->mq);
248     h->mq = NULL;
249   }
250   GNUNET_free (h->name);
251   GNUNET_CONFIGURATION_destroy (h->cfg);
252   GNUNET_free (h);
253 }
254
255 /* end of testbed_api_barriers.c */