12118f387SNathan Whitehorn /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 31de7b4b8SPedro F. Giffuni * 42118f387SNathan Whitehorn * Copyright (c) 2011 Nathan Whitehorn 582ac9f2bSDevin Teske * Copyright (c) 2014 Devin Teske <dteske@FreeBSD.org> 62118f387SNathan Whitehorn * All rights reserved. 72118f387SNathan Whitehorn * 82118f387SNathan Whitehorn * Redistribution and use in source and binary forms, with or without 92118f387SNathan Whitehorn * modification, are permitted provided that the following conditions 102118f387SNathan Whitehorn * are met: 112118f387SNathan Whitehorn * 1. Redistributions of source code must retain the above copyright 122118f387SNathan Whitehorn * notice, this list of conditions and the following disclaimer. 132118f387SNathan Whitehorn * 2. Redistributions in binary form must reproduce the above copyright 142118f387SNathan Whitehorn * notice, this list of conditions and the following disclaimer in the 152118f387SNathan Whitehorn * documentation and/or other materials provided with the distribution. 162118f387SNathan Whitehorn * 172118f387SNathan Whitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 182118f387SNathan Whitehorn * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 192118f387SNathan Whitehorn * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 202118f387SNathan Whitehorn * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 212118f387SNathan Whitehorn * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 222118f387SNathan Whitehorn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 232118f387SNathan Whitehorn * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 242118f387SNathan Whitehorn * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 252118f387SNathan Whitehorn * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 262118f387SNathan Whitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 272118f387SNathan Whitehorn * SUCH DAMAGE. 282118f387SNathan Whitehorn */ 292118f387SNathan Whitehorn 302118f387SNathan Whitehorn #include <sys/param.h> 31f27c6a3eSAlfonso S. Siciliano 32f27c6a3eSAlfonso S. Siciliano #include <bsddialog.h> 3382ac9f2bSDevin Teske #include <ctype.h> 3482ac9f2bSDevin Teske #include <err.h> 352118f387SNathan Whitehorn #include <errno.h> 36f27c6a3eSAlfonso S. Siciliano #include <stdio.h> 372118f387SNathan Whitehorn #include <fetch.h> 3882ac9f2bSDevin Teske #include <stdlib.h> 3982ac9f2bSDevin Teske #include <string.h> 4082ac9f2bSDevin Teske #include <unistd.h> 412118f387SNathan Whitehorn 42147585b4SBrad Davis #include "opt_osname.h" 43147585b4SBrad Davis 442118f387SNathan Whitehorn static int fetch_files(int nfiles, char **urls); 452118f387SNathan Whitehorn 462118f387SNathan Whitehorn int 472118f387SNathan Whitehorn main(void) 482118f387SNathan Whitehorn { 4956093150SWarner Losh char *diststring; 502118f387SNathan Whitehorn char **urls; 5182ac9f2bSDevin Teske int i; 5282ac9f2bSDevin Teske int ndists = 0; 5382ac9f2bSDevin Teske int nfetched; 5482ac9f2bSDevin Teske char error[PATH_MAX + 512]; 55f27c6a3eSAlfonso S. Siciliano struct bsddialog_conf conf; 56bfd258f7SNathan Whitehorn 5756093150SWarner Losh if (getenv("DISTRIBUTIONS") == NULL) 5882ac9f2bSDevin Teske errx(EXIT_FAILURE, "DISTRIBUTIONS variable is not set"); 59bfd258f7SNathan Whitehorn 6056093150SWarner Losh diststring = strdup(getenv("DISTRIBUTIONS")); 612118f387SNathan Whitehorn for (i = 0; diststring[i] != 0; i++) 622118f387SNathan Whitehorn if (isspace(diststring[i]) && !isspace(diststring[i+1])) 632118f387SNathan Whitehorn ndists++; 642118f387SNathan Whitehorn ndists++; /* Last one */ 652118f387SNathan Whitehorn 6656093150SWarner Losh urls = calloc(ndists, sizeof(const char *)); 6756093150SWarner Losh if (urls == NULL) { 68c725e3efSKevin Lo free(diststring); 69f27c6a3eSAlfonso S. Siciliano errx(EXIT_FAILURE, "Error: distfetch URLs out of memory!"); 7057bda1b6SNathan Whitehorn } 7157bda1b6SNathan Whitehorn 72f27c6a3eSAlfonso S. Siciliano if (bsddialog_init() == BSDDIALOG_ERROR) { 73f27c6a3eSAlfonso S. Siciliano free(diststring); 74f27c6a3eSAlfonso S. Siciliano errx(EXIT_FAILURE, "Error libbsddialog: %s\n", 75f27c6a3eSAlfonso S. Siciliano bsddialog_geterror()); 76f27c6a3eSAlfonso S. Siciliano } 77f27c6a3eSAlfonso S. Siciliano bsddialog_initconf(&conf); 78147585b4SBrad Davis bsddialog_backtitle(&conf, OSNAME " Installer"); 7957bda1b6SNathan Whitehorn 802118f387SNathan Whitehorn for (i = 0; i < ndists; i++) { 8156093150SWarner Losh urls[i] = malloc(PATH_MAX); 8291bdebc9Srilysh snprintf(urls[i], PATH_MAX, "%s/%s", 8356093150SWarner Losh getenv("BSDINSTALL_DISTSITE"), strsep(&diststring, " \t")); 8491bdebc9Srilysh } 8591bdebc9Srilysh 8656093150SWarner Losh if (chdir(getenv("BSDINSTALL_DISTDIR")) != 0) { 8782ac9f2bSDevin Teske snprintf(error, sizeof(error), 88e4e2a6c6SDevin Teske "Could not change to directory %s: %s\n", 8956093150SWarner Losh getenv("BSDINSTALL_DISTDIR"), strerror(errno)); 90f27c6a3eSAlfonso S. Siciliano conf.title = "Error"; 91f27c6a3eSAlfonso S. Siciliano bsddialog_msgbox(&conf, error, 0, 0); 92f27c6a3eSAlfonso S. Siciliano bsddialog_end(); 93bd1df636SDevin Teske return (EXIT_FAILURE); 9457bda1b6SNathan Whitehorn } 9557bda1b6SNathan Whitehorn 962118f387SNathan Whitehorn nfetched = fetch_files(ndists, urls); 972118f387SNathan Whitehorn 98f27c6a3eSAlfonso S. Siciliano bsddialog_end(); 9957bda1b6SNathan Whitehorn 1002118f387SNathan Whitehorn free(diststring); 1012118f387SNathan Whitehorn for (i = 0; i < ndists; i++) 1022118f387SNathan Whitehorn free(urls[i]); 1032118f387SNathan Whitehorn free(urls); 1042118f387SNathan Whitehorn 105bd1df636SDevin Teske return ((nfetched == ndists) ? EXIT_SUCCESS : EXIT_FAILURE); 1062118f387SNathan Whitehorn } 1072118f387SNathan Whitehorn 1082118f387SNathan Whitehorn static int 1092118f387SNathan Whitehorn fetch_files(int nfiles, char **urls) 1102118f387SNathan Whitehorn { 11182ac9f2bSDevin Teske FILE *fetch_out; 11282ac9f2bSDevin Teske FILE *file_out; 113f27c6a3eSAlfonso S. Siciliano const char **minilabel; 114f27c6a3eSAlfonso S. Siciliano int *miniperc; 115f27c6a3eSAlfonso S. Siciliano int perc; 11682ac9f2bSDevin Teske int i; 11782ac9f2bSDevin Teske int last_progress; 1182118f387SNathan Whitehorn int nsuccess = 0; /* Number of files successfully downloaded */ 11982ac9f2bSDevin Teske int progress = 0; 12082ac9f2bSDevin Teske size_t chunk; 12182ac9f2bSDevin Teske off_t current_bytes; 12282ac9f2bSDevin Teske off_t fsize; 12382ac9f2bSDevin Teske off_t total_bytes; 12455af0f96SAlfonso S. Siciliano float file_perc; 12555af0f96SAlfonso S. Siciliano float mainperc_file; 12682ac9f2bSDevin Teske struct url_stat ustat; 12782ac9f2bSDevin Teske char errormsg[PATH_MAX + 512]; 12882ac9f2bSDevin Teske uint8_t block[4096]; 129f27c6a3eSAlfonso S. Siciliano struct bsddialog_conf errconf; 130f27c6a3eSAlfonso S. Siciliano struct bsddialog_conf mgconf; 1312118f387SNathan Whitehorn 132f27c6a3eSAlfonso S. Siciliano /* Make the transfer list for mixedgauge */ 133*7cd0a4c8SJohn Baldwin minilabel = calloc(nfiles, sizeof(char *)); 134*7cd0a4c8SJohn Baldwin miniperc = calloc(nfiles, sizeof(int)); 135f27c6a3eSAlfonso S. Siciliano if (minilabel == NULL || miniperc == NULL) 136f27c6a3eSAlfonso S. Siciliano errx(EXIT_FAILURE, "Error: distfetch minibars out of memory!"); 13757bda1b6SNathan Whitehorn 1382118f387SNathan Whitehorn for (i = 0; i < nfiles; i++) { 139f27c6a3eSAlfonso S. Siciliano minilabel[i] = strrchr(urls[i], '/'); 140f27c6a3eSAlfonso S. Siciliano if (minilabel[i] != NULL) 141f27c6a3eSAlfonso S. Siciliano minilabel[i]++; 1422118f387SNathan Whitehorn else 143f27c6a3eSAlfonso S. Siciliano minilabel[i] = urls[i]; 144f27c6a3eSAlfonso S. Siciliano miniperc[i] = BSDDIALOG_MG_PENDING; 1452118f387SNathan Whitehorn } 1462118f387SNathan Whitehorn 147f27c6a3eSAlfonso S. Siciliano bsddialog_initconf(&errconf); 148f27c6a3eSAlfonso S. Siciliano bsddialog_infobox(&errconf, "Connecting to server.\nPlease wait...", 149f27c6a3eSAlfonso S. Siciliano 0, 0); 1502118f387SNathan Whitehorn 1512118f387SNathan Whitehorn /* Try to stat all the files */ 1522118f387SNathan Whitehorn total_bytes = 0; 1532118f387SNathan Whitehorn for (i = 0; i < nfiles; i++) { 15455af0f96SAlfonso S. Siciliano if (fetchStatURL(urls[i], &ustat, "") == 0 && ustat.size > 0) { 1552118f387SNathan Whitehorn total_bytes += ustat.size; 15655af0f96SAlfonso S. Siciliano } else { 15755af0f96SAlfonso S. Siciliano total_bytes = 0; 15855af0f96SAlfonso S. Siciliano break; 15955af0f96SAlfonso S. Siciliano } 1602118f387SNathan Whitehorn } 1612118f387SNathan Whitehorn 162f27c6a3eSAlfonso S. Siciliano errconf.title = "Fetch Error"; 163f27c6a3eSAlfonso S. Siciliano errconf.clear = true; 164f27c6a3eSAlfonso S. Siciliano bsddialog_initconf(&mgconf); 165f27c6a3eSAlfonso S. Siciliano mgconf.title = "Fetching Distribution"; 166f27c6a3eSAlfonso S. Siciliano mgconf.auto_minwidth = 40; 167f27c6a3eSAlfonso S. Siciliano 16855af0f96SAlfonso S. Siciliano mainperc_file = 100.0 / nfiles; 1692118f387SNathan Whitehorn current_bytes = 0; 1702118f387SNathan Whitehorn for (i = 0; i < nfiles; i++) { 1712118f387SNathan Whitehorn fetchLastErrCode = 0; 1722118f387SNathan Whitehorn fetch_out = fetchXGetURL(urls[i], &ustat, ""); 1732118f387SNathan Whitehorn if (fetch_out == NULL) { 1742118f387SNathan Whitehorn snprintf(errormsg, sizeof(errormsg), 175f27c6a3eSAlfonso S. Siciliano "Error (URL) while fetching %s: %s\n", urls[i], 1762118f387SNathan Whitehorn fetchLastErrString); 177f27c6a3eSAlfonso S. Siciliano miniperc[2] = BSDDIALOG_MG_FAILED; 178f27c6a3eSAlfonso S. Siciliano bsddialog_msgbox(&errconf, errormsg, 0, 0); 17955af0f96SAlfonso S. Siciliano total_bytes = 0; 1802118f387SNathan Whitehorn continue; 1812118f387SNathan Whitehorn } 1822118f387SNathan Whitehorn 183f27c6a3eSAlfonso S. Siciliano miniperc[i] = BSDDIALOG_MG_INPROGRESS; 1842118f387SNathan Whitehorn fsize = 0; 185f27c6a3eSAlfonso S. Siciliano file_out = fopen(minilabel[i], "w+"); 1862118f387SNathan Whitehorn if (file_out == NULL) { 1872118f387SNathan Whitehorn snprintf(errormsg, sizeof(errormsg), 188f27c6a3eSAlfonso S. Siciliano "Error (fopen) while fetching %s: %s\n", 1892118f387SNathan Whitehorn urls[i], strerror(errno)); 190f27c6a3eSAlfonso S. Siciliano miniperc[i] = BSDDIALOG_MG_FAILED; 191f27c6a3eSAlfonso S. Siciliano bsddialog_msgbox(&errconf, errormsg, 0, 0); 1922118f387SNathan Whitehorn fclose(fetch_out); 19355af0f96SAlfonso S. Siciliano total_bytes = 0; 1942118f387SNathan Whitehorn continue; 1952118f387SNathan Whitehorn } 1962118f387SNathan Whitehorn 1972118f387SNathan Whitehorn while ((chunk = fread(block, 1, sizeof(block), fetch_out)) 1982118f387SNathan Whitehorn > 0) { 1992118f387SNathan Whitehorn if (fwrite(block, 1, chunk, file_out) < chunk) 2002118f387SNathan Whitehorn break; 2012118f387SNathan Whitehorn 2022118f387SNathan Whitehorn current_bytes += chunk; 2032118f387SNathan Whitehorn fsize += chunk; 2042118f387SNathan Whitehorn 2052118f387SNathan Whitehorn last_progress = progress; 20655af0f96SAlfonso S. Siciliano if (total_bytes > 0) { 2072118f387SNathan Whitehorn progress = (current_bytes * 100) / total_bytes; 20855af0f96SAlfonso S. Siciliano } else { 20955af0f96SAlfonso S. Siciliano file_perc = ustat.size > 0 ? 21055af0f96SAlfonso S. Siciliano (fsize * 100) / ustat.size : 0; 21155af0f96SAlfonso S. Siciliano progress = (i * mainperc_file) + 21255af0f96SAlfonso S. Siciliano ((file_perc * mainperc_file) / 100); 2132118f387SNathan Whitehorn } 2142118f387SNathan Whitehorn 2152118f387SNathan Whitehorn if (ustat.size > 0) { 216f27c6a3eSAlfonso S. Siciliano perc = (fsize * 100) / ustat.size; 217f27c6a3eSAlfonso S. Siciliano miniperc[i] = perc; 2182118f387SNathan Whitehorn } 2192118f387SNathan Whitehorn 220f27c6a3eSAlfonso S. Siciliano if (progress > last_progress) { 221f27c6a3eSAlfonso S. Siciliano bsddialog_mixedgauge(&mgconf, 222f27c6a3eSAlfonso S. Siciliano "\nFetching distribution files...\n", 223f27c6a3eSAlfonso S. Siciliano 0, 0, progress, nfiles, minilabel, 224f27c6a3eSAlfonso S. Siciliano miniperc); 225f27c6a3eSAlfonso S. Siciliano } 2262118f387SNathan Whitehorn } 2272118f387SNathan Whitehorn 2282118f387SNathan Whitehorn if (ustat.size > 0 && fsize < ustat.size) { 2292118f387SNathan Whitehorn if (fetchLastErrCode == 0) 2302118f387SNathan Whitehorn snprintf(errormsg, sizeof(errormsg), 231f27c6a3eSAlfonso S. Siciliano "Error (undone) while fetching %s: %s\n", 2322118f387SNathan Whitehorn urls[i], strerror(errno)); 2332118f387SNathan Whitehorn else 2342118f387SNathan Whitehorn snprintf(errormsg, sizeof(errormsg), 235f27c6a3eSAlfonso S. Siciliano "Error (libfetch) while fetching %s: %s\n", 2362118f387SNathan Whitehorn urls[i], fetchLastErrString); 237f27c6a3eSAlfonso S. Siciliano miniperc[i] = BSDDIALOG_MG_FAILED; 238f27c6a3eSAlfonso S. Siciliano bsddialog_msgbox(&errconf, errormsg, 0, 0); 23955af0f96SAlfonso S. Siciliano total_bytes = 0; 2402118f387SNathan Whitehorn } else { 241f27c6a3eSAlfonso S. Siciliano miniperc[i] = BSDDIALOG_MG_DONE; 2422118f387SNathan Whitehorn nsuccess++; 2432118f387SNathan Whitehorn } 2442118f387SNathan Whitehorn 2452118f387SNathan Whitehorn fclose(fetch_out); 2462118f387SNathan Whitehorn fclose(file_out); 2472118f387SNathan Whitehorn } 2482118f387SNathan Whitehorn 249f27c6a3eSAlfonso S. Siciliano bsddialog_mixedgauge(&mgconf, "\nFetching distribution completed\n", 250f27c6a3eSAlfonso S. Siciliano 0, 0, progress, nfiles, minilabel, miniperc); 25155af0f96SAlfonso S. Siciliano 252f27c6a3eSAlfonso S. Siciliano free(minilabel); 253f27c6a3eSAlfonso S. Siciliano free(miniperc); 2542118f387SNathan Whitehorn return (nsuccess); 2552118f387SNathan Whitehorn } 256