2 This file is part of GNUnet.
3 (C) 2001--2012 Christian Grothoff (and other contributing authors)
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.
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.
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.
21 * @file fs/gnunet-auto-share.c
22 * @brief automatically publish files on GNUnet
23 * @author Christian Grothoff
26 #include "gnunet_util_lib.h"
30 * Item in our work queue (or in the set of files/directories
31 * we have successfully published).
37 * PENDING Work is kept in a linked list.
39 struct WorkItem *prev;
42 * PENDING Work is kept in a linked list.
44 struct WorkItem *next;
47 * Filename of the work item.
52 * Unique identity for this work item (used to detect
53 * if we need to do the work again).
55 struct GNUNET_HashCode id;
60 * Global return value from 'main'.
65 * Are we running 'verbosely'?
70 * Configuration to use.
72 static const struct GNUNET_CONFIGURATION_Handle *cfg;
75 * Disable extractor option to use for publishing.
77 static int disable_extractor;
80 * Disable creation time option to use for publishing.
82 static int do_disable_creation_time;
85 * Handle for the 'shutdown' task.
87 static GNUNET_SCHEDULER_TaskIdentifier kill_task;
90 * Handle for the main task that does scanning and working.
92 static GNUNET_SCHEDULER_TaskIdentifier run_task;
95 * Anonymity level option to use for publishing.
97 static unsigned int anonymity_level = 1;
100 * Content priority option to use for publishing.
102 static unsigned int content_priority = 365;
105 * Replication level option to use for publishing.
107 static unsigned int replication_level = 1;
110 * Top-level directory we monitor to auto-publish.
112 static const char *dir_name;
115 * Head of linked list of files still to publish.
117 static struct WorkItem *work_head;
120 * Tail of linked list of files still to publish.
122 static struct WorkItem *work_tail;
125 * Map from the hash of the filename (!) to a 'struct WorkItem'
128 static struct GNUNET_CONTAINER_MultiHashMap *work_finished;
131 * Set to GNUNET_YES if we are shutting down.
133 static int do_shutdown;
136 * Start time of the current round; used to determine how long
137 * one iteration takes (which influences how fast we schedule
140 static struct GNUNET_TIME_Absolute start_time;
144 * Compute the name of the state database file we will use.
151 GNUNET_asprintf (&ret,
154 (DIR_SEPARATOR == dir_name[strlen(dir_name)-1]) ? "" : DIR_SEPARATOR_STR);
160 * Load the set of 'work_finished' items from disk.
166 struct GNUNET_BIO_ReadHandle *rh;
168 struct GNUNET_HashCode id;
173 fn = get_state_file ();
174 rh = GNUNET_BIO_read_open (fn);
179 if (GNUNET_OK != GNUNET_BIO_read_int32 (rh, &n))
184 GNUNET_BIO_read_string (rh, "filename", &fn, 1024)) ||
186 GNUNET_BIO_read (rh, "id", &id, sizeof (struct GNUNET_HashCode))) )
188 wi = GNUNET_malloc (sizeof (struct WorkItem));
192 GNUNET_CRYPTO_hash (wi->filename,
193 strlen (wi->filename),
195 GNUNET_CONTAINER_multihashmap_put (work_finished,
198 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
201 GNUNET_BIO_read_close (rh, &emsg))
205 GNUNET_free_non_null (fn);
207 GNUNET_BIO_read_close (rh, &emsg);
208 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
209 _("Failed to load state: %s\n"),
211 GNUNET_free_non_null (emsg);
216 * Write work item from the work_finished map to the given write handle.
218 * @param cls the 'struct GNUNET_BIO_WriteHandle*'
219 * @param key key of the item in the map (unused)
220 * @param value the 'struct WorkItem' to write
221 * @return GNUNET_OK to continue to iterate (if write worked)
224 write_item (void *cls,
225 const struct GNUNET_HashCode *key,
228 struct GNUNET_BIO_WriteHandle *wh = cls;
229 struct WorkItem *wi = value;
232 GNUNET_BIO_write_string (wh, wi->filename)) ||
234 GNUNET_BIO_write (wh,
236 sizeof (struct GNUNET_HashCode))) )
237 return GNUNET_SYSERR; /* write error, abort iteration */
243 * Save the set of 'work_finished' items on disk.
249 struct GNUNET_BIO_WriteHandle *wh;
252 n = GNUNET_CONTAINER_multihashmap_size (work_finished);
253 fn = get_state_file ();
254 wh = GNUNET_BIO_write_open (fn);
256 GNUNET_BIO_write_int32 (wh, n))
258 (void) GNUNET_BIO_write_close (wh);
259 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
260 _("Failed to save state to file %s\n"),
265 (void) GNUNET_CONTAINER_multihashmap_iterate (work_finished,
268 if (GNUNET_OK != GNUNET_BIO_write_close (wh))
269 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
270 _("Failed to save state to file %s\n"),
277 * Task run on shutdown. Serializes our current state to disk.
279 * @param cls closure, unused
280 * @param tc scheduler context, unused
283 do_stop_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
285 kill_task = GNUNET_SCHEDULER_NO_TASK;
286 do_shutdown = GNUNET_YES;
287 if (GNUNET_SCHEDULER_NO_TASK != run_task)
289 GNUNET_SCHEDULER_cancel (run_task);
290 run_task = GNUNET_SCHEDULER_NO_TASK;
296 * Decide what the next task is (working or scanning) and schedule it.
299 schedule_next_task (void);
303 * Function called to process work items.
305 * @param cls closure, NULL
306 * @param tc scheduler context (unused)
310 const struct GNUNET_SCHEDULER_TaskContext *tc)
313 struct GNUNET_HashCode key;
315 run_task = GNUNET_SCHEDULER_NO_TASK;
317 GNUNET_CONTAINER_DLL_remove (work_head,
320 // FIXME: actually run 'publish' here!
322 GNUNET_CRYPTO_hash (wi->filename,
323 strlen (wi->filename),
325 GNUNET_CONTAINER_multihashmap_put (work_finished,
328 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
330 schedule_next_task ();
335 * Recursively scan the given file/directory structure to determine
336 * a unique ID that represents the current state of the hierarchy.
338 * @param cls where to store the unique ID we are computing
339 * @param filename file to scan
340 * @return GNUNET_OK (always)
343 determine_id (void *cls,
344 const char *filename)
346 struct GNUNET_HashCode *id = cls;
348 struct GNUNET_HashCode fx[2];
349 struct GNUNET_HashCode ft;
351 if (NULL != strstr (filename,
352 DIR_SEPARATOR_STR ".auto-share"))
353 return GNUNET_OK; /* skip internal file */
354 if (0 != STAT (filename, &sbuf))
356 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", filename);
359 GNUNET_CRYPTO_hash (filename, strlen (filename), &fx[0]);
360 if (!S_ISDIR (sbuf.st_mode))
362 uint64_t fsize = GNUNET_htonll (sbuf.st_size);
364 GNUNET_CRYPTO_hash (&fsize, sizeof (uint64_t), &fx[1]);
368 memset (&fx[1], 1, sizeof (struct GNUNET_HashCode));
369 GNUNET_DISK_directory_scan (filename,
373 /* use hash here to make hierarchical structure distinct from
374 all files on the same level */
375 GNUNET_CRYPTO_hash (fx, sizeof (fx), &ft);
376 /* use XOR here so that order of the files in the directory
378 GNUNET_CRYPTO_hash_xor (&ft, id, id);
384 * Function called with a filename (or directory name) to publish
385 * (if it has changed since the last time we published it). This function
386 * is called for the top-level files only.
388 * @param cls closure, NULL
389 * @param filename complete filename (absolute path)
390 * @return GNUNET_OK to continue to iterate, GNUNET_SYSERR during shutdown
394 const char *filename)
397 struct GNUNET_HashCode key;
398 struct GNUNET_HashCode id;
400 if (GNUNET_YES == do_shutdown)
401 return GNUNET_SYSERR;
402 GNUNET_CRYPTO_hash (filename,
405 wi = GNUNET_CONTAINER_multihashmap_get (work_finished,
407 memset (&id, 0, sizeof (struct GNUNET_HashCode));
408 determine_id (&id, filename);
411 if (0 == memcmp (&id,
413 sizeof (struct GNUNET_HashCode)))
414 return GNUNET_OK; /* skip: we did this one already */
415 /* contents changed, need to re-do the directory... */
416 GNUNET_CONTAINER_multihashmap_remove (work_finished,
423 wi = GNUNET_malloc (sizeof (struct WorkItem));
424 wi->filename = GNUNET_strdup (filename);
426 GNUNET_CONTAINER_DLL_insert (work_head,
429 if (GNUNET_YES == do_shutdown)
430 return GNUNET_SYSERR;
436 * Periodically run task to update our view of the directory to share.
439 * @param tc scheduler context, unused
442 scan (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
444 run_task = GNUNET_SCHEDULER_NO_TASK;
445 start_time = GNUNET_TIME_absolute_get ();
446 (void) GNUNET_DISK_directory_scan (dir_name,
449 schedule_next_task ();
454 * Decide what the next task is (working or scanning) and schedule it.
457 schedule_next_task ()
459 struct GNUNET_TIME_Relative delay;
461 if (GNUNET_YES == do_shutdown)
463 if (NULL == work_head)
465 /* delay by at most 4h, at least 1s, and otherwise in between depending
466 on how long it took to scan */
467 delay = GNUNET_TIME_absolute_get_duration (start_time);
468 delay = GNUNET_TIME_relative_min (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS,
470 GNUNET_TIME_relative_multiply (delay,
472 delay = GNUNET_TIME_relative_max (delay,
473 GNUNET_TIME_UNIT_MINUTES);
474 run_task = GNUNET_SCHEDULER_add_delayed (delay,
480 run_task = GNUNET_SCHEDULER_add_now (&work, NULL);
486 * Main function that will be run by the scheduler.
489 * @param args remaining command-line arguments
490 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
491 * @param c configuration
494 run (void *cls, char *const *args, const char *cfgfile,
495 const struct GNUNET_CONFIGURATION_Handle *c)
497 /* check arguments */
498 if ((args[0] == NULL) || (args[1] != NULL) ||
499 (GNUNET_YES != GNUNET_DISK_directory_test (args[0])))
501 printf (_("You must specify one and only one directory name for automatic publication.\n"));
507 work_finished = GNUNET_CONTAINER_multihashmap_create (1024);
509 run_task = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
513 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_stop_task,
519 * Free memory associated with the work item from the work_finished map.
521 * @param cls NULL (unused)
522 * @param key key of the item in the map (unused)
523 * @param value the 'struct WorkItem' to free
524 * @return GNUNET_OK to continue to iterate
527 free_item (void *cls,
528 const struct GNUNET_HashCode *key,
531 struct WorkItem *wi = value;
533 GNUNET_free (wi->filename);
539 * The main function to automatically publish content to GNUnet.
541 * @param argc number of arguments from the command line
542 * @param argv command line arguments
543 * @return 0 ok, 1 on error
546 main (int argc, char *const *argv)
548 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
549 {'a', "anonymity", "LEVEL",
550 gettext_noop ("set the desired LEVEL of sender-anonymity"),
551 1, &GNUNET_GETOPT_set_uint, &anonymity_level},
552 {'d', "disable-creation-time", NULL,
554 ("disable adding the creation time to the metadata of the uploaded file"),
555 0, &GNUNET_GETOPT_set_one, &do_disable_creation_time},
556 {'D', "disable-extractor", NULL,
557 gettext_noop ("do not use libextractor to add keywords or metadata"),
558 0, &GNUNET_GETOPT_set_one, &disable_extractor},
559 {'p', "priority", "PRIORITY",
560 gettext_noop ("specify the priority of the content"),
561 1, &GNUNET_GETOPT_set_uint, &content_priority},
562 {'r', "replication", "LEVEL",
563 gettext_noop ("set the desired replication LEVEL"),
564 1, &GNUNET_GETOPT_set_uint, &replication_level},
565 {'V', "verbose", NULL,
566 gettext_noop ("be verbose (print progress information)"),
567 0, &GNUNET_GETOPT_set_one, &verbose},
568 GNUNET_GETOPT_OPTION_END
573 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
576 GNUNET_PROGRAM_run (argc, argv, "gnunet-auto-share [OPTIONS] FILENAME",
578 ("Automatically publish files from a directory on GNUnet"),
579 options, &run, NULL)) ? ret : 1;
580 (void) GNUNET_CONTAINER_multihashmap_iterate (work_finished,
583 GNUNET_CONTAINER_multihashmap_destroy (work_finished);
584 while (NULL != (wi = work_head))
586 GNUNET_CONTAINER_DLL_remove (work_head, work_tail, wi);
587 GNUNET_free (wi->filename);
593 /* end of gnunet-auto-share.c */