gnunet-download.c (10747B)
1 /* 2 This file is part of GNUnet. 3 Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009 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-download.c 22 * @brief downloading for files on GNUnet 23 * @author Christian Grothoff 24 * @author Krista Bennett 25 * @author James Blackwell 26 * @author Igor Wronsky 27 */ 28 #include "platform.h" 29 30 #include "gnunet_fs_service.h" 31 32 static int ret; 33 34 static unsigned int verbose; 35 36 static int delete_incomplete; 37 38 static const struct GNUNET_CONFIGURATION_Handle *cfg; 39 40 static struct GNUNET_FS_Handle *ctx; 41 42 static struct GNUNET_FS_DownloadContext *dc; 43 44 static unsigned int anonymity = 1; 45 46 static unsigned int parallelism = 16; 47 48 static unsigned int request_parallelism = 4092; 49 50 static int do_recursive; 51 52 static char *filename; 53 54 static int local_only; 55 56 57 static void 58 cleanup_task (void *cls) 59 { 60 GNUNET_FS_stop (ctx); 61 ctx = NULL; 62 } 63 64 65 static void 66 shutdown_task (void *cls) 67 { 68 if (NULL != dc) 69 { 70 GNUNET_FS_download_stop (dc, delete_incomplete); 71 dc = NULL; 72 } 73 } 74 75 76 /** 77 * Display progress bar (if tty). 78 * 79 * @param x current position in the download 80 * @param n total size of the download 81 * @param w desired number of steps in the progress bar 82 */ 83 static void 84 display_bar (unsigned long long x, unsigned long long n, unsigned int w) 85 { 86 char buf[w + 20]; 87 unsigned int p; 88 unsigned int endeq; 89 float ratio_complete; 90 91 if (0 == isatty (1)) 92 return; 93 ratio_complete = x / (float) n; 94 endeq = ratio_complete * w; 95 GNUNET_snprintf (buf, sizeof(buf), "%3d%% [", (int) (ratio_complete * 100)); 96 for (p = 0; p < endeq; p++) 97 strcat (buf, "="); 98 for (p = endeq; p < w; p++) 99 strcat (buf, " "); 100 strcat (buf, "]\r"); 101 printf ("%s", buf); 102 fflush (stdout); 103 } 104 105 106 /** 107 * Called by FS client to give information about the progress of an 108 * operation. 109 * 110 * @param cls closure 111 * @param info details about the event, specifying the event type 112 * and various bits about the event 113 * @return client-context (for the next progress call 114 * for this operation; should be set to NULL for 115 * SUSPEND and STOPPED events). The value returned 116 * will be passed to future callbacks in the respective 117 * field in the `struct GNUNET_FS_ProgressInfo` 118 */ 119 static void * 120 progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info) 121 { 122 char *s; 123 const char *s2; 124 char *t; 125 126 switch (info->status) 127 { 128 case GNUNET_FS_STATUS_DOWNLOAD_START: 129 if (verbose > 1) 130 fprintf (stderr, 131 _ ("Starting download `%s'.\n"), 132 info->value.download.filename); 133 break; 134 135 case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS: 136 if (verbose) 137 { 138 s = GNUNET_strdup ( 139 GNUNET_STRINGS_relative_time_to_string (info->value.download.eta, 140 GNUNET_YES)); 141 if (info->value.download.specifics.progress.block_download_duration 142 .rel_value_us == GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us) 143 s2 = _ ("<unknown time>"); 144 else 145 s2 = GNUNET_STRINGS_relative_time_to_string (info->value.download 146 .specifics.progress 147 .block_download_duration, 148 GNUNET_YES); 149 t = GNUNET_STRINGS_byte_size_fancy ( 150 info->value.download.completed * 1000LL 151 / (info->value.download.duration.rel_value_us + 1)); 152 fprintf ( 153 stdout, 154 _ ( 155 "Downloading `%s' at %llu/%llu (%s remaining, %s/s). Block took %s to download\n"), 156 info->value.download.filename, 157 (unsigned long long) info->value.download.completed, 158 (unsigned long long) info->value.download.size, 159 s, 160 t, 161 s2); 162 GNUNET_free (s); 163 GNUNET_free (t); 164 } 165 else 166 { 167 display_bar (info->value.download.completed, 168 info->value.download.size, 169 60); 170 } 171 break; 172 173 case GNUNET_FS_STATUS_DOWNLOAD_ERROR: 174 if (0 != isatty (1)) 175 fprintf (stdout, "\n"); 176 fprintf (stderr, 177 _ ("Error downloading: %s.\n"), 178 info->value.download.specifics.error.message); 179 GNUNET_SCHEDULER_shutdown (); 180 break; 181 182 case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED: 183 s = GNUNET_STRINGS_byte_size_fancy ( 184 info->value.download.completed * 1000 185 / (info->value.download.duration.rel_value_us + 1)); 186 if (0 != isatty (1)) 187 fprintf (stdout, "\n"); 188 fprintf (stdout, 189 _ ("Downloading `%s' done (%s/s).\n"), 190 info->value.download.filename, 191 s); 192 GNUNET_free (s); 193 if (info->value.download.dc == dc) 194 GNUNET_SCHEDULER_shutdown (); 195 break; 196 197 case GNUNET_FS_STATUS_DOWNLOAD_STOPPED: 198 if (info->value.download.dc == dc) 199 GNUNET_SCHEDULER_add_now (&cleanup_task, NULL); 200 break; 201 202 case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE: 203 case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE: 204 break; 205 206 default: 207 fprintf (stderr, _ ("Unexpected status: %d\n"), info->status); 208 break; 209 } 210 return NULL; 211 } 212 213 214 /** 215 * Main function that will be run by the scheduler. 216 * 217 * @param cls closure 218 * @param args remaining command-line arguments 219 * @param cfgfile name of the configuration file used (for saving, can be NULL!) 220 * @param c configuration 221 */ 222 static void 223 run (void *cls, 224 char *const *args, 225 const char *cfgfile, 226 const struct GNUNET_CONFIGURATION_Handle *c) 227 { 228 struct GNUNET_FS_Uri *uri; 229 char *emsg; 230 enum GNUNET_FS_DownloadOptions options; 231 232 if (NULL == args[0]) 233 { 234 fprintf (stderr, "%s", _ ("You need to specify a URI argument.\n")); 235 return; 236 } 237 uri = GNUNET_FS_uri_parse (args[0], &emsg); 238 if (NULL == uri) 239 { 240 fprintf (stderr, _ ("Failed to parse URI: %s\n"), emsg); 241 GNUNET_free (emsg); 242 ret = 1; 243 return; 244 } 245 if ((! GNUNET_FS_uri_test_chk (uri)) && (! GNUNET_FS_uri_test_loc (uri))) 246 { 247 fprintf (stderr, "%s", _ ("Only CHK or LOC URIs supported.\n")); 248 ret = 1; 249 GNUNET_FS_uri_destroy (uri); 250 return; 251 } 252 if (NULL == filename) 253 { 254 fprintf (stderr, "%s", _ ("Target filename must be specified.\n")); 255 ret = 1; 256 GNUNET_FS_uri_destroy (uri); 257 return; 258 } 259 cfg = c; 260 ctx = GNUNET_FS_start (cfg, 261 "gnunet-download", 262 &progress_cb, 263 NULL, 264 GNUNET_FS_FLAGS_NONE, 265 GNUNET_FS_OPTIONS_DOWNLOAD_PARALLELISM, 266 parallelism, 267 GNUNET_FS_OPTIONS_REQUEST_PARALLELISM, 268 request_parallelism, 269 GNUNET_FS_OPTIONS_END); 270 if (NULL == ctx) 271 { 272 fprintf (stderr, _ ("Could not initialize `%s' subsystem.\n"), "FS"); 273 GNUNET_FS_uri_destroy (uri); 274 ret = 1; 275 return; 276 } 277 options = GNUNET_FS_DOWNLOAD_OPTION_NONE; 278 if (do_recursive) 279 options |= GNUNET_FS_DOWNLOAD_OPTION_RECURSIVE; 280 if (local_only) 281 options |= GNUNET_FS_DOWNLOAD_OPTION_LOOPBACK_ONLY; 282 dc = GNUNET_FS_download_start (ctx, 283 uri, 284 NULL, 285 filename, 286 NULL, 287 0, 288 GNUNET_FS_uri_chk_get_file_size (uri), 289 anonymity, 290 options, 291 NULL, 292 NULL); 293 GNUNET_FS_uri_destroy (uri); 294 if (dc == NULL) 295 { 296 GNUNET_FS_stop (ctx); 297 ctx = NULL; 298 return; 299 } 300 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); 301 } 302 303 304 /** 305 * The main function to download GNUnet. 306 * 307 * @param argc number of arguments from the command line 308 * @param argv command line arguments 309 * @return 0 ok, 1 on error 310 */ 311 int 312 main (int argc, char *const *argv) 313 { 314 struct GNUNET_GETOPT_CommandLineOption options[] = 315 { GNUNET_GETOPT_option_uint ('a', 316 "anonymity", 317 "LEVEL", 318 gettext_noop ( 319 "set the desired LEVEL of receiver-anonymity"), 320 &anonymity), 321 322 GNUNET_GETOPT_option_flag ( 323 'D', 324 "delete-incomplete", 325 gettext_noop ("delete incomplete downloads (when aborted with CTRL-C)"), 326 &delete_incomplete), 327 328 GNUNET_GETOPT_option_flag ( 329 'n', 330 "no-network", 331 gettext_noop ("only search the local peer (no P2P network search)"), 332 &local_only), 333 GNUNET_GETOPT_option_string ('o', 334 "output", 335 "FILENAME", 336 gettext_noop ("write the file to FILENAME"), 337 &filename), 338 GNUNET_GETOPT_option_uint ( 339 'p', 340 "parallelism", 341 "DOWNLOADS", 342 gettext_noop ( 343 "set the maximum number of parallel downloads that is allowed"), 344 ¶llelism), 345 GNUNET_GETOPT_option_uint ( 346 'r', 347 "request-parallelism", 348 "REQUESTS", 349 gettext_noop ( 350 "set the maximum number of parallel requests for blocks that is allowed"), 351 &request_parallelism), 352 GNUNET_GETOPT_option_flag ('R', 353 "recursive", 354 gettext_noop ( 355 "download a GNUnet directory recursively"), 356 &do_recursive), 357 GNUNET_GETOPT_option_increment_uint ( 358 'V', 359 "verbose", 360 gettext_noop ("be verbose (print progress information)"), 361 &verbose), 362 GNUNET_GETOPT_OPTION_END }; 363 364 ret = 365 (GNUNET_OK == 366 GNUNET_PROGRAM_run ( 367 GNUNET_OS_project_data_gnunet (), 368 argc, 369 argv, 370 "gnunet-download [OPTIONS] URI", 371 gettext_noop ( 372 "Download files from GNUnet using a GNUnet CHK or LOC URI (gnunet://fs/chk/...)"), 373 options, 374 &run, 375 NULL)) 376 ? ret 377 : 1; 378 return ret; 379 } 380 381 382 /* end of gnunet-download.c */