2 This file is part of GNUnet.
3 Copyright (C) 2012 Christian Grothoff
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 * @file fs/gnunet-daemon-fsprofiler.c
21 * @brief daemon that publishes and downloads (random) files
22 * @author Christian Grothoff
25 * - how to signal driver that we're done?
28 #include "gnunet_fs_service.h"
29 #include "gnunet_statistics_service.h"
32 * We use 'patterns' of the form (x,y,t) to specify desired download/publish
33 * activities of a peer. They are stored in a DLL.
48 * Execution context for the pattern (FS-handle to the operation).
53 * Secondary execution context for the pattern (FS-handle to the operation).
58 * When did the operation start?
60 struct GNUNET_TIME_Absolute start_time;
63 * With how much delay should this operation be started?
65 struct GNUNET_TIME_Relative delay;
68 * Task to run the operation.
70 struct GNUNET_SCHEDULER_Task * task;
73 * Secondary task to run the operation.
75 struct GNUNET_SCHEDULER_Task * stask;
90 * Return value from 'main'.
92 static int global_ret;
95 * Configuration we use.
97 static const struct GNUNET_CONFIGURATION_Handle *cfg;
100 * Handle to the statistics service.
102 static struct GNUNET_STATISTICS_Handle *stats_handle;
107 static struct GNUNET_FS_Handle *fs_handle;
110 * Unique number for this peer in the testbed.
112 static unsigned long long my_peerid;
115 * Desired anonymity level.
117 static unsigned long long anonymity_level;
120 * Desired replication level.
122 static unsigned long long replication_level;
125 * String describing which publishing operations this peer should
126 * perform. The format is "(SIZE,SEED,TIME)*", for example:
127 * "(1,5,0)(7,3,13)" means to publish a file with 1 byte and
128 * seed/keyword 5 immediately and another file with 7 bytes and
129 * seed/keyword 3 after 13 ms.
131 static char *publish_pattern;
134 * Head of the DLL of publish patterns.
136 static struct Pattern *publish_head;
139 * Tail of the DLL of publish patterns.
141 static struct Pattern *publish_tail;
144 * String describing which download operations this peer should
145 * perform. The format is "(KEYWORD,SIZE,DELAY)*"; for example,
146 * "(1,7,3)(3,8,8)" means to download one file of 7 bytes under
147 * keyword "1" starting the search after 3 ms; and another one of 8
148 * bytes under keyword '3' starting after 8 ms. The file size is
149 * used to determine which search result(s) should be used or ignored.
151 static char *download_pattern;
154 * Head of the DLL of publish patterns.
156 static struct Pattern *download_head;
159 * Tail of the DLL of publish patterns.
161 static struct Pattern *download_tail;
165 * Parse a pattern string and store the corresponding
166 * 'struct Pattern' in the given head/tail.
168 * @param head where to store the head
169 * @param tail where to store the tail
170 * @param pattern pattern to parse
171 * @return GNUNET_OK on success
174 parse_pattern (struct Pattern **head,
175 struct Pattern **tail,
179 unsigned long long x;
180 unsigned long long y;
181 unsigned long long t;
183 while (3 == sscanf (pattern,
187 p = GNUNET_new (struct Pattern);
190 p->delay.rel_value_us = (uint64_t) t;
191 GNUNET_CONTAINER_DLL_insert (*head, *tail, p);
192 pattern = strstr (pattern, ")");
193 GNUNET_assert (NULL != pattern);
196 return (0 == strlen (pattern)) ? GNUNET_OK : GNUNET_SYSERR;
201 * Create a KSK URI from a number.
203 * @param kval the number
204 * @return corresponding KSK URI
206 static struct GNUNET_FS_Uri *
207 make_keywords (uint64_t kval)
211 GNUNET_snprintf (kw, sizeof (kw),
212 "%llu", (unsigned long long) kval);
213 return GNUNET_FS_uri_ksk_create (kw, NULL);
218 * Create a file of the given length with a deterministic amount
219 * of data to be published under keyword 'kval'.
221 * @param length number of bytes in the file
222 * @param kval keyword value and seed for the data of the file
223 * @param ctx context to pass to 'fi'
224 * @return file information handle for the file
226 static struct GNUNET_FS_FileInformation *
227 make_file (uint64_t length,
231 struct GNUNET_FS_FileInformation *fi;
232 struct GNUNET_FS_BlockOptions bo;
234 struct GNUNET_FS_Uri *keywords;
235 unsigned long long i;
238 data = NULL; /* to make compilers happy */
239 if ( (0 != length) &&
240 (NULL == (data = GNUNET_malloc_large ((size_t) length))) )
242 /* initialize data with 'unique' data only depending on 'kval' and 'size',
243 making sure that blocks do not repeat */
244 for (i=0;i<length; i+=8)
246 xor = length ^ kval ^ (uint64_t) (i / 32 / 1024);
247 GNUNET_memcpy (&data[i], &xor, GNUNET_MIN (length - i, sizeof (uint64_t)));
249 bo.expiration_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_DAYS);
250 bo.anonymity_level = (uint32_t) anonymity_level;
251 bo.content_priority = 128;
252 bo.replication_level = (uint32_t) replication_level;
253 keywords = make_keywords (kval);
254 fi = GNUNET_FS_file_information_create_from_data (fs_handle,
258 NULL, GNUNET_NO, &bo);
259 GNUNET_FS_uri_destroy (keywords);
265 * Task run during shutdown.
270 shutdown_task (void *cls)
274 while (NULL != (p = publish_head))
277 GNUNET_SCHEDULER_cancel (p->task);
279 GNUNET_FS_publish_stop (p->ctx);
280 GNUNET_CONTAINER_DLL_remove (publish_head, publish_tail, p);
283 while (NULL != (p = download_head))
286 GNUNET_SCHEDULER_cancel (p->task);
287 if (NULL != p->stask)
288 GNUNET_SCHEDULER_cancel (p->stask);
290 GNUNET_FS_download_stop (p->ctx, GNUNET_YES);
292 GNUNET_FS_search_stop (p->sctx);
293 GNUNET_CONTAINER_DLL_remove (download_head, download_tail, p);
296 if (NULL != fs_handle)
298 GNUNET_FS_stop (fs_handle);
301 if (NULL != stats_handle)
303 GNUNET_STATISTICS_destroy (stats_handle, GNUNET_YES);
310 * Task run when a publish operation should be stopped.
312 * @param cls the 'struct Pattern' of the publish operation to stop
315 publish_stop_task (void *cls)
317 struct Pattern *p = cls;
320 GNUNET_FS_publish_stop (p->ctx);
325 * Task run when a download operation should be stopped.
327 * @param cls the 'struct Pattern' of the download operation to stop
330 download_stop_task (void *cls)
332 struct Pattern *p = cls;
335 GNUNET_FS_download_stop (p->ctx, GNUNET_YES);
340 * Task run when a download operation should be stopped.
342 * @param cls the 'struct Pattern' of the download operation to stop
345 search_stop_task (void *cls)
347 struct Pattern *p = cls;
350 GNUNET_FS_search_stop (p->sctx);
355 * Notification of FS to a client about the progress of an
356 * operation. Callbacks of this type will be used for uploads,
357 * downloads and searches. Some of the arguments depend a bit
358 * in their meaning on the context in which the callback is used.
361 * @param info details about the event, specifying the event type
362 * and various bits about the event
363 * @return client-context (for the next progress call
364 * for this operation; should be set to NULL for
365 * SUSPEND and STOPPED events). The value returned
366 * will be passed to future callbacks in the respective
367 * field in the GNUNET_FS_ProgressInfo struct.
370 progress_cb (void *cls,
371 const struct GNUNET_FS_ProgressInfo *info)
374 const struct GNUNET_FS_Uri *uri;
376 switch (info->status)
378 case GNUNET_FS_STATUS_PUBLISH_START:
379 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
380 p = info->value.publish.cctx;
382 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
383 p = info->value.publish.cctx;
385 case GNUNET_FS_STATUS_PUBLISH_ERROR:
386 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
387 "Publishing failed\n");
388 GNUNET_STATISTICS_update (stats_handle,
389 "# failed publish operations", 1, GNUNET_NO);
390 p = info->value.publish.cctx;
391 p->task = GNUNET_SCHEDULER_add_now (&publish_stop_task, p);
393 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
394 p = info->value.publish.cctx;
395 GNUNET_STATISTICS_update (stats_handle,
396 "# publishing time (ms)",
397 (long long) GNUNET_TIME_absolute_get_duration (p->start_time).rel_value_us / 1000LL,
399 p->task = GNUNET_SCHEDULER_add_now (&publish_stop_task, p);
401 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
402 p = info->value.publish.cctx;
404 GNUNET_CONTAINER_DLL_remove (publish_head, publish_tail, p);
407 case GNUNET_FS_STATUS_DOWNLOAD_START:
408 case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
409 case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
410 case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
411 p = info->value.download.cctx;
413 case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
414 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
415 "Download failed\n");
416 GNUNET_STATISTICS_update (stats_handle,
417 "# failed downloads", 1, GNUNET_NO);
418 p = info->value.download.cctx;
419 p->task = GNUNET_SCHEDULER_add_now (&download_stop_task, p);
421 case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
422 p = info->value.download.cctx;
423 GNUNET_STATISTICS_update (stats_handle,
424 "# download time (ms)",
425 (long long) GNUNET_TIME_absolute_get_duration (p->start_time).rel_value_us / 1000LL,
427 p->task = GNUNET_SCHEDULER_add_now (&download_stop_task, p);
429 case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
430 p = info->value.download.cctx;
434 GNUNET_CONTAINER_DLL_remove (download_head, download_tail, p);
438 case GNUNET_FS_STATUS_SEARCH_START:
439 case GNUNET_FS_STATUS_SEARCH_RESULT_NAMESPACE:
440 p = info->value.search.cctx;
442 case GNUNET_FS_STATUS_SEARCH_RESULT:
443 p = info->value.search.cctx;
444 uri = info->value.search.specifics.result.uri;
445 if (GNUNET_YES != GNUNET_FS_uri_test_chk (uri))
446 return NULL; /* not what we want */
447 if (p->y != GNUNET_FS_uri_chk_get_file_size (uri))
448 return NULL; /* not what we want */
449 GNUNET_STATISTICS_update (stats_handle,
450 "# search time (ms)",
451 (long long) GNUNET_TIME_absolute_get_duration (p->start_time).rel_value_us / 1000LL,
453 p->start_time = GNUNET_TIME_absolute_get ();
454 p->ctx = GNUNET_FS_download_start (fs_handle, uri,
456 0, GNUNET_FS_uri_chk_get_file_size (uri),
458 GNUNET_FS_DOWNLOAD_NO_TEMPORARIES,
461 p->stask = GNUNET_SCHEDULER_add_now (&search_stop_task, p);
463 case GNUNET_FS_STATUS_SEARCH_UPDATE:
464 case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED:
465 return NULL; /* don't care */
466 case GNUNET_FS_STATUS_SEARCH_ERROR:
467 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
469 GNUNET_STATISTICS_update (stats_handle,
470 "# failed searches", 1, GNUNET_NO);
471 p = info->value.search.cctx;
472 p->stask = GNUNET_SCHEDULER_add_now (&search_stop_task, p);
474 case GNUNET_FS_STATUS_SEARCH_STOPPED:
475 p = info->value.search.cctx;
479 GNUNET_CONTAINER_DLL_remove (download_head, download_tail, p);
484 /* unexpected event during profiling */
492 * Start publish operation.
494 * @param cls the 'struct Pattern' specifying the operation to perform
497 start_publish (void *cls)
499 struct Pattern *p = cls;
500 struct GNUNET_FS_FileInformation *fi;
503 fi = make_file (p->x, p->y, p);
504 p->start_time = GNUNET_TIME_absolute_get ();
505 p->ctx = GNUNET_FS_publish_start (fs_handle,
508 GNUNET_FS_PUBLISH_OPTION_NONE);
513 * Start download operation.
515 * @param cls the 'struct Pattern' specifying the operation to perform
518 start_download (void *cls)
520 struct Pattern *p = cls;
521 struct GNUNET_FS_Uri *keywords;
524 keywords = make_keywords (p->x);
525 p->start_time = GNUNET_TIME_absolute_get ();
526 p->sctx = GNUNET_FS_search_start (fs_handle, keywords,
528 GNUNET_FS_SEARCH_OPTION_NONE,
534 * @brief Main function that will be run by the scheduler.
537 * @param args remaining command-line arguments
538 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
539 * @param cfg_ configuration
542 run (void *cls, char *const *args GNUNET_UNUSED,
543 const char *cfgfile GNUNET_UNUSED,
544 const struct GNUNET_CONFIGURATION_Handle *cfg_)
550 /* Scheduled the task to clean up when shutdown is called */
551 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
555 GNUNET_CONFIGURATION_get_value_number (cfg,
559 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
560 "TESTBED", "PEERID");
561 global_ret = GNUNET_SYSERR;
562 GNUNET_SCHEDULER_shutdown ();
566 GNUNET_CONFIGURATION_get_value_number (cfg,
567 "FSPROFILER", "ANONYMITY_LEVEL",
571 GNUNET_CONFIGURATION_get_value_number (cfg,
572 "FSPROFILER", "REPLICATION_LEVEL",
574 replication_level = 1;
575 GNUNET_snprintf (myoptname, sizeof (myoptname),
576 "DOWNLOAD-PATTERN-%u", my_peerid);
578 GNUNET_CONFIGURATION_get_value_string (cfg,
579 "FSPROFILER", myoptname,
581 download_pattern = GNUNET_strdup ("");
582 GNUNET_snprintf (myoptname, sizeof (myoptname),
583 "PUBLISH-PATTERN-%u", my_peerid);
585 GNUNET_CONFIGURATION_get_value_string (cfg,
586 "FSPROFILER", myoptname,
588 publish_pattern = GNUNET_strdup ("");
590 parse_pattern (&download_head,
592 download_pattern)) ||
594 parse_pattern (&publish_head,
598 GNUNET_SCHEDULER_shutdown ();
602 stats_handle = GNUNET_STATISTICS_create ("fsprofiler", cfg);
604 GNUNET_FS_start (cfg,
607 GNUNET_FS_FLAGS_NONE,
608 GNUNET_FS_OPTIONS_DOWNLOAD_PARALLELISM, 1,
609 GNUNET_FS_OPTIONS_REQUEST_PARALLELISM, 1,
610 GNUNET_FS_OPTIONS_END);
611 if (NULL == fs_handle)
613 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not acquire FS handle. Exiting.\n");
614 global_ret = GNUNET_SYSERR;
615 GNUNET_SCHEDULER_shutdown ();
618 for (p = publish_head; NULL != p; p = p->next)
619 p->task = GNUNET_SCHEDULER_add_delayed (p->delay,
621 for (p = download_head; NULL != p; p = p->next)
622 p->task = GNUNET_SCHEDULER_add_delayed (p->delay,
628 * Program that performs various "random" FS activities.
630 * @param argc number of arguments from the command line
631 * @param argv command line arguments
632 * @return 0 ok, 1 on error
635 main (int argc, char *const *argv)
637 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
638 GNUNET_GETOPT_OPTION_END
641 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
644 GNUNET_PROGRAM_run (argc, argv, "gnunet-daemon-fsprofiler",
646 ("Daemon to use file-sharing to measure its performance."),
647 options, &run, NULL)) ? global_ret : 1;
650 /* end of gnunet-daemon-fsprofiler.c */