diff options
Diffstat (limited to 'src/arm/arm_api.c')
-rw-r--r-- | src/arm/arm_api.c | 1101 |
1 files changed, 0 insertions, 1101 deletions
diff --git a/src/arm/arm_api.c b/src/arm/arm_api.c deleted file mode 100644 index 5fcbfb0a9..000000000 --- a/src/arm/arm_api.c +++ /dev/null | |||
@@ -1,1101 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2009, 2010, 2012, 2013, 2016, 2019 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file arm/arm_api.c | ||
23 | * @brief API for accessing the ARM service | ||
24 | * @author Christian Grothoff | ||
25 | * @author LRN | ||
26 | */ | ||
27 | #include "platform.h" | ||
28 | #include "gnunet_util_lib.h" | ||
29 | #include "gnunet_arm_service.h" | ||
30 | #include "gnunet_protocols.h" | ||
31 | #include "arm.h" | ||
32 | |||
33 | #define LOG(kind, ...) GNUNET_log_from (kind, "arm-api", __VA_ARGS__) | ||
34 | |||
35 | |||
36 | /** | ||
37 | * Entry in a doubly-linked list of operations awaiting for replies | ||
38 | * (in-order) from the ARM service. | ||
39 | */ | ||
40 | struct GNUNET_ARM_Operation | ||
41 | { | ||
42 | /** | ||
43 | * This is a doubly-linked list. | ||
44 | */ | ||
45 | struct GNUNET_ARM_Operation *next; | ||
46 | |||
47 | /** | ||
48 | * This is a doubly-linked list. | ||
49 | */ | ||
50 | struct GNUNET_ARM_Operation *prev; | ||
51 | |||
52 | /** | ||
53 | * ARM handle. | ||
54 | */ | ||
55 | struct GNUNET_ARM_Handle *h; | ||
56 | |||
57 | /** | ||
58 | * Callback for service state change requests. | ||
59 | */ | ||
60 | GNUNET_ARM_ResultCallback result_cont; | ||
61 | |||
62 | /** | ||
63 | * Callback for service list requests. | ||
64 | */ | ||
65 | GNUNET_ARM_ServiceListCallback list_cont; | ||
66 | |||
67 | /** | ||
68 | * Closure for @e result_cont or @e list_cont. | ||
69 | */ | ||
70 | void *cont_cls; | ||
71 | |||
72 | /** | ||
73 | * Task for async completion. | ||
74 | */ | ||
75 | struct GNUNET_SCHEDULER_Task *async; | ||
76 | |||
77 | /** | ||
78 | * Unique ID for the request. | ||
79 | */ | ||
80 | uint64_t id; | ||
81 | |||
82 | /** | ||
83 | * Result of this operation for #notify_starting(). | ||
84 | */ | ||
85 | enum GNUNET_ARM_Result starting_ret; | ||
86 | |||
87 | /** | ||
88 | * File descriptor to close on operation stop, if not NULL. | ||
89 | */ | ||
90 | struct GNUNET_DISK_FileHandle *rfd; | ||
91 | |||
92 | /** | ||
93 | * Is this an operation to stop the ARM service? | ||
94 | */ | ||
95 | int is_arm_stop; | ||
96 | }; | ||
97 | |||
98 | |||
99 | /** | ||
100 | * Handle for interacting with ARM. | ||
101 | */ | ||
102 | struct GNUNET_ARM_Handle | ||
103 | { | ||
104 | /** | ||
105 | * Our connection to the ARM service. | ||
106 | */ | ||
107 | struct GNUNET_MQ_Handle *mq; | ||
108 | |||
109 | /** | ||
110 | * The configuration that we are using. | ||
111 | */ | ||
112 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
113 | |||
114 | /** | ||
115 | * Head of doubly-linked list of pending operations. | ||
116 | */ | ||
117 | struct GNUNET_ARM_Operation *operation_pending_head; | ||
118 | |||
119 | /** | ||
120 | * Tail of doubly-linked list of pending operations. | ||
121 | */ | ||
122 | struct GNUNET_ARM_Operation *operation_pending_tail; | ||
123 | |||
124 | /** | ||
125 | * Callback to invoke on connection/disconnection. | ||
126 | */ | ||
127 | GNUNET_ARM_ConnectionStatusCallback conn_status; | ||
128 | |||
129 | /** | ||
130 | * Closure for @e conn_status. | ||
131 | */ | ||
132 | void *conn_status_cls; | ||
133 | |||
134 | /** | ||
135 | * ARM operation where the goal is to wait for ARM shutdown to | ||
136 | * complete. This operation is special in that it waits for an | ||
137 | * error on the @e mq. So we complete it by calling the | ||
138 | * continuation in the #mq_error_handler(). Note that the operation | ||
139 | * is no longer in the @e operation_pending_head DLL once it is | ||
140 | * referenced from this field. | ||
141 | */ | ||
142 | struct GNUNET_ARM_Operation *thm; | ||
143 | |||
144 | /** | ||
145 | * ID of the reconnect task (if any). | ||
146 | */ | ||
147 | struct GNUNET_SCHEDULER_Task *reconnect_task; | ||
148 | |||
149 | /** | ||
150 | * Current delay we use for re-trying to connect to core. | ||
151 | */ | ||
152 | struct GNUNET_TIME_Relative retry_backoff; | ||
153 | |||
154 | /** | ||
155 | * Counter for request identifiers. They are used to match replies | ||
156 | * from ARM to operations in the @e operation_pending_head DLL. | ||
157 | */ | ||
158 | uint64_t request_id_counter; | ||
159 | |||
160 | /** | ||
161 | * Have we detected that ARM is up? | ||
162 | */ | ||
163 | int currently_up; | ||
164 | }; | ||
165 | |||
166 | |||
167 | /** | ||
168 | * Connect to arm. | ||
169 | * | ||
170 | * @param h arm handle | ||
171 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure | ||
172 | */ | ||
173 | static int | ||
174 | reconnect_arm (struct GNUNET_ARM_Handle *h); | ||
175 | |||
176 | |||
177 | /** | ||
178 | * Task scheduled to try to re-connect to arm. | ||
179 | * | ||
180 | * @param cls the `struct GNUNET_ARM_Handle` | ||
181 | */ | ||
182 | static void | ||
183 | reconnect_arm_task (void *cls) | ||
184 | { | ||
185 | struct GNUNET_ARM_Handle *h = cls; | ||
186 | |||
187 | h->reconnect_task = NULL; | ||
188 | reconnect_arm (h); | ||
189 | } | ||
190 | |||
191 | |||
192 | /** | ||
193 | * Close down any existing connection to the ARM service and | ||
194 | * try re-establishing it later. | ||
195 | * | ||
196 | * @param h our handle | ||
197 | */ | ||
198 | static void | ||
199 | reconnect_arm_later (struct GNUNET_ARM_Handle *h) | ||
200 | { | ||
201 | struct GNUNET_ARM_Operation *op; | ||
202 | |||
203 | if (NULL != h->mq) | ||
204 | { | ||
205 | GNUNET_MQ_destroy (h->mq); | ||
206 | h->mq = NULL; | ||
207 | } | ||
208 | h->currently_up = GNUNET_NO; | ||
209 | GNUNET_assert (NULL == h->reconnect_task); | ||
210 | h->reconnect_task = | ||
211 | GNUNET_SCHEDULER_add_delayed (h->retry_backoff, | ||
212 | &reconnect_arm_task, | ||
213 | h); | ||
214 | while (NULL != (op = h->operation_pending_head)) | ||
215 | { | ||
216 | if (NULL != op->result_cont) | ||
217 | op->result_cont (op->cont_cls, | ||
218 | GNUNET_ARM_REQUEST_DISCONNECTED, | ||
219 | 0); | ||
220 | if (NULL != op->list_cont) | ||
221 | op->list_cont (op->cont_cls, | ||
222 | GNUNET_ARM_REQUEST_DISCONNECTED, | ||
223 | 0, | ||
224 | NULL); | ||
225 | GNUNET_ARM_operation_cancel (op); | ||
226 | } | ||
227 | GNUNET_assert (NULL == h->operation_pending_head); | ||
228 | h->retry_backoff = GNUNET_TIME_STD_BACKOFF (h->retry_backoff); | ||
229 | if (NULL != h->conn_status) | ||
230 | h->conn_status (h->conn_status_cls, | ||
231 | GNUNET_NO); | ||
232 | } | ||
233 | |||
234 | |||
235 | /** | ||
236 | * Find a control message by its unique ID. | ||
237 | * | ||
238 | * @param h ARM handle | ||
239 | * @param id unique message ID to use for the lookup | ||
240 | * @return NULL if not found | ||
241 | */ | ||
242 | static struct GNUNET_ARM_Operation * | ||
243 | find_op_by_id (struct GNUNET_ARM_Handle *h, | ||
244 | uint64_t id) | ||
245 | { | ||
246 | for (struct GNUNET_ARM_Operation *result = h->operation_pending_head; | ||
247 | NULL != result; | ||
248 | result = result->next) | ||
249 | if (id == result->id) | ||
250 | return result; | ||
251 | return NULL; | ||
252 | } | ||
253 | |||
254 | |||
255 | /** | ||
256 | * Handler for ARM replies. | ||
257 | * | ||
258 | * @param cls our `struct GNUNET_ARM_Handle` | ||
259 | * @param res the message received from the arm service | ||
260 | */ | ||
261 | static void | ||
262 | handle_arm_result (void *cls, | ||
263 | const struct GNUNET_ARM_ResultMessage *res) | ||
264 | { | ||
265 | struct GNUNET_ARM_Handle *h = cls; | ||
266 | struct GNUNET_ARM_Operation *op; | ||
267 | uint64_t id; | ||
268 | enum GNUNET_ARM_Result result; | ||
269 | GNUNET_ARM_ResultCallback result_cont; | ||
270 | void *result_cont_cls; | ||
271 | |||
272 | id = GNUNET_ntohll (res->arm_msg.request_id); | ||
273 | op = find_op_by_id (h, | ||
274 | id); | ||
275 | if (NULL == op) | ||
276 | { | ||
277 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
278 | "Message with unknown id %llu\n", | ||
279 | (unsigned long long) id); | ||
280 | return; | ||
281 | } | ||
282 | |||
283 | result = (enum GNUNET_ARM_Result) ntohl (res->result); | ||
284 | if ( (GNUNET_YES == op->is_arm_stop) && | ||
285 | (GNUNET_ARM_RESULT_STOPPING == result) ) | ||
286 | { | ||
287 | /* special case: if we are stopping 'gnunet-service-arm', we do not just | ||
288 | wait for the result message, but also wait for the service to close | ||
289 | the connection (and then we have to close our client handle as well); | ||
290 | this is done by installing a different receive handler, waiting for | ||
291 | the connection to go down */if (NULL != h->thm) | ||
292 | { | ||
293 | GNUNET_break (0); | ||
294 | op->result_cont (h->thm->cont_cls, | ||
295 | GNUNET_ARM_REQUEST_SENT_OK, | ||
296 | GNUNET_ARM_RESULT_IS_NOT_KNOWN); | ||
297 | GNUNET_free (h->thm); | ||
298 | } | ||
299 | GNUNET_CONTAINER_DLL_remove (h->operation_pending_head, | ||
300 | h->operation_pending_tail, | ||
301 | op); | ||
302 | h->thm = op; | ||
303 | return; | ||
304 | } | ||
305 | result_cont = op->result_cont; | ||
306 | result_cont_cls = op->cont_cls; | ||
307 | GNUNET_ARM_operation_cancel (op); | ||
308 | if (NULL != result_cont) | ||
309 | result_cont (result_cont_cls, | ||
310 | GNUNET_ARM_REQUEST_SENT_OK, | ||
311 | result); | ||
312 | } | ||
313 | |||
314 | |||
315 | /** | ||
316 | * Read from a string pool. | ||
317 | * | ||
318 | * @param pool_start start of the string pool | ||
319 | * @param pool_size size of the string pool | ||
320 | * @param str_index index into the string pool | ||
321 | * @returns an index into the string pool, or | ||
322 | * NULL if the index is out of bounds | ||
323 | */ | ||
324 | static const char * | ||
325 | pool_get (const char *pool_start, | ||
326 | size_t pool_size, | ||
327 | size_t str_index) | ||
328 | { | ||
329 | const char *str_start; | ||
330 | const char *end; | ||
331 | |||
332 | if (str_index >= pool_size) | ||
333 | return NULL; | ||
334 | str_start = pool_start + str_index; | ||
335 | end = memchr (str_start, 0, pool_size - str_index); | ||
336 | if (NULL == end) | ||
337 | return NULL; | ||
338 | return str_start; | ||
339 | } | ||
340 | |||
341 | |||
342 | /** | ||
343 | * Check that list result message is well-formed. | ||
344 | * | ||
345 | * @param cls our `struct GNUNET_ARM_Handle` | ||
346 | * @param lres the message received from the arm service | ||
347 | * @return #GNUNET_OK if message is well-formed | ||
348 | */ | ||
349 | static int | ||
350 | check_arm_list_result (void *cls, | ||
351 | const struct GNUNET_ARM_ListResultMessage *lres) | ||
352 | { | ||
353 | uint16_t rcount = ntohs (lres->count); | ||
354 | uint16_t msize = ntohs (lres->arm_msg.header.size) - sizeof(*lres); | ||
355 | struct GNUNET_ARM_ServiceInfoMessage *ssm; | ||
356 | size_t pool_size; | ||
357 | char *pool_start; | ||
358 | |||
359 | (void) cls; | ||
360 | if ((rcount * sizeof (struct GNUNET_ARM_ServiceInfoMessage) > msize)) | ||
361 | { | ||
362 | GNUNET_break_op (0); | ||
363 | return GNUNET_NO; | ||
364 | } | ||
365 | ssm = (struct GNUNET_ARM_ServiceInfoMessage *) &lres[1]; | ||
366 | pool_start = (char *) (ssm + rcount); | ||
367 | pool_size = msize - (rcount * sizeof (struct GNUNET_ARM_ServiceInfoMessage)); | ||
368 | for (unsigned int i = 0; i < rcount; i++) | ||
369 | { | ||
370 | uint16_t name_index = ntohs (ssm->name_index); | ||
371 | uint16_t binary_index = ntohs (ssm->binary_index); | ||
372 | if (NULL == pool_get (pool_start, | ||
373 | pool_size, | ||
374 | name_index)) | ||
375 | { | ||
376 | GNUNET_break_op (0); | ||
377 | return GNUNET_NO; | ||
378 | } | ||
379 | if (NULL == pool_get (pool_start, | ||
380 | pool_size, | ||
381 | binary_index)) | ||
382 | { | ||
383 | GNUNET_break_op (0); | ||
384 | return GNUNET_NO; | ||
385 | } | ||
386 | ssm++; | ||
387 | } | ||
388 | return GNUNET_OK; | ||
389 | } | ||
390 | |||
391 | |||
392 | /** | ||
393 | * Handler for ARM list replies. | ||
394 | * | ||
395 | * @param cls our `struct GNUNET_ARM_Handle` | ||
396 | * @param lres the message received from the arm service | ||
397 | */ | ||
398 | static void | ||
399 | handle_arm_list_result (void *cls, | ||
400 | const struct GNUNET_ARM_ListResultMessage *lres) | ||
401 | { | ||
402 | struct GNUNET_ARM_Handle *h = cls; | ||
403 | uint16_t rcount = ntohs (lres->count); | ||
404 | uint16_t msize = ntohs (lres->arm_msg.header.size) - sizeof(*lres); | ||
405 | struct GNUNET_ARM_ServiceInfo list[rcount]; | ||
406 | struct GNUNET_ARM_ServiceInfoMessage *ssm; | ||
407 | struct GNUNET_ARM_Operation *op; | ||
408 | uint64_t id; | ||
409 | size_t pool_size; | ||
410 | char *pool_start; | ||
411 | |||
412 | id = GNUNET_ntohll (lres->arm_msg.request_id); | ||
413 | op = find_op_by_id (h, id); | ||
414 | if (NULL == op) | ||
415 | { | ||
416 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
417 | "Message with unknown id %llu\n", | ||
418 | (unsigned long long) id); | ||
419 | return; | ||
420 | } | ||
421 | |||
422 | GNUNET_assert ((rcount * sizeof (struct GNUNET_ARM_ServiceInfoMessage) <= | ||
423 | msize)); | ||
424 | |||
425 | ssm = (struct GNUNET_ARM_ServiceInfoMessage *) &lres[1]; | ||
426 | pool_start = (char *) (ssm + rcount); | ||
427 | pool_size = msize - (rcount * sizeof (struct GNUNET_ARM_ServiceInfoMessage)); | ||
428 | |||
429 | for (unsigned int i = 0; i < rcount; i++) | ||
430 | { | ||
431 | uint16_t name_index = ntohs (ssm->name_index); | ||
432 | uint16_t binary_index = ntohs (ssm->binary_index); | ||
433 | const char *name; | ||
434 | const char *binary; | ||
435 | |||
436 | name = pool_get (pool_start, pool_size, name_index); | ||
437 | binary = pool_get (pool_start, pool_size, binary_index); | ||
438 | GNUNET_assert (NULL != name); | ||
439 | GNUNET_assert (NULL != binary); | ||
440 | list[i] = (struct GNUNET_ARM_ServiceInfo) { | ||
441 | .name = name, | ||
442 | .binary = binary, | ||
443 | .status = ntohl (ssm->status), | ||
444 | .last_started_at = GNUNET_TIME_absolute_ntoh (ssm->last_started_at), | ||
445 | .restart_at = GNUNET_TIME_absolute_ntoh (ssm->restart_at), | ||
446 | .last_exit_status = ntohs (ssm->last_exit_status), | ||
447 | }; | ||
448 | ssm++; | ||
449 | } | ||
450 | if (NULL != op->list_cont) | ||
451 | op->list_cont (op->cont_cls, | ||
452 | GNUNET_ARM_REQUEST_SENT_OK, | ||
453 | rcount, | ||
454 | list); | ||
455 | GNUNET_ARM_operation_cancel (op); | ||
456 | } | ||
457 | |||
458 | |||
459 | /** | ||
460 | * Receive confirmation from test, ARM service is up. | ||
461 | * | ||
462 | * @param cls closure with the `struct GNUNET_ARM_Handle` | ||
463 | * @param msg message received | ||
464 | */ | ||
465 | static void | ||
466 | handle_confirm (void *cls, | ||
467 | const struct GNUNET_MessageHeader *msg) | ||
468 | { | ||
469 | struct GNUNET_ARM_Handle *h = cls; | ||
470 | |||
471 | (void) msg; | ||
472 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
473 | "Got confirmation from ARM that we are up!\n"); | ||
474 | if (GNUNET_NO == h->currently_up) | ||
475 | { | ||
476 | h->currently_up = GNUNET_YES; | ||
477 | if (NULL != h->conn_status) | ||
478 | h->conn_status (h->conn_status_cls, GNUNET_YES); | ||
479 | } | ||
480 | } | ||
481 | |||
482 | |||
483 | /** | ||
484 | * Generic error handler, called with the appropriate error code and | ||
485 | * the same closure specified at the creation of the message queue. | ||
486 | * Not every message queue implementation supports an error handler. | ||
487 | * | ||
488 | * @param cls closure with the `struct GNUNET_ARM_Handle *` | ||
489 | * @param error error code | ||
490 | */ | ||
491 | static void | ||
492 | mq_error_handler (void *cls, | ||
493 | enum GNUNET_MQ_Error error) | ||
494 | { | ||
495 | struct GNUNET_ARM_Handle *h = cls; | ||
496 | struct GNUNET_ARM_Operation *op; | ||
497 | |||
498 | (void) error; | ||
499 | h->currently_up = GNUNET_NO; | ||
500 | if (NULL != (op = h->thm)) | ||
501 | { | ||
502 | h->thm = NULL; | ||
503 | op->result_cont (op->cont_cls, | ||
504 | GNUNET_ARM_REQUEST_SENT_OK, | ||
505 | GNUNET_ARM_RESULT_STOPPED); | ||
506 | GNUNET_free (op); | ||
507 | } | ||
508 | reconnect_arm_later (h); | ||
509 | } | ||
510 | |||
511 | |||
512 | /** | ||
513 | * Connect to arm. | ||
514 | * | ||
515 | * @param h arm handle | ||
516 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure | ||
517 | */ | ||
518 | static int | ||
519 | reconnect_arm (struct GNUNET_ARM_Handle *h) | ||
520 | { | ||
521 | struct GNUNET_MQ_MessageHandler handlers[] = { | ||
522 | GNUNET_MQ_hd_fixed_size (arm_result, | ||
523 | GNUNET_MESSAGE_TYPE_ARM_RESULT, | ||
524 | struct GNUNET_ARM_ResultMessage, | ||
525 | h), | ||
526 | GNUNET_MQ_hd_var_size (arm_list_result, | ||
527 | GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT, | ||
528 | struct GNUNET_ARM_ListResultMessage, | ||
529 | h), | ||
530 | GNUNET_MQ_hd_fixed_size (confirm, | ||
531 | GNUNET_MESSAGE_TYPE_ARM_TEST, | ||
532 | struct GNUNET_MessageHeader, | ||
533 | h), | ||
534 | GNUNET_MQ_handler_end () | ||
535 | }; | ||
536 | struct GNUNET_MessageHeader *test; | ||
537 | struct GNUNET_MQ_Envelope *env; | ||
538 | |||
539 | if (NULL != h->mq) | ||
540 | return GNUNET_OK; | ||
541 | GNUNET_assert (GNUNET_NO == h->currently_up); | ||
542 | h->mq = GNUNET_CLIENT_connect (h->cfg, | ||
543 | "arm", | ||
544 | handlers, | ||
545 | &mq_error_handler, | ||
546 | h); | ||
547 | if (NULL == h->mq) | ||
548 | { | ||
549 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
550 | "GNUNET_CLIENT_connect returned NULL\n"); | ||
551 | if (NULL != h->conn_status) | ||
552 | h->conn_status (h->conn_status_cls, | ||
553 | GNUNET_SYSERR); | ||
554 | return GNUNET_SYSERR; | ||
555 | } | ||
556 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
557 | "Sending TEST message to ARM\n"); | ||
558 | env = GNUNET_MQ_msg (test, | ||
559 | GNUNET_MESSAGE_TYPE_ARM_TEST); | ||
560 | GNUNET_MQ_send (h->mq, env); | ||
561 | return GNUNET_OK; | ||
562 | } | ||
563 | |||
564 | |||
565 | /** | ||
566 | * Set up a context for communicating with ARM, then | ||
567 | * start connecting to the ARM service using that context. | ||
568 | * | ||
569 | * @param cfg configuration to use (needed to contact ARM; | ||
570 | * the ARM service may internally use a different | ||
571 | * configuration to determine how to start the service). | ||
572 | * @param conn_status will be called when connecting/disconnecting | ||
573 | * @param conn_status_cls closure for @a conn_status | ||
574 | * @return context to use for further ARM operations, NULL on error. | ||
575 | */ | ||
576 | struct GNUNET_ARM_Handle * | ||
577 | GNUNET_ARM_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
578 | GNUNET_ARM_ConnectionStatusCallback conn_status, | ||
579 | void *conn_status_cls) | ||
580 | { | ||
581 | struct GNUNET_ARM_Handle *h; | ||
582 | |||
583 | h = GNUNET_new (struct GNUNET_ARM_Handle); | ||
584 | h->cfg = cfg; | ||
585 | h->conn_status = conn_status; | ||
586 | h->conn_status_cls = conn_status_cls; | ||
587 | if (GNUNET_OK != reconnect_arm (h)) | ||
588 | { | ||
589 | GNUNET_free (h); | ||
590 | return NULL; | ||
591 | } | ||
592 | return h; | ||
593 | } | ||
594 | |||
595 | |||
596 | /** | ||
597 | * Disconnect from the ARM service (if connected) and destroy the context. | ||
598 | * | ||
599 | * @param h the handle that was being used | ||
600 | */ | ||
601 | void | ||
602 | GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *h) | ||
603 | { | ||
604 | struct GNUNET_ARM_Operation *op; | ||
605 | |||
606 | LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from ARM service\n"); | ||
607 | while (NULL != (op = h->operation_pending_head)) | ||
608 | { | ||
609 | GNUNET_CONTAINER_DLL_remove (h->operation_pending_head, | ||
610 | h->operation_pending_tail, | ||
611 | op); | ||
612 | if (NULL != op->result_cont) | ||
613 | op->result_cont (op->cont_cls, | ||
614 | GNUNET_ARM_REQUEST_DISCONNECTED, | ||
615 | 0); | ||
616 | if (NULL != op->list_cont) | ||
617 | op->list_cont (op->cont_cls, | ||
618 | GNUNET_ARM_REQUEST_DISCONNECTED, | ||
619 | 0, | ||
620 | NULL); | ||
621 | if (NULL != op->async) | ||
622 | { | ||
623 | GNUNET_SCHEDULER_cancel (op->async); | ||
624 | op->async = NULL; | ||
625 | } | ||
626 | GNUNET_free (op); | ||
627 | } | ||
628 | if (NULL != h->mq) | ||
629 | { | ||
630 | GNUNET_MQ_destroy (h->mq); | ||
631 | h->mq = NULL; | ||
632 | } | ||
633 | if (NULL != h->reconnect_task) | ||
634 | { | ||
635 | GNUNET_SCHEDULER_cancel (h->reconnect_task); | ||
636 | h->reconnect_task = NULL; | ||
637 | } | ||
638 | GNUNET_free (h); | ||
639 | } | ||
640 | |||
641 | |||
642 | /** | ||
643 | * A client specifically requested starting of ARM itself. | ||
644 | * Starts the ARM service. | ||
645 | * | ||
646 | * @param h the handle with configuration details | ||
647 | * @param std_inheritance inheritance of std streams | ||
648 | * @param sigfd socket to pass to ARM for signalling | ||
649 | * @return operation status code | ||
650 | */ | ||
651 | static enum GNUNET_ARM_Result | ||
652 | start_arm_service (struct GNUNET_ARM_Handle *h, | ||
653 | enum GNUNET_OS_InheritStdioFlags std_inheritance, | ||
654 | struct GNUNET_DISK_FileHandle *sigfd) | ||
655 | { | ||
656 | struct GNUNET_OS_Process *proc; | ||
657 | char *cbinary; | ||
658 | char *binary; | ||
659 | char *quotedbinary; | ||
660 | char *config; | ||
661 | char *loprefix; | ||
662 | char *lopostfix; | ||
663 | int ld[2]; | ||
664 | int *lsocks; | ||
665 | |||
666 | if (NULL == sigfd) | ||
667 | { | ||
668 | lsocks = NULL; | ||
669 | } | ||
670 | else | ||
671 | { | ||
672 | ld[0] = sigfd->fd; | ||
673 | ld[1] = -1; | ||
674 | lsocks = ld; | ||
675 | } | ||
676 | if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (h->cfg, | ||
677 | "arm", | ||
678 | "PREFIX", | ||
679 | &loprefix)) | ||
680 | loprefix = GNUNET_strdup (""); | ||
681 | else | ||
682 | loprefix = GNUNET_CONFIGURATION_expand_dollar (h->cfg, loprefix); | ||
683 | if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (h->cfg, | ||
684 | "arm", | ||
685 | "OPTIONS", | ||
686 | &lopostfix)) | ||
687 | lopostfix = GNUNET_strdup (""); | ||
688 | else | ||
689 | lopostfix = GNUNET_CONFIGURATION_expand_dollar (h->cfg, | ||
690 | lopostfix); | ||
691 | if (GNUNET_OK != | ||
692 | GNUNET_CONFIGURATION_get_value_string (h->cfg, | ||
693 | "arm", | ||
694 | "BINARY", | ||
695 | &cbinary)) | ||
696 | { | ||
697 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, | ||
698 | "arm", | ||
699 | "BINARY"); | ||
700 | GNUNET_free (loprefix); | ||
701 | GNUNET_free (lopostfix); | ||
702 | return GNUNET_ARM_RESULT_IS_NOT_KNOWN; | ||
703 | } | ||
704 | if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (h->cfg, | ||
705 | "arm", | ||
706 | "CONFIG", | ||
707 | &config)) | ||
708 | config = NULL; | ||
709 | binary = GNUNET_OS_get_libexec_binary_path (cbinary); | ||
710 | GNUNET_asprintf ("edbinary, | ||
711 | "\"%s\"", | ||
712 | binary); | ||
713 | GNUNET_free (cbinary); | ||
714 | if ( (GNUNET_YES == | ||
715 | GNUNET_CONFIGURATION_have_value (h->cfg, | ||
716 | "TESTING", | ||
717 | "WEAKRANDOM")) && | ||
718 | (GNUNET_YES == | ||
719 | GNUNET_CONFIGURATION_get_value_yesno (h->cfg, | ||
720 | "TESTING", | ||
721 | "WEAKRANDOM")) && | ||
722 | (GNUNET_NO == | ||
723 | GNUNET_CONFIGURATION_have_value (h->cfg, | ||
724 | "TESTING", | ||
725 | "HOSTFILE")) ) | ||
726 | { | ||
727 | /* Means we are ONLY running locally */ | ||
728 | /* we're clearly running a test, don't daemonize */ | ||
729 | if (NULL == config) | ||
730 | proc = GNUNET_OS_start_process_s (std_inheritance, | ||
731 | lsocks, | ||
732 | loprefix, | ||
733 | quotedbinary, | ||
734 | /* no daemonization! */ | ||
735 | lopostfix, | ||
736 | NULL); | ||
737 | else | ||
738 | proc = GNUNET_OS_start_process_s (std_inheritance, | ||
739 | lsocks, | ||
740 | loprefix, | ||
741 | quotedbinary, | ||
742 | "-c", | ||
743 | config, | ||
744 | /* no daemonization! */ | ||
745 | lopostfix, | ||
746 | NULL); | ||
747 | } | ||
748 | else | ||
749 | { | ||
750 | if (NULL == config) | ||
751 | proc = GNUNET_OS_start_process_s (std_inheritance, | ||
752 | lsocks, | ||
753 | loprefix, | ||
754 | quotedbinary, | ||
755 | "-d", /* do daemonize */ | ||
756 | lopostfix, | ||
757 | NULL); | ||
758 | else | ||
759 | proc = GNUNET_OS_start_process_s (std_inheritance, | ||
760 | lsocks, | ||
761 | loprefix, | ||
762 | quotedbinary, | ||
763 | "-c", | ||
764 | config, | ||
765 | "-d", /* do daemonize */ | ||
766 | lopostfix, | ||
767 | NULL); | ||
768 | } | ||
769 | GNUNET_free (binary); | ||
770 | GNUNET_free (quotedbinary); | ||
771 | if (NULL != config) | ||
772 | GNUNET_free (config); | ||
773 | GNUNET_free (loprefix); | ||
774 | GNUNET_free (lopostfix); | ||
775 | if (NULL == proc) | ||
776 | return GNUNET_ARM_RESULT_START_FAILED; | ||
777 | GNUNET_OS_process_destroy (proc); | ||
778 | return GNUNET_ARM_RESULT_STARTING; | ||
779 | } | ||
780 | |||
781 | |||
782 | /** | ||
783 | * Abort an operation. Only prevents the callback from being | ||
784 | * called, the operation may still complete. | ||
785 | * | ||
786 | * @param op operation to cancel | ||
787 | */ | ||
788 | void | ||
789 | GNUNET_ARM_operation_cancel (struct GNUNET_ARM_Operation *op) | ||
790 | { | ||
791 | struct GNUNET_ARM_Handle *h = op->h; | ||
792 | |||
793 | if (NULL != op->async) | ||
794 | { | ||
795 | GNUNET_SCHEDULER_cancel (op->async); | ||
796 | op->async = NULL; | ||
797 | } | ||
798 | if (NULL != op->rfd) | ||
799 | { | ||
800 | GNUNET_DISK_file_close (op->rfd); | ||
801 | op->rfd = NULL; | ||
802 | } | ||
803 | if (h->thm == op) | ||
804 | { | ||
805 | op->result_cont = NULL; | ||
806 | return; | ||
807 | } | ||
808 | GNUNET_CONTAINER_DLL_remove (h->operation_pending_head, | ||
809 | h->operation_pending_tail, | ||
810 | op); | ||
811 | GNUNET_free (op); | ||
812 | } | ||
813 | |||
814 | |||
815 | /** | ||
816 | * Start or stop a service. | ||
817 | * | ||
818 | * @param h handle to ARM | ||
819 | * @param service_name name of the service | ||
820 | * @param cb callback to invoke when service is ready | ||
821 | * @param cb_cls closure for @a cb | ||
822 | * @param type type of the request | ||
823 | * @return handle to queue, NULL on error | ||
824 | */ | ||
825 | static struct GNUNET_ARM_Operation * | ||
826 | change_service (struct GNUNET_ARM_Handle *h, | ||
827 | const char *service_name, | ||
828 | GNUNET_ARM_ResultCallback cb, | ||
829 | void *cb_cls, | ||
830 | uint16_t type) | ||
831 | { | ||
832 | struct GNUNET_ARM_Operation *op; | ||
833 | size_t slen; | ||
834 | struct GNUNET_MQ_Envelope *env; | ||
835 | struct GNUNET_ARM_Message *msg; | ||
836 | |||
837 | slen = strlen (service_name) + 1; | ||
838 | if (slen + sizeof(struct GNUNET_ARM_Message) >= GNUNET_MAX_MESSAGE_SIZE) | ||
839 | { | ||
840 | GNUNET_break (0); | ||
841 | return NULL; | ||
842 | } | ||
843 | if (0 == h->request_id_counter) | ||
844 | h->request_id_counter++; | ||
845 | op = GNUNET_new (struct GNUNET_ARM_Operation); | ||
846 | op->h = h; | ||
847 | op->result_cont = cb; | ||
848 | op->cont_cls = cb_cls; | ||
849 | op->id = h->request_id_counter++; | ||
850 | GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head, | ||
851 | h->operation_pending_tail, | ||
852 | op); | ||
853 | env = GNUNET_MQ_msg_extra (msg, slen, type); | ||
854 | msg->reserved = htonl (0); | ||
855 | msg->request_id = GNUNET_htonll (op->id); | ||
856 | GNUNET_memcpy (&msg[1], service_name, slen); | ||
857 | GNUNET_MQ_send (h->mq, env); | ||
858 | return op; | ||
859 | } | ||
860 | |||
861 | |||
862 | /** | ||
863 | * Task run to notify application that ARM is already up. | ||
864 | * | ||
865 | * @param cls the operation that asked ARM to be started | ||
866 | */ | ||
867 | static void | ||
868 | notify_running (void *cls) | ||
869 | { | ||
870 | struct GNUNET_ARM_Operation *op = cls; | ||
871 | struct GNUNET_ARM_Handle *h = op->h; | ||
872 | |||
873 | op->async = NULL; | ||
874 | GNUNET_CONTAINER_DLL_remove (h->operation_pending_head, | ||
875 | h->operation_pending_tail, | ||
876 | op); | ||
877 | if (NULL != op->result_cont) | ||
878 | op->result_cont (op->cont_cls, | ||
879 | GNUNET_ARM_REQUEST_SENT_OK, | ||
880 | GNUNET_ARM_RESULT_IS_STARTED_ALREADY); | ||
881 | if ( (GNUNET_YES == h->currently_up) && | ||
882 | (NULL != h->conn_status) ) | ||
883 | h->conn_status (h->conn_status_cls, | ||
884 | GNUNET_YES); | ||
885 | GNUNET_free (op); | ||
886 | } | ||
887 | |||
888 | |||
889 | /** | ||
890 | * Task run to notify application that ARM is being started. | ||
891 | * | ||
892 | * @param cls the operation that asked ARM to be started | ||
893 | */ | ||
894 | static void | ||
895 | notify_starting (void *cls) | ||
896 | { | ||
897 | struct GNUNET_ARM_Operation *op = cls; | ||
898 | struct GNUNET_ARM_Handle *h = op->h; | ||
899 | |||
900 | op->async = NULL; | ||
901 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
902 | "Notifying client that we started the ARM service\n"); | ||
903 | GNUNET_CONTAINER_DLL_remove (h->operation_pending_head, | ||
904 | h->operation_pending_tail, | ||
905 | op); | ||
906 | if (NULL != op->result_cont) | ||
907 | op->result_cont (op->cont_cls, | ||
908 | GNUNET_ARM_REQUEST_SENT_OK, | ||
909 | op->starting_ret); | ||
910 | GNUNET_free (op); | ||
911 | } | ||
912 | |||
913 | |||
914 | /** | ||
915 | * Request for a service to be started. | ||
916 | * | ||
917 | * @param h handle to ARM | ||
918 | * @param service_name name of the service | ||
919 | * @param std_inheritance inheritance of std streams | ||
920 | * @param cont callback to invoke after request is sent or not sent | ||
921 | * @param cont_cls closure for @a cont | ||
922 | * @return handle for the operation, NULL on error | ||
923 | */ | ||
924 | struct GNUNET_ARM_Operation * | ||
925 | GNUNET_ARM_request_service_start (struct GNUNET_ARM_Handle *h, | ||
926 | const char *service_name, | ||
927 | enum GNUNET_OS_InheritStdioFlags | ||
928 | std_inheritance, | ||
929 | GNUNET_ARM_ResultCallback cont, | ||
930 | void *cont_cls) | ||
931 | { | ||
932 | struct GNUNET_ARM_Operation *op; | ||
933 | enum GNUNET_ARM_Result ret; | ||
934 | struct GNUNET_DISK_PipeHandle *sig; | ||
935 | struct GNUNET_DISK_FileHandle *wsig; | ||
936 | |||
937 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
938 | "Starting service `%s'\n", | ||
939 | service_name); | ||
940 | if (0 != strcasecmp ("arm", | ||
941 | service_name)) | ||
942 | return change_service (h, | ||
943 | service_name, | ||
944 | cont, | ||
945 | cont_cls, | ||
946 | GNUNET_MESSAGE_TYPE_ARM_START); | ||
947 | |||
948 | /* Possible cases: | ||
949 | * 1) We're connected to ARM already. Invoke the callback immediately. | ||
950 | * 2) We're not connected to ARM. | ||
951 | * Cancel any reconnection attempts temporarily, then perform | ||
952 | * a service test. | ||
953 | */ | ||
954 | if (GNUNET_YES == h->currently_up) | ||
955 | { | ||
956 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
957 | "ARM is already running\n"); | ||
958 | op = GNUNET_new (struct GNUNET_ARM_Operation); | ||
959 | op->h = h; | ||
960 | op->result_cont = cont; | ||
961 | op->cont_cls = cont_cls; | ||
962 | GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head, | ||
963 | h->operation_pending_tail, | ||
964 | op); | ||
965 | op->async = GNUNET_SCHEDULER_add_now (¬ify_running, op); | ||
966 | return op; | ||
967 | } | ||
968 | /* This is an inherently uncertain choice, as it is of course | ||
969 | theoretically possible that ARM is up and we just did not | ||
970 | yet complete the MQ handshake. However, given that users | ||
971 | are unlikely to hammer 'gnunet-arm -s' on a busy system, | ||
972 | the above check should catch 99.99% of the cases where ARM | ||
973 | is already running. */ | ||
974 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
975 | "Starting ARM service\n"); | ||
976 | if (NULL == (sig = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE))) | ||
977 | { | ||
978 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, | ||
979 | "pipe"); | ||
980 | ret = GNUNET_ARM_RESULT_START_FAILED; | ||
981 | } | ||
982 | else | ||
983 | { | ||
984 | wsig = GNUNET_DISK_pipe_detach_end (sig, | ||
985 | GNUNET_DISK_PIPE_END_WRITE); | ||
986 | ret = start_arm_service (h, | ||
987 | std_inheritance, | ||
988 | wsig); | ||
989 | GNUNET_DISK_file_close (wsig); | ||
990 | if (GNUNET_ARM_RESULT_STARTING == ret) | ||
991 | reconnect_arm (h); | ||
992 | } | ||
993 | op = GNUNET_new (struct GNUNET_ARM_Operation); | ||
994 | op->h = h; | ||
995 | op->result_cont = cont; | ||
996 | op->cont_cls = cont_cls; | ||
997 | GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head, | ||
998 | h->operation_pending_tail, | ||
999 | op); | ||
1000 | op->starting_ret = ret; | ||
1001 | if (NULL != sig) | ||
1002 | { | ||
1003 | op->rfd = GNUNET_DISK_pipe_detach_end (sig, | ||
1004 | GNUNET_DISK_PIPE_END_READ); | ||
1005 | /* Wait at most a minute for gnunet-service-arm to be up, as beyond | ||
1006 | that something clearly just went wrong */ | ||
1007 | op->async = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_MINUTES, | ||
1008 | op->rfd, | ||
1009 | ¬ify_starting, | ||
1010 | op); | ||
1011 | GNUNET_DISK_pipe_close (sig); | ||
1012 | } | ||
1013 | else | ||
1014 | { | ||
1015 | op->async = GNUNET_SCHEDULER_add_now (¬ify_starting, | ||
1016 | op); | ||
1017 | } | ||
1018 | return op; | ||
1019 | } | ||
1020 | |||
1021 | |||
1022 | /** | ||
1023 | * Request a service to be stopped. Stopping arm itself will not | ||
1024 | * invalidate its handle, and ARM API will try to restore connection | ||
1025 | * to the ARM service, even if ARM connection was lost because you | ||
1026 | * asked for ARM to be stopped. Call | ||
1027 | * #GNUNET_ARM_disconnect() to free the handle and prevent | ||
1028 | * further connection attempts. | ||
1029 | * | ||
1030 | * @param h handle to ARM | ||
1031 | * @param service_name name of the service | ||
1032 | * @param cont callback to invoke after request is sent or is not sent | ||
1033 | * @param cont_cls closure for @a cont | ||
1034 | * @return handle for the operation, NULL on error | ||
1035 | */ | ||
1036 | struct GNUNET_ARM_Operation * | ||
1037 | GNUNET_ARM_request_service_stop (struct GNUNET_ARM_Handle *h, | ||
1038 | const char *service_name, | ||
1039 | GNUNET_ARM_ResultCallback cont, | ||
1040 | void *cont_cls) | ||
1041 | { | ||
1042 | struct GNUNET_ARM_Operation *op; | ||
1043 | |||
1044 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1045 | "Stopping service `%s'\n", | ||
1046 | service_name); | ||
1047 | op = change_service (h, | ||
1048 | service_name, | ||
1049 | cont, | ||
1050 | cont_cls, | ||
1051 | GNUNET_MESSAGE_TYPE_ARM_STOP); | ||
1052 | if (NULL == op) | ||
1053 | return NULL; | ||
1054 | /* If the service is ARM, set a flag as we will use MQ errors | ||
1055 | to detect that the process is really gone. */ | ||
1056 | if (0 == strcasecmp (service_name, | ||
1057 | "arm")) | ||
1058 | op->is_arm_stop = GNUNET_YES; | ||
1059 | return op; | ||
1060 | } | ||
1061 | |||
1062 | |||
1063 | /** | ||
1064 | * Request a list of running services. | ||
1065 | * | ||
1066 | * @param h handle to ARM | ||
1067 | * @param cont callback to invoke after request is sent or is not sent | ||
1068 | * @param cont_cls closure for @a cont | ||
1069 | * @return handle for the operation, NULL on error | ||
1070 | */ | ||
1071 | struct GNUNET_ARM_Operation * | ||
1072 | GNUNET_ARM_request_service_list (struct GNUNET_ARM_Handle *h, | ||
1073 | GNUNET_ARM_ServiceListCallback cont, | ||
1074 | void *cont_cls) | ||
1075 | { | ||
1076 | struct GNUNET_ARM_Operation *op; | ||
1077 | struct GNUNET_MQ_Envelope *env; | ||
1078 | struct GNUNET_ARM_Message *msg; | ||
1079 | |||
1080 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1081 | "Requesting LIST from ARM service\n"); | ||
1082 | if (0 == h->request_id_counter) | ||
1083 | h->request_id_counter++; | ||
1084 | op = GNUNET_new (struct GNUNET_ARM_Operation); | ||
1085 | op->h = h; | ||
1086 | op->list_cont = cont; | ||
1087 | op->cont_cls = cont_cls; | ||
1088 | op->id = h->request_id_counter++; | ||
1089 | GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head, | ||
1090 | h->operation_pending_tail, | ||
1091 | op); | ||
1092 | env = GNUNET_MQ_msg (msg, | ||
1093 | GNUNET_MESSAGE_TYPE_ARM_LIST); | ||
1094 | msg->reserved = htonl (0); | ||
1095 | msg->request_id = GNUNET_htonll (op->id); | ||
1096 | GNUNET_MQ_send (h->mq, env); | ||
1097 | return op; | ||
1098 | } | ||
1099 | |||
1100 | |||
1101 | /* end of arm_api.c */ | ||