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
main(void)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
fetch_files(int nfiles,char ** urls)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