gnunet

Main GNUnet Logic
Log | Files | Refs | Submodules | README | LICENSE

gnunet-auto-share.c (21964B)


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