]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - usr.sbin/bsdinstall/distfetch/distfetch.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / usr.sbin / bsdinstall / distfetch / distfetch.c
1 /*-
2  * Copyright (c) 2011 Nathan Whitehorn
3  * Copyright (c) 2014 Devin Teske <dteske@FreeBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/param.h>
32 #include <ctype.h>
33 #include <err.h>
34 #include <dialog.h>
35 #include <errno.h>
36 #include <fetch.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41
42 static int fetch_files(int nfiles, char **urls);
43
44 int
45 main(void)
46 {
47         char *diststring;
48         char **urls;
49         int i;
50         int ndists = 0;
51         int nfetched;
52         char error[PATH_MAX + 512];
53
54         if (getenv("DISTRIBUTIONS") == NULL)
55                 errx(EXIT_FAILURE, "DISTRIBUTIONS variable is not set");
56
57         diststring = strdup(getenv("DISTRIBUTIONS"));
58         for (i = 0; diststring[i] != 0; i++)
59                 if (isspace(diststring[i]) && !isspace(diststring[i+1]))
60                         ndists++;
61         ndists++; /* Last one */
62
63         urls = calloc(ndists, sizeof(const char *));
64         if (urls == NULL) {
65                 free(diststring);
66                 errx(EXIT_FAILURE, "Out of memory!");
67         }
68
69         init_dialog(stdin, stdout);
70         dialog_vars.backtitle = __DECONST(char *, "FreeBSD Installer");
71         dlg_put_backtitle();
72
73         for (i = 0; i < ndists; i++) {
74                 urls[i] = malloc(PATH_MAX);
75                 snprintf(urls[i], PATH_MAX, "%s/%s",
76                     getenv("BSDINSTALL_DISTSITE"), strsep(&diststring, " \t"));
77         }
78
79         if (chdir(getenv("BSDINSTALL_DISTDIR")) != 0) {
80                 snprintf(error, sizeof(error),
81                     "Could could change to directory %s: %s\n",
82                     getenv("BSDINSTALL_DISTDIR"), strerror(errno));
83                 dialog_msgbox("Error", error, 0, 0, TRUE);
84                 end_dialog();
85                 return (EXIT_FAILURE);
86         }
87
88         nfetched = fetch_files(ndists, urls);
89
90         end_dialog();
91
92         free(diststring);
93         for (i = 0; i < ndists; i++) 
94                 free(urls[i]);
95         free(urls);
96
97         return ((nfetched == ndists) ? EXIT_SUCCESS : EXIT_FAILURE);
98 }
99
100 static int
101 fetch_files(int nfiles, char **urls)
102 {
103         FILE *fetch_out;
104         FILE *file_out;
105         const char **items;
106         int i;
107         int last_progress;
108         int nsuccess = 0; /* Number of files successfully downloaded */
109         int progress = 0;
110         size_t chunk;
111         off_t current_bytes;
112         off_t fsize;
113         off_t total_bytes;
114         char status[8];
115         struct url_stat ustat;
116         char errormsg[PATH_MAX + 512];
117         uint8_t block[4096];
118
119         /* Make the transfer list for dialog */
120         items = calloc(sizeof(char *), nfiles * 2);
121         if (items == NULL)
122                 errx(EXIT_FAILURE, "Out of memory!");
123
124         for (i = 0; i < nfiles; i++) {
125                 items[i*2] = strrchr(urls[i], '/');
126                 if (items[i*2] != NULL)
127                         items[i*2]++;
128                 else
129                         items[i*2] = urls[i];
130                 items[i*2 + 1] = "Pending";
131         }
132
133         dialog_msgbox("", "Connecting to server.\nPlease wait...", 0, 0, FALSE);
134
135         /* Try to stat all the files */
136         total_bytes = 0;
137         for (i = 0; i < nfiles; i++) {
138                 if (fetchStatURL(urls[i], &ustat, "") == 0 && ustat.size > 0)
139                         total_bytes += ustat.size;
140         }
141
142         current_bytes = 0;
143         for (i = 0; i < nfiles; i++) {
144                 last_progress = progress;
145                 if (total_bytes == 0)
146                         progress = (i*100)/nfiles;
147
148                 fetchLastErrCode = 0;
149                 fetch_out = fetchXGetURL(urls[i], &ustat, "");
150                 if (fetch_out == NULL) {
151                         snprintf(errormsg, sizeof(errormsg),
152                             "Error while fetching %s: %s\n", urls[i],
153                             fetchLastErrString);
154                         items[i*2 + 1] = "Failed";
155                         dialog_msgbox("Fetch Error", errormsg, 0, 0,
156                             TRUE);
157                         continue;
158                 }
159
160                 items[i*2 + 1] = "In Progress";
161                 fsize = 0;
162                 file_out = fopen(items[i*2], "w+");
163                 if (file_out == NULL) {
164                         snprintf(errormsg, sizeof(errormsg),
165                             "Error while fetching %s: %s\n",
166                             urls[i], strerror(errno));
167                         items[i*2 + 1] = "Failed";
168                         dialog_msgbox("Fetch Error", errormsg, 0, 0,
169                             TRUE);
170                         fclose(fetch_out);
171                         continue;
172                 }
173
174                 while ((chunk = fread(block, 1, sizeof(block), fetch_out))
175                     > 0) {
176                         if (fwrite(block, 1, chunk, file_out) < chunk)
177                                 break;
178
179                         current_bytes += chunk;
180                         fsize += chunk;
181         
182                         if (total_bytes > 0) {
183                                 last_progress = progress;
184                                 progress = (current_bytes*100)/total_bytes; 
185                         }
186
187                         if (ustat.size > 0) {
188                                 snprintf(status, sizeof(status), "-%jd",
189                                     (fsize*100)/ustat.size);
190                                 items[i*2 + 1] = status;
191                         }
192
193                         if (progress > last_progress)
194                                 dialog_mixedgauge("Fetching Distribution",
195                                     "Fetching distribution files...", 0, 0,
196                                     progress, nfiles,
197                                     __DECONST(char **, items));
198                 }
199
200                 if (ustat.size > 0 && fsize < ustat.size) {
201                         if (fetchLastErrCode == 0) 
202                                 snprintf(errormsg, sizeof(errormsg),
203                                     "Error while fetching %s: %s\n",
204                                     urls[i], strerror(errno));
205                         else
206                                 snprintf(errormsg, sizeof(errormsg),
207                                     "Error while fetching %s: %s\n",
208                                     urls[i], fetchLastErrString);
209                         items[i*2 + 1] = "Failed";
210                         dialog_msgbox("Fetch Error", errormsg, 0, 0,
211                                     TRUE);
212                 } else {
213                         items[i*2 + 1] = "Done";
214                         nsuccess++;
215                 }
216
217                 fclose(fetch_out);
218                 fclose(file_out);
219         }
220
221         free(items);
222         return (nsuccess);
223 }