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 */