-improve UDP logging
[oweals/gnunet.git] / src / scalarproduct / gnunet-scalarproduct.c
index 0bce750858cd66d76b21daae6e0b4019c2a61841..f44f4c518a6f1e3edf28280886b0ff901bd8f33b 100644 (file)
@@ -1,6 +1,6 @@
 /*
      This file is part of GNUnet.
-     (C) 2013 Christian Grothoff (and other contributing authors)
+     Copyright (C) 2013, 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
 
 #define LOG(kind,...) GNUNET_log_from (kind, "gnunet-scalarproduct",__VA_ARGS__)
 
-struct ScalarProductCallbackClosure
-{
-  /**
-   * the session key identifying this computation
-   */
-  struct GNUNET_HashCode key;
 
-  /**
-   * PeerID we want to compute a scalar product with
-   */
-  struct GNUNET_PeerIdentity peer;
-};
+/**
+ * the session key identifying this computation
+ */
+static struct GNUNET_HashCode session_key;
+
+/**
+ * PeerID we want to compute a scalar product with
+ */
+static struct GNUNET_PeerIdentity peer_id;
 
 /**
  * Option -p: destination peer identity for checking message-ids with
  */
-static char *input_peer_id = NULL;
+static char *input_peer_id;
 
 /**
  * Option -p: destination peer identity for checking message-ids with
  */
-static char *input_key = NULL;
+static char *input_session_key;
 
 /**
  * Option -e: vector to calculate a scalarproduct with
  */
-static char *input_elements = NULL;
+static char *input_elements;
 
 /**
- * Option -m: message-ids to calculate a scalarproduct with
+ * Global return value
  */
-static char *input_mask = NULL;
+static int ret = -1;
 
 /**
- * Global return value
+ * our Scalarproduct Computation handle
  */
-static int ret;
+static struct GNUNET_SCALARPRODUCT_ComputationHandle *computation;
 
 
 /**
  * Callback called if we are initiating a new computation session
- * 
+ *
  * @param cls unused
- * @param status if our job was successfully processed 
+ * @param status if our job was successfully processed
  */
 static void
 responder_callback (void *cls,
                     enum GNUNET_SCALARPRODUCT_ResponseStatus status)
 {
-  struct ScalarProductCallbackClosure * closure = cls;
-  ret = -1;
-
   switch (status)
   {
-  case GNUNET_SCALARPRODUCT_Status_Success:
+  case GNUNET_SCALARPRODUCT_STATUS_SUCCESS:
     ret = 0;
-    LOG (GNUNET_ERROR_TYPE_INFO, "Session %s concluded.\n", GNUNET_h2s (&closure->key));
+    LOG (GNUNET_ERROR_TYPE_INFO,
+         "Session %s concluded.\n",
+         GNUNET_h2s (&session_key));
     break;
-  case GNUNET_SCALARPRODUCT_Status_InvalidResponse:
-    LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: invalid response\n", GNUNET_h2s (&closure->key));
+  case GNUNET_SCALARPRODUCT_STATUS_INVALID_RESPONSE:
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         "Session %s failed: invalid response\n",
+         GNUNET_h2s (&session_key));
     break;
-  case GNUNET_SCALARPRODUCT_Status_Failure:
-    LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: service failure\n", GNUNET_h2s (&closure->key));
-  case GNUNET_SCALARPRODUCT_Status_ServiceDisconnected:
-    LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: service disconnect!!\n", GNUNET_h2s (&closure->key));
+  case GNUNET_SCALARPRODUCT_STATUS_FAILURE:
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         "Session %s failed: service failure\n",
+         GNUNET_h2s (&session_key));
+    break;
+  case GNUNET_SCALARPRODUCT_STATUS_DISCONNECTED:
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         "Session %s failed: service disconnect!\n",
+         GNUNET_h2s (&session_key));
     break;
   default:
-    LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: return code %d\n", GNUNET_h2s (&closure->key), status);
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         "Session %s failed: return code %d\n",
+         GNUNET_h2s (&session_key),
+         status);
   }
-
-  GNUNET_SCALARPRODUCT_disconnect ();
+  computation = NULL;
   GNUNET_SCHEDULER_shutdown ();
 }
 
 
 /**
  * Callback called if we are initiating a new computation session
- * 
+ *
  * @param cls unused
- * @param key unused
- * @param peer unused
- * @param status if our job was successfully processed 
- * @param size size of the msg returned
- * @param msg the response we got.
- * @param type of the message received 
+ * @param status if our job was successfully processed
+ * @param result the result in gnu/gcry MPI format
  */
 static void
 requester_callback (void *cls,
                     enum GNUNET_SCALARPRODUCT_ResponseStatus status,
                     gcry_mpi_t result)
 {
-  struct ScalarProductCallbackClosure * closure = cls;
-  unsigned char * buf;
+  unsigned char *buf;
   gcry_error_t rc;
-  ret = -1;
 
   switch (status)
   {
-  case GNUNET_SCALARPRODUCT_Status_Success:
-
+  case GNUNET_SCALARPRODUCT_STATUS_SUCCESS:
     if (0 == (rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buf, NULL, result)))
-      printf ("Successfully computed result for session %s with peer %s: %s\n", GNUNET_h2s (&closure->key), GNUNET_i2s (&closure->peer), buf);
-    else {
-      printf ("Session %s with peer %s failed\n", GNUNET_h2s (&closure->key), GNUNET_i2s (&closure->peer));
-      LOG_GCRY(GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_aprint", rc);
+    {
+      ret = 0;
+      fprintf (stdout,
+               "%s\n",
+               buf);
+      fflush (stdout);
     }
+    else
+      LOG_GCRY (GNUNET_ERROR_TYPE_ERROR,
+                "gcry_mpi_aprint",
+                rc);
+    break;
+  case GNUNET_SCALARPRODUCT_STATUS_INVALID_RESPONSE:
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         "Session %s with peer %s failed: invalid response received\n",
+         GNUNET_h2s (&session_key),
+         GNUNET_i2s (&peer_id));
     break;
-  case GNUNET_SCALARPRODUCT_Status_InvalidResponse:
-    LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s with peer %s failed: invalid response received\n", GNUNET_h2s (&closure->key), GNUNET_i2s (&closure->peer));
+  case GNUNET_SCALARPRODUCT_STATUS_FAILURE:
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         "Session %s with peer %s failed: API failure\n",
+         GNUNET_h2s (&session_key),
+         GNUNET_i2s (&peer_id));
     break;
-  case GNUNET_SCALARPRODUCT_Status_Failure:
-    LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s with peer %s failed: API failure\n", GNUNET_h2s (&closure->key), GNUNET_i2s (&closure->peer));
-  case GNUNET_SCALARPRODUCT_Status_ServiceDisconnected:
-    LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s with peer %s was disconnected from service.\n", GNUNET_h2s (&closure->key), GNUNET_i2s (&closure->peer));
+  case GNUNET_SCALARPRODUCT_STATUS_DISCONNECTED:
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         "Session %s with peer %s was disconnected from service.\n",
+         GNUNET_h2s (&session_key),
+         GNUNET_i2s (&peer_id));
     break;
   default:
-    LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s with peer %s failed: return code %d\n", GNUNET_h2s (&closure->key), GNUNET_i2s (&closure->peer), status);
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         "Session %s with peer %s failed: return code %d\n",
+         GNUNET_h2s (&session_key),
+         GNUNET_i2s (&peer_id),
+         status);
   }
-  GNUNET_SCALARPRODUCT_disconnect ();
+  computation = NULL;
   GNUNET_SCHEDULER_shutdown ();
 }
 
 
+/**
+ * Task run during shutdown.
+ *
+ * @param cls unused
+ * @param tc unused
+ */
+static void
+shutdown_task (void *cls,
+               const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  if (NULL != computation)
+  {
+    GNUNET_SCALARPRODUCT_cancel (computation);
+    ret = 1; /* aborted */
+  }
+}
+
+
 /**
  * Main function that will be run by the scheduler.
  *
@@ -172,180 +209,161 @@ run (void *cls,
      const char *cfgfile,
      const struct GNUNET_CONFIGURATION_Handle *cfg)
 {
-  char * begin = input_elements;
-  char * end;
-  int32_t element;
-  int i;
-  ret = -1;
-  int32_t * elements;
-  unsigned char * mask;
-  unsigned short mask_bytes;
-  unsigned short element_count;
-  struct ScalarProductCallbackClosure * closure;
+  char *begin = input_elements;
+  char *end;
+  unsigned int i;
+  struct GNUNET_SCALARPRODUCT_Element *elements;
+  uint32_t element_count = 0;
 
   if (NULL == input_elements)
   {
-    FPRINTF (stderr, "%s", _ ("You must specify at least one message ID to check!\n"));
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         _("You must specify at least one message ID to check!\n"));
     return;
   }
-
-  if (NULL == input_key)
+  if ( (NULL == input_session_key) ||
+       (0 == strlen (input_session_key)) )
   {
-    FPRINTF (stderr, "%s", _ ("This program needs a session identifier for comparing vectors.\n"));
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         _("This program needs a session identifier for comparing vectors.\n"));
     return;
   }
-
-  if (1 > strnlen (input_key, sizeof (struct GNUNET_HashCode)))
+  GNUNET_CRYPTO_hash (input_session_key,
+                      strlen (input_session_key),
+                      &session_key);
+  if ( (NULL != input_peer_id) &&
+       (GNUNET_OK !=
+        GNUNET_CRYPTO_eddsa_public_key_from_string (input_peer_id,
+                                                    strlen (input_peer_id),
+                                                    &peer_id.public_key)) )
   {
-    FPRINTF (stderr, _ ("Please give a session key for --input_key!\n"));
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         _("Tried to set initiator mode, as peer ID was given. "
+           "However, `%s' is not a valid peer identifier.\n"),
+         input_peer_id);
     return;
   }
-  closure = GNUNET_new(struct ScalarProductCallbackClosure);
-  GNUNET_CRYPTO_hash (input_key, strlen (input_key), &closure->key);
-
-  if (input_peer_id && GNUNET_OK != GNUNET_CRYPTO_hash_from_string (input_peer_id,
-                                                                    (struct GNUNET_HashCode *) &closure->peer))
+  if ( ('\'' == *begin) &&
+       ('\'' == begin[strlen(begin)-1]) )
+  {
+    begin[strlen(begin)-1] = '\0';
+    if (strlen (begin) > 0)
+      begin++;
+  }
+  for (end = begin; 0 != *end; end++)
+    if (*end == ';')
+      element_count++;
+  if (0 == element_count)
   {
-    FPRINTF (stderr, _ ("Tried to set initiator mode, as peer ID was given. "
-                        "However, `%s' is not a valid peer identifier.\n"),
-             input_peer_id);
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         _("Need elements to compute the scalarproduct, got none.\n"));
     return;
   }
 
-  int exit_loop = 0;
-  /* Read input_elements_peer1, and put in elements_peer1 array */
-  do
-  {
-    unsigned int mcount = element_count;
-    //ignore empty rows of ,,,,,,
-    while (*begin == ',')
-      begin++;
-    // get the length of the current element and replace , with null
-    for (end = begin; *end && *end != ','; end++);
+  elements = GNUNET_malloc (sizeof(struct GNUNET_SCALARPRODUCT_Element) * element_count);
 
-    if (*end == '\0')
-      exit_loop = 1;
+  for (i = 0; i < element_count;i++)
+  {
+    struct GNUNET_SCALARPRODUCT_Element element;
+    char* separator = NULL;
 
-    if (*end == ',')
-      *end = '\0';
+    /* get the length of the current key,value; tupel */
+    for (end = begin; *end != ';'; end++)
+      if (*end == ',')
+        separator = end;
 
-    if (1 != sscanf (begin, "%" SCNd32, &element))
+    /* final element */
+    if ( (NULL == separator) ||
+         (begin == separator) ||
+         (separator == end - 1) )
     {
-      FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin);
+      LOG (GNUNET_ERROR_TYPE_ERROR,
+           _("Malformed input, could not parse `%s'\n"),
+           begin);
+      GNUNET_free (elements);
       return;
     }
-
-    GNUNET_array_append (elements, mcount, element);
-    element_count++;
-
-    begin = ++end;
-  }
-  while (!exit_loop);
-
-  GNUNET_assert (elements != NULL);
-  GNUNET_assert (element_count > 1);
-  mask_bytes = element_count / 8 + (element_count % 8 ? 1 : 0);
-  mask = GNUNET_malloc ((element_count / 8) + 2);
-
-  /* Read input_mask_peer1 and read in mask_peer1 array */
-  if (NULL != input_mask)
-  {
-    begin = input_mask;
-    unsigned short mask_count = 0;
-    int exit_loop = 0;
-
-    do
+    *separator = 0;
+    /* read the element's key */
+    GNUNET_CRYPTO_hash (begin,
+                        strlen (begin),
+                        &element.key);
+
+    /* read the element's value */
+    if (1 !=
+        sscanf (separator + 1,
+                "%" SCNd64 ";",
+                &element.value) )
     {
-      //ignore empty rows of ,,,,,,
-      while (* begin == ',')
-        begin++;
-      // get the length of the current element and replace , with null
-      // gnunet_ascii-armor uses base32, thus we can use , as separator!
-      for (end = begin; *end && *end != ','; end++);
-
-      if (*end == '\0')
-        exit_loop = 1;
-
-      if (*end == ',')
-        *end = '\0';
-
-      if (1 != sscanf (begin, "%" SCNd32, &element))
-      {
-        FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin);
-        return;
-      }
-
-      GNUNET_assert (mask_count <= element_count);
-
-      if (element)
-        mask[mask_count / 8] = mask[mask_count / 8] | 1 << (mask_count % 8);
-
-      mask_count++;
-      begin = ++end;
+      LOG (GNUNET_ERROR_TYPE_ERROR,
+           _("Could not convert `%s' to int64_t.\n"),
+           begin);
+      GNUNET_free (elements);
+      return;
     }
-    while (!exit_loop);
-    // +1 to see if we would have more data, which would indicate malformed/superficial input
-    GNUNET_assert (mask_count == element_count);
-  }
-  else if (input_peer_id)
-  {
-    for (i = 0; i <= mask_bytes; i++)
-      mask[i] = UCHAR_MAX; // all 1's
+    element.value = GNUNET_htonll (element.value);
+    elements[i] = element;
+    begin = end + 1;
   }
 
-
-  if (input_peer_id && !GNUNET_SCALARPRODUCT_request (cfg,
-                                                      &closure->key,
-                                                      &closure->peer,
-                                                      elements, element_count,
-                                                      mask, mask_bytes,
-                                                      &requester_callback,
-                                                      (void *) &closure))
-    return;
-
-
-  if (!input_peer_id && !GNUNET_SCALARPRODUCT_response (cfg,
-                                                        &closure->key,
-                                                        elements, element_count,
-                                                        &responder_callback,
-                                                        (void *) &closure))
+  if ( ( (NULL != input_peer_id) &&
+         (NULL == (computation
+                   = GNUNET_SCALARPRODUCT_start_computation (cfg,
+                                                             &session_key,
+                                                             &peer_id,
+                                                             elements, element_count,
+                                                             &requester_callback,
+                                                             NULL))) ) ||
+       ( (NULL == input_peer_id) &&
+         (NULL == (computation
+                   = GNUNET_SCALARPRODUCT_accept_computation (cfg,
+                                                              &session_key,
+                                                              elements, element_count,
+                                                              &responder_callback,
+                                                              NULL))) ) )
+  {
+    fprintf (stderr,
+             _("Failed to initiate computation, were all keys unique?\n"));
+    GNUNET_free (elements);
     return;
-
+  }
+  GNUNET_free (elements);
+  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
+                                &shutdown_task,
+                                NULL);
   ret = 0;
 }
 
 
-      /**
+/**
  * The main function to the scalarproduct client.
  *
  * @param argc number of arguments from the command line
  * @param argv command line arguments
  * @return 0 ok, 1 on error
  */
-      int
+int
 main (int argc, char *const *argv)
 {
-      static const struct GNUNET_GETOPT_CommandLineOption options[] = {
-    {'e', "elements", "\"val1,val2,...,valn\"",
+  static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+    {'e', "elements", "\"key1,val1;key2,val2;...,keyn,valn;\"",
       gettext_noop ("A comma separated list of elements to compare as vector with our remote peer."),
       1, &GNUNET_GETOPT_set_string, &input_elements},
-    {'m', "mask", "\"0,1,...,maskn\"",
-      gettext_noop ("A comma separated mask to select which elements should actually be compared."),
-      1, &GNUNET_GETOPT_set_string, &input_mask},
     {'p', "peer", "PEERID",
       gettext_noop ("[Optional] peer to calculate our scalarproduct with. If this parameter is not given, the service will wait for a remote peer to compute the request."),
       1, &GNUNET_GETOPT_set_string, &input_peer_id},
     {'k', "key", "TRANSACTION_ID",
       gettext_noop ("Transaction ID shared with peer."),
-      1, &GNUNET_GETOPT_set_string, &input_key},
-      GNUNET_GETOPT_OPTION_END
+      1, &GNUNET_GETOPT_set_string, &input_session_key},
+    GNUNET_GETOPT_OPTION_END
   };
 
-      return (GNUNET_OK ==
-              GNUNET_PROGRAM_run (argc,
-                                  argv,
-                                  "gnunet-scalarproduct",
-                                  gettext_noop ("Calculate the Vectorproduct with a GNUnet peer."),
-                                  options, &run, NULL)) ? ret : 1;
+  return (GNUNET_OK ==
+          GNUNET_PROGRAM_run (argc,
+                              argv,
+                              "gnunet-scalarproduct",
+                              gettext_noop ("Calculate the Vectorproduct with a GNUnet peer."),
+                              options, &run, NULL)) ? ret : 1;
 }
 
+/* end of gnunet-scalarproduct.c */