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