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