fix memleak
[oweals/gnunet.git] / src / arm / arm_api.c
index f98b4100af848bebafc6447aad1da70d03e840fa..15563e93398b840d64cf6ed0e3b53b50fbc6e66b 100644 (file)
@@ -1,10 +1,10 @@
 /*
      This file is part of GNUnet.
 /*
      This file is part of GNUnet.
-     (C) 2009, 2010 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"
 
+#define LOG(kind,...) GNUNET_log_from (kind, "arm-api",__VA_ARGS__)
 
 /**
  * Handle for interacting with ARM.
 
 /**
  * Handle for interacting with ARM.
@@ -49,23 +47,13 @@ struct GNUNET_ARM_Handle
    */
   struct GNUNET_CONFIGURATION_Handle *cfg;
 
    */
   struct GNUNET_CONFIGURATION_Handle *cfg;
 
-  /**
-   * Scheduler to use.
-   */
-  struct GNUNET_SCHEDULER_Handle *sched;
-
 };
 
 };
 
-
 /**
  * Context for handling the shutdown of a service.
  */
 struct ShutdownContext
 {
 /**
  * Context for handling the shutdown of a service.
  */
 struct ShutdownContext
 {
-  /**
-   * Scheduler to be used to call continuation
-   */
-  struct GNUNET_SCHEDULER_Handle *sched;
   /**
    * Connection to the service that is being shutdown.
    */
   /**
    * Connection to the service that is being shutdown.
    */
@@ -92,12 +80,13 @@ struct ShutdownContext
   void *cont_cls;
 
   /**
   void *cont_cls;
 
   /**
-   * We received a confirmation that the service will shut down.
+   * Handle for transmission request.
    */
    */
-  int confirmed;
+  struct GNUNET_CLIENT_TransmitHandle *th;
 
 };
 
 
 };
 
+
 /**
  * Handler receiving response to service shutdown requests.
  * First call with NULL: service misbehaving, or something.
 /**
  * Handler receiving response to service shutdown requests.
  * First call with NULL: service misbehaving, or something.
@@ -114,76 +103,40 @@ service_shutdown_handler (void *cls, const struct GNUNET_MessageHeader *msg)
 {
   struct ShutdownContext *shutdown_ctx = cls;
 
 {
   struct ShutdownContext *shutdown_ctx = cls;
 
-  if ((msg == NULL) && (shutdown_ctx->confirmed != GNUNET_YES))
-    {
-      /* Means the other side closed the connection and never confirmed a shutdown */
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                 "Service handle shutdown before ACK!\n");
-      if (shutdown_ctx->cont != NULL)
-        shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_SYSERR);
-      GNUNET_SCHEDULER_cancel(shutdown_ctx->sched, shutdown_ctx->cancel_task);
-      GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
-      GNUNET_free(shutdown_ctx);
-    }
-  else if ((msg == NULL) && (shutdown_ctx->confirmed == GNUNET_YES))
-    {
-#if DEBUG_ARM
-      GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
-                "Service shutdown complete.\n");
-#endif
-      if (shutdown_ctx->cont != NULL)
-        shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_NO);
-
-      GNUNET_SCHEDULER_cancel(shutdown_ctx->sched, shutdown_ctx->cancel_task);
-      GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
-      GNUNET_free(shutdown_ctx);
-    }
-  else
-    {
-      GNUNET_assert(ntohs(msg->size) == sizeof(struct GNUNET_MessageHeader));
-      switch (ntohs(msg->type))
-       {
-       case GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN_ACK:
-#if DEBUG_ARM
-         GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
-                    "Received confirmation for service shutdown.\n");
-#endif
-         shutdown_ctx->confirmed = GNUNET_YES;
-         GNUNET_CLIENT_receive (shutdown_ctx->sock,
-                                &service_shutdown_handler,
-                                shutdown_ctx,
-                                GNUNET_TIME_UNIT_FOREVER_REL);
-         break;
-       default: /* Fall through */
-#if DEBUG_ARM
-         GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
-                    "Service shutdown refused!\n");
-#endif
-         if (shutdown_ctx->cont != NULL)
-           shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_YES);
-
-         GNUNET_SCHEDULER_cancel(shutdown_ctx->sched, shutdown_ctx->cancel_task);
-         GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
-         GNUNET_free(shutdown_ctx);
-         break;
-       }
-    }
+  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)
  */
 /**
  * Shutting down took too long, cancel receive and return error.
  *
  * @param cls closure
  * @param tc context information (why was this task triggered now)
  */
-void service_shutdown_cancel (void *cls,
-                              const struct GNUNET_SCHEDULER_TaskContext * tc)
+static void
+service_shutdown_cancel (void *cls,
+                        const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct ShutdownContext *shutdown_ctx = cls;
 {
   struct ShutdownContext *shutdown_ctx = cls;
-  GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "service_shutdown_cancel called!\n");
-  shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_SYSERR);
-  GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
-  GNUNET_free(shutdown_ctx);
+
+  shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_COMMUNICATION_TIMEOUT);
+  GNUNET_CLIENT_disconnect (shutdown_ctx->sock);
+  GNUNET_free (shutdown_ctx);
 }
 
 
 }
 
 
@@ -199,26 +152,25 @@ void service_shutdown_cancel (void *cls,
 static size_t
 write_shutdown (void *cls, size_t size, void *buf)
 {
 static size_t
 write_shutdown (void *cls, size_t size, void *buf)
 {
-  struct GNUNET_MessageHeader *msg;
   struct ShutdownContext *shutdown_ctx = cls;
   struct ShutdownContext *shutdown_ctx = cls;
+  struct GNUNET_MessageHeader *msg;
 
 
+  shutdown_ctx->th = NULL;
   if (size < sizeof (struct GNUNET_MessageHeader))
   if (size < sizeof (struct GNUNET_MessageHeader))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  _("Failed to transmit shutdown request to client.\n"));
-      shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_SYSERR);
-      GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
-      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 (shutdown_ctx->sched,
-                                                           GNUNET_TIME_absolute_get_remaining(shutdown_ctx->timeout),
-                                                           &service_shutdown_cancel,
-                                                           shutdown_ctx);
+  {
+    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));
   msg = (struct GNUNET_MessageHeader *) buf;
   msg->type = htons (GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN);
   msg->size = htons (sizeof (struct GNUNET_MessageHeader));
@@ -233,7 +185,6 @@ write_shutdown (void *cls, size_t size, void *buf)
  * be used by the caller after this call
  * (calling this function frees "sock" after a while).
  *
  * be used by the caller after this call
  * (calling this function frees "sock" after a while).
  *
- * @param sched the scheduler to use for calling shutdown continuation
  * @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 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
@@ -241,25 +192,21 @@ write_shutdown (void *cls, size_t size, void *buf)
  *
  */
 static void
  *
  */
 static void
-arm_service_shutdown (struct GNUNET_SCHEDULER_Handle *sched,
-                     struct GNUNET_CLIENT_Connection *sock,
+arm_service_shutdown (struct GNUNET_CLIENT_Connection *sock,
                      struct GNUNET_TIME_Relative timeout,
                      struct GNUNET_TIME_Relative timeout,
-                     GNUNET_CLIENT_ShutdownTask cont,
-                     void *cont_cls)
+                     GNUNET_CLIENT_ShutdownTask cont, void *cont_cls)
 {
   struct ShutdownContext *shutdown_ctx;
 {
   struct ShutdownContext *shutdown_ctx;
-  shutdown_ctx = GNUNET_malloc(sizeof(struct ShutdownContext));
-  shutdown_ctx->sched = sched;
+
+  shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext));
   shutdown_ctx->cont = cont;
   shutdown_ctx->cont_cls = cont_cls;
   shutdown_ctx->sock = sock;
   shutdown_ctx->cont = cont;
   shutdown_ctx->cont_cls = cont_cls;
   shutdown_ctx->sock = sock;
-  shutdown_ctx->timeout = GNUNET_TIME_relative_to_absolute(timeout);
-  GNUNET_CLIENT_notify_transmit_ready (sock,
-                                      sizeof (struct
-                                              GNUNET_MessageHeader),
-                                      timeout,
-                                      GNUNET_NO,
-                                      &write_shutdown, shutdown_ctx);
+  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);
 }
 
 
 }
 
 
@@ -270,20 +217,17 @@ arm_service_shutdown (struct GNUNET_SCHEDULER_Handle *sched,
  * @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;
 
   ret = GNUNET_malloc (sizeof (struct GNUNET_ARM_Handle));
   ret->cfg = GNUNET_CONFIGURATION_dup (cfg);
                    const char *service)
 {
   struct GNUNET_ARM_Handle *ret;
 
   ret = GNUNET_malloc (sizeof (struct GNUNET_ARM_Handle));
   ret->cfg = GNUNET_CONFIGURATION_dup (cfg);
-  ret->sched = sched;
   return ret;
 }
 
   return ret;
 }
 
@@ -297,11 +241,12 @@ 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
 {
   /**
 struct ARM_ShutdownContext
 {
   /**
@@ -316,7 +261,6 @@ struct ARM_ShutdownContext
 };
 
 
 };
 
 
-
 /**
  * Internal state for a request with ARM.
  */
 /**
  * Internal state for a request with ARM.
  */
@@ -348,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"
@@ -363,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);
 }
 
@@ -471,50 +420,28 @@ static void
 handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
 {
   struct RequestContext *sc = cls;
 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);
 }
 
@@ -530,31 +457,28 @@ handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
  * @param type type of the request
  */
 static void
  * @param type type of the request
  */
 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;
@@ -567,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);
 }
 
@@ -594,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
@@ -601,62 +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 GNUNET_CLIENT_Connection *client;
   size_t slen;
 {
   struct RequestContext *sctx;
   struct GNUNET_CLIENT_Connection *client;
   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;
     }
-  if (h->client == NULL)
-    {
-      client = GNUNET_CLIENT_connect (h->sched, "arm", h->cfg);
-      if (client == NULL)
-       {
-         cb (cb_cls, GNUNET_SYSERR);
-         return;
-       }
-      GNUNET_CLIENT_ignore_shutdown (client, GNUNET_YES);
-      h->client = client;
-    }
-  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
 /**
  * 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, GNUNET_NO if arm is shutdown
- *        GNUNET_YES if arm remains running, and GNUNET_SYSERR on error
+ * @param reason reason for callback
  */
  */
-void arm_shutdown_callback (void *cls,
-                            int reason)
+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);
 {
   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);
+  GNUNET_free (arm_shutdown_ctx);
 }
 
 
 }
 
 
@@ -671,38 +594,197 @@ void arm_shutdown_callback (void *cls,
  */
 void
 GNUNET_ARM_stop_service (struct GNUNET_ARM_Handle *h,
  */
 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)
+                        const char *service_name,
+                        struct GNUNET_TIME_Relative timeout,
+                        GNUNET_ARM_Callback cb, void *cb_cls)
 {
   struct ARM_ShutdownContext *arm_shutdown_ctx;
   struct GNUNET_CLIENT_Connection *client;
 
 {
   struct ARM_ShutdownContext *arm_shutdown_ctx;
   struct GNUNET_CLIENT_Connection *client;
 
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              _("Stopping service `%s' within %llu ms\n"), service_name,
-             (unsigned long long) timeout.value);
+  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)
   if (h->client == NULL)
+  {
+    client = GNUNET_CLIENT_connect ("arm", h->cfg);
+    if (client == NULL)
     {
     {
-      client = GNUNET_CLIENT_connect (h->sched, "arm", h->cfg);
-      if (client == NULL)
-       {
-         cb (cb_cls, GNUNET_SYSERR);
-         return;
-       }
-      GNUNET_CLIENT_ignore_shutdown (client, GNUNET_YES);
-      h->client = client;
+      cb (cb_cls, GNUNET_SYSERR);
+      return;
     }
     }
+    h->client = client;
+  }
   if (0 == strcasecmp ("arm", service_name))
   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);
+}
+
+
+/**
+ * Internal state for a list request with ARM.
+ */
+struct ListRequestContext
+{
+
+  /**
+   * Pointer to our handle with ARM.
+   */
+  struct GNUNET_ARM_Handle *h;
+
+  /**
+   * Function to call with a status code for the requested operation.
+   */
+  GNUNET_ARM_List_Callback callback;
+
+  /**
+   * Closure for "callback".
+   */
+  void *cls;
+
+  /**
+   * Timeout for the operation.
+   */
+  struct GNUNET_TIME_Absolute timeout;
+};
+
+
+/**
+ * Process a response from ARM for the list request.
+ *
+ * @param cls the list request context
+ * @param msg the response
+ */
+static void
+handle_list_response (void *cls, const struct GNUNET_MessageHeader *msg)
+{
+  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 == 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++)
     {
     {
-      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->sched, h->client, timeout, &arm_shutdown_callback, arm_shutdown_ctx);
-      h->client = NULL;
-      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;
     }
     }
-  change_service (h, service_name, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_STOP);
+    sc->callback (sc->cls, GNUNET_YES, rcount, list);
+  }
+  GNUNET_free (sc);
 }
 
 
 }
 
 
+/**
+ * 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
+ */
+void
+GNUNET_ARM_list_running_services (struct GNUNET_ARM_Handle *h,
+                                  struct GNUNET_TIME_Relative timeout,
+                                  GNUNET_ARM_List_Callback cb, void *cb_cls)
+{
+  struct ListRequestContext *sctx;
+  struct GNUNET_MessageHeader msg;
+  struct GNUNET_CLIENT_Connection *client;
+  
+  if (h->client == NULL)
+  {
+    client = GNUNET_CLIENT_connect ("arm", h->cfg);
+    if (client == NULL)
+    {
+      GNUNET_break (0);
+      cb (cb_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR, 0, NULL);
+      return;
+    }
+    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 */