fix memleak
[oweals/gnunet.git] / src / arm / arm_api.c
index 69e6ea99eb8711c78ac33e7c325b23fea9993eb2..15563e93398b840d64cf6ed0e3b53b50fbc6e66b 100644 (file)
@@ -1,10 +1,10 @@
 /*
      This file is part of GNUnet.
 /*
      This file is part of GNUnet.
-     (C) 2009 Christian Grothoff (and other contributing authors)
+     (C) 2009, 2010, 2012 Christian Grothoff (and other contributing authors)
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
-     by the Free Software Foundation; either version 2, or (at your
+     by the Free Software Foundation; either version 3, or (at your
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
  */
 #include "platform.h"
 #include "gnunet_arm_service.h"
  */
 #include "platform.h"
 #include "gnunet_arm_service.h"
-#include "gnunet_client_lib.h"
-#include "gnunet_getopt_lib.h"
-#include "gnunet_os_lib.h"
+#include "gnunet_util_lib.h"
 #include "gnunet_protocols.h"
 #include "gnunet_protocols.h"
-#include "gnunet_server_lib.h"
 #include "arm.h"
 
 #include "arm.h"
 
-
-/**
- * How long are we willing to wait for a service operation during the multi-operation
- * request processing?
- */
-#define MULTI_TIMEOUT  GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
-
+#define LOG(kind,...) GNUNET_log_from (kind, "arm-api",__VA_ARGS__)
 
 /**
  * Handle for interacting with ARM.
 
 /**
  * Handle for interacting with ARM.
- */ 
+ */
 struct GNUNET_ARM_Handle
 {
 
 struct GNUNET_ARM_Handle
 {
 
@@ -56,14 +47,169 @@ struct GNUNET_ARM_Handle
    */
   struct GNUNET_CONFIGURATION_Handle *cfg;
 
    */
   struct GNUNET_CONFIGURATION_Handle *cfg;
 
+};
+
+/**
+ * Context for handling the shutdown of a service.
+ */
+struct ShutdownContext
+{
+  /**
+   * Connection to the service that is being shutdown.
+   */
+  struct GNUNET_CLIENT_Connection *sock;
+
+  /**
+   * Time allowed for shutdown to happen.
+   */
+  struct GNUNET_TIME_Absolute timeout;
+
+  /**
+   * Task set up to cancel the shutdown request on timeout.
+   */
+  GNUNET_SCHEDULER_TaskIdentifier cancel_task;
+
+  /**
+   * Task to call once shutdown complete
+   */
+  GNUNET_CLIENT_ShutdownTask cont;
+
   /**
   /**
-   * Scheduler to use.
+   * Closure for shutdown continuation
    */
    */
-  struct GNUNET_SCHEDULER_Handle *sched;
+  void *cont_cls;
+
+  /**
+   * Handle for transmission request.
+   */
+  struct GNUNET_CLIENT_TransmitHandle *th;
 
 };
 
 
 
 };
 
 
+/**
+ * Handler receiving response to service shutdown requests.
+ * First call with NULL: service misbehaving, or something.
+ * First call with GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN_ACK:
+ *   - service will shutdown
+ * Second call with NULL:
+ *   - service has now really shut down.
+ *
+ * @param cls closure
+ * @param msg NULL, indicating socket closure.
+ */
+static void
+service_shutdown_handler (void *cls, const struct GNUNET_MessageHeader *msg)
+{
+  struct ShutdownContext *shutdown_ctx = cls;
+
+  if (NULL != msg)
+  {
+    /* We just expected a disconnect! Report the error and be done with it... */
+    GNUNET_break (0);
+    shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR);
+    GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task);
+    GNUNET_CLIENT_disconnect (shutdown_ctx->sock);
+    GNUNET_free (shutdown_ctx);
+    return;
+  }
+  if (NULL != shutdown_ctx->cont)
+    /* shutdown is now complete, as we waited for the network disconnect... */
+    shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_DOWN);    
+  GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task);
+  GNUNET_CLIENT_disconnect (shutdown_ctx->sock);
+  GNUNET_free (shutdown_ctx);
+}
+
+
+/**
+ * Shutting down took too long, cancel receive and return error.
+ *
+ * @param cls closure
+ * @param tc context information (why was this task triggered now)
+ */
+static void
+service_shutdown_cancel (void *cls,
+                        const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct ShutdownContext *shutdown_ctx = cls;
+
+  shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_COMMUNICATION_TIMEOUT);
+  GNUNET_CLIENT_disconnect (shutdown_ctx->sock);
+  GNUNET_free (shutdown_ctx);
+}
+
+
+/**
+ * If possible, write a shutdown message to the target
+ * buffer and destroy the client connection.
+ *
+ * @param cls the "struct GNUNET_CLIENT_Connection" to destroy
+ * @param size number of bytes available in buf
+ * @param buf NULL on error, otherwise target buffer
+ * @return number of bytes written to buf
+ */
+static size_t
+write_shutdown (void *cls, size_t size, void *buf)
+{
+  struct ShutdownContext *shutdown_ctx = cls;
+  struct GNUNET_MessageHeader *msg;
+
+  shutdown_ctx->th = NULL;
+  if (size < sizeof (struct GNUNET_MessageHeader))
+  {
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+        _("Failed to transmit shutdown request to client.\n"));
+    shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR);
+    GNUNET_CLIENT_disconnect (shutdown_ctx->sock);
+    GNUNET_free (shutdown_ctx);
+    return 0;                  /* client disconnected */
+  }
+  GNUNET_CLIENT_receive (shutdown_ctx->sock, &service_shutdown_handler,
+                        shutdown_ctx, GNUNET_TIME_UNIT_FOREVER_REL);
+  shutdown_ctx->cancel_task =
+    GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
+                                 (shutdown_ctx->timeout),
+                                 &service_shutdown_cancel, shutdown_ctx);
+  msg = (struct GNUNET_MessageHeader *) buf;
+  msg->type = htons (GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN);
+  msg->size = htons (sizeof (struct GNUNET_MessageHeader));
+  return sizeof (struct GNUNET_MessageHeader);
+}
+
+
+/**
+ * Request that the service should shutdown.
+ * Afterwards, the connection will automatically be
+ * disconnected.  Hence the "sock" should not
+ * be used by the caller after this call
+ * (calling this function frees "sock" after a while).
+ *
+ * @param sock the socket connected to the service
+ * @param timeout how long to wait before giving up on transmission
+ * @param cont continuation to call once the service is really down
+ * @param cont_cls closure for continuation
+ *
+ */
+static void
+arm_service_shutdown (struct GNUNET_CLIENT_Connection *sock,
+                     struct GNUNET_TIME_Relative timeout,
+                     GNUNET_CLIENT_ShutdownTask cont, void *cont_cls)
+{
+  struct ShutdownContext *shutdown_ctx;
+
+  shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext));
+  shutdown_ctx->cont = cont;
+  shutdown_ctx->cont_cls = cont_cls;
+  shutdown_ctx->sock = sock;
+  shutdown_ctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
+  shutdown_ctx->th = GNUNET_CLIENT_notify_transmit_ready (sock,
+                                                         sizeof (struct GNUNET_MessageHeader),
+                                                         timeout, GNUNET_NO, &write_shutdown,
+                                                         shutdown_ctx);
+}
+
+
 /**
  * Setup a context for communicating with ARM.  Note that this
  * can be done even if the ARM service is not yet running.
 /**
  * Setup a context for communicating with ARM.  Note that this
  * can be done even if the ARM service is not yet running.
@@ -71,26 +217,17 @@ struct GNUNET_ARM_Handle
  * @param cfg configuration to use (needed to contact ARM;
  *        the ARM service may internally use a different
  *        configuration to determine how to start the service).
  * @param cfg configuration to use (needed to contact ARM;
  *        the ARM service may internally use a different
  *        configuration to determine how to start the service).
- * @param sched scheduler to use
  * @param service service that *this* process is implementing/providing, can be NULL
  * @return context to use for further ARM operations, NULL on error
  */
 struct GNUNET_ARM_Handle *
 GNUNET_ARM_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
  * @param service service that *this* process is implementing/providing, can be NULL
  * @return context to use for further ARM operations, NULL on error
  */
 struct GNUNET_ARM_Handle *
 GNUNET_ARM_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                   struct GNUNET_SCHEDULER_Handle *sched,
                    const char *service)
 {
   struct GNUNET_ARM_Handle *ret;
                    const char *service)
 {
   struct GNUNET_ARM_Handle *ret;
-  struct GNUNET_CLIENT_Connection *client;
 
 
-  client = GNUNET_CLIENT_connect (sched, "arm", cfg);
-  if (client == NULL)
-    return NULL;
-  GNUNET_CLIENT_ignore_shutdown (client, GNUNET_YES);
   ret = GNUNET_malloc (sizeof (struct GNUNET_ARM_Handle));
   ret->cfg = GNUNET_CONFIGURATION_dup (cfg);
   ret = GNUNET_malloc (sizeof (struct GNUNET_ARM_Handle));
   ret->cfg = GNUNET_CONFIGURATION_dup (cfg);
-  ret->sched = sched;
-  ret->client = client;
   return ret;
 }
 
   return ret;
 }
 
@@ -104,12 +241,26 @@ void
 GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *h)
 {
   if (h->client != NULL)
 GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *h)
 {
   if (h->client != NULL)
-    GNUNET_CLIENT_disconnect (h->client, GNUNET_NO);
+    GNUNET_CLIENT_disconnect (h->client);
   GNUNET_CONFIGURATION_destroy (h->cfg);
   GNUNET_free (h);
 }
 
 
   GNUNET_CONFIGURATION_destroy (h->cfg);
   GNUNET_free (h);
 }
 
 
+struct ARM_ShutdownContext
+{
+  /**
+   * Callback to call once shutdown complete.
+   */
+  GNUNET_ARM_Callback cb;
+
+  /**
+   * Closure for callback.
+   */
+  void *cb_cls;
+};
+
+
 /**
  * Internal state for a request with ARM.
  */
 /**
  * Internal state for a request with ARM.
  */
@@ -141,6 +292,11 @@ struct RequestContext
    */
   uint16_t type;
 
    */
   uint16_t type;
 
+  /**
+   * Flags for passing std descriptors to ARM (when starting ARM).
+   */
+  enum GNUNET_OS_InheritStdioFlags std_inheritance;
+
 };
 
 #include "do_start_process.c"
 };
 
 #include "do_start_process.c"
@@ -156,99 +312,99 @@ struct RequestContext
  * @param tc why were we called (reason says if ARM is running)
  */
 static void
  * @param tc why were we called (reason says if ARM is running)
  */
 static void
-arm_service_report (void *cls,
-                   const struct GNUNET_SCHEDULER_TaskContext *tc)
+arm_service_report (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct RequestContext *pos = cls;
 {
   struct RequestContext *pos = cls;
-  pid_t pid;
+  struct GNUNET_OS_Process *proc;
   char *binary;
   char *config;
   char *loprefix;
   char *lopostfix;
 
   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE))
   char *binary;
   char *config;
   char *loprefix;
   char *lopostfix;
 
   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE))
-    {
-#if DEBUG_ARM
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 "Looks like `%s' is already running.\n",
-                 "gnunet-service-arm");
-#endif
-      /* arm is running! */
-      if (pos->callback != NULL)
-        pos->callback (pos->cls, GNUNET_YES);
-      GNUNET_free (pos);
-      return;
-    }
-#if DEBUG_ARM
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Looks like `%s' is not running, will start it.\n",
-             "gnunet-service-arm");
-#endif
-  /* FIXME: should we check that HOSTNAME for 'arm' is localhost? */
-
- if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_string (pos->h->cfg,
-                                            "arm", "PREFIX", &loprefix))
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG, "Looks like `%s' is already running.\n",
+        "gnunet-service-arm");
+    /* arm is running! */
+    if (pos->callback != NULL)
+      pos->callback (pos->cls, GNUNET_ARM_PROCESS_ALREADY_RUNNING);
+    GNUNET_free (pos);
+    return;
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Looks like `%s' is not running, will start it.\n",
+       "gnunet-service-arm");
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "PREFIX",
+                                            &loprefix))
     loprefix = GNUNET_strdup ("");
   if (GNUNET_OK !=
     loprefix = GNUNET_strdup ("");
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_string (pos->h->cfg,
-                                            "arm", "OPTIONS", &lopostfix))
+      GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "OPTIONS",
+                                            &lopostfix))
     lopostfix = GNUNET_strdup ("");
   if (GNUNET_OK !=
     lopostfix = GNUNET_strdup ("");
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_string (pos->h->cfg,
-                                            "arm",
-                                            "BINARY",
+      GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "BINARY",
                                             &binary))
                                             &binary))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  _("Configuration failes to specify option `%s' in section `%s'!\n"),
-                 "BINARY",
-                 "arm");
-      if (pos->callback != NULL)
-        pos->callback (pos->cls, GNUNET_SYSERR);
-      GNUNET_free (pos);
-      GNUNET_free (loprefix);
-      GNUNET_free (lopostfix);
-      return;
-    }
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
+                              "arm", "BINARY");
+    if (pos->callback != NULL)
+      pos->callback (pos->cls, GNUNET_ARM_PROCESS_UNKNOWN);
+    GNUNET_free (pos);
+    GNUNET_free (loprefix);
+    GNUNET_free (lopostfix);
+    return;
+  }
   if (GNUNET_OK !=
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_filename (pos->h->cfg,
-                                              "arm", "CONFIG", &config))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  _("Configuration fails to specify option `%s' in section `%s'!\n"),
-                 "CONFIG",
-                 "arm");
-      if (pos->callback != NULL)
-        pos->callback (pos->cls, GNUNET_SYSERR);
-      GNUNET_free (binary);
-      GNUNET_free (pos);
-      GNUNET_free (loprefix);
-      GNUNET_free (lopostfix);
-      return;
-    }
-  pid = do_start_process (loprefix,
-                         binary, 
-                         "-c", config, 
-#if DEBUG_ARM
-                         "-L", "DEBUG",
-#endif
-                         "-d",
-                         lopostfix,
-                         NULL);
+      GNUNET_CONFIGURATION_get_value_filename (pos->h->cfg, "arm", "CONFIG",
+                                              &config))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
+                              "arm", "CONFIG");
+    if (pos->callback != NULL)
+      pos->callback (pos->cls, GNUNET_ARM_PROCESS_UNKNOWN);
+    GNUNET_free (binary);
+    GNUNET_free (pos);
+    GNUNET_free (loprefix);
+    GNUNET_free (lopostfix);
+    return;
+  }
+  if ((GNUNET_YES ==
+       GNUNET_CONFIGURATION_have_value (pos->h->cfg, "TESTING", "WEAKRANDOM"))
+      && (GNUNET_YES ==
+         GNUNET_CONFIGURATION_get_value_yesno (pos->h->cfg, "TESTING",
+                                               "WEAKRANDOM"))
+      && (GNUNET_NO ==
+         GNUNET_CONFIGURATION_have_value (pos->h->cfg, "TESTING",
+                                          "HOSTFILE")))
+  {
+    /* Means we are ONLY running locally */
+    /* we're clearly running a test, don't daemonize */
+    proc = do_start_process (GNUNET_NO, pos->std_inheritance,
+                            NULL, loprefix, binary, "-c", config,
+                            /* no daemonization! */
+                            lopostfix, NULL);
+  }
+  else
+  {
+    proc = do_start_process (GNUNET_NO, pos->std_inheritance,
+                            NULL, loprefix, binary, "-c", config,
+                            "-d", lopostfix, NULL);
+  }
   GNUNET_free (binary);
   GNUNET_free (config);
   GNUNET_free (loprefix);
   GNUNET_free (lopostfix);
   GNUNET_free (binary);
   GNUNET_free (config);
   GNUNET_free (loprefix);
   GNUNET_free (lopostfix);
-  if (pid == -1)
+  if (proc == NULL)
     {
       if (pos->callback != NULL)
     {
       if (pos->callback != NULL)
-        pos->callback (pos->cls, GNUNET_SYSERR);
+       pos->callback (pos->cls, GNUNET_ARM_PROCESS_FAILURE);
       GNUNET_free (pos);
       return;
     }
   if (pos->callback != NULL)
       GNUNET_free (pos);
       return;
     }
   if (pos->callback != NULL)
-    pos->callback (pos->cls, GNUNET_YES);
+    pos->callback (pos->cls, GNUNET_ARM_PROCESS_STARTING);
+  GNUNET_free (proc);
   GNUNET_free (pos);
 }
 
   GNUNET_free (pos);
 }
 
@@ -257,57 +413,35 @@ arm_service_report (void *cls,
  * Process a response from ARM to a request for a change in service
  * status.
  *
  * Process a response from ARM to a request for a change in service
  * status.
  *
- * @param cls the request context 
+ * @param cls the request context
  * @param msg the response
  */
 static void
 handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
 {
   struct RequestContext *sc = cls;
  * @param msg the response
  */
 static void
 handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
 {
   struct RequestContext *sc = cls;
-  int ret;
-
-  if (msg == NULL)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                 _("Error receiving response to `%s' request from ARM for service `%s'\n"),
-                 (sc->type == GNUNET_MESSAGE_TYPE_ARM_START) 
-                 ? "START"
-                 : "STOP",
-                 (const char*) &sc[1]);
-      GNUNET_CLIENT_disconnect (sc->h->client, GNUNET_NO);
-      sc->h->client = GNUNET_CLIENT_connect (sc->h->sched, 
-                                            "arm", 
-                                            sc->h->cfg);
-      GNUNET_assert (NULL != sc->h->client);
-      GNUNET_CLIENT_ignore_shutdown (sc->h->client, GNUNET_YES);
-      if (sc->callback != NULL)
-        sc->callback (sc->cls, GNUNET_SYSERR);
-      GNUNET_free (sc);
-      return;
-    }
-#if DEBUG_ARM
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Received response from ARM for service `%s': %u\n",
-             (const char*) &sc[1],
-             ntohs(msg->type));
-#endif
-  switch (ntohs (msg->type))
-    {
-    case GNUNET_MESSAGE_TYPE_ARM_IS_UP:
-      ret = GNUNET_YES;
-      break;
-    case GNUNET_MESSAGE_TYPE_ARM_IS_DOWN:
-      ret = GNUNET_NO;
-      break;
-    case GNUNET_MESSAGE_TYPE_ARM_IS_UNKNOWN:
-      ret = GNUNET_SYSERR;
-      break;
-    default:
-      GNUNET_break (0);
-      ret = GNUNET_SYSERR;
-    }
+  const struct GNUNET_ARM_ResultMessage *res;
+  enum GNUNET_ARM_ProcessStatus status;
+
+  if ((msg == NULL) ||
+      (ntohs (msg->size) != sizeof (struct GNUNET_ARM_ResultMessage)))
+  {
+    GNUNET_break (0);
+    GNUNET_CLIENT_disconnect (sc->h->client);
+    sc->h->client = GNUNET_CLIENT_connect ("arm", sc->h->cfg);
+    GNUNET_assert (NULL != sc->h->client);
+    if (sc->callback != NULL)
+      sc->callback (sc->cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR);
+    GNUNET_free (sc);
+    return;
+  }
+  res = (const struct GNUNET_ARM_ResultMessage *) msg;
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Received response from ARM for service `%s': %u\n",
+       (const char *) &sc[1], ntohs (msg->type));
+  status = (enum GNUNET_ARM_ProcessStatus) ntohl (res->status);
   if (sc->callback != NULL)
   if (sc->callback != NULL)
-    sc->callback (sc->cls, ret);
+    sc->callback (sc->cls, status);
   GNUNET_free (sc);
 }
 
   GNUNET_free (sc);
 }
 
@@ -320,34 +454,31 @@ handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
  * @param timeout how long to wait before failing for good
  * @param cb callback to invoke when service is ready
  * @param cb_cls closure for callback
  * @param timeout how long to wait before failing for good
  * @param cb callback to invoke when service is ready
  * @param cb_cls closure for callback
- * @param type type of the request 
+ * @param type type of the request
  */
 static void
  */
 static void
-change_service (struct GNUNET_ARM_Handle *h,
-               const char *service_name,
-                struct GNUNET_TIME_Relative timeout,
-                GNUNET_ARM_Callback cb, void *cb_cls, uint16_t type)
+change_service (struct GNUNET_ARM_Handle *h, const char *service_name,
+               struct GNUNET_TIME_Relative timeout, GNUNET_ARM_Callback cb,
+               void *cb_cls, uint16_t type)
 {
   struct RequestContext *sctx;
   size_t slen;
   struct GNUNET_MessageHeader *msg;
 
   slen = strlen (service_name) + 1;
 {
   struct RequestContext *sctx;
   size_t slen;
   struct GNUNET_MessageHeader *msg;
 
   slen = strlen (service_name) + 1;
-  if (slen + sizeof (struct GNUNET_MessageHeader) >
+  if (slen + sizeof (struct GNUNET_MessageHeader) >=
       GNUNET_SERVER_MAX_MESSAGE_SIZE)
       GNUNET_SERVER_MAX_MESSAGE_SIZE)
-    {
-      GNUNET_break (0);
-      if (cb != NULL)
-        cb (cb_cls, GNUNET_NO);
-      return;
-    }
-#if DEBUG_ARM
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              (type == GNUNET_MESSAGE_TYPE_ARM_START) 
-             ? _("Requesting start of service `%s'.\n") 
-             : _("Requesting termination of service `%s'.\n"),
-             service_name);
-#endif
+  {
+    GNUNET_break (0);
+    if (cb != NULL)
+      cb (cb_cls, GNUNET_NO);
+    return;
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       (type ==
+       GNUNET_MESSAGE_TYPE_ARM_START) ?
+       "Requesting start of service `%s'.\n" :
+       "Requesting termination of service `%s'.\n", service_name);
   sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen);
   sctx->h = h;
   sctx->callback = cb;
   sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen);
   sctx->h = h;
   sctx->callback = cb;
@@ -360,24 +491,18 @@ change_service (struct GNUNET_ARM_Handle *h,
   msg->type = htons (sctx->type);
   memcpy (&msg[1], service_name, slen);
   if (GNUNET_OK !=
   msg->type = htons (sctx->type);
   memcpy (&msg[1], service_name, slen);
   if (GNUNET_OK !=
-      GNUNET_CLIENT_transmit_and_get_response (sctx->h->client,
-                                              msg,
-                                              GNUNET_TIME_absolute_get_remaining (sctx->timeout),
-                                              GNUNET_YES,
-                                              &handle_response,
-                                              sctx))
-    {       
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                 (type == GNUNET_MESSAGE_TYPE_ARM_START)
-                 ? _("Error while trying to transmit request to start `%s' to ARM\n")
-                 : _("Error while trying to transmit request to stop `%s' to ARM\n"),
-                 (const char*) &service_name);
-      if (cb != NULL)
-       cb (cb_cls, GNUNET_SYSERR);
-      GNUNET_free (sctx);
-      GNUNET_free (msg);
-      return;
-    }
+      GNUNET_CLIENT_transmit_and_get_response (sctx->h->client, msg,
+                                              GNUNET_TIME_absolute_get_remaining
+                                              (sctx->timeout), GNUNET_YES,
+                                              &handle_response, sctx))
+  {
+    GNUNET_break (0);
+    if (cb != NULL)
+      cb (cb_cls, GNUNET_SYSERR);
+    GNUNET_free (sctx);
+    GNUNET_free (msg);
+    return;
+  }
   GNUNET_free (msg);
 }
 
   GNUNET_free (msg);
 }
 
@@ -387,6 +512,7 @@ change_service (struct GNUNET_ARM_Handle *h,
  *
  * @param h handle to ARM
  * @param service_name name of the service
  *
  * @param h handle to ARM
  * @param service_name name of the service
+ * @param std_inheritance inheritance of std streams
  * @param timeout how long to wait before failing for good
  * @param cb callback to invoke when service is ready
  * @param cb_cls closure for callback
  * @param timeout how long to wait before failing for good
  * @param cb callback to invoke when service is ready
  * @param cb_cls closure for callback
@@ -394,31 +520,66 @@ change_service (struct GNUNET_ARM_Handle *h,
 void
 GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h,
                          const char *service_name,
 void
 GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h,
                          const char *service_name,
-                          struct GNUNET_TIME_Relative timeout,
-                          GNUNET_ARM_Callback cb, void *cb_cls)
+                          enum GNUNET_OS_InheritStdioFlags std_inheritance,
+                         struct GNUNET_TIME_Relative timeout,
+                         GNUNET_ARM_Callback cb, void *cb_cls)
 {
   struct RequestContext *sctx;
 {
   struct RequestContext *sctx;
+  struct GNUNET_CLIENT_Connection *client;
   size_t slen;
   size_t slen;
-#if DEBUG_ARM
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              _("Asked to start service `%s' within %llu ms\n"), service_name,
-             (unsigned long long) timeout.value);
-#endif
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Asked to start service `%s' within %s\n", service_name,
+       GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_NO));
   if (0 == strcasecmp ("arm", service_name))
   if (0 == strcasecmp ("arm", service_name))
+  {
+    slen = strlen ("arm") + 1;
+    sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen);
+    sctx->h = h;
+    sctx->callback = cb;
+    sctx->cls = cb_cls;
+    sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
+    sctx->std_inheritance = std_inheritance;
+    memcpy (&sctx[1], service_name, slen);
+    GNUNET_CLIENT_service_test ("arm", h->cfg, timeout, &arm_service_report,
+                               sctx);
+    return;
+  }
+  if (NULL == h->client)
+  {
+    client = GNUNET_CLIENT_connect ("arm", h->cfg);
+    if (client == NULL)
     {
     {
-      slen = strlen ("arm") + 1;
-      sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen);
-      sctx->h = h;
-      sctx->callback = cb;
-      sctx->cls = cb_cls;
-      sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
-      memcpy (&sctx[1], service_name, slen);
-      GNUNET_CLIENT_service_test (h->sched,
-                                  "arm",
-                                  h->cfg, timeout, &arm_service_report, sctx);
+      LOG (GNUNET_ERROR_TYPE_DEBUG,
+          "arm_api, GNUNET_CLIENT_connect returned NULL\n");
+      cb (cb_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR);
       return;
     }
       return;
     }
-  change_service (h, service_name, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_START);
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+        "arm_api, GNUNET_CLIENT_connect returned non-NULL\n");
+    h->client = client;
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "arm_api, h->client non-NULL\n");
+  change_service (h, service_name, timeout, cb, cb_cls,
+                 GNUNET_MESSAGE_TYPE_ARM_START);
+}
+
+
+/**
+ * Callback from the arm stop service call, indicates that the arm service
+ * is well and truly dead, won't die, or an error occurred.
+ *
+ * @param cls closure for the callback
+ * @param reason reason for callback
+ */
+static void
+arm_shutdown_callback (void *cls, enum GNUNET_ARM_ProcessStatus reason)
+{
+  struct ARM_ShutdownContext *arm_shutdown_ctx = cls;
+
+  if (arm_shutdown_ctx->cb != NULL)
+    arm_shutdown_ctx->cb (arm_shutdown_ctx->cb_cls, reason);
+  GNUNET_free (arm_shutdown_ctx);
 }
 
 
 }
 
 
@@ -434,185 +595,196 @@ GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h,
 void
 GNUNET_ARM_stop_service (struct GNUNET_ARM_Handle *h,
                         const char *service_name,
 void
 GNUNET_ARM_stop_service (struct GNUNET_ARM_Handle *h,
                         const char *service_name,
-                         struct GNUNET_TIME_Relative timeout,
-                         GNUNET_ARM_Callback cb, void *cb_cls)
+                        struct GNUNET_TIME_Relative timeout,
+                        GNUNET_ARM_Callback cb, void *cb_cls)
 {
 {
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              _("Stopping service `%s' within %llu ms\n"), service_name,
-             (unsigned long long) timeout.value);
-  if (0 == strcasecmp ("arm", service_name))
+  struct ARM_ShutdownContext *arm_shutdown_ctx;
+  struct GNUNET_CLIENT_Connection *client;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG, 
+       "Stopping service `%s' within %s\n",
+       service_name, 
+       GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_NO));
+  if (h->client == NULL)
+  {
+    client = GNUNET_CLIENT_connect ("arm", h->cfg);
+    if (client == NULL)
     {
     {
-      GNUNET_CLIENT_service_shutdown (h->client);
-      h->client = NULL;
-      if (cb != NULL)
-        cb (cb_cls, GNUNET_NO);
+      cb (cb_cls, GNUNET_SYSERR);
       return;
     }
       return;
     }
-  change_service (h, service_name, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_STOP);
+    h->client = client;
+  }
+  if (0 == strcasecmp ("arm", service_name))
+  {
+    arm_shutdown_ctx = GNUNET_malloc (sizeof (struct ARM_ShutdownContext));
+    arm_shutdown_ctx->cb = cb;
+    arm_shutdown_ctx->cb_cls = cb_cls;
+    arm_service_shutdown (h->client, timeout, &arm_shutdown_callback,
+                         arm_shutdown_ctx);
+    h->client = NULL;
+    return;
+  }
+  change_service (h, service_name, timeout, cb, cb_cls,
+                 GNUNET_MESSAGE_TYPE_ARM_STOP);
 }
 
 
 /**
 }
 
 
 /**
- * Function to call for each service.
- *
- * @param h handle to ARM
- * @param service_name name of the service
- * @param timeout how long to wait before failing for good
- * @param cb callback to invoke when service is ready
- * @param cb_cls closure for callback
+ * Internal state for a list request with ARM.
  */
  */
-typedef void (*ServiceOperation) (struct GNUNET_ARM_Handle *h,
-                                 const char *service_name,
-                                 struct GNUNET_TIME_Relative timeout,
-                                 GNUNET_ARM_Callback cb, void *cb_cls);
-
-
-/**
- * Context for starting or stopping multiple services.
- */
-struct MultiContext
+struct ListRequestContext
 {
 {
+
   /**
   /**
-   * NULL-terminated array of services to start or stop.
+   * Pointer to our handle with ARM.
    */
    */
-  char **services;
+  struct GNUNET_ARM_Handle *h;
 
   /**
 
   /**
-   * Our handle to ARM.
+   * Function to call with a status code for the requested operation.
    */
    */
-  struct GNUNET_ARM_Handle *h;
+  GNUNET_ARM_List_Callback callback;
 
   /**
 
   /**
-   * Identifies the operation (start or stop).
+   * Closure for "callback".
    */
    */
-  ServiceOperation op;
+  void *cls;
 
   /**
 
   /**
-   * Current position in "services".
+   * Timeout for the operation.
    */
    */
-  unsigned int pos;
+  struct GNUNET_TIME_Absolute timeout;
 };
 
 
 /**
 };
 
 
 /**
- * Run the operation for the next service in the multi-service
- * request.
+ * Process a response from ARM for the list request.
  *
  *
- * @param cls the "struct MultiContext" that is being processed
- * @param success status of the previous operation (ignored)
+ * @param cls the list request context
+ * @param msg the response
  */
 static void
  */
 static void
-next_operation (void *cls,
-               int success)
+handle_list_response (void *cls, const struct GNUNET_MessageHeader *msg)
 {
 {
-  struct MultiContext *mc = cls;
-  char *pos;
+  struct ListRequestContext *sc = cls;
+  const struct GNUNET_ARM_ListResultMessage *res;
+  const char *pos;
+  uint16_t size_check;
+  uint16_t rcount;
+  uint16_t msize;
   
   
-  if (NULL == (pos = mc->services[mc->pos]))
+  if (NULL == msg)
+  {
+    GNUNET_break (0);
+    GNUNET_CLIENT_disconnect (sc->h->client);
+    sc->h->client = GNUNET_CLIENT_connect ("arm", sc->h->cfg);
+    GNUNET_assert (NULL != sc->h->client);
+    if (sc->callback != NULL)
+      sc->callback (sc->cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR, 0, NULL);
+    GNUNET_free (sc);
+    return;
+  }
+   
+  if (NULL == sc->callback) 
+  {
+    GNUNET_break (0);
+    GNUNET_free (sc);
+    return;
+  }  
+  msize = ntohs (msg->size);
+  if ( (msize < sizeof ( struct GNUNET_ARM_ListResultMessage)) ||
+       (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT) )
+  {
+    GNUNET_break (0);
+    sc->callback (sc->cls, GNUNET_NO, 0, NULL);
+    GNUNET_free (sc);
+    return;
+  }
+  size_check = 0;
+  res = (const struct GNUNET_ARM_ListResultMessage *) msg;
+  rcount = ntohs (res->count);
+  {
+    const char *list[rcount];
+    unsigned int i;
+    
+    pos = (const char *)&res[1];   
+    for (i=0; i<rcount; i++)
     {
     {
-      GNUNET_free (mc->services);
-      GNUNET_ARM_disconnect (mc->h);
-      GNUNET_free (mc);
-      return;
+      const char *end = memchr (pos, 0, msize - size_check);
+      if (NULL == end)
+      {
+       GNUNET_break (0);
+       sc->callback (sc->cls, GNUNET_NO, 0, NULL);
+       GNUNET_free (sc);
+       return;
+      }
+      list[i] = pos;
+      size_check += (end - pos) + 1;
+      pos = end + 1;
     }
     }
-  mc->pos++;
-  mc->op (mc->h, pos, MULTI_TIMEOUT, &next_operation, mc);
-  GNUNET_free (pos);
+    sc->callback (sc->cls, GNUNET_YES, rcount, list);
+  }
+  GNUNET_free (sc);
 }
 
 
 /**
 }
 
 
 /**
- * Run a multi-service request.
- *
- * @param cfg configuration to use (needed to contact ARM;
- *        the ARM service may internally use a different
- *        configuration to determine how to start the service).
- * @param sched scheduler to use
- * @param op the operation to perform for each service
- * @param va NULL-terminated list of services
+ * List all running services.
+ * 
+ * @param h handle to ARM
+ * @param timeout how long to wait before failing for good
+ * @param cb callback to invoke when service is ready
+ * @param cb_cls closure for callback
  */
  */
-static void
-run_multi_request (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                  struct GNUNET_SCHEDULER_Handle *sched,                   
-                  ServiceOperation op,
-                  va_list va)
+void
+GNUNET_ARM_list_running_services (struct GNUNET_ARM_Handle *h,
+                                  struct GNUNET_TIME_Relative timeout,
+                                  GNUNET_ARM_List_Callback cb, void *cb_cls)
 {
 {
-  va_list cp;
-  unsigned int total;
-  struct MultiContext *mc;
-  struct GNUNET_ARM_Handle *h;
-  const char *c;
+  struct ListRequestContext *sctx;
+  struct GNUNET_MessageHeader msg;
+  struct GNUNET_CLIENT_Connection *client;
   
   
-  h = GNUNET_ARM_connect (cfg, sched, NULL);
-  if (NULL == h)
+  if (h->client == NULL)
+  {
+    client = GNUNET_CLIENT_connect ("arm", h->cfg);
+    if (client == NULL)
     {
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                 _("Error while trying to transmit to ARM service\n"));
-      return; 
+      GNUNET_break (0);
+      cb (cb_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR, 0, NULL);
+      return;
     }
     }
-  total = 1;
-  va_copy (cp, va);
-  while (NULL != (va_arg (cp, const char*))) total++;
-  va_end (cp);
-  mc = GNUNET_malloc (sizeof(struct MultiContext));
-  mc->services = GNUNET_malloc (total * sizeof (char*));
-  mc->h = h;
-  mc->op = op;
-  total = 0;
-  va_copy (cp, va);
-  while (NULL != (c = va_arg (cp, const char*))) 
-    mc->services[total++] = GNUNET_strdup (c);
-  va_end (cp);
-  next_operation (mc, GNUNET_YES);
-}
-
-
-/**
- * Start multiple services in the specified order.  Convenience
- * function.  Works asynchronously, failures are not reported.
- *
- * @param cfg configuration to use (needed to contact ARM;
- *        the ARM service may internally use a different
- *        configuration to determine how to start the service).
- * @param sched scheduler to use
- * @param ... NULL-terminated list of service names (const char*)
- */
-void
-GNUNET_ARM_start_services (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                          struct GNUNET_SCHEDULER_Handle *sched,
-                          ...)
-{
-  va_list ap;
-
-  va_start (ap, sched);
-  run_multi_request (cfg, sched, &GNUNET_ARM_start_service, ap);
-  va_end (ap);
-}
-
-
-/**
- * Stop multiple services in the specified order.  Convenience
- * function.  Works asynchronously, failures are not reported.
- * Should normally only be called from gnunet-arm or testcases,
- * stopping a service is generally otherwise a bad idea.
- *
- * @param cfg configuration to use (needed to contact ARM;
- *        the ARM service may internally use a different
- *        configuration to determine how to start the service).
- * @param sched scheduler to use
- * @param ... NULL-terminated list of service names (const char*)
- */
-void
-GNUNET_ARM_stop_services (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                         struct GNUNET_SCHEDULER_Handle *sched,
-                         ...)
-{
-  va_list ap;
-
-  va_start (ap, sched);
-  run_multi_request (cfg, sched, &GNUNET_ARM_stop_service, ap);
-  va_end (ap);
+    h->client = client;
+  }
+  
+  sctx = GNUNET_malloc (sizeof (struct RequestContext));
+  sctx->h = h;
+  sctx->callback = cb;
+  sctx->cls = cb_cls;
+  sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
+  msg.size = htons (sizeof (struct GNUNET_MessageHeader));
+  msg.type = htons (GNUNET_MESSAGE_TYPE_ARM_LIST);
+  
+  LOG (GNUNET_ERROR_TYPE_DEBUG, 
+       "Requesting LIST from ARM service with timeout: %s\n", 
+       GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_YES));
+  
+  if (GNUNET_OK !=
+      GNUNET_CLIENT_transmit_and_get_response (sctx->h->client, 
+                                               &msg,
+                                               GNUNET_TIME_absolute_get_remaining
+                                               (sctx->timeout), 
+                                               GNUNET_YES,
+                                               &handle_list_response, 
+                                               sctx))
+  {
+    GNUNET_break (0);
+    if (cb != NULL)
+      cb (cb_cls, GNUNET_SYSERR, 0, NULL);
+    GNUNET_free (sctx);
+    return;
+  }
 }
 
 }
 
-
 /* end of arm_api.c */
 /* end of arm_api.c */