Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bsdinstall/distextract/distextract.c
Show All 27 Lines | |||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <archive.h> | #include <archive.h> | ||||
#include <ctype.h> | #include <ctype.h> | ||||
#include <dialog.h> | #include <bsddialog.h> | ||||
#include <dpv.h> | #include <bsddialog_progressview.h> | ||||
#include <err.h> | #include <err.h> | ||||
#include <errno.h> | #include <errno.h> | ||||
#include <limits.h> | #include <limits.h> | ||||
#include <signal.h> | |||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <unistd.h> | #include <unistd.h> | ||||
/* Data to process */ | /* Data to process */ | ||||
static char *distdir = NULL; | static char *distdir = NULL; | ||||
static struct archive *archive = NULL; | static struct archive *archive = NULL; | ||||
static struct dpv_file_node *dists = NULL; | |||||
/* Function prototypes */ | /* Function prototypes */ | ||||
static void sig_int(int sig); | static void sig_int(int sig); | ||||
static int count_files(const char *file); | static int count_files(const char *file); | ||||
static int extract_files(struct dpv_file_node *file, int out); | static int extract_files(struct bsddialog_fileminibar *file); | ||||
#define _errx(...) (end_dialog(), errx(__VA_ARGS__)) | #define _errx(...) (bsddialog_end(), errx(__VA_ARGS__)) | ||||
int | int | ||||
main(void) | main(void) | ||||
{ | { | ||||
char *chrootdir; | char *chrootdir; | ||||
char *distributions; | char *distributions; | ||||
unsigned int i; | |||||
int retval; | int retval; | ||||
size_t config_size = sizeof(struct dpv_config); | size_t minibar_size = sizeof(struct bsddialog_fileminibar); | ||||
size_t file_node_size = sizeof(struct dpv_file_node); | |||||
size_t span; | size_t span; | ||||
struct dpv_config *config; | unsigned int nminibars; | ||||
struct dpv_file_node *dist = dists; | struct bsddialog_fileminibar *dists; | ||||
static char backtitle[] = "FreeBSD Installer"; | struct bsddialog_progviewconf pvconf; | ||||
static char title[] = "Archive Extraction"; | struct bsddialog_conf conf; | ||||
static char aprompt[] = "\n Overall Progress:"; | |||||
static char pprompt[] = "Extracting distribution files...\n"; | |||||
struct sigaction act; | struct sigaction act; | ||||
char error[PATH_MAX + 512]; | char error[PATH_MAX + 512]; | ||||
if ((distributions = getenv("DISTRIBUTIONS")) == NULL) | if ((distributions = getenv("DISTRIBUTIONS")) == NULL) | ||||
errx(EXIT_FAILURE, "DISTRIBUTIONS variable is not set"); | errx(EXIT_FAILURE, "DISTRIBUTIONS variable is not set"); | ||||
if ((distdir = getenv("BSDINSTALL_DISTDIR")) == NULL) | if ((distdir = getenv("BSDINSTALL_DISTDIR")) == NULL) | ||||
distdir = __DECONST(char *, ""); | distdir = __DECONST(char *, ""); | ||||
/* Initialize dialog(3) */ | if (bsddialog_init() == BSDDIALOG_ERROR) | ||||
init_dialog(stdin, stdout); | errx(EXIT_FAILURE, "Cannot init libbsdialog"); | ||||
dialog_vars.backtitle = backtitle; | bsddialog_initconf(&conf); | ||||
dlg_put_backtitle(); | bsddialog_backtitle(&conf, __DECONST(char *, "FreeBSD Installer")); | ||||
bsddialog_infobox(&conf, | |||||
__DECONST(char *, "Checking distribution archives.\n" | |||||
"Please wait..."), 4, 35); | |||||
dialog_msgbox("", | /* Parse $DISTRIBUTIONS */ | ||||
"Checking distribution archives.\nPlease wait...", 4, 35, FALSE); | nminibars = 0; | ||||
dists = NULL; | |||||
/* | |||||
* Parse $DISTRIBUTIONS into dpv(3) linked-list | |||||
*/ | |||||
while (*distributions != '\0') { | while (*distributions != '\0') { | ||||
span = strcspn(distributions, "\t\n\v\f\r "); | span = strcspn(distributions, "\t\n\v\f\r "); | ||||
if (span < 1) { /* currently on whitespace */ | if (span < 1) { /* currently on whitespace */ | ||||
distributions++; | distributions++; | ||||
continue; | continue; | ||||
} | } | ||||
/* Allocate a new struct for the distribution */ | /* Allocate a new struct for the distribution */ | ||||
if (dist == NULL) { | dists = realloc(dists, (nminibars + 1) * minibar_size); | ||||
if ((dist = calloc(1, file_node_size)) == NULL) | if (dists == NULL) | ||||
_errx(EXIT_FAILURE, "Out of memory!"); | _errx(EXIT_FAILURE, "Out of memory!"); | ||||
dists = dist; | |||||
} else { | |||||
dist->next = calloc(1, file_node_size); | |||||
if (dist->next == NULL) | |||||
_errx(EXIT_FAILURE, "Out of memory!"); | |||||
dist = dist->next; | |||||
} | |||||
/* Set path */ | /* Set file path */ | ||||
if ((dist->path = malloc(span + 1)) == NULL) | if ((dists[nminibars].path = malloc(span + 1)) == NULL) | ||||
_errx(EXIT_FAILURE, "Out of memory!"); | _errx(EXIT_FAILURE, "Out of memory!"); | ||||
snprintf(dist->path, span + 1, "%s", distributions); | snprintf(dists[nminibars].path, span + 1, "%s", distributions); | ||||
dist->path[span] = '\0'; | dists[nminibars].path[span] = '\0'; | ||||
/* Set display name */ | /* Set mini bar label */ | ||||
dist->name = strrchr(dist->path, '/'); | dists[nminibars].label = strrchr(dists[nminibars].path, '/'); | ||||
if (dist->name == NULL) | if (dists[nminibars].label == NULL) | ||||
dist->name = dist->path; | dists[nminibars].label = dists[nminibars].path; | ||||
/* Set initial length in files (-1 == error) */ | /* Set initial length in files (-1 == error) */ | ||||
dist->length = count_files(dist->path); | dists[nminibars].size = count_files(dists[nminibars].path); | ||||
if (dist->length < 0) { | if (dists[nminibars].size < 0) { | ||||
end_dialog(); | bsddialog_end(); | ||||
return (EXIT_FAILURE); | return (EXIT_FAILURE); | ||||
} | } | ||||
/* Set initial status to pending */ | |||||
/* dists[nminibars].status = 10; */ | |||||
/* Set initial read */ | |||||
dists[nminibars].read = 0; | |||||
distributions += span; | distributions += span; | ||||
nminibars += 1; | |||||
} | } | ||||
/* Optionally chdir(2) into $BSDINSTALL_CHROOT */ | /* Optionally chdir(2) into $BSDINSTALL_CHROOT */ | ||||
chrootdir = getenv("BSDINSTALL_CHROOT"); | chrootdir = getenv("BSDINSTALL_CHROOT"); | ||||
if (chrootdir != NULL && chdir(chrootdir) != 0) { | if (chrootdir != NULL && chdir(chrootdir) != 0) { | ||||
snprintf(error, sizeof(error), | snprintf(error, sizeof(error), | ||||
"Could not change to directory %s: %s\n", | "Could not change to directory %s: %s\n", | ||||
chrootdir, strerror(errno)); | chrootdir, strerror(errno)); | ||||
dialog_msgbox("Error", error, 0, 0, TRUE); | conf.title = __DECONST(char *, "Error"); | ||||
end_dialog(); | bsddialog_msgbox(&conf, error, 0, 0); | ||||
bsddialog_end(); | |||||
return (EXIT_FAILURE); | return (EXIT_FAILURE); | ||||
} | } | ||||
/* Set cleanup routine for Ctrl-C action */ | /* Set cleanup routine for Ctrl-C action */ | ||||
act.sa_handler = sig_int; | act.sa_handler = sig_int; | ||||
sigaction(SIGINT, &act, 0); | sigaction(SIGINT, &act, 0); | ||||
/* | conf.title = __DECONST(char *, "Archive Extraction"); | ||||
* Hand off to dpv(3) | pvconf.callback = extract_files; | ||||
*/ | pvconf.refresh = 1; | ||||
if ((config = calloc(1, config_size)) == NULL) | pvconf.fmtbottomstr = __DECONST(char *, "%10lli files read @ %'9.1f files/sec."); | ||||
_errx(EXIT_FAILURE, "Out of memory!"); | bsddialog_total_progview = 0; | ||||
config->backtitle = backtitle; | bsddialog_interruptprogview = bsddialog_abortprogview = false; | ||||
config->title = title; | retval = bsddialog_progressview(&conf, | ||||
config->pprompt = pprompt; | __DECONST(char *, "Extracting distribution files..."), 0, 0, &pvconf, nminibars, dists); | ||||
config->aprompt = aprompt; | |||||
config->options |= DPV_WIDE_MODE; | |||||
config->label_size = -1; | |||||
config->action = extract_files; | |||||
config->status_solo = | |||||
"%10lli files read @ %'9.1f files/sec."; | |||||
config->status_many = | |||||
"%10lli files read @ %'9.1f files/sec. [%i/%i busy/wait]"; | |||||
end_dialog(); | |||||
retval = dpv(config, dists); | |||||
dpv_free(); | bsddialog_end(); | ||||
while ((dist = dists) != NULL) { | |||||
dists = dist->next; | for (i=0; i<nminibars; i++) { | ||||
if (dist->path != NULL) | if (dists[i].path != NULL) | ||||
free(dist->path); | free(dists[i].path); | ||||
free(dist); | |||||
} | } | ||||
if (dists != NULL) | |||||
free(dists); | |||||
return (retval); | return (retval); | ||||
} | } | ||||
static void | static void | ||||
sig_int(int sig __unused) | sig_int(int sig __unused) | ||||
{ | { | ||||
dpv_interrupt = TRUE; | bsddialog_interruptprogview = true; | ||||
} | } | ||||
/* | /* | ||||
* Returns number of files in archive file. Parses $BSDINSTALL_DISTDIR/MANIFEST | * Returns number of files in archive file. Parses $BSDINSTALL_DISTDIR/MANIFEST | ||||
* if it exists, otherwise uses archive(3) to read the archive file. | * if it exists, otherwise uses archive(3) to read the archive file. | ||||
*/ | */ | ||||
static int | static int | ||||
count_files(const char *file) | count_files(const char *file) | ||||
{ | { | ||||
static FILE *manifest = NULL; | static FILE *manifest = NULL; | ||||
char *p; | char *p; | ||||
int file_count; | int file_count; | ||||
int retval; | int retval; | ||||
size_t span; | size_t span; | ||||
struct archive_entry *entry; | struct archive_entry *entry; | ||||
char line[512]; | char line[512]; | ||||
char path[PATH_MAX]; | char path[PATH_MAX]; | ||||
char errormsg[PATH_MAX + 512]; | char errormsg[PATH_MAX + 512]; | ||||
struct bsddialog_conf conf; | |||||
if (manifest == NULL) { | if (manifest == NULL) { | ||||
snprintf(path, sizeof(path), "%s/MANIFEST", distdir); | snprintf(path, sizeof(path), "%s/MANIFEST", distdir); | ||||
manifest = fopen(path, "r"); | manifest = fopen(path, "r"); | ||||
} | } | ||||
if (manifest != NULL) { | if (manifest != NULL) { | ||||
rewind(manifest); | rewind(manifest); | ||||
Show All 17 Lines | while (fgets(line, sizeof(line), manifest) != NULL) { | ||||
} | } | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Either no manifest, or manifest didn't mention this archive. | * Either no manifest, or manifest didn't mention this archive. | ||||
* Use archive(3) to read the archive, counting files within. | * Use archive(3) to read the archive, counting files within. | ||||
*/ | */ | ||||
bsddialog_initconf(&conf); | |||||
if ((archive = archive_read_new()) == NULL) { | if ((archive = archive_read_new()) == NULL) { | ||||
snprintf(errormsg, sizeof(errormsg), | snprintf(errormsg, sizeof(errormsg), | ||||
"Error: %s\n", archive_error_string(NULL)); | "Error: %s\n", archive_error_string(NULL)); | ||||
dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE); | conf.title = __DECONST(char *, "Extract Error"); | ||||
bsddialog_msgbox(&conf, errormsg, 0, 0); | |||||
return (-1); | return (-1); | ||||
} | } | ||||
archive_read_support_format_all(archive); | archive_read_support_format_all(archive); | ||||
archive_read_support_filter_all(archive); | archive_read_support_filter_all(archive); | ||||
snprintf(path, sizeof(path), "%s/%s", distdir, file); | snprintf(path, sizeof(path), "%s/%s", distdir, file); | ||||
retval = archive_read_open_filename(archive, path, 4096); | retval = archive_read_open_filename(archive, path, 4096); | ||||
if (retval != ARCHIVE_OK) { | if (retval != ARCHIVE_OK) { | ||||
snprintf(errormsg, sizeof(errormsg), | snprintf(errormsg, sizeof(errormsg), | ||||
"Error while extracting %s: %s\n", file, | "Error while extracting %s: %s\n", file, | ||||
archive_error_string(archive)); | archive_error_string(archive)); | ||||
dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE); | conf.title = __DECONST(char *, "Extract Error"); | ||||
bsddialog_msgbox(&conf, errormsg, 0, 0); | |||||
archive = NULL; | archive = NULL; | ||||
return (-1); | return (-1); | ||||
} | } | ||||
file_count = 0; | file_count = 0; | ||||
while (archive_read_next_header(archive, &entry) == ARCHIVE_OK) | while (archive_read_next_header(archive, &entry) == ARCHIVE_OK) | ||||
file_count++; | file_count++; | ||||
archive_read_free(archive); | archive_read_free(archive); | ||||
archive = NULL; | archive = NULL; | ||||
return (file_count); | return (file_count); | ||||
} | } | ||||
static int | static int | ||||
extract_files(struct dpv_file_node *file, int out __unused) | extract_files(struct bsddialog_fileminibar *file) | ||||
{ | { | ||||
int retval; | int retval; | ||||
struct archive_entry *entry; | struct archive_entry *entry; | ||||
char path[PATH_MAX]; | char path[PATH_MAX]; | ||||
char errormsg[PATH_MAX + 512]; | char errormsg[PATH_MAX + 512]; | ||||
struct bsddialog_conf conf; | |||||
bsddialog_initconf(&conf); | |||||
/* Open the archive if necessary */ | /* Open the archive if necessary */ | ||||
if (archive == NULL) { | if (archive == NULL) { | ||||
if ((archive = archive_read_new()) == NULL) { | if ((archive = archive_read_new()) == NULL) { | ||||
snprintf(errormsg, sizeof(errormsg), | snprintf(errormsg, sizeof(errormsg), | ||||
"Error: %s\n", archive_error_string(NULL)); | "Error: %s\n", archive_error_string(NULL)); | ||||
dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE); | conf.title = __DECONST(char *, "Extract Error"); | ||||
dpv_abort = 1; | bsddialog_msgbox(&conf, errormsg, 0, 0); | ||||
bsddialog_abortprogview = true; | |||||
return (-1); | return (-1); | ||||
} | } | ||||
archive_read_support_format_all(archive); | archive_read_support_format_all(archive); | ||||
archive_read_support_filter_all(archive); | archive_read_support_filter_all(archive); | ||||
snprintf(path, sizeof(path), "%s/%s", distdir, file->path); | snprintf(path, sizeof(path), "%s/%s", distdir, file->path); | ||||
retval = archive_read_open_filename(archive, path, 4096); | retval = archive_read_open_filename(archive, path, 4096); | ||||
if (retval != 0) { | if (retval != 0) { | ||||
snprintf(errormsg, sizeof(errormsg), | snprintf(errormsg, sizeof(errormsg), | ||||
"Error opening %s: %s\n", file->name, | "Error opening %s: %s\n", file->label, | ||||
archive_error_string(archive)); | archive_error_string(archive)); | ||||
dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE); | conf.title = __DECONST(char *, "Extract Error"); | ||||
file->status = DPV_STATUS_FAILED; | bsddialog_msgbox(&conf, errormsg, 0, 0); | ||||
dpv_abort = 1; | file->status = 1; /* Failed */ | ||||
bsddialog_abortprogview = true; | |||||
return (-1); | return (-1); | ||||
} | } | ||||
} | } | ||||
/* Read the next archive header */ | /* Read the next archive header */ | ||||
retval = archive_read_next_header(archive, &entry); | retval = archive_read_next_header(archive, &entry); | ||||
/* If that went well, perform the extraction */ | /* If that went well, perform the extraction */ | ||||
if (retval == ARCHIVE_OK) | if (retval == ARCHIVE_OK) | ||||
retval = archive_read_extract(archive, entry, | retval = archive_read_extract(archive, entry, | ||||
ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_OWNER | | ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_OWNER | | ||||
ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL | | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL | | ||||
ARCHIVE_EXTRACT_XATTR | ARCHIVE_EXTRACT_FFLAGS); | ARCHIVE_EXTRACT_XATTR | ARCHIVE_EXTRACT_FFLAGS); | ||||
/* Test for either EOF or error */ | /* Test for either EOF or error */ | ||||
if (retval == ARCHIVE_EOF) { | if (retval == ARCHIVE_EOF) { | ||||
archive_read_free(archive); | archive_read_free(archive); | ||||
archive = NULL; | archive = NULL; | ||||
file->status = DPV_STATUS_DONE; | file->status = 5; /*Done*/; | ||||
return (100); | return (100); | ||||
} else if (retval != ARCHIVE_OK && | } else if (retval != ARCHIVE_OK && | ||||
!(retval == ARCHIVE_WARN && | !(retval == ARCHIVE_WARN && | ||||
strcmp(archive_error_string(archive), "Can't restore time") == 0)) { | strcmp(archive_error_string(archive), "Can't restore time") == 0)) { | ||||
/* | /* | ||||
* Print any warning/error messages except inability to set | * Print any warning/error messages except inability to set | ||||
* ctime/mtime, which is not fatal, or even interesting, | * ctime/mtime, which is not fatal, or even interesting, | ||||
* for our purposes. Would be nice if this were a libarchive | * for our purposes. Would be nice if this were a libarchive | ||||
* option. | * option. | ||||
*/ | */ | ||||
snprintf(errormsg, sizeof(errormsg), | snprintf(errormsg, sizeof(errormsg), | ||||
"Error while extracting %s: %s\n", file->name, | "Error while extracting %s: %s\n", file->label, | ||||
archive_error_string(archive)); | archive_error_string(archive)); | ||||
dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE); | conf.title = __DECONST(char *, "Extract Error"); | ||||
file->status = DPV_STATUS_FAILED; | bsddialog_msgbox(&conf, errormsg, 0, 0); | ||||
dpv_abort = 1; | file->status = 1; /* Failed */ | ||||
bsddialog_abortprogview = true; | |||||
return (-1); | return (-1); | ||||
} | } | ||||
dpv_overall_read++; | bsddialog_total_progview++; | ||||
file->read++; | file->read++; | ||||
/* Calculate [overall] percentage of completion (if possible) */ | /* Calculate [overall] percentage of completion (if possible) */ | ||||
if (file->length >= 0) | if (file->size >= 0) | ||||
return (file->read * 100 / file->length); | return (file->read * 100 / file->size); | ||||
else | else | ||||
return (-1); | return (-1); | ||||
} | } |