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