ae332af49f626204deb543cbdb5da663121f5bd9
[oweals/gnunet.git] / gnunet-auto-share.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2001--2012 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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20 /**
21  * @file fs/gnunet-auto-share.c
22  * @brief automatically publish files on GNUnet
23  * @author Christian Grothoff
24  *
25  * TODO:
26  * - support loading meta data / keywords from resource file
27  * - add stability timer (a la buildbot)
28  */
29 #include "platform.h"
30 #include "gnunet_util_lib.h"
31
32 #define MIN_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4)
33
34 #define MAX_FREQUENCY GNUNET_TIME_UNIT_MINUTES
35
36
37 /**
38  * Item in our work queue (or in the set of files/directories
39  * we have successfully published).
40  */
41 struct WorkItem
42 {
43
44   /**
45    * PENDING Work is kept in a linked list.
46    */
47   struct WorkItem *prev;
48
49   /**
50    * PENDING Work is kept in a linked list.
51    */
52   struct WorkItem *next;
53
54   /**
55    * Filename of the work item.
56    */
57   char *filename;
58
59   /**
60    * Unique identity for this work item (used to detect
61    * if we need to do the work again).
62    */
63   struct GNUNET_HashCode id;
64 };
65
66
67 /**
68  * Global return value from 'main'.
69  */
70 static int ret;
71
72 /**
73  * Are we running 'verbosely'?
74  */
75 static int verbose;
76
77 /**
78  * Configuration to use.
79  */
80 static const struct GNUNET_CONFIGURATION_Handle *cfg;
81
82 /**
83  * Name of the configuration file.
84  */
85 static char *cfg_filename;
86
87 /**
88  * Disable extractor option to use for publishing.
89  */
90 static int disable_extractor;
91
92 /**
93  * Disable creation time option to use for publishing.
94  */
95 static int do_disable_creation_time;
96
97 /**
98  * Handle for the 'shutdown' task.
99  */
100 static struct GNUNET_SCHEDULER_Task *kill_task;
101
102 /**
103  * Handle for the main task that does scanning and working.
104  */
105 static struct GNUNET_SCHEDULER_Task *run_task;
106
107 /**
108  * Anonymity level option to use for publishing.
109  */
110 static unsigned int anonymity_level = 1;
111
112 /**
113  * Content priority option to use for publishing.
114  */
115 static unsigned int content_priority = 365;
116
117 /**
118  * Replication level option to use for publishing.
119  */
120 static unsigned int replication_level = 1;
121
122 /**
123  * Top-level directory we monitor to auto-publish.
124  */
125 static const char *dir_name;
126
127 /**
128  * Head of linked list of files still to publish.
129  */
130 static struct WorkItem *work_head;
131
132 /**
133  * Tail of linked list of files still to publish.
134  */
135 static struct WorkItem *work_tail;
136
137 /**
138  * Map from the hash of the filename (!) to a `struct WorkItem`
139  * that was finished.
140  */
141 static struct GNUNET_CONTAINER_MultiHashMap *work_finished;
142
143 /**
144  * Set to #GNUNET_YES if we are shutting down.
145  */
146 static int do_shutdown;
147
148 /**
149  * Start time of the current round; used to determine how long
150  * one iteration takes (which influences how fast we schedule
151  * the next one).
152  */
153 static struct GNUNET_TIME_Absolute start_time;
154
155 /**
156  * Pipe used to communicate 'gnunet-publish' completion (SIGCHLD) via signal.
157  */
158 static struct GNUNET_DISK_PipeHandle *sigpipe;
159
160 /**
161  * Handle to the 'gnunet-publish' process that we executed.
162  */
163 static struct GNUNET_OS_Process *publish_proc;
164
165
166 /**
167  * Compute the name of the state database file we will use.
168  */
169 static char *
170 get_state_file ()
171 {
172   char *ret;
173
174   GNUNET_asprintf (&ret,
175                    "%s%s.auto-share",
176                    dir_name,
177                    (DIR_SEPARATOR == dir_name[strlen(dir_name)-1]) ? "" : DIR_SEPARATOR_STR);
178   return ret;
179 }
180
181
182 /**
183  * Load the set of #work_finished items from disk.
184  */
185 static void
186 load_state ()
187 {
188   char *fn;
189   struct GNUNET_BIO_ReadHandle *rh;
190   uint32_t n;
191   struct GNUNET_HashCode id;
192   struct WorkItem *wi;
193   char *emsg;
194
195   emsg = NULL;
196   fn = get_state_file ();
197   rh = GNUNET_BIO_read_open (fn);
198   GNUNET_free (fn);
199   if (NULL == rh)
200     return;
201   fn = NULL;
202   if (GNUNET_OK != GNUNET_BIO_read_int32 (rh, &n))
203     goto error;
204   while (n-- > 0)
205   {
206     if ( (GNUNET_OK !=
207           GNUNET_BIO_read_string (rh, "filename", &fn, 1024)) ||
208          (GNUNET_OK !=
209           GNUNET_BIO_read (rh, "id", &id, sizeof (struct GNUNET_HashCode))) )
210       goto error;
211     wi = GNUNET_new (struct WorkItem);
212     wi->id = id;
213     wi->filename = fn;
214     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
215                 "Loaded serialization ID for `%s' is `%s'\n",
216                 wi->filename,
217                 GNUNET_h2s (&id));
218     fn = NULL;
219     GNUNET_CRYPTO_hash (wi->filename,
220                         strlen (wi->filename),
221                         &id);
222     GNUNET_break (GNUNET_OK ==
223                   GNUNET_CONTAINER_multihashmap_put (work_finished,
224                                                      &id,
225                                                      wi,
226                                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
227   }
228   if (GNUNET_OK ==
229       GNUNET_BIO_read_close (rh, &emsg))
230     return;
231   rh = NULL;
232  error:
233   GNUNET_free_non_null (fn);
234   if (NULL != rh)
235     (void) GNUNET_BIO_read_close (rh, &emsg);
236   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
237               _("Failed to load state: %s\n"),
238               emsg);
239   GNUNET_free_non_null (emsg);
240 }
241
242
243 /**
244  * Write work item from the #work_finished map to the given write handle.
245  *
246  * @param cls the `struct GNUNET_BIO_WriteHandle *`
247  * @param key key of the item in the map (unused)
248  * @param value the `struct WorkItem` to write
249  * @return #GNUNET_OK to continue to iterate (if write worked)
250  */
251 static int
252 write_item (void *cls,
253             const struct GNUNET_HashCode *key,
254             void *value)
255 {
256   struct GNUNET_BIO_WriteHandle *wh = cls;
257   struct WorkItem *wi = value;
258
259   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
260               "Saving serialization ID of file `%s' with value `%s'\n",
261               wi->filename,
262               GNUNET_h2s (&wi->id));
263   if ( (GNUNET_OK !=
264         GNUNET_BIO_write_string (wh, wi->filename)) ||
265        (GNUNET_OK !=
266         GNUNET_BIO_write (wh,
267                           &wi->id,
268                           sizeof (struct GNUNET_HashCode))) )
269     return GNUNET_SYSERR; /* write error, abort iteration */
270   return GNUNET_OK;
271 }
272
273
274 /**
275  * Save the set of #work_finished items on disk.
276  */
277 static void
278 save_state ()
279 {
280   uint32_t n;
281   struct GNUNET_BIO_WriteHandle *wh;
282   char *fn;
283
284   n = GNUNET_CONTAINER_multihashmap_size (work_finished);
285   fn = get_state_file ();
286   wh = GNUNET_BIO_write_open (fn);
287   if (NULL == wh)
288   {
289     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
290                 _("Failed to save state to file %s\n"),
291                 fn);
292     GNUNET_free (fn);
293     return;
294   }
295   if (GNUNET_OK !=
296       GNUNET_BIO_write_int32 (wh, n))
297   {
298     (void) GNUNET_BIO_write_close (wh);
299     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
300                 _("Failed to save state to file %s\n"),
301                 fn);
302     GNUNET_free (fn);
303     return;
304   }
305   (void) GNUNET_CONTAINER_multihashmap_iterate (work_finished,
306                                                 &write_item,
307                                                 wh);
308   if (GNUNET_OK != GNUNET_BIO_write_close (wh))
309     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
310                 _("Failed to save state to file %s\n"),
311                 fn);
312   GNUNET_free (fn);
313 }
314
315
316 /**
317  * Task run on shutdown.  Serializes our current state to disk.
318  *
319  * @param cls closure, unused
320  * @param tc scheduler context, unused
321  */
322 static void
323 do_stop_task (void *cls, 
324               const struct GNUNET_SCHEDULER_TaskContext *tc)
325 {
326   kill_task = NULL;
327   do_shutdown = GNUNET_YES;
328   if (NULL != publish_proc)
329   {
330     GNUNET_OS_process_kill (publish_proc, 
331                             SIGKILL);
332     return;
333   }
334   if (NULL != run_task)
335   {
336     GNUNET_SCHEDULER_cancel (run_task);
337     run_task = NULL;
338   }
339 }
340
341
342 /**
343  * Decide what the next task is (working or scanning) and schedule it.
344  */
345 static void
346 schedule_next_task (void);
347
348
349 /**
350  * Task triggered whenever we receive a SIGCHLD (child
351  * process died).
352  *
353  * @param cls the `struct WorkItem` we were working on
354  * @param tc context
355  */
356 static void
357 maint_child_death (void *cls, 
358                    const struct GNUNET_SCHEDULER_TaskContext *tc)
359 {
360   struct WorkItem *wi = cls;
361   struct GNUNET_HashCode key;
362   enum GNUNET_OS_ProcessStatusType type;
363   unsigned long code;
364   int ret;
365   char c;
366   const struct GNUNET_DISK_FileHandle *pr;
367
368   run_task = NULL;
369   pr = GNUNET_DISK_pipe_handle (sigpipe,
370                                 GNUNET_DISK_PIPE_END_READ);
371   if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
372   {
373     /* shutdown scheduled us, someone else will kill child,
374        we should just try again */
375     run_task =
376       GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
377                                       pr,
378                                       &maint_child_death, wi);
379     return;
380   }
381   /* consume the signal */
382   GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c)));
383
384   ret = GNUNET_OS_process_status (publish_proc,
385                                   &type,
386                                   &code);
387   GNUNET_assert (GNUNET_SYSERR != ret);
388   if (GNUNET_NO == ret)
389   {
390     /* process still running? Then where did the SIGCHLD come from?
391        Well, let's declare it spurious (kernel bug?) and keep rolling.
392     */  
393     GNUNET_break (0);
394     run_task =
395       GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
396                                       pr,
397                                       &maint_child_death, wi);
398     return;
399   }
400   GNUNET_assert (GNUNET_OK == ret);
401   
402   GNUNET_OS_process_destroy (publish_proc);
403   publish_proc = NULL;
404
405   if (GNUNET_YES == do_shutdown)
406   {
407     GNUNET_free (wi->filename);
408     GNUNET_free (wi);
409     return;
410   }
411   if ( (GNUNET_OS_PROCESS_EXITED == type) &&
412        (0 == code) )
413   {
414     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
415                 _("Publication of `%s' done\n"),
416                 wi->filename);
417     GNUNET_CRYPTO_hash (wi->filename,
418                         strlen (wi->filename),
419                         &key);
420     GNUNET_break (GNUNET_OK ==
421                   GNUNET_CONTAINER_multihashmap_put (work_finished,
422                                                      &key,
423                                                      wi,
424                                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
425   }
426   else
427   {
428     GNUNET_CONTAINER_DLL_insert_tail (work_head,
429                                       work_tail,
430                                       wi);
431   }
432   save_state ();
433   schedule_next_task ();
434 }
435
436
437 /**
438  * Signal handler called for SIGCHLD.  Triggers the
439  * respective handler by writing to the trigger pipe.
440  */
441 static void
442 sighandler_child_death ()
443 {
444   static char c;
445   int old_errno = errno;        /* back-up errno */
446
447   GNUNET_break (1 ==
448                 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
449                                         (sigpipe,
450                                          GNUNET_DISK_PIPE_END_WRITE),
451                                         &c, sizeof (c)));
452   errno = old_errno;            /* restore errno */
453 }
454
455
456 /**
457  * Function called to process work items.
458  *
459  * @param cls closure, NULL
460  * @param tc scheduler context (unused)
461  */
462 static void
463 work (void *cls,
464       const struct GNUNET_SCHEDULER_TaskContext *tc)
465 {
466   static char *argv[14];
467   static char anon_level[20];
468   static char content_prio[20];
469   static char repl_level[20];
470   struct WorkItem *wi;
471   const struct GNUNET_DISK_FileHandle *pr;
472   int argc;
473
474   run_task = NULL;
475   wi = work_head;
476   GNUNET_CONTAINER_DLL_remove (work_head,
477                                work_tail,
478                                wi);
479   argc = 0;
480   argv[argc++] = "gnunet-publish";
481   if (verbose)
482     argv[argc++] = "-V";
483   if (disable_extractor)
484     argv[argc++] = "-D";
485   if (do_disable_creation_time)
486     argv[argc++] = "-d";
487   argv[argc++] = "-c";
488   argv[argc++] = cfg_filename;
489   GNUNET_snprintf (anon_level, sizeof (anon_level),
490                    "%u", anonymity_level);
491   argv[argc++] = "-a";
492   argv[argc++] = anon_level;
493   GNUNET_snprintf (content_prio, sizeof (content_prio),
494                    "%u", content_priority);
495   argv[argc++] = "-p";
496   argv[argc++] = content_prio;
497   GNUNET_snprintf (repl_level, sizeof (repl_level),
498                    "%u", replication_level);
499   argv[argc++] = "-r";
500   argv[argc++] = repl_level;
501   argv[argc++] = wi->filename;
502   argv[argc] = NULL;
503   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
504               _("Publishing `%s'\n"),
505               wi->filename);
506   GNUNET_assert (NULL == publish_proc);
507   publish_proc = GNUNET_OS_start_process_vap (GNUNET_YES,
508                                               0, NULL, NULL, NULL,
509                                               "gnunet-publish",
510                                               argv);
511   if (NULL == publish_proc)
512   {
513     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
514                 _("Failed to run `%s'\n"),
515                 "gnunet-publish");
516     GNUNET_CONTAINER_DLL_insert (work_head,
517                                  work_tail,
518                                  wi);
519     run_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
520                                              &work,
521                                              NULL);
522     return;
523   }
524   pr = GNUNET_DISK_pipe_handle (sigpipe, 
525                                 GNUNET_DISK_PIPE_END_READ);
526   run_task =
527     GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
528                                     pr,
529                                     &maint_child_death, wi);
530 }
531
532
533 /**
534  * Recursively scan the given file/directory structure to determine
535  * a unique ID that represents the current state of the hierarchy.
536  *
537  * @param cls where to store the unique ID we are computing
538  * @param filename file to scan
539  * @return #GNUNET_OK (always)
540  */
541 static int
542 determine_id (void *cls,
543               const char *filename)
544 {
545   struct GNUNET_HashCode *id = cls;
546   struct stat sbuf;
547   struct GNUNET_HashCode fx[2];
548   struct GNUNET_HashCode ft;
549
550   if (0 != STAT (filename, &sbuf))
551   {
552     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, 
553                               "stat", 
554                               filename);
555     return GNUNET_OK;
556   }
557   GNUNET_CRYPTO_hash (filename, 
558                       strlen (filename), 
559                       &fx[0]);
560   if (!S_ISDIR (sbuf.st_mode))
561   {
562     uint64_t fattr[2];
563
564     fattr[0] = GNUNET_htonll (sbuf.st_size);
565     fattr[0] = GNUNET_htonll (sbuf.st_mtime);
566
567     GNUNET_CRYPTO_hash (fattr, 
568                         sizeof (fattr), 
569                         &fx[1]);
570   }
571   else
572   {
573     memset (&fx[1],
574             1,
575             sizeof (struct GNUNET_HashCode));
576     GNUNET_DISK_directory_scan (filename,
577                                 &determine_id,
578                                 &fx[1]);
579   }
580   /* use hash here to make hierarchical structure distinct from
581      all files on the same level */
582   GNUNET_CRYPTO_hash (fx, 
583                       sizeof (fx),
584                       &ft);
585   /* use XOR here so that order of the files in the directory
586      does not matter! */
587   GNUNET_CRYPTO_hash_xor (&ft,
588                           id,
589                           id);
590   return GNUNET_OK;
591 }
592
593
594 /**
595  * Function called with a filename (or directory name) to publish
596  * (if it has changed since the last time we published it).  This function
597  * is called for the top-level files only.
598  *
599  * @param cls closure, NULL
600  * @param filename complete filename (absolute path)
601  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR during shutdown
602  */
603 static int
604 add_file (void *cls,
605           const char *filename)
606 {
607   struct WorkItem *wi;
608   struct GNUNET_HashCode key;
609   struct GNUNET_HashCode id;
610
611   if (GNUNET_YES == do_shutdown)
612     return GNUNET_SYSERR;
613   if ( (NULL != strstr (filename,
614                       "/.auto-share")) ||
615        (NULL != strstr (filename,
616                         "\\.auto-share")) )
617     return GNUNET_OK; /* skip internal file */
618   GNUNET_CRYPTO_hash (filename,
619                       strlen (filename),
620                       &key);
621   wi = GNUNET_CONTAINER_multihashmap_get (work_finished,
622                                           &key);
623   memset (&id, 0, sizeof (struct GNUNET_HashCode));
624   determine_id (&id, filename);
625   if (NULL != wi)
626   {
627     if (0 == memcmp (&id,
628                      &wi->id,
629                      sizeof (struct GNUNET_HashCode)))
630       return GNUNET_OK; /* skip: we did this one already */
631     /* contents changed, need to re-do the directory... */
632     GNUNET_assert (GNUNET_YES ==
633                    GNUNET_CONTAINER_multihashmap_remove (work_finished,
634                                                          &key,
635                                                          wi));
636   }
637   else
638   {
639     wi = GNUNET_new (struct WorkItem);
640     wi->filename = GNUNET_strdup (filename);
641   }
642   wi->id = id;
643   GNUNET_CONTAINER_DLL_insert (work_head,
644                                work_tail,
645                                wi);
646   if (GNUNET_YES == do_shutdown)
647     return GNUNET_SYSERR;
648   return GNUNET_OK;
649 }
650
651
652 /**
653  * Periodically run task to update our view of the directory to share.
654  *
655  * @param cls NULL
656  * @param tc scheduler context, unused
657  */
658 static void
659 scan (void *cls, 
660       const struct GNUNET_SCHEDULER_TaskContext *tc)
661 {
662   run_task = NULL;
663   start_time = GNUNET_TIME_absolute_get ();
664   (void) GNUNET_DISK_directory_scan (dir_name,
665                                      &add_file,
666                                      NULL);
667   schedule_next_task ();
668 }
669
670
671 /**
672  * Decide what the next task is (working or scanning) and schedule it.
673  */
674 static void
675 schedule_next_task ()
676 {
677   struct GNUNET_TIME_Relative delay;
678
679   if (GNUNET_YES == do_shutdown)
680     return;
681   GNUNET_assert (NULL == run_task);
682   if (NULL == work_head)
683   {
684     /* delay by at most 4h, at least 1s, and otherwise in between depending
685        on how long it took to scan */
686     delay = GNUNET_TIME_absolute_get_duration (start_time);
687     delay = GNUNET_TIME_relative_min (MIN_FREQUENCY,
688                                       GNUNET_TIME_relative_multiply (delay,
689                                                                      100));
690     delay = GNUNET_TIME_relative_max (delay,
691                                       MAX_FREQUENCY);
692     run_task = GNUNET_SCHEDULER_add_delayed (delay,
693                                              &scan,
694                                              NULL);
695   }
696   else
697   {
698     run_task = GNUNET_SCHEDULER_add_now (&work, 
699                                          NULL);
700   }
701 }
702
703
704 /**
705  * Main function that will be run by the scheduler.
706  *
707  * @param cls closure
708  * @param args remaining command-line arguments
709  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
710  * @param c configuration
711  */
712 static void
713 run (void *cls, 
714      char *const *args, 
715      const char *cfgfile,
716      const struct GNUNET_CONFIGURATION_Handle *c)
717 {
718   /* check arguments */
719   if ( (NULL == args[0]) || 
720        (NULL != args[1]) ||
721        (GNUNET_YES != 
722         GNUNET_DISK_directory_test (args[0],
723                                     GNUNET_YES)) )
724   {
725     printf (_("You must specify one and only one directory name for automatic publication.\n"));
726     ret = -1;
727     return;
728   }
729   cfg_filename = GNUNET_strdup (cfgfile);
730   cfg = c;
731   dir_name = args[0];
732   work_finished = GNUNET_CONTAINER_multihashmap_create (1024, 
733                                                         GNUNET_NO);
734   load_state ();
735   run_task = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
736                                                  &scan, 
737                                                  NULL);
738   kill_task =
739       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, 
740                                     &do_stop_task,
741                                     NULL);
742 }
743
744
745 /**
746  * Free memory associated with the work item from the work_finished map.
747  *
748  * @param cls NULL (unused)
749  * @param key key of the item in the map (unused)
750  * @param value the `struct WorkItem` to free
751  * @return #GNUNET_OK to continue to iterate
752  */
753 static int
754 free_item (void *cls,
755            const struct GNUNET_HashCode *key,
756            void *value)
757 {
758   struct WorkItem *wi = value;
759
760   GNUNET_free (wi->filename);
761   GNUNET_free (wi);
762   return GNUNET_OK;
763 }
764
765
766 /**
767  * The main function to automatically publish content to GNUnet.
768  *
769  * @param argc number of arguments from the command line
770  * @param argv command line arguments
771  * @return 0 ok, 1 on error
772  */
773 int
774 main (int argc, char *const *argv)
775 {
776   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
777     {'a', "anonymity", "LEVEL",
778      gettext_noop ("set the desired LEVEL of sender-anonymity"),
779      1, &GNUNET_GETOPT_set_uint, &anonymity_level},
780     {'d', "disable-creation-time", NULL,
781      gettext_noop
782      ("disable adding the creation time to the metadata of the uploaded file"),
783      0, &GNUNET_GETOPT_set_one, &do_disable_creation_time},
784     {'D', "disable-extractor", NULL,
785      gettext_noop ("do not use libextractor to add keywords or metadata"),
786      0, &GNUNET_GETOPT_set_one, &disable_extractor},
787     {'p', "priority", "PRIORITY",
788      gettext_noop ("specify the priority of the content"),
789      1, &GNUNET_GETOPT_set_uint, &content_priority},
790     {'r', "replication", "LEVEL",
791      gettext_noop ("set the desired replication LEVEL"),
792      1, &GNUNET_GETOPT_set_uint, &replication_level},
793     {'V', "verbose", NULL,
794      gettext_noop ("be verbose (print progress information)"),
795      0, &GNUNET_GETOPT_set_one, &verbose},
796     GNUNET_GETOPT_OPTION_END
797   };
798   struct WorkItem *wi;
799   int ok;
800   struct GNUNET_SIGNAL_Context *shc_chld;
801
802   if (GNUNET_OK != 
803       GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
804     return 2;
805   sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, 
806                               GNUNET_NO, GNUNET_NO);
807   GNUNET_assert (NULL != sigpipe);
808   shc_chld =
809     GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD,
810                                    &sighandler_child_death);
811   ok = (GNUNET_OK ==
812         GNUNET_PROGRAM_run (argc, argv, 
813                             "gnunet-auto-share [OPTIONS] FILENAME",
814                             gettext_noop
815                             ("Automatically publish files from a directory on GNUnet"),
816                             options, &run, NULL)) ? ret : 1;
817   if (NULL != work_finished)
818   {
819     (void) GNUNET_CONTAINER_multihashmap_iterate (work_finished,
820                                                   &free_item,
821                                                   NULL);
822     GNUNET_CONTAINER_multihashmap_destroy (work_finished);
823   }
824   while (NULL != (wi = work_head))
825   {
826     GNUNET_CONTAINER_DLL_remove (work_head, 
827                                  work_tail,
828                                  wi);
829     GNUNET_free (wi->filename);
830     GNUNET_free (wi);
831   }
832   GNUNET_SIGNAL_handler_uninstall (shc_chld);
833   shc_chld = NULL;
834   GNUNET_DISK_pipe_close (sigpipe);
835   sigpipe = NULL;
836   GNUNET_free (cfg_filename);
837   cfg_filename = NULL;
838   GNUNET_free ((void*) argv);
839   return ok;
840 }
841
842 /* end of gnunet-auto-share.c */