distfetch.c revision 330449
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: stable/11/usr.sbin/bsdinstall/distfetch/distfetch.c 330449 2018-03-05 07:26:05Z eadler $");
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
44static int fetch_files(int nfiles, char **urls);
45
46int
47main(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
102static int
103fetch_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