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