12118f387SNathan Whitehorn /*-
2*4d846d26SWarner 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
3046cc2c02SNathan Whitehorn #include <sys/param.h>
31263660c0SAlfonso Siciliano
3282ac9f2bSDevin Teske #include <archive.h>
3382ac9f2bSDevin Teske #include <ctype.h>
3418f20d5dSAlfonso Siciliano #include <bsddialog.h>
3518f20d5dSAlfonso Siciliano #include <bsddialog_progressview.h>
3682ac9f2bSDevin Teske #include <err.h>
372118f387SNathan Whitehorn #include <errno.h>
382118f387SNathan Whitehorn #include <limits.h>
3918f20d5dSAlfonso Siciliano #include <signal.h>
4082ac9f2bSDevin Teske #include <stdio.h>
4182ac9f2bSDevin Teske #include <stdlib.h>
4282ac9f2bSDevin Teske #include <string.h>
4382ac9f2bSDevin Teske #include <unistd.h>
442118f387SNathan Whitehorn
45147585b4SBrad Davis #include "opt_osname.h"
46147585b4SBrad Davis
47bd1df636SDevin Teske /* Data to process */
48f9549860SBaptiste Daroussin static const char *distdir = NULL;
494ff1fc3bSDevin Teske static struct archive *archive = NULL;
50bd1df636SDevin Teske
51bd1df636SDevin Teske /* Function prototypes */
524ff1fc3bSDevin Teske static void sig_int(int sig);
5382ac9f2bSDevin Teske static int count_files(const char *file);
5418f20d5dSAlfonso Siciliano static int extract_files(struct bsddialog_fileminibar *file);
55bd1df636SDevin Teske
5618f20d5dSAlfonso Siciliano #define _errx(...) (bsddialog_end(), errx(__VA_ARGS__))
572118f387SNathan Whitehorn
582118f387SNathan Whitehorn int
main(void)592118f387SNathan Whitehorn main(void)
602118f387SNathan Whitehorn {
61bd1df636SDevin Teske char *chrootdir;
62bd1df636SDevin Teske char *distributions;
63dd8ca6b2SBaptiste Daroussin char *distribs, *distrib;
6482ac9f2bSDevin Teske int retval;
6518f20d5dSAlfonso Siciliano size_t minibar_size = sizeof(struct bsddialog_fileminibar);
6618f20d5dSAlfonso Siciliano unsigned int nminibars;
6718f20d5dSAlfonso Siciliano struct bsddialog_fileminibar *dists;
6818f20d5dSAlfonso Siciliano struct bsddialog_progviewconf pvconf;
6918f20d5dSAlfonso Siciliano struct bsddialog_conf conf;
704ff1fc3bSDevin Teske struct sigaction act;
7182ac9f2bSDevin Teske char error[PATH_MAX + 512];
72bfd258f7SNathan Whitehorn
73bd1df636SDevin Teske if ((distributions = getenv("DISTRIBUTIONS")) == NULL)
7482ac9f2bSDevin Teske errx(EXIT_FAILURE, "DISTRIBUTIONS variable is not set");
75bd1df636SDevin Teske if ((distdir = getenv("BSDINSTALL_DISTDIR")) == NULL)
76f9549860SBaptiste Daroussin distdir = "";
77dd8ca6b2SBaptiste Daroussin if ((distribs = strdup(distributions)) == NULL)
78dd8ca6b2SBaptiste Daroussin errx(EXIT_FAILURE, "memory error");
79bfd258f7SNathan Whitehorn
8018f20d5dSAlfonso Siciliano if (bsddialog_init() == BSDDIALOG_ERROR)
81263660c0SAlfonso Siciliano errx(EXIT_FAILURE, "Error libbsdialog: %s",
82263660c0SAlfonso Siciliano bsddialog_geterror());
8318f20d5dSAlfonso Siciliano bsddialog_initconf(&conf);
84147585b4SBrad Davis bsddialog_backtitle(&conf, OSNAME " Installer");
8518f20d5dSAlfonso Siciliano bsddialog_infobox(&conf,
86263660c0SAlfonso Siciliano "Checking distribution archives.\nPlease wait...", 4, 35);
8757bda1b6SNathan Whitehorn
8818f20d5dSAlfonso Siciliano /* Parse $DISTRIBUTIONS */
8918f20d5dSAlfonso Siciliano nminibars = 0;
9018f20d5dSAlfonso Siciliano dists = NULL;
91dd8ca6b2SBaptiste Daroussin while ((distrib = strsep(&distribs, "\t\n\v\f\r ")) != NULL) {
92dd8ca6b2SBaptiste Daroussin if (strlen(distrib) == 0)
93bd1df636SDevin Teske continue;
94bd1df636SDevin Teske
95bd1df636SDevin Teske /* Allocate a new struct for the distribution */
9618f20d5dSAlfonso Siciliano dists = realloc(dists, (nminibars + 1) * minibar_size);
9718f20d5dSAlfonso Siciliano if (dists == NULL)
98bd1df636SDevin Teske _errx(EXIT_FAILURE, "Out of memory!");
99bd1df636SDevin Teske
10018f20d5dSAlfonso Siciliano /* Set file path */
101dd8ca6b2SBaptiste Daroussin dists[nminibars].path = distrib;
102bd1df636SDevin Teske
10318f20d5dSAlfonso Siciliano /* Set mini bar label */
10418f20d5dSAlfonso Siciliano dists[nminibars].label = strrchr(dists[nminibars].path, '/');
10518f20d5dSAlfonso Siciliano if (dists[nminibars].label == NULL)
10618f20d5dSAlfonso Siciliano dists[nminibars].label = dists[nminibars].path;
107bd1df636SDevin Teske
108bd1df636SDevin Teske /* Set initial length in files (-1 == error) */
10918f20d5dSAlfonso Siciliano dists[nminibars].size = count_files(dists[nminibars].path);
11018f20d5dSAlfonso Siciliano if (dists[nminibars].size < 0) {
11118f20d5dSAlfonso Siciliano bsddialog_end();
112bd1df636SDevin Teske return (EXIT_FAILURE);
113bd1df636SDevin Teske }
114bd1df636SDevin Teske
115263660c0SAlfonso Siciliano /* Set initial status and implicitly miniperc to pending */
116263660c0SAlfonso Siciliano dists[nminibars].status = BSDDIALOG_MG_PENDING;
11718f20d5dSAlfonso Siciliano
11818f20d5dSAlfonso Siciliano /* Set initial read */
11918f20d5dSAlfonso Siciliano dists[nminibars].read = 0;
12018f20d5dSAlfonso Siciliano
12118f20d5dSAlfonso Siciliano nminibars += 1;
122bd1df636SDevin Teske }
123bd1df636SDevin Teske
124bd1df636SDevin Teske /* Optionally chdir(2) into $BSDINSTALL_CHROOT */
125bd1df636SDevin Teske chrootdir = getenv("BSDINSTALL_CHROOT");
126bd1df636SDevin Teske if (chrootdir != NULL && chdir(chrootdir) != 0) {
12782ac9f2bSDevin Teske snprintf(error, sizeof(error),
128bd1df636SDevin Teske "Could not change to directory %s: %s\n",
129bd1df636SDevin Teske chrootdir, strerror(errno));
130f9549860SBaptiste Daroussin conf.title = "Error";
13118f20d5dSAlfonso Siciliano bsddialog_msgbox(&conf, error, 0, 0);
13218f20d5dSAlfonso Siciliano bsddialog_end();
13382ac9f2bSDevin Teske return (EXIT_FAILURE);
13457bda1b6SNathan Whitehorn }
13557bda1b6SNathan Whitehorn
1364ff1fc3bSDevin Teske /* Set cleanup routine for Ctrl-C action */
1374ff1fc3bSDevin Teske act.sa_handler = sig_int;
1384ff1fc3bSDevin Teske sigaction(SIGINT, &act, 0);
1392118f387SNathan Whitehorn
140263660c0SAlfonso Siciliano conf.title = "Archive Extraction";
141263660c0SAlfonso Siciliano conf.auto_minwidth = 40;
14218f20d5dSAlfonso Siciliano pvconf.callback = extract_files;
14318f20d5dSAlfonso Siciliano pvconf.refresh = 1;
144263660c0SAlfonso Siciliano pvconf.fmtbottomstr = "%10lli files read @ %'9.1f files/sec.";
14518f20d5dSAlfonso Siciliano bsddialog_total_progview = 0;
14618f20d5dSAlfonso Siciliano bsddialog_interruptprogview = bsddialog_abortprogview = false;
14718f20d5dSAlfonso Siciliano retval = bsddialog_progressview(&conf,
148263660c0SAlfonso Siciliano "\nExtracting distribution files...\n", 0, 0,
149263660c0SAlfonso Siciliano &pvconf, nminibars, dists);
150263660c0SAlfonso Siciliano
151263660c0SAlfonso Siciliano if (retval == BSDDIALOG_ERROR) {
152263660c0SAlfonso Siciliano fprintf(stderr, "progressview error: %s\n",
153263660c0SAlfonso Siciliano bsddialog_geterror());
154263660c0SAlfonso Siciliano }
15557bda1b6SNathan Whitehorn
15618f20d5dSAlfonso Siciliano bsddialog_end();
15718f20d5dSAlfonso Siciliano
158dd8ca6b2SBaptiste Daroussin free(distribs);
15918f20d5dSAlfonso Siciliano free(dists);
1602118f387SNathan Whitehorn
1612118f387SNathan Whitehorn return (retval);
1622118f387SNathan Whitehorn }
1632118f387SNathan Whitehorn
1644ff1fc3bSDevin Teske static void
sig_int(int sig __unused)1654ff1fc3bSDevin Teske sig_int(int sig __unused)
1664ff1fc3bSDevin Teske {
16718f20d5dSAlfonso Siciliano bsddialog_interruptprogview = true;
1684ff1fc3bSDevin Teske }
1694ff1fc3bSDevin Teske
170bd1df636SDevin Teske /*
171bd1df636SDevin Teske * Returns number of files in archive file. Parses $BSDINSTALL_DISTDIR/MANIFEST
172bd1df636SDevin Teske * if it exists, otherwise uses archive(3) to read the archive file.
173bd1df636SDevin Teske */
1742118f387SNathan Whitehorn static int
count_files(const char * file)17546cc2c02SNathan Whitehorn count_files(const char *file)
17646cc2c02SNathan Whitehorn {
17782ac9f2bSDevin Teske static FILE *manifest = NULL;
178bd1df636SDevin Teske char *p;
17982ac9f2bSDevin Teske int file_count;
18082ac9f2bSDevin Teske int retval;
181bd1df636SDevin Teske size_t span;
18246cc2c02SNathan Whitehorn struct archive_entry *entry;
18382ac9f2bSDevin Teske char line[512];
18482ac9f2bSDevin Teske char path[PATH_MAX];
18582ac9f2bSDevin Teske char errormsg[PATH_MAX + 512];
18618f20d5dSAlfonso Siciliano struct bsddialog_conf conf;
18746cc2c02SNathan Whitehorn
18846cc2c02SNathan Whitehorn if (manifest == NULL) {
189bd1df636SDevin Teske snprintf(path, sizeof(path), "%s/MANIFEST", distdir);
19046cc2c02SNathan Whitehorn manifest = fopen(path, "r");
19146cc2c02SNathan Whitehorn }
19246cc2c02SNathan Whitehorn
19346cc2c02SNathan Whitehorn if (manifest != NULL) {
194896a9484SNathan Whitehorn rewind(manifest);
19546cc2c02SNathan Whitehorn while (fgets(line, sizeof(line), manifest) != NULL) {
196bd1df636SDevin Teske p = &line[0];
197bd1df636SDevin Teske span = strcspn(p, "\t") ;
198bd1df636SDevin Teske if (span < 1 || strncmp(p, file, span) != 0)
19946cc2c02SNathan Whitehorn continue;
20046cc2c02SNathan Whitehorn
20146cc2c02SNathan Whitehorn /*
20246cc2c02SNathan Whitehorn * We're at the right manifest line. The file count is
20346cc2c02SNathan Whitehorn * in the third element
20446cc2c02SNathan Whitehorn */
205bd1df636SDevin Teske span = strcspn(p += span + (*p != '\0' ? 1 : 0), "\t");
206bd1df636SDevin Teske span = strcspn(p += span + (*p != '\0' ? 1 : 0), "\t");
207bd1df636SDevin Teske if (span > 0) {
208bd1df636SDevin Teske file_count = (int)strtol(p, (char **)NULL, 10);
209bd1df636SDevin Teske if (file_count == 0 && errno == EINVAL)
210bd1df636SDevin Teske continue;
211bd1df636SDevin Teske return (file_count);
212bd1df636SDevin Teske }
21346cc2c02SNathan Whitehorn }
21446cc2c02SNathan Whitehorn }
21546cc2c02SNathan Whitehorn
216bd1df636SDevin Teske /*
217bd1df636SDevin Teske * Either no manifest, or manifest didn't mention this archive.
218bd1df636SDevin Teske * Use archive(3) to read the archive, counting files within.
219bd1df636SDevin Teske */
22018f20d5dSAlfonso Siciliano bsddialog_initconf(&conf);
221bd1df636SDevin Teske if ((archive = archive_read_new()) == NULL) {
222bd1df636SDevin Teske snprintf(errormsg, sizeof(errormsg),
223bd1df636SDevin Teske "Error: %s\n", archive_error_string(NULL));
224263660c0SAlfonso Siciliano conf.title = "Extract Error";
22518f20d5dSAlfonso Siciliano bsddialog_msgbox(&conf, errormsg, 0, 0);
226bd1df636SDevin Teske return (-1);
227bd1df636SDevin Teske }
22846cc2c02SNathan Whitehorn archive_read_support_format_all(archive);
229ebb8fc42SMartin Matuska archive_read_support_filter_all(archive);
230bd1df636SDevin Teske snprintf(path, sizeof(path), "%s/%s", distdir, file);
23182ac9f2bSDevin Teske retval = archive_read_open_filename(archive, path, 4096);
23282ac9f2bSDevin Teske if (retval != ARCHIVE_OK) {
23346cc2c02SNathan Whitehorn snprintf(errormsg, sizeof(errormsg),
23446cc2c02SNathan Whitehorn "Error while extracting %s: %s\n", file,
23546cc2c02SNathan Whitehorn archive_error_string(archive));
236263660c0SAlfonso Siciliano conf.title = "Extract Error";
23718f20d5dSAlfonso Siciliano bsddialog_msgbox(&conf, errormsg, 0, 0);
2384ff1fc3bSDevin Teske archive = NULL;
23946cc2c02SNathan Whitehorn return (-1);
24046cc2c02SNathan Whitehorn }
24146cc2c02SNathan Whitehorn
24246cc2c02SNathan Whitehorn file_count = 0;
24346cc2c02SNathan Whitehorn while (archive_read_next_header(archive, &entry) == ARCHIVE_OK)
24446cc2c02SNathan Whitehorn file_count++;
24546cc2c02SNathan Whitehorn archive_read_free(archive);
2464ff1fc3bSDevin Teske archive = NULL;
24746cc2c02SNathan Whitehorn
24846cc2c02SNathan Whitehorn return (file_count);
24946cc2c02SNathan Whitehorn }
25046cc2c02SNathan Whitehorn
25146cc2c02SNathan Whitehorn static int
extract_files(struct bsddialog_fileminibar * file)25218f20d5dSAlfonso Siciliano extract_files(struct bsddialog_fileminibar *file)
2532118f387SNathan Whitehorn {
25482ac9f2bSDevin Teske int retval;
2552118f387SNathan Whitehorn struct archive_entry *entry;
25682ac9f2bSDevin Teske char path[PATH_MAX];
25782ac9f2bSDevin Teske char errormsg[PATH_MAX + 512];
25818f20d5dSAlfonso Siciliano struct bsddialog_conf conf;
25918f20d5dSAlfonso Siciliano
26018f20d5dSAlfonso Siciliano bsddialog_initconf(&conf);
2612118f387SNathan Whitehorn
2624ff1fc3bSDevin Teske /* Open the archive if necessary */
2634ff1fc3bSDevin Teske if (archive == NULL) {
264bd1df636SDevin Teske if ((archive = archive_read_new()) == NULL) {
265bd1df636SDevin Teske snprintf(errormsg, sizeof(errormsg),
266bd1df636SDevin Teske "Error: %s\n", archive_error_string(NULL));
267263660c0SAlfonso Siciliano conf.title = "Extract Error";
26818f20d5dSAlfonso Siciliano bsddialog_msgbox(&conf, errormsg, 0, 0);
26918f20d5dSAlfonso Siciliano bsddialog_abortprogview = true;
2704ff1fc3bSDevin Teske return (-1);
2712118f387SNathan Whitehorn }
2722118f387SNathan Whitehorn archive_read_support_format_all(archive);
273ebb8fc42SMartin Matuska archive_read_support_filter_all(archive);
274bd1df636SDevin Teske snprintf(path, sizeof(path), "%s/%s", distdir, file->path);
27582ac9f2bSDevin Teske retval = archive_read_open_filename(archive, path, 4096);
276bd1df636SDevin Teske if (retval != 0) {
277bd1df636SDevin Teske snprintf(errormsg, sizeof(errormsg),
27818f20d5dSAlfonso Siciliano "Error opening %s: %s\n", file->label,
279bd1df636SDevin Teske archive_error_string(archive));
280263660c0SAlfonso Siciliano conf.title = "Extract Error";
28118f20d5dSAlfonso Siciliano bsddialog_msgbox(&conf, errormsg, 0, 0);
282263660c0SAlfonso Siciliano file->status = BSDDIALOG_MG_FAILED;
28318f20d5dSAlfonso Siciliano bsddialog_abortprogview = true;
2844ff1fc3bSDevin Teske return (-1);
2854ff1fc3bSDevin Teske }
286bd1df636SDevin Teske }
2872118f387SNathan Whitehorn
2884ff1fc3bSDevin Teske /* Read the next archive header */
2894ff1fc3bSDevin Teske retval = archive_read_next_header(archive, &entry);
2902118f387SNathan Whitehorn
2914ff1fc3bSDevin Teske /* If that went well, perform the extraction */
2924ff1fc3bSDevin Teske if (retval == ARCHIVE_OK)
29382ac9f2bSDevin Teske retval = archive_read_extract(archive, entry,
2942118f387SNathan Whitehorn ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_OWNER |
2952118f387SNathan Whitehorn ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL |
2962118f387SNathan Whitehorn ARCHIVE_EXTRACT_XATTR | ARCHIVE_EXTRACT_FFLAGS);
2972118f387SNathan Whitehorn
2984ff1fc3bSDevin Teske /* Test for either EOF or error */
2994ff1fc3bSDevin Teske if (retval == ARCHIVE_EOF) {
3002118f387SNathan Whitehorn archive_read_free(archive);
3014ff1fc3bSDevin Teske archive = NULL;
302263660c0SAlfonso Siciliano file->status = BSDDIALOG_MG_DONE; /*Done*/;
3034ff1fc3bSDevin Teske return (100);
304a5837f2cSNathan Whitehorn } else if (retval != ARCHIVE_OK &&
305a5837f2cSNathan Whitehorn !(retval == ARCHIVE_WARN &&
306a5837f2cSNathan Whitehorn strcmp(archive_error_string(archive), "Can't restore time") == 0)) {
307a5837f2cSNathan Whitehorn /*
308a5837f2cSNathan Whitehorn * Print any warning/error messages except inability to set
309a5837f2cSNathan Whitehorn * ctime/mtime, which is not fatal, or even interesting,
310a5837f2cSNathan Whitehorn * for our purposes. Would be nice if this were a libarchive
311a5837f2cSNathan Whitehorn * option.
312a5837f2cSNathan Whitehorn */
3134ff1fc3bSDevin Teske snprintf(errormsg, sizeof(errormsg),
31418f20d5dSAlfonso Siciliano "Error while extracting %s: %s\n", file->label,
3154ff1fc3bSDevin Teske archive_error_string(archive));
316263660c0SAlfonso Siciliano conf.title = "Extract Error";
31718f20d5dSAlfonso Siciliano bsddialog_msgbox(&conf, errormsg, 0, 0);
318263660c0SAlfonso Siciliano file->status = BSDDIALOG_MG_FAILED; /* Failed */
31918f20d5dSAlfonso Siciliano bsddialog_abortprogview = true;
3204ff1fc3bSDevin Teske return (-1);
3212118f387SNathan Whitehorn }
3222118f387SNathan Whitehorn
32318f20d5dSAlfonso Siciliano bsddialog_total_progview++;
3244ff1fc3bSDevin Teske file->read++;
3254ff1fc3bSDevin Teske
3264ff1fc3bSDevin Teske /* Calculate [overall] percentage of completion (if possible) */
32718f20d5dSAlfonso Siciliano if (file->size >= 0)
32818f20d5dSAlfonso Siciliano return (file->read * 100 / file->size);
3294ff1fc3bSDevin Teske else
3304ff1fc3bSDevin Teske return (-1);
3312118f387SNathan Whitehorn }
332