-fixing main FS build, updating man page of gnunet-pseudonym
[oweals/gnunet.git] / src / fs / gnunet-daemon-fsprofiler.c
1 /*
2      This file is part of GNUnet.
3      (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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, 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   GNUNET_SCHEDULER_TaskIdentifier task;
73
74   /**
75    * Secondary task to run the operation.
76    */
77   GNUNET_SCHEDULER_TaskIdentifier 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_malloc (sizeof (struct Pattern));
190     p->x = x;
191     p->y = y;
192     p->delay.rel_value = (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 (GNUNET_SCHEDULER_NO_TASK != 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 (GNUNET_SCHEDULER_NO_TASK != p->task)
289       GNUNET_SCHEDULER_cancel (p->task);
290     if (GNUNET_SCHEDULER_NO_TASK != 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 = GNUNET_SCHEDULER_NO_TASK;
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 = GNUNET_SCHEDULER_NO_TASK;
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 = GNUNET_SCHEDULER_NO_TASK;
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_ERROR:
389     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
390                 "Publishing failed\n");
391     GNUNET_STATISTICS_update (stats_handle,
392                               "# failed publish operations", 1, GNUNET_NO);
393     p = info->value.publish.cctx;
394     p->task = GNUNET_SCHEDULER_add_now (&publish_stop_task, p);
395     return p;
396   case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
397     p = info->value.publish.cctx;
398     GNUNET_STATISTICS_update (stats_handle,
399                               "# publishing time (ms)", 
400                               (long long) GNUNET_TIME_absolute_get_duration (p->start_time).rel_value, 
401                               GNUNET_NO);
402     p->task = GNUNET_SCHEDULER_add_now (&publish_stop_task, p);
403     return p;
404   case GNUNET_FS_STATUS_PUBLISH_STOPPED:
405     p = info->value.publish.cctx;
406     p->ctx = NULL;
407     GNUNET_CONTAINER_DLL_remove (publish_head, publish_tail, p);
408     GNUNET_free (p);
409     return NULL;
410   case GNUNET_FS_STATUS_DOWNLOAD_START:
411   case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
412   case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
413   case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
414     p = info->value.download.cctx;
415     return p;
416   case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
417     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
418                 "Download failed\n");
419     GNUNET_STATISTICS_update (stats_handle,
420                               "# failed downloads", 1, GNUNET_NO);
421     p = info->value.download.cctx;
422     p->task = GNUNET_SCHEDULER_add_now (&download_stop_task, p);
423     return p;
424   case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
425     p = info->value.download.cctx;
426     GNUNET_STATISTICS_update (stats_handle,
427                               "# download time (ms)", 
428                               (long long) GNUNET_TIME_absolute_get_duration (p->start_time).rel_value, 
429                               GNUNET_NO);    p->task = GNUNET_SCHEDULER_add_now (&download_stop_task, p);
430     return p;
431   case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
432     p = info->value.download.cctx;
433     p->ctx = NULL;
434     if (NULL == p->sctx)
435     {
436       GNUNET_CONTAINER_DLL_remove (download_head, download_tail, p);
437       GNUNET_free (p);
438     }
439     return NULL;
440   case GNUNET_FS_STATUS_SEARCH_START:
441   case GNUNET_FS_STATUS_SEARCH_RESULT_NAMESPACE:
442     p = info->value.search.cctx;
443     return p;
444   case GNUNET_FS_STATUS_SEARCH_RESULT:
445     p = info->value.search.cctx;
446     uri = info->value.search.specifics.result.uri;
447     if (GNUNET_YES != GNUNET_FS_uri_test_chk (uri))
448       return NULL; /* not what we want */
449     if (p->y != GNUNET_FS_uri_chk_get_file_size (uri))
450       return NULL; /* not what we want */
451     GNUNET_STATISTICS_update (stats_handle,
452                               "# search time (ms)", 
453                               (long long) GNUNET_TIME_absolute_get_duration (p->start_time).rel_value, 
454                               GNUNET_NO);
455     p->start_time = GNUNET_TIME_absolute_get ();
456     p->ctx = GNUNET_FS_download_start (fs_handle, uri,
457                                        NULL, NULL, NULL, 
458                                        0, GNUNET_FS_uri_chk_get_file_size (uri),
459                                        anonymity_level,
460                                        GNUNET_FS_DOWNLOAD_NO_TEMPORARIES,
461                                        p,
462                                        NULL);
463     p->stask = GNUNET_SCHEDULER_add_now (&search_stop_task, p);
464     return NULL;
465   case GNUNET_FS_STATUS_SEARCH_UPDATE:
466   case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED:
467     return NULL; /* don't care */
468   case GNUNET_FS_STATUS_SEARCH_ERROR:
469     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
470                 "Search failed\n");
471     GNUNET_STATISTICS_update (stats_handle,
472                               "# failed searches", 1, GNUNET_NO);
473     p = info->value.search.cctx;
474     p->stask = GNUNET_SCHEDULER_add_now (&search_stop_task, p);
475     return p;
476   case GNUNET_FS_STATUS_SEARCH_STOPPED:
477     p = info->value.search.cctx;
478     p->sctx = NULL;
479     if (NULL == p->ctx)
480     {
481       GNUNET_CONTAINER_DLL_remove (download_head, download_tail, p);
482       GNUNET_free (p);
483     }
484     return NULL;
485   default: 
486     /* unexpected event during profiling */
487     GNUNET_break (0);
488     return NULL;
489   }
490 }
491
492
493 /**
494  * Start publish operation.
495  *
496  * @param cls the 'struct Pattern' specifying the operation to perform
497  * @param tc scheduler context
498  */
499 static void 
500 start_publish (void *cls,
501                 const struct GNUNET_SCHEDULER_TaskContext *tc)
502 {
503   struct Pattern *p = cls;
504   struct GNUNET_FS_FileInformation *fi;
505
506   p->task = GNUNET_SCHEDULER_NO_TASK;
507   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
508     return;
509   fi = make_file (p->x, p->y, p);
510   p->start_time = GNUNET_TIME_absolute_get ();
511   p->ctx = GNUNET_FS_publish_start (fs_handle,
512                                     fi,
513                                     NULL, NULL, NULL,
514                                     GNUNET_FS_PUBLISH_OPTION_NONE);
515 }
516
517
518 /**
519  * Start download operation.
520  *
521  * @param cls the 'struct Pattern' specifying the operation to perform
522  * @param tc scheduler context
523  */
524 static void 
525 start_download (void *cls,
526                 const struct GNUNET_SCHEDULER_TaskContext *tc)
527 {
528   struct Pattern *p = cls;
529   struct GNUNET_FS_Uri *keywords;
530
531   p->task = GNUNET_SCHEDULER_NO_TASK;
532   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
533     return;
534   keywords = make_keywords (p->x);
535   p->start_time = GNUNET_TIME_absolute_get ();
536   p->sctx = GNUNET_FS_search_start (fs_handle, keywords,
537                                     anonymity_level,
538                                     GNUNET_FS_SEARCH_OPTION_NONE,
539                                     p);
540 }
541
542
543 /**
544  * @brief Main function that will be run by the scheduler.
545  *
546  * @param cls closure
547  * @param args remaining command-line arguments
548  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
549  * @param cfg_ configuration
550  */
551 static void
552 run (void *cls, char *const *args GNUNET_UNUSED,
553      const char *cfgfile GNUNET_UNUSED,
554      const struct GNUNET_CONFIGURATION_Handle *cfg_)
555 {
556   char myoptname[128];
557   struct Pattern *p;
558
559   cfg = cfg_;
560   /* Scheduled the task to clean up when shutdown is called */
561   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
562                                 NULL);
563
564   if (GNUNET_OK !=
565       GNUNET_CONFIGURATION_get_value_number (cfg, 
566                                              "TESTBED", "PEERID",
567                                              &my_peerid))
568   {
569     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
570                                "TESTBED", "PEERID");
571     global_ret = GNUNET_SYSERR;
572     GNUNET_SCHEDULER_shutdown ();
573     return;
574   }
575   if (GNUNET_OK !=
576       GNUNET_CONFIGURATION_get_value_number (cfg, 
577                                              "FSPROFILER", "ANONYMITY_LEVEL",
578                                              &anonymity_level))
579     anonymity_level = 1;
580   if (GNUNET_OK !=
581       GNUNET_CONFIGURATION_get_value_number (cfg, 
582                                              "FSPROFILER", "REPLICATION_LEVEL",
583                                              &replication_level))   
584     replication_level = 1;
585   GNUNET_snprintf (myoptname, sizeof (myoptname),
586                    "DOWNLOAD-PATTERN-%u", my_peerid);
587   if (GNUNET_OK !=
588       GNUNET_CONFIGURATION_get_value_string (cfg, 
589                                              "FSPROFILER", myoptname,
590                                              &download_pattern))   
591     download_pattern = GNUNET_strdup ("");
592   GNUNET_snprintf (myoptname, sizeof (myoptname),
593                    "PUBLISH-PATTERN-%u", my_peerid);
594   if (GNUNET_OK !=
595       GNUNET_CONFIGURATION_get_value_string (cfg, 
596                                              "FSPROFILER", myoptname,
597                                              &publish_pattern))   
598     publish_pattern = GNUNET_strdup ("");
599   if ( (GNUNET_OK !=
600         parse_pattern (&download_head,
601                        &download_tail,
602                        download_pattern)) ||
603        (GNUNET_OK !=
604         parse_pattern (&publish_head,
605                        &publish_tail,
606                        publish_pattern)) )
607   {
608     GNUNET_SCHEDULER_shutdown ();
609     return;
610   }
611
612   stats_handle = GNUNET_STATISTICS_create ("fsprofiler", cfg);
613   fs_handle =
614     GNUNET_FS_start (cfg,
615                      "fsprofiler",
616                      &progress_cb, NULL,
617                      GNUNET_FS_FLAGS_NONE,
618                      GNUNET_FS_OPTIONS_DOWNLOAD_PARALLELISM, 1,
619                      GNUNET_FS_OPTIONS_REQUEST_PARALLELISM, 1,
620                      GNUNET_FS_OPTIONS_END);
621   if (NULL == fs_handle)
622   {
623     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not acquire FS handle. Exiting.\n");
624     global_ret = GNUNET_SYSERR;
625     GNUNET_SCHEDULER_shutdown ();
626     return;
627   }
628   for (p = publish_head; NULL != p; p = p->next)
629     p->task = GNUNET_SCHEDULER_add_delayed (p->delay,
630                                             &start_publish, p);
631   for (p = download_head; NULL != p; p = p->next)
632     p->task = GNUNET_SCHEDULER_add_delayed (p->delay,
633                                             &start_download, p);
634 }
635
636
637 /**
638  * Program that performs various "random" FS activities.
639  *
640  * @param argc number of arguments from the command line
641  * @param argv command line arguments
642  * @return 0 ok, 1 on error
643  */
644 int
645 main (int argc, char *const *argv)
646 {
647   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
648     GNUNET_GETOPT_OPTION_END
649   };
650
651   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
652     return 2;
653   return (GNUNET_OK ==
654           GNUNET_PROGRAM_run (argc, argv, "gnunet-daemon-fsprofiler",
655                               gettext_noop
656                               ("Daemon to use file-sharing to measure its performance."),
657                               options, &run, NULL)) ? global_ret : 1;
658 }
659
660 /* end of gnunet-daemon-fsprofiler.c */