]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bsdinstall/distfetch/distfetch.c
MFC r326276:
[FreeBSD/FreeBSD.git] / usr.sbin / bsdinstall / distfetch / distfetch.c
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$");
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
44 static int fetch_files(int nfiles, char **urls);
45
46 int
47 main(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
102 static int
103 fetch_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 }