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