1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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/param.h> 31 32 #include <bsddialog.h> 33 #include <ctype.h> 34 #include <err.h> 35 #include <errno.h> 36 #include <stdio.h> 37 #include <fetch.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <unistd.h> 41 42 #include "opt_osname.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 struct bsddialog_conf conf; 56 57 if (getenv("DISTRIBUTIONS") == NULL) 58 errx(EXIT_FAILURE, "DISTRIBUTIONS variable is not set"); 59 60 diststring = strdup(getenv("DISTRIBUTIONS")); 61 for (i = 0; diststring[i] != 0; i++) 62 if (isspace(diststring[i]) && !isspace(diststring[i+1])) 63 ndists++; 64 ndists++; /* Last one */ 65 66 urls = calloc(ndists, sizeof(const char *)); 67 if (urls == NULL) { 68 free(diststring); 69 errx(EXIT_FAILURE, "Error: distfetch URLs out of memory!"); 70 } 71 72 if (bsddialog_init() == BSDDIALOG_ERROR) { 73 free(diststring); 74 errx(EXIT_FAILURE, "Error libbsddialog: %s\n", 75 bsddialog_geterror()); 76 } 77 bsddialog_initconf(&conf); 78 bsddialog_backtitle(&conf, OSNAME " Installer"); 79 80 for (i = 0; i < ndists; i++) { 81 urls[i] = malloc(PATH_MAX); 82 snprintf(urls[i], PATH_MAX, "%s/%s", 83 getenv("BSDINSTALL_DISTSITE"), strsep(&diststring, " \t")); 84 } 85 86 if (chdir(getenv("BSDINSTALL_DISTDIR")) != 0) { 87 snprintf(error, sizeof(error), 88 "Could not change to directory %s: %s\n", 89 getenv("BSDINSTALL_DISTDIR"), strerror(errno)); 90 conf.title = "Error"; 91 bsddialog_msgbox(&conf, error, 0, 0); 92 bsddialog_end(); 93 return (EXIT_FAILURE); 94 } 95 96 nfetched = fetch_files(ndists, urls); 97 98 bsddialog_end(); 99 100 free(diststring); 101 for (i = 0; i < ndists; i++) 102 free(urls[i]); 103 free(urls); 104 105 return ((nfetched == ndists) ? EXIT_SUCCESS : EXIT_FAILURE); 106 } 107 108 static int 109 fetch_files(int nfiles, char **urls) 110 { 111 FILE *fetch_out; 112 FILE *file_out; 113 const char **minilabel; 114 int *miniperc; 115 int perc; 116 int i; 117 int last_progress; 118 int nsuccess = 0; /* Number of files successfully downloaded */ 119 int progress = 0; 120 size_t chunk; 121 off_t current_bytes; 122 off_t fsize; 123 off_t total_bytes; 124 float file_perc; 125 float mainperc_file; 126 struct url_stat ustat; 127 char errormsg[PATH_MAX + 512]; 128 uint8_t block[4096]; 129 struct bsddialog_conf errconf; 130 struct bsddialog_conf mgconf; 131 132 /* Make the transfer list for mixedgauge */ 133 minilabel = calloc(sizeof(char *), nfiles); 134 miniperc = calloc(sizeof(int), nfiles); 135 if (minilabel == NULL || miniperc == NULL) 136 errx(EXIT_FAILURE, "Error: distfetch minibars out of memory!"); 137 138 for (i = 0; i < nfiles; i++) { 139 minilabel[i] = strrchr(urls[i], '/'); 140 if (minilabel[i] != NULL) 141 minilabel[i]++; 142 else 143 minilabel[i] = urls[i]; 144 miniperc[i] = BSDDIALOG_MG_PENDING; 145 } 146 147 bsddialog_initconf(&errconf); 148 bsddialog_infobox(&errconf, "Connecting to server.\nPlease wait...", 149 0, 0); 150 151 /* Try to stat all the files */ 152 total_bytes = 0; 153 for (i = 0; i < nfiles; i++) { 154 if (fetchStatURL(urls[i], &ustat, "") == 0 && ustat.size > 0) { 155 total_bytes += ustat.size; 156 } else { 157 total_bytes = 0; 158 break; 159 } 160 } 161 162 errconf.title = "Fetch Error"; 163 errconf.clear = true; 164 bsddialog_initconf(&mgconf); 165 mgconf.title = "Fetching Distribution"; 166 mgconf.auto_minwidth = 40; 167 168 mainperc_file = 100.0 / nfiles; 169 current_bytes = 0; 170 for (i = 0; i < nfiles; i++) { 171 fetchLastErrCode = 0; 172 fetch_out = fetchXGetURL(urls[i], &ustat, ""); 173 if (fetch_out == NULL) { 174 snprintf(errormsg, sizeof(errormsg), 175 "Error (URL) while fetching %s: %s\n", urls[i], 176 fetchLastErrString); 177 miniperc[2] = BSDDIALOG_MG_FAILED; 178 bsddialog_msgbox(&errconf, errormsg, 0, 0); 179 total_bytes = 0; 180 continue; 181 } 182 183 miniperc[i] = BSDDIALOG_MG_INPROGRESS; 184 fsize = 0; 185 file_out = fopen(minilabel[i], "w+"); 186 if (file_out == NULL) { 187 snprintf(errormsg, sizeof(errormsg), 188 "Error (fopen) while fetching %s: %s\n", 189 urls[i], strerror(errno)); 190 miniperc[i] = BSDDIALOG_MG_FAILED; 191 bsddialog_msgbox(&errconf, errormsg, 0, 0); 192 fclose(fetch_out); 193 total_bytes = 0; 194 continue; 195 } 196 197 while ((chunk = fread(block, 1, sizeof(block), fetch_out)) 198 > 0) { 199 if (fwrite(block, 1, chunk, file_out) < chunk) 200 break; 201 202 current_bytes += chunk; 203 fsize += chunk; 204 205 last_progress = progress; 206 if (total_bytes > 0) { 207 progress = (current_bytes * 100) / total_bytes; 208 } else { 209 file_perc = ustat.size > 0 ? 210 (fsize * 100) / ustat.size : 0; 211 progress = (i * mainperc_file) + 212 ((file_perc * mainperc_file) / 100); 213 } 214 215 if (ustat.size > 0) { 216 perc = (fsize * 100) / ustat.size; 217 miniperc[i] = perc; 218 } 219 220 if (progress > last_progress) { 221 bsddialog_mixedgauge(&mgconf, 222 "\nFetching distribution files...\n", 223 0, 0, progress, nfiles, minilabel, 224 miniperc); 225 } 226 } 227 228 if (ustat.size > 0 && fsize < ustat.size) { 229 if (fetchLastErrCode == 0) 230 snprintf(errormsg, sizeof(errormsg), 231 "Error (undone) while fetching %s: %s\n", 232 urls[i], strerror(errno)); 233 else 234 snprintf(errormsg, sizeof(errormsg), 235 "Error (libfetch) while fetching %s: %s\n", 236 urls[i], fetchLastErrString); 237 miniperc[i] = BSDDIALOG_MG_FAILED; 238 bsddialog_msgbox(&errconf, errormsg, 0, 0); 239 total_bytes = 0; 240 } else { 241 miniperc[i] = BSDDIALOG_MG_DONE; 242 nsuccess++; 243 } 244 245 fclose(fetch_out); 246 fclose(file_out); 247 } 248 249 bsddialog_mixedgauge(&mgconf, "\nFetching distribution completed\n", 250 0, 0, progress, nfiles, minilabel, miniperc); 251 252 free(minilabel); 253 free(miniperc); 254 return (nsuccess); 255 } 256