use NULL value in load_path_suffix to NOT load any files
[oweals/gnunet.git] / src / arm / arm_api.c
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     GNUNET_assert (NULL != (name = pool_get (pool_start,
437                                              pool_size,
438                                              name_index)));
439     GNUNET_assert (NULL != (binary = pool_get (pool_start,
440                                                pool_size,
441                                                binary_index)));
442     list[i] = (struct GNUNET_ARM_ServiceInfo) {
443       .name = name,
444       .binary = binary,
445       .status = ntohl (ssm->status),
446       .last_started_at = GNUNET_TIME_absolute_ntoh (ssm->last_started_at),
447       .restart_at = GNUNET_TIME_absolute_ntoh (ssm->restart_at),
448       .last_exit_status = ntohs (ssm->last_exit_status),
449     };
450     ssm++;
451   }
452   if (NULL != op->list_cont)
453     op->list_cont (op->cont_cls,
454                    GNUNET_ARM_REQUEST_SENT_OK,
455                    rcount,
456                    list);
457   GNUNET_ARM_operation_cancel (op);
458 }
459
460
461 /**
462  * Receive confirmation from test, ARM service is up.
463  *
464  * @param cls closure with the `struct GNUNET_ARM_Handle`
465  * @param msg message received
466  */
467 static void
468 handle_confirm (void *cls,
469                 const struct GNUNET_MessageHeader *msg)
470 {
471   struct GNUNET_ARM_Handle *h = cls;
472
473   (void) msg;
474   LOG (GNUNET_ERROR_TYPE_DEBUG,
475        "Got confirmation from ARM that we are up!\n");
476   if (GNUNET_NO == h->currently_up)
477   {
478     h->currently_up = GNUNET_YES;
479     if (NULL != h->conn_status)
480       h->conn_status (h->conn_status_cls, GNUNET_YES);
481   }
482 }
483
484
485 /**
486  * Generic error handler, called with the appropriate error code and
487  * the same closure specified at the creation of the message queue.
488  * Not every message queue implementation supports an error handler.
489  *
490  * @param cls closure with the `struct GNUNET_ARM_Handle *`
491  * @param error error code
492  */
493 static void
494 mq_error_handler (void *cls,
495                   enum GNUNET_MQ_Error error)
496 {
497   struct GNUNET_ARM_Handle *h = cls;
498   struct GNUNET_ARM_Operation *op;
499
500   (void) error;
501   h->currently_up = GNUNET_NO;
502   if (NULL != (op = h->thm))
503   {
504     h->thm = NULL;
505     op->result_cont (op->cont_cls,
506                      GNUNET_ARM_REQUEST_SENT_OK,
507                      GNUNET_ARM_RESULT_STOPPED);
508     GNUNET_free (op);
509   }
510   reconnect_arm_later (h);
511 }
512
513
514 /**
515  * Connect to arm.
516  *
517  * @param h arm handle
518  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
519  */
520 static int
521 reconnect_arm (struct GNUNET_ARM_Handle *h)
522 {
523   struct GNUNET_MQ_MessageHandler handlers[] = {
524     GNUNET_MQ_hd_fixed_size (arm_result,
525                              GNUNET_MESSAGE_TYPE_ARM_RESULT,
526                              struct GNUNET_ARM_ResultMessage,
527                              h),
528     GNUNET_MQ_hd_var_size (arm_list_result,
529                            GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT,
530                            struct GNUNET_ARM_ListResultMessage,
531                            h),
532     GNUNET_MQ_hd_fixed_size (confirm,
533                              GNUNET_MESSAGE_TYPE_ARM_TEST,
534                              struct GNUNET_MessageHeader,
535                              h),
536     GNUNET_MQ_handler_end ()
537   };
538   struct GNUNET_MessageHeader *test;
539   struct GNUNET_MQ_Envelope *env;
540
541   if (NULL != h->mq)
542     return GNUNET_OK;
543   GNUNET_assert (GNUNET_NO == h->currently_up);
544   h->mq = GNUNET_CLIENT_connect (h->cfg,
545                                  "arm",
546                                  handlers,
547                                  &mq_error_handler,
548                                  h);
549   if (NULL == h->mq)
550   {
551     LOG (GNUNET_ERROR_TYPE_DEBUG,
552          "GNUNET_CLIENT_connect returned NULL\n");
553     if (NULL != h->conn_status)
554       h->conn_status (h->conn_status_cls,
555                       GNUNET_SYSERR);
556     return GNUNET_SYSERR;
557   }
558   LOG (GNUNET_ERROR_TYPE_DEBUG,
559        "Sending TEST message to ARM\n");
560   env = GNUNET_MQ_msg (test,
561                        GNUNET_MESSAGE_TYPE_ARM_TEST);
562   GNUNET_MQ_send (h->mq, env);
563   return GNUNET_OK;
564 }
565
566
567 /**
568  * Set up a context for communicating with ARM, then
569  * start connecting to the ARM service using that context.
570  *
571  * @param cfg configuration to use (needed to contact ARM;
572  *        the ARM service may internally use a different
573  *        configuration to determine how to start the service).
574  * @param conn_status will be called when connecting/disconnecting
575  * @param conn_status_cls closure for @a conn_status
576  * @return context to use for further ARM operations, NULL on error.
577  */
578 struct GNUNET_ARM_Handle *
579 GNUNET_ARM_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
580                     GNUNET_ARM_ConnectionStatusCallback conn_status,
581                     void *conn_status_cls)
582 {
583   struct GNUNET_ARM_Handle *h;
584
585   h = GNUNET_new (struct GNUNET_ARM_Handle);
586   h->cfg = cfg;
587   h->conn_status = conn_status;
588   h->conn_status_cls = conn_status_cls;
589   if (GNUNET_OK != reconnect_arm (h))
590   {
591     GNUNET_free (h);
592     return NULL;
593   }
594   return h;
595 }
596
597
598 /**
599  * Disconnect from the ARM service (if connected) and destroy the context.
600  *
601  * @param h the handle that was being used
602  */
603 void
604 GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *h)
605 {
606   struct GNUNET_ARM_Operation *op;
607
608   LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from ARM service\n");
609   while (NULL != (op = h->operation_pending_head))
610   {
611     GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
612                                  h->operation_pending_tail,
613                                  op);
614     if (NULL != op->result_cont)
615       op->result_cont (op->cont_cls,
616                        GNUNET_ARM_REQUEST_DISCONNECTED,
617                        0);
618     if (NULL != op->list_cont)
619       op->list_cont (op->cont_cls,
620                      GNUNET_ARM_REQUEST_DISCONNECTED,
621                      0,
622                      NULL);
623     if (NULL != op->async)
624     {
625       GNUNET_SCHEDULER_cancel (op->async);
626       op->async = NULL;
627     }
628     GNUNET_free (op);
629   }
630   if (NULL != h->mq)
631   {
632     GNUNET_MQ_destroy (h->mq);
633     h->mq = NULL;
634   }
635   if (NULL != h->reconnect_task)
636   {
637     GNUNET_SCHEDULER_cancel (h->reconnect_task);
638     h->reconnect_task = NULL;
639   }
640   GNUNET_free (h);
641 }
642
643
644 /**
645  * A client specifically requested starting of ARM itself.
646  * Starts the ARM service.
647  *
648  * @param h the handle with configuration details
649  * @param std_inheritance inheritance of std streams
650  * @param sigfd socket to pass to ARM for signalling
651  * @return operation status code
652  */
653 static enum GNUNET_ARM_Result
654 start_arm_service (struct GNUNET_ARM_Handle *h,
655                    enum GNUNET_OS_InheritStdioFlags std_inheritance,
656                    struct GNUNET_DISK_FileHandle *sigfd)
657 {
658   struct GNUNET_OS_Process *proc;
659   char *cbinary;
660   char *binary;
661   char *quotedbinary;
662   char *config;
663   char *loprefix;
664   char *lopostfix;
665   int ld[2];
666   int *lsocks;
667
668   if (NULL == sigfd)
669   {
670     lsocks = NULL;
671   }
672   else
673   {
674     ld[0] = sigfd->fd;
675     ld[1] = -1;
676     lsocks = ld;
677   }
678   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (h->cfg,
679                                                           "arm",
680                                                           "PREFIX",
681                                                           &loprefix))
682     loprefix = GNUNET_strdup ("");
683   else
684     loprefix = GNUNET_CONFIGURATION_expand_dollar (h->cfg, loprefix);
685   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (h->cfg,
686                                                           "arm",
687                                                           "OPTIONS",
688                                                           &lopostfix))
689     lopostfix = GNUNET_strdup ("");
690   else
691     lopostfix = GNUNET_CONFIGURATION_expand_dollar (h->cfg,
692                                                     lopostfix);
693   if (GNUNET_OK !=
694       GNUNET_CONFIGURATION_get_value_string (h->cfg,
695                                              "arm",
696                                              "BINARY",
697                                              &cbinary))
698   {
699     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
700                                "arm",
701                                "BINARY");
702     GNUNET_free (loprefix);
703     GNUNET_free (lopostfix);
704     return GNUNET_ARM_RESULT_IS_NOT_KNOWN;
705   }
706   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (h->cfg,
707                                                             "arm",
708                                                             "CONFIG",
709                                                             &config))
710     config = NULL;
711   binary = GNUNET_OS_get_libexec_binary_path (cbinary);
712   GNUNET_asprintf (&quotedbinary,
713                    "\"%s\"",
714                    binary);
715   GNUNET_free (cbinary);
716   if ( (GNUNET_YES ==
717         GNUNET_CONFIGURATION_have_value (h->cfg,
718                                          "TESTING",
719                                          "WEAKRANDOM")) &&
720        (GNUNET_YES ==
721         GNUNET_CONFIGURATION_get_value_yesno (h->cfg,
722                                               "TESTING",
723                                               "WEAKRANDOM")) &&
724        (GNUNET_NO ==
725         GNUNET_CONFIGURATION_have_value (h->cfg,
726                                          "TESTING",
727                                          "HOSTFILE")) )
728   {
729     /* Means we are ONLY running locally */
730     /* we're clearly running a test, don't daemonize */
731     if (NULL == config)
732       proc = GNUNET_OS_start_process_s (GNUNET_NO,
733                                         std_inheritance,
734                                         lsocks,
735                                         loprefix,
736                                         quotedbinary,
737                                         /* no daemonization! */
738                                         lopostfix,
739                                         NULL);
740     else
741       proc = GNUNET_OS_start_process_s (GNUNET_NO,
742                                         std_inheritance,
743                                         lsocks,
744                                         loprefix,
745                                         quotedbinary,
746                                         "-c",
747                                         config,
748                                         /* no daemonization! */
749                                         lopostfix,
750                                         NULL);
751   }
752   else
753   {
754     if (NULL == config)
755       proc = GNUNET_OS_start_process_s (GNUNET_NO,
756                                         std_inheritance,
757                                         lsocks,
758                                         loprefix,
759                                         quotedbinary,
760                                         "-d",  /* do daemonize */
761                                         lopostfix,
762                                         NULL);
763     else
764       proc = GNUNET_OS_start_process_s (GNUNET_NO,
765                                         std_inheritance,
766                                         lsocks,
767                                         loprefix,
768                                         quotedbinary,
769                                         "-c",
770                                         config,
771                                         "-d",  /* do daemonize */
772                                         lopostfix,
773                                         NULL);
774   }
775   GNUNET_free (binary);
776   GNUNET_free (quotedbinary);
777   GNUNET_free_non_null (config);
778   GNUNET_free (loprefix);
779   GNUNET_free (lopostfix);
780   if (NULL == proc)
781     return GNUNET_ARM_RESULT_START_FAILED;
782   GNUNET_OS_process_destroy (proc);
783   return GNUNET_ARM_RESULT_STARTING;
784 }
785
786
787 /**
788  * Abort an operation.  Only prevents the callback from being
789  * called, the operation may still complete.
790  *
791  * @param op operation to cancel
792  */
793 void
794 GNUNET_ARM_operation_cancel (struct GNUNET_ARM_Operation *op)
795 {
796   struct GNUNET_ARM_Handle *h = op->h;
797
798   if (NULL != op->async)
799   {
800     GNUNET_SCHEDULER_cancel (op->async);
801     op->async = NULL;
802   }
803   if (NULL != op->rfd)
804   {
805     GNUNET_DISK_file_close (op->rfd);
806     op->rfd = NULL;
807   }
808   if (h->thm == op)
809   {
810     op->result_cont = NULL;
811     return;
812   }
813   GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
814                                h->operation_pending_tail,
815                                op);
816   GNUNET_free (op);
817 }
818
819
820 /**
821  * Start or stop a service.
822  *
823  * @param h handle to ARM
824  * @param service_name name of the service
825  * @param cb callback to invoke when service is ready
826  * @param cb_cls closure for @a cb
827  * @param type type of the request
828  * @return handle to queue, NULL on error
829  */
830 static struct GNUNET_ARM_Operation *
831 change_service (struct GNUNET_ARM_Handle *h,
832                 const char *service_name,
833                 GNUNET_ARM_ResultCallback cb,
834                 void *cb_cls,
835                 uint16_t type)
836 {
837   struct GNUNET_ARM_Operation *op;
838   size_t slen;
839   struct GNUNET_MQ_Envelope *env;
840   struct GNUNET_ARM_Message *msg;
841
842   slen = strlen (service_name) + 1;
843   if (slen + sizeof(struct GNUNET_ARM_Message) >= GNUNET_MAX_MESSAGE_SIZE)
844   {
845     GNUNET_break (0);
846     return NULL;
847   }
848   if (0 == h->request_id_counter)
849     h->request_id_counter++;
850   op = GNUNET_new (struct GNUNET_ARM_Operation);
851   op->h = h;
852   op->result_cont = cb;
853   op->cont_cls = cb_cls;
854   op->id = h->request_id_counter++;
855   GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
856                                     h->operation_pending_tail,
857                                     op);
858   env = GNUNET_MQ_msg_extra (msg, slen, type);
859   msg->reserved = htonl (0);
860   msg->request_id = GNUNET_htonll (op->id);
861   GNUNET_memcpy (&msg[1], service_name, slen);
862   GNUNET_MQ_send (h->mq, env);
863   return op;
864 }
865
866
867 /**
868  * Task run to notify application that ARM is already up.
869  *
870  * @param cls the operation that asked ARM to be started
871  */
872 static void
873 notify_running (void *cls)
874 {
875   struct GNUNET_ARM_Operation *op = cls;
876   struct GNUNET_ARM_Handle *h = op->h;
877
878   op->async = NULL;
879   GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
880                                h->operation_pending_tail,
881                                op);
882   if (NULL != op->result_cont)
883     op->result_cont (op->cont_cls,
884                      GNUNET_ARM_REQUEST_SENT_OK,
885                      GNUNET_ARM_RESULT_IS_STARTED_ALREADY);
886   if ( (GNUNET_YES == h->currently_up) &&
887        (NULL != h->conn_status) )
888     h->conn_status (h->conn_status_cls,
889                     GNUNET_YES);
890   GNUNET_free (op);
891 }
892
893
894 /**
895  * Task run to notify application that ARM is being started.
896  *
897  * @param cls the operation that asked ARM to be started
898  */
899 static void
900 notify_starting (void *cls)
901 {
902   struct GNUNET_ARM_Operation *op = cls;
903   struct GNUNET_ARM_Handle *h = op->h;
904
905   op->async = NULL;
906   LOG (GNUNET_ERROR_TYPE_DEBUG,
907        "Notifying client that we started the ARM service\n");
908   GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
909                                h->operation_pending_tail,
910                                op);
911   if (NULL != op->result_cont)
912     op->result_cont (op->cont_cls,
913                      GNUNET_ARM_REQUEST_SENT_OK,
914                      op->starting_ret);
915   GNUNET_free (op);
916 }
917
918
919 /**
920  * Request for a service to be started.
921  *
922  * @param h handle to ARM
923  * @param service_name name of the service
924  * @param std_inheritance inheritance of std streams
925  * @param cont callback to invoke after request is sent or not sent
926  * @param cont_cls closure for @a cont
927  * @return handle for the operation, NULL on error
928  */
929 struct GNUNET_ARM_Operation *
930 GNUNET_ARM_request_service_start (struct GNUNET_ARM_Handle *h,
931                                   const char *service_name,
932                                   enum GNUNET_OS_InheritStdioFlags
933                                   std_inheritance,
934                                   GNUNET_ARM_ResultCallback cont,
935                                   void *cont_cls)
936 {
937   struct GNUNET_ARM_Operation *op;
938   enum GNUNET_ARM_Result ret;
939   struct GNUNET_DISK_PipeHandle *sig;
940   struct GNUNET_DISK_FileHandle *wsig;
941
942   LOG (GNUNET_ERROR_TYPE_DEBUG,
943        "Starting service `%s'\n",
944        service_name);
945   if (0 != strcasecmp ("arm",
946                        service_name))
947     return change_service (h,
948                            service_name,
949                            cont,
950                            cont_cls,
951                            GNUNET_MESSAGE_TYPE_ARM_START);
952
953   /* Possible cases:
954    * 1) We're connected to ARM already. Invoke the callback immediately.
955    * 2) We're not connected to ARM.
956    *    Cancel any reconnection attempts temporarily, then perform
957    *    a service test.
958    */if (GNUNET_YES == h->currently_up)
959   {
960     LOG (GNUNET_ERROR_TYPE_DEBUG,
961          "ARM is already running\n");
962     op = GNUNET_new (struct GNUNET_ARM_Operation);
963     op->h = h;
964     op->result_cont = cont;
965     op->cont_cls = cont_cls;
966     GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
967                                       h->operation_pending_tail,
968                                       op);
969     op->async = GNUNET_SCHEDULER_add_now (&notify_running, op);
970     return op;
971   }
972   /* This is an inherently uncertain choice, as it is of course
973      theoretically possible that ARM is up and we just did not
974      yet complete the MQ handshake.  However, given that users
975      are unlikely to hammer 'gnunet-arm -s' on a busy system,
976      the above check should catch 99.99% of the cases where ARM
977      is already running. */LOG (GNUNET_ERROR_TYPE_DEBUG,
978        "Starting ARM service\n");
979   if (NULL == (sig = GNUNET_DISK_pipe (GNUNET_NO,
980                                        GNUNET_NO,
981                                        GNUNET_NO,
982                                        GNUNET_YES)))
983   {
984     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
985                          "pipe");
986   }
987   wsig = GNUNET_DISK_pipe_detach_end (sig,
988                                       GNUNET_DISK_PIPE_END_WRITE);
989   ret = start_arm_service (h,
990                            std_inheritance,
991                            wsig);
992   GNUNET_DISK_file_close (wsig);
993   if (GNUNET_ARM_RESULT_STARTING == ret)
994     reconnect_arm (h);
995   op = GNUNET_new (struct GNUNET_ARM_Operation);
996   op->h = h;
997   op->result_cont = cont;
998   op->cont_cls = cont_cls;
999   GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
1000                                     h->operation_pending_tail,
1001                                     op);
1002   op->starting_ret = ret;
1003   if (NULL != sig)
1004   {
1005     op->rfd = GNUNET_DISK_pipe_detach_end (sig,
1006                                            GNUNET_DISK_PIPE_END_READ);
1007     /* Wait at most a minute for gnunet-service-arm to be up, as beyond
1008        that something clearly just went wrong */
1009     op->async = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_MINUTES,
1010                                                 op->rfd,
1011                                                 &notify_starting,
1012                                                 op);
1013   }
1014   else
1015   {
1016     op->async = GNUNET_SCHEDULER_add_now (&notify_starting,
1017                                           op);
1018   }
1019   GNUNET_DISK_pipe_close (sig);
1020   return op;
1021 }
1022
1023
1024 /**
1025  * Request a service to be stopped.  Stopping arm itself will not
1026  * invalidate its handle, and ARM API will try to restore connection
1027  * to the ARM service, even if ARM connection was lost because you
1028  * asked for ARM to be stopped.  Call
1029  * #GNUNET_ARM_disconnect() to free the handle and prevent
1030  * further connection attempts.
1031  *
1032  * @param h handle to ARM
1033  * @param service_name name of the service
1034  * @param cont callback to invoke after request is sent or is not sent
1035  * @param cont_cls closure for @a cont
1036  * @return handle for the operation, NULL on error
1037  */
1038 struct GNUNET_ARM_Operation *
1039 GNUNET_ARM_request_service_stop (struct GNUNET_ARM_Handle *h,
1040                                  const char *service_name,
1041                                  GNUNET_ARM_ResultCallback cont,
1042                                  void *cont_cls)
1043 {
1044   struct GNUNET_ARM_Operation *op;
1045
1046   LOG (GNUNET_ERROR_TYPE_DEBUG,
1047        "Stopping service `%s'\n",
1048        service_name);
1049   op = change_service (h,
1050                        service_name,
1051                        cont,
1052                        cont_cls,
1053                        GNUNET_MESSAGE_TYPE_ARM_STOP);
1054   if (NULL == op)
1055     return NULL;
1056   /* If the service is ARM, set a flag as we will use MQ errors
1057      to detect that the process is really gone. */
1058   if (0 == strcasecmp (service_name,
1059                        "arm"))
1060     op->is_arm_stop = GNUNET_YES;
1061   return op;
1062 }
1063
1064
1065 /**
1066  * Request a list of running services.
1067  *
1068  * @param h handle to ARM
1069  * @param cont callback to invoke after request is sent or is not sent
1070  * @param cont_cls closure for @a cont
1071  * @return handle for the operation, NULL on error
1072  */
1073 struct GNUNET_ARM_Operation *
1074 GNUNET_ARM_request_service_list (struct GNUNET_ARM_Handle *h,
1075                                  GNUNET_ARM_ServiceListCallback cont,
1076                                  void *cont_cls)
1077 {
1078   struct GNUNET_ARM_Operation *op;
1079   struct GNUNET_MQ_Envelope *env;
1080   struct GNUNET_ARM_Message *msg;
1081
1082   LOG (GNUNET_ERROR_TYPE_DEBUG,
1083        "Requesting LIST from ARM service\n");
1084   if (0 == h->request_id_counter)
1085     h->request_id_counter++;
1086   op = GNUNET_new (struct GNUNET_ARM_Operation);
1087   op->h = h;
1088   op->list_cont = cont;
1089   op->cont_cls = cont_cls;
1090   op->id = h->request_id_counter++;
1091   GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
1092                                     h->operation_pending_tail,
1093                                     op);
1094   env = GNUNET_MQ_msg (msg,
1095                        GNUNET_MESSAGE_TYPE_ARM_LIST);
1096   msg->reserved = htonl (0);
1097   msg->request_id = GNUNET_htonll (op->id);
1098   GNUNET_MQ_send (h->mq, env);
1099   return op;
1100 }
1101
1102
1103 /* end of arm_api.c */