gfs_download.c (6224B)
1 /* 2 This file is part of gnunet-fuse. 3 Copyright (C) 2012 GNUnet e.V. 4 5 gnunet-fuse 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-fuse is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 19 */ 20 /** 21 * @file fuse/gfs_download.h 22 * @brief download files using FS 23 * @author Christian Grothoff 24 */ 25 #include "gfs_download.h" 26 27 28 /** 29 * Context for a download operation. 30 */ 31 struct Context 32 { 33 34 /** 35 * Information about the file we are downloading. 36 */ 37 struct GNUNET_FUSE_PathInfo *path_info; 38 39 /** 40 * Download handle. 41 */ 42 struct GNUNET_FS_DownloadContext *dc; 43 44 /** 45 * FS handle. 46 */ 47 struct GNUNET_FS_Handle *fs; 48 49 /** 50 * Start offset. 51 */ 52 off_t start_offset; 53 54 /** 55 * Number of bytes to download. 56 */ 57 uint64_t length; 58 59 /** 60 * Return value for the operation, 0 on success. 61 */ 62 int ret; 63 64 }; 65 66 67 /** 68 * Task run when we shut down. 69 * 70 * @param cls our 'struct Context' 71 */ 72 static void 73 shutdown_task (void *cls) 74 { 75 struct Context *ctx = cls; 76 77 if (NULL != ctx->dc) 78 { 79 GNUNET_FS_download_stop (ctx->dc, GNUNET_YES); 80 ctx->dc = NULL; 81 } 82 if (NULL != ctx->fs) 83 { 84 GNUNET_FS_stop (ctx->fs); 85 ctx->fs = NULL; 86 } 87 } 88 89 90 /** 91 * Function called from FS with progress information. 92 * 93 * @param cls our 'struct Context' 94 * @param info progress information 95 * @return NULL 96 */ 97 static void * 98 progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info) 99 { 100 struct Context *ctx = cls; 101 char *s; 102 103 switch (info->status) 104 { 105 case GNUNET_FS_STATUS_DOWNLOAD_START: 106 GNUNET_break (info->value.download.dc == ctx->dc); 107 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 108 "Started download `%s'.\n", 109 info->value.download.filename); 110 break; 111 case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS: 112 GNUNET_break (info->value.download.dc == ctx->dc); 113 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 114 "Downloading `%s' at %llu/%llu\n", 115 info->value.download.filename, 116 (unsigned long long) info->value.download.completed, 117 (unsigned long long) info->value.download.size); 118 break; 119 case GNUNET_FS_STATUS_DOWNLOAD_ERROR: 120 GNUNET_break (info->value.download.dc == ctx->dc); 121 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 122 "Error downloading: %s.\n", 123 info->value.download.specifics.error.message); 124 GNUNET_SCHEDULER_shutdown (); 125 break; 126 case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED: 127 GNUNET_break (info->value.download.dc == ctx->dc); 128 s = 129 GNUNET_STRINGS_byte_size_fancy (info->value.download.completed * 130 1000000LL / 131 (info->value.download. 132 duration.rel_value_us + 1)); 133 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 134 "Downloading `%s' done (%s/s).\n", 135 info->value.download.filename, s); 136 GNUNET_free (s); 137 ctx->ret = 0; 138 GNUNET_SCHEDULER_shutdown (); 139 break; 140 case GNUNET_FS_STATUS_DOWNLOAD_STOPPED: 141 GNUNET_SCHEDULER_shutdown (); 142 break; 143 case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE: 144 case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE: 145 break; 146 default: 147 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 148 _("Unexpected status: %d\n"), info->status); 149 break; 150 } 151 return NULL; 152 } 153 154 155 /** 156 * Main task run by the helper process which downloads the file. 157 * 158 * @param cls 'struct Context' with information about the download 159 */ 160 static void 161 download_task (void *cls) 162 { 163 struct Context *ctx = cls; 164 165 ctx->fs = GNUNET_FS_start (cfg, "gnunet-fuse", &progress_cb, ctx, 166 GNUNET_FS_FLAGS_NONE, 167 GNUNET_FS_OPTIONS_DOWNLOAD_PARALLELISM, 1, 168 GNUNET_FS_OPTIONS_REQUEST_PARALLELISM, 1, 169 GNUNET_FS_OPTIONS_END); 170 if (NULL == ctx->fs) 171 { 172 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Could not initialize `%s' subsystem.\n"), "FS"); 173 return; 174 } 175 ctx->dc = GNUNET_FS_download_start (ctx->fs, 176 ctx->path_info->uri, ctx->path_info->meta, 177 ctx->path_info->tmpfile, NULL, 178 (uint64_t) ctx->start_offset, 179 ctx->length, 180 anonymity_level, 181 GNUNET_FS_DOWNLOAD_OPTION_NONE, 182 NULL, NULL); 183 if (NULL == ctx->dc) 184 { 185 GNUNET_FS_stop (ctx->fs); 186 ctx->fs = NULL; 187 return; 188 } 189 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, ctx); 190 } 191 192 193 /** 194 * Download a file. Blocks until we're done. 195 * 196 * @param path_info information about the file to download 197 * @param start_offset offset of the first byte to download 198 * @param length number of bytes to download from 'start_offset' 199 * @return GNUNET_OK on success 200 */ 201 int 202 GNUNET_FUSE_download_file (struct GNUNET_FUSE_PathInfo *path_info, 203 off_t start_offset, 204 uint64_t length) 205 { 206 struct Context ctx; 207 pid_t pid; 208 int status; 209 int ret; 210 211 /* lock to prevent two processes from downloading / manipulating the 212 same file at the same time */ 213 GNUNET_mutex_lock (path_info->lock); 214 pid = fork (); 215 if (-1 == pid) 216 { 217 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork"); 218 GNUNET_mutex_unlock (path_info->lock); 219 return GNUNET_SYSERR; 220 } 221 if (0 != pid) 222 { 223 while ( (-1 == (ret = waitpid (pid, &status, 0))) && 224 (EINTR == errno) ) ; 225 if (pid != ret) 226 { 227 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "waitpid"); 228 (void) kill (pid, SIGKILL); 229 (void) waitpid (pid, &status, 0); 230 GNUNET_mutex_unlock (path_info->lock); 231 return GNUNET_SYSERR; 232 } 233 GNUNET_mutex_unlock (path_info->lock); 234 if ( (WIFEXITED (status)) && 235 (0 == WEXITSTATUS (status)) ) 236 return GNUNET_OK; 237 return GNUNET_SYSERR; 238 } 239 memset (&ctx, 0, sizeof (ctx)); 240 ctx.ret = 1; 241 ctx.path_info = path_info; 242 ctx.start_offset = start_offset; 243 ctx.length = length; 244 GNUNET_SCHEDULER_run (&download_task, &ctx); 245 _exit (ctx.ret); 246 } 247 248 /* end of gfs_download.c */