trying to fix #3576
[oweals/gnunet.git] / src / fs / gnunet-daemon-fsprofiler.c
index e83b3e6f2f543a604a3b804855ce63552ef21aed..1549972a1cedbcbbe87c0efeeb27888fc256408d 100644 (file)
@@ -22,6 +22,9 @@
  * @file fs/gnunet-daemon-fsprofiler.c
  * @brief daemon that publishes and downloads (random) files
  * @author Christian Grothoff
+ *
+ * TODO:
+ * - how to signal driver that we're done?
  */
 #include "platform.h"
 #include "gnunet_fs_service.h"
@@ -68,6 +71,11 @@ struct Pattern
    */
   GNUNET_SCHEDULER_TaskIdentifier task;
 
+  /**
+   * Secondary task to run the operation.
+   */
+  GNUNET_SCHEDULER_TaskIdentifier stask;
+
   /**
    * X-value.
    */
@@ -156,7 +164,7 @@ static struct Pattern *download_tail;
 
 
 /**
- * Parse a pattern string and store the corresponding 
+ * Parse a pattern string and store the corresponding
  * 'struct Pattern' in the given head/tail.
  *
  * @param head where to store the head
@@ -178,10 +186,10 @@ parse_pattern (struct Pattern **head,
                      "(%llu,%llu,%llu)",
                      &x, &y, &t))
   {
-    p = GNUNET_malloc (sizeof (struct Pattern));
+    p = GNUNET_new (struct Pattern);
     p->x = x;
     p->y = y;
-    p->delay.rel_value = (uint64_t) t;
+    p->delay.rel_value_us = (uint64_t) t;
     GNUNET_CONTAINER_DLL_insert (*head, *tail, p);
     pattern = strstr (pattern, ")");
     GNUNET_assert (NULL != pattern);
@@ -191,6 +199,23 @@ parse_pattern (struct Pattern **head,
 }
 
 
+/**
+ * Create a KSK URI from a number.
+ *
+ * @param kval the number
+ * @return corresponding KSK URI
+ */
+static struct GNUNET_FS_Uri *
+make_keywords (uint64_t kval)
+{
+  char kw[128];
+
+  GNUNET_snprintf (kw, sizeof (kw),
+                  "%llu", (unsigned long long) kval);
+  return GNUNET_FS_uri_ksk_create (kw, NULL);
+}
+
+
 /**
  * Create a file of the given length with a deterministic amount
  * of data to be published under keyword 'kval'.
@@ -209,7 +234,6 @@ make_file (uint64_t length,
   struct GNUNET_FS_BlockOptions bo;
   char *data;
   struct GNUNET_FS_Uri *keywords;
-  char kw[128];
   unsigned long long i;
   uint64_t xor;
 
@@ -219,7 +243,7 @@ make_file (uint64_t length,
       return NULL;
   /* initialize data with 'unique' data only depending on 'kval' and 'size',
      making sure that blocks do not repeat */
-  for (i=0;i<length; i+=8)  
+  for (i=0;i<length; i+=8)
   {
     xor = length ^ kval ^ (uint64_t) (i / 32 / 1024);
     memcpy (&data[i], &xor, GNUNET_MIN (length - i, sizeof (uint64_t)));
@@ -228,9 +252,7 @@ make_file (uint64_t length,
   bo.anonymity_level = (uint32_t) anonymity_level;
   bo.content_priority = 128;
   bo.replication_level = (uint32_t) replication_level;
-  GNUNET_snprintf (kw, sizeof (kw),
-                  "%llu", (unsigned long long) kval);
-  keywords = GNUNET_FS_uri_ksk_create (kw, NULL);
+  keywords = make_keywords (kval);
   fi = GNUNET_FS_file_information_create_from_data (fs_handle,
                                                    ctx,
                                                    length,
@@ -265,6 +287,8 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   {
     if (GNUNET_SCHEDULER_NO_TASK != p->task)
       GNUNET_SCHEDULER_cancel (p->task);
+    if (GNUNET_SCHEDULER_NO_TASK != p->stask)
+      GNUNET_SCHEDULER_cancel (p->stask);
     if (NULL != p->ctx)
       GNUNET_FS_download_stop (p->ctx, GNUNET_YES);
     if (NULL != p->sctx)
@@ -285,6 +309,54 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 }
 
 
+/**
+ * Task run when a publish operation should be stopped.
+ *
+ * @param cls the 'struct Pattern' of the publish operation to stop
+ * @param tc unused
+ */
+static void
+publish_stop_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct Pattern *p = cls;
+
+  p->task = GNUNET_SCHEDULER_NO_TASK;
+  GNUNET_FS_publish_stop (p->ctx);
+}
+
+
+/**
+ * Task run when a download operation should be stopped.
+ *
+ * @param cls the 'struct Pattern' of the download operation to stop
+ * @param tc unused
+ */
+static void
+download_stop_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct Pattern *p = cls;
+
+  p->task = GNUNET_SCHEDULER_NO_TASK;
+  GNUNET_FS_download_stop (p->ctx, GNUNET_YES);
+}
+
+
+/**
+ * Task run when a download operation should be stopped.
+ *
+ * @param cls the 'struct Pattern' of the download operation to stop
+ * @param tc unused
+ */
+static void
+search_stop_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct Pattern *p = cls;
+
+  p->stask = GNUNET_SCHEDULER_NO_TASK;
+  GNUNET_FS_search_stop (p->sctx);
+}
+
+
 /**
  * Notification of FS to a client about the progress of an
  * operation.  Callbacks of this type will be used for uploads,
@@ -304,13 +376,121 @@ static void *
 progress_cb (void *cls,
             const struct GNUNET_FS_ProgressInfo *info)
 {
-  // FIXME:
-  // - search result => start download
-  // - publishing done => staitstic, terminate 'struct Pattern'
-  // - download done => statistic, terminate 'struct Pattern'
-  // => all patterns done => then what!? (how do we tell the
-  //    drive that we are done!?)
-  return NULL;
+  struct Pattern *p;
+  const struct GNUNET_FS_Uri *uri;
+
+  switch (info->status)
+  {
+  case GNUNET_FS_STATUS_PUBLISH_START:
+  case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
+    p = info->value.publish.cctx;
+    return p;
+  case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
+    p = info->value.publish.cctx;
+    return p;
+  case GNUNET_FS_STATUS_PUBLISH_ERROR:
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+               "Publishing failed\n");
+    GNUNET_STATISTICS_update (stats_handle,
+                             "# failed publish operations", 1, GNUNET_NO);
+    p = info->value.publish.cctx;
+    p->task = GNUNET_SCHEDULER_add_now (&publish_stop_task, p);
+    return p;
+  case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
+    p = info->value.publish.cctx;
+    GNUNET_STATISTICS_update (stats_handle,
+                             "# publishing time (ms)",
+                             (long long) GNUNET_TIME_absolute_get_duration (p->start_time).rel_value_us / 1000LL,
+                             GNUNET_NO);
+    p->task = GNUNET_SCHEDULER_add_now (&publish_stop_task, p);
+    return p;
+  case GNUNET_FS_STATUS_PUBLISH_STOPPED:
+    p = info->value.publish.cctx;
+    p->ctx = NULL;
+    GNUNET_CONTAINER_DLL_remove (publish_head, publish_tail, p);
+    GNUNET_free (p);
+    return NULL;
+  case GNUNET_FS_STATUS_DOWNLOAD_START:
+  case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
+  case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
+  case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
+    p = info->value.download.cctx;
+    return p;
+  case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+               "Download failed\n");
+    GNUNET_STATISTICS_update (stats_handle,
+                             "# failed downloads", 1, GNUNET_NO);
+    p = info->value.download.cctx;
+    p->task = GNUNET_SCHEDULER_add_now (&download_stop_task, p);
+    return p;
+  case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
+    p = info->value.download.cctx;
+    GNUNET_STATISTICS_update (stats_handle,
+                             "# download time (ms)",
+                             (long long) GNUNET_TIME_absolute_get_duration (p->start_time).rel_value_us / 1000LL,
+                             GNUNET_NO);
+    p->task = GNUNET_SCHEDULER_add_now (&download_stop_task, p);
+    return p;
+  case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
+    p = info->value.download.cctx;
+    p->ctx = NULL;
+    if (NULL == p->sctx)
+    {
+      GNUNET_CONTAINER_DLL_remove (download_head, download_tail, p);
+      GNUNET_free (p);
+    }
+    return NULL;
+  case GNUNET_FS_STATUS_SEARCH_START:
+  case GNUNET_FS_STATUS_SEARCH_RESULT_NAMESPACE:
+    p = info->value.search.cctx;
+    return p;
+  case GNUNET_FS_STATUS_SEARCH_RESULT:
+    p = info->value.search.cctx;
+    uri = info->value.search.specifics.result.uri;
+    if (GNUNET_YES != GNUNET_FS_uri_test_chk (uri))
+      return NULL; /* not what we want */
+    if (p->y != GNUNET_FS_uri_chk_get_file_size (uri))
+      return NULL; /* not what we want */
+    GNUNET_STATISTICS_update (stats_handle,
+                             "# search time (ms)",
+                             (long long) GNUNET_TIME_absolute_get_duration (p->start_time).rel_value_us / 1000LL,
+                             GNUNET_NO);
+    p->start_time = GNUNET_TIME_absolute_get ();
+    p->ctx = GNUNET_FS_download_start (fs_handle, uri,
+                                      NULL, NULL, NULL,
+                                      0, GNUNET_FS_uri_chk_get_file_size (uri),
+                                      anonymity_level,
+                                      GNUNET_FS_DOWNLOAD_NO_TEMPORARIES,
+                                      p,
+                                      NULL);
+    p->stask = GNUNET_SCHEDULER_add_now (&search_stop_task, p);
+    return NULL;
+  case GNUNET_FS_STATUS_SEARCH_UPDATE:
+  case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED:
+    return NULL; /* don't care */
+  case GNUNET_FS_STATUS_SEARCH_ERROR:
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+               "Search failed\n");
+    GNUNET_STATISTICS_update (stats_handle,
+                             "# failed searches", 1, GNUNET_NO);
+    p = info->value.search.cctx;
+    p->stask = GNUNET_SCHEDULER_add_now (&search_stop_task, p);
+    return p;
+  case GNUNET_FS_STATUS_SEARCH_STOPPED:
+    p = info->value.search.cctx;
+    p->sctx = NULL;
+    if (NULL == p->ctx)
+    {
+      GNUNET_CONTAINER_DLL_remove (download_head, download_tail, p);
+      GNUNET_free (p);
+    }
+    return NULL;
+  default:
+    /* unexpected event during profiling */
+    GNUNET_break (0);
+    return NULL;
+  }
 }
 
 
@@ -320,7 +500,7 @@ progress_cb (void *cls,
  * @param cls the 'struct Pattern' specifying the operation to perform
  * @param tc scheduler context
  */
-static void 
+static void
 start_publish (void *cls,
                const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
@@ -331,6 +511,7 @@ start_publish (void *cls,
   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
     return;
   fi = make_file (p->x, p->y, p);
+  p->start_time = GNUNET_TIME_absolute_get ();
   p->ctx = GNUNET_FS_publish_start (fs_handle,
                                    fi,
                                    NULL, NULL, NULL,
@@ -344,16 +525,22 @@ start_publish (void *cls,
  * @param cls the 'struct Pattern' specifying the operation to perform
  * @param tc scheduler context
  */
-static void 
+static void
 start_download (void *cls,
                const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct Pattern *p = cls;
+  struct GNUNET_FS_Uri *keywords;
 
   p->task = GNUNET_SCHEDULER_NO_TASK;
   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
     return;
-  // FIXME: start search operation
+  keywords = make_keywords (p->x);
+  p->start_time = GNUNET_TIME_absolute_get ();
+  p->sctx = GNUNET_FS_search_start (fs_handle, keywords,
+                                   anonymity_level,
+                                   GNUNET_FS_SEARCH_OPTION_NONE,
+                                   p);
 }
 
 
@@ -379,7 +566,7 @@ run (void *cls, char *const *args GNUNET_UNUSED,
                                 NULL);
 
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_number (cfg, 
+      GNUNET_CONFIGURATION_get_value_number (cfg,
                                             "TESTBED", "PEERID",
                                              &my_peerid))
   {
@@ -390,28 +577,28 @@ run (void *cls, char *const *args GNUNET_UNUSED,
     return;
   }
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_number (cfg, 
+      GNUNET_CONFIGURATION_get_value_number (cfg,
                                             "FSPROFILER", "ANONYMITY_LEVEL",
                                              &anonymity_level))
     anonymity_level = 1;
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_number (cfg, 
+      GNUNET_CONFIGURATION_get_value_number (cfg,
                                             "FSPROFILER", "REPLICATION_LEVEL",
-                                             &replication_level))   
+                                             &replication_level))
     replication_level = 1;
   GNUNET_snprintf (myoptname, sizeof (myoptname),
                   "DOWNLOAD-PATTERN-%u", my_peerid);
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_string (cfg, 
+      GNUNET_CONFIGURATION_get_value_string (cfg,
                                             "FSPROFILER", myoptname,
-                                             &download_pattern))   
+                                             &download_pattern))
     download_pattern = GNUNET_strdup ("");
   GNUNET_snprintf (myoptname, sizeof (myoptname),
                   "PUBLISH-PATTERN-%u", my_peerid);
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_string (cfg, 
+      GNUNET_CONFIGURATION_get_value_string (cfg,
                                             "FSPROFILER", myoptname,
-                                             &publish_pattern))   
+                                             &publish_pattern))
     publish_pattern = GNUNET_strdup ("");
   if ( (GNUNET_OK !=
        parse_pattern (&download_head,