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