-removed obsolete functions
[oweals/gnunet.git] / src / fs / gnunet-daemon-fsprofiler.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2012 Christian Grothoff
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20
21 /**
22  * @file fs/gnunet-daemon-fsprofiler.c
23  * @brief daemon that publishes and downloads (random) files
24  * @author Christian Grothoff
25  *
26  * TODO:
27  * - how to signal driver that we're done?
28  */
29 #include "platform.h"
30 #include "gnunet_fs_service.h"
31 #include "gnunet_statistics_service.h"
32
33 /**
34  * We use 'patterns' of the form (x,y,t) to specify desired download/publish
35  * activities of a peer.  They are stored in a DLL.
36  */
37 struct Pattern
38 {
39   /**
40    * Kept in a DLL.
41    */
42   struct Pattern *next;
43
44   /**
45    * Kept in a DLL.
46    */
47   struct Pattern *prev;
48
49   /**
50    * Execution context for the pattern (FS-handle to the operation).
51    */
52   void *ctx;
53
54   /**
55    * Secondary execution context for the pattern (FS-handle to the operation).
56    */
57   void *sctx;
58
59   /**
60    * When did the operation start?
61    */
62   struct GNUNET_TIME_Absolute start_time;
63
64   /**
65    * With how much delay should this operation be started?
66    */
67   struct GNUNET_TIME_Relative delay;
68
69   /**
70    * Task to run the operation.
71    */
72   struct GNUNET_SCHEDULER_Task * task;
73
74   /**
75    * Secondary task to run the operation.
76    */
77   struct GNUNET_SCHEDULER_Task * stask;
78
79   /**
80    * X-value.
81    */
82   unsigned long long x;
83
84   /**
85    * Y-value.
86    */
87   unsigned long long y;
88 };
89
90
91 /**
92  * Return value from 'main'.
93  */
94 static int global_ret;
95
96 /**
97  * Configuration we use.
98  */
99 static const struct GNUNET_CONFIGURATION_Handle *cfg;
100
101 /**
102  * Handle to the statistics service.
103  */
104 static struct GNUNET_STATISTICS_Handle *stats_handle;
105
106 /**
107  * Peer's FS handle.
108  */
109 static struct GNUNET_FS_Handle *fs_handle;
110
111 /**
112  * Unique number for this peer in the testbed.
113  */
114 static unsigned long long my_peerid;
115
116 /**
117  * Desired anonymity level.
118  */
119 static unsigned long long anonymity_level;
120
121 /**
122  * Desired replication level.
123  */
124 static unsigned long long replication_level;
125
126 /**
127  * String describing which publishing operations this peer should
128  * perform.  The format is "(SIZE,SEED,TIME)*", for example:
129  * "(1,5,0)(7,3,13)" means to publish a file with 1 byte and
130  * seed/keyword 5 immediately and another file with 7 bytes and
131  * seed/keyword 3 after 13 ms.
132  */
133 static char *publish_pattern;
134
135 /**
136  * Head of the DLL of publish patterns.
137  */
138 static struct Pattern *publish_head;
139
140 /**
141  * Tail of the DLL of publish patterns.
142  */
143 static struct Pattern *publish_tail;
144
145 /**
146  * String describing which download operations this peer should
147  * perform. The format is "(KEYWORD,SIZE,DELAY)*"; for example,
148  * "(1,7,3)(3,8,8)" means to download one file of 7 bytes under
149  * keyword "1" starting the search after 3 ms; and another one of 8
150  * bytes under keyword '3' starting after 8 ms.  The file size is
151  * used to determine which search result(s) should be used or ignored.
152  */
153 static char *download_pattern;
154
155 /**
156  * Head of the DLL of publish patterns.
157  */
158 static struct Pattern *download_head;
159
160 /**
161  * Tail of the DLL of publish patterns.
162  */
163 static struct Pattern *download_tail;
164
165
166 /**
167  * Parse a pattern string and store the corresponding
168  * 'struct Pattern' in the given head/tail.
169  *
170  * @param head where to store the head
171  * @param tail where to store the tail
172  * @param pattern pattern to parse
173  * @return GNUNET_OK on success
174  */
175 static int
176 parse_pattern (struct Pattern **head,
177                struct Pattern **tail,
178                const char *pattern)
179 {
180   struct Pattern *p;
181   unsigned long long x;
182   unsigned long long y;
183   unsigned long long t;
184
185   while (3 == sscanf (pattern,
186                       "(%llu,%llu,%llu)",
187                       &x, &y, &t))
188   {
189     p = GNUNET_new (struct Pattern);
190     p->x = x;
191     p->y = y;
192     p->delay.rel_value_us = (uint64_t) t;
193     GNUNET_CONTAINER_DLL_insert (*head, *tail, p);
194     pattern = strstr (pattern, ")");
195     GNUNET_assert (NULL != pattern);
196     pattern++;
197   }
198   return (0 == strlen (pattern)) ? GNUNET_OK : GNUNET_SYSERR;
199 }
200
201
202 /**
203  * Create a KSK URI from a number.
204  *
205  * @param kval the number
206  * @return corresponding KSK URI
207  */
208 static struct GNUNET_FS_Uri *
209 make_keywords (uint64_t kval)
210 {
211   char kw[128];
212
213   GNUNET_snprintf (kw, sizeof (kw),
214                    "%llu", (unsigned long long) kval);
215   return GNUNET_FS_uri_ksk_create (kw, NULL);
216 }
217
218
219 /**
220  * Create a file of the given length with a deterministic amount
221  * of data to be published under keyword 'kval'.
222  *
223  * @param length number of bytes in the file
224  * @param kval keyword value and seed for the data of the file
225  * @param ctx context to pass to 'fi'
226  * @return file information handle for the file
227  */
228 static struct GNUNET_FS_FileInformation *
229 make_file (uint64_t length,
230            uint64_t kval,
231            void *ctx)
232 {
233   struct GNUNET_FS_FileInformation *fi;
234   struct GNUNET_FS_BlockOptions bo;
235   char *data;
236   struct GNUNET_FS_Uri *keywords;
237   unsigned long long i;
238   uint64_t xor;
239
240   data = NULL; /* to make compilers happy */
241   if ( (0 != length) &&
242        (NULL == (data = GNUNET_malloc_large ((size_t) length))) )
243       return NULL;
244   /* initialize data with 'unique' data only depending on 'kval' and 'size',
245      making sure that blocks do not repeat */
246   for (i=0;i<length; i+=8)
247   {
248     xor = length ^ kval ^ (uint64_t) (i / 32 / 1024);
249     memcpy (&data[i], &xor, GNUNET_MIN (length - i, sizeof (uint64_t)));
250   }
251   bo.expiration_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_DAYS);
252   bo.anonymity_level = (uint32_t) anonymity_level;
253   bo.content_priority = 128;
254   bo.replication_level = (uint32_t) replication_level;
255   keywords = make_keywords (kval);
256   fi = GNUNET_FS_file_information_create_from_data (fs_handle,
257                                                     ctx,
258                                                     length,
259                                                     data, keywords,
260                                                     NULL, GNUNET_NO, &bo);
261   GNUNET_FS_uri_destroy (keywords);
262   return fi;
263 }
264
265
266 /**
267  * Task run during shutdown.
268  *
269  * @param cls unused
270  * @param tc unused
271  */
272 static void
273 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
274 {
275   struct Pattern *p;
276
277   while (NULL != (p = publish_head))
278   {
279     if (NULL != p->task)
280       GNUNET_SCHEDULER_cancel (p->task);
281     if (NULL != p->ctx)
282       GNUNET_FS_publish_stop (p->ctx);
283     GNUNET_CONTAINER_DLL_remove (publish_head, publish_tail, p);
284     GNUNET_free (p);
285   }
286   while (NULL != (p = download_head))
287   {
288     if (NULL != p->task)
289       GNUNET_SCHEDULER_cancel (p->task);
290     if (NULL != p->stask)
291       GNUNET_SCHEDULER_cancel (p->stask);
292     if (NULL != p->ctx)
293       GNUNET_FS_download_stop (p->ctx, GNUNET_YES);
294     if (NULL != p->sctx)
295       GNUNET_FS_search_stop (p->sctx);
296     GNUNET_CONTAINER_DLL_remove (download_head, download_tail, p);
297     GNUNET_free (p);
298   }
299   if (NULL != fs_handle)
300   {
301     GNUNET_FS_stop (fs_handle);
302     fs_handle = NULL;
303   }
304   if (NULL != stats_handle)
305   {
306     GNUNET_STATISTICS_destroy (stats_handle, GNUNET_YES);
307     stats_handle = NULL;
308   }
309 }
310
311
312 /**
313  * Task run when a publish operation should be stopped.
314  *
315  * @param cls the 'struct Pattern' of the publish operation to stop
316  * @param tc unused
317  */
318 static void
319 publish_stop_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
320 {
321   struct Pattern *p = cls;
322
323   p->task = NULL;
324   GNUNET_FS_publish_stop (p->ctx);
325 }
326
327
328 /**
329  * Task run when a download operation should be stopped.
330  *
331  * @param cls the 'struct Pattern' of the download operation to stop
332  * @param tc unused
333  */
334 static void
335 download_stop_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
336 {
337   struct Pattern *p = cls;
338
339   p->task = NULL;
340   GNUNET_FS_download_stop (p->ctx, GNUNET_YES);
341 }
342
343
344 /**
345  * Task run when a download operation should be stopped.
346  *
347  * @param cls the 'struct Pattern' of the download operation to stop
348  * @param tc unused
349  */
350 static void
351 search_stop_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
352 {
353   struct Pattern *p = cls;
354
355   p->stask = NULL;
356   GNUNET_FS_search_stop (p->sctx);
357 }
358
359
360 /**
361  * Notification of FS to a client about the progress of an
362  * operation.  Callbacks of this type will be used for uploads,
363  * downloads and searches.  Some of the arguments depend a bit
364  * in their meaning on the context in which the callback is used.
365  *
366  * @param cls closure
367  * @param info details about the event, specifying the event type
368  *        and various bits about the event
369  * @return client-context (for the next progress call
370  *         for this operation; should be set to NULL for
371  *         SUSPEND and STOPPED events).  The value returned
372  *         will be passed to future callbacks in the respective
373  *         field in the GNUNET_FS_ProgressInfo struct.
374  */
375 static void *
376 progress_cb (void *cls,
377              const struct GNUNET_FS_ProgressInfo *info)
378 {
379   struct Pattern *p;
380   const struct GNUNET_FS_Uri *uri;
381
382   switch (info->status)
383   {
384   case GNUNET_FS_STATUS_PUBLISH_START:
385   case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
386     p = info->value.publish.cctx;
387     return p;
388   case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
389     p = info->value.publish.cctx;
390     return p;
391   case GNUNET_FS_STATUS_PUBLISH_ERROR:
392     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
393                 "Publishing failed\n");
394     GNUNET_STATISTICS_update (stats_handle,
395                               "# failed publish operations", 1, GNUNET_NO);
396     p = info->value.publish.cctx;
397     p->task = GNUNET_SCHEDULER_add_now (&publish_stop_task, p);
398     return p;
399   case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
400     p = info->value.publish.cctx;
401     GNUNET_STATISTICS_update (stats_handle,
402                               "# publishing time (ms)",
403                               (long long) GNUNET_TIME_absolute_get_duration (p->start_time).rel_value_us / 1000LL,
404                               GNUNET_NO);
405     p->task = GNUNET_SCHEDULER_add_now (&publish_stop_task, p);
406     return p;
407   case GNUNET_FS_STATUS_PUBLISH_STOPPED:
408     p = info->value.publish.cctx;
409     p->ctx = NULL;
410     GNUNET_CONTAINER_DLL_remove (publish_head, publish_tail, p);
411     GNUNET_free (p);
412     return NULL;
413   case GNUNET_FS_STATUS_DOWNLOAD_START:
414   case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
415   case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
416   case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
417     p = info->value.download.cctx;
418     return p;
419   case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
420     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
421                 "Download failed\n");
422     GNUNET_STATISTICS_update (stats_handle,
423                               "# failed downloads", 1, GNUNET_NO);
424     p = info->value.download.cctx;
425     p->task = GNUNET_SCHEDULER_add_now (&download_stop_task, p);
426     return p;
427   case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
428     p = info->value.download.cctx;
429     GNUNET_STATISTICS_update (stats_handle,
430                               "# download time (ms)",
431                               (long long) GNUNET_TIME_absolute_get_duration (p->start_time).rel_value_us / 1000LL,
432                               GNUNET_NO);
433     p->task = GNUNET_SCHEDULER_add_now (&download_stop_task, p);
434     return p;
435   case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
436     p = info->value.download.cctx;
437     p->ctx = NULL;
438     if (NULL == p->sctx)
439     {
440       GNUNET_CONTAINER_DLL_remove (download_head, download_tail, p);
441       GNUNET_free (p);
442     }
443     return NULL;
444   case GNUNET_FS_STATUS_SEARCH_START:
445   case GNUNET_FS_STATUS_SEARCH_RESULT_NAMESPACE:
446     p = info->value.search.cctx;
447     return p;
448   case GNUNET_FS_STATUS_SEARCH_RESULT:
449     p = info->value.search.cctx;
450     uri = info->value.search.specifics.result.uri;
451     if (GNUNET_YES != GNUNET_FS_uri_test_chk (uri))
452       return NULL; /* not what we want */
453     if (p->y != GNUNET_FS_uri_chk_get_file_size (uri))
454       return NULL; /* not what we want */
455     GNUNET_STATISTICS_update (stats_handle,
456                               "# search time (ms)",
457                               (long long) GNUNET_TIME_absolute_get_duration (p->start_time).rel_value_us / 1000LL,
458                               GNUNET_NO);
459     p->start_time = GNUNET_TIME_absolute_get ();
460     p->ctx = GNUNET_FS_download_start (fs_handle, uri,
461                                        NULL, NULL, NULL,
462                                        0, GNUNET_FS_uri_chk_get_file_size (uri),
463                                        anonymity_level,
464                                        GNUNET_FS_DOWNLOAD_NO_TEMPORARIES,
465                                        p,
466                                        NULL);
467     p->stask = GNUNET_SCHEDULER_add_now (&search_stop_task, p);
468     return NULL;
469   case GNUNET_FS_STATUS_SEARCH_UPDATE:
470   case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED:
471     return NULL; /* don't care */
472   case GNUNET_FS_STATUS_SEARCH_ERROR:
473     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
474                 "Search failed\n");
475     GNUNET_STATISTICS_update (stats_handle,
476                               "# failed searches", 1, GNUNET_NO);
477     p = info->value.search.cctx;
478     p->stask = GNUNET_SCHEDULER_add_now (&search_stop_task, p);
479     return p;
480   case GNUNET_FS_STATUS_SEARCH_STOPPED:
481     p = info->value.search.cctx;
482     p->sctx = NULL;
483     if (NULL == p->ctx)
484     {
485       GNUNET_CONTAINER_DLL_remove (download_head, download_tail, p);
486       GNUNET_free (p);
487     }
488     return NULL;
489   default:
490     /* unexpected event during profiling */
491     GNUNET_break (0);
492     return NULL;
493   }
494 }
495
496
497 /**
498  * Start publish operation.
499  *
500  * @param cls the 'struct Pattern' specifying the operation to perform
501  * @param tc scheduler context
502  */
503 static void
504 start_publish (void *cls,
505                 const struct GNUNET_SCHEDULER_TaskContext *tc)
506 {
507   struct Pattern *p = cls;
508   struct GNUNET_FS_FileInformation *fi;
509
510   p->task = NULL;
511   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
512     return;
513   fi = make_file (p->x, p->y, p);
514   p->start_time = GNUNET_TIME_absolute_get ();
515   p->ctx = GNUNET_FS_publish_start (fs_handle,
516                                     fi,
517                                     NULL, NULL, NULL,
518                                     GNUNET_FS_PUBLISH_OPTION_NONE);
519 }
520
521
522 /**
523  * Start download operation.
524  *
525  * @param cls the 'struct Pattern' specifying the operation to perform
526  * @param tc scheduler context
527  */
528 static void
529 start_download (void *cls,
530                 const struct GNUNET_SCHEDULER_TaskContext *tc)
531 {
532   struct Pattern *p = cls;
533   struct GNUNET_FS_Uri *keywords;
534
535   p->task = NULL;
536   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
537     return;
538   keywords = make_keywords (p->x);
539   p->start_time = GNUNET_TIME_absolute_get ();
540   p->sctx = GNUNET_FS_search_start (fs_handle, keywords,
541                                     anonymity_level,
542                                     GNUNET_FS_SEARCH_OPTION_NONE,
543                                     p);
544 }
545
546
547 /**
548  * @brief Main function that will be run by the scheduler.
549  *
550  * @param cls closure
551  * @param args remaining command-line arguments
552  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
553  * @param cfg_ configuration
554  */
555 static void
556 run (void *cls, char *const *args GNUNET_UNUSED,
557      const char *cfgfile GNUNET_UNUSED,
558      const struct GNUNET_CONFIGURATION_Handle *cfg_)
559 {
560   char myoptname[128];
561   struct Pattern *p;
562
563   cfg = cfg_;
564   /* Scheduled the task to clean up when shutdown is called */
565   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
566                                 NULL);
567
568   if (GNUNET_OK !=
569       GNUNET_CONFIGURATION_get_value_number (cfg,
570                                              "TESTBED", "PEERID",
571                                              &my_peerid))
572   {
573     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
574                                "TESTBED", "PEERID");
575     global_ret = GNUNET_SYSERR;
576     GNUNET_SCHEDULER_shutdown ();
577     return;
578   }
579   if (GNUNET_OK !=
580       GNUNET_CONFIGURATION_get_value_number (cfg,
581                                              "FSPROFILER", "ANONYMITY_LEVEL",
582                                              &anonymity_level))
583     anonymity_level = 1;
584   if (GNUNET_OK !=
585       GNUNET_CONFIGURATION_get_value_number (cfg,
586                                              "FSPROFILER", "REPLICATION_LEVEL",
587                                              &replication_level))
588     replication_level = 1;
589   GNUNET_snprintf (myoptname, sizeof (myoptname),
590                    "DOWNLOAD-PATTERN-%u", my_peerid);
591   if (GNUNET_OK !=
592       GNUNET_CONFIGURATION_get_value_string (cfg,
593                                              "FSPROFILER", myoptname,
594                                              &download_pattern))
595     download_pattern = GNUNET_strdup ("");
596   GNUNET_snprintf (myoptname, sizeof (myoptname),
597                    "PUBLISH-PATTERN-%u", my_peerid);
598   if (GNUNET_OK !=
599       GNUNET_CONFIGURATION_get_value_string (cfg,
600                                              "FSPROFILER", myoptname,
601                                              &publish_pattern))
602     publish_pattern = GNUNET_strdup ("");
603   if ( (GNUNET_OK !=
604         parse_pattern (&download_head,
605                        &download_tail,
606                        download_pattern)) ||
607        (GNUNET_OK !=
608         parse_pattern (&publish_head,
609                        &publish_tail,
610                        publish_pattern)) )
611   {
612     GNUNET_SCHEDULER_shutdown ();
613     return;
614   }
615
616   stats_handle = GNUNET_STATISTICS_create ("fsprofiler", cfg);
617   fs_handle =
618     GNUNET_FS_start (cfg,
619                      "fsprofiler",
620                      &progress_cb, NULL,
621                      GNUNET_FS_FLAGS_NONE,
622                      GNUNET_FS_OPTIONS_DOWNLOAD_PARALLELISM, 1,
623                      GNUNET_FS_OPTIONS_REQUEST_PARALLELISM, 1,
624                      GNUNET_FS_OPTIONS_END);
625   if (NULL == fs_handle)
626   {
627     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not acquire FS handle. Exiting.\n");
628     global_ret = GNUNET_SYSERR;
629     GNUNET_SCHEDULER_shutdown ();
630     return;
631   }
632   for (p = publish_head; NULL != p; p = p->next)
633     p->task = GNUNET_SCHEDULER_add_delayed (p->delay,
634                                             &start_publish, p);
635   for (p = download_head; NULL != p; p = p->next)
636     p->task = GNUNET_SCHEDULER_add_delayed (p->delay,
637                                             &start_download, p);
638 }
639
640
641 /**
642  * Program that performs various "random" FS activities.
643  *
644  * @param argc number of arguments from the command line
645  * @param argv command line arguments
646  * @return 0 ok, 1 on error
647  */
648 int
649 main (int argc, char *const *argv)
650 {
651   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
652     GNUNET_GETOPT_OPTION_END
653   };
654
655   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
656     return 2;
657   return (GNUNET_OK ==
658           GNUNET_PROGRAM_run (argc, argv, "gnunet-daemon-fsprofiler",
659                               gettext_noop
660                               ("Daemon to use file-sharing to measure its performance."),
661                               options, &run, NULL)) ? global_ret : 1;
662 }
663
664 /* end of gnunet-daemon-fsprofiler.c */