RECLAIM/OIDC: code cleanup
[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      SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21 /**
22  * @file arm/arm_api.c
23  * @brief API for accessing the ARM service
24  * @author Christian Grothoff
25  * @author LRN
26  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_arm_service.h"
30 #include "gnunet_protocols.h"
31 #include "arm.h"
32
33 #define LOG(kind,...) GNUNET_log_from (kind, "arm-api",__VA_ARGS__)
34
35
36 /**
37  * Entry in a doubly-linked list of operations awaiting for replies
38  * (in-order) from the ARM service.
39  */
40 struct GNUNET_ARM_Operation
41 {
42   /**
43    * This is a doubly-linked list.
44    */
45   struct GNUNET_ARM_Operation *next;
46
47   /**
48    * This is a doubly-linked list.
49    */
50   struct GNUNET_ARM_Operation *prev;
51
52   /**
53    * ARM handle.
54    */
55   struct GNUNET_ARM_Handle *h;
56
57   /**
58    * Callback for service state change requests.
59    */
60   GNUNET_ARM_ResultCallback result_cont;
61
62   /**
63    * Callback for service list requests.
64    */
65   GNUNET_ARM_ServiceListCallback list_cont;
66
67   /**
68    * Closure for @e result_cont or @e list_cont.
69    */
70   void *cont_cls;
71
72   /**
73    * Task for async completion.
74    */
75   struct GNUNET_SCHEDULER_Task *async;
76
77   /**
78    * Unique ID for the request.
79    */
80   uint64_t id;
81
82   /**
83    * Result of this operation for #notify_starting().
84    */
85   enum GNUNET_ARM_Result starting_ret;
86
87   /**
88    * 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_string (h->cfg,
601                                              "arm",
602                                              "PREFIX",
603                                              &loprefix))
604     loprefix = GNUNET_strdup ("");
605   else
606     loprefix = GNUNET_CONFIGURATION_expand_dollar (h->cfg,
607                                                    loprefix);
608   if (GNUNET_OK !=
609       GNUNET_CONFIGURATION_get_value_string (h->cfg,
610                                              "arm",
611                                              "OPTIONS",
612                                              &lopostfix))
613     lopostfix = GNUNET_strdup ("");
614   else
615     lopostfix = GNUNET_CONFIGURATION_expand_dollar (h->cfg,
616                                                     lopostfix);
617   if (GNUNET_OK !=
618       GNUNET_CONFIGURATION_get_value_string (h->cfg,
619                                              "arm",
620                                              "BINARY",
621                                              &cbinary))
622   {
623     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
624                                "arm",
625                                "BINARY");
626     GNUNET_free (loprefix);
627     GNUNET_free (lopostfix);
628     return GNUNET_ARM_RESULT_IS_NOT_KNOWN;
629   }
630   if (GNUNET_OK !=
631       GNUNET_CONFIGURATION_get_value_filename (h->cfg,
632                                                "arm",
633                                                "CONFIG",
634                                                &config))
635     config = NULL;
636   binary = GNUNET_OS_get_libexec_binary_path (cbinary);
637   GNUNET_asprintf (&quotedbinary,
638                    "\"%s\"",
639                    binary);
640   GNUNET_free (cbinary);
641   if ( (GNUNET_YES ==
642         GNUNET_CONFIGURATION_have_value (h->cfg,
643                                          "TESTING",
644                                          "WEAKRANDOM")) &&
645        (GNUNET_YES ==
646         GNUNET_CONFIGURATION_get_value_yesno (h->cfg,
647                                               "TESTING",
648                                               "WEAKRANDOM")) &&
649        (GNUNET_NO ==
650         GNUNET_CONFIGURATION_have_value (h->cfg,
651                                          "TESTING",
652                                          "HOSTFILE")))
653   {
654     /* Means we are ONLY running locally */
655     /* we're clearly running a test, don't daemonize */
656     if (NULL == config)
657       proc = GNUNET_OS_start_process_s (GNUNET_NO,
658                                         std_inheritance,
659                                         NULL,
660                                         loprefix,
661                                         quotedbinary,
662                                         /* no daemonization! */
663                                         lopostfix,
664                                         NULL);
665     else
666       proc = GNUNET_OS_start_process_s (GNUNET_NO,
667                                         std_inheritance,
668                                         NULL,
669                                         loprefix,
670                                         quotedbinary,
671                                         "-c", config,
672                                         /* no daemonization! */
673                                         lopostfix,
674                                         NULL);
675   }
676   else
677   {
678     if (NULL == config)
679       proc = GNUNET_OS_start_process_s (GNUNET_NO,
680                                         std_inheritance,
681                                         NULL,
682                                         loprefix,
683                                         quotedbinary,
684                                         "-d", /* do daemonize */
685                                         lopostfix,
686                                         NULL);
687     else
688       proc = GNUNET_OS_start_process_s (GNUNET_NO,
689                                         std_inheritance,
690                                         NULL,
691                                         loprefix,
692                                         quotedbinary,
693                                         "-c", config,
694                                         "-d", /* do daemonize */
695                                         lopostfix,
696                                         NULL);
697   }
698   GNUNET_free (binary);
699   GNUNET_free (quotedbinary);
700   GNUNET_free_non_null (config);
701   GNUNET_free (loprefix);
702   GNUNET_free (lopostfix);
703   if (NULL == proc)
704     return GNUNET_ARM_RESULT_START_FAILED;
705   GNUNET_OS_process_destroy (proc);
706   return GNUNET_ARM_RESULT_STARTING;
707 }
708
709
710 /**
711  * Abort an operation.  Only prevents the callback from being
712  * called, the operation may still complete.
713  *
714  * @param op operation to cancel
715  */
716 void
717 GNUNET_ARM_operation_cancel (struct GNUNET_ARM_Operation *op)
718 {
719   struct GNUNET_ARM_Handle *h = op->h;
720
721   if (h->thm == op)
722   {
723     op->result_cont = NULL;
724     return;
725   }
726   GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
727                                h->operation_pending_tail,
728                                op);
729   GNUNET_free (op);
730 }
731
732
733 /**
734  * Start or stop a service.
735  *
736  * @param h handle to ARM
737  * @param service_name name of the service
738  * @param cb callback to invoke when service is ready
739  * @param cb_cls closure for @a cb
740  * @param type type of the request
741  * @return handle to queue, NULL on error
742  */
743 static struct GNUNET_ARM_Operation *
744 change_service (struct GNUNET_ARM_Handle *h,
745                 const char *service_name,
746                 GNUNET_ARM_ResultCallback cb,
747                 void *cb_cls,
748                 uint16_t type)
749 {
750   struct GNUNET_ARM_Operation *op;
751   size_t slen;
752   struct GNUNET_MQ_Envelope *env;
753   struct GNUNET_ARM_Message *msg;
754
755   slen = strlen (service_name) + 1;
756   if (slen + sizeof (struct GNUNET_ARM_Message) >=
757       GNUNET_MAX_MESSAGE_SIZE)
758   {
759     GNUNET_break (0);
760     return NULL;
761   }
762   if (0 == h->request_id_counter)
763     h->request_id_counter++;
764   op = GNUNET_new (struct GNUNET_ARM_Operation);
765   op->h = h;
766   op->result_cont = cb;
767   op->cont_cls = cb_cls;
768   op->id = h->request_id_counter++;
769   GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
770                                     h->operation_pending_tail,
771                                     op);
772   env = GNUNET_MQ_msg_extra (msg,
773                              slen,
774                              type);
775   msg->reserved = htonl (0);
776   msg->request_id = GNUNET_htonll (op->id);
777   GNUNET_memcpy (&msg[1],
778           service_name,
779           slen);
780   GNUNET_MQ_send (h->mq,
781                   env);
782   return op;
783 }
784
785
786 /**
787  * Task run to notify application that ARM is already up.
788  *
789  * @param cls the operation that asked ARM to be started
790  */
791 static void
792 notify_running (void *cls)
793 {
794   struct GNUNET_ARM_Operation *op = cls;
795   struct GNUNET_ARM_Handle *h = op->h;
796
797   op->async = NULL;
798   GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
799                                h->operation_pending_tail,
800                                op);
801   if (NULL != op->result_cont)
802     op->result_cont (op->cont_cls,
803                      GNUNET_ARM_REQUEST_SENT_OK,
804                      GNUNET_ARM_RESULT_IS_STARTED_ALREADY);
805   if ( (GNUNET_YES == h->currently_up) &&
806        (NULL != h->conn_status) )
807     h->conn_status (h->conn_status_cls,
808                     GNUNET_YES);
809   GNUNET_free (op);
810 }
811
812
813 /**
814  * Task run to notify application that ARM is being started.
815  *
816  * @param cls the operation that asked ARM to be started
817  */
818 static void
819 notify_starting (void *cls)
820 {
821   struct GNUNET_ARM_Operation *op = cls;
822   struct GNUNET_ARM_Handle *h = op->h;
823
824   op->async = NULL;
825   LOG (GNUNET_ERROR_TYPE_DEBUG,
826        "Notifying client that we started the ARM service\n");
827   GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
828                                h->operation_pending_tail,
829                                op);
830   if (NULL != op->result_cont)
831     op->result_cont (op->cont_cls,
832                      GNUNET_ARM_REQUEST_SENT_OK,
833                      op->starting_ret);
834   GNUNET_free (op);
835 }
836
837
838 /**
839  * Request for a service to be started.
840  *
841  * @param h handle to ARM
842  * @param service_name name of the service
843  * @param std_inheritance inheritance of std streams
844  * @param cont callback to invoke after request is sent or not sent
845  * @param cont_cls closure for @a cont
846  * @return handle for the operation, NULL on error
847  */
848 struct GNUNET_ARM_Operation *
849 GNUNET_ARM_request_service_start (struct GNUNET_ARM_Handle *h,
850                                   const char *service_name,
851                                   enum GNUNET_OS_InheritStdioFlags std_inheritance,
852                                   GNUNET_ARM_ResultCallback cont,
853                                   void *cont_cls)
854 {
855   struct GNUNET_ARM_Operation *op;
856   enum GNUNET_ARM_Result ret;
857
858   LOG (GNUNET_ERROR_TYPE_DEBUG,
859        "Starting service `%s'\n",
860        service_name);
861   if (0 != strcasecmp ("arm",
862                        service_name))
863     return change_service (h,
864                            service_name,
865                            cont,
866                            cont_cls,
867                            GNUNET_MESSAGE_TYPE_ARM_START);
868
869   /* Possible cases:
870    * 1) We're connected to ARM already. Invoke the callback immediately.
871    * 2) We're not connected to ARM.
872    *    Cancel any reconnection attempts temporarily, then perform
873    *    a service test.
874    */
875   if (GNUNET_YES == h->currently_up)
876   {
877     LOG (GNUNET_ERROR_TYPE_DEBUG,
878          "ARM is already running\n");
879     op = GNUNET_new (struct GNUNET_ARM_Operation);
880     op->h = h;
881     op->result_cont = cont;
882     op->cont_cls = cont_cls;
883     GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
884                                       h->operation_pending_tail,
885                                       op);
886     op->async = GNUNET_SCHEDULER_add_now (&notify_running,
887                                           op);
888     return op;
889   }
890   /* This is an inherently uncertain choice, as it is of course
891      theoretically possible that ARM is up and we just did not
892      yet complete the MQ handshake.  However, given that users
893      are unlikely to hammer 'gnunet-arm -s' on a busy system,
894      the above check should catch 99.99% of the cases where ARM
895      is already running. */
896   LOG (GNUNET_ERROR_TYPE_DEBUG,
897        "Starting ARM service\n");
898   ret = start_arm_service (h,
899                            std_inheritance);
900   if (GNUNET_ARM_RESULT_STARTING == ret)
901     reconnect_arm (h);
902   op = GNUNET_new (struct GNUNET_ARM_Operation);
903   op->h = h;
904   op->result_cont = cont;
905   op->cont_cls = cont_cls;
906   GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
907                                     h->operation_pending_tail,
908                                     op);
909   op->starting_ret = ret;
910   op->async = GNUNET_SCHEDULER_add_now (&notify_starting,
911                                         op);
912   return op;
913 }
914
915
916 /**
917  * Request a service to be stopped.  Stopping arm itself will not
918  * invalidate its handle, and ARM API will try to restore connection
919  * to the ARM service, even if ARM connection was lost because you
920  * asked for ARM to be stopped.  Call
921  * #GNUNET_ARM_disconnect() to free the handle and prevent
922  * further connection attempts.
923  *
924  * @param h handle to ARM
925  * @param service_name name of the service
926  * @param cont callback to invoke after request is sent or is not sent
927  * @param cont_cls closure for @a cont
928  * @return handle for the operation, NULL on error
929  */
930 struct GNUNET_ARM_Operation *
931 GNUNET_ARM_request_service_stop (struct GNUNET_ARM_Handle *h,
932                                  const char *service_name,
933                                  GNUNET_ARM_ResultCallback cont,
934                                  void *cont_cls)
935 {
936   struct GNUNET_ARM_Operation *op;
937
938   LOG (GNUNET_ERROR_TYPE_DEBUG,
939        "Stopping service `%s'\n",
940        service_name);
941   op = change_service (h,
942                        service_name,
943                        cont,
944                        cont_cls,
945                        GNUNET_MESSAGE_TYPE_ARM_STOP);
946   if (NULL == op)
947     return NULL;
948   /* If the service is ARM, set a flag as we will use MQ errors
949      to detect that the process is really gone. */
950   if (0 == strcasecmp (service_name,
951                        "arm"))
952     op->is_arm_stop = GNUNET_YES;
953   return op;
954 }
955
956
957 /**
958  * Request a list of running services.
959  *
960  * @param h handle to ARM
961  * @param cont callback to invoke after request is sent or is not sent
962  * @param cont_cls closure for @a cont
963  * @return handle for the operation, NULL on error
964  */
965 struct GNUNET_ARM_Operation *
966 GNUNET_ARM_request_service_list (struct GNUNET_ARM_Handle *h,
967                                  GNUNET_ARM_ServiceListCallback cont,
968                                  void *cont_cls)
969 {
970   struct GNUNET_ARM_Operation *op;
971   struct GNUNET_MQ_Envelope *env;
972   struct GNUNET_ARM_Message *msg;
973
974   LOG (GNUNET_ERROR_TYPE_DEBUG,
975        "Requesting LIST from ARM service\n");
976   if (0 == h->request_id_counter)
977     h->request_id_counter++;
978   op = GNUNET_new (struct GNUNET_ARM_Operation);
979   op->h = h;
980   op->list_cont = cont;
981   op->cont_cls = cont_cls;
982   op->id = h->request_id_counter++;
983   GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
984                                     h->operation_pending_tail,
985                                     op);
986   env = GNUNET_MQ_msg (msg,
987                        GNUNET_MESSAGE_TYPE_ARM_LIST);
988   msg->reserved = htonl (0);
989   msg->request_id = GNUNET_htonll (op->id);
990   GNUNET_MQ_send (h->mq,
991                   env);
992   return op;
993 }
994
995
996 /* end of arm_api.c */