-ensure external symbols have proper prefix for conversation service
[oweals/gnunet.git] / src / arm / arm_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010, 2012, 2013 Christian Grothoff (and other contributing authors)
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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, 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  * Handle for interacting with ARM.
37  */
38 struct GNUNET_ARM_Handle
39 {
40   /**
41    * Our control connection to the ARM service.
42    */
43   struct GNUNET_CLIENT_Connection *client;
44
45   /**
46    * The configuration that we are using.
47    */
48   struct GNUNET_CONFIGURATION_Handle *cfg;
49
50   /**
51    * Handle for our current transmission request.
52    */
53   struct GNUNET_CLIENT_TransmitHandle *cth;
54
55   /**
56    * Head of doubly-linked list of pending requests.
57    */
58   struct ARMControlMessage *control_pending_head;
59
60   /**
61    * Tail of doubly-linked list of pending requests.
62    */
63   struct ARMControlMessage *control_pending_tail;
64
65   /**
66    * Head of doubly-linked list of sent requests.
67    */
68   struct ARMControlMessage *control_sent_head;
69
70   /**
71    * Tail of doubly-linked list of sent requests.
72    */
73   struct ARMControlMessage *control_sent_tail;
74
75   /**
76    * Callback to invoke on connection/disconnection.
77    */
78   GNUNET_ARM_ConnectionStatusCallback conn_status;
79
80   /**
81    * Closure for conn_status.
82    */
83   void *conn_status_cls;
84
85   /**
86    * ARM control message for the 'arm_termination_handler'
87    * with the continuation to call once the ARM shutdown is done.
88    */
89   struct ARMControlMessage *thm;
90
91   /**
92    * ID of the reconnect task (if any).
93    */
94   GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
95
96   /**
97    * Current delay we use for re-trying to connect to core.
98    */
99   struct GNUNET_TIME_Relative retry_backoff;
100
101   /**
102    * Counter for request identifiers
103    */
104   uint64_t request_id_counter;
105
106   /**
107    * Are we currently disconnected and hence unable to send?
108    */
109   unsigned char currently_down;
110
111   /**
112    * GNUNET_YES if we're running a service test.
113    */
114   unsigned char service_test_is_active;
115 };
116
117
118 /**
119  * Entry in a doubly-linked list of control messages to be transmitted
120  * to the arm service.
121  *
122  * The actual message is allocated at the end of this struct.
123  */
124 struct ARMControlMessage
125 {
126   /**
127    * This is a doubly-linked list.
128    */
129   struct ARMControlMessage *next;
130
131   /**
132    * This is a doubly-linked list.
133    */
134   struct ARMControlMessage *prev;
135
136   /**
137    * ARM handle.
138    */
139   struct GNUNET_ARM_Handle *h;
140
141   /**
142    * Message to send.
143    */
144   struct GNUNET_ARM_Message *msg;
145
146   /**
147    * Callback for service state change requests.
148    */
149   GNUNET_ARM_ResultCallback result_cont;
150
151   /**
152    * Callback for service list requests.
153    */
154   GNUNET_ARM_ServiceListCallback list_cont;
155
156   /**
157    * Closure for 'result_cont' or 'list_cont'.
158    */
159   void *cont_cls;
160
161   /**
162    * Timeout for the operation.
163    */
164   struct GNUNET_TIME_Absolute timeout;
165
166   /**
167    * Task to run when request times out.
168    */
169   GNUNET_SCHEDULER_TaskIdentifier timeout_task_id;
170
171   /**
172    * Flags for passing std descriptors to ARM (when starting ARM).
173    */
174   enum GNUNET_OS_InheritStdioFlags std_inheritance;
175
176   /**
177    * Type of the request expressed as a message type (start, stop or list).
178    */
179   uint16_t type;
180 };
181
182
183 /**
184  * Connect to arm.
185  *
186  * @param h arm handle
187  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
188  */
189 static int
190 reconnect_arm (struct GNUNET_ARM_Handle *h);
191
192
193 /**
194  * Check the list of pending requests, send the next
195  * one to the arm.
196  *
197  * @param h arm handle
198  * @param ignore_currently_down transmit message even if not initialized?
199  */
200 static void
201 trigger_next_request (struct GNUNET_ARM_Handle *h, int ignore_currently_down);
202
203
204 /**
205  * Task scheduled to try to re-connect to arm.
206  *
207  * @param cls the 'struct GNUNET_ARM_Handle'
208  * @param tc task context
209  */
210 static void
211 reconnect_arm_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
212 {
213   struct GNUNET_ARM_Handle *h = cls;
214
215   h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
216   LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting to ARM service after delay\n");
217   reconnect_arm (h);
218 }
219
220
221 /**
222  * Close down any existing connection to the ARM service and
223  * try re-establishing it later.
224  *
225  * @param h our handle
226  */
227 static void
228 reconnect_arm_later (struct GNUNET_ARM_Handle *h)
229 {
230   if (GNUNET_NO != h->currently_down)
231     return;
232   if (NULL != h->cth)
233   {
234     GNUNET_CLIENT_notify_transmit_ready_cancel (h->cth);
235     h->cth = NULL;
236   }
237   if (NULL != h->client)
238   {
239     GNUNET_CLIENT_disconnect (h->client);
240     h->client = NULL;
241   }
242   h->currently_down = GNUNET_YES;
243   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == h->reconnect_task);
244   h->reconnect_task =
245       GNUNET_SCHEDULER_add_delayed (h->retry_backoff, &reconnect_arm_task, h);
246   /* Don't clear pending messages on disconnection, deliver them later 
247   clear_pending_messages (h, GNUNET_ARM_REQUEST_DISCONNECTED);
248   GNUNET_assert (NULL == h->control_pending_head);
249   */
250   h->retry_backoff = GNUNET_TIME_STD_BACKOFF (h->retry_backoff);
251   if (NULL != h->conn_status)
252     h->conn_status (h->conn_status_cls, GNUNET_NO);
253 }
254
255
256 /**
257  * Find a control message by its unique ID.
258  *
259  * @param h ARM handle
260  * @param id unique message ID to use for the lookup
261  * @return NULL if not found
262  */
263 static struct ARMControlMessage *
264 find_cm_by_id (struct GNUNET_ARM_Handle *h, uint64_t id)
265 {
266   struct ARMControlMessage *result;
267   for (result = h->control_sent_head; result; result = result->next)
268     if (id == result->msg->request_id)
269       return result;
270   return NULL;
271 }
272
273
274 /**
275  * Handler for ARM 'termination' reply (failure to receive).
276  *
277  * @param cls our "struct GNUNET_ARM_Handle"
278  * @param msg expected to be NULL
279  */
280 static void
281 arm_termination_handler (void *cls, const struct GNUNET_MessageHeader *msg)
282 {
283   struct GNUNET_ARM_Handle *h = cls;
284   struct ARMControlMessage *cm;
285
286   if (NULL != msg)
287   {
288     GNUNET_break (0);   
289     GNUNET_CLIENT_receive (h->client, &arm_termination_handler, h,
290                            GNUNET_TIME_UNIT_FOREVER_REL);
291     return;
292   }
293   cm = h->thm;
294   h->thm = NULL;
295   h->currently_down = GNUNET_YES;
296   GNUNET_CLIENT_disconnect (h->client);
297   h->client = NULL;
298   if (NULL != cm->result_cont)
299     cm->result_cont (cm->cont_cls, 
300                      GNUNET_ARM_REQUEST_SENT_OK,
301                      (const char *) &cm->msg[1], 
302                      GNUNET_ARM_RESULT_STOPPED); 
303   GNUNET_free (cm->msg);
304   GNUNET_free (cm);
305 }
306
307
308 /**
309  * Handler for ARM replies.
310  *
311  * @param cls our "struct GNUNET_ARM_Handle"
312  * @param msg the message received from the arm service
313  */
314 static void
315 client_notify_handler (void *cls, const struct GNUNET_MessageHeader *msg)
316 {
317   struct GNUNET_ARM_Handle *h = cls;
318   const struct GNUNET_ARM_Message *arm_msg;
319   const struct GNUNET_ARM_ResultMessage *res;
320   const struct GNUNET_ARM_ListResultMessage *lres;
321   struct ARMControlMessage *cm;
322   const char **list;
323   const char *pos;
324   uint64_t id;
325   enum GNUNET_ARM_Result result;
326   uint16_t size_check;
327   uint16_t rcount;
328   uint16_t msize;
329   unsigned char fail;
330
331   list = NULL;
332   rcount = 0;
333   if (NULL == msg)
334   {
335     LOG (GNUNET_ERROR_TYPE_INFO,
336          _("Client was disconnected from arm service, trying to reconnect.\n"));
337     reconnect_arm_later (h);
338     return;
339   }
340   msize = ntohs (msg->size);
341   LOG (GNUNET_ERROR_TYPE_DEBUG,
342        "Processing message of type %u and size %u from arm service\n",
343        ntohs (msg->type), msize);
344   if (msize < sizeof (struct GNUNET_ARM_Message))
345   {
346     GNUNET_break (0);
347     reconnect_arm_later (h);
348     return;
349   }
350   arm_msg = (const struct GNUNET_ARM_Message *) msg;
351   id = GNUNET_ntohll (arm_msg->request_id);
352   cm = find_cm_by_id (h, id);
353   if (NULL == cm)
354   {
355     LOG (GNUNET_ERROR_TYPE_DEBUG, "Message with unknown id %llu\n", id);
356     return;
357   }  
358   fail = GNUNET_NO;
359   switch (ntohs (msg->type))
360   {
361   case GNUNET_MESSAGE_TYPE_ARM_RESULT:
362     if (msize < sizeof (struct GNUNET_ARM_ResultMessage))
363     {
364       GNUNET_assert (0);
365       fail = GNUNET_YES;
366     }
367     break;
368   case GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT:
369     if (msize < sizeof (struct GNUNET_ARM_ListResultMessage))
370     {
371       GNUNET_break (0);
372       fail = GNUNET_YES;
373       break;
374     }
375     size_check = 0;
376     lres = (const struct GNUNET_ARM_ListResultMessage *) msg;
377     rcount = ntohs (lres->count);
378     {
379       unsigned int i;
380       
381       list = GNUNET_malloc (sizeof (const char *) * rcount);
382       pos = (const char *)&lres[1];
383       for (i = 0; i < rcount; i++)
384       {
385         const char *end = memchr (pos, 0, msize - size_check);
386         if (NULL == end)
387         {
388           GNUNET_break (0);
389           fail = GNUNET_YES;
390           break;
391         }
392         list[i] = pos;
393         size_check += (end - pos) + 1;
394         pos = end + 1;
395       }
396       if (GNUNET_YES == fail)
397       {
398         GNUNET_free (list);
399         list = NULL;
400       }
401     }
402     break;
403   default:
404     fail = GNUNET_YES;
405     break;
406   }
407   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cm->timeout_task_id);
408   GNUNET_SCHEDULER_cancel (cm->timeout_task_id);
409   GNUNET_CONTAINER_DLL_remove (h->control_sent_head,
410                                h->control_sent_tail, cm);
411   if (GNUNET_YES == fail)
412   {
413     reconnect_arm_later (h);
414     GNUNET_free (cm->msg);
415     GNUNET_free (cm);
416     return;
417   }
418   if ( (GNUNET_MESSAGE_TYPE_ARM_RESULT == ntohs (msg->type)) &&
419        (0 == strcasecmp ((const char *) &cm->msg[1],
420                          "arm")) &&
421        (NULL != (res = (const struct GNUNET_ARM_ResultMessage *) msg)) &&
422        (GNUNET_ARM_RESULT_STOPPING == ntohl (res->result)) )
423   {
424     /* special case: if we are stopping 'gnunet-service-arm', we do not just
425        wait for the result message, but also wait for the service to close
426        the connection (and then we have to close our client handle as well);
427        this is done by installing a different receive handler, waiting for
428        the connection to go down */
429     if (NULL != h->thm)
430     {
431       GNUNET_break (0);
432       cm->result_cont (h->thm->cont_cls, 
433                        GNUNET_ARM_REQUEST_SENT_OK,
434                        (const char *) &h->thm->msg[1], 
435                        GNUNET_ARM_RESULT_IS_NOT_KNOWN);
436       GNUNET_free (h->thm->msg);
437       GNUNET_free (h->thm);
438     }
439     h->thm = cm;
440     GNUNET_CLIENT_receive (h->client, &arm_termination_handler, h,
441                            GNUNET_TIME_UNIT_FOREVER_REL);
442     return;
443   }       
444   GNUNET_CLIENT_receive (h->client, &client_notify_handler, h,
445                          GNUNET_TIME_UNIT_FOREVER_REL);
446   switch (ntohs (msg->type))
447   {
448   case GNUNET_MESSAGE_TYPE_ARM_RESULT:  
449     res = (const struct GNUNET_ARM_ResultMessage *) msg;
450     LOG (GNUNET_ERROR_TYPE_DEBUG,
451          "Received response from ARM for service `%s': %u\n",
452          (const char *) &cm->msg[1], ntohs (msg->type));
453     result = (enum GNUNET_ARM_Result) ntohl (res->result);
454     if (NULL != cm->result_cont)
455       cm->result_cont (cm->cont_cls, GNUNET_ARM_REQUEST_SENT_OK,
456                        (const char *) &cm->msg[1], result);
457     break;
458   case GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT:
459     if (NULL != cm->list_cont)
460         cm->list_cont (cm->cont_cls, GNUNET_ARM_REQUEST_SENT_OK, rcount,
461                        list);
462     GNUNET_free (list);
463     break;
464   }  
465   GNUNET_free (cm->msg);
466   GNUNET_free (cm);
467 }
468
469
470 /**
471  * Transmit the next message to the arm service.
472  *
473  * @param cls closure with the 'struct GNUNET_ARM_Handle'
474  * @param size number of bytes available in buf
475  * @param buf where the callee should write the message
476  * @return number of bytes written to buf 
477  */
478 static size_t
479 transmit_arm_message (void *cls, size_t size, void *buf)
480 {
481   struct GNUNET_ARM_Handle *h = cls;
482   struct ARMControlMessage *cm;
483   struct GNUNET_ARM_Message *arm_msg;
484   uint64_t request_id;
485   int notify_connection;
486   uint16_t msize;
487
488   notify_connection = GNUNET_NO;
489   LOG (GNUNET_ERROR_TYPE_DEBUG,
490       "transmit_arm_message is running with %p buffer of size %lu. ARM is known to be %s\n",
491       buf, size, h->currently_down ? "unconnected" : "connected");
492   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == h->reconnect_task);
493   h->cth = NULL;  
494   if ((GNUNET_YES == h->currently_down) && (NULL != buf))
495   {
496     h->currently_down = GNUNET_NO;
497     notify_connection = GNUNET_YES;
498     h->retry_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
499     GNUNET_CLIENT_receive (h->client, &client_notify_handler, h,
500                            GNUNET_TIME_UNIT_FOREVER_REL);
501   }
502   if (NULL == buf)
503   {
504     LOG (GNUNET_ERROR_TYPE_DEBUG,
505          "Transmission failed, initiating reconnect\n");
506     reconnect_arm_later (h);
507     return 0;
508   }
509   if (NULL == (cm = h->control_pending_head))
510   {
511     LOG (GNUNET_ERROR_TYPE_DEBUG, "Queue is empty, not sending anything\n");
512     msize = 0;
513     goto end;
514   }
515   GNUNET_assert (NULL != cm->msg);
516   msize = ntohs (cm->msg->header.size);
517   if (size < msize)
518   {
519     LOG (GNUNET_ERROR_TYPE_DEBUG,
520         "Request is too big (%u < %u), not sending it\n", size, msize);
521     trigger_next_request (h, GNUNET_NO);
522     msize = 0;
523     goto end;
524   }
525   arm_msg = cm->msg;
526   if (0 == h->request_id_counter)
527     h->request_id_counter++;
528   request_id = h->request_id_counter++;
529   LOG (GNUNET_ERROR_TYPE_DEBUG,
530        "Transmitting control message with %u bytes of type %u to arm with id %llu\n",
531        (unsigned int) msize, (unsigned int) ntohs (cm->msg->header.type), request_id);
532   arm_msg->request_id = GNUNET_htonll (request_id);
533   memcpy (buf, cm->msg, msize);
534   /* Otherwise we won't be able to find it later! */
535   arm_msg->request_id = request_id;
536   GNUNET_CONTAINER_DLL_remove (h->control_pending_head,
537                                h->control_pending_tail, cm);
538   GNUNET_CONTAINER_DLL_insert_tail (h->control_sent_head,
539                                     h->control_sent_tail, cm);
540   /* Don't free msg, keep it around (kind of wasteful, but then we don't
541    * really have many messages to handle, and it'll be freed when it times
542    * out anyway.
543    */
544   trigger_next_request (h, GNUNET_NO);
545
546  end:
547   if ((GNUNET_YES == notify_connection) && (NULL != h->conn_status))
548     h->conn_status (h->conn_status_cls, GNUNET_YES);
549   return msize;
550 }
551
552
553 /**
554  * Check the list of pending requests, send the next
555  * one to the arm.
556  *
557  * @param h arm handle
558  * @param ignore_currently_down transmit message even if not initialized?
559  */
560 static void
561 trigger_next_request (struct GNUNET_ARM_Handle *h, int ignore_currently_down)
562 {
563   uint16_t msize;
564
565   msize = sizeof (struct GNUNET_MessageHeader);
566   if ((GNUNET_YES == h->currently_down) && (ignore_currently_down == GNUNET_NO))
567   {
568     LOG (GNUNET_ERROR_TYPE_DEBUG,
569          "ARM connection down, not processing queue\n");
570     return;
571   }
572   if (NULL != h->cth)
573   {
574     LOG (GNUNET_ERROR_TYPE_DEBUG, "Request pending, not processing queue\n");
575     return;
576   }
577   if (NULL != h->control_pending_head)
578     msize =
579         ntohs (h->control_pending_head->msg->header.size);
580   else if (GNUNET_NO == ignore_currently_down)
581   {
582     LOG (GNUNET_ERROR_TYPE_DEBUG,
583          "Request queue empty, not processing queue\n");
584     return;                     /* no pending message */
585   }
586   h->cth =
587       GNUNET_CLIENT_notify_transmit_ready (h->client, msize,
588                                            GNUNET_TIME_UNIT_FOREVER_REL,
589                                            GNUNET_NO, &transmit_arm_message, h);
590 }
591
592
593 /**
594  * Connect to arm.
595  *
596  * @param h arm handle
597  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
598  */
599 static int
600 reconnect_arm (struct GNUNET_ARM_Handle *h)
601 {
602   GNUNET_assert (NULL == h->client);
603   GNUNET_assert (GNUNET_YES == h->currently_down);
604   h->client = GNUNET_CLIENT_connect ("arm", h->cfg);
605   if (NULL == h->client)
606   {
607     LOG (GNUNET_ERROR_TYPE_DEBUG,
608            "arm_api, GNUNET_CLIENT_connect returned NULL\n");
609     if (NULL != h->conn_status)
610       h->conn_status (h->conn_status_cls, GNUNET_SYSERR);
611     return GNUNET_SYSERR;
612   }
613   LOG (GNUNET_ERROR_TYPE_DEBUG,
614          "arm_api, GNUNET_CLIENT_connect returned non-NULL\n");
615   trigger_next_request (h, GNUNET_YES);
616   return GNUNET_OK;
617 }
618
619
620 /**
621  * Set up a context for communicating with ARM, then
622  * start connecting to the ARM service using that context.
623  *
624  * @param cfg configuration to use (needed to contact ARM;
625  *        the ARM service may internally use a different
626  *        configuration to determine how to start the service).
627  * @param conn_status will be called when connecting/disconnecting
628  * @param cls closure for conn_status
629  * @return context to use for further ARM operations, NULL on error.
630  */
631 struct GNUNET_ARM_Handle *
632 GNUNET_ARM_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
633                     GNUNET_ARM_ConnectionStatusCallback conn_status, void *cls)
634 {
635   struct GNUNET_ARM_Handle *h;
636
637   h = GNUNET_malloc (sizeof (struct GNUNET_ARM_Handle));
638   h->cfg = GNUNET_CONFIGURATION_dup (cfg);
639   h->currently_down = GNUNET_YES;
640   h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
641   h->conn_status = conn_status;
642   h->conn_status_cls = cls;
643   if (GNUNET_OK != reconnect_arm (h))
644   {
645     GNUNET_free (h);
646     return NULL;
647   }
648   return h;
649 }
650
651
652 /**
653  * Disconnect from the ARM service (if connected) and destroy the context.
654  *
655  * @param h the handle that was being used
656  */
657 void
658 GNUNET_ARM_disconnect_and_free (struct GNUNET_ARM_Handle *h)
659 {
660   struct ARMControlMessage *cm;
661   
662   LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from ARM service\n");
663   if (NULL != h->cth)
664   {
665     GNUNET_CLIENT_notify_transmit_ready_cancel (h->cth);
666     h->cth = NULL;
667   }
668   while ((NULL != (cm = h->control_pending_head)) 
669          || (NULL != (cm = h->control_sent_head)) )
670   {
671     if (NULL != h->control_pending_head)
672       GNUNET_CONTAINER_DLL_remove (h->control_pending_head,
673                                    h->control_pending_tail, cm);
674     else
675       GNUNET_CONTAINER_DLL_remove (h->control_sent_head,
676                                    h->control_sent_tail, cm);
677     GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cm->timeout_task_id);
678     GNUNET_SCHEDULER_cancel (cm->timeout_task_id);
679     if (NULL != cm->result_cont)
680       cm->result_cont (cm->cont_cls, GNUNET_ARM_REQUEST_DISCONNECTED,
681                        NULL, 0);
682     /* FIXME: What about list callback? */
683     GNUNET_free_non_null (cm->msg);
684     GNUNET_free (cm);
685   }
686   if (NULL != h->client)
687   {
688     GNUNET_CLIENT_disconnect (h->client);
689     h->client = NULL;
690   }
691   if (GNUNET_SCHEDULER_NO_TASK != h->reconnect_task)
692   {
693     GNUNET_SCHEDULER_cancel (h->reconnect_task);
694     h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
695   }
696   if (GNUNET_NO == h->service_test_is_active)
697   {
698     GNUNET_CONFIGURATION_destroy (h->cfg);
699     GNUNET_free (h);
700   }
701 }
702
703
704 /**
705  * Message timed out. Remove it from the queue.
706  *
707  * @param cls the message (struct ARMControlMessage *)
708  * @param tc task context
709  */
710 static void
711 control_message_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
712 {
713   struct ARMControlMessage *cm = cls;
714   struct GNUNET_ARM_Message *arm_msg;
715   LOG (GNUNET_ERROR_TYPE_DEBUG,
716        "Control message timed out\n");
717   arm_msg = cm->msg;
718   if ((NULL == arm_msg) || (0 == arm_msg->request_id))
719   {
720     GNUNET_CONTAINER_DLL_remove (cm->h->control_pending_head,
721                                  cm->h->control_pending_tail, cm);
722   }
723   else
724   {
725     GNUNET_CONTAINER_DLL_remove (cm->h->control_sent_head,
726                                  cm->h->control_sent_tail, cm);
727   }
728   if (NULL != cm->result_cont)
729     cm->result_cont (cm->cont_cls, GNUNET_ARM_REQUEST_TIMEOUT, NULL, 0);
730   else if (NULL != cm->list_cont)
731     cm->list_cont (cm->cont_cls, GNUNET_ARM_REQUEST_TIMEOUT, 0, NULL);
732   GNUNET_free_non_null (cm->msg);
733   GNUNET_free (cm);
734 }
735
736
737 #include "do_start_process.c"
738
739
740 /**
741  * A client specifically requested starting of ARM itself.
742  * This function is called with information about whether
743  * or not ARM is running; if it is, report success.  If
744  * it is not, start the ARM process.
745  *
746  * @param cls the context for the request that we will report on (struct ARMControlMessage *)
747  * @param result GNUNET_YES if ARM is running
748  */
749 static void
750 arm_service_report (void *cls,
751                     int result)
752 {
753   struct ARMControlMessage *cm = cls;
754   struct GNUNET_ARM_Handle *h;
755   struct GNUNET_OS_Process *proc;
756   unsigned char test_is_active;
757   char *cbinary;
758   char *binary;
759   char *config;
760   char *loprefix;
761   char *lopostfix;
762
763   test_is_active = cm->h->service_test_is_active;
764   if ((GNUNET_YES == test_is_active) &&
765       (GNUNET_YES == result))
766   {
767     LOG (GNUNET_ERROR_TYPE_DEBUG, 
768          "Looks like `%s' is already running.\n",
769          "gnunet-service-arm");
770     /* arm is running! */
771     if (cm->result_cont)
772       cm->result_cont (cm->cont_cls, 
773                        GNUNET_ARM_REQUEST_SENT_OK, "arm", 
774                        GNUNET_ARM_RESULT_IS_STARTED_ALREADY);
775   }
776   if (GNUNET_NO == test_is_active)
777   {
778     /* User disconnected & destroyed ARM handle in the middle of
779      * the service test, so we kept the handle around until now.
780      */
781     GNUNET_CONFIGURATION_destroy (cm->h->cfg);
782     GNUNET_free (cm->h);
783   }
784   if ((GNUNET_YES == result) ||
785       (GNUNET_NO == test_is_active))
786   {
787     GNUNET_free (cm);
788     return;
789   }
790   cm->h->service_test_is_active = GNUNET_NO;
791   LOG (GNUNET_ERROR_TYPE_DEBUG,
792        "Looks like `%s' is not running, will start it.\n",
793        "gnunet-service-arm");
794   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (
795       cm->h->cfg, "arm", "PREFIX", &loprefix))
796     loprefix = GNUNET_strdup ("");
797   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (
798       cm->h->cfg, "arm", "OPTIONS", &lopostfix))
799     lopostfix = GNUNET_strdup ("");
800   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (
801       cm->h->cfg, "arm", "BINARY", &cbinary))
802   {
803     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, "arm", "BINARY");
804     if (cm->result_cont)
805       cm->result_cont (cm->cont_cls, 
806                        GNUNET_ARM_REQUEST_SENT_OK, "arm", 
807                        GNUNET_ARM_RESULT_IS_NOT_KNOWN);
808     GNUNET_free (cm);
809     GNUNET_free (loprefix);
810     GNUNET_free (lopostfix);
811     return;
812   }
813   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (
814       cm->h->cfg, "arm", "CONFIG", &config))
815     config = NULL;
816   binary = GNUNET_OS_get_libexec_binary_path (cbinary);
817   GNUNET_free (cbinary);
818   if ((GNUNET_YES == GNUNET_CONFIGURATION_have_value (
819           cm->h->cfg, "TESTING", "WEAKRANDOM")) &&
820       (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (
821           cm->h->cfg, "TESTING", "WEAKRANDOM")) &&
822       (GNUNET_NO == GNUNET_CONFIGURATION_have_value (
823           cm->h->cfg, "TESTING", "HOSTFILE")))
824   {
825     /* Means we are ONLY running locally */
826     /* we're clearly running a test, don't daemonize */
827     if (NULL == config)
828       proc = do_start_process (GNUNET_NO, cm->std_inheritance,
829                                NULL, loprefix, binary,
830                                /* no daemonization! */
831                                lopostfix, NULL);
832     else
833       proc = do_start_process (GNUNET_NO, cm->std_inheritance,
834                                NULL, loprefix, binary, "-c", config,
835                                /* no daemonization! */
836                                lopostfix, NULL);
837   }
838   else
839   {
840     if (NULL == config)
841       proc = do_start_process (GNUNET_NO, cm->std_inheritance,
842                                NULL, loprefix, binary,
843                                "-d", lopostfix, NULL);
844     else
845       proc = do_start_process (GNUNET_NO, cm->std_inheritance,
846                                NULL, loprefix, binary, "-c", config,
847                                "-d", lopostfix, NULL);
848   }
849   GNUNET_free (binary);
850   GNUNET_free_non_null (config);
851   GNUNET_free (loprefix);
852   GNUNET_free (lopostfix);
853   if (NULL == proc)
854   {
855     if (cm->result_cont)
856       cm->result_cont (cm->cont_cls, GNUNET_ARM_REQUEST_SENT_OK, "arm",
857           GNUNET_ARM_RESULT_START_FAILED);
858     GNUNET_free (cm);
859     return;
860   }
861   if (cm->result_cont)
862     cm->result_cont (cm->cont_cls, GNUNET_ARM_REQUEST_SENT_OK, "arm",
863         GNUNET_ARM_RESULT_STARTING);
864   GNUNET_OS_process_destroy (proc);
865   h = cm->h;
866   GNUNET_free (cm);
867   reconnect_arm (h);
868 }
869
870
871 /**
872  * Start or stop a service.
873  *
874  * @param h handle to ARM
875  * @param service_name name of the service
876  * @param timeout how long to wait before failing for good
877  * @param cb callback to invoke when service is ready
878  * @param cb_cls closure for callback
879  * @param type type of the request
880  */
881 static void
882 change_service (struct GNUNET_ARM_Handle *h, const char *service_name,
883                 struct GNUNET_TIME_Relative timeout, GNUNET_ARM_ResultCallback cb,
884                 void *cb_cls, uint16_t type)
885 {
886   struct ARMControlMessage *cm;
887   size_t slen;
888   struct GNUNET_ARM_Message *msg;
889
890   slen = strlen (service_name) + 1;
891   if (slen + sizeof (struct GNUNET_ARM_Message) >=
892       GNUNET_SERVER_MAX_MESSAGE_SIZE)
893   {
894     GNUNET_break (0);
895     if (cb != NULL)
896       cb (cb_cls, GNUNET_ARM_REQUEST_TOO_LONG, NULL, 0);
897     return;
898   }
899   LOG (GNUNET_ERROR_TYPE_DEBUG, "Requesting %s of service `%s'.\n",
900        (GNUNET_MESSAGE_TYPE_ARM_START == type) ? "start" : "termination",
901        service_name);
902   cm = GNUNET_malloc (sizeof (struct ARMControlMessage) + slen);
903   cm->h = h;
904   cm->result_cont = cb;
905   cm->cont_cls = cb_cls;
906   cm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
907   memcpy (&cm[1], service_name, slen);
908   msg = GNUNET_malloc (sizeof (struct GNUNET_ARM_Message) + slen);
909   msg->header.size = htons (sizeof (struct GNUNET_ARM_Message) + slen);
910   msg->header.type = htons (type);
911   memcpy (&msg[1], service_name, slen);
912   cm->msg = msg;
913   LOG (GNUNET_ERROR_TYPE_DEBUG,
914       "Inserting a control message into the queue. Timeout is %s\n",
915        GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_remaining (cm->timeout),
916                                                GNUNET_NO));
917   GNUNET_CONTAINER_DLL_insert_tail (h->control_pending_head,
918                                     h->control_pending_tail, cm);
919   cm->timeout_task_id =
920       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
921                                     (cm->timeout), &control_message_timeout, cm);
922   trigger_next_request (h, GNUNET_NO);
923 }
924
925
926 /**
927  * Request for a service to be started.
928  *
929  * @param h handle to ARM
930  * @param service_name name of the service
931  * @param std_inheritance inheritance of std streams
932  * @param timeout how long to wait before failing for good
933  * @param cont callback to invoke after request is sent or not sent
934  * @param cont_cls closure for callback
935  */
936 void
937 GNUNET_ARM_request_service_start (struct GNUNET_ARM_Handle *h,
938                                   const char *service_name, 
939                                   enum GNUNET_OS_InheritStdioFlags std_inheritance,
940                                   struct GNUNET_TIME_Relative timeout, 
941                                   GNUNET_ARM_ResultCallback cont,
942                                   void *cont_cls)
943 {
944   struct ARMControlMessage *cm;
945   size_t slen;
946
947   LOG (GNUNET_ERROR_TYPE_DEBUG,
948        "Asked to start service `%s' within %s\n", service_name,
949        GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_NO));
950   if (0 == strcasecmp ("arm", service_name))
951   {
952     /* Possible cases:
953      * 1) We're connected to ARM already. Invoke the callback immediately.
954      * 2) We're not connected to ARM.
955      *    Cancel any reconnection attempts temporarily, then perform
956      *    a service test.
957      */
958     if (GNUNET_NO == h->currently_down)
959     {
960       LOG (GNUNET_ERROR_TYPE_DEBUG, "ARM is already running\n");
961       if (NULL != cont)
962         cont (cont_cls, GNUNET_ARM_REQUEST_SENT_OK, "arm", GNUNET_ARM_RESULT_IS_STARTED_ALREADY);
963     }
964     else if (GNUNET_NO == h->service_test_is_active)
965     {
966       if (NULL != h->cth)
967       {
968         GNUNET_CLIENT_notify_transmit_ready_cancel (h->cth);
969         h->cth = NULL;
970       }
971       if (NULL != h->client)
972       {
973         GNUNET_CLIENT_disconnect (h->client);
974         h->client = NULL;
975       }
976       if (GNUNET_SCHEDULER_NO_TASK != h->reconnect_task)
977       {
978         GNUNET_SCHEDULER_cancel (h->reconnect_task);
979         h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
980       }
981
982       LOG (GNUNET_ERROR_TYPE_DEBUG,
983           "Not connected to ARM, will do a service test\n");
984
985       slen = strlen ("arm") + 1;
986       cm = GNUNET_malloc (sizeof (struct ARMControlMessage) + slen);
987       cm->h = h;
988       cm->result_cont = cont;
989       cm->cont_cls = cont_cls;
990       cm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
991       cm->std_inheritance = std_inheritance;
992       memcpy (&cm[1], service_name, slen);
993       h->service_test_is_active = GNUNET_YES;
994       GNUNET_CLIENT_service_test ("arm", h->cfg, timeout, &arm_service_report,
995                                   cm);
996     }
997     else
998     {
999       /* Service test is already running - tell user to chill out and try
1000        * again later.
1001        */
1002       LOG (GNUNET_ERROR_TYPE_DEBUG, "Service test is already in progress, we're busy\n");
1003       if (NULL != cont)
1004         cont (cont_cls, GNUNET_ARM_REQUEST_BUSY, NULL, 0);
1005     }
1006     return;
1007   }
1008   change_service (h, service_name, timeout, cont, cont_cls,
1009                   GNUNET_MESSAGE_TYPE_ARM_START);
1010 }
1011
1012
1013 /**
1014  * Request a service to be stopped.
1015  * Stopping arm itself will not invalidate its handle, and
1016  * ARM API will try to restore connection to the ARM service,
1017  * even if ARM connection was lost because you asked for ARM to be stopped.
1018  * Call GNUNET_ARM_disconnect_and_free () to free the handle and prevent
1019  * further connection attempts.
1020  *
1021  * @param h handle to ARM
1022  * @param service_name name of the service
1023  * @param timeout how long to wait before failing for good
1024  * @param cont callback to invoke after request is sent or is not sent
1025  * @param cont_cls closure for callback
1026  */
1027 void
1028 GNUNET_ARM_request_service_stop (struct GNUNET_ARM_Handle *h,
1029                                  const char *service_name, 
1030                                  struct GNUNET_TIME_Relative timeout,
1031                                  GNUNET_ARM_ResultCallback cont, 
1032                                  void *cont_cls)
1033 {
1034   LOG (GNUNET_ERROR_TYPE_DEBUG, 
1035        "Stopping service `%s' within %s\n",
1036        service_name, 
1037        GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_NO));
1038   change_service (h, service_name, timeout, cont, cont_cls,
1039                   GNUNET_MESSAGE_TYPE_ARM_STOP);
1040 }
1041
1042
1043 /**
1044  * Request a list of running services.
1045  *
1046  * @param h handle to ARM
1047  * @param timeout how long to wait before failing for good
1048  * @param cont callback to invoke after request is sent or is not sent
1049  * @param cont_cls closure for callback
1050  */
1051 void
1052 GNUNET_ARM_request_service_list (struct GNUNET_ARM_Handle *h,
1053                                  struct GNUNET_TIME_Relative timeout,
1054                                  GNUNET_ARM_ServiceListCallback cont, 
1055                                  void *cont_cls)
1056 {
1057   struct ARMControlMessage *cm;
1058   struct GNUNET_ARM_Message *msg;
1059
1060   LOG (GNUNET_ERROR_TYPE_DEBUG, 
1061        "Requesting LIST from ARM service with timeout: %s\n", 
1062        GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_YES));
1063   cm = GNUNET_malloc (sizeof (struct ARMControlMessage));
1064   cm->h = h;
1065   cm->list_cont = cont;
1066   cm->cont_cls = cont_cls;
1067   cm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1068   msg = GNUNET_malloc (sizeof (struct GNUNET_ARM_Message));
1069   msg->header.size = htons (sizeof (struct GNUNET_ARM_Message));
1070   msg->header.type = htons (GNUNET_MESSAGE_TYPE_ARM_LIST);
1071   cm->msg = msg;
1072   GNUNET_CONTAINER_DLL_insert_tail (h->control_pending_head,
1073                                     h->control_pending_tail, cm);
1074   cm->timeout_task_id =
1075       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
1076                                     (cm->timeout), &control_message_timeout, cm);
1077   trigger_next_request (h, GNUNET_NO);
1078 }
1079
1080
1081 /* end of arm_api.c */