towards fixing #3363: replacing old iteration API with new monitoring API for core...
[oweals/gnunet.git] / src / core / core_api_iterate_peers.c
index 6a4e7858d284ee394ec83572ea060ed6ff2c5d11..425d3579c3fb98cc34776add062a2b8a004f6921 100644 (file)
@@ -1,6 +1,6 @@
 /*
      This file is part of GNUnet.
-     (C) 2009, 2010 Christian Grothoff (and other contributing authors)
+     (C) 2009-2014 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
@@ -19,7 +19,7 @@
 */
 
 /**
- * @file core/core_api_iterate_peers.c
+ * @file core/core_api_monitor_peers.c
  * @brief implementation of the peer_iterate function
  * @author Christian Grothoff
  * @author Nathan Evans
 #include "core.h"
 
 
-struct GNUNET_CORE_RequestContext
+/**
+ * Handle to a CORE monitoring operation.
+ */
+struct GNUNET_CORE_MonitorHandle
 {
-  
+
+  /**
+   * Our configuration.
+   */
+  const struct GNUNET_CONFIGURATION_Handle *cfg;
+
   /**
    * Our connection to the service.
    */
@@ -45,140 +53,200 @@ struct GNUNET_CORE_RequestContext
   /**
    * Function called with the peer.
    */
-  GNUNET_CORE_ConnectEventHandler peer_cb;
+  GNUNET_CORE_MonitorCallback peer_cb;
 
   /**
-   * Closure for peer_cb.
+   * Closure for @e peer_cb.
    */
-  void *cb_cls;
+  void *peer_cb_cls;
 
 };
 
 
 /**
- * Receive reply from core service with information about a peer.
+ * Transmits the monitor request to the CORE service.
+ *
+ * Function is called to notify a client about the socket begin ready
+ * to queue more data.  @a buf will be NULL and @a size zero if the
+ * socket was closed for writing in the meantime.
+ *
+ * @param cls closure, our `struct GNUNET_CORE_MonitorHandle *`
+ * @param size number of bytes available in @a buf
+ * @param buf where the callee should write the message
+ * @return number of bytes written to @a buf
+ */
+static size_t
+transmit_monitor_request (void *cls,
+                          size_t size,
+                          void *buf);
+
+
+/**
+ * Protocol error, reconnect to CORE service and notify
+ * client.
+ *
+ * @param mh monitoring session to reconnect to CORE
+ */
+static void
+reconnect (struct GNUNET_CORE_MonitorHandle *mh)
+{
+  GNUNET_CLIENT_disconnect (mh->client);
+  /* FIXME: use backoff? */
+  mh->client = GNUNET_CLIENT_connect ("core", mh->cfg);
+  GNUNET_assert (NULL != mh->client);
+  mh->th =
+    GNUNET_CLIENT_notify_transmit_ready (mh->client,
+                                         sizeof (struct GNUNET_MessageHeader),
+                                         GNUNET_TIME_UNIT_FOREVER_REL,
+                                         GNUNET_YES,
+                                         &transmit_monitor_request, mh);
+  /* notify callback about reconnect */
+  mh->peer_cb (mh->peer_cb_cls,
+               NULL,
+               GNUNET_CORE_KX_CORE_DISCONNECT,
+               GNUNET_TIME_UNIT_FOREVER_ABS);
+}
+
+
+/**
+ * Receive reply from CORE service with information about a peer.
  *
- * @param cls our 'struct  GNUNET_CORE_RequestContext *'
+ * @param cls our `struct  GNUNET_CORE_MonitorHandle *`
  * @param msg NULL on error or last entry
  */
 static void
 receive_info (void *cls,
-             const struct GNUNET_MessageHeader *msg)
+              const struct GNUNET_MessageHeader *msg)
 {
-  struct GNUNET_CORE_RequestContext *request_context = cls;
-  const struct ConnectNotifyMessage *connect_message;
-  uint32_t ats_count;
+  struct GNUNET_CORE_MonitorHandle *mh = cls;
+  const struct MonitorNotifyMessage *mon_message;
   uint16_t msize;
 
-  /* Handle last message or error case, disconnect and clean up */
-  if ( (msg == NULL) ||
-      ((ntohs (msg->type) == GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS_END) &&
-      (ntohs (msg->size) == sizeof (struct GNUNET_MessageHeader))) )
-    {
-      if (request_context->peer_cb != NULL)
-       request_context->peer_cb (request_context->cb_cls,
-                                  NULL, NULL);
-      GNUNET_CLIENT_disconnect (request_context->client, GNUNET_NO);
-      GNUNET_free (request_context);
-      return;
-    }
-
+  if (NULL == msg)
+  {
+    reconnect (mh);
+    return;
+  }
   msize = ntohs (msg->size);
   /* Handle incorrect message type or size, disconnect and clean up */
-  if ( (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT) ||
-       (msize < sizeof (struct ConnectNotifyMessage)) )
-    {
-      GNUNET_break (0);
-      if (request_context->peer_cb != NULL)
-        request_context->peer_cb (request_context->cb_cls,
-                                  NULL, NULL);
-      GNUNET_CLIENT_disconnect (request_context->client, GNUNET_NO);
-      GNUNET_free (request_context);
-      return;
-    }
-  connect_message = (const struct ConnectNotifyMessage *) msg;
-  ats_count = ntohl (connect_message->ats_count);
-  if ( (msize != sizeof (struct ConnectNotifyMessage) + ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information)) ||
-       (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR != ntohl ((&connect_message->ats)[ats_count].type)) )
-    {
-      GNUNET_break (0);
-      if (request_context->peer_cb != NULL)
-        request_context->peer_cb (request_context->cb_cls,
-                                  NULL, NULL);
-      GNUNET_CLIENT_disconnect (request_context->client, GNUNET_NO);
-      GNUNET_free (request_context);
-      return;
-    }
-  /* Normal case */
-  if (request_context->peer_cb != NULL)
-    request_context->peer_cb (request_context->cb_cls,
-                              &connect_message->peer,
-                              &connect_message->ats);
-  GNUNET_CLIENT_receive(request_context->client, 
-                       &receive_info, 
-                       request_context,
-                       GNUNET_TIME_UNIT_FOREVER_REL);
+  if ((ntohs (msg->type) != GNUNET_MESSAGE_TYPE_CORE_MONITOR_NOTIFY) ||
+      (sizeof (struct MonitorNotifyMessage) != msize))
+  {
+    GNUNET_break (0);
+    reconnect (mh);
+    return;
+  }
+  mon_message = (const struct MonitorNotifyMessage *) msg;
+  GNUNET_CLIENT_receive (mh->client,
+                         &receive_info, mh,
+                         GNUNET_TIME_UNIT_FOREVER_REL);
+  mh->peer_cb (mh->peer_cb_cls,
+               &mon_message->peer,
+               (enum GNUNET_CORE_KxState) ntohl (mon_message->state),
+               GNUNET_TIME_absolute_ntoh (mon_message->timeout));
 }
 
+
 /**
- * Function called to notify a client about the socket
- * begin ready to queue more data.  "buf" will be
- * NULL and "size" zero if the socket was closed for
- * writing in the meantime.
+ * Transmits the monitor request to the CORE service.
+ *
+ * Function is called to notify a client about the socket begin ready
+ * to queue more data.  @a buf will be NULL and @a size zero if the
+ * socket was closed for writing in the meantime.
  *
- * @param cls closure
- * @param size number of bytes available in buf
+ * @param cls closure, our `struct GNUNET_CORE_MonitorHandle *`
+ * @param size number of bytes available in @a buf
  * @param buf where the callee should write the message
- * @return number of bytes written to buf
+ * @return number of bytes written to @a buf
  */
 static size_t
-transmit_request(void *cls,
-                 size_t size, void *buf)
+transmit_monitor_request (void *cls,
+                          size_t size,
+                          void *buf)
 {
+  struct GNUNET_CORE_MonitorHandle *mh = cls;
   struct GNUNET_MessageHeader *msg;
-  if ((size < sizeof(struct GNUNET_MessageHeader)) || (buf == NULL))
-    return 0;
+  int msize;
 
-  msg = (struct GNUNET_MessageHeader *)buf;
-  msg->size = htons (sizeof (struct GNUNET_MessageHeader));
-  msg->type = htons (GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS);
-  return sizeof(struct GNUNET_MessageHeader);
+  msize = sizeof (struct GNUNET_MessageHeader);
+  if ((size < msize) || (NULL == buf))
+  {
+    reconnect (mh);
+    return 0;
+  }
+  msg = (struct GNUNET_MessageHeader *) buf;
+  msg->size = htons (msize);
+  msg->type = htons (GNUNET_MESSAGE_TYPE_CORE_MONITOR_PEERS);
+  GNUNET_CLIENT_receive (mh->client,
+                         &receive_info, mh,
+                         GNUNET_TIME_UNIT_FOREVER_REL);
+  return msize;
 }
 
+
 /**
- * Obtain statistics and/or change preferences for the given peer.
+ * Monitor connectivity and KX status of all peers known to CORE.
+ * Calls @a peer_cb with the current status for each connected peer,
+ * and then once with NULL to indicate that all peers that are
+ * currently active have been handled.  After that, the iteration
+ * continues until it is cancelled.  Normal users of the CORE API are
+ * not expected to use this function.  It is different in that it
+ * truly lists all connections (including those where the KX is in
+ * progress), not just those relevant to the application.  This
+ * function is used by special applications for diagnostics.
  *
- * @param sched scheduler to use
- * @param cfg configuration to use
+ * @param cfg configuration handle
  * @param peer_cb function to call with the peer information
- * @param cb_cls closure for peer_cb
- * @return GNUNET_OK if iterating, GNUNET_SYSERR on error
+ * @param peer_cb_cls closure for @a peer_cb
+ * @return NULL on error
  */
-int
-GNUNET_CORE_iterate_peers (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                           GNUNET_CORE_ConnectEventHandler peer_cb,
-                           void *cb_cls)
+struct GNUNET_CORE_MonitorHandle *
+GNUNET_CORE_monitor_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
+                           GNUNET_CORE_MonitorCallback peer_cb,
+                           void *peer_cb_cls)
 {
-  struct GNUNET_CORE_RequestContext *request_context;
+  struct GNUNET_CORE_MonitorHandle *mh;
   struct GNUNET_CLIENT_Connection *client;
 
+  GNUNET_assert (NULL != peer_cb);
   client = GNUNET_CLIENT_connect ("core", cfg);
-  if (client == NULL)
-    return GNUNET_SYSERR;
-  request_context = GNUNET_malloc (sizeof (struct GNUNET_CORE_RequestContext));
-  request_context->client = client;
-  request_context->peer_cb = peer_cb;
-  request_context->cb_cls = cb_cls;
-
-  request_context->th = GNUNET_CLIENT_notify_transmit_ready(client,
-                                                            sizeof(struct GNUNET_MessageHeader),
-                                                            GNUNET_TIME_relative_get_forever(),
-                                                            GNUNET_YES,
-                                                            &transmit_request,
-                                                            NULL);
-
-  GNUNET_CLIENT_receive(client, &receive_info, request_context, GNUNET_TIME_relative_get_forever());
-  return GNUNET_OK;
+  if (NULL == client)
+    return NULL;
+  mh = GNUNET_new (struct GNUNET_CORE_MonitorHandle);
+  mh->cfg = cfg;
+  mh->client = client;
+  mh->peer_cb = peer_cb;
+  mh->peer_cb_cls = peer_cb_cls;
+  mh->th =
+    GNUNET_CLIENT_notify_transmit_ready (client,
+                                         sizeof (struct GNUNET_MessageHeader),
+                                         GNUNET_TIME_UNIT_FOREVER_REL,
+                                         GNUNET_YES,
+                                         &transmit_monitor_request, mh);
+  return mh;
 }
 
-/* end of core_api_iterate_peers.c */
+
+/**
+ * Stop monitoring CORE activity.
+ *
+ * @param mh monitor to stop
+ */
+void
+GNUNET_CORE_monitor_stop (struct GNUNET_CORE_MonitorHandle *mh)
+{
+  if (NULL != mh->th)
+  {
+    GNUNET_CLIENT_notify_transmit_ready_cancel (mh->th);
+    mh->th = NULL;
+  }
+  if (NULL != mh->client)
+  {
+    GNUNET_CLIENT_disconnect (mh->client);
+    mh->client = NULL;
+  }
+  GNUNET_free (mh);
+}
+
+
+/* end of core_api_monitor_peers.c */