- adapt mesh to strided regex implementation
[oweals/gnunet.git] / src / arm / arm_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010, 2012 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  */
26 #include "platform.h"
27 #include "gnunet_arm_service.h"
28 #include "gnunet_client_lib.h"
29 #include "gnunet_getopt_lib.h"
30 #include "gnunet_os_lib.h"
31 #include "gnunet_protocols.h"
32 #include "gnunet_server_lib.h"
33 #include "arm.h"
34
35 #define LOG(kind,...) GNUNET_log_from (kind, "arm-api",__VA_ARGS__)
36
37 /**
38  * Handle for interacting with ARM.
39  */
40 struct GNUNET_ARM_Handle
41 {
42
43   /**
44    * Our connection to the ARM service.
45    */
46   struct GNUNET_CLIENT_Connection *client;
47
48   /**
49    * The configuration that we are using.
50    */
51   struct GNUNET_CONFIGURATION_Handle *cfg;
52
53 };
54
55 /**
56  * Context for handling the shutdown of a service.
57  */
58 struct ShutdownContext
59 {
60   /**
61    * Connection to the service that is being shutdown.
62    */
63   struct GNUNET_CLIENT_Connection *sock;
64
65   /**
66    * Time allowed for shutdown to happen.
67    */
68   struct GNUNET_TIME_Absolute timeout;
69
70   /**
71    * Task set up to cancel the shutdown request on timeout.
72    */
73   GNUNET_SCHEDULER_TaskIdentifier cancel_task;
74
75   /**
76    * Task to call once shutdown complete
77    */
78   GNUNET_CLIENT_ShutdownTask cont;
79
80   /**
81    * Closure for shutdown continuation
82    */
83   void *cont_cls;
84
85   /**
86    * Handle for transmission request.
87    */
88   struct GNUNET_CLIENT_TransmitHandle *th;
89
90 };
91
92
93 /**
94  * Handler receiving response to service shutdown requests.
95  * First call with NULL: service misbehaving, or something.
96  * First call with GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN_ACK:
97  *   - service will shutdown
98  * Second call with NULL:
99  *   - service has now really shut down.
100  *
101  * @param cls closure
102  * @param msg NULL, indicating socket closure.
103  */
104 static void
105 service_shutdown_handler (void *cls, const struct GNUNET_MessageHeader *msg)
106 {
107   struct ShutdownContext *shutdown_ctx = cls;
108
109   if (NULL != msg)
110   {
111     /* We just expected a disconnect! Report the error and be done with it... */
112     GNUNET_break (0);
113     shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR);
114     GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task);
115     GNUNET_CLIENT_disconnect (shutdown_ctx->sock);
116     GNUNET_free (shutdown_ctx);
117     return;
118   }
119   if (NULL != shutdown_ctx->cont)
120     /* shutdown is now complete, as we waited for the network disconnect... */
121     shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_DOWN);    
122   GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task);
123   GNUNET_CLIENT_disconnect (shutdown_ctx->sock);
124   GNUNET_free (shutdown_ctx);
125 }
126
127
128 /**
129  * Shutting down took too long, cancel receive and return error.
130  *
131  * @param cls closure
132  * @param tc context information (why was this task triggered now)
133  */
134 static void
135 service_shutdown_cancel (void *cls,
136                          const struct GNUNET_SCHEDULER_TaskContext *tc)
137 {
138   struct ShutdownContext *shutdown_ctx = cls;
139
140   shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_COMMUNICATION_TIMEOUT);
141   GNUNET_CLIENT_disconnect (shutdown_ctx->sock);
142   GNUNET_free (shutdown_ctx);
143 }
144
145
146 /**
147  * If possible, write a shutdown message to the target
148  * buffer and destroy the client connection.
149  *
150  * @param cls the "struct GNUNET_CLIENT_Connection" to destroy
151  * @param size number of bytes available in buf
152  * @param buf NULL on error, otherwise target buffer
153  * @return number of bytes written to buf
154  */
155 static size_t
156 write_shutdown (void *cls, size_t size, void *buf)
157 {
158   struct ShutdownContext *shutdown_ctx = cls;
159   struct GNUNET_MessageHeader *msg;
160
161   shutdown_ctx->th = NULL;
162   if (size < sizeof (struct GNUNET_MessageHeader))
163   {
164     LOG (GNUNET_ERROR_TYPE_WARNING,
165          _("Failed to transmit shutdown request to client.\n"));
166     shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR);
167     GNUNET_CLIENT_disconnect (shutdown_ctx->sock);
168     GNUNET_free (shutdown_ctx);
169     return 0;                   /* client disconnected */
170   }
171   GNUNET_CLIENT_receive (shutdown_ctx->sock, &service_shutdown_handler,
172                          shutdown_ctx, GNUNET_TIME_UNIT_FOREVER_REL);
173   shutdown_ctx->cancel_task =
174     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
175                                   (shutdown_ctx->timeout),
176                                   &service_shutdown_cancel, shutdown_ctx);
177   msg = (struct GNUNET_MessageHeader *) buf;
178   msg->type = htons (GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN);
179   msg->size = htons (sizeof (struct GNUNET_MessageHeader));
180   return sizeof (struct GNUNET_MessageHeader);
181 }
182
183
184 /**
185  * Request that the service should shutdown.
186  * Afterwards, the connection will automatically be
187  * disconnected.  Hence the "sock" should not
188  * be used by the caller after this call
189  * (calling this function frees "sock" after a while).
190  *
191  * @param sock the socket connected to the service
192  * @param timeout how long to wait before giving up on transmission
193  * @param cont continuation to call once the service is really down
194  * @param cont_cls closure for continuation
195  *
196  */
197 static void
198 arm_service_shutdown (struct GNUNET_CLIENT_Connection *sock,
199                       struct GNUNET_TIME_Relative timeout,
200                       GNUNET_CLIENT_ShutdownTask cont, void *cont_cls)
201 {
202   struct ShutdownContext *shutdown_ctx;
203
204   shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext));
205   shutdown_ctx->cont = cont;
206   shutdown_ctx->cont_cls = cont_cls;
207   shutdown_ctx->sock = sock;
208   shutdown_ctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
209   shutdown_ctx->th = GNUNET_CLIENT_notify_transmit_ready (sock,
210                                                           sizeof (struct GNUNET_MessageHeader),
211                                                           timeout, GNUNET_NO, &write_shutdown,
212                                                           shutdown_ctx);
213 }
214
215
216 /**
217  * Setup a context for communicating with ARM.  Note that this
218  * can be done even if the ARM service is not yet running.
219  *
220  * @param cfg configuration to use (needed to contact ARM;
221  *        the ARM service may internally use a different
222  *        configuration to determine how to start the service).
223  * @param service service that *this* process is implementing/providing, can be NULL
224  * @return context to use for further ARM operations, NULL on error
225  */
226 struct GNUNET_ARM_Handle *
227 GNUNET_ARM_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
228                     const char *service)
229 {
230   struct GNUNET_ARM_Handle *ret;
231
232   ret = GNUNET_malloc (sizeof (struct GNUNET_ARM_Handle));
233   ret->cfg = GNUNET_CONFIGURATION_dup (cfg);
234   return ret;
235 }
236
237
238 /**
239  * Disconnect from the ARM service.
240  *
241  * @param h the handle that was being used
242  */
243 void
244 GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *h)
245 {
246   if (h->client != NULL)
247     GNUNET_CLIENT_disconnect (h->client);
248   GNUNET_CONFIGURATION_destroy (h->cfg);
249   GNUNET_free (h);
250 }
251
252
253 struct ARM_ShutdownContext
254 {
255   /**
256    * Callback to call once shutdown complete.
257    */
258   GNUNET_ARM_Callback cb;
259
260   /**
261    * Closure for callback.
262    */
263   void *cb_cls;
264 };
265
266
267 /**
268  * Internal state for a request with ARM.
269  */
270 struct RequestContext
271 {
272
273   /**
274    * Pointer to our handle with ARM.
275    */
276   struct GNUNET_ARM_Handle *h;
277
278   /**
279    * Function to call with a status code for the requested operation.
280    */
281   GNUNET_ARM_Callback callback;
282
283   /**
284    * Closure for "callback".
285    */
286   void *cls;
287
288   /**
289    * Timeout for the operation.
290    */
291   struct GNUNET_TIME_Absolute timeout;
292
293   /**
294    * Type of the request expressed as a message type (start or stop).
295    */
296   uint16_t type;
297
298   /**
299    * Flags for passing std descriptors to ARM (when starting ARM).
300    */
301   enum GNUNET_OS_InheritStdioFlags std_inheritance;
302
303 };
304
305 #include "do_start_process.c"
306
307
308 /**
309  * A client specifically requested starting of ARM itself.
310  * This function is called with information about whether
311  * or not ARM is running; if it is, report success.  If
312  * it is not, start the ARM process.
313  *
314  * @param cls the context for the request that we will report on (struct RequestContext*)
315  * @param tc why were we called (reason says if ARM is running)
316  */
317 static void
318 arm_service_report (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
319 {
320   struct RequestContext *pos = cls;
321   struct GNUNET_OS_Process *proc;
322   char *binary;
323   char *config;
324   char *loprefix;
325   char *lopostfix;
326
327   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE))
328   {
329     LOG (GNUNET_ERROR_TYPE_DEBUG, "Looks like `%s' is already running.\n",
330          "gnunet-service-arm");
331     /* arm is running! */
332     if (pos->callback != NULL)
333       pos->callback (pos->cls, GNUNET_ARM_PROCESS_ALREADY_RUNNING);
334     GNUNET_free (pos);
335     return;
336   }
337   LOG (GNUNET_ERROR_TYPE_DEBUG,
338        "Looks like `%s' is not running, will start it.\n",
339        "gnunet-service-arm");
340   if (GNUNET_OK !=
341       GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "PREFIX",
342                                              &loprefix))
343     loprefix = GNUNET_strdup ("");
344   if (GNUNET_OK !=
345       GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "OPTIONS",
346                                              &lopostfix))
347     lopostfix = GNUNET_strdup ("");
348   if (GNUNET_OK !=
349       GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "BINARY",
350                                              &binary))
351   {
352     LOG (GNUNET_ERROR_TYPE_WARNING,
353          _
354          ("Configuration failes to specify option `%s' in section `%s'!\n"),
355          "BINARY", "arm");
356     if (pos->callback != NULL)
357       pos->callback (pos->cls, GNUNET_ARM_PROCESS_UNKNOWN);
358     GNUNET_free (pos);
359     GNUNET_free (loprefix);
360     GNUNET_free (lopostfix);
361     return;
362   }
363   if (GNUNET_OK !=
364       GNUNET_CONFIGURATION_get_value_filename (pos->h->cfg, "arm", "CONFIG",
365                                                &config))
366   {
367     LOG (GNUNET_ERROR_TYPE_WARNING,
368          _("Configuration fails to specify option `%s' in section `%s'!\n"),
369          "CONFIG", "arm");
370     if (pos->callback != NULL)
371       pos->callback (pos->cls, GNUNET_ARM_PROCESS_UNKNOWN);
372     GNUNET_free (binary);
373     GNUNET_free (pos);
374     GNUNET_free (loprefix);
375     GNUNET_free (lopostfix);
376     return;
377   }
378   if ((GNUNET_YES ==
379        GNUNET_CONFIGURATION_have_value (pos->h->cfg, "TESTING", "WEAKRANDOM"))
380       && (GNUNET_YES ==
381           GNUNET_CONFIGURATION_get_value_yesno (pos->h->cfg, "TESTING",
382                                                 "WEAKRANDOM"))
383       && (GNUNET_NO ==
384           GNUNET_CONFIGURATION_have_value (pos->h->cfg, "TESTING",
385                                            "HOSTFILE")))
386   {
387     /* Means we are ONLY running locally */
388     /* we're clearly running a test, don't daemonize */
389     proc = do_start_process (GNUNET_NO, pos->std_inheritance,
390                              NULL, loprefix, binary, "-c", config,
391                              /* no daemonization! */
392                              lopostfix, NULL);
393   }
394   else
395   {
396     proc = do_start_process (GNUNET_NO, pos->std_inheritance,
397                              NULL, loprefix, binary, "-c", config,
398                              "-d", lopostfix, NULL);
399   }
400   GNUNET_free (binary);
401   GNUNET_free (config);
402   GNUNET_free (loprefix);
403   GNUNET_free (lopostfix);
404   if (proc == NULL)
405     {
406       if (pos->callback != NULL)
407         pos->callback (pos->cls, GNUNET_ARM_PROCESS_FAILURE);
408       GNUNET_free (pos);
409       return;
410     }
411   if (pos->callback != NULL)
412     pos->callback (pos->cls, GNUNET_ARM_PROCESS_STARTING);
413   GNUNET_free (proc);
414   GNUNET_free (pos);
415 }
416
417
418 /**
419  * Process a response from ARM to a request for a change in service
420  * status.
421  *
422  * @param cls the request context
423  * @param msg the response
424  */
425 static void
426 handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
427 {
428   struct RequestContext *sc = cls;
429   const struct GNUNET_ARM_ResultMessage *res;
430   enum GNUNET_ARM_ProcessStatus status;
431
432   if ((msg == NULL) ||
433       (ntohs (msg->size) != sizeof (struct GNUNET_ARM_ResultMessage)))
434   {
435     LOG (GNUNET_ERROR_TYPE_WARNING,
436          _
437          ("Error receiving response to `%s' request from ARM for service `%s'\n"),
438          (sc->type == GNUNET_MESSAGE_TYPE_ARM_START) ? "START" : "STOP",
439          (const char *) &sc[1]);
440     GNUNET_CLIENT_disconnect (sc->h->client);
441     sc->h->client = GNUNET_CLIENT_connect ("arm", sc->h->cfg);
442     GNUNET_assert (NULL != sc->h->client);
443     if (sc->callback != NULL)
444       sc->callback (sc->cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR);
445     GNUNET_free (sc);
446     return;
447   }
448   res = (const struct GNUNET_ARM_ResultMessage *) msg;
449   LOG (GNUNET_ERROR_TYPE_DEBUG,
450        "Received response from ARM for service `%s': %u\n",
451        (const char *) &sc[1], ntohs (msg->type));
452   status = (enum GNUNET_ARM_ProcessStatus) ntohl (res->status);
453   if (sc->callback != NULL)
454     sc->callback (sc->cls, status);
455   GNUNET_free (sc);
456 }
457
458
459 /**
460  * Start or stop a service.
461  *
462  * @param h handle to ARM
463  * @param service_name name of the service
464  * @param timeout how long to wait before failing for good
465  * @param cb callback to invoke when service is ready
466  * @param cb_cls closure for callback
467  * @param type type of the request
468  */
469 static void
470 change_service (struct GNUNET_ARM_Handle *h, const char *service_name,
471                 struct GNUNET_TIME_Relative timeout, GNUNET_ARM_Callback cb,
472                 void *cb_cls, uint16_t type)
473 {
474   struct RequestContext *sctx;
475   size_t slen;
476   struct GNUNET_MessageHeader *msg;
477
478   slen = strlen (service_name) + 1;
479   if (slen + sizeof (struct GNUNET_MessageHeader) >=
480       GNUNET_SERVER_MAX_MESSAGE_SIZE)
481   {
482     GNUNET_break (0);
483     if (cb != NULL)
484       cb (cb_cls, GNUNET_NO);
485     return;
486   }
487   LOG (GNUNET_ERROR_TYPE_DEBUG,
488        (type ==
489         GNUNET_MESSAGE_TYPE_ARM_START) ?
490        _("Requesting start of service `%s'.\n") :
491        _("Requesting termination of service `%s'.\n"), service_name);
492   sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen);
493   sctx->h = h;
494   sctx->callback = cb;
495   sctx->cls = cb_cls;
496   sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
497   sctx->type = type;
498   memcpy (&sctx[1], service_name, slen);
499   msg = GNUNET_malloc (sizeof (struct GNUNET_MessageHeader) + slen);
500   msg->size = htons (sizeof (struct GNUNET_MessageHeader) + slen);
501   msg->type = htons (sctx->type);
502   memcpy (&msg[1], service_name, slen);
503   if (GNUNET_OK !=
504       GNUNET_CLIENT_transmit_and_get_response (sctx->h->client, msg,
505                                                GNUNET_TIME_absolute_get_remaining
506                                                (sctx->timeout), GNUNET_YES,
507                                                &handle_response, sctx))
508   {
509     LOG (GNUNET_ERROR_TYPE_WARNING,
510          (type ==
511           GNUNET_MESSAGE_TYPE_ARM_START)
512          ? _("Error while trying to transmit request to start `%s' to ARM\n")    
513          : _("Error while trying to transmit request to stop `%s' to ARM\n"),
514          (const char *) &service_name);
515     if (cb != NULL)
516       cb (cb_cls, GNUNET_SYSERR);
517     GNUNET_free (sctx);
518     GNUNET_free (msg);
519     return;
520   }
521   GNUNET_free (msg);
522 }
523
524
525 /**
526  * Start a service.
527  *
528  * @param h handle to ARM
529  * @param service_name name of the service
530  * @param std_inheritance inheritance of std streams
531  * @param timeout how long to wait before failing for good
532  * @param cb callback to invoke when service is ready
533  * @param cb_cls closure for callback
534  */
535 void
536 GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h,
537                           const char *service_name,
538                           enum GNUNET_OS_InheritStdioFlags std_inheritance,
539                           struct GNUNET_TIME_Relative timeout,
540                           GNUNET_ARM_Callback cb, void *cb_cls)
541 {
542   struct RequestContext *sctx;
543   struct GNUNET_CLIENT_Connection *client;
544   size_t slen;
545
546   LOG (GNUNET_ERROR_TYPE_DEBUG,
547        _("Asked to start service `%s' within %llu ms\n"), service_name,
548        (unsigned long long) timeout.rel_value);
549   if (0 == strcasecmp ("arm", service_name))
550   {
551     slen = strlen ("arm") + 1;
552     sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen);
553     sctx->h = h;
554     sctx->callback = cb;
555     sctx->cls = cb_cls;
556     sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
557     sctx->std_inheritance = std_inheritance;
558     memcpy (&sctx[1], service_name, slen);
559     GNUNET_CLIENT_service_test ("arm", h->cfg, timeout, &arm_service_report,
560                                 sctx);
561     return;
562   }
563   if (NULL == h->client)
564   {
565     client = GNUNET_CLIENT_connect ("arm", h->cfg);
566     if (client == NULL)
567     {
568       LOG (GNUNET_ERROR_TYPE_DEBUG,
569            "arm_api, GNUNET_CLIENT_connect returned NULL\n");
570       cb (cb_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR);
571       return;
572     }
573     LOG (GNUNET_ERROR_TYPE_DEBUG,
574          "arm_api, GNUNET_CLIENT_connect returned non-NULL\n");
575     h->client = client;
576   }
577   LOG (GNUNET_ERROR_TYPE_DEBUG, "arm_api, h->client non-NULL\n");
578   change_service (h, service_name, timeout, cb, cb_cls,
579                   GNUNET_MESSAGE_TYPE_ARM_START);
580 }
581
582
583 /**
584  * Callback from the arm stop service call, indicates that the arm service
585  * is well and truly dead, won't die, or an error occurred.
586  *
587  * @param cls closure for the callback
588  * @param reason reason for callback
589  */
590 static void
591 arm_shutdown_callback (void *cls, enum GNUNET_ARM_ProcessStatus reason)
592 {
593   struct ARM_ShutdownContext *arm_shutdown_ctx = cls;
594
595   if (arm_shutdown_ctx->cb != NULL)
596     arm_shutdown_ctx->cb (arm_shutdown_ctx->cb_cls, reason);
597
598   GNUNET_free (arm_shutdown_ctx);
599 }
600
601
602 /**
603  * Stop a service.
604  *
605  * @param h handle to ARM
606  * @param service_name name of the service
607  * @param timeout how long to wait before failing for good
608  * @param cb callback to invoke when service is ready
609  * @param cb_cls closure for callback
610  */
611 void
612 GNUNET_ARM_stop_service (struct GNUNET_ARM_Handle *h,
613                          const char *service_name,
614                          struct GNUNET_TIME_Relative timeout,
615                          GNUNET_ARM_Callback cb, void *cb_cls)
616 {
617   struct ARM_ShutdownContext *arm_shutdown_ctx;
618   struct GNUNET_CLIENT_Connection *client;
619
620   LOG (GNUNET_ERROR_TYPE_INFO, _("Stopping service `%s' within %llu ms\n"),
621        service_name, (unsigned long long) timeout.rel_value);
622   if (h->client == NULL)
623   {
624     client = GNUNET_CLIENT_connect ("arm", h->cfg);
625     if (client == NULL)
626     {
627       cb (cb_cls, GNUNET_SYSERR);
628       return;
629     }
630     h->client = client;
631   }
632   if (0 == strcasecmp ("arm", service_name))
633   {
634     arm_shutdown_ctx = GNUNET_malloc (sizeof (struct ARM_ShutdownContext));
635     arm_shutdown_ctx->cb = cb;
636     arm_shutdown_ctx->cb_cls = cb_cls;
637     arm_service_shutdown (h->client, timeout, &arm_shutdown_callback,
638                           arm_shutdown_ctx);
639     h->client = NULL;
640     return;
641   }
642   change_service (h, service_name, timeout, cb, cb_cls,
643                   GNUNET_MESSAGE_TYPE_ARM_STOP);
644 }
645
646
647 /**
648  * Internal state for a list request with ARM.
649  */
650 struct ListRequestContext
651 {
652
653   /**
654    * Pointer to our handle with ARM.
655    */
656   struct GNUNET_ARM_Handle *h;
657
658   /**
659    * Function to call with a status code for the requested operation.
660    */
661   GNUNET_ARM_List_Callback callback;
662
663   /**
664    * Closure for "callback".
665    */
666   void *cls;
667
668   /**
669    * Timeout for the operation.
670    */
671   struct GNUNET_TIME_Absolute timeout;
672 };
673
674
675 /**
676  * Process a response from ARM for the list request.
677  *
678  * @param cls the list request context
679  * @param msg the response
680  */
681 static void
682 handle_list_response (void *cls, const struct GNUNET_MessageHeader *msg)
683 {
684   struct ListRequestContext *sc = cls;
685   const struct GNUNET_ARM_ListResultMessage *res;
686   const char *pos;
687   uint16_t size_check;
688   uint16_t rcount;
689   uint16_t msize;
690   
691   if (NULL == msg)
692   {
693     LOG (GNUNET_ERROR_TYPE_WARNING,
694          "Error receiving response to LIST request from ARM\n");
695     GNUNET_CLIENT_disconnect (sc->h->client);
696     sc->h->client = GNUNET_CLIENT_connect ("arm", sc->h->cfg);
697     GNUNET_assert (NULL != sc->h->client);
698     if (sc->callback != NULL)
699       sc->callback (sc->cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR, 0, NULL);
700     GNUNET_free (sc);
701     return;
702   }
703    
704   if (NULL == sc->callback) 
705   {
706     GNUNET_break (0);
707     GNUNET_free (sc);
708     return;
709   }  
710   msize = ntohs (msg->size);
711   if ( (msize < sizeof ( struct GNUNET_ARM_ListResultMessage)) ||
712        (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT) )
713   {
714     GNUNET_break (0);
715     sc->callback (sc->cls, GNUNET_NO, 0, NULL);
716     GNUNET_free (sc);
717     return;
718   }
719   size_check = 0;
720   res = (const struct GNUNET_ARM_ListResultMessage *) msg;
721   rcount = ntohs (res->count);
722   {
723     const char *list[rcount];
724     unsigned int i;
725     
726     pos = (const char *)&res[1];   
727     for (i=0; i<rcount; i++)
728     {
729       const char *end = memchr (pos, 0, msize - size_check);
730       if (NULL == end)
731       {
732         GNUNET_break (0);
733         sc->callback (sc->cls, GNUNET_NO, 0, NULL);
734         GNUNET_free (sc);
735         return;
736       }
737       list[i] = pos;
738       size_check += (end - pos) + 1;
739       pos = end + 1;
740     }
741     sc->callback (sc->cls, GNUNET_YES, rcount, list);
742   }
743   GNUNET_free (sc);
744 }
745
746
747 /**
748  * List all running services.
749  * 
750  * @param h handle to ARM
751  * @param timeout how long to wait before failing for good
752  * @param cb callback to invoke when service is ready
753  * @param cb_cls closure for callback
754  */
755 void
756 GNUNET_ARM_list_running_services (struct GNUNET_ARM_Handle *h,
757                                   struct GNUNET_TIME_Relative timeout,
758                                   GNUNET_ARM_List_Callback cb, void *cb_cls)
759 {
760   struct ListRequestContext *sctx;
761   struct GNUNET_MessageHeader msg;
762   struct GNUNET_CLIENT_Connection *client;
763   
764   if (h->client == NULL)
765   {
766     client = GNUNET_CLIENT_connect ("arm", h->cfg);
767     if (client == NULL)
768     {
769       LOG (GNUNET_ERROR_TYPE_DEBUG,
770            "arm_api, GNUNET_CLIENT_connect returned NULL\n");
771       cb (cb_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR, 0, NULL);
772       return;
773     }
774     LOG (GNUNET_ERROR_TYPE_DEBUG,
775          "arm_api, GNUNET_CLIENT_connect returned non-NULL\n");
776     h->client = client;
777   }
778   
779   sctx = GNUNET_malloc (sizeof (struct RequestContext));
780   sctx->h = h;
781   sctx->callback = cb;
782   sctx->cls = cb_cls;
783   sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
784   msg.size = htons (sizeof (struct GNUNET_MessageHeader));
785   msg.type = htons (GNUNET_MESSAGE_TYPE_ARM_LIST);
786   
787   LOG (GNUNET_ERROR_TYPE_DEBUG, 
788        "Requesting LIST from ARM service with timeout: %llu ms\n", 
789        (unsigned long long)timeout.rel_value);
790   
791   if (GNUNET_OK !=
792       GNUNET_CLIENT_transmit_and_get_response (sctx->h->client, 
793                                                &msg,
794                                                GNUNET_TIME_absolute_get_remaining
795                                                (sctx->timeout), 
796                                                GNUNET_YES,
797                                                &handle_list_response, 
798                                                sctx))
799   {
800     LOG (GNUNET_ERROR_TYPE_WARNING, 
801          "Error while trying to transmit request to list services to ARM\n");
802     if (cb != NULL)
803       cb (cb_cls, GNUNET_SYSERR, 0, NULL);
804     GNUNET_free (sctx);
805     return;
806   }
807 }
808
809 /* end of arm_api.c */