paragraph for gnunet devs that don't know how to use the web
[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, NULL);
684     else
685       proc = GNUNET_OS_start_process_s (GNUNET_NO,
686                                         std_inheritance,
687                                         NULL,
688                                         loprefix,
689                                         quotedbinary,
690                                         "-c", config,
691                                         "-d", /* do daemonize */
692                                         lopostfix,
693                                         NULL);
694   }
695   GNUNET_free (binary);
696   GNUNET_free (quotedbinary);
697   GNUNET_free_non_null (config);
698   GNUNET_free (loprefix);
699   GNUNET_free (lopostfix);
700   if (NULL == proc)
701     return GNUNET_ARM_RESULT_START_FAILED;
702   GNUNET_OS_process_destroy (proc);
703   return GNUNET_ARM_RESULT_STARTING;
704 }
705
706
707 /**
708  * Abort an operation.  Only prevents the callback from being
709  * called, the operation may still complete.
710  *
711  * @param op operation to cancel
712  */
713 void
714 GNUNET_ARM_operation_cancel (struct GNUNET_ARM_Operation *op)
715 {
716   struct GNUNET_ARM_Handle *h = op->h;
717
718   if (h->thm == op)
719   {
720     op->result_cont = NULL;
721     return;
722   }
723   GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
724                                h->operation_pending_tail,
725                                op);
726   GNUNET_free (op);
727 }
728
729
730 /**
731  * Start or stop a service.
732  *
733  * @param h handle to ARM
734  * @param service_name name of the service
735  * @param cb callback to invoke when service is ready
736  * @param cb_cls closure for @a cb
737  * @param type type of the request
738  * @return handle to queue, NULL on error
739  */
740 static struct GNUNET_ARM_Operation *
741 change_service (struct GNUNET_ARM_Handle *h,
742                 const char *service_name,
743                 GNUNET_ARM_ResultCallback cb,
744                 void *cb_cls,
745                 uint16_t type)
746 {
747   struct GNUNET_ARM_Operation *op;
748   size_t slen;
749   struct GNUNET_MQ_Envelope *env;
750   struct GNUNET_ARM_Message *msg;
751
752   slen = strlen (service_name) + 1;
753   if (slen + sizeof (struct GNUNET_ARM_Message) >=
754       GNUNET_MAX_MESSAGE_SIZE)
755   {
756     GNUNET_break (0);
757     return NULL;
758   }
759   if (0 == h->request_id_counter)
760     h->request_id_counter++;
761   op = GNUNET_new (struct GNUNET_ARM_Operation);
762   op->h = h;
763   op->result_cont = cb;
764   op->cont_cls = cb_cls;
765   op->id = h->request_id_counter++;
766   GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
767                                     h->operation_pending_tail,
768                                     op);
769   env = GNUNET_MQ_msg_extra (msg,
770                              slen,
771                              type);
772   msg->reserved = htonl (0);
773   msg->request_id = GNUNET_htonll (op->id);
774   GNUNET_memcpy (&msg[1],
775           service_name,
776           slen);
777   GNUNET_MQ_send (h->mq,
778                   env);
779   return op;
780 }
781
782
783 /**
784  * Task run to notify application that ARM is already up.
785  *
786  * @param cls the operation that asked ARM to be started
787  */
788 static void
789 notify_running (void *cls)
790 {
791   struct GNUNET_ARM_Operation *op = cls;
792   struct GNUNET_ARM_Handle *h = op->h;
793
794   op->async = NULL;
795   GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
796                                h->operation_pending_tail,
797                                op);
798   if (NULL != op->result_cont)
799     op->result_cont (op->cont_cls,
800                      GNUNET_ARM_REQUEST_SENT_OK,
801                      GNUNET_ARM_RESULT_IS_STARTED_ALREADY);
802   if ( (GNUNET_YES == h->currently_up) &&
803        (NULL != h->conn_status) )
804     h->conn_status (h->conn_status_cls,
805                     GNUNET_YES);
806   GNUNET_free (op);
807 }
808
809
810 /**
811  * Task run to notify application that ARM is being started.
812  *
813  * @param cls the operation that asked ARM to be started
814  */
815 static void
816 notify_starting (void *cls)
817 {
818   struct GNUNET_ARM_Operation *op = cls;
819   struct GNUNET_ARM_Handle *h = op->h;
820
821   op->async = NULL;
822   LOG (GNUNET_ERROR_TYPE_DEBUG,
823        "Notifying client that we started the ARM service\n");
824   GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
825                                h->operation_pending_tail,
826                                op);
827   if (NULL != op->result_cont)
828     op->result_cont (op->cont_cls,
829                      GNUNET_ARM_REQUEST_SENT_OK,
830                      op->starting_ret);
831   GNUNET_free (op);
832 }
833
834
835 /**
836  * Request for a service to be started.
837  *
838  * @param h handle to ARM
839  * @param service_name name of the service
840  * @param std_inheritance inheritance of std streams
841  * @param cont callback to invoke after request is sent or not sent
842  * @param cont_cls closure for @a cont
843  * @return handle for the operation, NULL on error
844  */
845 struct GNUNET_ARM_Operation *
846 GNUNET_ARM_request_service_start (struct GNUNET_ARM_Handle *h,
847                                   const char *service_name,
848                                   enum GNUNET_OS_InheritStdioFlags std_inheritance,
849                                   GNUNET_ARM_ResultCallback cont,
850                                   void *cont_cls)
851 {
852   struct GNUNET_ARM_Operation *op;
853   enum GNUNET_ARM_Result ret;
854
855   LOG (GNUNET_ERROR_TYPE_DEBUG,
856        "Starting service `%s'\n",
857        service_name);
858   if (0 != strcasecmp ("arm",
859                        service_name))
860     return change_service (h,
861                            service_name,
862                            cont,
863                            cont_cls,
864                            GNUNET_MESSAGE_TYPE_ARM_START);
865
866   /* Possible cases:
867    * 1) We're connected to ARM already. Invoke the callback immediately.
868    * 2) We're not connected to ARM.
869    *    Cancel any reconnection attempts temporarily, then perform
870    *    a service test.
871    */
872   if (GNUNET_YES == h->currently_up)
873   {
874     LOG (GNUNET_ERROR_TYPE_DEBUG,
875          "ARM is already running\n");
876     op = GNUNET_new (struct GNUNET_ARM_Operation);
877     op->h = h;
878     op->result_cont = cont;
879     op->cont_cls = cont_cls;
880     GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
881                                       h->operation_pending_tail,
882                                       op);
883     op->async = GNUNET_SCHEDULER_add_now (&notify_running,
884                                           op);
885     return op;
886   }
887   /* This is an inherently uncertain choice, as it is of course
888      theoretically possible that ARM is up and we just did not
889      yet complete the MQ handshake.  However, given that users
890      are unlikely to hammer 'gnunet-arm -s' on a busy system,
891      the above check should catch 99.99% of the cases where ARM
892      is already running. */
893   LOG (GNUNET_ERROR_TYPE_DEBUG,
894        "Starting ARM service\n");
895   ret = start_arm_service (h,
896                            std_inheritance);
897   if (GNUNET_ARM_RESULT_STARTING == ret)
898     reconnect_arm (h);
899   op = GNUNET_new (struct GNUNET_ARM_Operation);
900   op->h = h;
901   op->result_cont = cont;
902   op->cont_cls = cont_cls;
903   GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
904                                     h->operation_pending_tail,
905                                     op);
906   op->starting_ret = ret;
907   op->async = GNUNET_SCHEDULER_add_now (&notify_starting,
908                                         op);
909   return op;
910 }
911
912
913 /**
914  * Request a service to be stopped.  Stopping arm itself will not
915  * invalidate its handle, and ARM API will try to restore connection
916  * to the ARM service, even if ARM connection was lost because you
917  * asked for ARM to be stopped.  Call
918  * #GNUNET_ARM_disconnect() to free the handle and prevent
919  * further connection attempts.
920  *
921  * @param h handle to ARM
922  * @param service_name name of the service
923  * @param cont callback to invoke after request is sent or is not sent
924  * @param cont_cls closure for @a cont
925  * @return handle for the operation, NULL on error
926  */
927 struct GNUNET_ARM_Operation *
928 GNUNET_ARM_request_service_stop (struct GNUNET_ARM_Handle *h,
929                                  const char *service_name,
930                                  GNUNET_ARM_ResultCallback cont,
931                                  void *cont_cls)
932 {
933   struct GNUNET_ARM_Operation *op;
934
935   LOG (GNUNET_ERROR_TYPE_DEBUG,
936        "Stopping service `%s'\n",
937        service_name);
938   op = change_service (h,
939                        service_name,
940                        cont,
941                        cont_cls,
942                        GNUNET_MESSAGE_TYPE_ARM_STOP);
943   if (NULL == op)
944     return NULL;
945   /* If the service is ARM, set a flag as we will use MQ errors
946      to detect that the process is really gone. */
947   if (0 == strcasecmp (service_name,
948                        "arm"))
949     op->is_arm_stop = GNUNET_YES;
950   return op;
951 }
952
953
954 /**
955  * Request a list of running services.
956  *
957  * @param h handle to ARM
958  * @param cont callback to invoke after request is sent or is not sent
959  * @param cont_cls closure for @a cont
960  * @return handle for the operation, NULL on error
961  */
962 struct GNUNET_ARM_Operation *
963 GNUNET_ARM_request_service_list (struct GNUNET_ARM_Handle *h,
964                                  GNUNET_ARM_ServiceListCallback cont,
965                                  void *cont_cls)
966 {
967   struct GNUNET_ARM_Operation *op;
968   struct GNUNET_MQ_Envelope *env;
969   struct GNUNET_ARM_Message *msg;
970
971   LOG (GNUNET_ERROR_TYPE_DEBUG,
972        "Requesting LIST from ARM service\n");
973   if (0 == h->request_id_counter)
974     h->request_id_counter++;
975   op = GNUNET_new (struct GNUNET_ARM_Operation);
976   op->h = h;
977   op->list_cont = cont;
978   op->cont_cls = cont_cls;
979   op->id = h->request_id_counter++;
980   GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
981                                     h->operation_pending_tail,
982                                     op);
983   env = GNUNET_MQ_msg (msg,
984                        GNUNET_MESSAGE_TYPE_ARM_LIST);
985   msg->reserved = htonl (0);
986   msg->request_id = GNUNET_htonll (op->id);
987   GNUNET_MQ_send (h->mq,
988                   env);
989   return op;
990 }
991
992
993 /* end of arm_api.c */