Merge branch 'master' of gnunet.org:gnunet
[oweals/gnunet.git] / src / testbed / testbed_api_services.c
1 /*
2       This file is part of GNUnet
3       Copyright (C) 2008--2013 GNUnet e.V.
4
5       GNUnet is free software: you can redistribute it and/or modify it
6       under the terms of the GNU Affero General Public License as published
7       by the Free Software Foundation, either version 3 of the License,
8       or (at your 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       Affero General Public License for more details.
14      
15       You should have received a copy of the GNU Affero General Public License
16       along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 /**
20  * @file testbed/testbed_api_services.c
21  * @brief convenience functions for accessing services
22  * @author Christian Grothoff
23  */
24 #include "platform.h"
25 #include "testbed_api.h"
26 #include "testbed_api_peers.h"
27 #include "testbed_api_operations.h"
28
29
30 /**
31  * States for Service connect operations
32  */
33 enum State
34 {
35   /**
36    * Initial state
37    */
38   INIT,
39
40   /**
41    * The configuration request has been sent
42    */
43   CFG_REQUEST_QUEUED,
44
45   /**
46    * connected to service
47    */
48   SERVICE_CONNECTED
49 };
50
51
52 /**
53  * Data accessed during service connections
54  */
55 struct ServiceConnectData
56 {
57   /**
58    * helper function callback to establish the connection
59    */
60   GNUNET_TESTBED_ConnectAdapter ca;
61
62   /**
63    * helper function callback to close the connection
64    */
65   GNUNET_TESTBED_DisconnectAdapter da;
66
67   /**
68    * Closure to the above callbacks
69    */
70   void *cada_cls;
71
72   /**
73    * Service name
74    */
75   char *service_name;
76
77   /**
78    * Closure for operation event
79    */
80   void *op_cls;
81
82   /**
83    * The operation which created this structure
84    */
85   struct GNUNET_TESTBED_Operation *operation;
86
87   /**
88    * The operation context from GNUNET_TESTBED_forward_operation_msg_()
89    */
90   struct OperationContext *opc;
91
92   /**
93    * The peer handle
94    */
95   struct GNUNET_TESTBED_Peer *peer;
96
97   /**
98    * The acquired configuration of the peer
99    */
100   struct GNUNET_CONFIGURATION_Handle *cfg;
101
102   /**
103    * The op_result pointer from ConnectAdapter
104    */
105   void *op_result;
106
107   /**
108    * The operation completion callback
109    */
110   GNUNET_TESTBED_ServiceConnectCompletionCallback cb;
111
112   /**
113    * The closure for operation completion callback
114    */
115   void *cb_cls;
116
117   /**
118    * State information
119    */
120   enum State state;
121
122 };
123
124
125 /**
126  * Type of a function to call when we receive a message
127  * from the service.
128  *
129  * @param cls ServiceConnectData
130  * @param msg message received, NULL on timeout or fatal error
131  */
132 static void
133 configuration_receiver (void *cls, const struct GNUNET_MessageHeader *msg)
134 {
135   struct ServiceConnectData *data = cls;
136   struct GNUNET_TESTBED_Controller *c;
137   const char *emsg;
138   struct GNUNET_TESTBED_EventInformation info;
139   uint16_t mtype;
140
141   c = data->peer->controller;
142   mtype = ntohs (msg->type);
143   emsg = NULL;
144   info.type = GNUNET_TESTBED_ET_OPERATION_FINISHED;
145   info.op = data->operation;
146   info.op_cls = data->op_cls;
147   if (GNUNET_MESSAGE_TYPE_TESTBED_OPERATION_FAIL_EVENT == mtype)
148   {
149     emsg =
150         GNUNET_TESTBED_parse_error_string_ ((const struct
151                                              GNUNET_TESTBED_OperationFailureEventMessage
152                                              *) msg);
153     if (NULL == emsg)
154       emsg = "Unknown error";
155     info.details.operation_finished.emsg = emsg;
156     info.details.operation_finished.generic = NULL;
157     goto call_cb;
158   }
159   data->cfg = GNUNET_TESTBED_extract_config_ (msg);
160   GNUNET_assert (NULL == data->op_result);
161   data->op_result = data->ca (data->cada_cls, data->cfg);
162   info.details.operation_finished.emsg = NULL;
163   info.details.operation_finished.generic = data->op_result;
164   data->state = SERVICE_CONNECTED;
165
166 call_cb:
167   if ((0 != (GNUNET_TESTBED_ET_OPERATION_FINISHED & c->event_mask)) &&
168       (NULL != c->cc))
169     c->cc (c->cc_cls, &info);
170   if (NULL != data->cb)
171     data->cb (data->cb_cls, data->operation, data->op_result, emsg);
172 }
173
174
175 /**
176  * Function called when a service connect operation is ready
177  *
178  * @param cls the closure from GNUNET_TESTBED_operation_create_()
179  */
180 static void
181 opstart_service_connect (void *cls)
182 {
183   struct ServiceConnectData *data = cls;
184   struct GNUNET_TESTBED_PeerGetConfigurationMessage *msg;
185   struct GNUNET_TESTBED_Controller *c;
186   uint64_t op_id;
187
188   GNUNET_assert (NULL != data);
189   GNUNET_assert (NULL != data->peer);
190   c = data->peer->controller;
191   op_id = GNUNET_TESTBED_get_next_op_id (c);
192   msg =
193       GNUNET_TESTBED_generate_peergetconfig_msg_ (data->peer->unique_id, op_id);
194   data->opc =
195       GNUNET_TESTBED_forward_operation_msg_ (c, op_id, &msg->header,
196                                              &configuration_receiver, data);
197   GNUNET_free (msg);
198   data->state = CFG_REQUEST_QUEUED;
199 }
200
201
202 /**
203  * Callback which will be called when service connect type operation is
204  * released
205  *
206  * @param cls the closure from GNUNET_TESTBED_operation_create_()
207  */
208 static void
209 oprelease_service_connect (void *cls)
210 {
211   struct ServiceConnectData *data = cls;
212
213   switch (data->state)
214   {
215   case INIT:
216     break;
217   case CFG_REQUEST_QUEUED:
218     GNUNET_assert (NULL != data->opc);
219     GNUNET_TESTBED_forward_operation_msg_cancel_ (data->opc);
220     break;
221   case SERVICE_CONNECTED:
222     GNUNET_assert (NULL != data->cfg);
223     GNUNET_CONFIGURATION_destroy (data->cfg);
224     if (NULL != data->da)
225       data->da (data->cada_cls, data->op_result);
226     break;
227   }
228   GNUNET_free (data);
229 }
230
231
232 /**
233  * Connect to a service offered by the given peer.  Will ensure that
234  * the request is queued to not overwhelm our ability to create and
235  * maintain connections with other systems.  The actual service
236  * handle is then returned via the 'op_result' member in the event
237  * callback.  The 'ca' callback is used to create the connection
238  * when the time is right; the 'da' callback will be used to
239  * destroy the connection (upon 'GNUNET_TESTBED_operation_done').
240  * 'GNUNET_TESTBED_operation_done' can be used to abort this
241  * operation until the event callback has been called.
242  *
243  * @param op_cls closure to pass in operation event
244  * @param peer peer that runs the service
245  * @param service_name name of the service to connect to
246  * @param cb the callback to call when this operation finishes
247  * @param cb_cls closure for the above callback
248  * @param ca helper function to establish the connection
249  * @param da helper function to close the connection
250  * @param cada_cls closure for ca and da
251  * @return handle for the operation
252  */
253 struct GNUNET_TESTBED_Operation *
254 GNUNET_TESTBED_service_connect (void *op_cls, struct GNUNET_TESTBED_Peer *peer,
255                                 const char *service_name,
256                                 GNUNET_TESTBED_ServiceConnectCompletionCallback
257                                 cb, void *cb_cls,
258                                 GNUNET_TESTBED_ConnectAdapter ca,
259                                 GNUNET_TESTBED_DisconnectAdapter da,
260                                 void *cada_cls)
261 {
262   struct ServiceConnectData *data;
263
264   data = GNUNET_new (struct ServiceConnectData);
265   data->ca = ca;
266   data->da = da;
267   data->cada_cls = cada_cls;
268   data->op_cls = op_cls;
269   data->peer = peer;
270   data->state = INIT;
271   data->cb = cb;
272   data->cb_cls = cb_cls;
273   data->operation =
274       GNUNET_TESTBED_operation_create_ (data, &opstart_service_connect,
275                                         &oprelease_service_connect);
276   GNUNET_TESTBED_operation_queue_insert_ (peer->
277                                           controller->opq_parallel_service_connections,
278                                           data->operation);
279   GNUNET_TESTBED_operation_queue_insert_ (peer->
280                                           controller->opq_parallel_operations,
281                                           data->operation);
282   GNUNET_TESTBED_operation_begin_wait_ (data->operation);
283   return data->operation;
284 }
285
286 /* end of testbed_api_services.c */