xref: /freebsd-src/usr.sbin/bsdinstall/distfetch/distfetch.c (revision 7cd0a4c85dbe5e8cd000f6b293ef2d579d22edfb)
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