uncrustify as demanded.
[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      SPDX-License-Identifier: AGPL3.0-or-later
19  */
20
21 /**
22  * @file testbed/testbed_api_services.c
23  * @brief convenience functions for accessing services
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "testbed_api.h"
28 #include "testbed_api_peers.h"
29 #include "testbed_api_operations.h"
30
31
32 /**
33  * States for Service connect operations
34  */
35 enum State {
36   /**
37    * Initial state
38    */
39   INIT,
40
41   /**
42    * The configuration request has been sent
43    */
44   CFG_REQUEST_QUEUED,
45
46   /**
47    * connected to service
48    */
49   SERVICE_CONNECTED
50 };
51
52
53 /**
54  * Data accessed during service connections
55  */
56 struct ServiceConnectData {
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  * Type of a function to call when we receive a message
126  * from the service.
127  *
128  * @param cls ServiceConnectData
129  * @param msg message received, NULL on timeout or fatal error
130  */
131 static void
132 configuration_receiver(void *cls, const struct GNUNET_MessageHeader *msg)
133 {
134   struct ServiceConnectData *data = cls;
135   struct GNUNET_TESTBED_Controller *c;
136   const char *emsg;
137   struct GNUNET_TESTBED_EventInformation info;
138   uint16_t mtype;
139
140   c = data->peer->controller;
141   mtype = ntohs(msg->type);
142   emsg = NULL;
143   info.type = GNUNET_TESTBED_ET_OPERATION_FINISHED;
144   info.op = data->operation;
145   info.op_cls = data->op_cls;
146   if (GNUNET_MESSAGE_TYPE_TESTBED_OPERATION_FAIL_EVENT == mtype)
147     {
148       emsg =
149         GNUNET_TESTBED_parse_error_string_((const struct
150                                             GNUNET_TESTBED_OperationFailureEventMessage
151                                             *)msg);
152       if (NULL == emsg)
153         emsg = "Unknown error";
154       info.details.operation_finished.emsg = emsg;
155       info.details.operation_finished.generic = NULL;
156       goto call_cb;
157     }
158   data->cfg = GNUNET_TESTBED_extract_config_(msg);
159   GNUNET_assert(NULL == data->op_result);
160   data->op_result = data->ca(data->cada_cls, data->cfg);
161   info.details.operation_finished.emsg = NULL;
162   info.details.operation_finished.generic = data->op_result;
163   data->state = SERVICE_CONNECTED;
164
165 call_cb:
166   if ((0 != (GNUNET_TESTBED_ET_OPERATION_FINISHED & c->event_mask)) &&
167       (NULL != c->cc))
168     c->cc(c->cc_cls, &info);
169   if (NULL != data->cb)
170     data->cb(data->cb_cls, data->operation, data->op_result, emsg);
171 }
172
173
174 /**
175  * Function called when a service connect operation is ready
176  *
177  * @param cls the closure from GNUNET_TESTBED_operation_create_()
178  */
179 static void
180 opstart_service_connect(void *cls)
181 {
182   struct ServiceConnectData *data = cls;
183   struct GNUNET_TESTBED_PeerGetConfigurationMessage *msg;
184   struct GNUNET_TESTBED_Controller *c;
185   uint64_t op_id;
186
187   GNUNET_assert(NULL != data);
188   GNUNET_assert(NULL != data->peer);
189   c = data->peer->controller;
190   op_id = GNUNET_TESTBED_get_next_op_id(c);
191   msg =
192     GNUNET_TESTBED_generate_peergetconfig_msg_(data->peer->unique_id, op_id);
193   data->opc =
194     GNUNET_TESTBED_forward_operation_msg_(c, op_id, &msg->header,
195                                           &configuration_receiver, data);
196   GNUNET_free(msg);
197   data->state = CFG_REQUEST_QUEUED;
198 }
199
200
201 /**
202  * Callback which will be called when service connect type operation is
203  * released
204  *
205  * @param cls the closure from GNUNET_TESTBED_operation_create_()
206  */
207 static void
208 oprelease_service_connect(void *cls)
209 {
210   struct ServiceConnectData *data = cls;
211
212   switch (data->state)
213     {
214     case INIT:
215       break;
216
217     case CFG_REQUEST_QUEUED:
218       GNUNET_assert(NULL != data->opc);
219       GNUNET_TESTBED_forward_operation_msg_cancel_(data->opc);
220       break;
221
222     case SERVICE_CONNECTED:
223       GNUNET_assert(NULL != data->cfg);
224       GNUNET_CONFIGURATION_destroy(data->cfg);
225       if (NULL != data->da)
226         data->da(data->cada_cls, data->op_result);
227       break;
228     }
229   GNUNET_free(data);
230 }
231
232
233 /**
234  * Connect to a service offered by the given peer.  Will ensure that
235  * the request is queued to not overwhelm our ability to create and
236  * maintain connections with other systems.  The actual service
237  * handle is then returned via the 'op_result' member in the event
238  * callback.  The 'ca' callback is used to create the connection
239  * when the time is right; the 'da' callback will be used to
240  * destroy the connection (upon 'GNUNET_TESTBED_operation_done').
241  * 'GNUNET_TESTBED_operation_done' can be used to abort this
242  * operation until the event callback has been called.
243  *
244  * @param op_cls closure to pass in operation event
245  * @param peer peer that runs the service
246  * @param service_name name of the service to connect to
247  * @param cb the callback to call when this operation finishes
248  * @param cb_cls closure for the above callback
249  * @param ca helper function to establish the connection
250  * @param da helper function to close the connection
251  * @param cada_cls closure for ca and da
252  * @return handle for the operation
253  */
254 struct GNUNET_TESTBED_Operation *
255 GNUNET_TESTBED_service_connect(void *op_cls, struct GNUNET_TESTBED_Peer *peer,
256                                const char *service_name,
257                                GNUNET_TESTBED_ServiceConnectCompletionCallback
258                                cb, void *cb_cls,
259                                GNUNET_TESTBED_ConnectAdapter ca,
260                                GNUNET_TESTBED_DisconnectAdapter da,
261                                void *cada_cls)
262 {
263   struct ServiceConnectData *data;
264
265   data = GNUNET_new(struct ServiceConnectData);
266   data->ca = ca;
267   data->da = da;
268   data->cada_cls = cada_cls;
269   data->op_cls = op_cls;
270   data->peer = peer;
271   data->state = INIT;
272   data->cb = cb;
273   data->cb_cls = cb_cls;
274   data->operation =
275     GNUNET_TESTBED_operation_create_(data, &opstart_service_connect,
276                                      &oprelease_service_connect);
277   GNUNET_TESTBED_operation_queue_insert_(peer->
278                                          controller->opq_parallel_service_connections,
279                                          data->operation);
280   GNUNET_TESTBED_operation_queue_insert_(peer->
281                                          controller->opq_parallel_operations,
282                                          data->operation);
283   GNUNET_TESTBED_operation_begin_wait_(data->operation);
284   return data->operation;
285 }
286
287 /* end of testbed_api_services.c */