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