2 This file is part of GNUnet.
3 (C) 2012 Christian Grothoff
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.
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.
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.
22 * @file fs/gnunet-daemon-fsprofiler.c
23 * @brief daemon that publishes and downloads (random) files
24 * @author Christian Grothoff
27 * - how to signal driver that we're done?
30 #include "gnunet_fs_service.h"
31 #include "gnunet_statistics_service.h"
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.
50 * Execution context for the pattern (FS-handle to the operation).
55 * Secondary execution context for the pattern (FS-handle to the operation).
60 * When did the operation start?
62 struct GNUNET_TIME_Absolute start_time;
65 * With how much delay should this operation be started?
67 struct GNUNET_TIME_Relative delay;
70 * Task to run the operation.
72 GNUNET_SCHEDULER_TaskIdentifier task;
75 * Secondary task to run the operation.
77 GNUNET_SCHEDULER_TaskIdentifier stask;
92 * Return value from 'main'.
94 static int global_ret;
97 * Configuration we use.
99 static const struct GNUNET_CONFIGURATION_Handle *cfg;
102 * Handle to the statistics service.
104 static struct GNUNET_STATISTICS_Handle *stats_handle;
109 static struct GNUNET_FS_Handle *fs_handle;
112 * Unique number for this peer in the testbed.
114 static unsigned long long my_peerid;
117 * Desired anonymity level.
119 static unsigned long long anonymity_level;
122 * Desired replication level.
124 static unsigned long long replication_level;
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.
133 static char *publish_pattern;
136 * Head of the DLL of publish patterns.
138 static struct Pattern *publish_head;
141 * Tail of the DLL of publish patterns.
143 static struct Pattern *publish_tail;
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.
153 static char *download_pattern;
156 * Head of the DLL of publish patterns.
158 static struct Pattern *download_head;
161 * Tail of the DLL of publish patterns.
163 static struct Pattern *download_tail;
167 * Parse a pattern string and store the corresponding
168 * 'struct Pattern' in the given head/tail.
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
176 parse_pattern (struct Pattern **head,
177 struct Pattern **tail,
181 unsigned long long x;
182 unsigned long long y;
183 unsigned long long t;
185 while (3 == sscanf (pattern,
189 p = GNUNET_new (struct Pattern);
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);
198 return (0 == strlen (pattern)) ? GNUNET_OK : GNUNET_SYSERR;
203 * Create a KSK URI from a number.
205 * @param kval the number
206 * @return corresponding KSK URI
208 static struct GNUNET_FS_Uri *
209 make_keywords (uint64_t kval)
213 GNUNET_snprintf (kw, sizeof (kw),
214 "%llu", (unsigned long long) kval);
215 return GNUNET_FS_uri_ksk_create (kw, NULL);
220 * Create a file of the given length with a deterministic amount
221 * of data to be published under keyword 'kval'.
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
228 static struct GNUNET_FS_FileInformation *
229 make_file (uint64_t length,
233 struct GNUNET_FS_FileInformation *fi;
234 struct GNUNET_FS_BlockOptions bo;
236 struct GNUNET_FS_Uri *keywords;
237 unsigned long long i;
240 data = NULL; /* to make compilers happy */
241 if ( (0 != length) &&
242 (NULL == (data = GNUNET_malloc_large ((size_t) length))) )
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)
248 xor = length ^ kval ^ (uint64_t) (i / 32 / 1024);
249 memcpy (&data[i], &xor, GNUNET_MIN (length - i, sizeof (uint64_t)));
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,
260 NULL, GNUNET_NO, &bo);
261 GNUNET_FS_uri_destroy (keywords);
267 * Task run during shutdown.
273 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
277 while (NULL != (p = publish_head))
279 if (GNUNET_SCHEDULER_NO_TASK != p->task)
280 GNUNET_SCHEDULER_cancel (p->task);
282 GNUNET_FS_publish_stop (p->ctx);
283 GNUNET_CONTAINER_DLL_remove (publish_head, publish_tail, p);
286 while (NULL != (p = download_head))
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);
293 GNUNET_FS_download_stop (p->ctx, GNUNET_YES);
295 GNUNET_FS_search_stop (p->sctx);
296 GNUNET_CONTAINER_DLL_remove (download_head, download_tail, p);
299 if (NULL != fs_handle)
301 GNUNET_FS_stop (fs_handle);
304 if (NULL != stats_handle)
306 GNUNET_STATISTICS_destroy (stats_handle, GNUNET_YES);
313 * Task run when a publish operation should be stopped.
315 * @param cls the 'struct Pattern' of the publish operation to stop
319 publish_stop_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
321 struct Pattern *p = cls;
323 p->task = GNUNET_SCHEDULER_NO_TASK;
324 GNUNET_FS_publish_stop (p->ctx);
329 * Task run when a download operation should be stopped.
331 * @param cls the 'struct Pattern' of the download operation to stop
335 download_stop_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
337 struct Pattern *p = cls;
339 p->task = GNUNET_SCHEDULER_NO_TASK;
340 GNUNET_FS_download_stop (p->ctx, GNUNET_YES);
345 * Task run when a download operation should be stopped.
347 * @param cls the 'struct Pattern' of the download operation to stop
351 search_stop_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
353 struct Pattern *p = cls;
355 p->stask = GNUNET_SCHEDULER_NO_TASK;
356 GNUNET_FS_search_stop (p->sctx);
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.
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.
376 progress_cb (void *cls,
377 const struct GNUNET_FS_ProgressInfo *info)
380 const struct GNUNET_FS_Uri *uri;
382 switch (info->status)
384 case GNUNET_FS_STATUS_PUBLISH_START:
385 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
386 p = info->value.publish.cctx;
388 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
389 p = info->value.publish.cctx;
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);
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,
405 p->task = GNUNET_SCHEDULER_add_now (&publish_stop_task, p);
407 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
408 p = info->value.publish.cctx;
410 GNUNET_CONTAINER_DLL_remove (publish_head, publish_tail, p);
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;
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);
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,
433 p->task = GNUNET_SCHEDULER_add_now (&download_stop_task, p);
435 case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
436 p = info->value.download.cctx;
440 GNUNET_CONTAINER_DLL_remove (download_head, download_tail, p);
444 case GNUNET_FS_STATUS_SEARCH_START:
445 case GNUNET_FS_STATUS_SEARCH_RESULT_NAMESPACE:
446 p = info->value.search.cctx;
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,
459 p->start_time = GNUNET_TIME_absolute_get ();
460 p->ctx = GNUNET_FS_download_start (fs_handle, uri,
462 0, GNUNET_FS_uri_chk_get_file_size (uri),
464 GNUNET_FS_DOWNLOAD_NO_TEMPORARIES,
467 p->stask = GNUNET_SCHEDULER_add_now (&search_stop_task, p);
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,
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);
480 case GNUNET_FS_STATUS_SEARCH_STOPPED:
481 p = info->value.search.cctx;
485 GNUNET_CONTAINER_DLL_remove (download_head, download_tail, p);
490 /* unexpected event during profiling */
498 * Start publish operation.
500 * @param cls the 'struct Pattern' specifying the operation to perform
501 * @param tc scheduler context
504 start_publish (void *cls,
505 const struct GNUNET_SCHEDULER_TaskContext *tc)
507 struct Pattern *p = cls;
508 struct GNUNET_FS_FileInformation *fi;
510 p->task = GNUNET_SCHEDULER_NO_TASK;
511 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
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,
518 GNUNET_FS_PUBLISH_OPTION_NONE);
523 * Start download operation.
525 * @param cls the 'struct Pattern' specifying the operation to perform
526 * @param tc scheduler context
529 start_download (void *cls,
530 const struct GNUNET_SCHEDULER_TaskContext *tc)
532 struct Pattern *p = cls;
533 struct GNUNET_FS_Uri *keywords;
535 p->task = GNUNET_SCHEDULER_NO_TASK;
536 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
538 keywords = make_keywords (p->x);
539 p->start_time = GNUNET_TIME_absolute_get ();
540 p->sctx = GNUNET_FS_search_start (fs_handle, keywords,
542 GNUNET_FS_SEARCH_OPTION_NONE,
548 * @brief Main function that will be run by the scheduler.
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
556 run (void *cls, char *const *args GNUNET_UNUSED,
557 const char *cfgfile GNUNET_UNUSED,
558 const struct GNUNET_CONFIGURATION_Handle *cfg_)
564 /* Scheduled the task to clean up when shutdown is called */
565 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
569 GNUNET_CONFIGURATION_get_value_number (cfg,
573 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
574 "TESTBED", "PEERID");
575 global_ret = GNUNET_SYSERR;
576 GNUNET_SCHEDULER_shutdown ();
580 GNUNET_CONFIGURATION_get_value_number (cfg,
581 "FSPROFILER", "ANONYMITY_LEVEL",
585 GNUNET_CONFIGURATION_get_value_number (cfg,
586 "FSPROFILER", "REPLICATION_LEVEL",
588 replication_level = 1;
589 GNUNET_snprintf (myoptname, sizeof (myoptname),
590 "DOWNLOAD-PATTERN-%u", my_peerid);
592 GNUNET_CONFIGURATION_get_value_string (cfg,
593 "FSPROFILER", myoptname,
595 download_pattern = GNUNET_strdup ("");
596 GNUNET_snprintf (myoptname, sizeof (myoptname),
597 "PUBLISH-PATTERN-%u", my_peerid);
599 GNUNET_CONFIGURATION_get_value_string (cfg,
600 "FSPROFILER", myoptname,
602 publish_pattern = GNUNET_strdup ("");
604 parse_pattern (&download_head,
606 download_pattern)) ||
608 parse_pattern (&publish_head,
612 GNUNET_SCHEDULER_shutdown ();
616 stats_handle = GNUNET_STATISTICS_create ("fsprofiler", cfg);
618 GNUNET_FS_start (cfg,
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)
627 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not acquire FS handle. Exiting.\n");
628 global_ret = GNUNET_SYSERR;
629 GNUNET_SCHEDULER_shutdown ();
632 for (p = publish_head; NULL != p; p = p->next)
633 p->task = GNUNET_SCHEDULER_add_delayed (p->delay,
635 for (p = download_head; NULL != p; p = p->next)
636 p->task = GNUNET_SCHEDULER_add_delayed (p->delay,
642 * Program that performs various "random" FS activities.
644 * @param argc number of arguments from the command line
645 * @param argv command line arguments
646 * @return 0 ok, 1 on error
649 main (int argc, char *const *argv)
651 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
652 GNUNET_GETOPT_OPTION_END
655 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
658 GNUNET_PROGRAM_run (argc, argv, "gnunet-daemon-fsprofiler",
660 ("Daemon to use file-sharing to measure its performance."),
661 options, &run, NULL)) ? global_ret : 1;
664 /* end of gnunet-daemon-fsprofiler.c */