gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] r26402 - in gnunet/src: arm include regex util


From: gnunet
Subject: [GNUnet-SVN] r26402 - in gnunet/src: arm include regex util
Date: Wed, 13 Mar 2013 18:49:26 +0100

Author: LRN
Date: 2013-03-13 18:49:26 +0100 (Wed, 13 Mar 2013)
New Revision: 26402

Modified:
   gnunet/src/arm/Makefile.am
   gnunet/src/arm/arm.h
   gnunet/src/arm/arm_api.c
   gnunet/src/arm/gnunet-arm.c
   gnunet/src/arm/gnunet-service-arm.c
   gnunet/src/arm/mockup-service.c
   gnunet/src/arm/test_arm_api.c
   gnunet/src/arm/test_exponential_backoff.c
   gnunet/src/arm/test_gnunet_service_arm.c
   gnunet/src/include/gnunet_arm_service.h
   gnunet/src/include/gnunet_protocols.h
   gnunet/src/include/gnunet_server_lib.h
   gnunet/src/regex/gnunet-regex-profiler.c
   gnunet/src/regex/regex_test_lib.c
   gnunet/src/util/client.c
   gnunet/src/util/connection.c
   gnunet/src/util/server.c
Log:
All-encompassing ARM update

Modified: gnunet/src/arm/Makefile.am
===================================================================
--- gnunet/src/arm/Makefile.am  2013-03-13 17:49:17 UTC (rev 26401)
+++ gnunet/src/arm/Makefile.am  2013-03-13 17:49:26 UTC (rev 26402)
@@ -19,7 +19,7 @@
 lib_LTLIBRARIES = libgnunetarm.la
 
 libgnunetarm_la_SOURCES = \
-  arm_api.c arm.h
+  arm_api.c arm_monitor_api.c arm.h
 libgnunetarm_la_LIBADD = \
   $(top_builddir)/src/util/libgnunetutil.la \
   $(GN_LIBINTL) $(XLIB)

Modified: gnunet/src/arm/arm.h
===================================================================
--- gnunet/src/arm/arm.h        2013-03-13 17:49:17 UTC (rev 26401)
+++ gnunet/src/arm/arm.h        2013-03-13 17:49:26 UTC (rev 26402)
@@ -36,23 +36,63 @@
 GNUNET_NETWORK_STRUCT_BEGIN
 
 /**
- * Reply from ARM to client.
+ * Status update from ARM to client.
  */
-struct GNUNET_ARM_ResultMessage
+struct GNUNET_ARM_StatusMessage
 {
 
   /**
-   * Reply to client, of type is GNUNET_MESSAGE_TYPE_ARM_RESULT. 
+   * Reply to client, of type is GNUNET_MESSAGE_TYPE_ARM_STATUS. 
    */
   struct GNUNET_MessageHeader header;
   
   /**
-   * Status from the 'enum GNUNET_ARM_ProcessStatus'
+   * Status from the 'enum GNUNET_ARM_ServiceStatus'
    */
   uint32_t status;
+
+  /* followed by a 0-terminated service name */
 };
 
+struct GNUNET_ARM_Message
+{
+  /**
+   * Reply to client, type is GNUNET_MESSAGE_TYPE_ARM_RESULT or
+   * GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT. 
+   * OR
+   * Request from client, type is GNUNET_MESSAGE_TYPE_ARM_REQUEST
+   */
+  struct GNUNET_MessageHeader header;
+  
+  /**
+   * ID of a request that is being replied to.
+   * OR
+   * ID of a request that is being sent.
+   */
+  uint64_t request_id;
+
+  /* For requests - followed by a 0-terminated service name */
+};
+
+
 /**
+ * Reply from ARM to client.
+ */
+struct GNUNET_ARM_ResultMessage
+{
+
+  /**
+   * Reply to client, of type is GNUNET_MESSAGE_TYPE_ARM_RESULT, with an ID.
+   */
+  struct GNUNET_ARM_Message arm_msg;
+  
+  /**
+   * Result from the 'enum GNUNET_ARM_Result'
+   */
+  uint32_t result;
+};
+
+/**
  * Reply from ARM to client for the 
  * GNUNET_MESSAGE_TYPE_ARM_LIST request followed by count 
  * '\0' terminated strings. header->size contains the
@@ -61,9 +101,10 @@
 struct GNUNET_ARM_ListResultMessage
 {
   /**
-   * Reply to client is of type GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT
+   * Reply to client, of type is GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT,
+   * with an ID.
    */
-  struct GNUNET_MessageHeader header;
+  struct GNUNET_ARM_Message arm_msg;
 
   /**
    * Number of '\0' terminated strings that follow

Modified: gnunet/src/arm/arm_api.c
===================================================================
--- gnunet/src/arm/arm_api.c    2013-03-13 17:49:17 UTC (rev 26401)
+++ gnunet/src/arm/arm_api.c    2013-03-13 17:49:26 UTC (rev 26402)
@@ -1,6 +1,6 @@
 /*
      This file is part of GNUnet.
-     (C) 2009, 2010, 2012 Christian Grothoff (and other contributing authors)
+     (C) 2009, 2010, 2012, 2013 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
@@ -21,7 +21,7 @@
 /**
  * @file arm/arm_api.c
  * @brief API for accessing the ARM service
- * @author Christian Grothoff
+ * @author Christian Grothoff, LRN
  */
 #include "platform.h"
 #include "gnunet_arm_service.h"
@@ -38,7 +38,7 @@
 {
 
   /**
-   * Our connection to the ARM service.
+   * Our control connection to the ARM service.
    */
   struct GNUNET_CLIENT_Connection *client;
 
@@ -47,258 +47,470 @@
    */
   struct GNUNET_CONFIGURATION_Handle *cfg;
 
+  /**
+   * Handle for our current transmission request.
+   */
+  struct GNUNET_CLIENT_TransmitHandle *cth;
+
+  /**
+   * Head of doubly-linked list of pending requests.
+   */
+  struct ARMControlMessage *control_pending_head;
+
+  /**
+   * Tail of doubly-linked list of pending requests.
+   */
+  struct ARMControlMessage *control_pending_tail;
+
+  /**
+   * Head of doubly-linked list of sent requests.
+   */
+  struct ARMControlMessage *control_sent_head;
+
+  /**
+   * Tail of doubly-linked list of sent requests.
+   */
+  struct ARMControlMessage *control_sent_tail;
+
+  /**
+   * ID of the reconnect task (if any).
+   */
+  GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
+
+  /**
+   * Current delay we use for re-trying to connect to core.
+   */
+  struct GNUNET_TIME_Relative retry_backoff;
+
+  /**
+   * Are we currently disconnected and hence unable to send?
+   */
+  unsigned char currently_down;
+
+  /**
+   * Callback to invoke on connection/disconnection.
+   */
+  GNUNET_ARM_ConnectionStatusCallback conn_status;
+
+  /**
+   * Closure for conn_status.
+   */
+  void *conn_status_cls;
+
+  /**
+   * GNUNET_YES if we're running a service test.
+   */
+  unsigned char service_test_is_active;
+
+  /**
+   * Counter for request identifiers
+   */
+  uint64_t request_id_counter;
 };
 
+
 /**
- * Context for handling the shutdown of a service.
+ * Entry in a doubly-linked list of control messages to be transmitted
+ * to the arm service.
+ *
+ * The actual message is allocated at the end of this struct.
  */
-struct ShutdownContext
+struct ARMControlMessage
 {
   /**
-   * Connection to the service that is being shutdown.
+   * This is a doubly-linked list.
    */
-  struct GNUNET_CLIENT_Connection *sock;
+  struct ARMControlMessage *next;
 
   /**
-   * Time allowed for shutdown to happen.
+   * This is a doubly-linked list.
    */
-  struct GNUNET_TIME_Absolute timeout;
+  struct ARMControlMessage *prev;
 
   /**
-   * Task set up to cancel the shutdown request on timeout.
+   * Callback for service state change requests.
    */
-  GNUNET_SCHEDULER_TaskIdentifier cancel_task;
+  GNUNET_ARM_ResultCallback result_cont;
 
   /**
-   * Task to call once shutdown complete
+   * Callback for service list requests.
    */
-  GNUNET_CLIENT_ShutdownTask cont;
+  GNUNET_ARM_ServiceListCallback list_cont;
 
   /**
-   * Closure for shutdown continuation
+   * Closure for 'result_cont' or 'list_cont'.
    */
   void *cont_cls;
 
   /**
-   * Handle for transmission request.
+   * Timeout for the operation.
    */
-  struct GNUNET_CLIENT_TransmitHandle *th;
+  struct GNUNET_TIME_Absolute timeout;
 
+  /**
+   * Type of the request expressed as a message type (start, stop or list).
+   */
+  uint16_t type;
+
+  /**
+   * Flags for passing std descriptors to ARM (when starting ARM).
+   */
+  enum GNUNET_OS_InheritStdioFlags std_inheritance;
+
+  /**
+   * ARM handle.
+   */
+  struct GNUNET_ARM_Handle *h;
+
+  /**
+   * Message to send.
+   */
+  struct GNUNET_ARM_Message *msg;
+
+  /**
+   * Task to run when request times out.
+   */
+  GNUNET_SCHEDULER_TaskIdentifier timeout_task_id;
 };
 
+static void
+client_notify_handler (void *cls, const struct GNUNET_MessageHeader *msg);
 
+static void
+reconnect_arm (struct GNUNET_ARM_Handle *h);
+
+static void
+trigger_next_request (struct GNUNET_ARM_Handle *h, int ignore_currently_down);
+
+
 /**
- * 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.
+ * Task scheduled to try to re-connect to arm.
  *
- * @param cls closure
- * @param msg NULL, indicating socket closure.
+ * @param cls the 'struct GNUNET_ARM_Handle'
+ * @param tc task context
  */
 static void
-service_shutdown_handler (void *cls, const struct GNUNET_MessageHeader *msg)
+reconnect_arm_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
-  struct ShutdownContext *shutdown_ctx = cls;
+  struct GNUNET_ARM_Handle *h = cls;
 
-  if (NULL != msg)
+  h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting to ARM service after delay\n");
+  reconnect_arm (h);
+}
+
+
+static void
+clear_pending_messages (struct GNUNET_ARM_Handle *h, enum 
GNUNET_ARM_RequestStatus result)
+{
+  struct ARMControlMessage *cm;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Clearing pending messages\n");
+
+  while (NULL != (cm = h->control_pending_head))
   {
-    /* 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;
+    GNUNET_CONTAINER_DLL_remove (h->control_pending_head,
+                                 h->control_pending_tail, cm);
+    GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cm->timeout_task_id);
+    GNUNET_SCHEDULER_cancel (cm->timeout_task_id);
+    if (NULL != cm->result_cont)
+      cm->result_cont (cm->cont_cls, cm->h, result, NULL, 0);
+    GNUNET_free_non_null (cm->msg);
+    GNUNET_free (cm);
   }
-  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.
+ * Close down any existing connection to the ARM service and
+ * try re-establishing it later.
  *
- * @param cls closure
- * @param tc context information (why was this task triggered now)
+ * @param h our handle
  */
 static void
-service_shutdown_cancel (void *cls,
-                        const struct GNUNET_SCHEDULER_TaskContext *tc)
+reconnect_arm_later (struct GNUNET_ARM_Handle *h)
 {
-  struct ShutdownContext *shutdown_ctx = cls;
+  if (GNUNET_NO != h->currently_down)
+    return;
 
-  shutdown_ctx->cont (shutdown_ctx->cont_cls, 
GNUNET_ARM_PROCESS_COMMUNICATION_TIMEOUT);
-  GNUNET_CLIENT_disconnect (shutdown_ctx->sock);
-  GNUNET_free (shutdown_ctx);
+  if (NULL != h->cth)
+  {
+    GNUNET_CLIENT_notify_transmit_ready_cancel (h->cth);
+    h->cth = NULL;
+  }
+
+  if (NULL != h->client)
+  {
+    GNUNET_CLIENT_disconnect (h->client);
+    h->client = NULL;
+  }
+
+  if (NULL != h->conn_status)
+    h->conn_status (h->conn_status_cls, h, GNUNET_NO, GNUNET_NO);
+
+  h->currently_down = GNUNET_YES;
+
+  GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == h->reconnect_task);
+  h->reconnect_task =
+      GNUNET_SCHEDULER_add_delayed (h->retry_backoff, &reconnect_arm_task, h);
+  /* Don't clear pending messages on disconnection, deliver them later 
+  clear_pending_messages (h, GNUNET_ARM_REQUEST_DISCONNECTED);
+  GNUNET_assert (NULL == h->control_pending_head);
+  */
+  h->retry_backoff = GNUNET_TIME_STD_BACKOFF (h->retry_backoff);
 }
 
-
 /**
- * If possible, write a shutdown message to the target
- * buffer and destroy the client connection.
+ * Transmit the next message to the arm service.
  *
- * @param cls the "struct GNUNET_CLIENT_Connection" to destroy
+ * @param cls closure with the 'struct GNUNET_ARM_Handle'
  * @param size number of bytes available in buf
- * @param buf NULL on error, otherwise target buffer
- * @return number of bytes written to buf
+ * @param buf where the callee should write the message
+ * @return number of bytes written to buf 
  */
 static size_t
-write_shutdown (void *cls, size_t size, void *buf)
+transmit_arm_message (void *cls, size_t size, void *buf)
 {
-  struct ShutdownContext *shutdown_ctx = cls;
-  struct GNUNET_MessageHeader *msg;
+  struct GNUNET_ARM_Handle *h = cls;
+  struct ARMControlMessage *cm;
+  struct GNUNET_ARM_Message *arm_msg;
+  uint16_t msize;
+  uint64_t request_id;
 
-  shutdown_ctx->th = NULL;
-  if (size < sizeof (struct GNUNET_MessageHeader))
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+      "transmit_arm_message is running with %p buffer of size %lu. ARM is 
known to be %s\n",
+      buf, size, h->currently_down ? "unconnected" : "connected");
+  GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == h->reconnect_task);
+  h->cth = NULL;
+  if ((GNUNET_YES == h->currently_down) && (NULL != buf))
   {
-    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 */
+    h->currently_down = GNUNET_NO;
+    if (NULL != h->conn_status)
+      h->conn_status (h->conn_status_cls, h, GNUNET_YES, GNUNET_NO);
+    h->retry_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
+    GNUNET_CLIENT_receive (h->client, &client_notify_handler, h,
+                           GNUNET_TIME_UNIT_FOREVER_REL);
   }
-  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);
+  if (NULL == buf)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Transmission failed, initiating reconnect\n");
+    reconnect_arm_later (h);
+    return 0;
+  }
+  if (NULL == (cm = h->control_pending_head))
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG, "Queue is empty, not sending anything\n");
+    return 0;
+  }
+
+  GNUNET_assert (NULL != cm->msg);
+  msize = ntohs (cm->msg->header.size);
+  if (size < msize)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+        "Request is too big (%u < %u), not sending it\n", size, msize);
+    trigger_next_request (h, GNUNET_NO);
+    return 0;
+  }
+  arm_msg = cm->msg;
+  if (0 == h->request_id_counter)
+    h->request_id_counter++;
+  request_id = h->request_id_counter++;
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Transmitting control message with %u bytes of type %u to arm with id 
%llu\n",
+       (unsigned int) msize, (unsigned int) ntohs (cm->msg->header.type), 
request_id);
+  arm_msg->request_id = GNUNET_htonll (request_id);
+  memcpy (buf, cm->msg, msize);
+  /* Otherwise we won't be able to find it later! */
+  arm_msg->request_id = request_id;
+
+  GNUNET_CONTAINER_DLL_remove (h->control_pending_head,
+                               h->control_pending_tail, cm);
+  GNUNET_CONTAINER_DLL_insert_tail (h->control_sent_head,
+                                    h->control_sent_tail, cm);
+
+  /* Don't free msg, keep it around (kind of wasteful, but then we don't
+   * really have many messages to handle, and it'll be freed when it times
+   * out anyway.
+   */
+  trigger_next_request (h, GNUNET_NO);
+  return msize;
 }
 
 
 /**
- * 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).
+ * Check the list of pending requests, send the next
+ * one to the arm.
  *
- * @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
- *
+ * @param h arm handle
+ * @param ignore_currently_down transmit message even if not initialized?
  */
 static void
-arm_service_shutdown (struct GNUNET_CLIENT_Connection *sock,
-                     struct GNUNET_TIME_Relative timeout,
-                     GNUNET_CLIENT_ShutdownTask cont, void *cont_cls)
+trigger_next_request (struct GNUNET_ARM_Handle *h, int ignore_currently_down)
 {
-  struct ShutdownContext *shutdown_ctx;
+  uint16_t msize;
 
-  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);
+  if ((GNUNET_YES == h->currently_down) && (ignore_currently_down == 
GNUNET_NO))
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "ARM connection down, not processing queue\n");
+    return;
+  }
+  if (NULL != h->cth)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG, "Request pending, not processing queue\n");
+    return;
+  }
+  if (NULL != h->control_pending_head)
+    msize =
+        ntohs (((struct GNUNET_MessageHeader *) &h->
+                control_pending_head[1])->size);
+  else if (GNUNET_NO == ignore_currently_down)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Request queue empty, not processing queue\n");
+    return;                     /* no pending message */
+  }
+  h->cth =
+      GNUNET_CLIENT_notify_transmit_ready (h->client, msize,
+                                           GNUNET_TIME_UNIT_FOREVER_REL,
+                                           GNUNET_NO, &transmit_arm_message, 
h);
 }
 
 
 /**
- * Setup a context for communicating with ARM.  Note that this
+ * Connect to arm.
+ *
+ * @param h arm handle
+ */
+static void
+reconnect_arm (struct GNUNET_ARM_Handle *h)
+{
+  GNUNET_assert (NULL == h->client);
+  GNUNET_assert (GNUNET_YES == h->currently_down);
+  h->client = GNUNET_CLIENT_connect ("arm", h->cfg);
+  if (NULL == h->client)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+          "arm_api, GNUNET_CLIENT_connect returned NULL\n");
+    if (NULL != h->conn_status)
+      h->conn_status (h->conn_status_cls, h, GNUNET_NO, GNUNET_YES);
+    return;
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+        "arm_api, GNUNET_CLIENT_connect returned non-NULL\n");
+  trigger_next_request (h, GNUNET_YES);
+}
+
+
+/**
+ * Set up a context for communicating with ARM.  Note that this
  * can be done even if the ARM service is not yet running.
+ * Never fails.
  *
  * @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 service service that *this* process is implementing/providing, can 
be NULL
- * @return context to use for further ARM operations, NULL on error
+ * @return context to use for further ARM operations
  */
 struct GNUNET_ARM_Handle *
-GNUNET_ARM_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                   const char *service)
+GNUNET_ARM_alloc (const struct GNUNET_CONFIGURATION_Handle *cfg)
 {
   struct GNUNET_ARM_Handle *ret;
 
   ret = GNUNET_malloc (sizeof (struct GNUNET_ARM_Handle));
   ret->cfg = GNUNET_CONFIGURATION_dup (cfg);
+  ret->currently_down = GNUNET_YES;
+  ret->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
   return ret;
 }
 
 
 /**
- * Disconnect from the ARM service.
+ * Start connecting to the ARM service using the context.
  *
- * @param h the handle that was being used
+ * @param h ARM handle
+ * @param conn_status will be called when connecting/disconnecting
+ * @param cls closure for conn_status
  */
 void
-GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *h)
+GNUNET_ARM_connect (struct GNUNET_ARM_Handle *h,
+                    GNUNET_ARM_ConnectionStatusCallback conn_status, void *cls)
 {
-  if (h->client != NULL)
-    GNUNET_CLIENT_disconnect (h->client);
-  GNUNET_CONFIGURATION_destroy (h->cfg);
-  GNUNET_free (h);
+  h->conn_status = conn_status;
+  h->conn_status_cls = cls;
+  reconnect_arm (h);
 }
 
 
-struct ARM_ShutdownContext
+/**
+ * Disconnect from the ARM service (if connected) and destroy the context.
+ * Don't call inside an ARM callback!
+ *
+ * @param h the handle that was being used
+ */
+void
+GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *handle)
 {
-  /**
-   * Callback to call once shutdown complete.
-   */
-  GNUNET_ARM_Callback cb;
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from ARM service\n");
+  if (NULL != handle->cth)
+  {
+    GNUNET_CLIENT_notify_transmit_ready_cancel (handle->cth);
+    handle->cth = NULL;
+  }
+  clear_pending_messages (handle, GNUNET_ARM_REQUEST_DISCONNECTED);
+  if (NULL != handle->client)
+  {
+    GNUNET_CLIENT_disconnect (handle->client);
+    handle->client = NULL;
+  }
+  if (GNUNET_SCHEDULER_NO_TASK != handle->reconnect_task)
+  {
+    GNUNET_SCHEDULER_cancel (handle->reconnect_task);
+    handle->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
+  }
+  if (GNUNET_NO == handle->service_test_is_active)
+  {
+    GNUNET_CONFIGURATION_destroy (handle->cfg);
+    GNUNET_free (handle);
+  }
+}
 
-  /**
-   * Closure for callback.
-   */
-  void *cb_cls;
-};
 
-
 /**
- * Internal state for a request with ARM.
+ * Message timed out. Remove it from the queue.
+ *
+ * @param cls the message (struct ARMControlMessage *)
+ * @param tc task context
  */
-struct RequestContext
+static void
+control_message_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext 
*tc)
 {
+  struct ARMControlMessage *cm = cls;
+  struct GNUNET_ARM_Message *arm_msg;
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Control message timed out\n");
+  arm_msg = cm->msg;
+  if ((NULL == arm_msg) || (0 == arm_msg->request_id))
+  {
+    GNUNET_CONTAINER_DLL_remove (cm->h->control_pending_head,
+                                 cm->h->control_pending_tail, cm);
+  }
+  else
+  {
+    GNUNET_CONTAINER_DLL_remove (cm->h->control_sent_head,
+                                 cm->h->control_sent_tail, cm);
+  }
+  if (NULL != cm->result_cont)
+    cm->result_cont (cm->cont_cls, cm->h, GNUNET_ARM_REQUEST_TIMEOUT, NULL, 0);
+  else if (NULL != cm->list_cont)
+    cm->list_cont (cm->cont_cls, cm->h, GNUNET_ARM_REQUEST_TIMEOUT, 0, NULL);
+  GNUNET_free_non_null (cm->msg);
+  GNUNET_free (cm);
+}
 
-  /**
-   * Pointer to our handle with ARM.
-   */
-  struct GNUNET_ARM_Handle *h;
 
-  /**
-   * Function to call with a status code for the requested operation.
-   */
-  GNUNET_ARM_Callback callback;
-
-  /**
-   * Closure for "callback".
-   */
-  void *cls;
-
-  /**
-   * Timeout for the operation.
-   */
-  struct GNUNET_TIME_Absolute timeout;
-
-  /**
-   * Type of the request expressed as a message type (start or stop).
-   */
-  uint16_t type;
-
-  /**
-   * Flags for passing std descriptors to ARM (when starting ARM).
-   */
-  enum GNUNET_OS_InheritStdioFlags std_inheritance;
-
-};
-
 #include "do_start_process.c"
 
 
@@ -308,78 +520,89 @@
  * or not ARM is running; if it is, report success.  If
  * it is not, start the ARM process.
  *
- * @param cls the context for the request that we will report on (struct 
RequestContext*)
+ * @param cls the context for the request that we will report on (struct 
ARMControlMessage *)
  * @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)
 {
-  struct RequestContext *pos = cls;
+  struct ARMControlMessage *cm = cls;
   struct GNUNET_OS_Process *proc;
+  unsigned char test_is_active;
   char *cbinary;
   char *binary;
   char *config;
   char *loprefix;
   char *lopostfix;
 
-  if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE))
+  test_is_active = cm->h->service_test_is_active;
+
+  /* FIXME: shouldn't we check for GNUNET_SCHEDULER_REASON_SHUTDOWN ? */
+  if ((GNUNET_YES == test_is_active) &&
+      (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE)))
   {
     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);
+    if (cm->result_cont)
+      cm->result_cont (cm->cont_cls, cm->h, GNUNET_ARM_REQUEST_SENT_OK, "arm", 
GNUNET_ARM_RESULT_IS_STARTED_ALREADY);
+  }
+  if (GNUNET_NO == test_is_active)
+  {
+    /* User disconnected & destroyed ARM handle in the middle of
+     * the service test, so we kept the handle around until now.
+     */
+    GNUNET_CONFIGURATION_destroy (cm->h->cfg);
+    GNUNET_free (cm->h);
+  }
+  if ((0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE)) ||
+      (GNUNET_NO == test_is_active))
+  {
+    GNUNET_free (cm);
     return;
   }
+  cm->h->service_test_is_active = GNUNET_NO;
   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))
+  if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (
+      cm->h->cfg, "arm", "PREFIX", &loprefix))
     loprefix = GNUNET_strdup ("");
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "OPTIONS",
-                                            &lopostfix))
+  if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (
+      cm->h->cfg, "arm", "OPTIONS", &lopostfix))
     lopostfix = GNUNET_strdup ("");
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "BINARY",
-                                            &cbinary))
+  if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (
+      cm->h->cfg, "arm", "BINARY", &cbinary))
   {
-    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_log_config_missing (GNUNET_ERROR_TYPE_WARNING, "arm", "BINARY");
+    if (cm->result_cont)
+      cm->result_cont (cm->cont_cls, cm->h, GNUNET_ARM_REQUEST_SENT_OK, "arm", 
GNUNET_ARM_RESULT_IS_NOT_KNOWN);
+    GNUNET_free (cm);
     GNUNET_free (loprefix);
     GNUNET_free (lopostfix);
     return;
   }
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_filename (pos->h->cfg, "arm", "CONFIG",
-                                              &config))
+  if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (
+      cm->h->cfg, "arm", "CONFIG", &config))
     config = NULL;
   binary = GNUNET_OS_get_libexec_binary_path (cbinary);
   GNUNET_free (cbinary);
-  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")))
+  if ((GNUNET_YES == GNUNET_CONFIGURATION_have_value (
+          cm->h->cfg, "TESTING", "WEAKRANDOM")) &&
+      (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (
+          cm->h->cfg, "TESTING", "WEAKRANDOM")) &&
+      (GNUNET_NO == GNUNET_CONFIGURATION_have_value (
+          cm->h->cfg, "TESTING", "HOSTFILE")))
   {
     /* Means we are ONLY running locally */
     /* we're clearly running a test, don't daemonize */
     if (NULL == config)
-      proc = do_start_process (GNUNET_NO, pos->std_inheritance,
+      proc = do_start_process (GNUNET_NO, cm->std_inheritance,
                               NULL, loprefix, binary,
                               /* no daemonization! */
                               lopostfix, NULL);
     else
-      proc = do_start_process (GNUNET_NO, pos->std_inheritance,
+      proc = do_start_process (GNUNET_NO, cm->std_inheritance,
                               NULL, loprefix, binary, "-c", config,
                               /* no daemonization! */
                               lopostfix, NULL);
@@ -387,11 +610,11 @@
   else
   {
     if (NULL == config)
-      proc = do_start_process (GNUNET_NO, pos->std_inheritance,
+      proc = do_start_process (GNUNET_NO, cm->std_inheritance,
                               NULL, loprefix, binary,
                               "-d", lopostfix, NULL);
     else
-      proc = do_start_process (GNUNET_NO, pos->std_inheritance,
+      proc = do_start_process (GNUNET_NO, cm->std_inheritance,
                               NULL, loprefix, binary, "-c", config,
                               "-d", lopostfix, NULL);
   }
@@ -401,56 +624,22 @@
   GNUNET_free (lopostfix);
   if (NULL == proc)
   {
-    if (pos->callback != NULL)
-      pos->callback (pos->cls, GNUNET_ARM_PROCESS_FAILURE);
-    GNUNET_free (pos);
+    if (cm->result_cont)
+      cm->result_cont (cm->cont_cls, cm->h, GNUNET_ARM_REQUEST_SENT_OK, "arm",
+          GNUNET_ARM_RESULT_START_FAILED);
+    GNUNET_free (cm);
     return;
   }
-  if (pos->callback != NULL)
-    pos->callback (pos->cls, GNUNET_ARM_PROCESS_STARTING);
+  if (cm->result_cont)
+    cm->result_cont (cm->cont_cls, cm->h, GNUNET_ARM_REQUEST_SENT_OK, "arm",
+        GNUNET_ARM_RESULT_STARTING);
   GNUNET_OS_process_destroy (proc);
-  GNUNET_free (pos);
+  reconnect_arm (cm->h);
+  GNUNET_free (cm);
 }
 
 
 /**
- * Process a response from ARM to a request for a change in service
- * status.
- *
- * @param cls the request context
- * @param msg the response
- */
-static void
-handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
-{
-  struct RequestContext *sc = cls;
-  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)
-    sc->callback (sc->cls, status);
-  GNUNET_free (sc);
-}
-
-
-/**
  * Start or stop a service.
  *
  * @param h handle to ARM
@@ -462,74 +651,65 @@
  */
 static void
 change_service (struct GNUNET_ARM_Handle *h, const char *service_name,
-               struct GNUNET_TIME_Relative timeout, GNUNET_ARM_Callback cb,
+               struct GNUNET_TIME_Relative timeout, GNUNET_ARM_ResultCallback 
cb,
                void *cb_cls, uint16_t type)
 {
-  struct RequestContext *sctx;
+  struct ARMControlMessage *cm;
   size_t slen;
-  struct GNUNET_MessageHeader *msg;
+  struct GNUNET_ARM_Message *msg;
 
   slen = strlen (service_name) + 1;
-  if (slen + sizeof (struct GNUNET_MessageHeader) >=
+  if (slen + sizeof (struct GNUNET_ARM_Message) >=
       GNUNET_SERVER_MAX_MESSAGE_SIZE)
   {
     GNUNET_break (0);
     if (cb != NULL)
-      cb (cb_cls, GNUNET_NO);
+      cb (cb_cls, h, GNUNET_ARM_REQUEST_TOO_LONG, NULL, 0);
     return;
   }
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "Requesting %s of service `%s'.\n",
+       (GNUNET_MESSAGE_TYPE_ARM_START == type) ? "start" : "termination",
+       service_name);
+  cm = GNUNET_malloc (sizeof (struct ARMControlMessage) + slen);
+  cm->h = h;
+  cm->result_cont = cb;
+  cm->cont_cls = cb_cls;
+  cm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
+  memcpy (&cm[1], service_name, slen);
+  msg = GNUNET_malloc (sizeof (struct GNUNET_ARM_Message) + slen);
+  msg->header.size = htons (sizeof (struct GNUNET_ARM_Message) + slen);
+  msg->header.type = htons (type);
+  memcpy (&msg[1], service_name, slen);
+  cm->msg = msg;
   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->timeout = GNUNET_TIME_relative_to_absolute (timeout);
-  sctx->type = type;
-  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);
+      "Inserting a control message into the queue. Timeout is %llu\n",
+      GNUNET_TIME_absolute_get_remaining (cm->timeout).rel_value);
+  GNUNET_CONTAINER_DLL_insert_tail (h->control_pending_head,
+                                    h->control_pending_tail, cm);
+  cm->timeout_task_id =
+      GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
+                                    (cm->timeout), &control_message_timeout, 
cm);
+  trigger_next_request (h, GNUNET_NO);
 }
 
 
 /**
- * Start a service.
+ * Request for a service to be started.
  *
  * @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 cont callback to invoke after request is sent or not sent
+ * @param cont_cls closure for callback
  */
 void
-GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h,
-                         const char *service_name,
-                          enum GNUNET_OS_InheritStdioFlags std_inheritance,
-                         struct GNUNET_TIME_Relative timeout,
-                         GNUNET_ARM_Callback cb, void *cb_cls)
+GNUNET_ARM_request_service_start (struct GNUNET_ARM_Handle *h,
+    const char *service_name, enum GNUNET_OS_InheritStdioFlags std_inheritance,
+    struct GNUNET_TIME_Relative timeout, GNUNET_ARM_ResultCallback cont,
+    void *cont_cls)
 {
-  struct RequestContext *sctx;
-  struct GNUNET_CLIENT_Connection *client;
+  struct ARMControlMessage *cm;
   size_t slen;
 
   LOG (GNUNET_ERROR_TYPE_DEBUG,
@@ -537,258 +717,266 @@
        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)
+    /* Possible cases:
+     * 1) We're connected to ARM already. Invoke the callback immediately.
+     * 2) We're not connected to ARM.
+     *    Cancel any reconnection attempts temporarily, then perform
+     *    a service test.
+     */
+    if (GNUNET_NO == h->currently_down)
     {
+      LOG (GNUNET_ERROR_TYPE_DEBUG, "ARM is already running\n");
+      if (NULL != cont)
+        cont (cont_cls, h, GNUNET_ARM_REQUEST_SENT_OK, "arm", 
GNUNET_ARM_RESULT_IS_STARTED_ALREADY);
+    }
+    else if (GNUNET_NO == h->service_test_is_active)
+    {
+      if (NULL != h->cth)
+      {
+        GNUNET_CLIENT_notify_transmit_ready_cancel (h->cth);
+        h->cth = NULL;
+      }
+      if (NULL != h->client)
+      {
+        GNUNET_CLIENT_disconnect (h->client);
+        h->client = NULL;
+      }
+      if (GNUNET_SCHEDULER_NO_TASK != h->reconnect_task)
+      {
+        GNUNET_SCHEDULER_cancel (h->reconnect_task);
+        h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
+      }
+
       LOG (GNUNET_ERROR_TYPE_DEBUG,
-          "arm_api, GNUNET_CLIENT_connect returned NULL\n");
-      cb (cb_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR);
-      return;
+          "Not connected to ARM, will do a service test\n");
+
+      slen = strlen ("arm") + 1;
+      cm = GNUNET_malloc (sizeof (struct ARMControlMessage) + slen);
+      cm->h = h;
+      cm->result_cont = cont;
+      cm->cont_cls = cont_cls;
+      cm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
+      cm->std_inheritance = std_inheritance;
+      memcpy (&cm[1], service_name, slen);
+      h->service_test_is_active = GNUNET_YES;
+      GNUNET_CLIENT_service_test ("arm", h->cfg, timeout, &arm_service_report,
+                                 cm);
     }
-    LOG (GNUNET_ERROR_TYPE_DEBUG,
-        "arm_api, GNUNET_CLIENT_connect returned non-NULL\n");
-    h->client = client;
+    else
+    {
+      /* Service test is already running - tell user to chill out and try
+       * again later.
+       */
+      LOG (GNUNET_ERROR_TYPE_DEBUG, "Service test is already in progress, 
we're busy\n");
+      if (NULL != cont)
+        cont (cont_cls, h, GNUNET_ARM_REQUEST_BUSY, NULL, 0);
+    }
+    return;
   }
-  LOG (GNUNET_ERROR_TYPE_DEBUG, "arm_api, h->client non-NULL\n");
-  change_service (h, service_name, timeout, cb, cb_cls,
+  change_service (h, service_name, timeout, cont, cont_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.
+ * Request a service to be stopped.
+ * Stopping arm itself will not invalidate its handle, and
+ * ARM API will try to restore connection to the ARM service,
+ * even if ARM connection was lost because you asked for ARM to be stopped.
+ * Call GNUNET_ARM_disconnect () to free the handle and prevent
+ * further connection attempts.
  *
- * @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);
-}
-
-
-/**
- * Stop a 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
+ * @param cont callback to invoke after request is sent or is not sent
+ * @param cont_cls closure for callback
  */
 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)
+GNUNET_ARM_request_service_stop (struct GNUNET_ARM_Handle *h,
+    const char *service_name, struct GNUNET_TIME_Relative timeout,
+    GNUNET_ARM_ResultCallback cont, void *cont_cls)
 {
-  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)
-    {
-      cb (cb_cls, GNUNET_SYSERR);
-      return;
-    }
-    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,
+  change_service (h, service_name, timeout, cont, cont_cls,
                  GNUNET_MESSAGE_TYPE_ARM_STOP);
 }
 
 
 /**
- * Internal state for a list request with ARM.
+ * Request a list of running services.
+ *
+ * @param h handle to ARM
+ * @param timeout how long to wait before failing for good
+ * @param cont callback to invoke after request is sent or is not sent
+ * @param cont_cls closure for callback
  */
-struct ListRequestContext
+void
+GNUNET_ARM_request_service_list (struct GNUNET_ARM_Handle *h,
+    struct GNUNET_TIME_Relative timeout,
+    GNUNET_ARM_ServiceListCallback cont, void *cont_cls)
 {
+  struct ARMControlMessage *cm;
+  struct GNUNET_ARM_Message *msg;
 
-  /**
-   * Pointer to our handle with ARM.
-   */
-  struct GNUNET_ARM_Handle *h;
+  LOG (GNUNET_ERROR_TYPE_DEBUG, 
+       "Requesting LIST from ARM service with timeout: %s\n", 
+       GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_YES));
 
-  /**
-   * Function to call with a status code for the requested operation.
-   */
-  GNUNET_ARM_List_Callback callback;
+  cm = GNUNET_malloc (sizeof (struct ARMControlMessage));
+  cm->h = h;
+  cm->list_cont = cont;
+  cm->cont_cls = cont_cls;
+  cm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
+  msg = GNUNET_malloc (sizeof (struct GNUNET_ARM_Message));
+  msg->header.size = htons (sizeof (struct GNUNET_ARM_Message));
+  msg->header.type = htons (GNUNET_MESSAGE_TYPE_ARM_LIST);
+  cm->msg = msg;
+  GNUNET_CONTAINER_DLL_insert_tail (h->control_pending_head,
+                                    h->control_pending_tail, cm);
+  cm->timeout_task_id =
+      GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
+                                    (cm->timeout), &control_message_timeout, 
cm);
+  trigger_next_request (h, GNUNET_NO);
+}
 
-  /**
-   * Closure for "callback".
-   */
-  void *cls;
+static struct ARMControlMessage *
+find_cm_by_id (struct GNUNET_ARM_Handle *h, uint64_t id)
+{
+  struct ARMControlMessage *result;
+  for (result = h->control_sent_head; result; result = result->next)
+    if (id == result->msg->request_id)
+      return result;
+  return NULL;
+}
 
-  /**
-   * Timeout for the operation.
-   */
-  struct GNUNET_TIME_Absolute timeout;
-};
 
-
 /**
- * Process a response from ARM for the list request.
+ * Handler for ARM replies.
  *
- * @param cls the list request context
- * @param msg the response
+ * @param cls our "struct GNUNET_ARM_Handle"
+ * @param msg the message received from the arm service
  */
 static void
-handle_list_response (void *cls, const struct GNUNET_MessageHeader *msg)
+client_notify_handler (void *cls, const struct GNUNET_MessageHeader *msg)
 {
-  struct ListRequestContext *sc = cls;
-  const struct GNUNET_ARM_ListResultMessage *res;
+  struct GNUNET_ARM_Handle *h = cls;
+
+  uint16_t msize;
+  uint64_t id;
+  unsigned char fail;
+
+  const struct GNUNET_ARM_Message *arm_msg;
+  const struct GNUNET_ARM_ResultMessage *res;
+  const struct GNUNET_ARM_ListResultMessage *lres;
+  enum GNUNET_ARM_Result result;
+  struct ARMControlMessage *cm;
+
   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);
+    LOG (GNUNET_ERROR_TYPE_INFO,
+         _("Client was disconnected from arm service, trying to 
reconnect.\n"));
+    reconnect_arm_later (h);
     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) )
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Processing message of type %u and size %u from arm service\n",
+       ntohs (msg->type), msize);
+  if (msize < sizeof (struct GNUNET_ARM_Message))
   {
     GNUNET_break (0);
-    sc->callback (sc->cls, GNUNET_NO, 0, NULL);
-    GNUNET_free (sc);
+    reconnect_arm_later (h);
     return;
   }
-  size_check = 0;
-  res = (const struct GNUNET_ARM_ListResultMessage *) msg;
-  rcount = ntohs (res->count);
+  arm_msg = (const struct GNUNET_ARM_Message *) msg;
+  id = GNUNET_ntohll (arm_msg->request_id);
+  cm = find_cm_by_id (h, id);
+  if (NULL == cm)
   {
-    const char *list[rcount];
-    unsigned int i;
-    
-    pos = (const char *)&res[1];   
-    for (i=0; i<rcount; i++)
-    {
-      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;
-    }
-    sc->callback (sc->cls, GNUNET_YES, rcount, list);
+    LOG (GNUNET_ERROR_TYPE_DEBUG, "Message with unknown id %llu\n", id);
+    return;
   }
-  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)
+  fail = GNUNET_NO;
+  switch (ntohs (msg->type))
   {
-    client = GNUNET_CLIENT_connect ("arm", h->cfg);
-    if (client == NULL)
+  case GNUNET_MESSAGE_TYPE_ARM_RESULT:
+    if (msize < sizeof (struct GNUNET_ARM_ResultMessage))
     {
+      GNUNET_assert (0);
+      fail = GNUNET_YES;
+      break;
+    }
+    res = (const struct GNUNET_ARM_ResultMessage *) msg;
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Received response from ARM for service `%s': %u\n",
+         (const char *) &cm->msg[1], ntohs (msg->type));
+    result = (enum GNUNET_ARM_Result) ntohl (res->result);
+    if (NULL != cm->result_cont)
+      cm->result_cont (cm->cont_cls, h, GNUNET_ARM_REQUEST_SENT_OK, (const 
char *) &cm->msg[1], result);
+    break;
+  case GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT:
+    if (msize < sizeof (struct GNUNET_ARM_ListResultMessage))
+    {
       GNUNET_break (0);
-      cb (cb_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR, 0, NULL);
+      fail = GNUNET_YES;
       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);
+    else
+    {
+      size_check = 0;
+      lres = (const struct GNUNET_ARM_ListResultMessage *) msg;
+      rcount = ntohs (lres->count);
+      {
+        const char *list[rcount];
+        unsigned int i;
+
+        pos = (const char *)&lres[1];
+        for (i = 0; i < rcount; i++)
+        {
+          const char *end = memchr (pos, 0, msize - size_check);
+          if (NULL == end)
+          {
+            GNUNET_break (0);
+            fail = GNUNET_YES;
+            break;
+          }
+          list[i] = pos;
+          size_check += (end - pos) + 1;
+          pos = end + 1;
+        }
+        if (GNUNET_YES == fail)
+          break;
+        if (NULL != cm->list_cont)
+          cm->list_cont (cm->cont_cls, h, GNUNET_ARM_REQUEST_SENT_OK, rcount, 
list);
+      }
+    }
+    break;
+  default:
+    fail = GNUNET_YES;
     return;
   }
+
+  GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cm->timeout_task_id);
+  GNUNET_SCHEDULER_cancel (cm->timeout_task_id);
+  GNUNET_CONTAINER_DLL_remove (h->control_sent_head,
+                               h->control_sent_tail, cm);
+  GNUNET_free (cm->msg);
+  GNUNET_free (cm);
+
+  if (GNUNET_YES == fail)
+    reconnect_arm_later (h);
+  else
+    GNUNET_CLIENT_receive (h->client, &client_notify_handler, h,
+        GNUNET_TIME_UNIT_FOREVER_REL);
 }
 
 /* end of arm_api.c */

Modified: gnunet/src/arm/gnunet-arm.c
===================================================================
--- gnunet/src/arm/gnunet-arm.c 2013-03-13 17:49:17 UTC (rev 26401)
+++ gnunet/src/arm/gnunet-arm.c 2013-03-13 17:49:26 UTC (rev 26402)
@@ -111,6 +111,11 @@
 static struct GNUNET_ARM_Handle *h;
 
 /**
+ * Monitor connection with ARM.
+ */
+static struct GNUNET_ARM_MonitorHandle *m;
+
+/**
  * Our configuration.
  */
 static struct GNUNET_CONFIGURATION_Handle *cfg;
@@ -137,105 +142,6 @@
 
 
 /**
- * Main continuation-passing-style loop.  Runs the various
- * jobs that we've been asked to do in order.
- *
- * @param cls closure, unused
- * @param tc context, unused
- */
-static void
-cps_loop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
-
-
-/**
- * Callback invoked with the status of the last operation.  Reports to the
- * user and then runs the next phase in the FSM.
- *
- * @param cls pointer to "const char*" identifying service that was manipulated
- * @param result result of the operation
- */
-static void
-confirm_cb (void *cls, 
-           enum GNUNET_ARM_ProcessStatus result)
-{
-  const char *service = cls;
-
-  switch (result)
-  {
-  case GNUNET_ARM_PROCESS_UNKNOWN:
-    FPRINTF (stderr, _("Service `%s' is unknown to ARM.\n"), service);
-    ret = 1;
-    break;
-  case GNUNET_ARM_PROCESS_DOWN:
-    if (quiet != GNUNET_YES)
-      FPRINTF (stdout, _("Service `%s' has been stopped.\n"), service);
-    break;
-  case GNUNET_ARM_PROCESS_ALREADY_RUNNING:
-    FPRINTF (stderr, _("Service `%s' was already running.\n"), service);
-    ret = 1;
-    break;
-  case GNUNET_ARM_PROCESS_STARTING:
-    if (quiet != GNUNET_YES)
-      FPRINTF (stdout, _("Service `%s' has been started.\n"), service);
-    break;
-  case GNUNET_ARM_PROCESS_ALREADY_STOPPING:
-    FPRINTF (stderr, _("Service `%s' was already being stopped.\n"), service);
-    ret = 1;
-    break;
-  case GNUNET_ARM_PROCESS_ALREADY_DOWN:
-    FPRINTF (stderr, _("Service `%s' was already not running.\n"), service);
-    ret = 1;
-    break;
-  case GNUNET_ARM_PROCESS_SHUTDOWN:
-    FPRINTF (stderr, "%s", _("Request ignored as ARM is shutting down.\n"));
-    ret = 1;
-    break;
-  case GNUNET_ARM_PROCESS_COMMUNICATION_ERROR:
-    FPRINTF (stderr, "%s", _("Error communicating with ARM service.\n"));
-    ret = 1;
-    break;
-  case GNUNET_ARM_PROCESS_COMMUNICATION_TIMEOUT:
-    FPRINTF (stderr, "%s",  _("Timeout communicating with ARM service.\n"));
-    ret = 1;
-    break;
-  case GNUNET_ARM_PROCESS_FAILURE:
-    FPRINTF (stderr, "%s",  _("Operation failed.\n"));
-    ret = 1;
-    break;
-  default:
-    FPRINTF (stderr, "%s",  _("Unknown response code from ARM.\n"));
-    break;
-  }
-  GNUNET_SCHEDULER_add_now (&cps_loop, NULL);
-}
-
-
-/**
- * Callback invoked with the list of running services.  
- * Reports to the user and then runs the next phase in the FSM.
- *
- * @param cls currently not used
- * @param result result of the operation
- * @param count number of running services
- * @param list copy of the list of running services
- */
-static void
-list_cb (void *cls, int result, unsigned int count, const char *const*list)
-{
-  unsigned int i;
-
-  if ( (result != GNUNET_YES) || (NULL == list) )
-  {
-    FPRINTF (stderr, "%s", _("Error communicating with ARM. ARM not 
running?\n"));
-    return;
-  }
-  FPRINTF (stdout, "%s", _("Running services:\n"));
-  for (i=0; i<count; i++)
-    FPRINTF (stdout, "%s\n", list[i]);
-}
-
-
-/**
  * Attempts to delete configuration file and SERVICEHOME
  * on arm shutdown provided the end and delete options
  * were specified when gnunet-arm was run.
@@ -273,7 +179,9 @@
 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   GNUNET_ARM_disconnect (h);
+  GNUNET_ARM_monitor_disconnect (m);
   h = NULL;
+  m = NULL;
   if ((end == GNUNET_YES) && (delete == GNUNET_YES))
     delete_files ();   
   GNUNET_CONFIGURATION_destroy (cfg);
@@ -281,68 +189,248 @@
 }
 
 
+static const char *
+req_string (enum GNUNET_ARM_RequestStatus rs)
+{
+  switch (rs)
+  {
+  case GNUNET_ARM_REQUEST_SENT_OK:
+    return _("Message was sent successfully");
+  case GNUNET_ARM_REQUEST_CONFIGURATION_ERROR:
+    return _("Misconfiguration (can't connect to the ARM service)");
+  case GNUNET_ARM_REQUEST_DISCONNECTED:
+    return _("We disconnected from ARM before we could send a request");
+  case GNUNET_ARM_REQUEST_BUSY:
+    return _("ARM API is busy");
+  case GNUNET_ARM_REQUEST_TOO_LONG:
+    return _("Request doesn't fit into a message");
+  case GNUNET_ARM_REQUEST_TIMEOUT:
+    return _("Request timed out");
+  default:
+    return _("Unknown request status");
+  }
+}
+
+static const char *
+ret_string (enum GNUNET_ARM_Result result)
+{
+  switch (result)
+  {
+  case GNUNET_ARM_RESULT_STOPPED:
+    return _("%s is stopped");
+  case GNUNET_ARM_RESULT_STARTING:
+    return _("%s is starting");
+  case GNUNET_ARM_RESULT_IS_STARTING_ALREADY:
+    return _("%s is starting already");
+  case GNUNET_ARM_RESULT_IS_STOPPING_ALREADY:
+    return _("%s is stopping already");
+  case GNUNET_ARM_RESULT_IS_STARTED_ALREADY:
+    return _("%s is started already");
+  case GNUNET_ARM_RESULT_IS_STOPPED_ALREADY:
+    return _("%s is stopped already");
+  case GNUNET_ARM_RESULT_IS_NOT_KNOWN:
+    return _("%s service is not known to ARM");
+  case GNUNET_ARM_RESULT_START_FAILED:
+    return _("%s service failed to start");
+  case GNUNET_ARM_RESULT_IN_SHUTDOWN:
+    return _("%s service can't be started because ARM is shutting down");
+  default:
+    return _("Unknown result code.");
+  }
+}
+
+static void action_loop (void *cls, const struct GNUNET_SCHEDULER_TaskContext 
*tc);
+
 /**
- * Main function that will be run by the scheduler.
+ * Function called whenever we connect to or disconnect from ARM.
  *
  * @param cls closure
- * @param args remaining command-line arguments
- * @param cfgfile name of the configuration file used (for saving, can be 
NULL!)
- * @param c configuration
+ * @param connected GNUNET_YES if connected, GNUNET_NO if disconnected
+ * @param error GNUNET_YES if we encountered a permanent error, and there
+ *              will be no re-connection.
  */
+void
+conn_status (void *cls, struct GNUNET_ARM_Handle *arm, unsigned char 
connected, unsigned char error)
+{
+  if (GNUNET_YES == error)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+               _("Fatal error initializing ARM API.\n"));
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+/*
+  if (connected)
+    GNUNET_SCHEDULER_add_now (action_loop, NULL);
+*/
+}
+
+
 static void
-run (void *cls, char *const *args, const char *cfgfile,
-     const struct GNUNET_CONFIGURATION_Handle *c)
+term_callback (void *cls, struct GNUNET_ARM_Handle *arm,
+    enum GNUNET_ARM_RequestStatus rs, const char *service,
+    enum GNUNET_ARM_Result result)
 {
-  char *armconfig;
+  if (GNUNET_ARM_REQUEST_SENT_OK != rs)
+  {
+    char *msg;
+    GNUNET_asprintf (&msg, _("Failed to send a request to kill the `%s' 
service: %%s\n"), term);
+    FPRINTF (stdout, msg, req_string (rs));
+    GNUNET_free (msg);
+    GNUNET_SCHEDULER_shutdown ();
+  }
+  if ((GNUNET_ARM_RESULT_STOPPED == result) ||
+      (GNUNET_ARM_RESULT_IS_STOPPED_ALREADY == result))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service %s shutdown successful\n", 
term);
+    term = NULL;
+    GNUNET_SCHEDULER_add_now (action_loop, NULL);
+  }
+  else
+  {
+    char *msg;
+    GNUNET_asprintf (&msg, _("Failed to kill the `%s' service: %%s\n"), term);
+    FPRINTF (stdout, msg, ret_string (result));
+    GNUNET_free (msg);
+    GNUNET_SCHEDULER_shutdown ();
+  }
+}
 
-  cfg = GNUNET_CONFIGURATION_dup (c);
-  config_file = cfgfile;
-  if (GNUNET_CONFIGURATION_get_value_string
-      (cfg, "PATHS", "SERVICEHOME", &dir) != GNUNET_OK)
+static void
+end_callback (void *cls, struct GNUNET_ARM_Handle *arm,
+    enum GNUNET_ARM_RequestStatus rs, const char *service,
+    enum GNUNET_ARM_Result result)
+{
+  if (GNUNET_ARM_REQUEST_SENT_OK != rs)
   {
-    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                              "PATHS", "SERVICEHOME");
-    return;
-    }
-  if (NULL != cfgfile)
+    char *msg;
+    GNUNET_asprintf (&msg, "%s", _("Failed to send a stop request to the ARM 
service: %%s\n"));
+    FPRINTF (stdout, msg, req_string (rs));
+    GNUNET_free (msg);
+    GNUNET_SCHEDULER_shutdown ();
+  }
+  if ((GNUNET_ARM_RESULT_STOPPING == result) ||
+      (GNUNET_ARM_RESULT_STOPPED == result) ||
+      (GNUNET_ARM_RESULT_IS_STOPPED_ALREADY == result))
   {
-    if (GNUNET_OK !=
-        GNUNET_CONFIGURATION_get_value_filename (cfg, "arm", "CONFIG",
-                                                &armconfig))
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ARM service shutdown successful\n");
+    end = 0;
+    if (restart)
     {
-      GNUNET_CONFIGURATION_set_value_string (cfg, "arm", "CONFIG",
-                                             cfgfile);
+      restart = 0;
+      start = 1;
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Initiating an ARM restart\n");
     }
-    else
-      GNUNET_free (armconfig);
+    GNUNET_SCHEDULER_add_now (action_loop, NULL);
   }
-  if (NULL == (h = GNUNET_ARM_connect (cfg, NULL)))
+  else
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-               _("Fatal error initializing ARM API.\n"));
-    ret = 1;
-    GNUNET_CONFIGURATION_destroy (cfg);
-    cfg = NULL;
+    char *msg;
+    GNUNET_asprintf (&msg, "%s", _("Failed to stop the ARM service: %%s\n"));
+    FPRINTF (stdout, msg, ret_string (result));
+    GNUNET_free (msg);
+    GNUNET_SCHEDULER_shutdown ();
+  }
+}
+
+static void
+start_callback (void *cls, struct GNUNET_ARM_Handle *arm,
+    enum GNUNET_ARM_RequestStatus rs, const char *service,
+    enum GNUNET_ARM_Result result)
+{
+  if (GNUNET_ARM_REQUEST_SENT_OK != rs)
+  {
+    char *msg;
+    GNUNET_asprintf (&msg, "%s", _("Failed to start the ARM service: %%s\n"));
+    FPRINTF (stdout, msg, req_string (rs));
+    GNUNET_free (msg);
+    GNUNET_SCHEDULER_shutdown ();
+  }
+  if ((GNUNET_ARM_RESULT_STARTING == result) ||
+      (GNUNET_ARM_RESULT_IS_STARTED_ALREADY == result))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ARM service [re]start successful\n");
+    start = 0;
+    GNUNET_SCHEDULER_add_now (action_loop, NULL);
+  }
+  else
+  {
+    char *msg;
+    GNUNET_asprintf (&msg, "%s", _("Failed to start the ARM service: %%s\n"));
+    FPRINTF (stdout, msg, ret_string (result));
+    GNUNET_free (msg);
+    GNUNET_SCHEDULER_shutdown ();
+  }
+}
+
+
+static void
+init_callback (void *cls, struct GNUNET_ARM_Handle *arm,
+    enum GNUNET_ARM_RequestStatus rs, const char *service,
+    enum GNUNET_ARM_Result result)
+{
+  if (GNUNET_ARM_REQUEST_SENT_OK != rs)
+  {
+    char *msg;
+    GNUNET_asprintf (&msg, _("Failed to send a request to start the `%s' 
service: %%s\n"), init);
+    FPRINTF (stdout, msg, req_string (rs));
+    GNUNET_free (msg);
+    GNUNET_SCHEDULER_shutdown ();
+  }
+  if ((GNUNET_ARM_RESULT_STARTING == result) ||
+      (GNUNET_ARM_RESULT_IS_STARTED_ALREADY == result))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service %s [re]start successful\n", 
init);
+    init = NULL;
+    GNUNET_SCHEDULER_add_now (action_loop, NULL);
+  }
+  else
+  {
+    char *msg;
+    GNUNET_asprintf (&msg, _("Failed to start the `%s' service: %%s\n"), init);
+    FPRINTF (stdout, msg, ret_string (result));
+    GNUNET_free (msg);
+    GNUNET_SCHEDULER_shutdown ();
+  }
+}
+
+
+static void
+list_callback (void *cls, struct GNUNET_ARM_Handle *arm,
+    enum GNUNET_ARM_RequestStatus rs, unsigned int count,
+    const char *const*list)
+{
+  unsigned int i;
+  if (GNUNET_ARM_REQUEST_SENT_OK != rs)
+  {
+    char *msg;
+    GNUNET_asprintf (&msg, "%s", _("Failed to request a list of services: 
%%s\n"));
+    FPRINTF (stdout, msg, req_string (rs));
+    GNUNET_free (msg);
+    GNUNET_SCHEDULER_shutdown ();
+  }
+  if (NULL == list)
+  {
+    FPRINTF (stderr, "%s", _("Error communicating with ARM. ARM not 
running?\n"));
     return;
   }
-  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
-                               &shutdown_task, NULL);
-  GNUNET_SCHEDULER_add_now (&cps_loop, NULL);
+  FPRINTF (stdout, "%s", _("Running services:\n"));
+  for (i = 0; i < count; i++)
+    FPRINTF (stdout, "%s\n", list[i]);
 }
 
 
 /**
- * Main continuation-passing-style loop.  Runs the various
+ * Main action loop.  Runs the various
  * jobs that we've been asked to do in order.
  *
  * @param cls closure, unused
  * @param tc context, unused
  */
 static void
-cps_loop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+action_loop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
-  if (NULL == h)
-    return;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Running requested actions\n");
   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
     return;
   while (1)
@@ -352,82 +440,54 @@
     case 0:
       if (NULL != term)
       {
-       GNUNET_ARM_stop_service (h, term,
-                                (0 ==
-                                 timeout.rel_value) ? STOP_TIMEOUT :
-                                timeout, &confirm_cb, term);
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Termination action\n");
+        GNUNET_ARM_request_service_stop (h, term, (0 ==
+            timeout.rel_value) ? STOP_TIMEOUT : timeout,
+            term_callback, NULL);
        return;
       }
       break;
     case 1:
-      if ((end) || (restart))
+      if (end || restart)
       {
-       GNUNET_ARM_stop_service (h, "arm",
-                                (0 ==
-                                 timeout.rel_value) ? STOP_TIMEOUT_ARM
-                                : timeout, &confirm_cb, "arm");
-       return;
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "End action\n");
+        GNUNET_ARM_request_service_stop (h, "arm", (0 ==
+            timeout.rel_value) ? STOP_TIMEOUT_ARM : timeout,
+            end_callback, NULL);
+        return;
       }
       break;
     case 2:
       if (start)
       {
-       GNUNET_ARM_start_service (h, "arm",
-                                 (no_stdout ? 0 : GNUNET_OS_INHERIT_STD_OUT) |
-                                 (no_stderr ? 0 : GNUNET_OS_INHERIT_STD_ERR),
-                                 (0 ==
-                                  timeout.rel_value) ? START_TIMEOUT :
-                                 timeout, &confirm_cb, "arm");
-       return;
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Start action\n");
+        GNUNET_ARM_request_service_start (h, "arm",
+            (no_stdout ? 0 : GNUNET_OS_INHERIT_STD_OUT) |
+            (no_stderr ? 0 : GNUNET_OS_INHERIT_STD_ERR),
+            (0 == timeout.rel_value) ? START_TIMEOUT: timeout,
+            start_callback, NULL);
+        return;
       }
       break;
     case 3:
       if (NULL != init)
-       {
-        GNUNET_ARM_start_service (h, init,
-                                  (no_stdout ? 0 : GNUNET_OS_INHERIT_STD_OUT) |
-                                  (no_stderr ? 0 : GNUNET_OS_INHERIT_STD_ERR),
-                                  (0 ==
-                                   timeout.rel_value) ? START_TIMEOUT :
-                                  timeout, &confirm_cb, init);
-        return;
-       }
-      break;
-    case 4:
-      if (restart)
       {
-       GNUNET_ARM_disconnect (h);
-       phase = 0;
-       end = 0;
-       start = 1;
-       restart = 0;
-       if (NULL == (h = GNUNET_ARM_connect (cfg, NULL)))
-       {
-         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                     _("Fatal error initializing ARM API.\n"));
-         ret = 1;
-         return;
-       }
-       GNUNET_SCHEDULER_add_now (&cps_loop, NULL);
-       return;
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Initialization action\n");
+        GNUNET_ARM_request_service_start (h, init, GNUNET_OS_INHERIT_STD_NONE,
+            (0 == timeout.rel_value) ? STOP_TIMEOUT : timeout,
+            init_callback, NULL);
+        return;
       }
       break;
-    case 5:
+    case 4:
       if (list) 
       {
        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                    "Going to list all running services controlled by ARM.\n");
        
-       if (NULL == h) 
-       {
-         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                     _("Fatal error initializing ARM API.\n"));
-         return;
-       }       
-       GNUNET_ARM_list_running_services (h, 
-                                         (0 == 
-                                          timeout.rel_value) ? LIST_TIMEOUT : 
-                                         timeout, &list_cb, NULL);
+        GNUNET_ARM_request_service_list (h,
+            (0 == timeout.rel_value) ? LIST_TIMEOUT : timeout,
+            list_callback, &list);
        return;
       }
       /* Fall through */
@@ -440,6 +500,89 @@
 
 
 /**
+ * Function called when a service starts or stops.
+ *
+ * @param cls closure
+ * @param service service name
+ * @param status status of the service
+ */
+static void
+srv_status (void *cls, struct GNUNET_ARM_MonitorHandle *arm,
+    const char *service, enum GNUNET_ARM_ServiceStatus status)
+{
+  const char *msg;
+  switch (status)
+  {
+  case GNUNET_ARM_SERVICE_MONITORING_STARTED:
+    msg = _("Began monitoring ARM for service status changes\n");
+    break;
+  case GNUNET_ARM_SERVICE_STOPPED:
+    msg = _("Stopped %s.\n");
+    break;
+  case GNUNET_ARM_SERVICE_STARTING:
+    msg = _("Starting %s...\n");
+    break;
+  case GNUNET_ARM_SERVICE_STOPPING:
+    msg = _("Stopping %s...\n");
+    break;
+  default:
+    msg = NULL;
+    break;
+  }
+  if (NULL != msg)
+    FPRINTF (stderr, msg, service);
+  else
+    FPRINTF (stderr, _("Unknown status %u for service %s.\n"), status, 
service);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got service %s status %u\n", service, 
status);
+}
+
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be 
NULL!)
+ * @param c configuration
+ */
+static void
+run (void *cls, char *const *args, const char *cfgfile,
+     const struct GNUNET_CONFIGURATION_Handle *c)
+{
+  char *armconfig;
+
+  cfg = GNUNET_CONFIGURATION_dup (c);
+  config_file = cfgfile;
+  if (GNUNET_CONFIGURATION_get_value_string
+      (cfg, "PATHS", "SERVICEHOME", &dir) != GNUNET_OK)
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                              "PATHS", "SERVICEHOME");
+    return;
+    }
+  if (NULL != cfgfile)
+  {
+    if (GNUNET_OK !=
+        GNUNET_CONFIGURATION_get_value_filename (cfg, "arm", "CONFIG",
+                                                &armconfig))
+    {
+      GNUNET_CONFIGURATION_set_value_string (cfg, "arm", "CONFIG",
+                                             cfgfile);
+    }
+    else
+      GNUNET_free (armconfig);
+  }
+  h = GNUNET_ARM_alloc (cfg);
+  m = GNUNET_ARM_monitor_alloc (cfg);
+  GNUNET_ARM_connect (h, conn_status, NULL);
+  GNUNET_ARM_monitor (m, srv_status, NULL);
+  GNUNET_SCHEDULER_add_now (action_loop, NULL);
+  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
+                               shutdown_task, NULL);
+}
+
+
+/**
  * The main function to obtain arm from gnunetd.
  *
  * @param argc number of arguments from the command line

Modified: gnunet/src/arm/gnunet-service-arm.c
===================================================================
--- gnunet/src/arm/gnunet-service-arm.c 2013-03-13 17:49:17 UTC (rev 26401)
+++ gnunet/src/arm/gnunet-service-arm.c 2013-03-13 17:49:26 UTC (rev 26402)
@@ -30,6 +30,13 @@
 #include "arm.h"
 
 /**
+ * How many messages do we queue up at most for optional
+ * notifications to a client?  (this can cause notifications
+ * about outgoing messages to be dropped).
+ */
+#define MAX_NOTIFY_QUEUE 1024
+
+/**
  * List of our services.
  */
 struct ServiceList;
@@ -125,6 +132,11 @@
   struct GNUNET_SERVER_Client *killing_client;
 
   /**
+   * ID of the request that killed the service (for reporting back).
+   */
+  uint64_t killing_client_request_id;
+
+  /**
    * Process structure pointer of the child.
    */
   struct GNUNET_OS_Process *proc;
@@ -217,17 +229,154 @@
  */
 static struct GNUNET_SERVER_Handle *server;
 
+/**
+ * Context for notifications we need to send to our clients.
+ */
+static struct GNUNET_SERVER_NotificationContext *notifier;
 
+
 #include "do_start_process.c"
 
+/**
+ * Transmit a status result message.
+ *
+ * @param cls pointer to "unit16_t*" with message type
+ * @param size number of bytes available in buf
+ * @param buf where to copy the message, NULL on error
+ * @return number of bytes copied to buf
+ */
+static size_t
+write_result (void *cls, size_t size, void *buf)
+{
+  struct GNUNET_ARM_ResultMessage *msg = cls;
+  size_t msize;
 
+  if (buf == NULL)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+               _("Could not send status result to client\n"));
+    GNUNET_free (msg);
+    return 0;                  /* error, not much we can do */
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Sending status response %u to client\n", (unsigned int) 
msg->result);
+  msize = msg->arm_msg.header.size;
+  GNUNET_assert (size >= msize);
+  msg->arm_msg.header.size = htons (msg->arm_msg.header.size);
+  msg->arm_msg.header.type = htons (msg->arm_msg.header.type);
+  msg->result = htonl (msg->result);
+  msg->arm_msg.request_id = GNUNET_htonll (msg->arm_msg.request_id);
+  memcpy (buf, msg, msize);
+  GNUNET_free (msg);
+  return msize;
+}
+
 /**
+ * Transmit the list of running services.
+ * 
+ * @param cls pointer to struct GNUNET_ARM_ListResultMessage with the message
+ * @param size number of bytes available in buf
+ * @param buf where to copy the message, NULL on error
+ * @return number of bytes copied to buf
+ */
+static size_t
+write_list_result (void *cls, size_t size, void *buf)
+{
+  struct GNUNET_ARM_ListResultMessage *msg = cls;
+  size_t rslt_size;
+  
+  if (buf == NULL)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                _("Could not send list result to client\n"));
+    GNUNET_free (msg);
+    return 0;                   /* error, not much we can do */
+  }
+  
+  rslt_size = msg->arm_msg.header.size;
+  GNUNET_assert (size >= rslt_size);
+  msg->arm_msg.header.size = htons (msg->arm_msg.header.size);
+  msg->arm_msg.header.type = htons (msg->arm_msg.header.type);
+  msg->arm_msg.request_id = GNUNET_htonll (msg->arm_msg.request_id);
+  msg->count = htons (msg->count);
+  
+  memcpy (buf, msg, rslt_size);
+  GNUNET_free (msg);
+  return rslt_size;
+}
+
+
+/**
+ * Signal our client that we will start or stop the
+ * service.
+ *
+ * @param client who is being signalled
+ * @param name name of the service
+ * @param result message type to send
+ * @return NULL if it was not found
+ */
+static void
+signal_result (struct GNUNET_SERVER_Client *client, const char *name,
+              uint64_t request_id, enum GNUNET_ARM_Result result)
+{
+  struct GNUNET_ARM_ResultMessage *msg;
+  size_t msize;
+
+  msize = sizeof (struct GNUNET_ARM_ResultMessage);
+  msg = GNUNET_malloc (msize);
+  msg->arm_msg.header.size = msize;
+  msg->arm_msg.header.type = GNUNET_MESSAGE_TYPE_ARM_RESULT;
+  msg->result = result;
+  msg->arm_msg.request_id = request_id;
+
+  GNUNET_SERVER_notify_transmit_ready (client, msize,
+      GNUNET_TIME_UNIT_FOREVER_REL, write_result, msg);
+}
+
+
+/**
+ * Tell all clients about status change of a service.
+ *
+ * @param name name of the service
+ * @param status message type to send
+ */
+static void
+broadcast_status (const char *name, enum GNUNET_ARM_ServiceStatus status,
+    struct GNUNET_SERVER_Client *unicast)
+{
+  struct GNUNET_ARM_StatusMessage *msg;
+  size_t namelen;
+
+  if (NULL == notifier)
+    return;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+      "Sending status %u of service `%s' to client\n",
+      (unsigned int) status, name);
+  namelen = strlen (name);
+  msg = GNUNET_malloc (sizeof (struct GNUNET_ARM_StatusMessage) + namelen + 1);
+  msg->header.size = htons (sizeof (struct GNUNET_ARM_StatusMessage) + namelen 
+ 1);
+  msg->header.type = htons (GNUNET_MESSAGE_TYPE_ARM_STATUS);
+  msg->status = htonl ((uint32_t) (status));
+  memcpy ((char *) &msg[1], name, namelen + 1);
+
+  if (NULL == unicast)
+    GNUNET_SERVER_notification_context_broadcast (notifier,
+        (struct GNUNET_MessageHeader *) msg, GNUNET_YES);
+  else
+    GNUNET_SERVER_notification_context_unicast (notifier, unicast,
+        (const struct GNUNET_MessageHeader *) msg, GNUNET_NO);
+  GNUNET_free (msg);
+}
+
+
+/**
  * Actually start the process for the given service.
  *
  * @param sl identifies service to start
+ * @param client that asked to start the service (may be NULL)
  */
 static void
-start_process (struct ServiceList *sl)
+start_process (struct ServiceList *sl, struct GNUNET_SERVER_Client *client, 
uint64_t request_id)
 {
   char *loprefix;
   char *options;
@@ -342,11 +491,20 @@
   }
   GNUNET_free (binary);
   if (sl->proc == NULL)
+  {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to start service `%s'\n"),
                sl->name);
+    if (client)
+      signal_result (client, sl->name, request_id, 
GNUNET_ARM_RESULT_START_FAILED);
+  }
   else
+  {
     GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Starting service `%s'\n"),
                sl->name);
+    broadcast_status (sl->name, GNUNET_ARM_SERVICE_STARTING, NULL);
+    if (client)
+      signal_result (client, sl->name, request_id, GNUNET_ARM_RESULT_STARTING);
+  }
   /* clean up */
   GNUNET_free (loprefix);
   GNUNET_free (options);
@@ -355,104 +513,6 @@
 
 
 /**
- * Transmit a status result message.
- *
- * @param cls pointer to "unit16_t*" with message type
- * @param size number of bytes available in buf
- * @param buf where to copy the message, NULL on error
- * @return number of bytes copied to buf
- */
-static size_t
-write_result (void *cls, size_t size, void *buf)
-{
-  enum GNUNET_ARM_ProcessStatus *res = cls;
-  struct GNUNET_ARM_ResultMessage *msg;
-
-  if (buf == NULL)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-               _("Could not send status result to client\n"));
-    return 0;                  /* error, not much we can do */
-  }
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Sending status response %u to client\n", (unsigned int) *res);
-  GNUNET_assert (size >= sizeof (struct GNUNET_ARM_ResultMessage));
-  msg = buf;
-  msg->header.size = htons (sizeof (struct GNUNET_ARM_ResultMessage));
-  msg->header.type = htons (GNUNET_MESSAGE_TYPE_ARM_RESULT);
-  msg->status = htonl ((uint32_t) (*res));
-  GNUNET_free (res);
-  return sizeof (struct GNUNET_ARM_ResultMessage);
-}
-
-/**
- * Transmit the list of running services.
- * 
- * @param cls pointer to struct GNUNET_ARM_ListResultMessage with the message
- * @param size number of bytes available in buf
- * @param buf where to copy the message, NULL on error
- * @return number of bytes copied to buf
- */
-static size_t
-write_list_result (void *cls, size_t size, void *buf)
-{
-  struct GNUNET_ARM_ListResultMessage *msg = cls;
-  struct GNUNET_ARM_ListResultMessage *rslt;
-  size_t rslt_size;
-  
-  if (buf == NULL)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                _("Could not send list result to client\n"));
-    return 0;                   /* error, not much we can do */
-  }
-  
-  GNUNET_assert (size >= msg->header.size);
-  rslt = buf;
-  rslt->header.size = htons (msg->header.size);
-  rslt->header.type = htons (msg->header.type);
-  rslt->count = htons (msg->count);
-  
-  size_t list_size = msg->header.size 
-                     - sizeof (struct GNUNET_ARM_ListResultMessage);  
-  memcpy (&rslt[1], &msg[1], list_size);
-
-  rslt_size = msg->header.size;
-  GNUNET_free (msg);
-  return rslt_size;
-}
-
-
-/**
- * Signal our client that we will start or stop the
- * service.
- *
- * @param client who is being signalled
- * @param name name of the service
- * @param result message type to send
- * @return NULL if it was not found
- */
-static void
-signal_result (struct GNUNET_SERVER_Client *client, const char *name,
-              enum GNUNET_ARM_ProcessStatus result)
-{
-  enum GNUNET_ARM_ProcessStatus *res;
-
-  if (NULL == client)
-    return;
-  /* FIXME: this is not super-clean yet... */
-  res = GNUNET_malloc (sizeof (enum GNUNET_ARM_ProcessStatus));
-  *res = result;
-  GNUNET_SERVER_notify_transmit_ready (client,
-                                      sizeof (struct
-                                              GNUNET_ARM_ResultMessage),
-                                      GNUNET_TIME_UNIT_FOREVER_REL,
-                                      &write_result, res);
-  GNUNET_SERVER_receive_done (client, GNUNET_OK);
-}
-
-
-/**
  * Find the process with the given service
  * name in the given list and return it.
  *
@@ -492,7 +552,7 @@
   GNUNET_assert (GNUNET_NO == in_shutdown);
   if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
     return;
-  start_process (sl);
+  start_process (sl, NULL, 0);
 }
 
 
@@ -619,10 +679,14 @@
   const char *servicename;
   struct ServiceList *sl;
   uint16_t size;
-  
-  size = ntohs (message->size);
-  size -= sizeof (struct GNUNET_MessageHeader);
-  servicename = (const char *) &message[1];
+  uint64_t request_id;
+  struct GNUNET_ARM_Message *amsg;
+
+  amsg = (struct GNUNET_ARM_Message *) message;  
+  request_id = GNUNET_ntohll (amsg->request_id);
+  size = ntohs (amsg->header.size);
+  size -= sizeof (struct GNUNET_ARM_Message);
+  servicename = (const char *) &amsg[1];
   if ((size == 0) || (servicename[size - 1] != '\0'))
     {
       GNUNET_break (0);
@@ -631,27 +695,44 @@
     }
   if (GNUNET_YES == in_shutdown)
     {
-      signal_result (client, servicename, GNUNET_ARM_PROCESS_SHUTDOWN);
+      signal_result (client, servicename, request_id, 
GNUNET_ARM_RESULT_IN_SHUTDOWN);
+      GNUNET_SERVER_receive_done (client, GNUNET_OK);
       return;
     }
   sl = find_service (servicename);
   if (NULL == sl)
     {
-      signal_result (client, servicename, GNUNET_ARM_PROCESS_UNKNOWN);
+      signal_result (client, servicename, request_id, 
GNUNET_ARM_RESULT_IS_NOT_KNOWN);
+      GNUNET_SERVER_receive_done (client, GNUNET_OK);
       return;
     }
   sl->is_default = GNUNET_YES;
   if (sl->proc != NULL)
     {
-      signal_result (client, servicename, GNUNET_ARM_PROCESS_ALREADY_RUNNING);
+      signal_result (client, servicename, request_id, 
GNUNET_ARM_RESULT_IS_STARTED_ALREADY);
+      GNUNET_SERVER_receive_done (client, GNUNET_OK);
       return;
     }
-  start_process (sl);
-  signal_result (client, servicename, GNUNET_ARM_PROCESS_STARTING);
+  start_process (sl, client, request_id);
+  GNUNET_SERVER_receive_done (client, GNUNET_OK);
 }
 
 
 /**
+ * Start a shutdown sequence.
+ *
+ * @param cls closure (refers to service)
+ * @param tc task context
+ */
+static void
+trigger_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Triggering shutdown\n");
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+
+/**
  * Handle STOP-message.
  *
  * @param cls closure (always NULL)
@@ -667,10 +748,14 @@
   struct ServiceList *sl;
   const char *servicename;
   uint16_t size;
+  uint64_t request_id;
+  struct GNUNET_ARM_Message *amsg;
 
-  size = ntohs (message->size);
-  size -= sizeof (struct GNUNET_MessageHeader);
-  servicename = (const char *) &message[1];
+  amsg = (struct GNUNET_ARM_Message *) message;  
+  request_id = GNUNET_ntohll (amsg->request_id);
+  size = ntohs (amsg->header.size);
+  size -= sizeof (struct GNUNET_ARM_Message);
+  servicename = (const char *) &amsg[1];
   if ((size == 0) || (servicename[size - 1] != '\0'))
     {
       GNUNET_break (0);
@@ -679,40 +764,58 @@
     }
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
              _("Preparing to stop `%s'\n"), servicename);
+  if (0 == strcasecmp (servicename, "arm"))
+  {
+    broadcast_status (servicename, GNUNET_ARM_SERVICE_STOPPING, NULL);
+    signal_result (client, servicename, request_id, 
GNUNET_ARM_RESULT_STOPPING);
+    GNUNET_SERVER_client_persist_ (client);
+    GNUNET_SCHEDULER_add_now (trigger_shutdown, NULL);
+    GNUNET_SERVER_receive_done (client, GNUNET_OK);
+    return;
+  }
   sl = find_service (servicename);
   if (sl == NULL)
     {
-      signal_result (client, servicename, GNUNET_ARM_PROCESS_UNKNOWN);
+      signal_result (client, servicename, request_id, 
GNUNET_ARM_RESULT_IS_NOT_KNOWN);
+      GNUNET_SERVER_receive_done (client, GNUNET_OK);
       return;
     }
   sl->is_default = GNUNET_NO;
   if (GNUNET_YES == in_shutdown)
     {
       /* shutdown in progress */
-      signal_result (client, servicename, GNUNET_ARM_PROCESS_SHUTDOWN);
+      signal_result (client, servicename, request_id, 
GNUNET_ARM_RESULT_IN_SHUTDOWN);
+      GNUNET_SERVER_receive_done (client, GNUNET_OK);
       return;
     }
   if (sl->killing_client != NULL)
     {
       /* killing already in progress */
-      signal_result (client, servicename,
-                    GNUNET_ARM_PROCESS_ALREADY_STOPPING);
+      signal_result (client, servicename, request_id,
+          GNUNET_ARM_RESULT_IS_STOPPING_ALREADY);
+      GNUNET_SERVER_receive_done (client, GNUNET_OK);
       return;
     }
   if (sl->proc == NULL)
     {
       /* process is down */
-      signal_result (client, servicename, GNUNET_ARM_PROCESS_ALREADY_DOWN);
+      signal_result (client, servicename, request_id,
+          GNUNET_ARM_RESULT_IS_STOPPED_ALREADY);
+      GNUNET_SERVER_receive_done (client, GNUNET_OK);
       return;
     }
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Sending kill signal to service `%s', waiting for process to 
die.\n",
              servicename);
+  broadcast_status (servicename, GNUNET_ARM_SERVICE_STOPPING, NULL);
+  /* no signal_start - only when it's STOPPED */
   sl->killed_at = GNUNET_TIME_absolute_get ();
   if (0 != GNUNET_OS_process_kill (sl->proc, SIGTERM))
     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
   sl->killing_client = client;
+  sl->killing_client_request_id = request_id;
   GNUNET_SERVER_client_keep (client);
+  GNUNET_SERVER_receive_done (client, GNUNET_YES);
 }
 
 /**
@@ -727,6 +830,7 @@
              const struct GNUNET_MessageHeader *message)
 {
   struct GNUNET_ARM_ListResultMessage *msg;
+  struct GNUNET_ARM_Message *request;
   size_t string_list_size;
   size_t total_size;
   struct ServiceList *sl;
@@ -735,6 +839,7 @@
   if (NULL == client)
     return;
   
+  request = (struct GNUNET_ARM_Message *) message;
   count = 0;
   string_list_size = 0;
   /* first count the running processes get their name's size */
@@ -748,11 +853,13 @@
       count++;
     }
   }
+
   total_size = sizeof (struct GNUNET_ARM_ListResultMessage) 
                + string_list_size;
   msg = GNUNET_malloc (total_size);
-  msg->header.size = total_size;
-  msg->header.type = GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT;
+  msg->arm_msg.header.size = total_size;
+  msg->arm_msg.header.type = GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT;
+  msg->arm_msg.request_id = GNUNET_ntohll (request->request_id);
   msg->count = count;
   
   char *pos = (char *)&msg[1];
@@ -767,9 +874,9 @@
   }
   
   GNUNET_SERVER_notify_transmit_ready (client,
-                                       msg->header.size,
+                                       total_size,
                                        GNUNET_TIME_UNIT_FOREVER_REL,
-                                       &write_list_result, msg);
+                                       write_list_result, msg);
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
 }
 
@@ -780,6 +887,12 @@
 static void
 do_shutdown ()
 {
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Last shutdown phase\n");
+  if (NULL != notifier)
+  {
+    GNUNET_SERVER_notification_context_destroy (notifier);
+    notifier = NULL;
+  }
   if (NULL != server)
     {
       GNUNET_SERVER_destroy (server);
@@ -792,6 +905,15 @@
     }
 }
 
+unsigned int
+list_count (struct ServiceList *running_head)
+{
+  struct ServiceList *i;
+  unsigned int res = 0;
+  for (res = 0, i = running_head; i; i = i->next, res++)
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%s\n", i->name);
+  return res;
+}
 
 /**
  * Task run for shutdown.
@@ -806,6 +928,7 @@
   struct ServiceList *nxt;
   struct ServiceListeningInfo *sli;
 
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "First shutdown phase\n");
   if (GNUNET_SCHEDULER_NO_TASK != child_restart_task)
   {
     GNUNET_SCHEDULER_cancel (child_restart_task);
@@ -851,6 +974,9 @@
   /* finally, should all service processes be already gone, terminate for real 
*/
   if (running_head == NULL)
     do_shutdown ();
+  else
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+        "Delaying shutdown, have %u childs still running\n", list_count 
(running_head));
 }
 
 
@@ -890,7 +1016,7 @@
        /* process should run by default, start immediately */
        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                    _("Restarting service `%s'.\n"), sl->name);
-       start_process (sl);
+       start_process (sl, NULL, 0);
       }
       else
       {
@@ -1004,74 +1130,63 @@
       }
       GNUNET_OS_process_destroy (pos->proc);
       pos->proc = NULL;
+      broadcast_status (pos->name, GNUNET_ARM_SERVICE_STOPPED, NULL);
       if (NULL != pos->killing_client)
-       {
-         signal_result (pos->killing_client, pos->name,
-                        GNUNET_ARM_PROCESS_DOWN);
-         GNUNET_SERVER_client_drop (pos->killing_client);
-         pos->killing_client = NULL;
-         /* process can still be re-started on-demand, ensure it is re-started 
if there is demand */
-         for (sli = pos->listen_head; NULL != sli; sli = sli->next)
-           {
-             GNUNET_break (GNUNET_SCHEDULER_NO_TASK == sli->accept_task);
-             sli->accept_task =
-               GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                              sli->listen_socket,
-                                              &accept_connection, sli);
-           }
-       }
+      {
+        signal_result (pos->killing_client, pos->name,
+            pos->killing_client_request_id, GNUNET_ARM_RESULT_STOPPED);
+        GNUNET_SERVER_client_drop (pos->killing_client);
+        pos->killing_client = NULL;
+        pos->killing_client_request_id = 0;
+        /* process can still be re-started on-demand, ensure it is re-started 
if there is demand */
+        for (sli = pos->listen_head; NULL != sli; sli = sli->next)
+        {
+          GNUNET_break (GNUNET_SCHEDULER_NO_TASK == sli->accept_task);
+          sli->accept_task =
+              GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                  sli->listen_socket, &accept_connection, sli);
+        }
+      }
       if (GNUNET_YES != in_shutdown)
-       {
-         if ((statusType == GNUNET_OS_PROCESS_EXITED) && (statcode == 0))
-           {
-             /* process terminated normally, allow restart at any time */
-             pos->restart_at.abs_value = 0;
-           }
-          else
-            {
-             if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
-               GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                           _
-                           ("Service `%s' terminated with status %s/%d, will 
restart in %s\n"),
-                           pos->name, statstr, statcode, 
-                           GNUNET_STRINGS_relative_time_to_string 
(pos->backoff, GNUNET_YES));
-             /* schedule restart */
-             pos->restart_at = GNUNET_TIME_relative_to_absolute (pos->backoff);
-             pos->backoff = GNUNET_TIME_STD_BACKOFF (pos->backoff);
-            }
-         if (GNUNET_SCHEDULER_NO_TASK != child_restart_task)
-           GNUNET_SCHEDULER_cancel (child_restart_task);
-         child_restart_task =
-           GNUNET_SCHEDULER_add_with_priority
-           (GNUNET_SCHEDULER_PRIORITY_IDLE, 
-            &delayed_restart_task, NULL);
+      {
+        if ((statusType == GNUNET_OS_PROCESS_EXITED) && (statcode == 0))
+        {
+          /* process terminated normally, allow restart at any time */
+          pos->restart_at.abs_value = 0;
+          GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              _("Service `%s' terminated normally, will restart at any 
time\n"),
+              pos->name);
        }
+        else
+        {
+         if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+               _("Service `%s' terminated with status %s/%d, will restart in 
%s\n"),
+                pos->name, statstr, statcode,
+                GNUNET_STRINGS_relative_time_to_string (pos->backoff, 
GNUNET_YES));
+         /* schedule restart */
+         pos->restart_at = GNUNET_TIME_relative_to_absolute (pos->backoff);
+         pos->backoff = GNUNET_TIME_STD_BACKOFF (pos->backoff);
+        }
+       if (GNUNET_SCHEDULER_NO_TASK != child_restart_task)
+         GNUNET_SCHEDULER_cancel (child_restart_task);
+       child_restart_task = GNUNET_SCHEDULER_add_with_priority (
+            GNUNET_SCHEDULER_PRIORITY_IDLE, &delayed_restart_task, NULL);
+      }
       else
-       {
-         free_service (pos);
-       }
+      {
+        free_service (pos);
+      }
     }
-  child_death_task =
-    GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
-                                   pr, &maint_child_death, NULL);
+  child_death_task = GNUNET_SCHEDULER_add_read_file (
+      GNUNET_TIME_UNIT_FOREVER_REL, pr, &maint_child_death, NULL);
   if ((NULL == running_head) && (GNUNET_YES == in_shutdown))
     do_shutdown ();
-}
+  else if (GNUNET_YES == in_shutdown)
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+        "Delaying shutdown after child's death, still have %u children\n",
+        list_count (running_head));
 
-
-/**
- * Handler for SHUTDOWN message.
- *
- * @param cls closure (refers to service)
- * @param client identification of the client
- * @param message the actual message
- */
-static void
-handle_shutdown (void *cls, struct GNUNET_SERVER_Client *client,
-                const struct GNUNET_MessageHeader *message)
-{
-  GNUNET_SCHEDULER_shutdown ();
-  GNUNET_SERVER_client_persist_ (client);
 }
 
 
@@ -1174,6 +1289,43 @@
 
 
 /**
+ * A client connected, add it to the notification context.
+ *
+ * @param cls closure
+ * @param client identification of the client
+ */
+static void
+handle_client_connecting (void *cls, struct GNUNET_SERVER_Client *client)
+{
+  /* All clients are considered to be of the "monitor" kind
+   * (that is, they don't affect ARM shutdown).
+   */
+  if (NULL != client)
+    GNUNET_SERVER_client_mark_monitor (client);
+}
+
+/**
+ * Handle MONITOR-message.
+ *
+ * @param cls closure (always NULL)
+ * @param client identification of the client
+ * @param message the actual message
+ * @return GNUNET_OK to keep the connection open,
+ *         GNUNET_SYSERR to close it (signal serious error)
+ */
+static void
+handle_monitor (void *cls, struct GNUNET_SERVER_Client *client,
+            const struct GNUNET_MessageHeader *message)
+{
+  /* Removal is handled by the server implementation, internally. */
+  if ((NULL != client) && (NULL != notifier))
+  {
+    GNUNET_SERVER_notification_context_add (notifier, client);
+    broadcast_status ("arm", GNUNET_ARM_SERVICE_MONITORING_STARTED, client);
+  }
+}
+
+/**
  * Process arm requests.
  *
  * @param cls closure
@@ -1187,8 +1339,7 @@
   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
     {&handle_start, NULL, GNUNET_MESSAGE_TYPE_ARM_START, 0},
     {&handle_stop, NULL, GNUNET_MESSAGE_TYPE_ARM_STOP, 0},
-    {&handle_shutdown, NULL, GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN,
-     sizeof (struct GNUNET_MessageHeader)},
+    {&handle_monitor, NULL, GNUNET_MESSAGE_TYPE_ARM_MONITOR, 0},
     {&handle_list, NULL, GNUNET_MESSAGE_TYPE_ARM_LIST, 
      sizeof (struct GNUNET_MessageHeader)},
     {NULL, NULL, 0, 0}
@@ -1241,7 +1392,7 @@
                  continue;
                }
              sl->is_default = GNUNET_YES;
-             start_process (sl);
+             start_process (sl, NULL, 0);
            }
        }
       GNUNET_free (defaultservices);
@@ -1253,6 +1404,9 @@
                  ("No default services configured, GNUnet will not really 
start right now.\n"));
     }
 
+  notifier =
+      GNUNET_SERVER_notification_context_create (server, MAX_NOTIFY_QUEUE);
+  GNUNET_SERVER_connect_notify (server, handle_client_connecting, NULL);
   /* process client requests */
   GNUNET_SERVER_add_handlers (server, handlers);
 }

Modified: gnunet/src/arm/mockup-service.c
===================================================================
--- gnunet/src/arm/mockup-service.c     2013-03-13 17:49:17 UTC (rev 26401)
+++ gnunet/src/arm/mockup-service.c     2013-03-13 17:49:26 UTC (rev 26402)
@@ -29,21 +29,27 @@
 #include "gnunet_time_lib.h"
 
 
+static int special_ret = 0;
+
 /**
- * Handler for SHUTDOWN message.
+ * Handler for STOP message.
  *
  * @param cls closure (refers to service)
  * @param client identification of the client
  * @param message the actual message
  */
 static void
-handle_shutdown (void *cls, struct GNUNET_SERVER_Client *client,
+handle_stop (void *cls, struct GNUNET_SERVER_Client *client,
                  const struct GNUNET_MessageHeader *message)
 {
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               _("Initiating shutdown as requested by client.\n"));
   GNUNET_SERVER_client_persist_ (client);
   GNUNET_SCHEDULER_shutdown ();
+  /* ARM won't exponentially increase restart delay if we
+   * terminate normally. This changes the return code.
+   */
+  special_ret = 1;
 }
 
 
@@ -52,7 +58,7 @@
      const struct GNUNET_CONFIGURATION_Handle *cfg)
 {
   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
-    {&handle_shutdown, NULL, GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN,
+    {&handle_stop, NULL, GNUNET_MESSAGE_TYPE_ARM_STOP,
      sizeof (struct GNUNET_MessageHeader)},
     {NULL, NULL, 0, 0}
   };
@@ -70,5 +76,7 @@
       (GNUNET_OK ==
        GNUNET_SERVICE_run (argc, argv, "do-nothing", 
GNUNET_SERVICE_OPTION_NONE,
                            &run, NULL)) ? 0 : 1;
+  if (0 != special_ret)
+    return special_ret;
   return ret;
 }

Modified: gnunet/src/arm/test_arm_api.c
===================================================================
--- gnunet/src/arm/test_arm_api.c       2013-03-13 17:49:17 UTC (rev 26401)
+++ gnunet/src/arm/test_arm_api.c       2013-03-13 17:49:26 UTC (rev 26402)
@@ -42,77 +42,144 @@
 
 static int ok = 1;
 
+static int phase = 0;
+
 static void
-arm_stopped (void *cls,  enum GNUNET_ARM_ProcessStatus success)
+arm_stop_cb (void *cls, struct GNUNET_ARM_Handle *h, enum 
GNUNET_ARM_RequestStatus status, const char *servicename, enum 
GNUNET_ARM_Result result)
 {
-  GNUNET_break (success == GNUNET_ARM_PROCESS_DOWN);
-  if (success != GNUNET_ARM_PROCESS_DOWN)
-    ok = 3;
-  else if (ok == 1)
-    ok = 0;
+  /* (6), a stop request should be sent to ARM successfully */
+  /* ARM should report that it is stopping */
+  GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
+  GNUNET_break (result == GNUNET_ARM_RESULT_STOPPING);
+  GNUNET_break (phase == 6);
+  phase++;
+  FPRINTF (stderr, "Sent 'STOP' request for arm to ARM %s\n", (status == 
GNUNET_ARM_REQUEST_SENT_OK) ? "successfully" : "unsuccessfully");
 }
 
-
 static void
-arm_notify_stop (void *cls, enum GNUNET_ARM_ProcessStatus success)
+resolver_stop_cb (void *cls, struct GNUNET_ARM_Handle *h, enum 
GNUNET_ARM_RequestStatus status, const char *servicename, enum 
GNUNET_ARM_Result result)
 {
-  GNUNET_break (success == GNUNET_ARM_PROCESS_DOWN);
+  /* (5), a stop request should be sent to ARM successfully.
+   * ARM should report that resolver is stopped.
+   */
+  GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
+  GNUNET_break (result == GNUNET_ARM_RESULT_STOPPED);
+  GNUNET_break (phase == 5);
+  FPRINTF (stderr, "Sent 'STOP' request for resolver to ARM %s\n", (status == 
GNUNET_ARM_REQUEST_SENT_OK) ? "successfully" : "unsuccessfully");
+  phase++;
 #if START_ARM
-  GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL);
+  GNUNET_ARM_request_service_stop (arm, "arm", TIMEOUT, arm_stop_cb, NULL);
+#else
+  arm_stop_cb (NULL, GNUNET_ARM_STATUS_SENT_OK, "arm", 
GNUNET_ARM_SERVICE_STOPPING);
+  arm_conn (NULL, GNUNET_NO, GNUNET_NO);
 #endif
 }
 
-
 static void
 dns_notify (void *cls, const struct sockaddr *addr, socklen_t addrlen)
 {
   if (addr == NULL)
     {
+      /* (4), resolver should finish resolving localhost */
+      GNUNET_break (phase == 4);
+      phase++;
+      FPRINTF (stderr, "%s", "Finished resolving localhost\n");
       if (ok != 0)
-       {
-         GNUNET_break (0);
-         ok = 2;
-       }
-      GNUNET_ARM_stop_service (arm, "resolver", TIMEOUT, &arm_notify_stop,
-                              NULL);
+        ok = 2;
+      GNUNET_ARM_request_service_stop (arm, "resolver", TIMEOUT, 
resolver_stop_cb, NULL);
       return;
     }
+  /* (3), resolver should resolve localhost */
+  GNUNET_break (phase == 3);
+  FPRINTF (stderr, "%s", "Resolved localhost\n");
+  phase++;
   GNUNET_break (addr != NULL);
   ok = 0;
 }
 
-
 static void
-resolver_notify (void *cls, enum GNUNET_ARM_ProcessStatus success)
+resolver_start_cb (void *cls, struct GNUNET_ARM_Handle *h, enum 
GNUNET_ARM_RequestStatus status, const char *servicename, enum 
GNUNET_ARM_Result result)
 {
-  if (success != GNUNET_ARM_PROCESS_STARTING)
-    {
-      GNUNET_break (0);
-      ok = 2;
-#if START_ARM
-      GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL);
-#endif
-      return;
-    }
+  /* (2), the start request for resolver should be sent successfully
+   * ARM should report that resolver service is starting.
+   */
+  GNUNET_assert (status == GNUNET_ARM_REQUEST_SENT_OK);
+  GNUNET_break (phase == 2);
+  GNUNET_break (result == GNUNET_ARM_RESULT_STARTING);
+  FPRINTF (stderr, "Sent 'START' request for resolver to ARM %s\n", (status == 
GNUNET_ARM_REQUEST_SENT_OK) ? "successfully" : "unsuccessfully");
+  phase++;
   GNUNET_RESOLVER_ip_get ("localhost", AF_INET, TIMEOUT, &dns_notify, NULL);
 }
 
-
 static void
-arm_notify (void *cls, enum GNUNET_ARM_ProcessStatus success)
+trigger_disconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
-  if (success != GNUNET_ARM_PROCESS_STARTING)
-    {
-      GNUNET_break (0);
-      ok = 2;
+  GNUNET_ARM_disconnect ((struct GNUNET_ARM_Handle *) cls);
+}
+
+
+void
+arm_conn (void *cls, struct GNUNET_ARM_Handle *arm, unsigned char connected, 
unsigned char error)
+{
+  if (GNUNET_YES == error)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+               _("Fatal error initializing ARM API.\n"));
+    GNUNET_SCHEDULER_shutdown ();
+    GNUNET_assert (0);
+    return;
+  }
+  if (connected)
+  {
+    /* (1), arm connection should be established */
+    FPRINTF (stderr, "%s", "Connected to ARM\n");
+    GNUNET_break (phase == 1);
+    phase++;
+    GNUNET_ARM_request_service_start (arm, "resolver", 
GNUNET_OS_INHERIT_STD_OUT_AND_ERR, START_TIMEOUT, resolver_start_cb, NULL);
+  }
+  else
+  {
+    /* (7), ARM should stop (we disconnect from it) */
+    FPRINTF (stderr, "%s", "Disconnected from ARM\n");
+    GNUNET_break (phase == 7);
+    if (phase != 7)
+      ok = 3;
+    else if (ok == 1)
+      ok = 0;
+    GNUNET_SCHEDULER_add_now (trigger_disconnect, arm);
+  }
+}
+
+void
+srv_status (void *cls, const char *service, enum GNUNET_ARM_ServiceStatus 
status)
+{
+  FPRINTF (stderr, "Service %s is %u\n", service, status);
+  switch (phase)
+  {
+  default:
+    FPRINTF (stderr, "Unexpectedly got status %u for service %s\n", service);
+    GNUNET_break (0);
+    ok = 2;
 #if START_ARM
-      GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL);
+    GNUNET_ARM_request_service_stop (arm, "arm", TIMEOUT, NULL, NULL);
 #endif
-    }
-  GNUNET_ARM_start_service (arm, "resolver", 
GNUNET_OS_INHERIT_STD_OUT_AND_ERR, START_TIMEOUT, &resolver_notify,
-                           NULL);
+  }
 }
 
+static void
+arm_start_cb (void *cls, struct GNUNET_ARM_Handle *h, enum 
GNUNET_ARM_RequestStatus status, const char *servicename, enum 
GNUNET_ARM_Result result)
+{
+  /* (0) The request should be "sent" successfully
+   * ("sent", because it isn't going anywhere, ARM API starts ARM service
+   * by itself).
+   * ARM API should report that ARM service is starting.
+   */
+  GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
+  GNUNET_break (phase == 0);
+  FPRINTF (stderr, "Sent 'START' request for arm to ARM %s\n", (status == 
GNUNET_ARM_REQUEST_SENT_OK) ? "successfully" : "unsuccessfully");
+  GNUNET_break (result == GNUNET_ARM_RESULT_STARTING);
+  phase++;
+}
 
 static void
 task (void *cls, char *const *args, const char *cfgfile,
@@ -133,11 +200,13 @@
     else
       GNUNET_free (armconfig);
   }
-  arm = GNUNET_ARM_connect (cfg, NULL);
+  arm = GNUNET_ARM_alloc (cfg);
+  GNUNET_ARM_connect (arm, arm_conn, NULL);
 #if START_ARM
-  GNUNET_ARM_start_service (arm, "arm", GNUNET_OS_INHERIT_STD_OUT_AND_ERR, 
START_TIMEOUT, &arm_notify, NULL);
+  GNUNET_ARM_request_service_start (arm, "arm", 
GNUNET_OS_INHERIT_STD_OUT_AND_ERR, START_TIMEOUT, arm_start_cb, NULL);
 #else
-  arm_notify (NULL, GNUNET_YES);
+  arm_start_cb (NULL, arm, GNUNET_ARM_REQUEST_SENT_OK, "arm", 
GNUNET_ARM_RESULT_STARTING);
+  arm_conn (NULL, GNUNET_YES, GNUNET_NO);
 #endif
 }
 

Modified: gnunet/src/arm/test_exponential_backoff.c
===================================================================
--- gnunet/src/arm/test_exponential_backoff.c   2013-03-13 17:49:17 UTC (rev 
26401)
+++ gnunet/src/arm/test_exponential_backoff.c   2013-03-13 17:49:26 UTC (rev 
26402)
@@ -43,14 +43,20 @@
 
 static struct GNUNET_ARM_Handle *arm;
 
+static struct GNUNET_ARM_MonitorHandle *mon;
+
 static int ok = 1;
 
+static int phase = 0;
+
 static int trialCount;
 
 static struct GNUNET_TIME_Absolute startedWaitingAt;
 
 struct GNUNET_TIME_Relative waitedFor;
 
+struct GNUNET_TIME_Relative waitedFor_prev;
+
 #if LOG_BACKOFF
 static FILE *killLogFilePtr;
 
@@ -97,11 +103,8 @@
 
 /**
  * Handler receiving response to service shutdown requests.
- * First call with NULL: service misbehaving, or something.
- * First call with GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN:
- *   - service will shutdown
- * Second call with NULL:
- *   - service has now really shut down.
+ * We expect it to be called with NULL, since the service that
+ * we are shutting down will just die without replying.
  *
  * @param cls closure
  * @param msg NULL, indicating socket closure.
@@ -111,7 +114,7 @@
 {
   struct ShutdownContext *shutdown_ctx = cls;
 
-  if (msg == NULL) 
+  if (NULL == msg) 
   {
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service shutdown complete.\n");
     if (shutdown_ctx->cont != NULL)
@@ -122,29 +125,7 @@
     GNUNET_free (shutdown_ctx);
     return;
   }
-  GNUNET_assert (ntohs (msg->size) ==
-                sizeof (struct GNUNET_MessageHeader));
-  switch (ntohs (msg->type))
-  {
-  case GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN:
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-               "Received confirmation for service shutdown.\n");
-    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 */
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-               "Service shutdown refused!\n");
-    if (shutdown_ctx->cont != NULL)
-      shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_YES);
-    
-    GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task);
-    GNUNET_CLIENT_disconnect (shutdown_ctx->sock);
-    GNUNET_free (shutdown_ctx);
-    break;
-  }
+  GNUNET_assert (0);
 }
 
 
@@ -183,25 +164,27 @@
   struct ShutdownContext *shutdown_ctx = cls;
 
   if (size < sizeof (struct GNUNET_MessageHeader))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 _("Failed to transmit shutdown request to client.\n"));
-      shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_SYSERR);
-      GNUNET_CLIENT_disconnect (shutdown_ctx->sock);
-      GNUNET_free (shutdown_ctx);
-      return 0;                        /* client disconnected */
-    }
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+        _("Failed to transmit shutdown request to client.\n"));
+    FPRINTF (stderr, "%s", "Failed to send a shutdown request\n");
+    shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_SYSERR);
+    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);
+  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->type = htons (GNUNET_MESSAGE_TYPE_ARM_STOP);
   msg->size = htons (sizeof (struct GNUNET_MessageHeader));
-  return sizeof (struct GNUNET_MessageHeader);
+  strcpy ((char *) &msg[1], "do-nothing");
+  FPRINTF (stderr, "%s", "Sent a shutdown request\n");
+  return sizeof (struct GNUNET_MessageHeader) + strlen ("do-nothing") + 1;
 }
 
 
@@ -219,7 +202,7 @@
  *
  */
 static void
-arm_service_shutdown (struct GNUNET_CLIENT_Connection *sock,
+do_nothing_service_shutdown (struct GNUNET_CLIENT_Connection *sock,
                      struct GNUNET_TIME_Relative timeout,
                      GNUNET_CLIENT_ShutdownTask cont, void *cont_cls)
 {
@@ -231,125 +214,132 @@
   shutdown_ctx->sock = sock;
   shutdown_ctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
   GNUNET_CLIENT_notify_transmit_ready (sock,
-                                      sizeof (struct GNUNET_MessageHeader),
+                                      sizeof (struct GNUNET_MessageHeader) + 
strlen ("do-nothing") + 1,
                                       timeout, GNUNET_NO, &write_shutdown,
                                       shutdown_ctx);
 }
 
-
 static void
-arm_notify_stop (void *cls, enum GNUNET_ARM_ProcessStatus status)
-{
-  GNUNET_assert ( (status == GNUNET_ARM_PROCESS_DOWN) ||
-                 (status == GNUNET_ARM_PROCESS_ALREADY_DOWN) );
-#if START_ARM
-  GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, NULL, NULL);
-#endif
-}
-
-
-static void
 kill_task (void *cbData, const struct GNUNET_SCHEDULER_TaskContext *tc);
 
-
 static void
-do_nothing_notify (void *cls, enum GNUNET_ARM_ProcessStatus status)
+shutdown_cont (void *cls, int reason)
 {
-  GNUNET_assert (status == GNUNET_ARM_PROCESS_STARTING);
-  ok = 1;
-  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &kill_task, NULL);
+  if (GNUNET_NO != reason)
+  {
+    /* Re-try shutdown */
+    FPRINTF (stderr, "%s", "do-nothing didn't die, trying again\n");
+    GNUNET_SCHEDULER_add_now (kill_task, NULL);
+    return;
+  }
+  startedWaitingAt = GNUNET_TIME_absolute_get ();
+  FPRINTF (stderr, "%s", "do-nothing is dead, starting the countdown\n");
 }
 
 static void
-arm_notify (void *cls, enum GNUNET_ARM_ProcessStatus status)
+kill_task (void *cbData, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
-  GNUNET_assert (status == GNUNET_ARM_PROCESS_STARTING);
-  GNUNET_ARM_start_service (arm, "do-nothing", 
GNUNET_OS_INHERIT_STD_OUT_AND_ERR, TIMEOUT, &do_nothing_notify,
-                           NULL);
-}
+  static struct GNUNET_CLIENT_Connection *doNothingConnection = NULL;
 
-
-static void
-kill_task (void *cbData, const struct GNUNET_SCHEDULER_TaskContext *tc);
-
-
-static void
-do_nothing_restarted_notify_task (void *cls,
-                                 const struct GNUNET_SCHEDULER_TaskContext
-                                 *tc)
-{
-  static char a;
-
-  trialCount++;
-
+  if (NULL != cbData)
+  {
+    waitedFor = GNUNET_TIME_absolute_get_duration (startedWaitingAt);
+    FPRINTF (stderr, "Waited for: %llu ms\n", waitedFor.rel_value);
 #if LOG_BACKOFF
-  if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
-    {
-      FPRINTF (killLogFilePtr, "%d.Reason is shutdown!\n", trialCount);
-    }
-  else if ((tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT) != 0)
-    {
-      FPRINTF (killLogFilePtr, "%d.Reason is timeout!\n", trialCount);
-    }
-  else if ((tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE) != 0)
-    {
-      FPRINTF (killLogFilePtr, "%d.Service is running!\n", trialCount);
-    }
+    FPRINTF (killLogFilePtr, "Waited for: %llu ms\n",
+      (unsigned long long) waitedFor.rel_value);
 #endif
-  GNUNET_SCHEDULER_add_now (&kill_task, &a);
+  }
+  else
+  {
+    waitedFor.rel_value = 0;
+  }
+  /* Connect to the doNothing task */
+  doNothingConnection = GNUNET_CLIENT_connect ("do-nothing", cfg);
+  GNUNET_assert (doNothingConnection != NULL);
+  if (trialCount == 12)
+    waitedFor_prev = waitedFor;
+  else if (trialCount == 13)
+  {
+    GNUNET_CLIENT_disconnect (doNothingConnection);
+    GNUNET_ARM_request_service_stop (arm, "do-nothing", TIMEOUT, NULL, NULL);
+    if (waitedFor_prev.rel_value >= waitedFor.rel_value)
+      ok = 9;
+    else
+      ok = 0;
+    trialCount += 1;
+    return;
+  }
+  trialCount += 1;
+  /* Use the created connection to kill the doNothingTask */
+  do_nothing_service_shutdown (doNothingConnection,
+      TIMEOUT, &shutdown_cont, NULL);
 }
 
-
 static void
-do_test (void *cbData, const struct GNUNET_SCHEDULER_TaskContext *tc)
+trigger_disconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
-  GNUNET_CLIENT_service_test ("do-nothing", cfg, TIMEOUT,
-                             &do_nothing_restarted_notify_task, NULL);
+  GNUNET_ARM_disconnect (arm);
+  GNUNET_ARM_monitor_disconnect (mon);
 }
 
 
 static void
-shutdown_cont (void *cls, int reason)
+arm_stop_cb (void *cls, struct GNUNET_ARM_Handle *h, enum 
GNUNET_ARM_RequestStatus status, const char *servicename, enum 
GNUNET_ARM_Result result)
 {
-  trialCount++;
-  startedWaitingAt = GNUNET_TIME_absolute_get ();
-  GNUNET_SCHEDULER_add_delayed (waitedFor, &do_test, NULL);
+  GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
+  GNUNET_break (result == GNUNET_ARM_RESULT_STOPPING);
+  FPRINTF (stderr, "%s", "ARM service stopped\n");
+  GNUNET_SCHEDULER_add_now (trigger_disconnect, NULL);
 }
 
-
-static void
-kill_task (void *cbData, const struct GNUNET_SCHEDULER_TaskContext *tc)
+void
+srv_status (void *cls, struct GNUNET_ARM_MonitorHandle *mon, const char 
*service, enum GNUNET_ARM_ServiceStatus status)
 {
-  static struct GNUNET_CLIENT_Connection *doNothingConnection = NULL;
-
-  if (NULL != cbData)
+  FPRINTF (stderr, "Service %s is %u, phase %u\n", service, status, phase);
+  if (status == GNUNET_ARM_SERVICE_MONITORING_STARTED)
+  {
+    phase++;
+    GNUNET_ARM_request_service_start (arm, "do-nothing",
+        GNUNET_OS_INHERIT_STD_OUT_AND_ERR, TIMEOUT, NULL, NULL);
+    return;
+  }
+  if (phase == 1)
+  {
+    GNUNET_break (status == GNUNET_ARM_SERVICE_STARTING);
+    GNUNET_break (0 == strcasecmp (service, "do-nothing"));
+    GNUNET_break (phase == 1);
+    FPRINTF (stderr, "%s", "do-nothing is starting\n");
+    phase++;
+    ok = 1;
+    GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &kill_task, NULL);
+  }
+  else if ((phase == 2) && (strcasecmp ("do-nothing", service) == 0))
+  {
+    /* We passively monitor ARM for status updates. ARM should tell us
+     * when do-nothing dies (no need to run a service upness test ourselves).
+     */
+    if (status == GNUNET_ARM_SERVICE_STARTING)
     {
-      waitedFor = GNUNET_TIME_absolute_get_duration (startedWaitingAt);
-
-#if LOG_BACKOFF
-      FPRINTF (killLogFilePtr, "Waited for: %llu ms\n",
-              (unsigned long long) waitedFor.rel_value);
-#endif
+      FPRINTF (stderr, "%s", "do-nothing is starting\n");
+      GNUNET_SCHEDULER_add_now (kill_task, &ok);
     }
-  else
+    else if ((status == GNUNET_ARM_SERVICE_STOPPED) && (trialCount == 14))
     {
-      waitedFor.rel_value = 0;
+      phase++;
+      GNUNET_ARM_request_service_stop (arm, "arm", TIMEOUT, arm_stop_cb, NULL);
     }
-  /* Connect to the doNothing task */
-  doNothingConnection = GNUNET_CLIENT_connect ("do-nothing", cfg);
-  GNUNET_assert (doNothingConnection != NULL);
-  if (trialCount == 12)
-    {
-      GNUNET_CLIENT_disconnect (doNothingConnection);
-      GNUNET_ARM_stop_service (arm, "do-nothing", TIMEOUT, &arm_notify_stop,
-                              NULL);
-      ok = 0;
-      return;
-    }
-  /* Use the created connection to kill the doNothingTask */
-  arm_service_shutdown (doNothingConnection, TIMEOUT, &shutdown_cont, NULL);
+  }
 }
 
+static void
+arm_start_cb (void *cls, struct GNUNET_ARM_Handle *h, enum 
GNUNET_ARM_RequestStatus status, const char *servicename, enum 
GNUNET_ARM_Result result)
+{
+  GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
+  GNUNET_break (result == GNUNET_ARM_RESULT_STARTING);
+  GNUNET_break (phase == 0);
+  FPRINTF (stderr, "Sent 'START' request for arm to ARM %s\n", (status == 
GNUNET_ARM_REQUEST_SENT_OK) ? "successfully" : "unsuccessfully");
+}
 
 static void
 task (void *cls, char *const *args, const char *cfgfile,
@@ -359,9 +349,8 @@
   cfg = c;
   if (NULL != cfgfile)
   {
-    if (GNUNET_OK !=
-        GNUNET_CONFIGURATION_get_value_filename (cfg, "arm", "CONFIG",
-                                              &armconfig))
+    if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "arm",
+        "CONFIG", &armconfig))
     {
       GNUNET_CONFIGURATION_set_value_string ((struct 
GNUNET_CONFIGURATION_Handle
                                               *) cfg, "arm", "CONFIG",
@@ -371,16 +360,18 @@
       GNUNET_free (armconfig);
   }
 
-  arm = GNUNET_ARM_connect (cfg, NULL);
+  arm = GNUNET_ARM_alloc (cfg);
+  GNUNET_ARM_connect (arm, NULL, NULL);
+  mon = GNUNET_ARM_monitor_alloc (cfg);
+  GNUNET_ARM_monitor (mon, srv_status, NULL);
 #if START_ARM
-  GNUNET_ARM_start_service (arm, "arm", GNUNET_OS_INHERIT_STD_OUT_AND_ERR, 
GNUNET_TIME_UNIT_ZERO, &arm_notify,
-                           NULL);
+  GNUNET_ARM_request_service_start (arm, "arm",
+      GNUNET_OS_INHERIT_STD_OUT_AND_ERR, GNUNET_TIME_UNIT_ZERO, arm_start_cb, 
NULL);
 #else
-  arm_do_nothing (NULL, GNUNET_YES);
+  arm_start_cb (NULL, arm, GNUNET_ARM_REQUEST_SENT_OK, "arm", 
GNUNET_ARM_SERVICE_STARTING);
 #endif
 }
 
-
 static int
 check ()
 {

Modified: gnunet/src/arm/test_gnunet_service_arm.c
===================================================================
--- gnunet/src/arm/test_gnunet_service_arm.c    2013-03-13 17:49:17 UTC (rev 
26401)
+++ gnunet/src/arm/test_gnunet_service_arm.c    2013-03-13 17:49:26 UTC (rev 
26402)
@@ -42,23 +42,22 @@
 
 static struct GNUNET_ARM_Handle *arm;
 
-
 static void
-arm_stopped (void *cls, enum GNUNET_ARM_ProcessStatus success)
+trigger_disconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
-  if (success != GNUNET_ARM_PROCESS_DOWN)
-    {
-      GNUNET_break (0);
-      ret = 4;
-    }
-  else
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ARM stopped\n");
-    }
   GNUNET_ARM_disconnect (arm);
   arm = NULL;
 }
 
+static void
+arm_stop_cb (void *cls, struct GNUNET_ARM_Handle *h, enum 
GNUNET_ARM_RequestStatus status, const char *servicename, enum 
GNUNET_ARM_Result result)
+{
+  GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
+  GNUNET_break (result == GNUNET_ARM_RESULT_STOPPING);
+  if (result != GNUNET_ARM_RESULT_STOPPING)
+    ret = 4;
+  GNUNET_SCHEDULER_add_now (trigger_disconnect, NULL);
+}
 
 static void
 hostNameResolveCB (void *cls, const struct sockaddr *addr, socklen_t addrlen)
@@ -66,43 +65,37 @@
   if ((ret == 0) || (ret == 4))
     return;
   if (NULL == addr)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Name not resolved!\n");
-      GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL);
-      ret = 3;
-      return;
-    }
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Resolved hostname, now stopping ARM\n");
-  ret = 0;
-  GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL);
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Name not resolved!\n");
+    ret = 3;
+  }
+  else
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+        "Resolved hostname, now stopping ARM\n");
+    ret = 0;
+  }
+  GNUNET_ARM_request_service_stop (arm, "arm", TIMEOUT, arm_stop_cb, NULL);
 }
 
-
 static void
-arm_notify (void *cls, enum GNUNET_ARM_ProcessStatus success)
+arm_start_cb (void *cls, struct GNUNET_ARM_Handle *h, enum 
GNUNET_ARM_RequestStatus status, const char *servicename, enum 
GNUNET_ARM_Result result)
 {
-  if (success != GNUNET_ARM_PROCESS_STARTING)
-    {
-      GNUNET_break (0);
-      ret = 1;
-      return;
-    }
+  GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
+  GNUNET_break (result == GNUNET_ARM_RESULT_STARTING);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Trying to resolve our own hostname!\n");
+      "Trying to resolve our own hostname!\n");
   /* connect to the resolver service */
-  if (NULL ==
-      GNUNET_RESOLVER_hostname_resolve (AF_UNSPEC, TIMEOUT,
-                                       &hostNameResolveCB, NULL))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                 "Unable initiate connection to resolver service\n");
-      ret = 2;
-      GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL);
-    }
+  if (NULL == GNUNET_RESOLVER_hostname_resolve (
+      AF_UNSPEC, TIMEOUT, &hostNameResolveCB, NULL))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+        "Unable initiate connection to resolver service\n");
+    ret = 2;
+    GNUNET_ARM_request_service_stop (arm, "arm", TIMEOUT, arm_stop_cb, NULL);
+  }
 }
 
-
 static void
 run (void *cls, char *const *args, const char *cfgfile,
      const struct GNUNET_CONFIGURATION_Handle *c)
@@ -122,10 +115,10 @@
     else
       GNUNET_free (armconfig);
   }
-  arm = GNUNET_ARM_connect (c, NULL);
-  GNUNET_ARM_start_service (arm, "arm",
-                           GNUNET_OS_INHERIT_STD_OUT_AND_ERR, START_TIMEOUT, 
-                           &arm_notify, NULL);
+  arm = GNUNET_ARM_alloc (c);
+  GNUNET_ARM_connect (arm, NULL, NULL);
+  GNUNET_ARM_request_service_start (arm, "arm",
+      GNUNET_OS_INHERIT_STD_OUT_AND_ERR, START_TIMEOUT, arm_start_cb, NULL);
 }
 
 

Modified: gnunet/src/include/gnunet_arm_service.h
===================================================================
--- gnunet/src/include/gnunet_arm_service.h     2013-03-13 17:49:17 UTC (rev 
26401)
+++ gnunet/src/include/gnunet_arm_service.h     2013-03-13 17:49:26 UTC (rev 
26402)
@@ -43,112 +43,208 @@
 /**
  * Version of the arm API.
  */
-#define GNUNET_ARM_VERSION 0x00000001
+#define GNUNET_ARM_VERSION 0x00000002
 
 
 /**
- * Values characterizing GNUnet process states.
+ * Statuses of the requests that client can send to ARM.
  */
-enum GNUNET_ARM_ProcessStatus
+enum GNUNET_ARM_RequestStatus
 {
   /**
-   * Service name is unknown to ARM.
+   * Message was sent successfully.
    */
-  GNUNET_ARM_PROCESS_UNKNOWN = -1,
+  GNUNET_ARM_REQUEST_SENT_OK = 0,
 
   /**
-   * Service is now down (due to client request).
+   * Misconfiguration (can't connect to the ARM service).
    */
-  GNUNET_ARM_PROCESS_DOWN = 0,
+  GNUNET_ARM_REQUEST_CONFIGURATION_ERROR = 1,
 
   /**
-   * Service is already running.
+   * We disconnected from ARM, and request was not sent.
    */
-  GNUNET_ARM_PROCESS_ALREADY_RUNNING = 1,
+  GNUNET_ARM_REQUEST_DISCONNECTED = 2,
 
   /**
-   * Service is currently being started (due to client request).
+   * ARM API is busy (probably trying to connect to ARM),
+   * and request was not sent. Try again later.
    */
-  GNUNET_ARM_PROCESS_STARTING = 2,
-  
+  GNUNET_ARM_REQUEST_BUSY = 3,
+
   /**
-   * Service is already being stopped by some other client.
+   * It was discovered that the request would be too long to fit in a message,
+   * and thus it was not sent.
    */
-  GNUNET_ARM_PROCESS_ALREADY_STOPPING = 3,
+  GNUNET_ARM_REQUEST_TOO_LONG = 4,
 
   /**
-   * Service is already down (no action taken)
+   * Request time ran out before we had a chance to send it.
    */
-  GNUNET_ARM_PROCESS_ALREADY_DOWN = 4,
+  GNUNET_ARM_REQUEST_TIMEOUT = 5
 
+};
+
+
+/**
+ * Statuses of services.
+ */
+enum GNUNET_ARM_ServiceStatus
+{
   /**
-   * ARM is currently being shut down (no more process starts)
+   * Dummy message.
    */
-  GNUNET_ARM_PROCESS_SHUTDOWN = 5,
+  GNUNET_ARM_SERVICE_MONITORING_STARTED = 0,
 
   /**
-   * Error in communication with ARM
+   * Service was stopped.
    */
-  GNUNET_ARM_PROCESS_COMMUNICATION_ERROR = 6,
+  GNUNET_ARM_SERVICE_STOPPED = 1,
 
   /**
-   * Timeout in communication with ARM
+   * Service starting was initiated
    */
-  GNUNET_ARM_PROCESS_COMMUNICATION_TIMEOUT = 7,
+  GNUNET_ARM_SERVICE_STARTING = 2,
 
   /**
-   * Failure to perform operation
+   * Service stopping was initiated
    */
-  GNUNET_ARM_PROCESS_FAILURE = 8
+  GNUNET_ARM_SERVICE_STOPPING = 3
 };
 
+/**
+ * Replies to ARM requests
+ */
+enum GNUNET_ARM_Result
+{
+  /**
+   * Service was stopped (never sent for ARM itself).
+   */
+  GNUNET_ARM_RESULT_STOPPED = 0,
 
+  /**
+   * ARM stopping was initiated (there's no "stopped" for ARM itself).
+   */
+  GNUNET_ARM_RESULT_STOPPING = 1,
+
+  /**
+   * Service starting was initiated
+   */
+  GNUNET_ARM_RESULT_STARTING = 2,
+
+  /**
+   * Asked to start it, but it's already starting.
+   */
+  GNUNET_ARM_RESULT_IS_STARTING_ALREADY = 3,
+
+  /**
+   * Asked to stop it, but it's already stopping.
+   */
+  GNUNET_ARM_RESULT_IS_STOPPING_ALREADY = 4,
+
+  /**
+   * Asked to start it, but it's already started.
+   */
+  GNUNET_ARM_RESULT_IS_STARTED_ALREADY = 5,
+
+  /**
+   * Asked to stop it, but it's already stopped.
+   */
+  GNUNET_ARM_RESULT_IS_STOPPED_ALREADY = 6,
+
+  /**
+   * Asked to start or stop a service, but it's not known.
+   */
+  GNUNET_ARM_RESULT_IS_NOT_KNOWN = 7,
+
+  /**
+   * Tried to start a service, but that failed for some reason.
+   */
+  GNUNET_ARM_RESULT_START_FAILED = 8,
+
+  /**
+   * Asked to start something, but ARM is shutting down and can't comply.
+   */
+  GNUNET_ARM_RESULT_IN_SHUTDOWN = 9
+};
+
+
 /**
- * Callback function invoked when operation is complete.
+ * Handle for interacting with ARM.
+ */
+struct GNUNET_ARM_Handle;
+
+
+/**
+ * Function called whenever we connect to or disconnect from ARM.
  *
  * @param cls closure
- * @param result outcome of the operation
+ * @param connected GNUNET_YES if connected, GNUNET_NO if disconnected
+ * @param error GNUNET_YES if we encountered a permanent error, and there
+ *              will be no re-connection.
  */
-typedef void (*GNUNET_ARM_Callback) (void *cls, 
-                                    enum GNUNET_ARM_ProcessStatus result);
+typedef void (*GNUNET_ARM_ConnectionStatusCallback) (void *cls, struct 
GNUNET_ARM_Handle *arm, unsigned char connected, unsigned char error);
 
+
 /**
- * Callback function invoked when list operation is complete.
+ * Function called in response to a start/stop request.
+ * Will be called when request was not sent successfully,
+ * or when a reply comes. If the request was not sent successfully,
+ * 'rs' will indicate that, and 'service' and 'result' will be undefined.
  *
  * @param cls closure
- * @param result outcome of the operation (GNUNET_YES if successful)
- * @param count number of strings in the list
- * @param list list of running services
+ * @param arm handle to the arm connection
+ * @param rs status of the request
+ * @param service service name
+ * @param result result of the operation
  */
-typedef void (*GNUNET_ARM_List_Callback) (void *cls, 
-                                          int result,
-                                          unsigned int count,
-                                          const char *const *list);
+typedef void (*GNUNET_ARM_ResultCallback) (void *cls, struct GNUNET_ARM_Handle 
*arm, enum GNUNET_ARM_RequestStatus rs, const char *service, enum 
GNUNET_ARM_Result result);
 
 
 /**
- * Handle for interacting with ARM.
+ * Callback function invoked when list operation is complete.
+ * Will be called when request was not sent successfully,
+ * or when a reply comes. If the request was not sent successfully,
+ * 'rs' will indicate that, and 'count' and 'list' will be undefined.
+ *
+ * @param cls closure
+ * @param arm handle to the arm connection
+ * @param rs status of the request
+ * @param count number of strings in the list
+ * @param list list of running services
  */
-struct GNUNET_ARM_Handle;
+typedef void (*GNUNET_ARM_ServiceListCallback) (void *cls, struct 
GNUNET_ARM_Handle *arm, enum GNUNET_ARM_RequestStatus rs, unsigned int count, 
const char *const*list);
 
 
 /**
  * Setup a context for communicating with ARM.  Note that this
  * can be done even if the ARM service is not yet running.
+ * Never fails.
  *
  * @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 service service that *this* process is implementing/providing, can 
be NULL
- * @return context to use for further ARM operations, NULL on error
+ * @return context to use for further ARM operations
  */
 struct GNUNET_ARM_Handle *
-GNUNET_ARM_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                    const char *service);
+GNUNET_ARM_alloc (const struct GNUNET_CONFIGURATION_Handle *cfg);
 
+/**
+ * Start connecting to the ARM service using the context.
+ * @param conn_status called when we (dis)connect from/to ARM.
+ *        It's also called on connection errors.
+ * @param cls closure for conn_status
+ *
+ * @param h ARM handle
+ */
+void
+GNUNET_ARM_connect (struct GNUNET_ARM_Handle *h,
+    GNUNET_ARM_ConnectionStatusCallback conn_status, void *cls);
 
+
 /**
- * Disconnect from the ARM service.
+ * Disconnect from the ARM service and destroy the handle.
+ * Don't call this from inside an ARM callback!
  *
  * @param h the handle that was being used
  */
@@ -157,61 +253,108 @@
 
 
 /**
- * Start a service.  Note that this function merely asks ARM to start
- * the service and that ARM merely confirms that it forked the
- * respective process.  The specified callback may thus return before
- * the service has started to listen on the server socket and it may
- * also be that the service has crashed in the meantime.  Clients
- * should repeatedly try to connect to the service at the respective
- * port (with some delays in between) before assuming that the service
- * actually failed to start.  Note that if an error is returned to the
- * callback, clients obviously should not bother with trying to
- * contact the service.
+ * Request a list of running services.
  *
  * @param h handle to ARM
- * @param service_name name of the service
- * @param std_inheritance flags controlling std descriptors inheritance
  * @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 cont callback to invoke after request is sent or is not sent
+ * @param cont_cls closure for callback
  */
 void
-GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h, const char 
*service_name,
-                          enum GNUNET_OS_InheritStdioFlags std_inheritance,
-                          struct GNUNET_TIME_Relative timeout,
-                          GNUNET_ARM_Callback cb, void *cb_cls);
+GNUNET_ARM_request_service_list (struct GNUNET_ARM_Handle *h,
+    struct GNUNET_TIME_Relative timeout,
+    GNUNET_ARM_ServiceListCallback cont, void *cont_cls);
 
 
 /**
- * Stop a service.  Note that the callback is invoked as soon
- * as ARM confirms that it will ask the service to terminate.
- * The actual termination may still take some time.
+ * Request a service to be stopped.
+ * Stopping arm itself will not invalidate its handle, and
+ * ARM API will try to restore connection to the ARM service,
+ * even if ARM connection was lost because you asked for ARM to be stopped.
+ * Call GNUNET_ARM_disconnect () to free the handle and prevent
+ * further connection attempts.
  *
  * @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
+ * @param cont callback to invoke after request is sent or is not sent
+ * @param cont_cls closure for callback
  */
 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);
+GNUNET_ARM_request_service_stop (struct GNUNET_ARM_Handle *h,
+    const char *service_name, struct GNUNET_TIME_Relative timeout,
+    GNUNET_ARM_ResultCallback cont, void *cont_cls);
 
 
 /**
- * List all running services.
- * 
+ * Request for a service to be started.
+ *
  * @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 cont callback to invoke after request is sent or not sent
+ * @param cont_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);
+GNUNET_ARM_request_service_start (struct GNUNET_ARM_Handle *h,
+    const char *service_name, enum GNUNET_OS_InheritStdioFlags std_inheritance,
+    struct GNUNET_TIME_Relative timeout, GNUNET_ARM_ResultCallback cont,
+    void *cont_cls);
 
+
+/**
+ * Handle for monitoring ARM.
+ */
+struct GNUNET_ARM_MonitorHandle;
+
+
+/**
+ * Function called in when a status update arrives.
+ *
+ * @param cls closure
+ * @param arm handle to the arm connection
+ * @param service service name
+ * @param status status of the service
+ */
+typedef void (*GNUNET_ARM_ServiceStatusCallback) (void *cls, struct 
GNUNET_ARM_MonitorHandle *arm, const char *service, enum 
GNUNET_ARM_ServiceStatus status);
+
+
+/**
+ * Setup a context for monitoring ARM.  Note that this
+ * can be done even if the ARM service is not yet running.
+ * Never fails.
+ *
+ * @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).
+ * @return context to use for further ARM monitor operations
+ */
+struct GNUNET_ARM_MonitorHandle *
+GNUNET_ARM_monitor_alloc (const struct GNUNET_CONFIGURATION_Handle *cfg);
+
+/**
+ * Start connecting to the ARM service for monitoring using the context.
+ *
+ * @param h ARM monitor handle
+ * @param cont callback to invoke on status updates
+ * @param cont_cls closure
+ */
+void
+GNUNET_ARM_monitor (struct GNUNET_ARM_MonitorHandle *h,
+    GNUNET_ARM_ServiceStatusCallback cont, void *cont_cls);
+
+
+/**
+ * Disconnect from the ARM service and destroy the handle.
+ * Don't call this from inside an ARM callback!
+ *
+ * @param h the handle that was being used
+ */
+void
+GNUNET_ARM_monitor_disconnect (struct GNUNET_ARM_MonitorHandle *h);
+
+
 #if 0                           /* keep Emacsens' auto-indent happy */
 {
 #endif

Modified: gnunet/src/include/gnunet_protocols.h
===================================================================
--- gnunet/src/include/gnunet_protocols.h       2013-03-13 17:49:17 UTC (rev 
26401)
+++ gnunet/src/include/gnunet_protocols.h       2013-03-13 17:49:26 UTC (rev 
26402)
@@ -78,14 +78,14 @@
 #define GNUNET_MESSAGE_TYPE_ARM_STOP 9
 
 /**
- * Request ARM service itself to shutdown.
+ * Response from ARM.
  */
-#define GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN 10
+#define GNUNET_MESSAGE_TYPE_ARM_RESULT 10
 
 /**
- * Response from ARM.
+ * Status update from ARM.
  */
-#define GNUNET_MESSAGE_TYPE_ARM_RESULT 11
+#define GNUNET_MESSAGE_TYPE_ARM_STATUS 11
 
 /**
  * Request to ARM to list all currently running services
@@ -97,6 +97,11 @@
  */
 #define GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT 13
 
+/**
+ * Request to ARM to notify client of service status changes
+ */
+#define GNUNET_MESSAGE_TYPE_ARM_MONITOR 14
+
 
/*******************************************************************************
  * HELLO message types
  
******************************************************************************/

Modified: gnunet/src/include/gnunet_server_lib.h
===================================================================
--- gnunet/src/include/gnunet_server_lib.h      2013-03-13 17:49:17 UTC (rev 
26401)
+++ gnunet/src/include/gnunet_server_lib.h      2013-03-13 17:49:26 UTC (rev 
26402)
@@ -395,7 +395,18 @@
                                                   struct GNUNET_SERVER_Client *
                                                   client);
 
+/**
+ * Functions with this signature are called whenever a client
+ * is connected on the network level.
+ *
+ * @param cls closure
+ * @param client identification of the client
+ */
+typedef void (*GNUNET_SERVER_ConnectCallback) (void *cls,
+    struct GNUNET_SERVER_Client *client);
 
+
+
 /**
  * Ask the server to notify us whenever a client disconnects.
  * This function is called whenever the actual network connection
@@ -417,6 +428,20 @@
 
 
 /**
+ * Ask the server to notify us whenever a client connects.
+ * This function is called whenever the actual network connection
+ * is opened.
+ *
+ * @param server the server manageing the clients
+ * @param callback function to call on sconnect
+ * @param callback_cls closure for callback
+ */
+void
+GNUNET_SERVER_connect_notify (struct GNUNET_SERVER_Handle *server,
+    GNUNET_SERVER_ConnectCallback callback, void *callback_cls);
+
+
+/**
  * Ask the server to stop notifying us whenever a client disconnects.
  *
  * @param server the server manageing the clients
@@ -430,6 +455,18 @@
 
 
 /**
+ * Ask the server to stop notifying us whenever a client connects.
+ *
+ * @param server the server manageing the clients
+ * @param callback function to call on connect
+ * @param callback_cls closure for callback
+ */
+void
+GNUNET_SERVER_connect_notify_cancel (struct GNUNET_SERVER_Handle *server,
+    GNUNET_SERVER_ConnectCallback callback, void *callback_cls);
+
+
+/**
  * Ask the server to disconnect from the given client.
  * This is the same as returning GNUNET_SYSERR from a message
  * handler, except that it allows dropping of a client even

Modified: gnunet/src/regex/gnunet-regex-profiler.c
===================================================================
--- gnunet/src/regex/gnunet-regex-profiler.c    2013-03-13 17:49:17 UTC (rev 
26401)
+++ gnunet/src/regex/gnunet-regex-profiler.c    2013-03-13 17:49:26 UTC (rev 
26402)
@@ -997,7 +997,18 @@
 }
 
 
+
 /**
+ * Start announcing the next regex in the DHT.
+ *
+ * @param cls Index of the next peer in the peers array.
+ * @param tc TaskContext.
+ */
+void
+announce_next_regex (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
+
+
+/**
  * ARM connect adapter. Opens a connection to the ARM service.
  *
  * @param cls Closure (peer).
@@ -1010,7 +1021,8 @@
 {
   struct RegexPeer *peer = cls;
 
-  peer->arm_handle = GNUNET_ARM_connect (cfg, NULL);
+  peer->arm_handle = GNUNET_ARM_alloc (cfg);
+  GNUNET_ARM_connect (peer->arm_handle, NULL, NULL);
 
   return peer->arm_handle;
 }
@@ -1036,104 +1048,52 @@
   }
 }
 
-
-/**
- * Start announcing the next regex in the DHT.
- *
- * @param cls Index of the next peer in the peers array.
- * @param tc TaskContext.
- */
-void
-announce_next_regex (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
-
-
-/**
- * Callback function invoked when ARM peration is complete: deamon is started.
- *
- * @param cls Closure (RegexPeer).
- * @param result Outcome of the operation.
- */
 static void
-arm_start_cb (void *cls, enum GNUNET_ARM_ProcessStatus result)
+regexprofiler_start_cb (void *cls, struct GNUNET_ARM_Handle *arm,
+    enum GNUNET_ARM_RequestStatus rs, const char *service,
+    enum GNUNET_ARM_Result result)
 {
   struct RegexPeer *peer = (struct RegexPeer *) cls;
-  static unsigned int peer_cnt;
   unsigned int next_p;
 
-  switch (result)
+  if (rs != GNUNET_ARM_REQUEST_SENT_OK)
   {
-      /**
-       * Service is currently being started (due to client request).
-       */
-    case GNUNET_ARM_PROCESS_STARTING:
-      GNUNET_TESTBED_operation_done (peer->op_handle);
-      peer->op_handle = NULL;
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "ARM request was not sent: %u\n", 
rs);
+    GNUNET_abort ();
+  }
+  else if (result != GNUNET_ARM_RESULT_STARTING)
+  {
+    /* FIXME: maybe check for other acceptable results (already starting,
+     * already started)?
+     */
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "ARM failed to start regexprofiler: 
%u\n", result);
+    GNUNET_abort ();
+  }
+  GNUNET_TESTBED_operation_done (peer->op_handle);
+  peer->op_handle = NULL;
 
-      if (peer_cnt < (num_peers - 1))
-      {
-        next_p = (++peer_cnt % num_peers);
-        GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply(
-                                        GNUNET_TIME_UNIT_MILLISECONDS,
-                                        400),
-                                      &announce_next_regex,
-                                      (void *) (long) next_p);
-      }
-      else
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                    "All daemons started."
-                    " Waiting %s to start string searches\n",
-                    GNUNET_STRINGS_relative_time_to_string (search_delay,
-                                                            GNUNET_NO));
-        GNUNET_SCHEDULER_add_delayed (search_delay,
-                                      do_connect_by_string, 
-                                      NULL);
-      }
-      break;
-
-      /**
-       * Service name is unknown to ARM.
-       */
-    case GNUNET_ARM_PROCESS_UNKNOWN:
-      /**
-       * Service is now down (due to client request).
-       */
-    case GNUNET_ARM_PROCESS_DOWN:
-      /**
-       * Service is already running.
-       */
-    case GNUNET_ARM_PROCESS_ALREADY_RUNNING:
-      /**
-       * Service is already being stopped by some other client.
-       */
-    case GNUNET_ARM_PROCESS_ALREADY_STOPPING:
-      /**
-       * Service is already down (no action taken)
-       */
-    case GNUNET_ARM_PROCESS_ALREADY_DOWN: 
-      /**
-       * ARM is currently being shut down (no more process starts)
-       */
-    case GNUNET_ARM_PROCESS_SHUTDOWN:
-      /**
-       * Error in communication with ARM
-       */
-    case GNUNET_ARM_PROCESS_COMMUNICATION_ERROR:
-      /**
-       * Timeout in communication with ARM
-       */
-    case GNUNET_ARM_PROCESS_COMMUNICATION_TIMEOUT:
-      /**
-      * Failure to perform operation
-      */
-    case GNUNET_ARM_PROCESS_FAILURE:
-    default:
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "ARM returned %d\n", result);
-      GNUNET_abort ();
+  if (peer_cnt < (num_peers - 1))
+  {
+    next_p = (++peer_cnt % num_peers);
+    GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply(
+                                    GNUNET_TIME_UNIT_MILLISECONDS,
+                                    400),
+                                  &announce_next_regex,
+                                  (void *) (long) next_p);
   }
+  else
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "All daemons started."
+                " Waiting %s to start string searches\n",
+                GNUNET_STRINGS_relative_time_to_string (search_delay,
+                                                        GNUNET_NO));
+    GNUNET_SCHEDULER_add_delayed (search_delay,
+                                  do_connect_by_string, 
+                                  NULL);
+  }
 }
 
-
 /**
  * ARM connect callback. Called when we are connected to the arm service for
  * the peer in 'cls'. If successfull we start the regex deamon to start
@@ -1160,11 +1120,10 @@
   GNUNET_assert (peer->op_handle == op);
   GNUNET_assert (peer->arm_handle == ca_result);
 
-  GNUNET_ARM_start_service (ca_result, "regexprofiler",
-                            GNUNET_OS_INHERIT_STD_NONE,
-                            GNUNET_TIME_UNIT_FOREVER_REL,
-                            &arm_start_cb,
-                            peer);
+  GNUNET_ARM_request_service_start (ca_result, "regexprofiler",
+      GNUNET_OS_INHERIT_STD_NONE,
+      GNUNET_TIME_UNIT_FOREVER_REL,
+      regexprofiler_start_cb, cls);
 }
 
 

Modified: gnunet/src/regex/regex_test_lib.c
===================================================================
--- gnunet/src/regex/regex_test_lib.c   2013-03-13 17:49:17 UTC (rev 26401)
+++ gnunet/src/regex/regex_test_lib.c   2013-03-13 17:49:26 UTC (rev 26402)
@@ -267,7 +267,7 @@
   struct GNUNET_DISK_FileHandle *f;
   unsigned int nr;
   unsigned int offset;
-  off_t size;
+  uint64_t size;
   size_t len;
   char *buffer;
   char *regex;

Modified: gnunet/src/util/client.c
===================================================================
--- gnunet/src/util/client.c    2013-03-13 17:49:17 UTC (rev 26401)
+++ gnunet/src/util/client.c    2013-03-13 17:49:26 UTC (rev 26402)
@@ -1007,6 +1007,7 @@
   size_t ret;
   struct GNUNET_TIME_Relative delay;
 
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "client_notify is running\n");
   th->th = NULL;
   client->th = NULL;
   if (NULL == buf)

Modified: gnunet/src/util/connection.c
===================================================================
--- gnunet/src/util/connection.c        2013-03-13 17:49:17 UTC (rev 26401)
+++ gnunet/src/util/connection.c        2013-03-13 17:49:26 UTC (rev 26402)
@@ -1153,14 +1153,22 @@
   size_t size;
   GNUNET_CONNECTION_TransmitReadyNotify notify;
 
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "process_notify is running\n");
+
   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == connection->write_task);
   if (NULL == (notify = connection->nth.notify_ready))
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG, "Noone to notify\n");
     return GNUNET_NO;
+  }
   used = connection->write_buffer_off - connection->write_buffer_pos;
   avail = connection->write_buffer_size - used;
   size = connection->nth.notify_size;
   if (size > avail)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG, "Not enough buffer\n");
     return GNUNET_NO;
+  }
   connection->nth.notify_ready = NULL;
   if (connection->write_buffer_size - connection->write_buffer_off < size)
   {

Modified: gnunet/src/util/server.c
===================================================================
--- gnunet/src/util/server.c    2013-03-13 17:49:17 UTC (rev 26401)
+++ gnunet/src/util/server.c    2013-03-13 17:49:26 UTC (rev 26402)
@@ -111,6 +111,16 @@
   struct NotifyList *disconnect_notify_list_tail;
 
   /**
+   * Head of linked list of functions to call on connects by clients.
+   */
+  struct NotifyList *connect_notify_list_head;
+
+  /**
+   * Tail of linked list of functions to call on connects by clients.
+   */
+  struct NotifyList *connect_notify_list_tail;
+
+  /**
    * Function to call for access control.
    */
   GNUNET_CONNECTION_AccessCheck access;
@@ -756,6 +766,14 @@
                                 npos);
     GNUNET_free (npos);
   }
+  while (NULL != (npos = server->connect_notify_list_head))
+  {
+    npos->callback (npos->callback_cls, NULL);
+    GNUNET_CONTAINER_DLL_remove (server->connect_notify_list_head,
+                                server->connect_notify_list_tail,
+                                npos);
+    GNUNET_free (npos);
+  }
   GNUNET_free (server);
 }
 
@@ -1161,6 +1179,7 @@
                               struct GNUNET_CONNECTION_Handle *connection)
 {
   struct GNUNET_SERVER_Client *client;
+  struct NotifyList *n;
 
   client = GNUNET_malloc (sizeof (struct GNUNET_SERVER_Client));
   client->connection = connection;
@@ -1178,6 +1197,9 @@
     client->mst =
         GNUNET_SERVER_mst_create (&client_message_tokenizer_callback, server);
   GNUNET_assert (NULL != client->mst);
+  for (n = server->connect_notify_list_head; NULL != n; n = n->next)
+    n->callback (n->callback_cls, client);
+
   client->receive_pending = GNUNET_YES;
   GNUNET_CONNECTION_receive (client->connection,
                              GNUNET_SERVER_MAX_MESSAGE_SIZE - 1,
@@ -1277,13 +1299,37 @@
 
 
 /**
- * Ask the server to stop notifying us whenever a client disconnects.
+ * Ask the server to notify us whenever a client connects.
+ * This function is called whenever the actual network connection
+ * is opened.
  *
  * @param server the server manageing the clients
- * @param callback function to call on disconnect
+ * @param callback function to call on sconnect
  * @param callback_cls closure for callback
  */
 void
+GNUNET_SERVER_connect_notify (struct GNUNET_SERVER_Handle *server,
+    GNUNET_SERVER_ConnectCallback callback, void *callback_cls)
+{
+  struct NotifyList *n;
+
+  n = GNUNET_malloc (sizeof (struct NotifyList));
+  n->callback = callback;
+  n->callback_cls = callback_cls;
+  GNUNET_CONTAINER_DLL_insert (server->connect_notify_list_head,
+                              server->connect_notify_list_tail,
+                              n);
+}
+
+
+/**
+ * Ask the server to stop notifying us whenever a client connects.
+ *
+ * @param server the server manageing the clients
+ * @param callback function to call on connect
+ * @param callback_cls closure for callback
+ */
+void
 GNUNET_SERVER_disconnect_notify_cancel (struct GNUNET_SERVER_Handle *server,
                                         GNUNET_SERVER_DisconnectCallback
                                         callback, void *callback_cls)
@@ -1306,6 +1352,34 @@
 
 
 /**
+ * Ask the server to stop notifying us whenever a client disconnects.
+ *
+ * @param server the server manageing the clients
+ * @param callback function to call on disconnect
+ * @param callback_cls closure for callback
+ */
+void
+GNUNET_SERVER_connect_notify_cancel (struct GNUNET_SERVER_Handle *server,
+    GNUNET_SERVER_ConnectCallback callback, void *callback_cls)
+{
+  struct NotifyList *pos;
+
+  for (pos = server->connect_notify_list_head; NULL != pos; pos = pos->next)
+    if ((pos->callback == callback) && (pos->callback_cls == callback_cls))
+      break;
+  if (NULL == pos)
+  {
+    GNUNET_break (0);
+    return;
+  }
+  GNUNET_CONTAINER_DLL_remove (server->connect_notify_list_head,
+                              server->connect_notify_list_tail,
+                              pos);
+  GNUNET_free (pos);
+}
+
+
+/**
  * Destroy the connection that is passed in via 'cls'.  Used
  * as calling 'GNUNET_CONNECTION_destroy' from within a function
  * that was itself called from within 'process_notify' of




reply via email to

[Prev in Thread] Current Thread [Next in Thread]