-remove trailing whitespace
[oweals/gnunet.git] / src / fs / gnunet-auto-share.c
1 /*
2      This file is part of GNUnet.
3      (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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, 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 GNUNET_SCHEDULER_TaskIdentifier kill_task;
101
102 /**
103  * Handle for the main task that does scanning and working.
104  */
105 static GNUNET_SCHEDULER_TaskIdentifier 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_malloc (sizeof (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_CONTAINER_multihashmap_put (work_finished,
223                                        &id,
224                                        wi,
225                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
226   }
227   if (GNUNET_OK ==
228       GNUNET_BIO_read_close (rh, &emsg))
229     return;
230   rh = NULL;
231  error:
232   GNUNET_free_non_null (fn);
233   if (NULL != rh)
234     (void) GNUNET_BIO_read_close (rh, &emsg);
235   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
236               _("Failed to load state: %s\n"),
237               emsg);
238   GNUNET_free_non_null (emsg);
239 }
240
241
242 /**
243  * Write work item from the work_finished map to the given write handle.
244  *
245  * @param cls the 'struct GNUNET_BIO_WriteHandle*'
246  * @param key key of the item in the map (unused)
247  * @param value the 'struct WorkItem' to write
248  * @return GNUNET_OK to continue to iterate (if write worked)
249  */
250 static int
251 write_item (void *cls,
252             const struct GNUNET_HashCode *key,
253             void *value)
254 {
255   struct GNUNET_BIO_WriteHandle *wh = cls;
256   struct WorkItem *wi = value;
257
258   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
259               "Saving serialization ID of file `%s' with value `%s'\n",
260               wi->filename,
261               GNUNET_h2s (&wi->id));
262   if ( (GNUNET_OK !=
263         GNUNET_BIO_write_string (wh, wi->filename)) ||
264        (GNUNET_OK !=
265         GNUNET_BIO_write (wh,
266                           &wi->id,
267                           sizeof (struct GNUNET_HashCode))) )
268     return GNUNET_SYSERR; /* write error, abort iteration */
269   return GNUNET_OK;
270 }
271
272
273 /**
274  * Save the set of 'work_finished' items on disk.
275  */
276 static void
277 save_state ()
278 {
279   uint32_t n;
280   struct GNUNET_BIO_WriteHandle *wh;
281   char *fn;
282
283   n = GNUNET_CONTAINER_multihashmap_size (work_finished);
284   fn = get_state_file ();
285   wh = GNUNET_BIO_write_open (fn);
286   if (NULL == wh)
287   {
288     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
289                 _("Failed to save state to file %s\n"),
290                 fn);
291     GNUNET_free (fn);
292     return;
293   }
294   if (GNUNET_OK !=
295       GNUNET_BIO_write_int32 (wh, n))
296   {
297     (void) GNUNET_BIO_write_close (wh);
298     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
299                 _("Failed to save state to file %s\n"),
300                 fn);
301     GNUNET_free (fn);
302     return;
303   }
304   (void) GNUNET_CONTAINER_multihashmap_iterate (work_finished,
305                                                 &write_item,
306                                                 wh);
307   if (GNUNET_OK != GNUNET_BIO_write_close (wh))
308     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
309                 _("Failed to save state to file %s\n"),
310                 fn);
311   GNUNET_free (fn);
312 }
313
314
315 /**
316  * Task run on shutdown.  Serializes our current state to disk.
317  *
318  * @param cls closure, unused
319  * @param tc scheduler context, unused
320  */
321 static void
322 do_stop_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
323 {
324   kill_task = GNUNET_SCHEDULER_NO_TASK;
325   do_shutdown = GNUNET_YES;
326   if (NULL != publish_proc)
327   {
328     GNUNET_OS_process_kill (publish_proc, SIGKILL);
329     return;
330   }
331   if (GNUNET_SCHEDULER_NO_TASK != run_task)
332   {
333     GNUNET_SCHEDULER_cancel (run_task);
334     run_task = GNUNET_SCHEDULER_NO_TASK;
335   }
336 }
337
338
339 /**
340  * Decide what the next task is (working or scanning) and schedule it.
341  */
342 static void
343 schedule_next_task (void);
344
345
346 /**
347  * Task triggered whenever we receive a SIGCHLD (child
348  * process died).
349  *
350  * @param cls the 'struct WorkItem' we were working on
351  * @param tc context
352  */
353 static void
354 maint_child_death (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
355 {
356   struct WorkItem *wi = cls;
357   struct GNUNET_HashCode key;
358   enum GNUNET_OS_ProcessStatusType type;
359   unsigned long code;
360   int ret;
361   char c;
362   const struct GNUNET_DISK_FileHandle *pr;
363
364
365   run_task = GNUNET_SCHEDULER_NO_TASK;
366   pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
367   if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
368   {
369     /* shutdown scheduled us, ignore! */
370     run_task =
371       GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
372                                       pr, &maint_child_death, wi);
373     return;
374   }
375
376   ret = GNUNET_OS_process_status (publish_proc,
377                                   &type,
378                                   &code);
379   GNUNET_assert (GNUNET_SYSERR != ret);
380   if (GNUNET_NO == ret)
381   {
382     GNUNET_break (0);
383     GNUNET_OS_process_kill (publish_proc, SIGKILL);
384     type = GNUNET_OS_PROCESS_SIGNALED;
385   }
386   GNUNET_OS_process_destroy (publish_proc);
387   publish_proc = NULL;
388   /* consume the signal */
389   GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c)));
390
391   if (GNUNET_YES == do_shutdown)
392   {
393     GNUNET_free (wi->filename);
394     GNUNET_free (wi);
395     return;
396   }
397   if ( (GNUNET_OS_PROCESS_EXITED == type) &&
398        (0 == code) )
399   {
400     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
401                 _("Publication of `%s' done\n"),
402                 wi->filename);
403     GNUNET_CRYPTO_hash (wi->filename,
404                         strlen (wi->filename),
405                         &key);
406     GNUNET_CONTAINER_multihashmap_put (work_finished,
407                                        &key,
408                                        wi,
409                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
410   }
411   else
412   {
413     GNUNET_CONTAINER_DLL_insert_tail (work_head,
414                                       work_tail,
415                                       wi);
416   }
417   save_state ();
418   schedule_next_task ();
419 }
420
421
422 /**
423  * Signal handler called for SIGCHLD.  Triggers the
424  * respective handler by writing to the trigger pipe.
425  */
426 static void
427 sighandler_child_death ()
428 {
429   static char c;
430   int old_errno = errno;        /* back-up errno */
431
432   GNUNET_break (1 ==
433                 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
434                                         (sigpipe, GNUNET_DISK_PIPE_END_WRITE),
435                                         &c, sizeof (c)));
436   errno = old_errno;            /* restore errno */
437 }
438
439
440 /**
441  * Function called to process work items.
442  *
443  * @param cls closure, NULL
444  * @param tc scheduler context (unused)
445  */
446 static void
447 work (void *cls,
448       const struct GNUNET_SCHEDULER_TaskContext *tc)
449 {
450   static char *argv[14];
451   static char anon_level[20];
452   static char content_prio[20];
453   static char repl_level[20];
454   struct WorkItem *wi;
455   const struct GNUNET_DISK_FileHandle *pr;
456   int argc;
457
458   run_task = GNUNET_SCHEDULER_NO_TASK;
459   wi = work_head;
460   GNUNET_CONTAINER_DLL_remove (work_head,
461                                work_tail,
462                                wi);
463   argc = 0;
464   argv[argc++] = "gnunet-publish";
465   if (verbose)
466     argv[argc++] = "-V";
467   if (disable_extractor)
468     argv[argc++] = "-D";
469   if (do_disable_creation_time)
470     argv[argc++] = "-d";
471   argv[argc++] = "-c";
472   argv[argc++] = cfg_filename;
473   GNUNET_snprintf (anon_level, sizeof (anon_level),
474                    "%u", anonymity_level);
475   argv[argc++] = "-a";
476   argv[argc++] = anon_level;
477   GNUNET_snprintf (content_prio, sizeof (content_prio),
478                    "%u", content_priority);
479   argv[argc++] = "-p";
480   argv[argc++] = content_prio;
481   GNUNET_snprintf (repl_level, sizeof (repl_level),
482                    "%u", replication_level);
483   argv[argc++] = "-r";
484   argv[argc++] = repl_level;
485   argv[argc++] = wi->filename;
486   argv[argc] = NULL;
487   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
488               _("Publishing `%s'\n"),
489               wi->filename);
490   publish_proc = GNUNET_OS_start_process_vap (GNUNET_YES,
491                                               0, NULL, NULL,
492                                               "gnunet-publish",
493                                               argv);
494   if (NULL == publish_proc)
495   {
496     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
497                 _("Failed to run `%s'\n"),
498                 "gnunet-publish");
499     GNUNET_CONTAINER_DLL_insert (work_head,
500                                  work_tail,
501                                  wi);
502     run_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
503                                              &work,
504                                              NULL);
505     return;
506   }
507   pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
508   run_task =
509     GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
510                                     pr, &maint_child_death, wi);
511 }
512
513
514 /**
515  * Recursively scan the given file/directory structure to determine
516  * a unique ID that represents the current state of the hierarchy.
517  *
518  * @param cls where to store the unique ID we are computing
519  * @param filename file to scan
520  * @return GNUNET_OK (always)
521  */
522 static int
523 determine_id (void *cls,
524               const char *filename)
525 {
526   struct GNUNET_HashCode *id = cls;
527   struct stat sbuf;
528   struct GNUNET_HashCode fx[2];
529   struct GNUNET_HashCode ft;
530
531   if (0 != STAT (filename, &sbuf))
532   {
533     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", filename);
534     return GNUNET_OK;
535   }
536   GNUNET_CRYPTO_hash (filename, strlen (filename), &fx[0]);
537   if (!S_ISDIR (sbuf.st_mode))
538   {
539     uint64_t fattr[2];
540
541     fattr[0] = GNUNET_htonll (sbuf.st_size);
542     fattr[0] = GNUNET_htonll (sbuf.st_mtime);
543
544     GNUNET_CRYPTO_hash (fattr, sizeof (fattr), &fx[1]);
545   }
546   else
547   {
548     memset (&fx[1], 1, sizeof (struct GNUNET_HashCode));
549     GNUNET_DISK_directory_scan (filename,
550                                 &determine_id,
551                                 &fx[1]);
552   }
553   /* use hash here to make hierarchical structure distinct from
554      all files on the same level */
555   GNUNET_CRYPTO_hash (fx, sizeof (fx), &ft);
556   /* use XOR here so that order of the files in the directory
557      does not matter! */
558   GNUNET_CRYPTO_hash_xor (&ft, id, id);
559   return GNUNET_OK;
560 }
561
562
563 /**
564  * Function called with a filename (or directory name) to publish
565  * (if it has changed since the last time we published it).  This function
566  * is called for the top-level files only.
567  *
568  * @param cls closure, NULL
569  * @param filename complete filename (absolute path)
570  * @return GNUNET_OK to continue to iterate, GNUNET_SYSERR during shutdown
571  */
572 static int
573 add_file (void *cls,
574           const char *filename)
575 {
576   struct WorkItem *wi;
577   struct GNUNET_HashCode key;
578   struct GNUNET_HashCode id;
579
580   if (GNUNET_YES == do_shutdown)
581     return GNUNET_SYSERR;
582   if ( (NULL != strstr (filename,
583                       "/.auto-share")) ||
584        (NULL != strstr (filename,
585                         "\\.auto-share")) )
586     return GNUNET_OK; /* skip internal file */
587   GNUNET_CRYPTO_hash (filename,
588                       strlen (filename),
589                       &key);
590   wi = GNUNET_CONTAINER_multihashmap_get (work_finished,
591                                           &key);
592   memset (&id, 0, sizeof (struct GNUNET_HashCode));
593   determine_id (&id, filename);
594   if (NULL != wi)
595   {
596     if (0 == memcmp (&id,
597                      &wi->id,
598                      sizeof (struct GNUNET_HashCode)))
599       return GNUNET_OK; /* skip: we did this one already */
600     /* contents changed, need to re-do the directory... */
601     GNUNET_assert (GNUNET_YES ==
602                    GNUNET_CONTAINER_multihashmap_remove (work_finished,
603                                                          &key,
604                                                          wi));
605   }
606   else
607   {
608     wi = GNUNET_malloc (sizeof (struct WorkItem));
609     wi->filename = GNUNET_strdup (filename);
610   }
611   wi->id = id;
612   GNUNET_CONTAINER_DLL_insert (work_head,
613                                work_tail,
614                                wi);
615   if (GNUNET_YES == do_shutdown)
616     return GNUNET_SYSERR;
617   return GNUNET_OK;
618 }
619
620
621 /**
622  * Periodically run task to update our view of the directory to share.
623  *
624  * @param cls NULL
625  * @param tc scheduler context, unused
626  */
627 static void
628 scan (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
629 {
630   run_task = GNUNET_SCHEDULER_NO_TASK;
631   start_time = GNUNET_TIME_absolute_get ();
632   (void) GNUNET_DISK_directory_scan (dir_name,
633                                      &add_file,
634                                      NULL);
635   schedule_next_task ();
636 }
637
638
639 /**
640  * Decide what the next task is (working or scanning) and schedule it.
641  */
642 static void
643 schedule_next_task ()
644 {
645   struct GNUNET_TIME_Relative delay;
646
647   if (GNUNET_YES == do_shutdown)
648     return;
649   if (NULL == work_head)
650   {
651     /* delay by at most 4h, at least 1s, and otherwise in between depending
652        on how long it took to scan */
653     delay = GNUNET_TIME_absolute_get_duration (start_time);
654     delay = GNUNET_TIME_relative_min (MIN_FREQUENCY,
655                                       GNUNET_TIME_relative_multiply (delay,
656                                                                      100));
657     delay = GNUNET_TIME_relative_max (delay,
658                                       MAX_FREQUENCY);
659     run_task = GNUNET_SCHEDULER_add_delayed (delay,
660                                              &scan,
661                                              NULL);
662   }
663   else
664   {
665     run_task = GNUNET_SCHEDULER_add_now (&work, NULL);
666   }
667 }
668
669
670 /**
671  * Main function that will be run by the scheduler.
672  *
673  * @param cls closure
674  * @param args remaining command-line arguments
675  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
676  * @param c configuration
677  */
678 static void
679 run (void *cls, char *const *args, const char *cfgfile,
680      const struct GNUNET_CONFIGURATION_Handle *c)
681 {
682   /* check arguments */
683   if ((args[0] == NULL) || (args[1] != NULL) ||
684       (GNUNET_YES != GNUNET_DISK_directory_test (args[0], GNUNET_YES)))
685   {
686     printf (_("You must specify one and only one directory name for automatic publication.\n"));
687     ret = -1;
688     return;
689   }
690   cfg_filename = GNUNET_strdup (cfgfile);
691   cfg = c;
692   dir_name = args[0];
693   work_finished = GNUNET_CONTAINER_multihashmap_create (1024, GNUNET_NO);
694   load_state ();
695   run_task = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
696                                                  &scan, NULL);
697
698   kill_task =
699       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_stop_task,
700                                     NULL);
701 }
702
703
704 /**
705  * Free memory associated with the work item from the work_finished map.
706  *
707  * @param cls NULL (unused)
708  * @param key key of the item in the map (unused)
709  * @param value the 'struct WorkItem' to free
710  * @return GNUNET_OK to continue to iterate
711  */
712 static int
713 free_item (void *cls,
714            const struct GNUNET_HashCode *key,
715            void *value)
716 {
717   struct WorkItem *wi = value;
718
719   GNUNET_free (wi->filename);
720   GNUNET_free (wi);
721   return GNUNET_OK;
722 }
723
724
725 /**
726  * The main function to automatically publish content to GNUnet.
727  *
728  * @param argc number of arguments from the command line
729  * @param argv command line arguments
730  * @return 0 ok, 1 on error
731  */
732 int
733 main (int argc, char *const *argv)
734 {
735   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
736     {'a', "anonymity", "LEVEL",
737      gettext_noop ("set the desired LEVEL of sender-anonymity"),
738      1, &GNUNET_GETOPT_set_uint, &anonymity_level},
739     {'d', "disable-creation-time", NULL,
740      gettext_noop
741      ("disable adding the creation time to the metadata of the uploaded file"),
742      0, &GNUNET_GETOPT_set_one, &do_disable_creation_time},
743     {'D', "disable-extractor", NULL,
744      gettext_noop ("do not use libextractor to add keywords or metadata"),
745      0, &GNUNET_GETOPT_set_one, &disable_extractor},
746     {'p', "priority", "PRIORITY",
747      gettext_noop ("specify the priority of the content"),
748      1, &GNUNET_GETOPT_set_uint, &content_priority},
749     {'r', "replication", "LEVEL",
750      gettext_noop ("set the desired replication LEVEL"),
751      1, &GNUNET_GETOPT_set_uint, &replication_level},
752     {'V', "verbose", NULL,
753      gettext_noop ("be verbose (print progress information)"),
754      0, &GNUNET_GETOPT_set_one, &verbose},
755     GNUNET_GETOPT_OPTION_END
756   };
757   struct WorkItem *wi;
758   int ok;
759   struct GNUNET_SIGNAL_Context *shc_chld;
760
761   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
762     return 2;
763   sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO, GNUNET_NO);
764   GNUNET_assert (sigpipe != NULL);
765   shc_chld =
766     GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death);
767   ok = (GNUNET_OK ==
768         GNUNET_PROGRAM_run (argc, argv, "gnunet-auto-share [OPTIONS] FILENAME",
769                             gettext_noop
770                             ("Automatically publish files from a directory on GNUnet"),
771                             options, &run, NULL)) ? ret : 1;
772   if (NULL != work_finished)
773   {
774     (void) GNUNET_CONTAINER_multihashmap_iterate (work_finished,
775                                                   &free_item,
776                                                   NULL);
777     GNUNET_CONTAINER_multihashmap_destroy (work_finished);
778   }
779   while (NULL != (wi = work_head))
780   {
781     GNUNET_CONTAINER_DLL_remove (work_head, work_tail, wi);
782     GNUNET_free (wi->filename);
783     GNUNET_free (wi);
784   }
785   GNUNET_SIGNAL_handler_uninstall (shc_chld);
786   shc_chld = NULL;
787   GNUNET_DISK_pipe_close (sigpipe);
788   sigpipe = NULL;
789   GNUNET_free (cfg_filename);
790   cfg_filename = NULL;
791   GNUNET_free ((void*) argv);
792   return ok;
793 }
794
795 /* end of gnunet-auto-share.c */