fix memleak
[oweals/gnunet.git] / src / arm / arm_api.c
index 49e25b38ddb57804b28b5afd59e6945293f413d8..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 often do we re-try tranmsitting requests to ARM before
- * giving up?  Note that if we succeeded transmitting a request
- * but failed to read a response, we do NOT re-try (since that
- * might result in ARM getting a request twice).
- */
-#define MAX_ATTEMPTS 4
+#define LOG(kind,...) GNUNET_log_from (kind, "arm-api",__VA_ARGS__)
 
 /**
 
 /**
- * Minimum delay between attempts to talk to ARM.
+ * Handle for interacting with ARM.
  */
  */
-#define MIN_RETRY_DELAY  GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 100)
+struct GNUNET_ARM_Handle
+{
 
 
+  /**
+   * Our connection to the ARM service.
+   */
+  struct GNUNET_CLIENT_Connection *client;
 
 
-/**
- * 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, 5)
+  /**
+   * The configuration that we are using.
+   */
+  struct GNUNET_CONFIGURATION_Handle *cfg;
 
 
+};
 
 /**
 
 /**
- * Handle for interacting with ARM.
- */ 
-struct GNUNET_ARM_Handle
+ * Context for handling the shutdown of a service.
+ */
+struct ShutdownContext
 {
 {
+  /**
+   * Connection to the service that is being shutdown.
+   */
+  struct GNUNET_CLIENT_Connection *sock;
 
   /**
 
   /**
-   * Our connection to the ARM service.
+   * Time allowed for shutdown to happen.
    */
    */
-  struct GNUNET_CLIENT_Connection *client;
+  struct GNUNET_TIME_Absolute timeout;
 
   /**
 
   /**
-   * The configuration that we are using.
+   * Task set up to cancel the shutdown request on timeout.
    */
    */
-  const struct GNUNET_CONFIGURATION_Handle *cfg;
+  GNUNET_SCHEDULER_TaskIdentifier cancel_task;
 
   /**
 
   /**
-   * Scheduler to use.
+   * Task to call once shutdown complete
    */
    */
-  struct GNUNET_SCHEDULER_Handle *sched;
+  GNUNET_CLIENT_ShutdownTask cont;
+
+  /**
+   * Closure for shutdown continuation
+   */
+  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.
@@ -84,25 +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;
   ret = GNUNET_malloc (sizeof (struct GNUNET_ARM_Handle));
   ret = GNUNET_malloc (sizeof (struct GNUNET_ARM_Handle));
-  ret->cfg = cfg;
-  ret->sched = sched;
-  ret->client = client;
+  ret->cfg = GNUNET_CONFIGURATION_dup (cfg);
   return ret;
 }
 
   return ret;
 }
 
@@ -117,10 +242,25 @@ GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *h)
 {
   if (h->client != NULL)
     GNUNET_CLIENT_disconnect (h->client);
 {
   if (h->client != NULL)
     GNUNET_CLIENT_disconnect (h->client);
+  GNUNET_CONFIGURATION_destroy (h->cfg);
   GNUNET_free (h);
 }
 
 
   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.
  */
@@ -142,35 +282,25 @@ struct RequestContext
    */
   void *cls;
 
    */
   void *cls;
 
-  /**
-   * The service that is being manipulated.  Do not free.
-   */
-  const char *service_name;
-
   /**
    * Timeout for the operation.
    */
   struct GNUNET_TIME_Absolute timeout;
 
   /**
   /**
    * Timeout for the operation.
    */
   struct GNUNET_TIME_Absolute timeout;
 
   /**
-   * Length of service_name plus one.
-   */
-  size_t slen;
-
-  /**
-   * Number of attempts left for transmitting the request to ARM.
-   * We may fail the first time (say because ARM is not yet up),
-   * in which case we wait a bit and re-try (timeout permitting).
+   * Type of the request expressed as a message type (start or stop).
    */
    */
-  unsigned int attempts_left;
+  uint16_t type;
 
   /**
 
   /**
-   * Type of the request expressed as a message type (start or stop).
+   * Flags for passing std descriptors to ARM (when starting ARM).
    */
    */
-  uint16_t type;
+  enum GNUNET_OS_InheritStdioFlags std_inheritance;
 
 };
 
 
 };
 
+#include "do_start_process.c"
+
 
 /**
  * A client specifically requested starting of ARM itself.
 
 /**
  * A client specifically requested starting of ARM itself.
@@ -182,69 +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 *binary;
   char *config;
+  char *loprefix;
+  char *lopostfix;
 
   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE))
 
   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE))
-    {
-      /* arm is running! */
-      if (pos->callback != NULL)
-        pos->callback (pos->cls, GNUNET_YES);
-      GNUNET_free (pos);
-      return;
-    }
-  /* FIXME: should we check that HOSTNAME for 'arm' is localhost? */
-  /* start service */
+  {
+    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 !=
+      GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "OPTIONS",
+                                            &lopostfix))
+    lopostfix = GNUNET_strdup ("");
   if (GNUNET_OK !=
   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);
-      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);
-      return;
-    }
-  pid = GNUNET_OS_start_process (binary, binary, "-d", "-c", config,
-#if DEBUG_ARM
-                                 "-L", "DEBUG",
-#endif
-                                 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 (binary);
   GNUNET_free (config);
-  if (pid == -1)
+  GNUNET_free (loprefix);
+  GNUNET_free (lopostfix);
+  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);
 }
 
@@ -253,170 +413,39 @@ 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 from ARM service\n"));
-      GNUNET_CLIENT_disconnect (sc->h->client);
-      sc->h->client = GNUNET_CLIENT_connect (sc->h->sched, 
-                                            "arm", 
-                                            sc->h->cfg);
-      GNUNET_assert (NULL != sc->h->client);
-      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 service: %u\n",
-             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);
 }
 
 
-/**
- * We've failed to transmit the request to the ARM service.
- * Report our failure and clean up the state.
- *
- * @param sctx the state of the (now failed) request
- */
-static void
-report_transmit_failure (struct RequestContext *sctx)
-{
-  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-             _("Error while trying to transmit to ARM service\n"));
-  if (sctx->callback != NULL)
-    sctx->callback (sctx->cls, GNUNET_SYSERR);
-  GNUNET_free (sctx);
-}
-
-
-/**
- * Transmit a request for a service status change to the
- * ARM service.
- *
- * @param cls the "struct RequestContext" identifying the request
- * @param size how many bytes are available in buf
- * @param buf where to write the request, NULL on error
- * @return number of bytes written to buf
- */
-static size_t
-send_service_msg (void *cls, size_t size, void *buf);
-
-
-/**
- * We've failed to transmit the request to the ARM service but
- * are now going to try again.
- * 
- * @param cls state of the request
- * @param tc task context (unused)
- */
-static void
-retry_request (void *cls,
-              const struct GNUNET_SCHEDULER_TaskContext *tc)
-{
-  struct RequestContext *sctx = cls;
-
-  if (NULL ==
-      GNUNET_CLIENT_notify_transmit_ready (sctx->h->client,
-                                          sctx->slen +
-                                          sizeof (struct
-                                                  GNUNET_MessageHeader),
-                                          GNUNET_TIME_absolute_get_remaining (sctx->timeout),
-                                          &send_service_msg, 
-                                          sctx))
-    {
-      report_transmit_failure (sctx);    
-      return;
-    }
-}
-
-
-/**
- * Transmit a request for a service status change to the
- * ARM service.
- *
- * @param cls the "struct RequestContext" identifying the request
- * @param size how many bytes are available in buf
- * @param buf where to write the request, NULL on error
- * @return number of bytes written to buf
- */
-static size_t
-send_service_msg (void *cls, size_t size, void *buf)
-{
-  struct RequestContext *sctx = cls;
-  struct GNUNET_MessageHeader *msg;
-  struct GNUNET_TIME_Relative rem;
-
-  if (buf == NULL)
-    {
-      GNUNET_CLIENT_disconnect (sctx->h->client);
-      sctx->h->client = GNUNET_CLIENT_connect (sctx->h->sched, 
-                                              "arm", 
-                                              sctx->h->cfg);
-      GNUNET_assert (sctx->h->client != NULL);
-      rem = GNUNET_TIME_absolute_get_remaining (sctx->timeout);
-      if ( (sctx->attempts_left-- > 0) &&
-          (rem.value > 0) )
-       {
-         GNUNET_SCHEDULER_add_delayed (sctx->h->sched,
-                                       GNUNET_NO,
-                                       GNUNET_SCHEDULER_PRIORITY_KEEP,
-                                       GNUNET_SCHEDULER_NO_TASK,
-                                       GNUNET_TIME_relative_min (MIN_RETRY_DELAY,
-                                                                 rem),
-                                       &retry_request,
-                                       sctx);
-         return 0;
-       }
-      report_transmit_failure (sctx);
-      return 0;
-    }
-#if DEBUG_ARM
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              _("Transmitting service request to ARM.\n"));
-#endif
-  GNUNET_assert (size >= sctx->slen);
-  msg = buf;
-  msg->size = htons (sizeof (struct GNUNET_MessageHeader) + sctx->slen);
-  msg->type = htons (sctx->type);
-  memcpy (&msg[1], sctx->service_name, sctx->slen);
-  GNUNET_CLIENT_receive (sctx->h->client,
-                         &handle_response,
-                         sctx,
-                         GNUNET_TIME_absolute_get_remaining (sctx->timeout));
-  return sctx->slen + sizeof (struct GNUNET_MessageHeader);
-}
-
-
 /**
  * Start or stop a service.
  *
 /**
  * Start or stop a service.
  *
@@ -425,43 +454,56 @@ send_service_msg (void *cls, size_t size, void *buf)
  * @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 RequestContext *sctx;
   size_t slen;
+  struct GNUNET_MessageHeader *msg;
 
   slen = strlen (service_name) + 1;
 
   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,
-              _("ARM requests starting 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->cls = cb_cls;
   sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen);
   sctx->h = h;
   sctx->callback = cb;
   sctx->cls = cb_cls;
-  sctx->service_name = (const char*) &sctx[1];
-  memcpy (&sctx[1],
-         service_name,
-         slen);
   sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
   sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
-  sctx->slen = slen;
-  sctx->attempts_left = MAX_ATTEMPTS;
   sctx->type = type;
   sctx->type = type;
-  retry_request (sctx, NULL);
+  memcpy (&sctx[1], service_name, slen);
+  msg = GNUNET_malloc (sizeof (struct GNUNET_MessageHeader) + slen);
+  msg->size = htons (sizeof (struct GNUNET_MessageHeader) + slen);
+  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_break (0);
+    if (cb != NULL)
+      cb (cb_cls, GNUNET_SYSERR);
+    GNUNET_free (sctx);
+    GNUNET_free (msg);
+    return;
+  }
+  GNUNET_free (msg);
 }
 
 
 }
 
 
@@ -470,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
@@ -477,26 +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;
 
 
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              _("Starting service `%s'\n"), service_name);
-  if (0 == strcmp ("arm", service_name))
+  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))
+  {
+    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)
     {
     {
-      sctx = GNUNET_malloc (sizeof (struct RequestContext));
-      sctx->h = h;
-      sctx->callback = cb;
-      sctx->cls = cb_cls;
-      sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
-      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);
 }
 
 
 }
 
 
@@ -512,181 +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'\n"), service_name);
-  if (0 == strcmp ("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);
-      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
- */
-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.
+ * Internal state for a list request with ARM.
  */
  */
-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.
- *
- * @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 */