#include <sys/param.h>
#include <archive.h>
#include <ctype.h>
#include <bsddialog.h>
#include <bsddialog_progressview.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "opt_osname.h"
static const char *distdir = NULL;
static struct archive *archive = NULL;
static void sig_int(int sig);
static int count_files(const char *file);
static int extract_files(struct bsddialog_fileminibar *file);
#define _errx(...) (bsddialog_end(), errx(__VA_ARGS__))
int
main(void)
{
char *chrootdir;
char *distributions;
char *distribs, *distrib;
int retval;
size_t minibar_size = sizeof(struct bsddialog_fileminibar);
unsigned int nminibars;
struct bsddialog_fileminibar *dists;
struct bsddialog_progviewconf pvconf;
struct bsddialog_conf conf;
struct sigaction act;
char error[PATH_MAX + 512];
if ((distributions = getenv("DISTRIBUTIONS")) == NULL)
errx(EXIT_FAILURE, "DISTRIBUTIONS variable is not set");
if ((distdir = getenv("BSDINSTALL_DISTDIR")) == NULL)
distdir = "";
if ((distribs = strdup(distributions)) == NULL)
errx(EXIT_FAILURE, "memory error");
if (bsddialog_init() == BSDDIALOG_ERROR)
errx(EXIT_FAILURE, "Error libbsdialog: %s",
bsddialog_geterror());
bsddialog_initconf(&conf);
bsddialog_backtitle(&conf, OSNAME " Installer");
bsddialog_infobox(&conf,
"Checking distribution archives.\nPlease wait...", 4, 35);
nminibars = 0;
dists = NULL;
while ((distrib = strsep(&distribs, "\t\n\v\f\r ")) != NULL) {
if (strlen(distrib) == 0)
continue;
dists = realloc(dists, (nminibars + 1) * minibar_size);
if (dists == NULL)
_errx(EXIT_FAILURE, "Out of memory!");
dists[nminibars].path = distrib;
dists[nminibars].label = strrchr(dists[nminibars].path, '/');
if (dists[nminibars].label == NULL)
dists[nminibars].label = dists[nminibars].path;
dists[nminibars].size = count_files(dists[nminibars].path);
if (dists[nminibars].size < 0) {
bsddialog_end();
return (EXIT_FAILURE);
}
dists[nminibars].status = BSDDIALOG_MG_PENDING;
dists[nminibars].read = 0;
nminibars += 1;
}
chrootdir = getenv("BSDINSTALL_CHROOT");
if (chrootdir != NULL && chdir(chrootdir) != 0) {
snprintf(error, sizeof(error),
"Could not change to directory %s: %s\n",
chrootdir, strerror(errno));
conf.title = "Error";
bsddialog_msgbox(&conf, error, 0, 0);
bsddialog_end();
return (EXIT_FAILURE);
}
act.sa_handler = sig_int;
sigaction(SIGINT, &act, 0);
conf.title = "Archive Extraction";
conf.auto_minwidth = 40;
pvconf.callback = extract_files;
pvconf.refresh = 1;
pvconf.fmtbottomstr = "%10lli files read @ %'9.1f files/sec.";
bsddialog_total_progview = 0;
bsddialog_interruptprogview = bsddialog_abortprogview = false;
retval = bsddialog_progressview(&conf,
"\nExtracting distribution files...\n", 0, 0,
&pvconf, nminibars, dists);
if (retval == BSDDIALOG_ERROR) {
fprintf(stderr, "progressview error: %s\n",
bsddialog_geterror());
}
bsddialog_end();
free(distribs);
free(dists);
return (retval);
}
static void
sig_int(int sig __unused)
{
bsddialog_interruptprogview = true;
}
static int
count_files(const char *file)
{
static FILE *manifest = NULL;
char *p;
int file_count;
int retval;
size_t span;
struct archive_entry *entry;
char line[512];
char path[PATH_MAX];
char errormsg[PATH_MAX + 512];
struct bsddialog_conf conf;
if (manifest == NULL) {
snprintf(path, sizeof(path), "%s/MANIFEST", distdir);
manifest = fopen(path, "r");
}
if (manifest != NULL) {
rewind(manifest);
while (fgets(line, sizeof(line), manifest) != NULL) {
p = &line[0];
span = strcspn(p, "\t") ;
if (span < 1 || strncmp(p, file, span) != 0)
continue;
span = strcspn(p += span + (*p != '\0' ? 1 : 0), "\t");
span = strcspn(p += span + (*p != '\0' ? 1 : 0), "\t");
if (span > 0) {
file_count = (int)strtol(p, (char **)NULL, 10);
if (file_count == 0 && errno == EINVAL)
continue;
return (file_count);
}
}
}
bsddialog_initconf(&conf);
if ((archive = archive_read_new()) == NULL) {
snprintf(errormsg, sizeof(errormsg),
"Error: %s\n", archive_error_string(NULL));
conf.title = "Extract Error";
bsddialog_msgbox(&conf, errormsg, 0, 0);
return (-1);
}
archive_read_support_format_all(archive);
archive_read_support_filter_all(archive);
snprintf(path, sizeof(path), "%s/%s", distdir, file);
retval = archive_read_open_filename(archive, path, 4096);
if (retval != ARCHIVE_OK) {
snprintf(errormsg, sizeof(errormsg),
"Error while extracting %s: %s\n", file,
archive_error_string(archive));
conf.title = "Extract Error";
bsddialog_msgbox(&conf, errormsg, 0, 0);
archive = NULL;
return (-1);
}
file_count = 0;
while (archive_read_next_header(archive, &entry) == ARCHIVE_OK)
file_count++;
archive_read_free(archive);
archive = NULL;
return (file_count);
}
static int
extract_files(struct bsddialog_fileminibar *file)
{
int retval;
struct archive_entry *entry;
char path[PATH_MAX];
char errormsg[PATH_MAX + 512];
struct bsddialog_conf conf;
bsddialog_initconf(&conf);
if (archive == NULL) {
if ((archive = archive_read_new()) == NULL) {
snprintf(errormsg, sizeof(errormsg),
"Error: %s\n", archive_error_string(NULL));
conf.title = "Extract Error";
bsddialog_msgbox(&conf, errormsg, 0, 0);
bsddialog_abortprogview = true;
return (-1);
}
archive_read_support_format_all(archive);
archive_read_support_filter_all(archive);
snprintf(path, sizeof(path), "%s/%s", distdir, file->path);
retval = archive_read_open_filename(archive, path, 4096);
if (retval != 0) {
snprintf(errormsg, sizeof(errormsg),
"Error opening %s: %s\n", file->label,
archive_error_string(archive));
conf.title = "Extract Error";
bsddialog_msgbox(&conf, errormsg, 0, 0);
file->status = BSDDIALOG_MG_FAILED;
bsddialog_abortprogview = true;
return (-1);
}
}
retval = archive_read_next_header(archive, &entry);
if (retval == ARCHIVE_OK)
retval = archive_read_extract(archive, entry,
ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_OWNER |
ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL |
ARCHIVE_EXTRACT_XATTR | ARCHIVE_EXTRACT_FFLAGS);
if (retval == ARCHIVE_EOF) {
archive_read_free(archive);
archive = NULL;
file->status = BSDDIALOG_MG_DONE; ;
return (100);
} else if (retval != ARCHIVE_OK &&
!(retval == ARCHIVE_WARN &&
strcmp(archive_error_string(archive), "Can't restore time") == 0)) {
snprintf(errormsg, sizeof(errormsg),
"Error while extracting %s: %s\n", file->label,
archive_error_string(archive));
conf.title = "Extract Error";
bsddialog_msgbox(&conf, errormsg, 0, 0);
file->status = BSDDIALOG_MG_FAILED;
bsddialog_abortprogview = true;
return (-1);
}
bsddialog_total_progview++;
file->read++;
if (file->size >= 0)
return (file->read * 100 / file->size);
else
return (-1);
}