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