fix
[oweals/gnunet.git] / src / fs / fs_test_lib.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010 Christian Grothoff (and other contributing authors)
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/fs_test_lib.c
23  * @brief library routines for testing FS publishing and downloading
24  *        with multiple peers; this code is limited to flat files
25  *        and no keywords (those functions can be tested with
26  *        single-peer setups; this is for testing routing).
27  * @author Christian Grothoff
28  */
29 #include "platform.h"
30 #include "fs_test_lib.h"
31 #include "gnunet_testing_lib.h"
32
33 #define CONNECT_ATTEMPTS 4
34
35 /**
36  * Handle for a daemon started for testing FS.
37  */
38 struct GNUNET_FS_TestDaemon
39 {
40
41   /**
42    * Handle to the file sharing context using this daemon.
43    */
44   struct GNUNET_FS_Handle *fs;
45
46   /**
47    * Handle to the daemon via testing.
48    */
49   struct GNUNET_TESTING_Daemon *daemon;
50
51   /**
52    * Note that 'group' will be the same value for all of the
53    * daemons started jointly.
54    */
55   struct GNUNET_TESTING_PeerGroup *group;
56
57   /**
58    * Configuration for accessing this peer.
59    */
60   struct GNUNET_CONFIGURATION_Handle *cfg;
61
62   /**
63    * ID of this peer.
64    */
65   struct GNUNET_PeerIdentity id;
66
67   /**
68    * Scheduler to use (for publish_cont).
69    */
70   struct GNUNET_SCHEDULER_Handle *publish_sched;
71
72   /**
73    * Function to call when upload is done.
74    */
75   GNUNET_FS_TEST_UriContinuation publish_cont;
76   
77   /**
78    * Closure for publish_cont.
79    */
80   void *publish_cont_cls;
81
82   /**
83    * Task to abort publishing (timeout).
84    */
85   GNUNET_SCHEDULER_TaskIdentifier publish_timeout_task;
86
87   /**
88    * Seed for file generation.
89    */
90   uint32_t publish_seed;
91
92   /**
93    * Context for current publishing operation.
94    */
95   struct GNUNET_FS_PublishContext *publish_context;
96
97   /**
98    * Result URI.
99    */
100   struct GNUNET_FS_Uri *publish_uri;
101
102   /**
103    * Scheduler to use (for download_cont).
104    */
105   struct GNUNET_SCHEDULER_Handle *download_sched;
106
107   /**
108    * Function to call when download is done.
109    */
110   GNUNET_SCHEDULER_Task download_cont;
111
112   /**
113    * Closure for download_cont.
114    */
115   void *download_cont_cls;
116
117   /**
118    * Seed for download verification.
119    */
120   uint32_t download_seed;
121
122   /**
123    * Task to abort downloading (timeout).
124    */
125   GNUNET_SCHEDULER_TaskIdentifier download_timeout_task;
126
127   /**
128    * Context for current download operation.
129    */  
130   struct GNUNET_FS_DownloadContext *download_context;
131
132   /**
133    * Verbosity level of the current operation.
134    */
135   int verbose;
136
137                 
138 };
139
140 /**
141  * Check whether peers successfully shut down.
142  */
143 void shutdown_callback (void *cls,
144                         const char *emsg)
145 {
146   if (emsg != NULL)
147     {
148 #if VERBOSE
149       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
150                   "Shutdown of peers failed!\n");
151 #endif
152     }
153   else
154     {
155 #if VERBOSE
156       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
157                   "All peers successfully shut down!\n");
158 #endif
159     }
160 }
161
162 static void
163 report_uri (void *cls,
164             const struct GNUNET_SCHEDULER_TaskContext *tc)
165 {
166   struct GNUNET_FS_TestDaemon *daemon = cls;
167   GNUNET_FS_TEST_UriContinuation cont;
168   struct GNUNET_FS_Uri *uri;
169
170   GNUNET_FS_publish_stop (daemon->publish_context);
171   daemon->publish_context = NULL;
172   daemon->publish_sched = NULL;
173   cont = daemon->publish_cont;
174   daemon->publish_cont = NULL;
175   uri = daemon->publish_uri;
176   cont (daemon->publish_cont_cls,
177         uri);
178   GNUNET_FS_uri_destroy (uri);
179 }            
180
181
182 static void
183 report_success (void *cls,
184                 const struct GNUNET_SCHEDULER_TaskContext *tc)
185 {
186   struct GNUNET_FS_TestDaemon *daemon = cls;
187
188   GNUNET_FS_download_stop (daemon->download_context, GNUNET_YES);
189   daemon->download_context = NULL;
190   GNUNET_SCHEDULER_add_continuation (daemon->download_sched,
191                                      daemon->download_cont,
192                                      daemon->download_cont_cls,
193                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);      
194   daemon->download_cont = NULL;
195   daemon->download_sched = NULL;
196 }
197
198 static void*
199 progress_cb (void *cls,
200              const struct GNUNET_FS_ProgressInfo *info)
201 {
202   struct GNUNET_FS_TestDaemon *daemon = cls;
203
204   switch (info->status)
205     {
206     case GNUNET_FS_STATUS_PUBLISH_COMPLETED:      
207       GNUNET_SCHEDULER_cancel (daemon->publish_sched,
208                                daemon->publish_timeout_task);
209       daemon->publish_timeout_task = GNUNET_SCHEDULER_NO_TASK;
210       daemon->publish_uri = GNUNET_FS_uri_dup (info->value.publish.specifics.completed.chk_uri);
211       GNUNET_SCHEDULER_add_continuation (daemon->publish_sched,
212                                          &report_uri,
213                                          daemon,
214                                          GNUNET_SCHEDULER_REASON_PREREQ_DONE);
215       break;
216     case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
217       if (daemon->verbose)
218         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
219                     "Publishing at %llu/%llu bytes\n",
220                     (unsigned long long) info->value.publish.completed,
221                     (unsigned long long) info->value.publish.size);
222       break;
223     case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
224       if (daemon->verbose)
225         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
226                     "Download at %llu/%llu bytes\n",
227                     (unsigned long long) info->value.download.completed,
228                     (unsigned long long) info->value.download.size);
229       break;
230     case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
231       GNUNET_SCHEDULER_cancel (daemon->download_sched,
232                                daemon->download_timeout_task);
233       daemon->download_timeout_task = GNUNET_SCHEDULER_NO_TASK;
234       GNUNET_SCHEDULER_add_continuation (daemon->download_sched,
235                                          &report_success,
236                                          daemon,
237                                          GNUNET_SCHEDULER_REASON_PREREQ_DONE);
238       break;
239     case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
240     case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
241       break;
242       /* FIXME: monitor data correctness during download progress */
243       /* FIXME: do performance reports given sufficient verbosity */
244       /* FIXME: advance timeout task to "immediate" on error */
245     default:
246       break;
247     }
248   return NULL;
249 }
250
251
252 struct StartContext
253 {
254   struct GNUNET_SCHEDULER_Handle *sched;
255   struct GNUNET_TIME_Relative timeout;
256   unsigned int total;
257   unsigned int have;
258   struct GNUNET_FS_TestDaemon **daemons;
259   GNUNET_SCHEDULER_Task cont;
260   void *cont_cls;
261   struct GNUNET_TESTING_PeerGroup *group;
262   struct GNUNET_CONFIGURATION_Handle *cfg;
263   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
264 };
265
266
267 static void 
268 notify_running (void *cls,
269                 const struct GNUNET_PeerIdentity *id,
270                 const struct GNUNET_CONFIGURATION_Handle *cfg,
271                 struct GNUNET_TESTING_Daemon *d,
272                 const char *emsg)
273 {
274   struct StartContext *sctx = cls;
275   unsigned int i;
276
277   if (emsg != NULL)
278     {
279       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
280                   _("Failed to start daemon: %s\n"),
281                   emsg);
282       return;
283     }
284   i = 0;
285   while (i < sctx->total)
286     {
287       if (GNUNET_TESTING_daemon_get (sctx->group,
288                                      i) == d)
289         break;
290       i++;
291     }
292   GNUNET_assert (i < sctx->total);
293   GNUNET_assert (sctx->have < sctx->total);
294   GNUNET_assert (sctx->daemons[i]->cfg == NULL);
295   sctx->daemons[i]->cfg = GNUNET_CONFIGURATION_dup (cfg);
296   sctx->daemons[i]->group = sctx->group;
297   sctx->daemons[i]->daemon = d;
298   sctx->daemons[i]->id = *id;
299   sctx->have++;
300   if (sctx->have == sctx->total)
301     {
302       GNUNET_SCHEDULER_add_continuation (sctx->sched,
303                                          sctx->cont,
304                                          sctx->cont_cls,
305                                          GNUNET_SCHEDULER_REASON_PREREQ_DONE);
306       GNUNET_CONFIGURATION_destroy (sctx->cfg);
307       GNUNET_SCHEDULER_cancel (sctx->sched,
308                                sctx->timeout_task);
309       for (i=0;i<sctx->total;i++)
310         {
311           sctx->daemons[i]->fs = GNUNET_FS_start (sctx->sched,
312                                                   sctx->daemons[i]->cfg,
313                                                   "<tester>",
314                                                   &progress_cb,
315                                                   sctx->daemons[i],
316                                                   GNUNET_FS_FLAGS_NONE,
317                                                   GNUNET_FS_OPTIONS_END);
318         }
319       GNUNET_free (sctx);
320     }
321 }
322
323
324 static void
325 start_timeout (void *cls,
326                const struct GNUNET_SCHEDULER_TaskContext *tc)
327 {
328   struct StartContext *sctx = cls;
329   unsigned int i;
330
331   GNUNET_TESTING_daemons_stop (sctx->group,
332                                GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 30), 
333                                &shutdown_callback,
334                                NULL);
335   for (i=0;i<sctx->total;i++)
336     {
337       if (i < sctx->have)
338         GNUNET_CONFIGURATION_destroy (sctx->daemons[i]->cfg);
339       GNUNET_free (sctx->daemons[i]);
340       sctx->daemons[i] = NULL;
341     }
342   GNUNET_CONFIGURATION_destroy (sctx->cfg);
343   GNUNET_SCHEDULER_add_continuation (sctx->sched,
344                                      sctx->cont,
345                                      sctx->cont_cls,
346                                      GNUNET_SCHEDULER_REASON_TIMEOUT);
347   GNUNET_free (sctx);
348 }
349
350
351 /**
352  * Start daemons for testing.
353  *
354  * @param sched scheduler to use
355  * @param template_cfg_file configuration template to use
356  * @param timeout if this operation cannot be completed within the
357  *                given period, call the continuation with an error code
358  * @param total number of daemons to start
359  * @param daemons array of 'total' entries to be initialized
360  *                (array must already be allocated, will be filled)
361  * @param cont function to call when done
362  * @param cont_cls closure for cont
363  */
364 void
365 GNUNET_FS_TEST_daemons_start (struct GNUNET_SCHEDULER_Handle *sched,
366                               const char *template_cfg_file,
367                               struct GNUNET_TIME_Relative timeout,
368                               unsigned int total,
369                               struct GNUNET_FS_TestDaemon **daemons,
370                               GNUNET_SCHEDULER_Task cont,
371                               void *cont_cls)
372 {
373   struct StartContext *sctx;
374   unsigned int i;
375
376   GNUNET_assert (total > 0);
377   sctx = GNUNET_malloc (sizeof (struct StartContext));
378   sctx->sched = sched;
379   sctx->daemons = daemons;
380   sctx->total = total;
381   sctx->cont = cont;
382   sctx->cont_cls = cont_cls;
383   sctx->cfg = GNUNET_CONFIGURATION_create ();
384   if (GNUNET_OK !=
385       GNUNET_CONFIGURATION_load (sctx->cfg,
386                                  template_cfg_file))
387     {
388       GNUNET_break (0);
389       GNUNET_CONFIGURATION_destroy (sctx->cfg);
390       GNUNET_free (sctx);
391       GNUNET_SCHEDULER_add_continuation (sched,
392                                          cont,
393                                          cont_cls,
394                                          GNUNET_SCHEDULER_REASON_TIMEOUT);
395       return;
396     }
397   for (i=0;i<total;i++)
398     daemons[i] = GNUNET_malloc (sizeof (struct GNUNET_FS_TestDaemon));
399   sctx->group = GNUNET_TESTING_daemons_start (sched,
400                                               sctx->cfg,
401                                               total,
402                                               timeout,
403                                               NULL,
404                                               NULL,
405                                               &notify_running,
406                                               sctx,
407                                               NULL, NULL,
408                                               NULL);
409   sctx->timeout_task = GNUNET_SCHEDULER_add_delayed (sched,
410                                                      timeout,
411                                                      &start_timeout,
412                                                      sctx);
413 }
414
415
416 struct ConnectContext
417 {
418   struct GNUNET_SCHEDULER_Handle *sched;
419   GNUNET_SCHEDULER_Task cont;
420   void *cont_cls;
421 };
422
423 /**
424  * Prototype of a function that will be called whenever
425  * two daemons are connected by the testing library.
426  *
427  * @param cls closure
428  * @param first peer id for first daemon
429  * @param second peer id for the second daemon
430  * @param distance distance between the connected peers
431  * @param first_cfg config for the first daemon
432  * @param second_cfg config for the second daemon
433  * @param first_daemon handle for the first daemon
434  * @param second_daemon handle for the second daemon
435  * @param emsg error message (NULL on success)
436  */
437 static void
438 notify_connection (void *cls,
439                    const struct GNUNET_PeerIdentity *first,
440                    const struct GNUNET_PeerIdentity *second,
441                    uint32_t distance,
442                    const struct GNUNET_CONFIGURATION_Handle *first_cfg,
443                    const struct GNUNET_CONFIGURATION_Handle *second_cfg,
444                    struct GNUNET_TESTING_Daemon *first_daemon,
445                    struct GNUNET_TESTING_Daemon *second_daemon,
446                    const char *emsg)
447 {
448   struct ConnectContext *cc = cls;
449   
450   if (emsg != NULL)
451     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
452                 _("Failed to connect peers: %s\n"),
453                 emsg);
454   GNUNET_SCHEDULER_add_continuation (cc->sched,
455                                      cc->cont,
456                                      cc->cont_cls,
457                                      (emsg != NULL) 
458                                      ? GNUNET_SCHEDULER_REASON_TIMEOUT 
459                                      : GNUNET_SCHEDULER_REASON_PREREQ_DONE);
460   GNUNET_free (cc);
461 }
462
463
464 /**
465  * Connect two daemons for testing.
466  *
467  * @param sched scheduler to use
468  * @param daemon1 first daemon to connect
469  * @param daemon2 second first daemon to connect
470  * @param timeout if this operation cannot be completed within the
471  *                given period, call the continuation with an error code
472  * @param cont function to call when done
473  * @param cont_cls closure for cont
474  */
475 void
476 GNUNET_FS_TEST_daemons_connect (struct GNUNET_SCHEDULER_Handle *sched,
477                                 struct GNUNET_FS_TestDaemon *daemon1,
478                                 struct GNUNET_FS_TestDaemon *daemon2,
479                                 struct GNUNET_TIME_Relative timeout,
480                                 GNUNET_SCHEDULER_Task cont,
481                                 void *cont_cls)
482 {
483   struct ConnectContext *ncc;
484
485   ncc = GNUNET_malloc (sizeof (struct ConnectContext));
486   ncc->sched = sched;
487   ncc->cont = cont;
488   ncc->cont_cls = cont_cls;
489   GNUNET_TESTING_daemons_connect (daemon1->daemon,
490                                   daemon2->daemon,
491                                   timeout,
492                                   CONNECT_ATTEMPTS,
493                                   &notify_connection,
494                                   ncc);
495 }
496
497
498 /**
499  * Obtain peer group used for testing.
500  *
501  * @param daemons array with the daemons (must contain at least one)
502  * @return peer group
503  */
504 struct GNUNET_TESTING_PeerGroup *
505 GNUNET_FS_TEST_get_group (struct GNUNET_FS_TestDaemon **daemons)
506 {
507   return daemons[0]->group;  
508 }
509
510
511 /**
512  * Stop daemons used for testing.
513  *
514  * @param sched scheduler to use
515  * @param total number of daemons to stop
516  * @param daemons array with the daemons (values will be clobbered)
517  */
518 void
519 GNUNET_FS_TEST_daemons_stop (struct GNUNET_SCHEDULER_Handle *sched,
520                              unsigned int total,
521                              struct GNUNET_FS_TestDaemon **daemons)
522 {
523   unsigned int i;
524   struct GNUNET_TESTING_PeerGroup *pg;
525
526   GNUNET_assert (total > 0);
527   pg = daemons[0]->group;
528   for (i=0;i<total;i++)
529     {
530       if (daemons[i]->fs != NULL)
531         GNUNET_FS_stop (daemons[i]->fs);
532       if (daemons[i]->cfg != NULL)
533         GNUNET_CONFIGURATION_destroy (daemons[i]->cfg);
534       GNUNET_free (daemons[i]);
535       daemons[i] = NULL;
536     }  
537   GNUNET_TESTING_daemons_stop (pg, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 30), &shutdown_callback, NULL);
538 }
539
540
541 static void
542 publish_timeout (void *cls,
543                  const struct GNUNET_SCHEDULER_TaskContext *tc)
544 {
545   struct GNUNET_FS_TestDaemon *daemon = cls;
546   GNUNET_FS_TEST_UriContinuation cont;
547   
548   cont = daemon->publish_cont;
549   daemon->publish_timeout_task = GNUNET_SCHEDULER_NO_TASK;
550   daemon->publish_cont = NULL;
551   GNUNET_FS_publish_stop (daemon->publish_context);
552   daemon->publish_context = NULL;
553   cont (daemon->publish_cont_cls,
554         NULL);
555 }
556
557
558 static size_t
559 file_generator (void *cls, 
560                 uint64_t offset,
561                 size_t max, 
562                 void *buf,
563                 char **emsg)
564 {
565   struct GNUNET_FS_TestDaemon *daemon = cls;
566   uint64_t pos;
567   uint8_t *cbuf = buf;
568   int mod;
569
570   for (pos=0;pos<max;pos++)
571     {
572       mod = (255 - (offset / 1024 / 32));
573       if (mod == 0)
574         mod = 1;
575       cbuf[pos] = (uint8_t) ((offset * daemon->publish_seed) % mod);  
576     }
577   return max;
578 }
579
580
581
582 /**
583  * Publish a file at the given daemon.
584  *
585  * @param sched scheduler to use
586  * @param daemon where to publish
587  * @param timeout if this operation cannot be completed within the
588  *                given period, call the continuation with an error code
589  * @param anonymity option for publication
590  * @param do_index GNUNET_YES for index, GNUNET_NO for insertion,
591  *                GNUNET_SYSERR for simulation
592  * @param size size of the file to publish
593  * @param seed seed to use for file generation
594  * @param verbose how verbose to be in reporting
595  * @param cont function to call when done
596  * @param cont_cls closure for cont
597  */
598 void
599 GNUNET_FS_TEST_publish (struct GNUNET_SCHEDULER_Handle *sched,
600                         struct GNUNET_FS_TestDaemon *daemon,
601                         struct GNUNET_TIME_Relative timeout,
602                         uint32_t anonymity,
603                         int do_index,
604                         uint64_t size,
605                         uint32_t seed,
606                         unsigned int verbose,
607                         GNUNET_FS_TEST_UriContinuation cont,
608                         void *cont_cls)
609 {
610   GNUNET_assert (daemon->publish_cont == NULL);
611   struct GNUNET_FS_FileInformation *fi;
612
613   daemon->publish_cont = cont;
614   daemon->publish_cont_cls = cont_cls;
615   daemon->publish_seed = seed;
616   daemon->verbose = verbose;
617   daemon->publish_sched = sched;
618   fi = GNUNET_FS_file_information_create_from_reader (daemon->fs,
619                                                       daemon,                                                 
620                                                       size,
621                                                       &file_generator,
622                                                       daemon,
623                                                       NULL,
624                                                       NULL,
625                                                       do_index,
626                                                       anonymity,
627                                                       42 /* priority */,
628                                                       GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS));
629   daemon->publish_context = GNUNET_FS_publish_start (daemon->fs,
630                                                      fi,
631                                                      NULL, NULL, NULL,
632                                                      GNUNET_FS_PUBLISH_OPTION_NONE);
633   daemon->publish_timeout_task = GNUNET_SCHEDULER_add_delayed (sched,
634                                                                timeout,
635                                                                &publish_timeout,
636                                                                daemon);
637 }
638
639
640 static void
641 download_timeout (void *cls,
642                   const struct GNUNET_SCHEDULER_TaskContext *tc)
643 {
644   struct GNUNET_FS_TestDaemon *daemon = cls;
645
646   daemon->download_timeout_task = GNUNET_SCHEDULER_NO_TASK;
647   GNUNET_FS_download_stop (daemon->download_context, GNUNET_YES);
648   daemon->download_context = NULL;
649   GNUNET_SCHEDULER_add_continuation (daemon->download_sched,
650                                      daemon->download_cont,
651                                      daemon->download_cont_cls,
652                                      GNUNET_SCHEDULER_REASON_TIMEOUT);
653   daemon->download_cont = NULL;
654   daemon->download_sched = NULL;
655 }
656
657
658 /**
659  * Perform test download.
660  *
661  * @param sched scheduler to use
662  * @param daemon which peer to download from
663  * @param timeout if this operation cannot be completed within the
664  *                given period, call the continuation with an error code
665  * @param anonymity option for download
666  * @param seed used for file validation
667  * @param uri URI of file to download (CHK/LOC only)
668  * @param verbose how verbose to be in reporting
669  * @param cont function to call when done
670  * @param cont_cls closure for cont
671  */
672 void
673 GNUNET_FS_TEST_download (struct GNUNET_SCHEDULER_Handle *sched,
674                          struct GNUNET_FS_TestDaemon *daemon,
675                          struct GNUNET_TIME_Relative timeout,
676                          uint32_t anonymity,
677                          uint32_t seed,
678                          const struct GNUNET_FS_Uri *uri,
679                          unsigned int verbose,
680                          GNUNET_SCHEDULER_Task cont,
681                          void *cont_cls)
682 {
683   uint64_t size;
684  
685   GNUNET_assert (daemon->download_cont == NULL);
686   size = GNUNET_FS_uri_chk_get_file_size (uri);
687   daemon->verbose = verbose;
688   daemon->download_sched = sched;
689   daemon->download_cont = cont;
690   daemon->download_cont_cls = cont_cls;
691   daemon->download_seed = seed;  
692   daemon->download_context = GNUNET_FS_download_start (daemon->fs,
693                                                        uri,
694                                                        NULL, NULL,
695                                                        NULL,
696                                                        0,
697                                                        size,
698                                                        anonymity,
699                                                        GNUNET_FS_DOWNLOAD_OPTION_NONE,
700                                                        NULL,
701                                                        NULL);
702   daemon->download_timeout_task = GNUNET_SCHEDULER_add_delayed (sched,
703                                                                 timeout,
704                                                                 &download_timeout,
705                                                                 daemon);
706 }
707
708 /* end of test_fs_lib.c */