towards fixing #3363: replacing old iteration API with new monitoring API for core...
[oweals/gnunet.git] / src / core / core_api_iterate_peers.c
index 0ecd98ed71bc2b032c3bc2da9b50addd6e10524a..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.
    */
@@ -44,204 +53,200 @@ struct GNUNET_CORE_RequestContext
   /**
    * Function called with the peer.
    */
-  GNUNET_CORE_ConnectEventHandler peer_cb;
+  GNUNET_CORE_MonitorCallback peer_cb;
 
   /**
-   * Peer to check for.
+   * Closure for @e peer_cb.
    */
-  struct GNUNET_PeerIdentity *peer;
-
-  /**
-   * Closure for 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 cls our 'struct  GNUNET_CORE_RequestContext *'
+ * @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_MonitorHandle *`
  * @param msg NULL on error or last entry
  */
 static void
-receive_info (void *cls, const struct GNUNET_MessageHeader *msg)
+receive_info (void *cls,
+              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 (NULL == msg)
   {
-    if (request_context->peer_cb != NULL)
-      request_context->peer_cb (request_context->cb_cls, NULL, NULL, 0);
-    GNUNET_CLIENT_disconnect (request_context->client);
-    GNUNET_free (request_context);
+    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)))
+  if ((ntohs (msg->type) != GNUNET_MESSAGE_TYPE_CORE_MONITOR_NOTIFY) ||
+      (sizeof (struct MonitorNotifyMessage) != msize))
   {
     GNUNET_break (0);
-    if (request_context->peer_cb != NULL)
-      request_context->peer_cb (request_context->cb_cls, NULL, NULL, 0);
-    GNUNET_CLIENT_disconnect (request_context->client);
-    GNUNET_free (request_context);
+    reconnect (mh);
     return;
   }
-  connect_message = (const struct ConnectNotifyMessage *) msg;
-  ats_count = ntohl (connect_message->ats_count);
-  if (msize !=
-      sizeof (struct ConnectNotifyMessage) +
-      ats_count * sizeof (struct GNUNET_ATS_Information))
-  {
-    GNUNET_break (0);
-    if (request_context->peer_cb != NULL)
-      request_context->peer_cb (request_context->cb_cls, NULL, NULL, 0);
-    GNUNET_CLIENT_disconnect (request_context->client);
-    GNUNET_free (request_context);
-    return;
-  }
-  /* Normal case */
-  if (request_context->peer_cb != NULL)
-    request_context->peer_cb (request_context->cb_cls, &connect_message->peer,
-                              (const struct GNUNET_ATS_Information *)
-                              &connect_message[1], ats_count);
-  GNUNET_CLIENT_receive (request_context->client, &receive_info,
-                         request_context, GNUNET_TIME_UNIT_FOREVER_REL);
+  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;
-  struct GNUNET_PeerIdentity *peer = cls;
   int msize;
 
-  if (peer == NULL)
-    msize = sizeof (struct GNUNET_MessageHeader);
-  else
-    msize =
-        sizeof (struct GNUNET_MessageHeader) +
-        sizeof (struct GNUNET_PeerIdentity);
-
-  if ((size < msize) || (buf == NULL))
+  msize = sizeof (struct GNUNET_MessageHeader);
+  if ((size < msize) || (NULL == buf))
+  {
+    reconnect (mh);
     return 0;
-
+  }
   msg = (struct GNUNET_MessageHeader *) buf;
   msg->size = htons (msize);
-  if (peer != NULL)
-  {
-    msg->type = htons (GNUNET_MESSAGE_TYPE_CORE_PEER_CONNECTED);
-    memcpy (&msg[1], peer, sizeof (struct GNUNET_PeerIdentity));
-  }
-  else
-    msg->type = htons (GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS);
-
+  msg->type = htons (GNUNET_MESSAGE_TYPE_CORE_MONITOR_PEERS);
+  GNUNET_CLIENT_receive (mh->client,
+                         &receive_info, mh,
+                         GNUNET_TIME_UNIT_FOREVER_REL);
   return msize;
 }
 
+
 /**
- * Iterate over all currently connected peers.
- * Calls peer_cb with each connected peer, and then
- * once with NULL to indicate that all peers have
- * been handled.
+ * 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 cfg configuration to use
- * @param peer the specific peer to check for
+ * @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_is_peer_connected (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                               struct GNUNET_PeerIdentity *peer,
-                               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;
-  GNUNET_assert (peer != NULL);
-  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->peer = peer;
-
-  request_context->th =
-      GNUNET_CLIENT_notify_transmit_ready (client,
-                                           sizeof (struct GNUNET_MessageHeader)
-                                           +
-                                           sizeof (struct GNUNET_PeerIdentity),
-                                           GNUNET_TIME_relative_get_forever (),
-                                           GNUNET_YES, &transmit_request, peer);
-  GNUNET_assert (request_context->th != 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;
 }
 
+
 /**
- * Iterate over all currently connected peers.
- * Calls peer_cb with each connected peer, and then
- * once with NULL to indicate that all peers have
- * been handled.
+ * Stop monitoring CORE activity.
  *
- * @param cfg configuration to use
- * @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 mh monitor to stop
  */
-int
-GNUNET_CORE_iterate_peers (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                           GNUNET_CORE_ConnectEventHandler peer_cb,
-                           void *cb_cls)
+void
+GNUNET_CORE_monitor_stop (struct GNUNET_CORE_MonitorHandle *mh)
 {
-  struct GNUNET_CORE_RequestContext *request_context;
-  struct GNUNET_CLIENT_Connection *client;
-
-  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 != 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_iterate_peers.c */
+
+/* end of core_api_monitor_peers.c */