1 /*- 2 * Copyright (c) 2011 Nathan Whitehorn 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29 #include <sys/param.h> 30 #include <stdio.h> 31 #include <errno.h> 32 #include <fetch.h> 33 #include <dialog.h> 34 35 static int fetch_files(int nfiles, char **urls); 36 37 int 38 main(void) 39 { 40 char *diststring; 41 char **urls; 42 int i, nfetched, ndists = 0; 43 44 if (getenv("DISTRIBUTIONS") == NULL) { 45 fprintf(stderr, "DISTRIBUTIONS variable is not set\n"); 46 return (1); 47 } 48 49 diststring = strdup(getenv("DISTRIBUTIONS")); 50 for (i = 0; diststring[i] != 0; i++) 51 if (isspace(diststring[i]) && !isspace(diststring[i+1])) 52 ndists++; 53 ndists++; /* Last one */ 54 55 urls = calloc(ndists, sizeof(const char *)); 56 if (urls == NULL) { 57 fprintf(stderr, "Out of memory!\n"); 58 free(diststring); 59 return (1); 60 } 61 62 init_dialog(stdin, stdout); 63 dialog_vars.backtitle = __DECONST(char *, "FreeBSD Installer"); 64 dlg_put_backtitle(); 65 66 for (i = 0; i < ndists; i++) { 67 urls[i] = malloc(PATH_MAX); 68 sprintf(urls[i], "%s/%s", getenv("BSDINSTALL_DISTSITE"), 69 strsep(&diststring, " \t")); 70 } 71 72 if (chdir(getenv("BSDINSTALL_DISTDIR")) != 0) { 73 char error[512]; 74 sprintf(error, "Could could change to directory %s: %s\n", 75 getenv("BSDINSTALL_DISTDIR"), strerror(errno)); 76 dialog_msgbox("Error", error, 0, 0, TRUE); 77 end_dialog(); 78 return (1); 79 } 80 81 nfetched = fetch_files(ndists, urls); 82 83 end_dialog(); 84 85 free(diststring); 86 for (i = 0; i < ndists; i++) 87 free(urls[i]); 88 free(urls); 89 90 return ((nfetched == ndists) ? 0 : 1); 91 } 92 93 static int 94 fetch_files(int nfiles, char **urls) 95 { 96 const char **items; 97 FILE *fetch_out, *file_out; 98 struct url_stat ustat; 99 off_t total_bytes, current_bytes, fsize; 100 char status[8]; 101 char errormsg[512]; 102 uint8_t block[4096]; 103 size_t chunk; 104 int i, progress, last_progress; 105 int nsuccess = 0; /* Number of files successfully downloaded */ 106 107 progress = 0; 108 109 /* Make the transfer list for dialog */ 110 items = calloc(sizeof(char *), nfiles * 2); 111 if (items == NULL) { 112 fprintf(stderr, "Out of memory!\n"); 113 return (-1); 114 } 115 116 for (i = 0; i < nfiles; i++) { 117 items[i*2] = strrchr(urls[i], '/'); 118 if (items[i*2] != NULL) 119 items[i*2]++; 120 else 121 items[i*2] = urls[i]; 122 items[i*2 + 1] = "Pending"; 123 } 124 125 dialog_msgbox("", "Connecting to server.\nPlease wait...", 0, 0, FALSE); 126 127 /* Try to stat all the files */ 128 total_bytes = 0; 129 for (i = 0; i < nfiles; i++) { 130 if (fetchStatURL(urls[i], &ustat, "") == 0 && ustat.size > 0) 131 total_bytes += ustat.size; 132 } 133 134 current_bytes = 0; 135 for (i = 0; i < nfiles; i++) { 136 last_progress = progress; 137 if (total_bytes == 0) 138 progress = (i*100)/nfiles; 139 140 fetchLastErrCode = 0; 141 fetch_out = fetchXGetURL(urls[i], &ustat, ""); 142 if (fetch_out == NULL) { 143 snprintf(errormsg, sizeof(errormsg), 144 "Error while fetching %s: %s\n", urls[i], 145 fetchLastErrString); 146 items[i*2 + 1] = "Failed"; 147 dialog_msgbox("Fetch Error", errormsg, 0, 0, 148 TRUE); 149 continue; 150 } 151 152 items[i*2 + 1] = "In Progress"; 153 fsize = 0; 154 file_out = fopen(items[i*2], "w+"); 155 if (file_out == NULL) { 156 snprintf(errormsg, sizeof(errormsg), 157 "Error while fetching %s: %s\n", 158 urls[i], strerror(errno)); 159 items[i*2 + 1] = "Failed"; 160 dialog_msgbox("Fetch Error", errormsg, 0, 0, 161 TRUE); 162 fclose(fetch_out); 163 continue; 164 } 165 166 while ((chunk = fread(block, 1, sizeof(block), fetch_out)) 167 > 0) { 168 if (fwrite(block, 1, chunk, file_out) < chunk) 169 break; 170 171 current_bytes += chunk; 172 fsize += chunk; 173 174 if (total_bytes > 0) { 175 last_progress = progress; 176 progress = (current_bytes*100)/total_bytes; 177 } 178 179 if (ustat.size > 0) { 180 sprintf(status, "-%jd", (fsize*100)/ustat.size); 181 items[i*2 + 1] = status; 182 } 183 184 if (progress > last_progress) 185 dialog_mixedgauge("Fetching Distribution", 186 "Fetching distribution files...", 0, 0, 187 progress, nfiles, 188 __DECONST(char **, items)); 189 } 190 191 if (ustat.size > 0 && fsize < ustat.size) { 192 if (fetchLastErrCode == 0) 193 snprintf(errormsg, sizeof(errormsg), 194 "Error while fetching %s: %s\n", 195 urls[i], strerror(errno)); 196 else 197 snprintf(errormsg, sizeof(errormsg), 198 "Error while fetching %s: %s\n", 199 urls[i], fetchLastErrString); 200 items[i*2 + 1] = "Failed"; 201 dialog_msgbox("Fetch Error", errormsg, 0, 0, 202 TRUE); 203 } else { 204 items[i*2 + 1] = "Done"; 205 nsuccess++; 206 } 207 208 fclose(fetch_out); 209 fclose(file_out); 210 } 211 212 free(items); 213 return (nsuccess); 214 } 215 216