aboutsummaryrefslogtreecommitdiff
path: root/src/arm/arm_api.c
diff options
context:
space:
mode:
authorLRN <lrn1986@gmail.com>2013-03-13 17:49:26 +0000
committerLRN <lrn1986@gmail.com>2013-03-13 17:49:26 +0000
commit405f776bc08486af4edb80e18149c0829732b347 (patch)
treed5fc635a51641dec6b53cb2540276f34ae8f6210 /src/arm/arm_api.c
parent3ceae682287492ecc768aea5c4c463216a35774d (diff)
downloadgnunet-405f776bc08486af4edb80e18149c0829732b347.tar.gz
gnunet-405f776bc08486af4edb80e18149c0829732b347.zip
All-encompassing ARM update
Diffstat (limited to 'src/arm/arm_api.c')
-rw-r--r--src/arm/arm_api.c1130
1 files changed, 659 insertions, 471 deletions
diff --git a/src/arm/arm_api.c b/src/arm/arm_api.c
index aee9afb24..d434ff3af 100644
--- a/src/arm/arm_api.c
+++ b/src/arm/arm_api.c
@@ -1,6 +1,6 @@
1/* 1/*
2 This file is part of GNUnet. 2 This file is part of GNUnet.
3 (C) 2009, 2010, 2012 Christian Grothoff (and other contributing authors) 3 (C) 2009, 2010, 2012, 2013 Christian Grothoff (and other contributing authors)
4 4
5 GNUnet is free software; you can redistribute it and/or modify 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 6 it under the terms of the GNU General Public License as published
@@ -21,7 +21,7 @@
21/** 21/**
22 * @file arm/arm_api.c 22 * @file arm/arm_api.c
23 * @brief API for accessing the ARM service 23 * @brief API for accessing the ARM service
24 * @author Christian Grothoff 24 * @author Christian Grothoff, LRN
25 */ 25 */
26#include "platform.h" 26#include "platform.h"
27#include "gnunet_arm_service.h" 27#include "gnunet_arm_service.h"
@@ -38,7 +38,7 @@ struct GNUNET_ARM_Handle
38{ 38{
39 39
40 /** 40 /**
41 * Our connection to the ARM service. 41 * Our control connection to the ARM service.
42 */ 42 */
43 struct GNUNET_CLIENT_Connection *client; 43 struct GNUNET_CLIENT_Connection *client;
44 44
@@ -47,257 +47,469 @@ struct GNUNET_ARM_Handle
47 */ 47 */
48 struct GNUNET_CONFIGURATION_Handle *cfg; 48 struct GNUNET_CONFIGURATION_Handle *cfg;
49 49
50 /**
51 * Handle for our current transmission request.
52 */
53 struct GNUNET_CLIENT_TransmitHandle *cth;
54
55 /**
56 * Head of doubly-linked list of pending requests.
57 */
58 struct ARMControlMessage *control_pending_head;
59
60 /**
61 * Tail of doubly-linked list of pending requests.
62 */
63 struct ARMControlMessage *control_pending_tail;
64
65 /**
66 * Head of doubly-linked list of sent requests.
67 */
68 struct ARMControlMessage *control_sent_head;
69
70 /**
71 * Tail of doubly-linked list of sent requests.
72 */
73 struct ARMControlMessage *control_sent_tail;
74
75 /**
76 * ID of the reconnect task (if any).
77 */
78 GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
79
80 /**
81 * Current delay we use for re-trying to connect to core.
82 */
83 struct GNUNET_TIME_Relative retry_backoff;
84
85 /**
86 * Are we currently disconnected and hence unable to send?
87 */
88 unsigned char currently_down;
89
90 /**
91 * Callback to invoke on connection/disconnection.
92 */
93 GNUNET_ARM_ConnectionStatusCallback conn_status;
94
95 /**
96 * Closure for conn_status.
97 */
98 void *conn_status_cls;
99
100 /**
101 * GNUNET_YES if we're running a service test.
102 */
103 unsigned char service_test_is_active;
104
105 /**
106 * Counter for request identifiers
107 */
108 uint64_t request_id_counter;
50}; 109};
51 110
111
52/** 112/**
53 * Context for handling the shutdown of a service. 113 * Entry in a doubly-linked list of control messages to be transmitted
114 * to the arm service.
115 *
116 * The actual message is allocated at the end of this struct.
54 */ 117 */
55struct ShutdownContext 118struct ARMControlMessage
56{ 119{
57 /** 120 /**
58 * Connection to the service that is being shutdown. 121 * This is a doubly-linked list.
59 */ 122 */
60 struct GNUNET_CLIENT_Connection *sock; 123 struct ARMControlMessage *next;
61 124
62 /** 125 /**
63 * Time allowed for shutdown to happen. 126 * This is a doubly-linked list.
64 */ 127 */
65 struct GNUNET_TIME_Absolute timeout; 128 struct ARMControlMessage *prev;
66 129
67 /** 130 /**
68 * Task set up to cancel the shutdown request on timeout. 131 * Callback for service state change requests.
69 */ 132 */
70 GNUNET_SCHEDULER_TaskIdentifier cancel_task; 133 GNUNET_ARM_ResultCallback result_cont;
71 134
72 /** 135 /**
73 * Task to call once shutdown complete 136 * Callback for service list requests.
74 */ 137 */
75 GNUNET_CLIENT_ShutdownTask cont; 138 GNUNET_ARM_ServiceListCallback list_cont;
76 139
77 /** 140 /**
78 * Closure for shutdown continuation 141 * Closure for 'result_cont' or 'list_cont'.
79 */ 142 */
80 void *cont_cls; 143 void *cont_cls;
81 144
82 /** 145 /**
83 * Handle for transmission request. 146 * Timeout for the operation.
84 */ 147 */
85 struct GNUNET_CLIENT_TransmitHandle *th; 148 struct GNUNET_TIME_Absolute timeout;
86 149
150 /**
151 * Type of the request expressed as a message type (start, stop or list).
152 */
153 uint16_t type;
154
155 /**
156 * Flags for passing std descriptors to ARM (when starting ARM).
157 */
158 enum GNUNET_OS_InheritStdioFlags std_inheritance;
159
160 /**
161 * ARM handle.
162 */
163 struct GNUNET_ARM_Handle *h;
164
165 /**
166 * Message to send.
167 */
168 struct GNUNET_ARM_Message *msg;
169
170 /**
171 * Task to run when request times out.
172 */
173 GNUNET_SCHEDULER_TaskIdentifier timeout_task_id;
87}; 174};
88 175
176static void
177client_notify_handler (void *cls, const struct GNUNET_MessageHeader *msg);
178
179static void
180reconnect_arm (struct GNUNET_ARM_Handle *h);
181
182static void
183trigger_next_request (struct GNUNET_ARM_Handle *h, int ignore_currently_down);
184
89 185
90/** 186/**
91 * Handler receiving response to service shutdown requests. 187 * Task scheduled to try to re-connect to arm.
92 * First call with NULL: service misbehaving, or something.
93 * First call with GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN_ACK:
94 * - service will shutdown
95 * Second call with NULL:
96 * - service has now really shut down.
97 * 188 *
98 * @param cls closure 189 * @param cls the 'struct GNUNET_ARM_Handle'
99 * @param msg NULL, indicating socket closure. 190 * @param tc task context
100 */ 191 */
101static void 192static void
102service_shutdown_handler (void *cls, const struct GNUNET_MessageHeader *msg) 193reconnect_arm_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
194{
195 struct GNUNET_ARM_Handle *h = cls;
196
197 h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
198 LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting to ARM service after delay\n");
199 reconnect_arm (h);
200}
201
202
203static void
204clear_pending_messages (struct GNUNET_ARM_Handle *h, enum GNUNET_ARM_RequestStatus result)
103{ 205{
104 struct ShutdownContext *shutdown_ctx = cls; 206 struct ARMControlMessage *cm;
105 207
106 if (NULL != msg) 208 LOG (GNUNET_ERROR_TYPE_DEBUG,
209 "Clearing pending messages\n");
210
211 while (NULL != (cm = h->control_pending_head))
107 { 212 {
108 /* We just expected a disconnect! Report the error and be done with it... */ 213 GNUNET_CONTAINER_DLL_remove (h->control_pending_head,
109 GNUNET_break (0); 214 h->control_pending_tail, cm);
110 shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR); 215 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cm->timeout_task_id);
111 GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task); 216 GNUNET_SCHEDULER_cancel (cm->timeout_task_id);
112 GNUNET_CLIENT_disconnect (shutdown_ctx->sock); 217 if (NULL != cm->result_cont)
113 GNUNET_free (shutdown_ctx); 218 cm->result_cont (cm->cont_cls, cm->h, result, NULL, 0);
114 return; 219 GNUNET_free_non_null (cm->msg);
220 GNUNET_free (cm);
115 } 221 }
116 if (NULL != shutdown_ctx->cont)
117 /* shutdown is now complete, as we waited for the network disconnect... */
118 shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_DOWN);
119 GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task);
120 GNUNET_CLIENT_disconnect (shutdown_ctx->sock);
121 GNUNET_free (shutdown_ctx);
122} 222}
123 223
124
125/** 224/**
126 * Shutting down took too long, cancel receive and return error. 225 * Close down any existing connection to the ARM service and
226 * try re-establishing it later.
127 * 227 *
128 * @param cls closure 228 * @param h our handle
129 * @param tc context information (why was this task triggered now)
130 */ 229 */
131static void 230static void
132service_shutdown_cancel (void *cls, 231reconnect_arm_later (struct GNUNET_ARM_Handle *h)
133 const struct GNUNET_SCHEDULER_TaskContext *tc)
134{ 232{
135 struct ShutdownContext *shutdown_ctx = cls; 233 if (GNUNET_NO != h->currently_down)
234 return;
136 235
137 shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_COMMUNICATION_TIMEOUT); 236 if (NULL != h->cth)
138 GNUNET_CLIENT_disconnect (shutdown_ctx->sock); 237 {
139 GNUNET_free (shutdown_ctx); 238 GNUNET_CLIENT_notify_transmit_ready_cancel (h->cth);
140} 239 h->cth = NULL;
240 }
241
242 if (NULL != h->client)
243 {
244 GNUNET_CLIENT_disconnect (h->client);
245 h->client = NULL;
246 }
247
248 if (NULL != h->conn_status)
249 h->conn_status (h->conn_status_cls, h, GNUNET_NO, GNUNET_NO);
250
251 h->currently_down = GNUNET_YES;
141 252
253 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == h->reconnect_task);
254 h->reconnect_task =
255 GNUNET_SCHEDULER_add_delayed (h->retry_backoff, &reconnect_arm_task, h);
256 /* Don't clear pending messages on disconnection, deliver them later
257 clear_pending_messages (h, GNUNET_ARM_REQUEST_DISCONNECTED);
258 GNUNET_assert (NULL == h->control_pending_head);
259 */
260 h->retry_backoff = GNUNET_TIME_STD_BACKOFF (h->retry_backoff);
261}
142 262
143/** 263/**
144 * If possible, write a shutdown message to the target 264 * Transmit the next message to the arm service.
145 * buffer and destroy the client connection.
146 * 265 *
147 * @param cls the "struct GNUNET_CLIENT_Connection" to destroy 266 * @param cls closure with the 'struct GNUNET_ARM_Handle'
148 * @param size number of bytes available in buf 267 * @param size number of bytes available in buf
149 * @param buf NULL on error, otherwise target buffer 268 * @param buf where the callee should write the message
150 * @return number of bytes written to buf 269 * @return number of bytes written to buf
151 */ 270 */
152static size_t 271static size_t
153write_shutdown (void *cls, size_t size, void *buf) 272transmit_arm_message (void *cls, size_t size, void *buf)
154{ 273{
155 struct ShutdownContext *shutdown_ctx = cls; 274 struct GNUNET_ARM_Handle *h = cls;
156 struct GNUNET_MessageHeader *msg; 275 struct ARMControlMessage *cm;
276 struct GNUNET_ARM_Message *arm_msg;
277 uint16_t msize;
278 uint64_t request_id;
157 279
158 shutdown_ctx->th = NULL; 280 LOG (GNUNET_ERROR_TYPE_DEBUG,
159 if (size < sizeof (struct GNUNET_MessageHeader)) 281 "transmit_arm_message is running with %p buffer of size %lu. ARM is known to be %s\n",
282 buf, size, h->currently_down ? "unconnected" : "connected");
283 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == h->reconnect_task);
284 h->cth = NULL;
285 if ((GNUNET_YES == h->currently_down) && (NULL != buf))
286 {
287 h->currently_down = GNUNET_NO;
288 if (NULL != h->conn_status)
289 h->conn_status (h->conn_status_cls, h, GNUNET_YES, GNUNET_NO);
290 h->retry_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
291 GNUNET_CLIENT_receive (h->client, &client_notify_handler, h,
292 GNUNET_TIME_UNIT_FOREVER_REL);
293 }
294 if (NULL == buf)
295 {
296 LOG (GNUNET_ERROR_TYPE_DEBUG,
297 "Transmission failed, initiating reconnect\n");
298 reconnect_arm_later (h);
299 return 0;
300 }
301 if (NULL == (cm = h->control_pending_head))
160 { 302 {
161 LOG (GNUNET_ERROR_TYPE_WARNING, 303 LOG (GNUNET_ERROR_TYPE_DEBUG, "Queue is empty, not sending anything\n");
162 _("Failed to transmit shutdown request to client.\n")); 304 return 0;
163 shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR);
164 GNUNET_CLIENT_disconnect (shutdown_ctx->sock);
165 GNUNET_free (shutdown_ctx);
166 return 0; /* client disconnected */
167 } 305 }
168 GNUNET_CLIENT_receive (shutdown_ctx->sock, &service_shutdown_handler, 306
169 shutdown_ctx, GNUNET_TIME_UNIT_FOREVER_REL); 307 GNUNET_assert (NULL != cm->msg);
170 shutdown_ctx->cancel_task = 308 msize = ntohs (cm->msg->header.size);
171 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining 309 if (size < msize)
172 (shutdown_ctx->timeout), 310 {
173 &service_shutdown_cancel, shutdown_ctx); 311 LOG (GNUNET_ERROR_TYPE_DEBUG,
174 msg = (struct GNUNET_MessageHeader *) buf; 312 "Request is too big (%u < %u), not sending it\n", size, msize);
175 msg->type = htons (GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN); 313 trigger_next_request (h, GNUNET_NO);
176 msg->size = htons (sizeof (struct GNUNET_MessageHeader)); 314 return 0;
177 return sizeof (struct GNUNET_MessageHeader); 315 }
316 arm_msg = cm->msg;
317 if (0 == h->request_id_counter)
318 h->request_id_counter++;
319 request_id = h->request_id_counter++;
320 LOG (GNUNET_ERROR_TYPE_DEBUG,
321 "Transmitting control message with %u bytes of type %u to arm with id %llu\n",
322 (unsigned int) msize, (unsigned int) ntohs (cm->msg->header.type), request_id);
323 arm_msg->request_id = GNUNET_htonll (request_id);
324 memcpy (buf, cm->msg, msize);
325 /* Otherwise we won't be able to find it later! */
326 arm_msg->request_id = request_id;
327
328 GNUNET_CONTAINER_DLL_remove (h->control_pending_head,
329 h->control_pending_tail, cm);
330 GNUNET_CONTAINER_DLL_insert_tail (h->control_sent_head,
331 h->control_sent_tail, cm);
332
333 /* Don't free msg, keep it around (kind of wasteful, but then we don't
334 * really have many messages to handle, and it'll be freed when it times
335 * out anyway.
336 */
337 trigger_next_request (h, GNUNET_NO);
338 return msize;
178} 339}
179 340
180 341
181/** 342/**
182 * Request that the service should shutdown. 343 * Check the list of pending requests, send the next
183 * Afterwards, the connection will automatically be 344 * one to the arm.
184 * disconnected. Hence the "sock" should not
185 * be used by the caller after this call
186 * (calling this function frees "sock" after a while).
187 * 345 *
188 * @param sock the socket connected to the service 346 * @param h arm handle
189 * @param timeout how long to wait before giving up on transmission 347 * @param ignore_currently_down transmit message even if not initialized?
190 * @param cont continuation to call once the service is really down 348 */
191 * @param cont_cls closure for continuation 349static void
350trigger_next_request (struct GNUNET_ARM_Handle *h, int ignore_currently_down)
351{
352 uint16_t msize;
353
354 if ((GNUNET_YES == h->currently_down) && (ignore_currently_down == GNUNET_NO))
355 {
356 LOG (GNUNET_ERROR_TYPE_DEBUG,
357 "ARM connection down, not processing queue\n");
358 return;
359 }
360 if (NULL != h->cth)
361 {
362 LOG (GNUNET_ERROR_TYPE_DEBUG, "Request pending, not processing queue\n");
363 return;
364 }
365 if (NULL != h->control_pending_head)
366 msize =
367 ntohs (((struct GNUNET_MessageHeader *) &h->
368 control_pending_head[1])->size);
369 else if (GNUNET_NO == ignore_currently_down)
370 {
371 LOG (GNUNET_ERROR_TYPE_DEBUG,
372 "Request queue empty, not processing queue\n");
373 return; /* no pending message */
374 }
375 h->cth =
376 GNUNET_CLIENT_notify_transmit_ready (h->client, msize,
377 GNUNET_TIME_UNIT_FOREVER_REL,
378 GNUNET_NO, &transmit_arm_message, h);
379}
380
381
382/**
383 * Connect to arm.
192 * 384 *
385 * @param h arm handle
193 */ 386 */
194static void 387static void
195arm_service_shutdown (struct GNUNET_CLIENT_Connection *sock, 388reconnect_arm (struct GNUNET_ARM_Handle *h)
196 struct GNUNET_TIME_Relative timeout,
197 GNUNET_CLIENT_ShutdownTask cont, void *cont_cls)
198{ 389{
199 struct ShutdownContext *shutdown_ctx; 390 GNUNET_assert (NULL == h->client);
200 391 GNUNET_assert (GNUNET_YES == h->currently_down);
201 shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext)); 392 h->client = GNUNET_CLIENT_connect ("arm", h->cfg);
202 shutdown_ctx->cont = cont; 393 if (NULL == h->client)
203 shutdown_ctx->cont_cls = cont_cls; 394 {
204 shutdown_ctx->sock = sock; 395 LOG (GNUNET_ERROR_TYPE_DEBUG,
205 shutdown_ctx->timeout = GNUNET_TIME_relative_to_absolute (timeout); 396 "arm_api, GNUNET_CLIENT_connect returned NULL\n");
206 shutdown_ctx->th = GNUNET_CLIENT_notify_transmit_ready (sock, 397 if (NULL != h->conn_status)
207 sizeof (struct GNUNET_MessageHeader), 398 h->conn_status (h->conn_status_cls, h, GNUNET_NO, GNUNET_YES);
208 timeout, GNUNET_NO, &write_shutdown, 399 return;
209 shutdown_ctx); 400 }
401 LOG (GNUNET_ERROR_TYPE_DEBUG,
402 "arm_api, GNUNET_CLIENT_connect returned non-NULL\n");
403 trigger_next_request (h, GNUNET_YES);
210} 404}
211 405
212 406
213/** 407/**
214 * Setup a context for communicating with ARM. Note that this 408 * Set up a context for communicating with ARM. Note that this
215 * can be done even if the ARM service is not yet running. 409 * can be done even if the ARM service is not yet running.
410 * Never fails.
216 * 411 *
217 * @param cfg configuration to use (needed to contact ARM; 412 * @param cfg configuration to use (needed to contact ARM;
218 * the ARM service may internally use a different 413 * the ARM service may internally use a different
219 * configuration to determine how to start the service). 414 * configuration to determine how to start the service).
220 * @param service service that *this* process is implementing/providing, can be NULL 415 * @return context to use for further ARM operations
221 * @return context to use for further ARM operations, NULL on error
222 */ 416 */
223struct GNUNET_ARM_Handle * 417struct GNUNET_ARM_Handle *
224GNUNET_ARM_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, 418GNUNET_ARM_alloc (const struct GNUNET_CONFIGURATION_Handle *cfg)
225 const char *service)
226{ 419{
227 struct GNUNET_ARM_Handle *ret; 420 struct GNUNET_ARM_Handle *ret;
228 421
229 ret = GNUNET_malloc (sizeof (struct GNUNET_ARM_Handle)); 422 ret = GNUNET_malloc (sizeof (struct GNUNET_ARM_Handle));
230 ret->cfg = GNUNET_CONFIGURATION_dup (cfg); 423 ret->cfg = GNUNET_CONFIGURATION_dup (cfg);
424 ret->currently_down = GNUNET_YES;
425 ret->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
231 return ret; 426 return ret;
232} 427}
233 428
234 429
235/** 430/**
236 * Disconnect from the ARM service. 431 * Start connecting to the ARM service using the context.
237 * 432 *
238 * @param h the handle that was being used 433 * @param h ARM handle
434 * @param conn_status will be called when connecting/disconnecting
435 * @param cls closure for conn_status
239 */ 436 */
240void 437void
241GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *h) 438GNUNET_ARM_connect (struct GNUNET_ARM_Handle *h,
439 GNUNET_ARM_ConnectionStatusCallback conn_status, void *cls)
242{ 440{
243 if (h->client != NULL) 441 h->conn_status = conn_status;
244 GNUNET_CLIENT_disconnect (h->client); 442 h->conn_status_cls = cls;
245 GNUNET_CONFIGURATION_destroy (h->cfg); 443 reconnect_arm (h);
246 GNUNET_free (h);
247} 444}
248 445
249 446
250struct ARM_ShutdownContext 447/**
448 * Disconnect from the ARM service (if connected) and destroy the context.
449 * Don't call inside an ARM callback!
450 *
451 * @param h the handle that was being used
452 */
453void
454GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *handle)
251{ 455{
252 /** 456 LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from ARM service\n");
253 * Callback to call once shutdown complete. 457 if (NULL != handle->cth)
254 */ 458 {
255 GNUNET_ARM_Callback cb; 459 GNUNET_CLIENT_notify_transmit_ready_cancel (handle->cth);
256 460 handle->cth = NULL;
257 /** 461 }
258 * Closure for callback. 462 clear_pending_messages (handle, GNUNET_ARM_REQUEST_DISCONNECTED);
259 */ 463 if (NULL != handle->client)
260 void *cb_cls; 464 {
261}; 465 GNUNET_CLIENT_disconnect (handle->client);
466 handle->client = NULL;
467 }
468 if (GNUNET_SCHEDULER_NO_TASK != handle->reconnect_task)
469 {
470 GNUNET_SCHEDULER_cancel (handle->reconnect_task);
471 handle->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
472 }
473 if (GNUNET_NO == handle->service_test_is_active)
474 {
475 GNUNET_CONFIGURATION_destroy (handle->cfg);
476 GNUNET_free (handle);
477 }
478}
262 479
263 480
264/** 481/**
265 * Internal state for a request with ARM. 482 * Message timed out. Remove it from the queue.
483 *
484 * @param cls the message (struct ARMControlMessage *)
485 * @param tc task context
266 */ 486 */
267struct RequestContext 487static void
488control_message_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
268{ 489{
490 struct ARMControlMessage *cm = cls;
491 struct GNUNET_ARM_Message *arm_msg;
492 LOG (GNUNET_ERROR_TYPE_DEBUG,
493 "Control message timed out\n");
494 arm_msg = cm->msg;
495 if ((NULL == arm_msg) || (0 == arm_msg->request_id))
496 {
497 GNUNET_CONTAINER_DLL_remove (cm->h->control_pending_head,
498 cm->h->control_pending_tail, cm);
499 }
500 else
501 {
502 GNUNET_CONTAINER_DLL_remove (cm->h->control_sent_head,
503 cm->h->control_sent_tail, cm);
504 }
505 if (NULL != cm->result_cont)
506 cm->result_cont (cm->cont_cls, cm->h, GNUNET_ARM_REQUEST_TIMEOUT, NULL, 0);
507 else if (NULL != cm->list_cont)
508 cm->list_cont (cm->cont_cls, cm->h, GNUNET_ARM_REQUEST_TIMEOUT, 0, NULL);
509 GNUNET_free_non_null (cm->msg);
510 GNUNET_free (cm);
511}
269 512
270 /**
271 * Pointer to our handle with ARM.
272 */
273 struct GNUNET_ARM_Handle *h;
274
275 /**
276 * Function to call with a status code for the requested operation.
277 */
278 GNUNET_ARM_Callback callback;
279
280 /**
281 * Closure for "callback".
282 */
283 void *cls;
284
285 /**
286 * Timeout for the operation.
287 */
288 struct GNUNET_TIME_Absolute timeout;
289
290 /**
291 * Type of the request expressed as a message type (start or stop).
292 */
293 uint16_t type;
294
295 /**
296 * Flags for passing std descriptors to ARM (when starting ARM).
297 */
298 enum GNUNET_OS_InheritStdioFlags std_inheritance;
299
300};
301 513
302#include "do_start_process.c" 514#include "do_start_process.c"
303 515
@@ -308,78 +520,89 @@ struct RequestContext
308 * or not ARM is running; if it is, report success. If 520 * or not ARM is running; if it is, report success. If
309 * it is not, start the ARM process. 521 * it is not, start the ARM process.
310 * 522 *
311 * @param cls the context for the request that we will report on (struct RequestContext*) 523 * @param cls the context for the request that we will report on (struct ARMControlMessage *)
312 * @param tc why were we called (reason says if ARM is running) 524 * @param tc why were we called (reason says if ARM is running)
313 */ 525 */
314static void 526static void
315arm_service_report (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) 527arm_service_report (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
316{ 528{
317 struct RequestContext *pos = cls; 529 struct ARMControlMessage *cm = cls;
318 struct GNUNET_OS_Process *proc; 530 struct GNUNET_OS_Process *proc;
531 unsigned char test_is_active;
319 char *cbinary; 532 char *cbinary;
320 char *binary; 533 char *binary;
321 char *config; 534 char *config;
322 char *loprefix; 535 char *loprefix;
323 char *lopostfix; 536 char *lopostfix;
324 537
325 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE)) 538 test_is_active = cm->h->service_test_is_active;
539
540 /* FIXME: shouldn't we check for GNUNET_SCHEDULER_REASON_SHUTDOWN ? */
541 if ((GNUNET_YES == test_is_active) &&
542 (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE)))
326 { 543 {
327 LOG (GNUNET_ERROR_TYPE_DEBUG, "Looks like `%s' is already running.\n", 544 LOG (GNUNET_ERROR_TYPE_DEBUG, "Looks like `%s' is already running.\n",
328 "gnunet-service-arm"); 545 "gnunet-service-arm");
329 /* arm is running! */ 546 /* arm is running! */
330 if (pos->callback != NULL) 547 if (cm->result_cont)
331 pos->callback (pos->cls, GNUNET_ARM_PROCESS_ALREADY_RUNNING); 548 cm->result_cont (cm->cont_cls, cm->h, GNUNET_ARM_REQUEST_SENT_OK, "arm", GNUNET_ARM_RESULT_IS_STARTED_ALREADY);
332 GNUNET_free (pos); 549 }
550 if (GNUNET_NO == test_is_active)
551 {
552 /* User disconnected & destroyed ARM handle in the middle of
553 * the service test, so we kept the handle around until now.
554 */
555 GNUNET_CONFIGURATION_destroy (cm->h->cfg);
556 GNUNET_free (cm->h);
557 }
558 if ((0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE)) ||
559 (GNUNET_NO == test_is_active))
560 {
561 GNUNET_free (cm);
333 return; 562 return;
334 } 563 }
564 cm->h->service_test_is_active = GNUNET_NO;
335 LOG (GNUNET_ERROR_TYPE_DEBUG, 565 LOG (GNUNET_ERROR_TYPE_DEBUG,
336 "Looks like `%s' is not running, will start it.\n", 566 "Looks like `%s' is not running, will start it.\n",
337 "gnunet-service-arm"); 567 "gnunet-service-arm");
338 if (GNUNET_OK != 568 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (
339 GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "PREFIX", 569 cm->h->cfg, "arm", "PREFIX", &loprefix))
340 &loprefix))
341 loprefix = GNUNET_strdup (""); 570 loprefix = GNUNET_strdup ("");
342 if (GNUNET_OK != 571 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (
343 GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "OPTIONS", 572 cm->h->cfg, "arm", "OPTIONS", &lopostfix))
344 &lopostfix))
345 lopostfix = GNUNET_strdup (""); 573 lopostfix = GNUNET_strdup ("");
346 if (GNUNET_OK != 574 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (
347 GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "BINARY", 575 cm->h->cfg, "arm", "BINARY", &cbinary))
348 &cbinary))
349 { 576 {
350 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, 577 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, "arm", "BINARY");
351 "arm", "BINARY"); 578 if (cm->result_cont)
352 if (pos->callback != NULL) 579 cm->result_cont (cm->cont_cls, cm->h, GNUNET_ARM_REQUEST_SENT_OK, "arm", GNUNET_ARM_RESULT_IS_NOT_KNOWN);
353 pos->callback (pos->cls, GNUNET_ARM_PROCESS_UNKNOWN); 580 GNUNET_free (cm);
354 GNUNET_free (pos);
355 GNUNET_free (loprefix); 581 GNUNET_free (loprefix);
356 GNUNET_free (lopostfix); 582 GNUNET_free (lopostfix);
357 return; 583 return;
358 } 584 }
359 if (GNUNET_OK != 585 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (
360 GNUNET_CONFIGURATION_get_value_filename (pos->h->cfg, "arm", "CONFIG", 586 cm->h->cfg, "arm", "CONFIG", &config))
361 &config))
362 config = NULL; 587 config = NULL;
363 binary = GNUNET_OS_get_libexec_binary_path (cbinary); 588 binary = GNUNET_OS_get_libexec_binary_path (cbinary);
364 GNUNET_free (cbinary); 589 GNUNET_free (cbinary);
365 if ((GNUNET_YES == 590 if ((GNUNET_YES == GNUNET_CONFIGURATION_have_value (
366 GNUNET_CONFIGURATION_have_value (pos->h->cfg, "TESTING", "WEAKRANDOM")) 591 cm->h->cfg, "TESTING", "WEAKRANDOM")) &&
367 && (GNUNET_YES == 592 (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (
368 GNUNET_CONFIGURATION_get_value_yesno (pos->h->cfg, "TESTING", 593 cm->h->cfg, "TESTING", "WEAKRANDOM")) &&
369 "WEAKRANDOM")) 594 (GNUNET_NO == GNUNET_CONFIGURATION_have_value (
370 && (GNUNET_NO == 595 cm->h->cfg, "TESTING", "HOSTFILE")))
371 GNUNET_CONFIGURATION_have_value (pos->h->cfg, "TESTING",
372 "HOSTFILE")))
373 { 596 {
374 /* Means we are ONLY running locally */ 597 /* Means we are ONLY running locally */
375 /* we're clearly running a test, don't daemonize */ 598 /* we're clearly running a test, don't daemonize */
376 if (NULL == config) 599 if (NULL == config)
377 proc = do_start_process (GNUNET_NO, pos->std_inheritance, 600 proc = do_start_process (GNUNET_NO, cm->std_inheritance,
378 NULL, loprefix, binary, 601 NULL, loprefix, binary,
379 /* no daemonization! */ 602 /* no daemonization! */
380 lopostfix, NULL); 603 lopostfix, NULL);
381 else 604 else
382 proc = do_start_process (GNUNET_NO, pos->std_inheritance, 605 proc = do_start_process (GNUNET_NO, cm->std_inheritance,
383 NULL, loprefix, binary, "-c", config, 606 NULL, loprefix, binary, "-c", config,
384 /* no daemonization! */ 607 /* no daemonization! */
385 lopostfix, NULL); 608 lopostfix, NULL);
@@ -387,11 +610,11 @@ arm_service_report (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
387 else 610 else
388 { 611 {
389 if (NULL == config) 612 if (NULL == config)
390 proc = do_start_process (GNUNET_NO, pos->std_inheritance, 613 proc = do_start_process (GNUNET_NO, cm->std_inheritance,
391 NULL, loprefix, binary, 614 NULL, loprefix, binary,
392 "-d", lopostfix, NULL); 615 "-d", lopostfix, NULL);
393 else 616 else
394 proc = do_start_process (GNUNET_NO, pos->std_inheritance, 617 proc = do_start_process (GNUNET_NO, cm->std_inheritance,
395 NULL, loprefix, binary, "-c", config, 618 NULL, loprefix, binary, "-c", config,
396 "-d", lopostfix, NULL); 619 "-d", lopostfix, NULL);
397 } 620 }
@@ -401,52 +624,18 @@ arm_service_report (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
401 GNUNET_free (lopostfix); 624 GNUNET_free (lopostfix);
402 if (NULL == proc) 625 if (NULL == proc)
403 { 626 {
404 if (pos->callback != NULL) 627 if (cm->result_cont)
405 pos->callback (pos->cls, GNUNET_ARM_PROCESS_FAILURE); 628 cm->result_cont (cm->cont_cls, cm->h, GNUNET_ARM_REQUEST_SENT_OK, "arm",
406 GNUNET_free (pos); 629 GNUNET_ARM_RESULT_START_FAILED);
630 GNUNET_free (cm);
407 return; 631 return;
408 } 632 }
409 if (pos->callback != NULL) 633 if (cm->result_cont)
410 pos->callback (pos->cls, GNUNET_ARM_PROCESS_STARTING); 634 cm->result_cont (cm->cont_cls, cm->h, GNUNET_ARM_REQUEST_SENT_OK, "arm",
635 GNUNET_ARM_RESULT_STARTING);
411 GNUNET_OS_process_destroy (proc); 636 GNUNET_OS_process_destroy (proc);
412 GNUNET_free (pos); 637 reconnect_arm (cm->h);
413} 638 GNUNET_free (cm);
414
415
416/**
417 * Process a response from ARM to a request for a change in service
418 * status.
419 *
420 * @param cls the request context
421 * @param msg the response
422 */
423static void
424handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
425{
426 struct RequestContext *sc = cls;
427 const struct GNUNET_ARM_ResultMessage *res;
428 enum GNUNET_ARM_ProcessStatus status;
429
430 if ((msg == NULL) ||
431 (ntohs (msg->size) != sizeof (struct GNUNET_ARM_ResultMessage)))
432 {
433 GNUNET_break (0);
434 GNUNET_CLIENT_disconnect (sc->h->client);
435 sc->h->client = GNUNET_CLIENT_connect ("arm", sc->h->cfg);
436 GNUNET_assert (NULL != sc->h->client);
437 if (sc->callback != NULL)
438 sc->callback (sc->cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR);
439 GNUNET_free (sc);
440 return;
441 }
442 res = (const struct GNUNET_ARM_ResultMessage *) msg;
443 LOG (GNUNET_ERROR_TYPE_DEBUG,
444 "Received response from ARM for service `%s': %u\n",
445 (const char *) &sc[1], ntohs (msg->type));
446 status = (enum GNUNET_ARM_ProcessStatus) ntohl (res->status);
447 if (sc->callback != NULL)
448 sc->callback (sc->cls, status);
449 GNUNET_free (sc);
450} 639}
451 640
452 641
@@ -462,74 +651,65 @@ handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
462 */ 651 */
463static void 652static void
464change_service (struct GNUNET_ARM_Handle *h, const char *service_name, 653change_service (struct GNUNET_ARM_Handle *h, const char *service_name,
465 struct GNUNET_TIME_Relative timeout, GNUNET_ARM_Callback cb, 654 struct GNUNET_TIME_Relative timeout, GNUNET_ARM_ResultCallback cb,
466 void *cb_cls, uint16_t type) 655 void *cb_cls, uint16_t type)
467{ 656{
468 struct RequestContext *sctx; 657 struct ARMControlMessage *cm;
469 size_t slen; 658 size_t slen;
470 struct GNUNET_MessageHeader *msg; 659 struct GNUNET_ARM_Message *msg;
471 660
472 slen = strlen (service_name) + 1; 661 slen = strlen (service_name) + 1;
473 if (slen + sizeof (struct GNUNET_MessageHeader) >= 662 if (slen + sizeof (struct GNUNET_ARM_Message) >=
474 GNUNET_SERVER_MAX_MESSAGE_SIZE) 663 GNUNET_SERVER_MAX_MESSAGE_SIZE)
475 { 664 {
476 GNUNET_break (0); 665 GNUNET_break (0);
477 if (cb != NULL) 666 if (cb != NULL)
478 cb (cb_cls, GNUNET_NO); 667 cb (cb_cls, h, GNUNET_ARM_REQUEST_TOO_LONG, NULL, 0);
479 return; 668 return;
480 } 669 }
481 LOG (GNUNET_ERROR_TYPE_DEBUG, 670 LOG (GNUNET_ERROR_TYPE_DEBUG, "Requesting %s of service `%s'.\n",
482 (type == 671 (GNUNET_MESSAGE_TYPE_ARM_START == type) ? "start" : "termination",
483 GNUNET_MESSAGE_TYPE_ARM_START) ? 672 service_name);
484 "Requesting start of service `%s'.\n" : 673 cm = GNUNET_malloc (sizeof (struct ARMControlMessage) + slen);
485 "Requesting termination of service `%s'.\n", service_name); 674 cm->h = h;
486 sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen); 675 cm->result_cont = cb;
487 sctx->h = h; 676 cm->cont_cls = cb_cls;
488 sctx->callback = cb; 677 cm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
489 sctx->cls = cb_cls; 678 memcpy (&cm[1], service_name, slen);
490 sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout); 679 msg = GNUNET_malloc (sizeof (struct GNUNET_ARM_Message) + slen);
491 sctx->type = type; 680 msg->header.size = htons (sizeof (struct GNUNET_ARM_Message) + slen);
492 memcpy (&sctx[1], service_name, slen); 681 msg->header.type = htons (type);
493 msg = GNUNET_malloc (sizeof (struct GNUNET_MessageHeader) + slen);
494 msg->size = htons (sizeof (struct GNUNET_MessageHeader) + slen);
495 msg->type = htons (sctx->type);
496 memcpy (&msg[1], service_name, slen); 682 memcpy (&msg[1], service_name, slen);
497 if (GNUNET_OK != 683 cm->msg = msg;
498 GNUNET_CLIENT_transmit_and_get_response (sctx->h->client, msg, 684 LOG (GNUNET_ERROR_TYPE_DEBUG,
499 GNUNET_TIME_absolute_get_remaining 685 "Inserting a control message into the queue. Timeout is %llu\n",
500 (sctx->timeout), GNUNET_YES, 686 GNUNET_TIME_absolute_get_remaining (cm->timeout).rel_value);
501 &handle_response, sctx)) 687 GNUNET_CONTAINER_DLL_insert_tail (h->control_pending_head,
502 { 688 h->control_pending_tail, cm);
503 GNUNET_break (0); 689 cm->timeout_task_id =
504 if (cb != NULL) 690 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
505 cb (cb_cls, GNUNET_SYSERR); 691 (cm->timeout), &control_message_timeout, cm);
506 GNUNET_free (sctx); 692 trigger_next_request (h, GNUNET_NO);
507 GNUNET_free (msg);
508 return;
509 }
510 GNUNET_free (msg);
511} 693}
512 694
513 695
514/** 696/**
515 * Start a service. 697 * Request for a service to be started.
516 * 698 *
517 * @param h handle to ARM 699 * @param h handle to ARM
518 * @param service_name name of the service 700 * @param service_name name of the service
519 * @param std_inheritance inheritance of std streams 701 * @param std_inheritance inheritance of std streams
520 * @param timeout how long to wait before failing for good 702 * @param timeout how long to wait before failing for good
521 * @param cb callback to invoke when service is ready 703 * @param cont callback to invoke after request is sent or not sent
522 * @param cb_cls closure for callback 704 * @param cont_cls closure for callback
523 */ 705 */
524void 706void
525GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h, 707GNUNET_ARM_request_service_start (struct GNUNET_ARM_Handle *h,
526 const char *service_name, 708 const char *service_name, enum GNUNET_OS_InheritStdioFlags std_inheritance,
527 enum GNUNET_OS_InheritStdioFlags std_inheritance, 709 struct GNUNET_TIME_Relative timeout, GNUNET_ARM_ResultCallback cont,
528 struct GNUNET_TIME_Relative timeout, 710 void *cont_cls)
529 GNUNET_ARM_Callback cb, void *cb_cls)
530{ 711{
531 struct RequestContext *sctx; 712 struct ARMControlMessage *cm;
532 struct GNUNET_CLIENT_Connection *client;
533 size_t slen; 713 size_t slen;
534 714
535 LOG (GNUNET_ERROR_TYPE_DEBUG, 715 LOG (GNUNET_ERROR_TYPE_DEBUG,
@@ -537,258 +717,266 @@ GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h,
537 GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_NO)); 717 GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_NO));
538 if (0 == strcasecmp ("arm", service_name)) 718 if (0 == strcasecmp ("arm", service_name))
539 { 719 {
540 slen = strlen ("arm") + 1; 720 /* Possible cases:
541 sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen); 721 * 1) We're connected to ARM already. Invoke the callback immediately.
542 sctx->h = h; 722 * 2) We're not connected to ARM.
543 sctx->callback = cb; 723 * Cancel any reconnection attempts temporarily, then perform
544 sctx->cls = cb_cls; 724 * a service test.
545 sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout); 725 */
546 sctx->std_inheritance = std_inheritance; 726 if (GNUNET_NO == h->currently_down)
547 memcpy (&sctx[1], service_name, slen); 727 {
548 GNUNET_CLIENT_service_test ("arm", h->cfg, timeout, &arm_service_report, 728 LOG (GNUNET_ERROR_TYPE_DEBUG, "ARM is already running\n");
549 sctx); 729 if (NULL != cont)
550 return; 730 cont (cont_cls, h, GNUNET_ARM_REQUEST_SENT_OK, "arm", GNUNET_ARM_RESULT_IS_STARTED_ALREADY);
551 } 731 }
552 if (NULL == h->client) 732 else if (GNUNET_NO == h->service_test_is_active)
553 {
554 client = GNUNET_CLIENT_connect ("arm", h->cfg);
555 if (client == NULL)
556 { 733 {
734 if (NULL != h->cth)
735 {
736 GNUNET_CLIENT_notify_transmit_ready_cancel (h->cth);
737 h->cth = NULL;
738 }
739 if (NULL != h->client)
740 {
741 GNUNET_CLIENT_disconnect (h->client);
742 h->client = NULL;
743 }
744 if (GNUNET_SCHEDULER_NO_TASK != h->reconnect_task)
745 {
746 GNUNET_SCHEDULER_cancel (h->reconnect_task);
747 h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
748 }
749
557 LOG (GNUNET_ERROR_TYPE_DEBUG, 750 LOG (GNUNET_ERROR_TYPE_DEBUG,
558 "arm_api, GNUNET_CLIENT_connect returned NULL\n"); 751 "Not connected to ARM, will do a service test\n");
559 cb (cb_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR); 752
560 return; 753 slen = strlen ("arm") + 1;
754 cm = GNUNET_malloc (sizeof (struct ARMControlMessage) + slen);
755 cm->h = h;
756 cm->result_cont = cont;
757 cm->cont_cls = cont_cls;
758 cm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
759 cm->std_inheritance = std_inheritance;
760 memcpy (&cm[1], service_name, slen);
761 h->service_test_is_active = GNUNET_YES;
762 GNUNET_CLIENT_service_test ("arm", h->cfg, timeout, &arm_service_report,
763 cm);
561 } 764 }
562 LOG (GNUNET_ERROR_TYPE_DEBUG, 765 else
563 "arm_api, GNUNET_CLIENT_connect returned non-NULL\n"); 766 {
564 h->client = client; 767 /* Service test is already running - tell user to chill out and try
768 * again later.
769 */
770 LOG (GNUNET_ERROR_TYPE_DEBUG, "Service test is already in progress, we're busy\n");
771 if (NULL != cont)
772 cont (cont_cls, h, GNUNET_ARM_REQUEST_BUSY, NULL, 0);
773 }
774 return;
565 } 775 }
566 LOG (GNUNET_ERROR_TYPE_DEBUG, "arm_api, h->client non-NULL\n"); 776 change_service (h, service_name, timeout, cont, cont_cls,
567 change_service (h, service_name, timeout, cb, cb_cls,
568 GNUNET_MESSAGE_TYPE_ARM_START); 777 GNUNET_MESSAGE_TYPE_ARM_START);
569} 778}
570 779
571 780
572/** 781/**
573 * Callback from the arm stop service call, indicates that the arm service 782 * Request a service to be stopped.
574 * is well and truly dead, won't die, or an error occurred. 783 * Stopping arm itself will not invalidate its handle, and
575 * 784 * ARM API will try to restore connection to the ARM service,
576 * @param cls closure for the callback 785 * even if ARM connection was lost because you asked for ARM to be stopped.
577 * @param reason reason for callback 786 * Call GNUNET_ARM_disconnect () to free the handle and prevent
578 */ 787 * further connection attempts.
579static void
580arm_shutdown_callback (void *cls, enum GNUNET_ARM_ProcessStatus reason)
581{
582 struct ARM_ShutdownContext *arm_shutdown_ctx = cls;
583
584 if (arm_shutdown_ctx->cb != NULL)
585 arm_shutdown_ctx->cb (arm_shutdown_ctx->cb_cls, reason);
586 GNUNET_free (arm_shutdown_ctx);
587}
588
589
590/**
591 * Stop a service.
592 * 788 *
593 * @param h handle to ARM 789 * @param h handle to ARM
594 * @param service_name name of the service 790 * @param service_name name of the service
595 * @param timeout how long to wait before failing for good 791 * @param timeout how long to wait before failing for good
596 * @param cb callback to invoke when service is ready 792 * @param cont callback to invoke after request is sent or is not sent
597 * @param cb_cls closure for callback 793 * @param cont_cls closure for callback
598 */ 794 */
599void 795void
600GNUNET_ARM_stop_service (struct GNUNET_ARM_Handle *h, 796GNUNET_ARM_request_service_stop (struct GNUNET_ARM_Handle *h,
601 const char *service_name, 797 const char *service_name, struct GNUNET_TIME_Relative timeout,
602 struct GNUNET_TIME_Relative timeout, 798 GNUNET_ARM_ResultCallback cont, void *cont_cls)
603 GNUNET_ARM_Callback cb, void *cb_cls)
604{ 799{
605 struct ARM_ShutdownContext *arm_shutdown_ctx;
606 struct GNUNET_CLIENT_Connection *client;
607
608 LOG (GNUNET_ERROR_TYPE_DEBUG, 800 LOG (GNUNET_ERROR_TYPE_DEBUG,
609 "Stopping service `%s' within %s\n", 801 "Stopping service `%s' within %s\n",
610 service_name, 802 service_name,
611 GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_NO)); 803 GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_NO));
612 if (h->client == NULL) 804 change_service (h, service_name, timeout, cont, cont_cls,
613 {
614 client = GNUNET_CLIENT_connect ("arm", h->cfg);
615 if (client == NULL)
616 {
617 cb (cb_cls, GNUNET_SYSERR);
618 return;
619 }
620 h->client = client;
621 }
622 if (0 == strcasecmp ("arm", service_name))
623 {
624 arm_shutdown_ctx = GNUNET_malloc (sizeof (struct ARM_ShutdownContext));
625 arm_shutdown_ctx->cb = cb;
626 arm_shutdown_ctx->cb_cls = cb_cls;
627 arm_service_shutdown (h->client, timeout, &arm_shutdown_callback,
628 arm_shutdown_ctx);
629 h->client = NULL;
630 return;
631 }
632 change_service (h, service_name, timeout, cb, cb_cls,
633 GNUNET_MESSAGE_TYPE_ARM_STOP); 805 GNUNET_MESSAGE_TYPE_ARM_STOP);
634} 806}
635 807
636 808
637/** 809/**
638 * Internal state for a list request with ARM. 810 * Request a list of running services.
811 *
812 * @param h handle to ARM
813 * @param timeout how long to wait before failing for good
814 * @param cont callback to invoke after request is sent or is not sent
815 * @param cont_cls closure for callback
639 */ 816 */
640struct ListRequestContext 817void
818GNUNET_ARM_request_service_list (struct GNUNET_ARM_Handle *h,
819 struct GNUNET_TIME_Relative timeout,
820 GNUNET_ARM_ServiceListCallback cont, void *cont_cls)
641{ 821{
822 struct ARMControlMessage *cm;
823 struct GNUNET_ARM_Message *msg;
642 824
643 /** 825 LOG (GNUNET_ERROR_TYPE_DEBUG,
644 * Pointer to our handle with ARM. 826 "Requesting LIST from ARM service with timeout: %s\n",
645 */ 827 GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_YES));
646 struct GNUNET_ARM_Handle *h;
647
648 /**
649 * Function to call with a status code for the requested operation.
650 */
651 GNUNET_ARM_List_Callback callback;
652 828
653 /** 829 cm = GNUNET_malloc (sizeof (struct ARMControlMessage));
654 * Closure for "callback". 830 cm->h = h;
655 */ 831 cm->list_cont = cont;
656 void *cls; 832 cm->cont_cls = cont_cls;
833 cm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
834 msg = GNUNET_malloc (sizeof (struct GNUNET_ARM_Message));
835 msg->header.size = htons (sizeof (struct GNUNET_ARM_Message));
836 msg->header.type = htons (GNUNET_MESSAGE_TYPE_ARM_LIST);
837 cm->msg = msg;
838 GNUNET_CONTAINER_DLL_insert_tail (h->control_pending_head,
839 h->control_pending_tail, cm);
840 cm->timeout_task_id =
841 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
842 (cm->timeout), &control_message_timeout, cm);
843 trigger_next_request (h, GNUNET_NO);
844}
657 845
658 /** 846static struct ARMControlMessage *
659 * Timeout for the operation. 847find_cm_by_id (struct GNUNET_ARM_Handle *h, uint64_t id)
660 */ 848{
661 struct GNUNET_TIME_Absolute timeout; 849 struct ARMControlMessage *result;
662}; 850 for (result = h->control_sent_head; result; result = result->next)
851 if (id == result->msg->request_id)
852 return result;
853 return NULL;
854}
663 855
664 856
665/** 857/**
666 * Process a response from ARM for the list request. 858 * Handler for ARM replies.
667 * 859 *
668 * @param cls the list request context 860 * @param cls our "struct GNUNET_ARM_Handle"
669 * @param msg the response 861 * @param msg the message received from the arm service
670 */ 862 */
671static void 863static void
672handle_list_response (void *cls, const struct GNUNET_MessageHeader *msg) 864client_notify_handler (void *cls, const struct GNUNET_MessageHeader *msg)
673{ 865{
674 struct ListRequestContext *sc = cls; 866 struct GNUNET_ARM_Handle *h = cls;
675 const struct GNUNET_ARM_ListResultMessage *res; 867
868 uint16_t msize;
869 uint64_t id;
870 unsigned char fail;
871
872 const struct GNUNET_ARM_Message *arm_msg;
873 const struct GNUNET_ARM_ResultMessage *res;
874 const struct GNUNET_ARM_ListResultMessage *lres;
875 enum GNUNET_ARM_Result result;
876 struct ARMControlMessage *cm;
877
676 const char *pos; 878 const char *pos;
677 uint16_t size_check; 879 uint16_t size_check;
678 uint16_t rcount; 880 uint16_t rcount;
679 uint16_t msize; 881
680
681 if (NULL == msg) 882 if (NULL == msg)
682 { 883 {
683 GNUNET_break (0); 884 LOG (GNUNET_ERROR_TYPE_INFO,
684 GNUNET_CLIENT_disconnect (sc->h->client); 885 _("Client was disconnected from arm service, trying to reconnect.\n"));
685 sc->h->client = GNUNET_CLIENT_connect ("arm", sc->h->cfg); 886 reconnect_arm_later (h);
686 GNUNET_assert (NULL != sc->h->client);
687 if (sc->callback != NULL)
688 sc->callback (sc->cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR, 0, NULL);
689 GNUNET_free (sc);
690 return; 887 return;
691 } 888 }
692
693 if (NULL == sc->callback)
694 {
695 GNUNET_break (0);
696 GNUNET_free (sc);
697 return;
698 }
699 msize = ntohs (msg->size); 889 msize = ntohs (msg->size);
700 if ( (msize < sizeof ( struct GNUNET_ARM_ListResultMessage)) || 890 LOG (GNUNET_ERROR_TYPE_DEBUG,
701 (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT) ) 891 "Processing message of type %u and size %u from arm service\n",
892 ntohs (msg->type), msize);
893 if (msize < sizeof (struct GNUNET_ARM_Message))
702 { 894 {
703 GNUNET_break (0); 895 GNUNET_break (0);
704 sc->callback (sc->cls, GNUNET_NO, 0, NULL); 896 reconnect_arm_later (h);
705 GNUNET_free (sc);
706 return; 897 return;
707 } 898 }
708 size_check = 0; 899 arm_msg = (const struct GNUNET_ARM_Message *) msg;
709 res = (const struct GNUNET_ARM_ListResultMessage *) msg; 900 id = GNUNET_ntohll (arm_msg->request_id);
710 rcount = ntohs (res->count); 901 cm = find_cm_by_id (h, id);
902 if (NULL == cm)
711 { 903 {
712 const char *list[rcount]; 904 LOG (GNUNET_ERROR_TYPE_DEBUG, "Message with unknown id %llu\n", id);
713 unsigned int i; 905 return;
714
715 pos = (const char *)&res[1];
716 for (i=0; i<rcount; i++)
717 {
718 const char *end = memchr (pos, 0, msize - size_check);
719 if (NULL == end)
720 {
721 GNUNET_break (0);
722 sc->callback (sc->cls, GNUNET_NO, 0, NULL);
723 GNUNET_free (sc);
724 return;
725 }
726 list[i] = pos;
727 size_check += (end - pos) + 1;
728 pos = end + 1;
729 }
730 sc->callback (sc->cls, GNUNET_YES, rcount, list);
731 } 906 }
732 GNUNET_free (sc);
733}
734
735
736/**
737 * List all running services.
738 *
739 * @param h handle to ARM
740 * @param timeout how long to wait before failing for good
741 * @param cb callback to invoke when service is ready
742 * @param cb_cls closure for callback
743 */
744void
745GNUNET_ARM_list_running_services (struct GNUNET_ARM_Handle *h,
746 struct GNUNET_TIME_Relative timeout,
747 GNUNET_ARM_List_Callback cb, void *cb_cls)
748{
749 struct ListRequestContext *sctx;
750 struct GNUNET_MessageHeader msg;
751 struct GNUNET_CLIENT_Connection *client;
752 907
753 if (h->client == NULL) 908 fail = GNUNET_NO;
909 switch (ntohs (msg->type))
754 { 910 {
755 client = GNUNET_CLIENT_connect ("arm", h->cfg); 911 case GNUNET_MESSAGE_TYPE_ARM_RESULT:
756 if (client == NULL) 912 if (msize < sizeof (struct GNUNET_ARM_ResultMessage))
913 {
914 GNUNET_assert (0);
915 fail = GNUNET_YES;
916 break;
917 }
918 res = (const struct GNUNET_ARM_ResultMessage *) msg;
919 LOG (GNUNET_ERROR_TYPE_DEBUG,
920 "Received response from ARM for service `%s': %u\n",
921 (const char *) &cm->msg[1], ntohs (msg->type));
922 result = (enum GNUNET_ARM_Result) ntohl (res->result);
923 if (NULL != cm->result_cont)
924 cm->result_cont (cm->cont_cls, h, GNUNET_ARM_REQUEST_SENT_OK, (const char *) &cm->msg[1], result);
925 break;
926 case GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT:
927 if (msize < sizeof (struct GNUNET_ARM_ListResultMessage))
757 { 928 {
758 GNUNET_break (0); 929 GNUNET_break (0);
759 cb (cb_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR, 0, NULL); 930 fail = GNUNET_YES;
760 return; 931 return;
761 } 932 }
762 h->client = client; 933 else
763 } 934 {
764 935 size_check = 0;
765 sctx = GNUNET_malloc (sizeof (struct RequestContext)); 936 lres = (const struct GNUNET_ARM_ListResultMessage *) msg;
766 sctx->h = h; 937 rcount = ntohs (lres->count);
767 sctx->callback = cb; 938 {
768 sctx->cls = cb_cls; 939 const char *list[rcount];
769 sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout); 940 unsigned int i;
770 msg.size = htons (sizeof (struct GNUNET_MessageHeader)); 941
771 msg.type = htons (GNUNET_MESSAGE_TYPE_ARM_LIST); 942 pos = (const char *)&lres[1];
772 943 for (i = 0; i < rcount; i++)
773 LOG (GNUNET_ERROR_TYPE_DEBUG, 944 {
774 "Requesting LIST from ARM service with timeout: %s\n", 945 const char *end = memchr (pos, 0, msize - size_check);
775 GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_YES)); 946 if (NULL == end)
776 947 {
777 if (GNUNET_OK != 948 GNUNET_break (0);
778 GNUNET_CLIENT_transmit_and_get_response (sctx->h->client, 949 fail = GNUNET_YES;
779 &msg, 950 break;
780 GNUNET_TIME_absolute_get_remaining 951 }
781 (sctx->timeout), 952 list[i] = pos;
782 GNUNET_YES, 953 size_check += (end - pos) + 1;
783 &handle_list_response, 954 pos = end + 1;
784 sctx)) 955 }
785 { 956 if (GNUNET_YES == fail)
786 GNUNET_break (0); 957 break;
787 if (cb != NULL) 958 if (NULL != cm->list_cont)
788 cb (cb_cls, GNUNET_SYSERR, 0, NULL); 959 cm->list_cont (cm->cont_cls, h, GNUNET_ARM_REQUEST_SENT_OK, rcount, list);
789 GNUNET_free (sctx); 960 }
961 }
962 break;
963 default:
964 fail = GNUNET_YES;
790 return; 965 return;
791 } 966 }
967
968 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cm->timeout_task_id);
969 GNUNET_SCHEDULER_cancel (cm->timeout_task_id);
970 GNUNET_CONTAINER_DLL_remove (h->control_sent_head,
971 h->control_sent_tail, cm);
972 GNUNET_free (cm->msg);
973 GNUNET_free (cm);
974
975 if (GNUNET_YES == fail)
976 reconnect_arm_later (h);
977 else
978 GNUNET_CLIENT_receive (h->client, &client_notify_handler, h,
979 GNUNET_TIME_UNIT_FOREVER_REL);
792} 980}
793 981
794/* end of arm_api.c */ 982/* end of arm_api.c */