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 3082ac9f2bSDevin Teske #include <sys/cdefs.h> 3182ac9f2bSDevin Teske __FBSDID("$FreeBSD$"); 3282ac9f2bSDevin Teske 3346cc2c02SNathan Whitehorn #include <sys/param.h> 34263660c0SAlfonso Siciliano 3582ac9f2bSDevin Teske #include <archive.h> 3682ac9f2bSDevin Teske #include <ctype.h> 3718f20d5dSAlfonso Siciliano #include <bsddialog.h> 3818f20d5dSAlfonso Siciliano #include <bsddialog_progressview.h> 3982ac9f2bSDevin Teske #include <err.h> 402118f387SNathan Whitehorn #include <errno.h> 412118f387SNathan Whitehorn #include <limits.h> 4218f20d5dSAlfonso Siciliano #include <signal.h> 4382ac9f2bSDevin Teske #include <stdio.h> 4482ac9f2bSDevin Teske #include <stdlib.h> 4582ac9f2bSDevin Teske #include <string.h> 4682ac9f2bSDevin Teske #include <unistd.h> 472118f387SNathan Whitehorn 48147585b4SBrad Davis #include "opt_osname.h" 49147585b4SBrad Davis 50bd1df636SDevin Teske /* Data to process */ 51f9549860SBaptiste Daroussin static const char *distdir = NULL; 524ff1fc3bSDevin Teske static struct archive *archive = NULL; 53bd1df636SDevin Teske 54bd1df636SDevin Teske /* Function prototypes */ 554ff1fc3bSDevin Teske static void sig_int(int sig); 5682ac9f2bSDevin Teske static int count_files(const char *file); 5718f20d5dSAlfonso Siciliano static int extract_files(struct bsddialog_fileminibar *file); 58bd1df636SDevin Teske 5918f20d5dSAlfonso Siciliano #define _errx(...) (bsddialog_end(), errx(__VA_ARGS__)) 602118f387SNathan Whitehorn 612118f387SNathan Whitehorn int 622118f387SNathan Whitehorn main(void) 632118f387SNathan Whitehorn { 64bd1df636SDevin Teske char *chrootdir; 65bd1df636SDevin Teske char *distributions; 66dd8ca6b2SBaptiste Daroussin char *distribs, *distrib; 6782ac9f2bSDevin Teske int retval; 6818f20d5dSAlfonso Siciliano size_t minibar_size = sizeof(struct bsddialog_fileminibar); 6918f20d5dSAlfonso Siciliano unsigned int nminibars; 7018f20d5dSAlfonso Siciliano struct bsddialog_fileminibar *dists; 7118f20d5dSAlfonso Siciliano struct bsddialog_progviewconf pvconf; 7218f20d5dSAlfonso Siciliano struct bsddialog_conf conf; 734ff1fc3bSDevin Teske struct sigaction act; 7482ac9f2bSDevin Teske char error[PATH_MAX + 512]; 75bfd258f7SNathan Whitehorn 76bd1df636SDevin Teske if ((distributions = getenv("DISTRIBUTIONS")) == NULL) 7782ac9f2bSDevin Teske errx(EXIT_FAILURE, "DISTRIBUTIONS variable is not set"); 78bd1df636SDevin Teske if ((distdir = getenv("BSDINSTALL_DISTDIR")) == NULL) 79f9549860SBaptiste Daroussin distdir = ""; 80dd8ca6b2SBaptiste Daroussin if ((distribs = strdup(distributions)) == NULL) 81dd8ca6b2SBaptiste Daroussin errx(EXIT_FAILURE, "memory error"); 82bfd258f7SNathan Whitehorn 8318f20d5dSAlfonso Siciliano if (bsddialog_init() == BSDDIALOG_ERROR) 84263660c0SAlfonso Siciliano errx(EXIT_FAILURE, "Error libbsdialog: %s", 85263660c0SAlfonso Siciliano bsddialog_geterror()); 8618f20d5dSAlfonso Siciliano bsddialog_initconf(&conf); 87147585b4SBrad Davis bsddialog_backtitle(&conf, OSNAME " Installer"); 8818f20d5dSAlfonso Siciliano bsddialog_infobox(&conf, 89263660c0SAlfonso Siciliano "Checking distribution archives.\nPlease wait...", 4, 35); 9057bda1b6SNathan Whitehorn 9118f20d5dSAlfonso Siciliano /* Parse $DISTRIBUTIONS */ 9218f20d5dSAlfonso Siciliano nminibars = 0; 9318f20d5dSAlfonso Siciliano dists = NULL; 94dd8ca6b2SBaptiste Daroussin while ((distrib = strsep(&distribs, "\t\n\v\f\r ")) != NULL) { 95dd8ca6b2SBaptiste Daroussin if (strlen(distrib) == 0) 96bd1df636SDevin Teske continue; 97bd1df636SDevin Teske 98bd1df636SDevin Teske /* Allocate a new struct for the distribution */ 9918f20d5dSAlfonso Siciliano dists = realloc(dists, (nminibars + 1) * minibar_size); 10018f20d5dSAlfonso Siciliano if (dists == NULL) 101bd1df636SDevin Teske _errx(EXIT_FAILURE, "Out of memory!"); 102bd1df636SDevin Teske 10318f20d5dSAlfonso Siciliano /* Set file path */ 104dd8ca6b2SBaptiste Daroussin dists[nminibars].path = distrib; 105bd1df636SDevin Teske 10618f20d5dSAlfonso Siciliano /* Set mini bar label */ 10718f20d5dSAlfonso Siciliano dists[nminibars].label = strrchr(dists[nminibars].path, '/'); 10818f20d5dSAlfonso Siciliano if (dists[nminibars].label == NULL) 10918f20d5dSAlfonso Siciliano dists[nminibars].label = dists[nminibars].path; 110bd1df636SDevin Teske 111bd1df636SDevin Teske /* Set initial length in files (-1 == error) */ 11218f20d5dSAlfonso Siciliano dists[nminibars].size = count_files(dists[nminibars].path); 11318f20d5dSAlfonso Siciliano if (dists[nminibars].size < 0) { 11418f20d5dSAlfonso Siciliano bsddialog_end(); 115bd1df636SDevin Teske return (EXIT_FAILURE); 116bd1df636SDevin Teske } 117bd1df636SDevin Teske 118263660c0SAlfonso Siciliano /* Set initial status and implicitly miniperc to pending */ 119263660c0SAlfonso Siciliano dists[nminibars].status = BSDDIALOG_MG_PENDING; 12018f20d5dSAlfonso Siciliano 12118f20d5dSAlfonso Siciliano /* Set initial read */ 12218f20d5dSAlfonso Siciliano dists[nminibars].read = 0; 12318f20d5dSAlfonso Siciliano 12418f20d5dSAlfonso Siciliano nminibars += 1; 125bd1df636SDevin Teske } 126bd1df636SDevin Teske 127bd1df636SDevin Teske /* Optionally chdir(2) into $BSDINSTALL_CHROOT */ 128bd1df636SDevin Teske chrootdir = getenv("BSDINSTALL_CHROOT"); 129bd1df636SDevin Teske if (chrootdir != NULL && chdir(chrootdir) != 0) { 13082ac9f2bSDevin Teske snprintf(error, sizeof(error), 131bd1df636SDevin Teske "Could not change to directory %s: %s\n", 132bd1df636SDevin Teske chrootdir, strerror(errno)); 133f9549860SBaptiste Daroussin conf.title = "Error"; 13418f20d5dSAlfonso Siciliano bsddialog_msgbox(&conf, error, 0, 0); 13518f20d5dSAlfonso Siciliano bsddialog_end(); 13682ac9f2bSDevin Teske return (EXIT_FAILURE); 13757bda1b6SNathan Whitehorn } 13857bda1b6SNathan Whitehorn 1394ff1fc3bSDevin Teske /* Set cleanup routine for Ctrl-C action */ 1404ff1fc3bSDevin Teske act.sa_handler = sig_int; 1414ff1fc3bSDevin Teske sigaction(SIGINT, &act, 0); 1422118f387SNathan Whitehorn 143263660c0SAlfonso Siciliano conf.title = "Archive Extraction"; 144263660c0SAlfonso Siciliano conf.auto_minwidth = 40; 14518f20d5dSAlfonso Siciliano pvconf.callback = extract_files; 14618f20d5dSAlfonso Siciliano pvconf.refresh = 1; 147263660c0SAlfonso Siciliano pvconf.fmtbottomstr = "%10lli files read @ %'9.1f files/sec."; 14818f20d5dSAlfonso Siciliano bsddialog_total_progview = 0; 14918f20d5dSAlfonso Siciliano bsddialog_interruptprogview = bsddialog_abortprogview = false; 15018f20d5dSAlfonso Siciliano retval = bsddialog_progressview(&conf, 151263660c0SAlfonso Siciliano "\nExtracting distribution files...\n", 0, 0, 152263660c0SAlfonso Siciliano &pvconf, nminibars, dists); 153263660c0SAlfonso Siciliano 154263660c0SAlfonso Siciliano if (retval == BSDDIALOG_ERROR) { 155263660c0SAlfonso Siciliano fprintf(stderr, "progressview error: %s\n", 156263660c0SAlfonso Siciliano bsddialog_geterror()); 157263660c0SAlfonso Siciliano } 15857bda1b6SNathan Whitehorn 15918f20d5dSAlfonso Siciliano bsddialog_end(); 16018f20d5dSAlfonso Siciliano 161dd8ca6b2SBaptiste Daroussin free(distribs); 16218f20d5dSAlfonso Siciliano free(dists); 1632118f387SNathan Whitehorn 1642118f387SNathan Whitehorn return (retval); 1652118f387SNathan Whitehorn } 1662118f387SNathan Whitehorn 1674ff1fc3bSDevin Teske static void 1684ff1fc3bSDevin Teske sig_int(int sig __unused) 1694ff1fc3bSDevin Teske { 17018f20d5dSAlfonso Siciliano bsddialog_interruptprogview = true; 1714ff1fc3bSDevin Teske } 1724ff1fc3bSDevin Teske 173bd1df636SDevin Teske /* 174bd1df636SDevin Teske * Returns number of files in archive file. Parses $BSDINSTALL_DISTDIR/MANIFEST 175bd1df636SDevin Teske * if it exists, otherwise uses archive(3) to read the archive file. 176bd1df636SDevin Teske */ 1772118f387SNathan Whitehorn static int 17846cc2c02SNathan Whitehorn count_files(const char *file) 17946cc2c02SNathan Whitehorn { 18082ac9f2bSDevin Teske static FILE *manifest = NULL; 181bd1df636SDevin Teske char *p; 18282ac9f2bSDevin Teske int file_count; 18382ac9f2bSDevin Teske int retval; 184bd1df636SDevin Teske size_t span; 18546cc2c02SNathan Whitehorn struct archive_entry *entry; 18682ac9f2bSDevin Teske char line[512]; 18782ac9f2bSDevin Teske char path[PATH_MAX]; 18882ac9f2bSDevin Teske char errormsg[PATH_MAX + 512]; 18918f20d5dSAlfonso Siciliano struct bsddialog_conf conf; 19046cc2c02SNathan Whitehorn 19146cc2c02SNathan Whitehorn if (manifest == NULL) { 192bd1df636SDevin Teske snprintf(path, sizeof(path), "%s/MANIFEST", distdir); 19346cc2c02SNathan Whitehorn manifest = fopen(path, "r"); 19446cc2c02SNathan Whitehorn } 19546cc2c02SNathan Whitehorn 19646cc2c02SNathan Whitehorn if (manifest != NULL) { 197896a9484SNathan Whitehorn rewind(manifest); 19846cc2c02SNathan Whitehorn while (fgets(line, sizeof(line), manifest) != NULL) { 199bd1df636SDevin Teske p = &line[0]; 200bd1df636SDevin Teske span = strcspn(p, "\t") ; 201bd1df636SDevin Teske if (span < 1 || strncmp(p, file, span) != 0) 20246cc2c02SNathan Whitehorn continue; 20346cc2c02SNathan Whitehorn 20446cc2c02SNathan Whitehorn /* 20546cc2c02SNathan Whitehorn * We're at the right manifest line. The file count is 20646cc2c02SNathan Whitehorn * in the third element 20746cc2c02SNathan Whitehorn */ 208bd1df636SDevin Teske span = strcspn(p += span + (*p != '\0' ? 1 : 0), "\t"); 209bd1df636SDevin Teske span = strcspn(p += span + (*p != '\0' ? 1 : 0), "\t"); 210bd1df636SDevin Teske if (span > 0) { 211bd1df636SDevin Teske file_count = (int)strtol(p, (char **)NULL, 10); 212bd1df636SDevin Teske if (file_count == 0 && errno == EINVAL) 213bd1df636SDevin Teske continue; 214bd1df636SDevin Teske return (file_count); 215bd1df636SDevin Teske } 21646cc2c02SNathan Whitehorn } 21746cc2c02SNathan Whitehorn } 21846cc2c02SNathan Whitehorn 219bd1df636SDevin Teske /* 220bd1df636SDevin Teske * Either no manifest, or manifest didn't mention this archive. 221bd1df636SDevin Teske * Use archive(3) to read the archive, counting files within. 222bd1df636SDevin Teske */ 22318f20d5dSAlfonso Siciliano bsddialog_initconf(&conf); 224bd1df636SDevin Teske if ((archive = archive_read_new()) == NULL) { 225bd1df636SDevin Teske snprintf(errormsg, sizeof(errormsg), 226bd1df636SDevin Teske "Error: %s\n", archive_error_string(NULL)); 227263660c0SAlfonso Siciliano conf.title = "Extract Error"; 22818f20d5dSAlfonso Siciliano bsddialog_msgbox(&conf, errormsg, 0, 0); 229bd1df636SDevin Teske return (-1); 230bd1df636SDevin Teske } 23146cc2c02SNathan Whitehorn archive_read_support_format_all(archive); 232ebb8fc42SMartin Matuska archive_read_support_filter_all(archive); 233bd1df636SDevin Teske snprintf(path, sizeof(path), "%s/%s", distdir, file); 23482ac9f2bSDevin Teske retval = archive_read_open_filename(archive, path, 4096); 23582ac9f2bSDevin Teske if (retval != ARCHIVE_OK) { 23646cc2c02SNathan Whitehorn snprintf(errormsg, sizeof(errormsg), 23746cc2c02SNathan Whitehorn "Error while extracting %s: %s\n", file, 23846cc2c02SNathan Whitehorn archive_error_string(archive)); 239263660c0SAlfonso Siciliano conf.title = "Extract Error"; 24018f20d5dSAlfonso Siciliano bsddialog_msgbox(&conf, errormsg, 0, 0); 2414ff1fc3bSDevin Teske archive = NULL; 24246cc2c02SNathan Whitehorn return (-1); 24346cc2c02SNathan Whitehorn } 24446cc2c02SNathan Whitehorn 24546cc2c02SNathan Whitehorn file_count = 0; 24646cc2c02SNathan Whitehorn while (archive_read_next_header(archive, &entry) == ARCHIVE_OK) 24746cc2c02SNathan Whitehorn file_count++; 24846cc2c02SNathan Whitehorn archive_read_free(archive); 2494ff1fc3bSDevin Teske archive = NULL; 25046cc2c02SNathan Whitehorn 25146cc2c02SNathan Whitehorn return (file_count); 25246cc2c02SNathan Whitehorn } 25346cc2c02SNathan Whitehorn 25446cc2c02SNathan Whitehorn static int 25518f20d5dSAlfonso Siciliano extract_files(struct bsddialog_fileminibar *file) 2562118f387SNathan Whitehorn { 25782ac9f2bSDevin Teske int retval; 2582118f387SNathan Whitehorn struct archive_entry *entry; 25982ac9f2bSDevin Teske char path[PATH_MAX]; 26082ac9f2bSDevin Teske char errormsg[PATH_MAX + 512]; 26118f20d5dSAlfonso Siciliano struct bsddialog_conf conf; 26218f20d5dSAlfonso Siciliano 26318f20d5dSAlfonso Siciliano bsddialog_initconf(&conf); 2642118f387SNathan Whitehorn 2654ff1fc3bSDevin Teske /* Open the archive if necessary */ 2664ff1fc3bSDevin Teske if (archive == NULL) { 267bd1df636SDevin Teske if ((archive = archive_read_new()) == NULL) { 268bd1df636SDevin Teske snprintf(errormsg, sizeof(errormsg), 269bd1df636SDevin Teske "Error: %s\n", archive_error_string(NULL)); 270263660c0SAlfonso Siciliano conf.title = "Extract Error"; 27118f20d5dSAlfonso Siciliano bsddialog_msgbox(&conf, errormsg, 0, 0); 27218f20d5dSAlfonso Siciliano bsddialog_abortprogview = true; 2734ff1fc3bSDevin Teske return (-1); 2742118f387SNathan Whitehorn } 2752118f387SNathan Whitehorn archive_read_support_format_all(archive); 276ebb8fc42SMartin Matuska archive_read_support_filter_all(archive); 277bd1df636SDevin Teske snprintf(path, sizeof(path), "%s/%s", distdir, file->path); 27882ac9f2bSDevin Teske retval = archive_read_open_filename(archive, path, 4096); 279bd1df636SDevin Teske if (retval != 0) { 280bd1df636SDevin Teske snprintf(errormsg, sizeof(errormsg), 28118f20d5dSAlfonso Siciliano "Error opening %s: %s\n", file->label, 282bd1df636SDevin Teske archive_error_string(archive)); 283263660c0SAlfonso Siciliano conf.title = "Extract Error"; 28418f20d5dSAlfonso Siciliano bsddialog_msgbox(&conf, errormsg, 0, 0); 285263660c0SAlfonso Siciliano file->status = BSDDIALOG_MG_FAILED; 28618f20d5dSAlfonso Siciliano bsddialog_abortprogview = true; 2874ff1fc3bSDevin Teske return (-1); 2884ff1fc3bSDevin Teske } 289bd1df636SDevin Teske } 2902118f387SNathan Whitehorn 2914ff1fc3bSDevin Teske /* Read the next archive header */ 2924ff1fc3bSDevin Teske retval = archive_read_next_header(archive, &entry); 2932118f387SNathan Whitehorn 2944ff1fc3bSDevin Teske /* If that went well, perform the extraction */ 2954ff1fc3bSDevin Teske if (retval == ARCHIVE_OK) 29682ac9f2bSDevin Teske retval = archive_read_extract(archive, entry, 2972118f387SNathan Whitehorn ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_OWNER | 2982118f387SNathan Whitehorn ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL | 2992118f387SNathan Whitehorn ARCHIVE_EXTRACT_XATTR | ARCHIVE_EXTRACT_FFLAGS); 3002118f387SNathan Whitehorn 3014ff1fc3bSDevin Teske /* Test for either EOF or error */ 3024ff1fc3bSDevin Teske if (retval == ARCHIVE_EOF) { 3032118f387SNathan Whitehorn archive_read_free(archive); 3044ff1fc3bSDevin Teske archive = NULL; 305263660c0SAlfonso Siciliano file->status = BSDDIALOG_MG_DONE; /*Done*/; 3064ff1fc3bSDevin Teske return (100); 307a5837f2cSNathan Whitehorn } else if (retval != ARCHIVE_OK && 308a5837f2cSNathan Whitehorn !(retval == ARCHIVE_WARN && 309a5837f2cSNathan Whitehorn strcmp(archive_error_string(archive), "Can't restore time") == 0)) { 310a5837f2cSNathan Whitehorn /* 311a5837f2cSNathan Whitehorn * Print any warning/error messages except inability to set 312a5837f2cSNathan Whitehorn * ctime/mtime, which is not fatal, or even interesting, 313a5837f2cSNathan Whitehorn * for our purposes. Would be nice if this were a libarchive 314a5837f2cSNathan Whitehorn * option. 315a5837f2cSNathan Whitehorn */ 3164ff1fc3bSDevin Teske snprintf(errormsg, sizeof(errormsg), 31718f20d5dSAlfonso Siciliano "Error while extracting %s: %s\n", file->label, 3184ff1fc3bSDevin Teske archive_error_string(archive)); 319263660c0SAlfonso Siciliano conf.title = "Extract Error"; 32018f20d5dSAlfonso Siciliano bsddialog_msgbox(&conf, errormsg, 0, 0); 321263660c0SAlfonso Siciliano file->status = BSDDIALOG_MG_FAILED; /* Failed */ 32218f20d5dSAlfonso Siciliano bsddialog_abortprogview = true; 3234ff1fc3bSDevin Teske return (-1); 3242118f387SNathan Whitehorn } 3252118f387SNathan Whitehorn 32618f20d5dSAlfonso Siciliano bsddialog_total_progview++; 3274ff1fc3bSDevin Teske file->read++; 3284ff1fc3bSDevin Teske 3294ff1fc3bSDevin Teske /* Calculate [overall] percentage of completion (if possible) */ 33018f20d5dSAlfonso Siciliano if (file->size >= 0) 33118f20d5dSAlfonso Siciliano return (file->read * 100 / file->size); 3324ff1fc3bSDevin Teske else 3334ff1fc3bSDevin Teske return (-1); 3342118f387SNathan Whitehorn } 335