aboutsummaryrefslogtreecommitdiff
path: root/src/arm/arm_api.c
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2016-06-23 14:22:34 +0000
committerChristian Grothoff <christian@grothoff.org>2016-06-23 14:22:34 +0000
commit5742938289524f4c5fba7883742e4dd69cccf11d (patch)
treee16fea8b9d778f9825f897237b0c1880305776a0 /src/arm/arm_api.c
parentf3edb5a8d6ba6f43f5df18f2e98bc1dae90c9d7a (diff)
downloadgnunet-5742938289524f4c5fba7883742e4dd69cccf11d.tar.gz
gnunet-5742938289524f4c5fba7883742e4dd69cccf11d.zip
refactoring ARM api to use new MQ
Diffstat (limited to 'src/arm/arm_api.c')
-rw-r--r--src/arm/arm_api.c1219
1 files changed, 505 insertions, 714 deletions
diff --git a/src/arm/arm_api.c b/src/arm/arm_api.c
index a89d423ec..ed36c61cd 100644
--- a/src/arm/arm_api.c
+++ b/src/arm/arm_api.c
@@ -32,151 +32,131 @@
32 32
33#define LOG(kind,...) GNUNET_log_from (kind, "arm-api",__VA_ARGS__) 33#define LOG(kind,...) GNUNET_log_from (kind, "arm-api",__VA_ARGS__)
34 34
35
35/** 36/**
36 * Handle for interacting with ARM. 37 * Entry in a doubly-linked list of operations awaiting for replies
38 * (in-order) from the ARM service.
37 */ 39 */
38struct GNUNET_ARM_Handle 40struct GNUNET_ARM_Operation
39{ 41{
40 /** 42 /**
41 * Our control connection to the ARM service. 43 * This is a doubly-linked list.
42 */
43 struct GNUNET_CLIENT_Connection *client;
44
45 /**
46 * The configuration that we are using.
47 */
48 struct GNUNET_CONFIGURATION_Handle *cfg;
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 */ 44 */
68 struct ARMControlMessage *control_sent_head; 45 struct GNUNET_ARM_Operation *next;
69 46
70 /** 47 /**
71 * Tail of doubly-linked list of sent requests. 48 * This is a doubly-linked list.
72 */ 49 */
73 struct ARMControlMessage *control_sent_tail; 50 struct GNUNET_ARM_Operation *prev;
74 51
75 /** 52 /**
76 * Callback to invoke on connection/disconnection. 53 * ARM handle.
77 */ 54 */
78 GNUNET_ARM_ConnectionStatusCallback conn_status; 55 struct GNUNET_ARM_Handle *h;
79 56
80 /** 57 /**
81 * Closure for conn_status. 58 * Callback for service state change requests.
82 */ 59 */
83 void *conn_status_cls; 60 GNUNET_ARM_ResultCallback result_cont;
84 61
85 /** 62 /**
86 * ARM control message for the 'arm_termination_handler' 63 * Callback for service list requests.
87 * with the continuation to call once the ARM shutdown is done.
88 */ 64 */
89 struct ARMControlMessage *thm; 65 GNUNET_ARM_ServiceListCallback list_cont;
90 66
91 /** 67 /**
92 * ID of the reconnect task (if any). 68 * Closure for @e result_cont or @e list_cont.
93 */ 69 */
94 struct GNUNET_SCHEDULER_Task *reconnect_task; 70 void *cont_cls;
95 71
96 /** 72 /**
97 * Current delay we use for re-trying to connect to core. 73 * Task for async completion.
98 */ 74 */
99 struct GNUNET_TIME_Relative retry_backoff; 75 struct GNUNET_SCHEDULER_Task *async;
100 76
101 /** 77 /**
102 * Counter for request identifiers 78 * Unique ID for the request.
103 */ 79 */
104 uint64_t request_id_counter; 80 uint64_t id;
105 81
106 /** 82 /**
107 * Are we currently disconnected and hence unable to send? 83 * Result of this operation for #notify_starting().
108 */ 84 */
109 unsigned char currently_down; 85 enum GNUNET_ARM_Result starting_ret;
110 86
111 /** 87 /**
112 * #GNUNET_YES if we're running a service test. 88 * Is this an operation to stop the ARM service?
113 */ 89 */
114 unsigned char service_test_is_active; 90 int is_arm_stop;
115}; 91};
116 92
117 93
118/** 94/**
119 * Entry in a doubly-linked list of control messages to be transmitted 95 * Handle for interacting with ARM.
120 * to the arm service.
121 *
122 * The actual message is allocated at the end of this struct.
123 */ 96 */
124struct ARMControlMessage 97struct GNUNET_ARM_Handle
125{ 98{
126 /** 99 /**
127 * This is a doubly-linked list. 100 * Our connection to the ARM service.
128 */ 101 */
129 struct ARMControlMessage *next; 102 struct GNUNET_MQ_Handle *mq;
130 103
131 /** 104 /**
132 * This is a doubly-linked list. 105 * The configuration that we are using.
133 */ 106 */
134 struct ARMControlMessage *prev; 107 const struct GNUNET_CONFIGURATION_Handle *cfg;
135 108
136 /** 109 /**
137 * ARM handle. 110 * Head of doubly-linked list of pending operations.
138 */ 111 */
139 struct GNUNET_ARM_Handle *h; 112 struct GNUNET_ARM_Operation *operation_pending_head;
140 113
141 /** 114 /**
142 * Message to send. 115 * Tail of doubly-linked list of pending operations.
143 */ 116 */
144 struct GNUNET_ARM_Message *msg; 117 struct GNUNET_ARM_Operation *operation_pending_tail;
145 118
146 /** 119 /**
147 * Callback for service state change requests. 120 * Callback to invoke on connection/disconnection.
148 */ 121 */
149 GNUNET_ARM_ResultCallback result_cont; 122 GNUNET_ARM_ConnectionStatusCallback conn_status;
150 123
151 /** 124 /**
152 * Callback for service list requests. 125 * Closure for @e conn_status.
153 */ 126 */
154 GNUNET_ARM_ServiceListCallback list_cont; 127 void *conn_status_cls;
155 128
156 /** 129 /**
157 * Closure for @e result_cont or @e list_cont. 130 * ARM operation where the goal is to wait for ARM shutdown to
131 * complete. This operation is special in that it waits for an
132 * error on the @e mq. So we complete it by calling the
133 * continuation in the #mq_error_handler(). Note that the operation
134 * is no longer in the @e operation_pending_head DLL once it is
135 * referenced from this field.
158 */ 136 */
159 void *cont_cls; 137 struct GNUNET_ARM_Operation *thm;
160 138
161 /** 139 /**
162 * Timeout for the operation. 140 * ID of the reconnect task (if any).
163 */ 141 */
164 struct GNUNET_TIME_Absolute timeout; 142 struct GNUNET_SCHEDULER_Task *reconnect_task;
165 143
166 /** 144 /**
167 * Task to run when request times out. 145 * Current delay we use for re-trying to connect to core.
168 */ 146 */
169 struct GNUNET_SCHEDULER_Task *timeout_task_id; 147 struct GNUNET_TIME_Relative retry_backoff;
170 148
171 /** 149 /**
172 * Flags for passing std descriptors to ARM (when starting ARM). 150 * Counter for request identifiers. They are used to match replies
151 * from ARM to operations in the @e operation_pending_head DLL.
173 */ 152 */
174 enum GNUNET_OS_InheritStdioFlags std_inheritance; 153 uint64_t request_id_counter;
175 154
176 /** 155 /**
177 * Type of the request expressed as a message type (start, stop or list). 156 * Have we detected that ARM is up?
178 */ 157 */
179 uint16_t type; 158 int currently_up;
159
180}; 160};
181 161
182 162
@@ -191,18 +171,6 @@ reconnect_arm (struct GNUNET_ARM_Handle *h);
191 171
192 172
193/** 173/**
194 * Check the list of pending requests, send the next
195 * one to the arm.
196 *
197 * @param h arm handle
198 * @param ignore_currently_down transmit message even if not initialized?
199 */
200static void
201trigger_next_request (struct GNUNET_ARM_Handle *h,
202 int ignore_currently_down);
203
204
205/**
206 * Task scheduled to try to re-connect to arm. 174 * Task scheduled to try to re-connect to arm.
207 * 175 *
208 * @param cls the `struct GNUNET_ARM_Handle` 176 * @param cls the `struct GNUNET_ARM_Handle`
@@ -213,8 +181,6 @@ reconnect_arm_task (void *cls)
213 struct GNUNET_ARM_Handle *h = cls; 181 struct GNUNET_ARM_Handle *h = cls;
214 182
215 h->reconnect_task = NULL; 183 h->reconnect_task = NULL;
216 LOG (GNUNET_ERROR_TYPE_DEBUG,
217 "Connecting to ARM service after delay\n");
218 reconnect_arm (h); 184 reconnect_arm (h);
219} 185}
220 186
@@ -228,28 +194,33 @@ reconnect_arm_task (void *cls)
228static void 194static void
229reconnect_arm_later (struct GNUNET_ARM_Handle *h) 195reconnect_arm_later (struct GNUNET_ARM_Handle *h)
230{ 196{
231 if (GNUNET_NO != h->currently_down) 197 struct GNUNET_ARM_Operation *op;
232 return; 198
233 if (NULL != h->cth) 199 if (NULL != h->mq)
234 {
235 GNUNET_CLIENT_notify_transmit_ready_cancel (h->cth);
236 h->cth = NULL;
237 }
238 if (NULL != h->client)
239 { 200 {
240 GNUNET_CLIENT_disconnect (h->client); 201 GNUNET_MQ_destroy (h->mq);
241 h->client = NULL; 202 h->mq = NULL;
242 } 203 }
243 h->currently_down = GNUNET_YES; 204 h->currently_up = GNUNET_NO;
244 GNUNET_assert (NULL == h->reconnect_task); 205 GNUNET_assert (NULL == h->reconnect_task);
245 h->reconnect_task = 206 h->reconnect_task =
246 GNUNET_SCHEDULER_add_delayed (h->retry_backoff, 207 GNUNET_SCHEDULER_add_delayed (h->retry_backoff,
247 &reconnect_arm_task, 208 &reconnect_arm_task,
248 h); 209 h);
249 /* Don't clear pending messages on disconnection, deliver them later 210 while (NULL != (op = h->operation_pending_head))
250 clear_pending_messages (h, GNUNET_ARM_REQUEST_DISCONNECTED); 211 {
251 GNUNET_assert (NULL == h->control_pending_head); 212 if (NULL != op->result_cont)
252 */ 213 op->result_cont (op->cont_cls,
214 GNUNET_ARM_REQUEST_DISCONNECTED,
215 0);
216 if (NULL != op->list_cont)
217 op->list_cont (op->cont_cls,
218 GNUNET_ARM_REQUEST_DISCONNECTED,
219 0,
220 NULL);
221 GNUNET_ARM_operation_cancel (op);
222 }
223 GNUNET_assert (NULL == h->operation_pending_head);
253 h->retry_backoff = GNUNET_TIME_STD_BACKOFF (h->retry_backoff); 224 h->retry_backoff = GNUNET_TIME_STD_BACKOFF (h->retry_backoff);
254 if (NULL != h->conn_status) 225 if (NULL != h->conn_status)
255 h->conn_status (h->conn_status_cls, 226 h->conn_status (h->conn_status_cls,
@@ -264,176 +235,50 @@ reconnect_arm_later (struct GNUNET_ARM_Handle *h)
264 * @param id unique message ID to use for the lookup 235 * @param id unique message ID to use for the lookup
265 * @return NULL if not found 236 * @return NULL if not found
266 */ 237 */
267static struct ARMControlMessage * 238static struct GNUNET_ARM_Operation *
268find_cm_by_id (struct GNUNET_ARM_Handle *h, 239find_op_by_id (struct GNUNET_ARM_Handle *h,
269 uint64_t id) 240 uint64_t id)
270{ 241{
271 struct ARMControlMessage *result; 242 struct GNUNET_ARM_Operation *result;
272 243
273 for (result = h->control_sent_head; NULL != result; result = result->next) 244 for (result = h->operation_pending_head; NULL != result; result = result->next)
274 if (id == result->msg->request_id) 245 if (id == result->id)
275 return result; 246 return result;
276 return NULL; 247 return NULL;
277} 248}
278 249
279 250
280/** 251/**
281 * Handler for ARM 'termination' reply (failure to receive).
282 *
283 * @param cls our `struct GNUNET_ARM_Handle`
284 * @param msg expected to be NULL
285 */
286static void
287arm_termination_handler (void *cls,
288 const struct GNUNET_MessageHeader *msg)
289{
290 struct GNUNET_ARM_Handle *h = cls;
291 struct ARMControlMessage *cm;
292
293 if (NULL != msg)
294 {
295 GNUNET_break (0);
296 GNUNET_CLIENT_receive (h->client,
297 &arm_termination_handler,
298 h,
299 GNUNET_TIME_UNIT_FOREVER_REL);
300 return;
301 }
302 cm = h->thm;
303 h->thm = NULL;
304 h->currently_down = GNUNET_YES;
305 GNUNET_CLIENT_disconnect (h->client);
306 h->client = NULL;
307 if (NULL != cm->result_cont)
308 cm->result_cont (cm->cont_cls,
309 GNUNET_ARM_REQUEST_SENT_OK,
310 (const char *) &cm->msg[1],
311 GNUNET_ARM_RESULT_STOPPED);
312 GNUNET_free (cm->msg);
313 GNUNET_free (cm);
314}
315
316
317/**
318 * Handler for ARM replies. 252 * Handler for ARM replies.
319 * 253 *
320 * @param cls our `struct GNUNET_ARM_Handle` 254 * @param cls our `struct GNUNET_ARM_Handle`
321 * @param msg the message received from the arm service 255 * @param res the message received from the arm service
322 */ 256 */
323static void 257static void
324client_notify_handler (void *cls, 258handle_arm_result (void *cls,
325 const struct GNUNET_MessageHeader *msg) 259 const struct GNUNET_ARM_ResultMessage *res)
326{ 260{
327 struct GNUNET_ARM_Handle *h = cls; 261 struct GNUNET_ARM_Handle *h = cls;
328 const struct GNUNET_ARM_Message *arm_msg; 262 struct GNUNET_ARM_Operation *op;
329 const struct GNUNET_ARM_ResultMessage *res;
330 const struct GNUNET_ARM_ListResultMessage *lres;
331 struct ARMControlMessage *cm;
332 const char **list;
333 const char *pos;
334 uint64_t id; 263 uint64_t id;
335 enum GNUNET_ARM_Result result; 264 enum GNUNET_ARM_Result result;
336 uint16_t size_check; 265 GNUNET_ARM_ResultCallback result_cont;
337 uint16_t rcount; 266 void *result_cont_cls;
338 uint16_t msize;
339 unsigned char fail;
340 267
341 list = NULL; 268 id = GNUNET_ntohll (res->arm_msg.request_id);
342 rcount = 0; 269 op = find_op_by_id (h,
343 if (NULL == msg) 270 id);
344 { 271 if (NULL == op)
345 LOG (GNUNET_ERROR_TYPE_INFO,
346 _("Client was disconnected from arm service, trying to reconnect.\n"));
347 reconnect_arm_later (h);
348 return;
349 }
350 msize = ntohs (msg->size);
351 LOG (GNUNET_ERROR_TYPE_DEBUG,
352 "Processing message of type %u and size %u from arm service\n",
353 ntohs (msg->type), msize);
354 if (msize < sizeof (struct GNUNET_ARM_Message))
355 {
356 GNUNET_break (0);
357 reconnect_arm_later (h);
358 return;
359 }
360 arm_msg = (const struct GNUNET_ARM_Message *) msg;
361 GNUNET_break (0 == ntohl (arm_msg->reserved));
362 id = GNUNET_ntohll (arm_msg->request_id);
363 cm = find_cm_by_id (h, id);
364 if (NULL == cm)
365 { 272 {
366 LOG (GNUNET_ERROR_TYPE_DEBUG, 273 LOG (GNUNET_ERROR_TYPE_DEBUG,
367 "Message with unknown id %llu\n", 274 "Message with unknown id %llu\n",
368 id); 275 (unsigned long long) id);
369 return;
370 }
371 fail = GNUNET_NO;
372 switch (ntohs (msg->type))
373 {
374 case GNUNET_MESSAGE_TYPE_ARM_RESULT:
375 if (msize < sizeof (struct GNUNET_ARM_ResultMessage))
376 {
377 GNUNET_assert (0);
378 fail = GNUNET_YES;
379 }
380 break;
381 case GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT:
382 if (msize < sizeof (struct GNUNET_ARM_ListResultMessage))
383 {
384 GNUNET_break (0);
385 fail = GNUNET_YES;
386 break;
387 }
388 size_check = 0;
389 lres = (const struct GNUNET_ARM_ListResultMessage *) msg;
390 rcount = ntohs (lres->count);
391 {
392 unsigned int i;
393
394 list = GNUNET_malloc (sizeof (const char *) * rcount);
395 pos = (const char *)&lres[1];
396 for (i = 0; i < rcount; i++)
397 {
398 const char *end = memchr (pos, 0, msize - size_check);
399 if (NULL == end)
400 {
401 GNUNET_break (0);
402 fail = GNUNET_YES;
403 break;
404 }
405 list[i] = pos;
406 size_check += (end - pos) + 1;
407 pos = end + 1;
408 }
409 if (GNUNET_YES == fail)
410 {
411 GNUNET_free (list);
412 list = NULL;
413 }
414 }
415 break;
416 default:
417 fail = GNUNET_YES;
418 break;
419 }
420 GNUNET_assert (NULL != cm->timeout_task_id);
421 GNUNET_SCHEDULER_cancel (cm->timeout_task_id);
422 GNUNET_CONTAINER_DLL_remove (h->control_sent_head,
423 h->control_sent_tail,
424 cm);
425 if (GNUNET_YES == fail)
426 {
427 reconnect_arm_later (h);
428 GNUNET_free (cm->msg);
429 GNUNET_free (cm);
430 return; 276 return;
431 } 277 }
432 if ( (GNUNET_MESSAGE_TYPE_ARM_RESULT == ntohs (msg->type)) && 278
433 (0 == strcasecmp ((const char *) &cm->msg[1], 279 result = (enum GNUNET_ARM_Result) ntohl (res->result);
434 "arm")) && 280 if ( (GNUNET_YES == op->is_arm_stop) &&
435 (NULL != (res = (const struct GNUNET_ARM_ResultMessage *) msg)) && 281 (GNUNET_ARM_RESULT_STOPPING == result) )
436 (GNUNET_ARM_RESULT_STOPPING == ntohl (res->result)) )
437 { 282 {
438 /* special case: if we are stopping 'gnunet-service-arm', we do not just 283 /* special case: if we are stopping 'gnunet-service-arm', we do not just
439 wait for the result message, but also wait for the service to close 284 wait for the result message, but also wait for the service to close
@@ -443,184 +288,159 @@ client_notify_handler (void *cls,
443 if (NULL != h->thm) 288 if (NULL != h->thm)
444 { 289 {
445 GNUNET_break (0); 290 GNUNET_break (0);
446 cm->result_cont (h->thm->cont_cls, 291 op->result_cont (h->thm->cont_cls,
447 GNUNET_ARM_REQUEST_SENT_OK, 292 GNUNET_ARM_REQUEST_SENT_OK,
448 (const char *) &h->thm->msg[1],
449 GNUNET_ARM_RESULT_IS_NOT_KNOWN); 293 GNUNET_ARM_RESULT_IS_NOT_KNOWN);
450 GNUNET_free (h->thm->msg);
451 GNUNET_free (h->thm); 294 GNUNET_free (h->thm);
452 } 295 }
453 h->thm = cm; 296 GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
454 GNUNET_CLIENT_receive (h->client, 297 h->operation_pending_tail,
455 &arm_termination_handler, 298 op);
456 h, 299 h->thm = op;
457 GNUNET_TIME_UNIT_FOREVER_REL);
458 return; 300 return;
459 } 301 }
460 GNUNET_CLIENT_receive (h->client, 302 result_cont = op->result_cont;
461 &client_notify_handler, 303 result_cont_cls = op->cont_cls;
462 h, 304 GNUNET_ARM_operation_cancel (op);
463 GNUNET_TIME_UNIT_FOREVER_REL); 305 if (NULL != result_cont)
464 switch (ntohs (msg->type)) 306 result_cont (result_cont_cls,
465 { 307 GNUNET_ARM_REQUEST_SENT_OK,
466 case GNUNET_MESSAGE_TYPE_ARM_RESULT: 308 result);
467 res = (const struct GNUNET_ARM_ResultMessage *) msg;
468 LOG (GNUNET_ERROR_TYPE_DEBUG,
469 "Received response from ARM for service `%s': %u\n",
470 (const char *) &cm->msg[1], ntohs (msg->type));
471 result = (enum GNUNET_ARM_Result) ntohl (res->result);
472 if (NULL != cm->result_cont)
473 cm->result_cont (cm->cont_cls,
474 GNUNET_ARM_REQUEST_SENT_OK,
475 (const char *) &cm->msg[1],
476 result);
477 break;
478 case GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT:
479 if (NULL != cm->list_cont)
480 cm->list_cont (cm->cont_cls,
481 GNUNET_ARM_REQUEST_SENT_OK,
482 rcount,
483 list);
484 GNUNET_free_non_null (list);
485 break;
486 }
487 GNUNET_free (cm->msg);
488 GNUNET_free (cm);
489} 309}
490 310
491 311
492/** 312/**
493 * Transmit the next message to the arm service. 313 * Checked that list result message is well-formed.
494 * 314 *
495 * @param cls closure with the `struct GNUNET_ARM_Handle` 315 * @param cls our `struct GNUNET_ARM_Handle`
496 * @param size number of bytes available in @a buf 316 * @param lres the message received from the arm service
497 * @param buf where the callee should write the message 317 * @return #GNUNET_OK if message is well-formed
498 * @return number of bytes written to @a buf
499 */ 318 */
500static size_t 319static int
501transmit_arm_message (void *cls, 320check_arm_list_result (void *cls,
502 size_t size, 321 const struct GNUNET_ARM_ListResultMessage *lres)
503 void *buf)
504{ 322{
505 struct GNUNET_ARM_Handle *h = cls; 323 const char *pos = (const char *) &lres[1];
506 struct ARMControlMessage *cm; 324 uint16_t rcount = ntohs (lres->count);
507 struct GNUNET_ARM_Message *arm_msg; 325 uint16_t msize = ntohs (lres->arm_msg.header.size);
508 uint64_t request_id; 326 uint16_t size_check;
509 int notify_connection;
510 uint16_t msize;
511 327
512 notify_connection = GNUNET_NO; 328 size_check = 0;
513 LOG (GNUNET_ERROR_TYPE_DEBUG, 329 for (unsigned int i = 0; i < rcount; i++)
514 "transmit_arm_message is running with %p buffer of size %lu. ARM is known to be %s\n",
515 buf, size, h->currently_down ? "unconnected" : "connected");
516 GNUNET_assert (NULL == h->reconnect_task);
517 h->cth = NULL;
518 if ((GNUNET_YES == h->currently_down) && (NULL != buf))
519 {
520 h->currently_down = GNUNET_NO;
521 notify_connection = GNUNET_YES;
522 h->retry_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
523 GNUNET_CLIENT_receive (h->client, &client_notify_handler, h,
524 GNUNET_TIME_UNIT_FOREVER_REL);
525 }
526 if (NULL == buf)
527 { 330 {
528 LOG (GNUNET_ERROR_TYPE_DEBUG, 331 const char *end = memchr (pos, 0, msize - size_check);
529 "Transmission failed, initiating reconnect\n"); 332 if (NULL == end)
530 reconnect_arm_later (h); 333 {
531 return 0; 334 GNUNET_break (0);
532 } 335 return GNUNET_SYSERR;
533 if (NULL == (cm = h->control_pending_head)) 336 }
534 { 337 size_check += (end - pos) + 1;
535 LOG (GNUNET_ERROR_TYPE_DEBUG, 338 pos = end + 1;
536 "Queue is empty, not sending anything\n");
537 msize = 0;
538 goto end;
539 }
540 GNUNET_assert (NULL != cm->msg);
541 msize = ntohs (cm->msg->header.size);
542 if (size < msize)
543 {
544 LOG (GNUNET_ERROR_TYPE_DEBUG,
545 "Request is too big (%u < %u), not sending it\n", size, msize);
546 trigger_next_request (h, GNUNET_NO);
547 msize = 0;
548 goto end;
549 } 339 }
550 arm_msg = cm->msg; 340 return GNUNET_OK;
551 if (0 == h->request_id_counter)
552 h->request_id_counter++;
553 request_id = h->request_id_counter++;
554 LOG (GNUNET_ERROR_TYPE_DEBUG,
555 "Transmitting control message with %u bytes of type %u to arm with id %llu\n",
556 (unsigned int) msize,
557 (unsigned int) ntohs (cm->msg->header.type),
558 request_id);
559 arm_msg->reserved = htonl (0);
560 arm_msg->request_id = GNUNET_htonll (request_id);
561 memcpy (buf, cm->msg, msize);
562 /* Otherwise we won't be able to find it later! */
563 arm_msg->request_id = request_id;
564 GNUNET_CONTAINER_DLL_remove (h->control_pending_head,
565 h->control_pending_tail,
566 cm);
567 GNUNET_CONTAINER_DLL_insert_tail (h->control_sent_head,
568 h->control_sent_tail,
569 cm);
570 /* Don't free msg, keep it around (kind of wasteful, but then we don't
571 * really have many messages to handle, and it'll be freed when it times
572 * out anyway.
573 */
574 trigger_next_request (h, GNUNET_NO);
575
576 end:
577 if ((GNUNET_YES == notify_connection) && (NULL != h->conn_status))
578 h->conn_status (h->conn_status_cls, GNUNET_YES);
579 return msize;
580} 341}
581 342
582 343
583/** 344/**
584 * Check the list of pending requests, send the next 345 * Handler for ARM list replies.
585 * one to the arm.
586 * 346 *
587 * @param h arm handle 347 * @param cls our `struct GNUNET_ARM_Handle`
588 * @param ignore_currently_down transmit message even if not initialized? 348 * @param lres the message received from the arm service
589 */ 349 */
590static void 350static void
591trigger_next_request (struct GNUNET_ARM_Handle *h, 351handle_arm_list_result (void *cls,
592 int ignore_currently_down) 352 const struct GNUNET_ARM_ListResultMessage *lres)
593{ 353{
594 uint16_t msize; 354 struct GNUNET_ARM_Handle *h = cls;
355 uint16_t rcount = ntohs (lres->count);
356 const char *list[rcount];
357 const char *pos = (const char *) &lres[1];
358 uint16_t msize = ntohs (lres->arm_msg.header.size);
359 struct GNUNET_ARM_Operation *op;
360 uint16_t size_check;
361 uint64_t id;
595 362
596 msize = sizeof (struct GNUNET_MessageHeader); 363 id = GNUNET_ntohll (lres->arm_msg.request_id);
597 if ((GNUNET_YES == h->currently_down) && (ignore_currently_down == GNUNET_NO)) 364 op = find_op_by_id (h,
365 id);
366 if (NULL == op)
598 { 367 {
599 LOG (GNUNET_ERROR_TYPE_DEBUG, 368 LOG (GNUNET_ERROR_TYPE_DEBUG,
600 "ARM connection down, not processing queue\n"); 369 "Message with unknown id %llu\n",
370 (unsigned long long) id);
601 return; 371 return;
602 } 372 }
603 if (NULL != h->cth) 373 size_check = 0;
374 for (unsigned int i = 0; i < rcount; i++)
375 {
376 const char *end = memchr (pos,
377 0,
378 msize - size_check);
379
380 /* Assert, as this was already checked in #check_arm_list_result() */
381 GNUNET_assert (NULL != end);
382 list[i] = pos;
383 size_check += (end - pos) + 1;
384 pos = end + 1;
385 }
386 if (NULL != op->list_cont)
387 op->list_cont (op->cont_cls,
388 GNUNET_ARM_REQUEST_SENT_OK,
389 rcount,
390 list);
391 GNUNET_ARM_operation_cancel (op);
392}
393
394
395/**
396 * Receive confirmation from test, ARM service is up.
397 *
398 * @param cls closure with the `struct GNUNET_ARM_Handle`
399 * @param msg message received
400 */
401static void
402handle_confirm (void *cls,
403 const struct GNUNET_MessageHeader *msg)
404{
405 struct GNUNET_ARM_Handle *h = cls;
406
407 LOG (GNUNET_ERROR_TYPE_DEBUG,
408 "Got confirmation from ARM that we are up!\n");
409 if (GNUNET_NO == h->currently_up)
604 { 410 {
605 LOG (GNUNET_ERROR_TYPE_DEBUG, 411 h->currently_up = GNUNET_YES;
606 "Request pending, not processing queue\n"); 412 if (NULL != h->conn_status)
607 return; 413 h->conn_status (h->conn_status_cls,
414 GNUNET_YES);
608 } 415 }
609 if (NULL != h->control_pending_head) 416}
610 msize = 417
611 ntohs (h->control_pending_head->msg->header.size); 418
612 else if (GNUNET_NO == ignore_currently_down) 419/**
420 * Generic error handler, called with the appropriate error code and
421 * the same closure specified at the creation of the message queue.
422 * Not every message queue implementation supports an error handler.
423 *
424 * @param cls closure with the `struct GNUNET_ARM_Handle *`
425 * @param error error code
426 */
427static void
428mq_error_handler (void *cls,
429 enum GNUNET_MQ_Error error)
430{
431 struct GNUNET_ARM_Handle *h = cls;
432 struct GNUNET_ARM_Operation *op;
433
434 h->currently_up = GNUNET_NO;
435 if (NULL != (op = h->thm))
613 { 436 {
614 LOG (GNUNET_ERROR_TYPE_DEBUG, 437 h->thm = NULL;
615 "Request queue empty, not processing queue\n"); 438 op->result_cont (op->cont_cls,
616 return; /* no pending message */ 439 GNUNET_ARM_REQUEST_SENT_OK,
440 GNUNET_ARM_RESULT_STOPPED);
441 GNUNET_free (op);
617 } 442 }
618 h->cth = 443 reconnect_arm_later (h);
619 GNUNET_CLIENT_notify_transmit_ready (h->client,
620 msize,
621 GNUNET_TIME_UNIT_FOREVER_REL,
622 GNUNET_NO,
623 &transmit_arm_message, h);
624} 444}
625 445
626 446
@@ -633,22 +453,47 @@ trigger_next_request (struct GNUNET_ARM_Handle *h,
633static int 453static int
634reconnect_arm (struct GNUNET_ARM_Handle *h) 454reconnect_arm (struct GNUNET_ARM_Handle *h)
635{ 455{
636 GNUNET_assert (NULL == h->client); 456 GNUNET_MQ_hd_fixed_size (arm_result,
637 GNUNET_assert (GNUNET_YES == h->currently_down); 457 GNUNET_MESSAGE_TYPE_ARM_RESULT,
638 h->client = GNUNET_CLIENT_connect ("arm", h->cfg); 458 struct GNUNET_ARM_ResultMessage);
639 if (NULL == h->client) 459 GNUNET_MQ_hd_var_size (arm_list_result,
460 GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT,
461 struct GNUNET_ARM_ListResultMessage);
462 GNUNET_MQ_hd_fixed_size (confirm,
463 GNUNET_MESSAGE_TYPE_TEST,
464 struct GNUNET_MessageHeader);
465 struct GNUNET_MQ_MessageHandler handlers[] = {
466 make_arm_result_handler (h),
467 make_arm_list_result_handler (h),
468 make_confirm_handler (h),
469 GNUNET_MQ_handler_end ()
470 };
471 struct GNUNET_MessageHeader *test;
472 struct GNUNET_MQ_Envelope *env;
473
474 if (NULL != h->mq)
475 return GNUNET_OK;
476 GNUNET_assert (GNUNET_NO == h->currently_up);
477 h->mq = GNUNET_CLIENT_connecT (h->cfg,
478 "arm",
479 handlers,
480 &mq_error_handler,
481 h);
482 if (NULL == h->mq)
640 { 483 {
641 LOG (GNUNET_ERROR_TYPE_DEBUG, 484 LOG (GNUNET_ERROR_TYPE_DEBUG,
642 "arm_api, GNUNET_CLIENT_connect returned NULL\n"); 485 "GNUNET_CLIENT_connect returned NULL\n");
643 if (NULL != h->conn_status) 486 if (NULL != h->conn_status)
644 h->conn_status (h->conn_status_cls, 487 h->conn_status (h->conn_status_cls,
645 GNUNET_SYSERR); 488 GNUNET_SYSERR);
646 return GNUNET_SYSERR; 489 return GNUNET_SYSERR;
647 } 490 }
648 LOG (GNUNET_ERROR_TYPE_DEBUG, 491 LOG (GNUNET_ERROR_TYPE_DEBUG,
649 "arm_api, GNUNET_CLIENT_connect returned non-NULL\n"); 492 "Sending TEST message to ARM\n");
650 trigger_next_request (h, 493 env = GNUNET_MQ_msg (test,
651 GNUNET_YES); 494 GNUNET_MESSAGE_TYPE_TEST);
495 GNUNET_MQ_send (h->mq,
496 env);
652 return GNUNET_OK; 497 return GNUNET_OK;
653} 498}
654 499
@@ -661,22 +506,20 @@ reconnect_arm (struct GNUNET_ARM_Handle *h)
661 * the ARM service may internally use a different 506 * the ARM service may internally use a different
662 * configuration to determine how to start the service). 507 * configuration to determine how to start the service).
663 * @param conn_status will be called when connecting/disconnecting 508 * @param conn_status will be called when connecting/disconnecting
664 * @param cls closure for conn_status 509 * @param conn_status_cls closure for @a conn_status
665 * @return context to use for further ARM operations, NULL on error. 510 * @return context to use for further ARM operations, NULL on error.
666 */ 511 */
667struct GNUNET_ARM_Handle * 512struct GNUNET_ARM_Handle *
668GNUNET_ARM_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, 513GNUNET_ARM_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
669 GNUNET_ARM_ConnectionStatusCallback conn_status, 514 GNUNET_ARM_ConnectionStatusCallback conn_status,
670 void *cls) 515 void *conn_status_cls)
671{ 516{
672 struct GNUNET_ARM_Handle *h; 517 struct GNUNET_ARM_Handle *h;
673 518
674 h = GNUNET_new (struct GNUNET_ARM_Handle); 519 h = GNUNET_new (struct GNUNET_ARM_Handle);
675 h->cfg = GNUNET_CONFIGURATION_dup (cfg); 520 h->cfg = cfg;
676 h->currently_down = GNUNET_YES;
677 h->reconnect_task = NULL;
678 h->conn_status = conn_status; 521 h->conn_status = conn_status;
679 h->conn_status_cls = cls; 522 h->conn_status_cls = conn_status_cls;
680 if (GNUNET_OK != reconnect_arm (h)) 523 if (GNUNET_OK != reconnect_arm (h))
681 { 524 {
682 GNUNET_free (h); 525 GNUNET_free (h);
@@ -692,113 +535,60 @@ GNUNET_ARM_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
692 * @param h the handle that was being used 535 * @param h the handle that was being used
693 */ 536 */
694void 537void
695GNUNET_ARM_disconnect_and_free (struct GNUNET_ARM_Handle *h) 538GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *h)
696{ 539{
697 struct ARMControlMessage *cm; 540 struct GNUNET_ARM_Operation *op;
698 541
699 LOG (GNUNET_ERROR_TYPE_DEBUG, 542 LOG (GNUNET_ERROR_TYPE_DEBUG,
700 "Disconnecting from ARM service\n"); 543 "Disconnecting from ARM service\n");
701 if (NULL != h->cth) 544 while (NULL != (op = h->operation_pending_head))
702 { 545 {
703 GNUNET_CLIENT_notify_transmit_ready_cancel (h->cth); 546 GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
704 h->cth = NULL; 547 h->operation_pending_tail,
705 } 548 op);
706 while ((NULL != (cm = h->control_pending_head)) 549 if (NULL != op->result_cont)
707 || (NULL != (cm = h->control_sent_head)) ) 550 op->result_cont (op->cont_cls,
708 {
709 if (NULL != h->control_pending_head)
710 GNUNET_CONTAINER_DLL_remove (h->control_pending_head,
711 h->control_pending_tail,
712 cm);
713 else
714 GNUNET_CONTAINER_DLL_remove (h->control_sent_head,
715 h->control_sent_tail,
716 cm);
717 GNUNET_assert (NULL != cm->timeout_task_id);
718 GNUNET_SCHEDULER_cancel (cm->timeout_task_id);
719 if (NULL != cm->result_cont)
720 cm->result_cont (cm->cont_cls,
721 GNUNET_ARM_REQUEST_DISCONNECTED, 551 GNUNET_ARM_REQUEST_DISCONNECTED,
722 NULL,
723 0); 552 0);
724 /* FIXME: What about list callback? */ 553 if (NULL != op->list_cont)
725 GNUNET_free_non_null (cm->msg); 554 op->list_cont (op->cont_cls,
726 GNUNET_free (cm); 555 GNUNET_ARM_REQUEST_DISCONNECTED,
556 0,
557 NULL);
558 if (NULL != op->async)
559 {
560 GNUNET_SCHEDULER_cancel (op->async);
561 op->async = NULL;
562 }
563 GNUNET_free (op);
727 } 564 }
728 if (NULL != h->client) 565 if (NULL != h->mq)
729 { 566 {
730 GNUNET_CLIENT_disconnect (h->client); 567 GNUNET_MQ_destroy (h->mq);
731 h->client = NULL; 568 h->mq = NULL;
732 } 569 }
733 if (NULL != h->reconnect_task) 570 if (NULL != h->reconnect_task)
734 { 571 {
735 GNUNET_SCHEDULER_cancel (h->reconnect_task); 572 GNUNET_SCHEDULER_cancel (h->reconnect_task);
736 h->reconnect_task = NULL; 573 h->reconnect_task = NULL;
737 } 574 }
738 if (GNUNET_NO == h->service_test_is_active) 575 GNUNET_free (h);
739 {
740 GNUNET_CONFIGURATION_destroy (h->cfg);
741 GNUNET_free (h);
742 }
743}
744
745
746/**
747 * Message timed out. Remove it from the queue.
748 *
749 * @param cls the message (struct ARMControlMessage *)
750 */
751static void
752control_message_timeout (void *cls)
753{
754 struct ARMControlMessage *cm = cls;
755 struct GNUNET_ARM_Message *arm_msg;
756
757 LOG (GNUNET_ERROR_TYPE_DEBUG,
758 "Control message timed out\n");
759 arm_msg = cm->msg;
760 if ((NULL == arm_msg) || (0 == arm_msg->request_id))
761 {
762 GNUNET_CONTAINER_DLL_remove (cm->h->control_pending_head,
763 cm->h->control_pending_tail,
764 cm);
765 }
766 else
767 {
768 GNUNET_CONTAINER_DLL_remove (cm->h->control_sent_head,
769 cm->h->control_sent_tail,
770 cm);
771 }
772 if (NULL != cm->result_cont)
773 cm->result_cont (cm->cont_cls,
774 GNUNET_ARM_REQUEST_TIMEOUT,
775 NULL, 0);
776 else if (NULL != cm->list_cont)
777 cm->list_cont (cm->cont_cls,
778 GNUNET_ARM_REQUEST_TIMEOUT,
779 0, NULL);
780 GNUNET_free_non_null (cm->msg);
781 GNUNET_free (cm);
782} 576}
783 577
784 578
785/** 579/**
786 * A client specifically requested starting of ARM itself. 580 * A client specifically requested starting of ARM itself.
787 * This function is called with information about whether 581 * Starts the ARM service.
788 * or not ARM is running; if it is, report success. If
789 * it is not, start the ARM process.
790 * 582 *
791 * @param cls the context for the request that we will report on (struct ARMControlMessage *) 583 * @param h the handle with configuration details
792 * @param result #GNUNET_YES if ARM is running 584 * @param std_inheritance inheritance of std streams
585 * @return operation status code
793 */ 586 */
794static void 587static enum GNUNET_ARM_Result
795arm_service_report (void *cls, 588start_arm_service (struct GNUNET_ARM_Handle *h,
796 int result) 589 enum GNUNET_OS_InheritStdioFlags std_inheritance)
797{ 590{
798 struct ARMControlMessage *cm = cls;
799 struct GNUNET_ARM_Handle *h;
800 struct GNUNET_OS_Process *proc; 591 struct GNUNET_OS_Process *proc;
801 unsigned char test_is_active;
802 char *cbinary; 592 char *cbinary;
803 char *binary; 593 char *binary;
804 char *quotedbinary; 594 char *quotedbinary;
@@ -806,51 +596,20 @@ arm_service_report (void *cls,
806 char *loprefix; 596 char *loprefix;
807 char *lopostfix; 597 char *lopostfix;
808 598
809 test_is_active = cm->h->service_test_is_active;
810 if ((GNUNET_YES == test_is_active) &&
811 (GNUNET_YES == result))
812 {
813 LOG (GNUNET_ERROR_TYPE_DEBUG,
814 "Looks like `%s' is already running.\n",
815 "gnunet-service-arm");
816 /* arm is running! */
817 if (cm->result_cont)
818 cm->result_cont (cm->cont_cls,
819 GNUNET_ARM_REQUEST_SENT_OK, "arm",
820 GNUNET_ARM_RESULT_IS_STARTED_ALREADY);
821 }
822 if (GNUNET_NO == test_is_active)
823 {
824 /* User disconnected & destroyed ARM handle in the middle of
825 * the service test, so we kept the handle around until now.
826 */
827 GNUNET_CONFIGURATION_destroy (cm->h->cfg);
828 GNUNET_free (cm->h);
829 }
830 if ((GNUNET_YES == result) ||
831 (GNUNET_NO == test_is_active))
832 {
833 GNUNET_free (cm);
834 return;
835 }
836 cm->h->service_test_is_active = GNUNET_NO;
837 LOG (GNUNET_ERROR_TYPE_DEBUG,
838 "Looks like `%s' is not running, will start it.\n",
839 "gnunet-service-arm");
840 if (GNUNET_OK != 599 if (GNUNET_OK !=
841 GNUNET_CONFIGURATION_get_value_string (cm->h->cfg, 600 GNUNET_CONFIGURATION_get_value_string (h->cfg,
842 "arm", 601 "arm",
843 "PREFIX", 602 "PREFIX",
844 &loprefix)) 603 &loprefix))
845 loprefix = GNUNET_strdup (""); 604 loprefix = GNUNET_strdup ("");
846 if (GNUNET_OK != 605 if (GNUNET_OK !=
847 GNUNET_CONFIGURATION_get_value_string (cm->h->cfg, 606 GNUNET_CONFIGURATION_get_value_string (h->cfg,
848 "arm", 607 "arm",
849 "OPTIONS", 608 "OPTIONS",
850 &lopostfix)) 609 &lopostfix))
851 lopostfix = GNUNET_strdup (""); 610 lopostfix = GNUNET_strdup ("");
852 if (GNUNET_OK != 611 if (GNUNET_OK !=
853 GNUNET_CONFIGURATION_get_value_string (cm->h->cfg, 612 GNUNET_CONFIGURATION_get_value_string (h->cfg,
854 "arm", 613 "arm",
855 "BINARY", 614 "BINARY",
856 &cbinary)) 615 &cbinary))
@@ -858,18 +617,14 @@ arm_service_report (void *cls,
858 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, 617 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
859 "arm", 618 "arm",
860 "BINARY"); 619 "BINARY");
861 if (cm->result_cont)
862 cm->result_cont (cm->cont_cls,
863 GNUNET_ARM_REQUEST_SENT_OK, "arm",
864 GNUNET_ARM_RESULT_IS_NOT_KNOWN);
865 GNUNET_free (cm);
866 GNUNET_free (loprefix); 620 GNUNET_free (loprefix);
867 GNUNET_free (lopostfix); 621 GNUNET_free (lopostfix);
868 return; 622 return GNUNET_ARM_RESULT_IS_NOT_KNOWN;
869 } 623 }
870 if (GNUNET_OK != 624 if (GNUNET_OK !=
871 GNUNET_CONFIGURATION_get_value_filename (cm->h->cfg, 625 GNUNET_CONFIGURATION_get_value_filename (h->cfg,
872 "arm", "CONFIG", 626 "arm",
627 "CONFIG",
873 &config)) 628 &config))
874 config = NULL; 629 config = NULL;
875 binary = GNUNET_OS_get_libexec_binary_path (cbinary); 630 binary = GNUNET_OS_get_libexec_binary_path (cbinary);
@@ -878,15 +633,15 @@ arm_service_report (void *cls,
878 binary); 633 binary);
879 GNUNET_free (cbinary); 634 GNUNET_free (cbinary);
880 if ( (GNUNET_YES == 635 if ( (GNUNET_YES ==
881 GNUNET_CONFIGURATION_have_value (cm->h->cfg, 636 GNUNET_CONFIGURATION_have_value (h->cfg,
882 "TESTING", 637 "TESTING",
883 "WEAKRANDOM")) && 638 "WEAKRANDOM")) &&
884 (GNUNET_YES == 639 (GNUNET_YES ==
885 GNUNET_CONFIGURATION_get_value_yesno (cm->h->cfg, 640 GNUNET_CONFIGURATION_get_value_yesno (h->cfg,
886 "TESTING", 641 "TESTING",
887 "WEAKRANDOM")) && 642 "WEAKRANDOM")) &&
888 (GNUNET_NO == 643 (GNUNET_NO ==
889 GNUNET_CONFIGURATION_have_value (cm->h->cfg, 644 GNUNET_CONFIGURATION_have_value (h->cfg,
890 "TESTING", 645 "TESTING",
891 "HOSTFILE"))) 646 "HOSTFILE")))
892 { 647 {
@@ -894,39 +649,43 @@ arm_service_report (void *cls,
894 /* we're clearly running a test, don't daemonize */ 649 /* we're clearly running a test, don't daemonize */
895 if (NULL == config) 650 if (NULL == config)
896 proc = GNUNET_OS_start_process_s (GNUNET_NO, 651 proc = GNUNET_OS_start_process_s (GNUNET_NO,
897 cm->std_inheritance, 652 std_inheritance,
898 NULL, 653 NULL,
899 loprefix, 654 loprefix,
900 quotedbinary, 655 quotedbinary,
901 /* no daemonization! */ 656 /* no daemonization! */
902 lopostfix, NULL); 657 lopostfix,
658 NULL);
903 else 659 else
904 proc = GNUNET_OS_start_process_s (GNUNET_NO, 660 proc = GNUNET_OS_start_process_s (GNUNET_NO,
905 cm->std_inheritance, 661 std_inheritance,
906 NULL, 662 NULL,
907 loprefix, 663 loprefix,
908 quotedbinary, 664 quotedbinary,
909 "-c", config, 665 "-c", config,
910 /* no daemonization! */ 666 /* no daemonization! */
911 lopostfix, NULL); 667 lopostfix,
668 NULL);
912 } 669 }
913 else 670 else
914 { 671 {
915 if (NULL == config) 672 if (NULL == config)
916 proc = GNUNET_OS_start_process_s (GNUNET_NO, 673 proc = GNUNET_OS_start_process_s (GNUNET_NO,
917 cm->std_inheritance, 674 std_inheritance,
918 NULL, 675 NULL,
919 loprefix, 676 loprefix,
920 quotedbinary, 677 quotedbinary,
921 "-d", lopostfix, NULL); 678 "-d", /* do daemonize */
679 lopostfix, NULL);
922 else 680 else
923 proc = GNUNET_OS_start_process_s (GNUNET_NO, 681 proc = GNUNET_OS_start_process_s (GNUNET_NO,
924 cm->std_inheritance, 682 std_inheritance,
925 NULL, 683 NULL,
926 loprefix, 684 loprefix,
927 quotedbinary, 685 quotedbinary,
928 "-c", config, 686 "-c", config,
929 "-d", lopostfix, 687 "-d", /* do daemonize */
688 lopostfix,
930 NULL); 689 NULL);
931 } 690 }
932 GNUNET_free (binary); 691 GNUNET_free (binary);
@@ -935,22 +694,32 @@ arm_service_report (void *cls,
935 GNUNET_free (loprefix); 694 GNUNET_free (loprefix);
936 GNUNET_free (lopostfix); 695 GNUNET_free (lopostfix);
937 if (NULL == proc) 696 if (NULL == proc)
697 return GNUNET_ARM_RESULT_START_FAILED;
698 GNUNET_OS_process_destroy (proc);
699 return GNUNET_ARM_RESULT_STARTING;
700}
701
702
703/**
704 * Abort an operation. Only prevents the callback from being
705 * called, the operation may still complete.
706 *
707 * @param op operation to cancel
708 */
709void
710GNUNET_ARM_operation_cancel (struct GNUNET_ARM_Operation *op)
711{
712 struct GNUNET_ARM_Handle *h = op->h;
713
714 if (h->thm == op)
938 { 715 {
939 if (cm->result_cont) 716 op->result_cont = NULL;
940 cm->result_cont (cm->cont_cls,
941 GNUNET_ARM_REQUEST_SENT_OK, "arm",
942 GNUNET_ARM_RESULT_START_FAILED);
943 GNUNET_free (cm);
944 return; 717 return;
945 } 718 }
946 if (cm->result_cont) 719 GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
947 cm->result_cont (cm->cont_cls, 720 h->operation_pending_tail,
948 GNUNET_ARM_REQUEST_SENT_OK, "arm", 721 op);
949 GNUNET_ARM_RESULT_STARTING); 722 GNUNET_free (op);
950 GNUNET_OS_process_destroy (proc);
951 h = cm->h;
952 GNUNET_free (cm);
953 reconnect_arm (h);
954} 723}
955 724
956 725
@@ -959,21 +728,21 @@ arm_service_report (void *cls,
959 * 728 *
960 * @param h handle to ARM 729 * @param h handle to ARM
961 * @param service_name name of the service 730 * @param service_name name of the service
962 * @param timeout how long to wait before failing for good
963 * @param cb callback to invoke when service is ready 731 * @param cb callback to invoke when service is ready
964 * @param cb_cls closure for @a cb 732 * @param cb_cls closure for @a cb
965 * @param type type of the request 733 * @param type type of the request
734 * @return handle to queue, NULL on error
966 */ 735 */
967static void 736static struct GNUNET_ARM_Operation *
968change_service (struct GNUNET_ARM_Handle *h, 737change_service (struct GNUNET_ARM_Handle *h,
969 const char *service_name, 738 const char *service_name,
970 struct GNUNET_TIME_Relative timeout,
971 GNUNET_ARM_ResultCallback cb, 739 GNUNET_ARM_ResultCallback cb,
972 void *cb_cls, 740 void *cb_cls,
973 uint16_t type) 741 uint16_t type)
974{ 742{
975 struct ARMControlMessage *cm; 743 struct GNUNET_ARM_Operation *op;
976 size_t slen; 744 size_t slen;
745 struct GNUNET_MQ_Envelope *env;
977 struct GNUNET_ARM_Message *msg; 746 struct GNUNET_ARM_Message *msg;
978 747
979 slen = strlen (service_name) + 1; 748 slen = strlen (service_name) + 1;
@@ -981,38 +750,81 @@ change_service (struct GNUNET_ARM_Handle *h,
981 GNUNET_SERVER_MAX_MESSAGE_SIZE) 750 GNUNET_SERVER_MAX_MESSAGE_SIZE)
982 { 751 {
983 GNUNET_break (0); 752 GNUNET_break (0);
984 if (cb != NULL) 753 return NULL;
985 cb (cb_cls, GNUNET_ARM_REQUEST_TOO_LONG, NULL, 0);
986 return;
987 } 754 }
988 LOG (GNUNET_ERROR_TYPE_DEBUG, "Requesting %s of service `%s'.\n", 755 if (0 == h->request_id_counter)
989 (GNUNET_MESSAGE_TYPE_ARM_START == type) ? "start" : "termination", 756 h->request_id_counter++;
990 service_name); 757 op = GNUNET_new (struct GNUNET_ARM_Operation);
991 cm = GNUNET_malloc (sizeof (struct ARMControlMessage) + slen); 758 op->h = h;
992 cm->h = h; 759 op->result_cont = cb;
993 cm->result_cont = cb; 760 op->cont_cls = cb_cls;
994 cm->cont_cls = cb_cls; 761 op->id = h->request_id_counter++;
995 cm->timeout = GNUNET_TIME_relative_to_absolute (timeout); 762 GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
996 memcpy (&cm[1], service_name, slen); 763 h->operation_pending_tail,
997 msg = GNUNET_malloc (sizeof (struct GNUNET_ARM_Message) + slen); 764 op);
998 msg->header.size = htons (sizeof (struct GNUNET_ARM_Message) + slen); 765 env = GNUNET_MQ_msg_extra (msg,
999 msg->header.type = htons (type); 766 slen,
767 type);
1000 msg->reserved = htonl (0); 768 msg->reserved = htonl (0);
1001 memcpy (&msg[1], service_name, slen); 769 msg->request_id = GNUNET_htonll (op->id);
1002 cm->msg = msg; 770 memcpy (&msg[1],
771 service_name,
772 slen);
773 GNUNET_MQ_send (h->mq,
774 env);
775 return op;
776}
777
778
779/**
780 * Task run to notify application that ARM is already up.
781 *
782 * @param cls the operation that asked ARM to be started
783 */
784static void
785notify_running (void *cls)
786{
787 struct GNUNET_ARM_Operation *op = cls;
788 struct GNUNET_ARM_Handle *h = op->h;
789
790 op->async = NULL;
791 GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
792 h->operation_pending_tail,
793 op);
794 if (NULL != op->result_cont)
795 op->result_cont (op->cont_cls,
796 GNUNET_ARM_REQUEST_SENT_OK,
797 GNUNET_ARM_RESULT_IS_STARTED_ALREADY);
798 if ( (GNUNET_YES == h->currently_up) &&
799 (NULL != h->conn_status) )
800 h->conn_status (h->conn_status_cls,
801 GNUNET_YES);
802 GNUNET_free (op);
803}
804
805
806/**
807 * Task run to notify application that ARM is being started.
808 *
809 * @param cls the operation that asked ARM to be started
810 */
811static void
812notify_starting (void *cls)
813{
814 struct GNUNET_ARM_Operation *op = cls;
815 struct GNUNET_ARM_Handle *h = op->h;
816
817 op->async = NULL;
1003 LOG (GNUNET_ERROR_TYPE_DEBUG, 818 LOG (GNUNET_ERROR_TYPE_DEBUG,
1004 "Inserting a control message into the queue. Timeout is %s\n", 819 "Notifying client that we started the ARM service\n");
1005 GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_remaining (cm->timeout), 820 GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
1006 GNUNET_NO)); 821 h->operation_pending_tail,
1007 GNUNET_CONTAINER_DLL_insert_tail (h->control_pending_head, 822 op);
1008 h->control_pending_tail, 823 if (NULL != op->result_cont)
1009 cm); 824 op->result_cont (op->cont_cls,
1010 cm->timeout_task_id = 825 GNUNET_ARM_REQUEST_SENT_OK,
1011 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining 826 op->starting_ret);
1012 (cm->timeout), 827 GNUNET_free (op);
1013 &control_message_timeout,
1014 cm);
1015 trigger_next_request (h, GNUNET_NO);
1016} 828}
1017 829
1018 830
@@ -1022,132 +834,116 @@ change_service (struct GNUNET_ARM_Handle *h,
1022 * @param h handle to ARM 834 * @param h handle to ARM
1023 * @param service_name name of the service 835 * @param service_name name of the service
1024 * @param std_inheritance inheritance of std streams 836 * @param std_inheritance inheritance of std streams
1025 * @param timeout how long to wait before failing for good
1026 * @param cont callback to invoke after request is sent or not sent 837 * @param cont callback to invoke after request is sent or not sent
1027 * @param cont_cls closure for @a cont 838 * @param cont_cls closure for @a cont
839 * @return handle for the operation, NULL on error
1028 */ 840 */
1029void 841struct GNUNET_ARM_Operation *
1030GNUNET_ARM_request_service_start (struct GNUNET_ARM_Handle *h, 842GNUNET_ARM_request_service_start (struct GNUNET_ARM_Handle *h,
1031 const char *service_name, 843 const char *service_name,
1032 enum GNUNET_OS_InheritStdioFlags std_inheritance, 844 enum GNUNET_OS_InheritStdioFlags std_inheritance,
1033 struct GNUNET_TIME_Relative timeout,
1034 GNUNET_ARM_ResultCallback cont, 845 GNUNET_ARM_ResultCallback cont,
1035 void *cont_cls) 846 void *cont_cls)
1036{ 847{
1037 struct ARMControlMessage *cm; 848 struct GNUNET_ARM_Operation *op;
1038 size_t slen; 849 enum GNUNET_ARM_Result ret;
1039 850
1040 LOG (GNUNET_ERROR_TYPE_DEBUG, 851 LOG (GNUNET_ERROR_TYPE_DEBUG,
1041 "Asked to start service `%s' within %s\n", service_name, 852 "Starting service `%s'\n",
1042 GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_NO)); 853 service_name);
1043 if (0 == strcasecmp ("arm", service_name)) 854 if (0 != strcasecmp ("arm",
855 service_name))
856 return change_service (h,
857 service_name,
858 cont,
859 cont_cls,
860 GNUNET_MESSAGE_TYPE_ARM_START);
861
862 /* Possible cases:
863 * 1) We're connected to ARM already. Invoke the callback immediately.
864 * 2) We're not connected to ARM.
865 * Cancel any reconnection attempts temporarily, then perform
866 * a service test.
867 */
868 if (GNUNET_YES == h->currently_up)
1044 { 869 {
1045 /* Possible cases: 870 LOG (GNUNET_ERROR_TYPE_DEBUG,
1046 * 1) We're connected to ARM already. Invoke the callback immediately. 871 "ARM is already running\n");
1047 * 2) We're not connected to ARM. 872 op = GNUNET_new (struct GNUNET_ARM_Operation);
1048 * Cancel any reconnection attempts temporarily, then perform 873 op->h = h;
1049 * a service test. 874 op->result_cont = cont;
1050 */ 875 op->cont_cls = cont_cls;
1051 if (GNUNET_NO == h->currently_down) 876 GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
1052 { 877 h->operation_pending_tail,
1053 LOG (GNUNET_ERROR_TYPE_DEBUG, 878 op);
1054 "ARM is already running\n"); 879 op->async = GNUNET_SCHEDULER_add_now (&notify_running,
1055 if (NULL != cont) 880 op);
1056 cont (cont_cls, 881 return op;
1057 GNUNET_ARM_REQUEST_SENT_OK, 882 }
1058 "arm", 883 /* This is an inherently uncertain choice, as it is of course
1059 GNUNET_ARM_RESULT_IS_STARTED_ALREADY); 884 theoretically possible that ARM is up and we just did not
1060 } 885 yet complete the MQ handshake. However, given that users
1061 else if (GNUNET_NO == h->service_test_is_active) 886 are unlikely to hammer 'gnunet-arm -s' on a busy system,
1062 { 887 the above check should catch 99.99% of the cases where ARM
1063 if (NULL != h->cth) 888 is already running. */
1064 { 889 LOG (GNUNET_ERROR_TYPE_DEBUG,
1065 GNUNET_CLIENT_notify_transmit_ready_cancel (h->cth); 890 "Starting ARM service\n");
1066 h->cth = NULL; 891 ret = start_arm_service (h,
1067 } 892 std_inheritance);
1068 if (NULL != h->client) 893 if (GNUNET_ARM_RESULT_STARTING == ret)
1069 { 894 reconnect_arm (h);
1070 GNUNET_CLIENT_disconnect (h->client); 895 op = GNUNET_new (struct GNUNET_ARM_Operation);
1071 h->client = NULL; 896 op->h = h;
1072 } 897 op->result_cont = cont;
1073 if (NULL != h->reconnect_task) 898 op->cont_cls = cont_cls;
1074 { 899 GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
1075 GNUNET_SCHEDULER_cancel (h->reconnect_task); 900 h->operation_pending_tail,
1076 h->reconnect_task = NULL; 901 op);
1077 } 902 op->starting_ret = ret;
1078 903 op->async = GNUNET_SCHEDULER_add_now (&notify_starting,
1079 LOG (GNUNET_ERROR_TYPE_DEBUG, 904 op);
1080 "Not connected to ARM, will do a service test\n"); 905 return op;
1081
1082 slen = strlen ("arm") + 1;
1083 cm = GNUNET_malloc (sizeof (struct ARMControlMessage) + slen);
1084 cm->h = h;
1085 cm->result_cont = cont;
1086 cm->cont_cls = cont_cls;
1087 cm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1088 cm->std_inheritance = std_inheritance;
1089 memcpy (&cm[1], service_name, slen);
1090 h->service_test_is_active = GNUNET_YES;
1091 GNUNET_CLIENT_service_test ("arm",
1092 h->cfg,
1093 timeout,
1094 &arm_service_report,
1095 cm);
1096 }
1097 else
1098 {
1099 /* Service test is already running - tell user to chill out and try
1100 * again later.
1101 */
1102 LOG (GNUNET_ERROR_TYPE_DEBUG,
1103 "Service test is already in progress, we're busy\n");
1104 if (NULL != cont)
1105 cont (cont_cls,
1106 GNUNET_ARM_REQUEST_BUSY,
1107 NULL, 0);
1108 }
1109 return;
1110 }
1111 change_service (h,
1112 service_name,
1113 timeout,
1114 cont, cont_cls,
1115 GNUNET_MESSAGE_TYPE_ARM_START);
1116} 906}
1117 907
1118 908
1119/** 909/**
1120 * Request a service to be stopped. 910 * Request a service to be stopped. Stopping arm itself will not
1121 * Stopping arm itself will not invalidate its handle, and 911 * invalidate its handle, and ARM API will try to restore connection
1122 * ARM API will try to restore connection to the ARM service, 912 * to the ARM service, even if ARM connection was lost because you
1123 * even if ARM connection was lost because you asked for ARM to be stopped. 913 * asked for ARM to be stopped. Call
1124 * Call #GNUNET_ARM_disconnect_and_free() to free the handle and prevent 914 * #GNUNET_ARM_disconnect() to free the handle and prevent
1125 * further connection attempts. 915 * further connection attempts.
1126 * 916 *
1127 * @param h handle to ARM 917 * @param h handle to ARM
1128 * @param service_name name of the service 918 * @param service_name name of the service
1129 * @param timeout how long to wait before failing for good
1130 * @param cont callback to invoke after request is sent or is not sent 919 * @param cont callback to invoke after request is sent or is not sent
1131 * @param cont_cls closure for @a cont 920 * @param cont_cls closure for @a cont
921 * @return handle for the operation, NULL on error
1132 */ 922 */
1133void 923struct GNUNET_ARM_Operation *
1134GNUNET_ARM_request_service_stop (struct GNUNET_ARM_Handle *h, 924GNUNET_ARM_request_service_stop (struct GNUNET_ARM_Handle *h,
1135 const char *service_name, 925 const char *service_name,
1136 struct GNUNET_TIME_Relative timeout,
1137 GNUNET_ARM_ResultCallback cont, 926 GNUNET_ARM_ResultCallback cont,
1138 void *cont_cls) 927 void *cont_cls)
1139{ 928{
929 struct GNUNET_ARM_Operation *op;
930
1140 LOG (GNUNET_ERROR_TYPE_DEBUG, 931 LOG (GNUNET_ERROR_TYPE_DEBUG,
1141 "Stopping service `%s' within %s\n", 932 "Stopping service `%s'\n",
1142 service_name, 933 service_name);
1143 GNUNET_STRINGS_relative_time_to_string (timeout, 934 op = change_service (h,
1144 GNUNET_NO)); 935 service_name,
1145 change_service (h, 936 cont,
1146 service_name, 937 cont_cls,
1147 timeout, 938 GNUNET_MESSAGE_TYPE_ARM_STOP);
1148 cont, 939 if (NULL == op)
1149 cont_cls, 940 return NULL;
1150 GNUNET_MESSAGE_TYPE_ARM_STOP); 941 /* If the service is ARM, set a flag as we will use MQ errors
942 to detect that the process is really gone. */
943 if (0 == strcasecmp (service_name,
944 "arm"))
945 op->is_arm_stop = GNUNET_YES;
946 return op;
1151} 947}
1152 948
1153 949
@@ -1155,43 +951,38 @@ GNUNET_ARM_request_service_stop (struct GNUNET_ARM_Handle *h,
1155 * Request a list of running services. 951 * Request a list of running services.
1156 * 952 *
1157 * @param h handle to ARM 953 * @param h handle to ARM
1158 * @param timeout how long to wait before failing for good
1159 * @param cont callback to invoke after request is sent or is not sent 954 * @param cont callback to invoke after request is sent or is not sent
1160 * @param cont_cls closure for @a cont 955 * @param cont_cls closure for @a cont
956 * @return handle for the operation, NULL on error
1161 */ 957 */
1162void 958struct GNUNET_ARM_Operation *
1163GNUNET_ARM_request_service_list (struct GNUNET_ARM_Handle *h, 959GNUNET_ARM_request_service_list (struct GNUNET_ARM_Handle *h,
1164 struct GNUNET_TIME_Relative timeout,
1165 GNUNET_ARM_ServiceListCallback cont, 960 GNUNET_ARM_ServiceListCallback cont,
1166 void *cont_cls) 961 void *cont_cls)
1167{ 962{
1168 struct ARMControlMessage *cm; 963 struct GNUNET_ARM_Operation *op;
964 struct GNUNET_MQ_Envelope *env;
1169 struct GNUNET_ARM_Message *msg; 965 struct GNUNET_ARM_Message *msg;
1170 966
1171 LOG (GNUNET_ERROR_TYPE_DEBUG, 967 LOG (GNUNET_ERROR_TYPE_DEBUG,
1172 "Requesting LIST from ARM service with timeout: %s\n", 968 "Requesting LIST from ARM service\n");
1173 GNUNET_STRINGS_relative_time_to_string (timeout, 969 if (0 == h->request_id_counter)
1174 GNUNET_YES)); 970 h->request_id_counter++;
1175 cm = GNUNET_new (struct ARMControlMessage); 971 op = GNUNET_new (struct GNUNET_ARM_Operation);
1176 cm->h = h; 972 op->h = h;
1177 cm->list_cont = cont; 973 op->list_cont = cont;
1178 cm->cont_cls = cont_cls; 974 op->cont_cls = cont_cls;
1179 cm->timeout = GNUNET_TIME_relative_to_absolute (timeout); 975 op->id = h->request_id_counter++;
1180 msg = GNUNET_malloc (sizeof (struct GNUNET_ARM_Message)); 976 GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
1181 msg->header.size = htons (sizeof (struct GNUNET_ARM_Message)); 977 h->operation_pending_tail,
1182 msg->header.type = htons (GNUNET_MESSAGE_TYPE_ARM_LIST); 978 op);
979 env = GNUNET_MQ_msg (msg,
980 GNUNET_MESSAGE_TYPE_ARM_LIST);
1183 msg->reserved = htonl (0); 981 msg->reserved = htonl (0);
1184 cm->msg = msg; 982 msg->request_id = GNUNET_htonll (op->id);
1185 GNUNET_CONTAINER_DLL_insert_tail (h->control_pending_head, 983 GNUNET_MQ_send (h->mq,
1186 h->control_pending_tail, 984 env);
1187 cm); 985 return op;
1188 cm->timeout_task_id =
1189 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
1190 (cm->timeout),
1191 &control_message_timeout,
1192 cm);
1193 trigger_next_request (h,
1194 GNUNET_NO);
1195} 986}
1196 987
1197 988