aboutsummaryrefslogtreecommitdiff
path: root/src/arm/arm_api.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/arm/arm_api.c')
-rw-r--r--src/arm/arm_api.c1101
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 */
40struct 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 */
102struct 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 */
173static int
174reconnect_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 */
182static void
183reconnect_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 */
198static void
199reconnect_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 */
242static struct GNUNET_ARM_Operation *
243find_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 */
261static void
262handle_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 */
324static const char *
325pool_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 */
349static int
350check_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 */
398static void
399handle_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 */
465static void
466handle_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 */
491static void
492mq_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 */
518static int
519reconnect_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 */
576struct GNUNET_ARM_Handle *
577GNUNET_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 */
601void
602GNUNET_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 */
651static enum GNUNET_ARM_Result
652start_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 (&quotedbinary,
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 */
788void
789GNUNET_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 */
825static struct GNUNET_ARM_Operation *
826change_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 */
867static void
868notify_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 */
894static void
895notify_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 */
924struct GNUNET_ARM_Operation *
925GNUNET_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 (&notify_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 &notify_starting,
1010 op);
1011 GNUNET_DISK_pipe_close (sig);
1012 }
1013 else
1014 {
1015 op->async = GNUNET_SCHEDULER_add_now (&notify_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 */
1036struct GNUNET_ARM_Operation *
1037GNUNET_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 */
1071struct GNUNET_ARM_Operation *
1072GNUNET_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 */