diff options
Diffstat (limited to 'pkgtools/pkg_install/files/add/perform.c')
-rw-r--r-- | pkgtools/pkg_install/files/add/perform.c | 1897 |
1 files changed, 1036 insertions, 861 deletions
diff --git a/pkgtools/pkg_install/files/add/perform.c b/pkgtools/pkg_install/files/add/perform.c index 66d34420fdc..fa72d03576e 100644 --- a/pkgtools/pkg_install/files/add/perform.c +++ b/pkgtools/pkg_install/files/add/perform.c @@ -1,5 +1,4 @@ -/* $NetBSD: perform.c,v 1.70 2008/02/08 00:58:17 joerg Exp $ */ - +/* $NetBSD: perform.c,v 1.71 2008/04/26 14:56:34 joerg Exp $ */ #if HAVE_CONFIG_H #include "config.h" #endif @@ -7,1047 +6,1223 @@ #if HAVE_SYS_CDEFS_H #include <sys/cdefs.h> #endif -#if HAVE_SYS_QUEUE_H -#include <sys/queue.h> -#endif -#ifndef lint -#if 0 -static const char *rcsid = "from FreeBSD Id: perform.c,v 1.44 1997/10/13 15:03:46 jkh Exp"; -#else -__RCSID("$NetBSD: perform.c,v 1.70 2008/02/08 00:58:17 joerg Exp $"); -#endif -#endif - -/* - * FreeBSD install - a package for the installation and maintainance - * of non-core utilities. +__RCSID("$NetBSD: perform.c,v 1.71 2008/04/26 14:56:34 joerg Exp $"); + +/*- + * Copyright (c) 2003 Grant Beattie <grant@NetBSD.org> + * Copyright (c) 2005 Dieter Baron <dillo@NetBSD.org> + * Copyright (c) 2007 Roland Illig <rillig@NetBSD.org> + * Copyright (c) 2008 Joerg Sonnenberger <joerg@NetBSD.org> + * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: + * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Jordan K. Hubbard - * 18 July 1993 - * - * This is the main body of the add module. + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ -#if HAVE_ASSERT_H -#include <assert.h> -#endif -#if HAVE_ERR_H +#include <sys/utsname.h> +#include <archive.h> +#include <archive_entry.h> #include <err.h> -#endif -#if HAVE_ERRNO_H #include <errno.h> -#endif -#include "defs.h" +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + #include "lib.h" #include "add.h" -#include "verify.h" -#if HAVE_INTTYPES_H -#include <inttypes.h> -#endif -#if HAVE_SIGNAL_H -#include <signal.h> -#endif -#if HAVE_STRING_H -#include <string.h> -#endif -#if HAVE_STDLIB_H -#include <stdlib.h> -#endif -#if HAVE_SYS_UTSNAME_H -#include <sys/utsname.h> -#endif +struct pkg_meta { + char *meta_contents; + char *meta_comment; + char *meta_desc; + char *meta_mtree; + char *meta_build_version; + char *meta_build_info; + char *meta_size_pkg; + char *meta_size_all; + char *meta_required_by; + char *meta_display; + char *meta_install; + char *meta_deinstall; + char *meta_preserve; + char *meta_views; + char *meta_installed_info; +}; -static char LogDir[MaxPathSize]; -static int zapLogDir; /* Should we delete LogDir? */ +struct pkg_task { + const char *pkgname; -static package_t Plist; -static char *Home; + const char *prefix; + const char *install_prefix; -static lfile_head_t files; + char *logdir; + char *other_version; -/* used in build information */ -enum { - Good, - Missing, - Warning, - Fatal + package_t plist; + + struct pkg_meta meta_data; + + struct archive *archive; + struct archive_entry *entry; + + char *buildinfo[BI_ENUM_COUNT]; + + size_t dep_length, dep_allocated; + char **dependencies; }; -static void -normalise_platform(struct utsname *host_name) -{ -#ifdef NUMERIC_VERSION_ONLY - size_t span; +static const struct pkg_meta_desc { + size_t entry_offset; + const char *entry_filename; + int required_file; + mode_t perm; +} pkg_meta_descriptors[] = { + { offsetof(struct pkg_meta, meta_contents), CONTENTS_FNAME, 1, 0644 }, + { offsetof(struct pkg_meta, meta_comment), COMMENT_FNAME, 1, 0444}, + { offsetof(struct pkg_meta, meta_desc), DESC_FNAME, 1, 0444}, + { offsetof(struct pkg_meta, meta_install), INSTALL_FNAME, 0, 0555 }, + { offsetof(struct pkg_meta, meta_deinstall), DEINSTALL_FNAME, 0, 0555 }, + { offsetof(struct pkg_meta, meta_display), DISPLAY_FNAME, 0, 0444 }, + { offsetof(struct pkg_meta, meta_mtree), MTREE_FNAME, 0, 0444 }, + { offsetof(struct pkg_meta, meta_build_version), BUILD_VERSION_FNAME, 0, 0444 }, + { offsetof(struct pkg_meta, meta_build_info), BUILD_INFO_FNAME, 0, 0444 }, + { offsetof(struct pkg_meta, meta_size_pkg), SIZE_PKG_FNAME, 0, 0444 }, + { offsetof(struct pkg_meta, meta_size_all), SIZE_ALL_FNAME, 0, 0444 }, + { offsetof(struct pkg_meta, meta_preserve), PRESERVE_FNAME, 0, 0444 }, + { offsetof(struct pkg_meta, meta_views), VIEWS_FNAME, 0, 0444 }, + { offsetof(struct pkg_meta, meta_required_by), REQUIRED_BY_FNAME, 0, 0644 }, + { offsetof(struct pkg_meta, meta_installed_info), INSTALLED_INFO_FNAME, 0, 0644 }, + { 0, NULL, 0 }, +}; - span = strspn(host_name->release, "0123456789."); - host_name->release[span] = '\0'; -#endif +static int pkg_do(const char *, int); + +static int +mkdir_p(const char *path) +{ + return fexec(MKDIR_CMD, "-p", path, (void *)NULL); } -/* Read package build information */ +/* + * Read meta data from archive. + * Bail out if a required entry is missing or entries are in the wrong order. + */ static int -read_buildinfo(char **buildinfo) +read_meta_data(struct pkg_task *pkg) { - char *key; - char *line; - size_t len; - FILE *fp; + const struct pkg_meta_desc *descr, *last_descr; + const char *fname; + char **target; + int64_t size; + int r, found_required; + + found_required = 0; + + last_descr = 0; + while ((r = archive_read_next_header(pkg->archive, &pkg->entry)) == + ARCHIVE_OK) { + fname = archive_entry_pathname(pkg->entry); + + for (descr = pkg_meta_descriptors; descr->entry_filename; + ++descr) { + if (strcmp(descr->entry_filename, fname) == 0) + break; + } + if (descr->entry_filename == NULL) + break; - if ((fp = fopen(BUILD_INFO_FNAME, "r")) == NULL) { - warnx("unable to open %s file.", BUILD_INFO_FNAME); - return 0; - } + if (descr->required_file) + ++found_required; - while ((line = fgetln(fp, &len)) != NULL) { - if (line[len - 1] == '\n') - line[len - 1] = '\0'; + target = (char **)((char *)&pkg->meta_data + + descr->entry_offset); + if (*target) { + warnx("duplicate entry, package corrupt"); + return -1; + } + if (descr < last_descr) { + warnx("misordered package"); + return -1; + } + last_descr = descr; - if ((key = strsep(&line, "=")) == NULL) - continue; + size = archive_entry_size(pkg->entry); + if (size > SSIZE_MAX - 1) { + warnx("package meta data too large to process"); + return -1; + } + if ((*target = malloc(size + 1)) == NULL) + err(2, "cannot allocate meta data"); + if (archive_read_data(pkg->archive, *target, size) != size) { + warn("cannot read package meta data"); + return -1; + } + (*target)[size] = '\0'; + } - /* - * pkgsrc used to create the BUILDINFO file using - * "key= value", so skip the space if it's there. - */ - if (line == NULL) - continue; - if (line[0] == ' ') - line += sizeof(char); + if (r != ARCHIVE_OK) + pkg->entry = NULL; - /* - * we only care about opsys, arch, version, and - * dependency recommendations - */ - if (line[0] != '\0') { - if (strcmp(key, "OPSYS") == 0) - buildinfo[BI_OPSYS] = strdup(line); - else if (strcmp(key, "OS_VERSION") == 0) - buildinfo[BI_OS_VERSION] = strdup(line); - else if (strcmp(key, "MACHINE_ARCH") == 0) - buildinfo[BI_MACHINE_ARCH] = strdup(line); - else if (strcmp(key, "IGNORE_RECOMMENDED") == 0) - buildinfo[BI_IGNORE_RECOMMENDED] = strdup(line); - else if (strcmp(key, "USE_ABI_DEPENDS") == 0) - buildinfo[BI_USE_ABI_DEPENDS] = strdup(line); - } + for (descr = pkg_meta_descriptors; descr->entry_filename; ++descr) { + if (descr->required_file) + --found_required; } - (void) fclose(fp); - if (buildinfo[BI_OPSYS] == NULL || - buildinfo[BI_OS_VERSION] == NULL || - buildinfo[BI_MACHINE_ARCH] == NULL) { - warnx("couldn't extract build information from package."); - return 0; + + return !found_required ? 0 : -1; +} + +/* + * Free meta data. + */ +static void +free_meta_data(struct pkg_task *pkg) +{ + const struct pkg_meta_desc *descr; + char **target; + + for (descr = pkg_meta_descriptors; descr->entry_filename; ++descr) { + target = (char **)((char *)&pkg->meta_data + + descr->entry_offset); + free(*target); + *target = NULL; } - return 1; } +/* + * Parse PLIST and populate pkg. + */ static int -sanity_check(const char *pkg) +pkg_parse_plist(struct pkg_task *pkg) { - int errc = 0; - - if (!fexists(CONTENTS_FNAME)) { - warnx("package %s has no CONTENTS file!", pkg); - errc = 1; - } else if (!fexists(COMMENT_FNAME)) { - warnx("package %s has no COMMENT file!", pkg); - errc = 1; - } else if (!fexists(DESC_FNAME)) { - warnx("package %s has no DESC file!", pkg); - errc = 1; + plist_t *p; + + parse_plist(&pkg->plist, pkg->meta_data.meta_contents); + if ((p = find_plist(&pkg->plist, PLIST_NAME)) == NULL) { + warnx("Invalid PLIST: missing @name"); + return -1; + } + pkg->pkgname = p->name; + if ((p = find_plist(&pkg->plist, PLIST_CWD)) == NULL) { + warnx("Invalid PLIST: missing @cwd"); + return -1; } - return errc; + /* XXX change first @cwd in PLIST? */ + pkg->prefix = p->name; + pkg->install_prefix = Prefix != NULL ? Prefix : pkg->prefix; + + return 0; +} + +/* + * Helper function to extract value from a string of the + * form key=value ending at eol. + */ +static char * +dup_value(const char *line, const char *eol) +{ + const char *key; + char *val; + + key = strchr(line, '='); + val = malloc(eol - key); + if (val == NULL) + err(2, "malloc failed"); + memcpy(val, key + 1, eol - key - 1); + val[eol - key - 1] = '\0'; + return val; } -/* install a pre-requisite package. Returns 1 if it installed it */ static int -installprereq(const char *name, int *errc, int doupdate) +check_already_installed(struct pkg_task *pkg) { - int ret; - ret = 0; + char *filename; + int fd; - if (Verbose) - printf("Loading it from %s.\n", name); - path_setenv("PKG_PATH"); - - if (fexec_skipempty(BINDIR "/pkg_add", "-K", _pkgdb_getPKGDB_DIR(), - "-s", get_verification(), - doupdate > 1 ? "-uu" : (doupdate ? "-u" : ""), - Fake ? "-n" : "", - NoView ? "-L" : "", - View ? "-w" : "", View ? View : "", - Viewbase ? "-W" : "", Viewbase ? Viewbase : "", - Force ? "-f" : "", - Prefix ? "-p" : "", Prefix ? Prefix : "", - Verbose ? "-v" : "", - OverrideMachine ? "-m" : "", - OverrideMachine ? OverrideMachine : "", - NoInstall ? "-I" : "", - "-A", name, NULL)) { - warnx("autoload of dependency `%s' failed%s", - name, Force ? " (proceeding anyway)" : "!"); - if (!Force) - ++(*errc); + if (Force) + return -1; + + filename = pkgdb_pkg_file(pkg->pkgname, CONTENTS_FNAME); + fd = open(filename, O_RDONLY); + free(filename); + if (fd == -1) + return -1; + + /* We can only arrive here for explicitly requested packages. */ + if (!Automatic && is_automatic_installed(pkg->pkgname)) { + if (Fake || + mark_as_automatic_installed(pkg->pkgname, 0) == 0) + warnx("package `%s' was already installed as " + "dependency, now marked as installed " + "manually", pkg->pkgname); } else { - ret = 1; + warnx("package `%s' already recorded as installed", + pkg->pkgname); } + return 0; - return ret; } static int -pkg_do_installed(int *replacing, char replace_via[MaxPathSize], char replace_to[MaxPathSize], - Boolean is_depoted_pkg, const char *dbdir) +check_other_installed(struct pkg_task *pkg) { - char replace_from[MaxPathSize]; - char *s; - char buf[MaxPathSize]; - char *best_installed; - - const size_t replace_via_size = MaxPathSize; - const size_t replace_to_size = MaxPathSize; + FILE *f, *f_pkg; + size_t len; + char *pkgbase, *iter, *filename; + package_t plist; + plist_t *p; + int status; - if ((s = strrchr(PkgName, '-')) == NULL) { - warnx("Package name %s does not contain a version, bailing out", PkgName); + if ((pkgbase = strdup(pkg->pkgname)) == NULL) { + warnx("strdup failed"); return -1; } - - /* - * See if the pkg is already installed. If so, we might want to - * upgrade/replace it. Otherwise, just return and let pkg_do work. - */ - (void) snprintf(buf, sizeof(buf), "%.*s[0-9]*", - (int)(s - PkgName) + 1, PkgName); - best_installed = find_best_matching_installed_pkg(buf); - if (best_installed == NULL) + if ((iter = strrchr(pkgbase, '-')) == NULL) { + free(pkgbase); + warnx("Invalid package name %s", pkg->pkgname); + return -1; + } + *iter = '\0'; + pkg->other_version = find_best_matching_installed_pkg(pkgbase); + free(pkgbase); + if (pkg->other_version == NULL) return 0; - if (!Replace || Fake) { - if (is_depoted_pkg) { - free(best_installed); - return 0; - } else { - warnx("other version '%s' already installed", best_installed); - free(best_installed); - return 1; /* close enough for government work */ - } + if (!Replace) { + /* XXX This is redundant to the implicit conflict check. */ + warnx("A different version of %s is already installed: %s", + pkg->pkgname, pkg->other_version); + return -1; } - /* XXX Should list the steps in Fake mode */ - snprintf(replace_from, sizeof(replace_from), "%s/%s/" REQUIRED_BY_FNAME, - dbdir, best_installed); - snprintf(replace_via, replace_via_size, "%s/.%s." REQUIRED_BY_FNAME, - dbdir, best_installed); - snprintf(replace_to, replace_to_size, "%s/%s/" REQUIRED_BY_FNAME, - dbdir, PkgName); - - if (Verbose) - printf("Upgrading %s to %s.\n", best_installed, PkgName); + filename = pkgdb_pkg_file(pkg->other_version, REQUIRED_BY_FNAME); + errno = 0; + f = fopen(filename, "r"); + free(filename); + if (f == NULL) { + if (errno == ENOENT) { + /* No packages depend on this, so everything is well. */ + return 0; + } + warnx("Can't open +REQUIRED_BY of %s", pkg->other_version); + return -1; + } - if (fexists(replace_from)) { /* Are there any dependencies? */ - /* - * Upgrade step 1/4: Check if the new version is ok with all pkgs - * (from +REQUIRED_BY) that require this pkg - */ - FILE *rb; /* +REQUIRED_BY file */ - char pkg2chk[MaxPathSize]; - - rb = fopen(replace_from, "r"); - if (! rb) { - warnx("Cannot open '%s' for reading%s", replace_from, - Force ? " (proceeding anyways)" : ""); - if (Force) - goto ignore_replace_depends_check; - else - return -1; + status = 0; + + while ((iter = fgetln(f, &len)) != NULL) { + if (iter[len - 1] == '\n') + iter[len - 1] = '\0'; + filename = pkgdb_pkg_file(iter, CONTENTS_FNAME); + if ((f_pkg = fopen(filename, "r")) == NULL) { + warnx("Can't open +CONTENTS of depending package %s", + iter); + fclose(f); + return -1; } - while (fgets(pkg2chk, sizeof(pkg2chk), rb)) { - package_t depPlist; - FILE *depf; - plist_t *depp; - char depC[MaxPathSize]; - - depPlist.head = depPlist.tail = NULL; - - s = strrchr(pkg2chk, '\n'); - if (s) - *s = '\0'; /* strip trailing '\n' */ - - /* - * step into pkg2chk, read it's +CONTENTS file and see if - * all @pkgdep lines agree with PkgName (using pkg_match()) + plist.head = plist.tail = NULL; + read_plist(&plist, f_pkg); + fclose(f_pkg); + for (p = plist.head; p != NULL; p = p->next) { + if (p->type == PLIST_IGNORE) { + p = p->next; + continue; + } else if (p->type != PLIST_PKGDEP) + continue; + /* + * XXX This is stricter than necessary. + * XXX One pattern might be fulfilled by + * XXX a different package and still need this + * XXX one for a different pattern. */ - snprintf(depC, sizeof(depC), "%s/%s/%s", dbdir, pkg2chk, CONTENTS_FNAME); - depf = fopen(depC , "r"); - if (depf == NULL) { - warnx("Cannot check depends in '%s'%s", depC, - Force ? " (proceeding anyways)" : "!" ); - if (Force) - goto ignore_replace_depends_check; - else - return -1; - } - read_plist(&depPlist, depf); - fclose(depf); - - for (depp = depPlist.head; depp; depp = depp->next) { - char base_new[MaxPathSize]; - char base_exist[MaxPathSize]; - char *s2; - - if (depp->type != PLIST_PKGDEP) - continue; - - /* - * Prepare basename (no versions) of both pkgs, - * to see if we want to compare against that - * one at all. - */ - strlcpy(base_new, PkgName, sizeof(base_new)); - s2 = strpbrk(base_new, "<>[]?*{"); /* } */ - if (s2) - *s2 = '\0'; - else { - s2 = strrchr(base_new, '-'); - if (s2) - *s2 = '\0'; - } - strlcpy(base_exist, depp->name, sizeof(base_exist)); - s2 = strpbrk(base_exist, "<>[]?*{"); /* } */ - if (s2) - *s2 = '\0'; - else { - s2 = strrchr(base_exist, '-'); - if (s2) - *s2 = '\0'; - } - if (strcmp(base_new, base_exist) == 0) { - /* Same pkg, so do the interesting compare */ - if (pkg_match(depp->name, PkgName)) { - if (Verbose) - printf("@pkgdep check: %s is ok for %s (in %s pkg)\n", - PkgName, depp->name, pkg2chk); - } else { - printf("Package %s requires %s, \n\tCannot replace with %s%s\n", - pkg2chk, depp->name, PkgName, - Force? " (proceeding anyways)" : "!"); - if (! Force) - return -1; - } - } - } + if (pkg_match(p->name, pkg->other_version) == 0) + continue; + if (pkg_match(p->name, pkg->pkgname) == 1) + continue; /* Both match, ok. */ + warnx("Dependency of %s fulfilled by %s, but not by %s", + iter, pkg->other_version, pkg->pkgname); + if (!Force) + status = -1; + break; } - fclose(rb); + free_plist(&plist); + } -ignore_replace_depends_check: - /* - * Upgrade step 2/4: Do the actual update by moving aside - * the +REQUIRED_BY file, deinstalling the old pkg, adding - * the new one and moving the +REQUIRED_BY file back - * into place (finished in step 3/4) - */ - if (Verbose) - printf("mv %s %s\n", replace_from, replace_via); - if (rename(replace_from, replace_via) != 0) - err(EXIT_FAILURE, "renaming \"%s\" to \"%s\" failed", replace_from, replace_via); + fclose(f); - *replacing = 1; - } + return status; +} - if (Verbose) { - printf("%s/pkg_delete -K %s '%s'\n", - BINDIR, dbdir, best_installed); +/* + * Read package build information from meta data. + */ +static int +read_buildinfo(struct pkg_task *pkg) +{ + const char *data, *eol, *next_line; + + data = pkg->meta_data.meta_build_info; + + for (; *data != '\0'; data = next_line) { + if ((eol = strchr(data, '\n')) == NULL) { + eol = data + strlen(data); + next_line = eol; + } else + next_line = eol + 1; + + if (strncmp(data, "OPSYS=", 6) == 0) + pkg->buildinfo[BI_OPSYS] = dup_value(data, eol); + else if (strncmp(data, "OS_VERSION=", 11) == 0) + pkg->buildinfo[BI_OS_VERSION] = dup_value(data, eol); + else if (strncmp(data, "MACHINE_ARCH=", 13) == 0) + pkg->buildinfo[BI_MACHINE_ARCH] = dup_value(data, eol); + else if (strncmp(data, "IGNORE_RECOMMENDED=", 19) == 0) + pkg->buildinfo[BI_IGNORE_RECOMMENDED] = dup_value(data, + eol); + else if (strncmp(data, "USE_ABI_DEPENDS=", 16) == 0) + pkg->buildinfo[BI_USE_ABI_DEPENDS] = dup_value(data, + eol); + } + if (pkg->buildinfo[BI_OPSYS] == NULL || + pkg->buildinfo[BI_OS_VERSION] == NULL || + pkg->buildinfo[BI_MACHINE_ARCH] == NULL) { + warnx("Not all required build information are present."); + return -1; } - fexec(BINDIR "/pkg_delete", "-K", dbdir, best_installed, NULL); - free(best_installed); + if ((pkg->buildinfo[BI_USE_ABI_DEPENDS] != NULL && + strcasecmp(pkg->buildinfo[BI_USE_ABI_DEPENDS], "YES") != 0) || + (pkg->buildinfo[BI_IGNORE_RECOMMENDED] != NULL && + strcasecmp(pkg->buildinfo[BI_IGNORE_RECOMMENDED], "NO") != 0)) { + warnx("%s was built to ignore ABI dependencies", pkg->pkgname); + } return 0; } /* - * Install a single package - * Returns 0 if everything is ok, >0 else + * Free buildinfo. */ -static int -pkg_do(const char *pkg, lpkg_head_t *pkgs) +static void +free_buildinfo(struct pkg_task *pkg) { - char playpen[MaxPathSize]; - char replace_via[MaxPathSize]; - char replace_to[MaxPathSize]; - char *buildinfo[BI_ENUM_COUNT]; - int replacing = 0; - char dbdir[MaxPathSize]; - const char *tmppkg; - FILE *cfile; - int errc, err_prescan; - plist_t *p; - struct stat sb; - struct utsname host_uname; - uint64_t needed; - Boolean is_depoted_pkg = FALSE; - lfile_t *lfp; - int result; - - errc = 0; - zapLogDir = 0; - LogDir[0] = '\0'; - strlcpy(playpen, FirstPen, sizeof(playpen)); - memset(buildinfo, '\0', sizeof(buildinfo)); - - umask(DEF_UMASK); - - tmppkg = fileFindByPath(pkg); - if (tmppkg == NULL) { - warnx("no pkg found for '%s', sorry.", pkg); - return 1; + size_t i; + + for (i = 0; i < BI_ENUM_COUNT; ++i) { + free(pkg->buildinfo[i]); + pkg->buildinfo[i] = NULL; } +} - pkg = tmppkg; +/* + * Write meta data files to pkgdb after creating the directory. + */ +static int +write_meta_data(struct pkg_task *pkg) +{ + const struct pkg_meta_desc *descr; + char *filename, **target; + size_t len; + ssize_t ret; + int fd; - if (IS_URL(pkg)) { - Home = fileGetURL(pkg); - if (Home == NULL) { - warnx("unable to fetch `%s' by URL", pkg); - } + if (Fake) + return 0; + + if (mkdir_p(pkg->logdir)) { + warn("Can't create pkgdb entry: %s", pkg->logdir); + return -1; + } - /* make sure the pkg is verified */ - if (!verify(pkg)) { - warnx("Package %s will not be extracted", pkg); - goto bomb; + for (descr = pkg_meta_descriptors; descr->entry_filename; ++descr) { + target = (char **)((char *)&pkg->meta_data + + descr->entry_offset); + if (*target == NULL) + continue; + if (asprintf(&filename, "%s/%s", pkg->logdir, + descr->entry_filename) == -1) { + warn("asprintf failed"); + return -1; } - } else { /* local */ - if (!IS_STDIN(pkg)) { - /* not stdin */ - if (!ispkgpattern(pkg)) { - if (stat(pkg, &sb) == FAIL) { - warnx("can't stat package file '%s'", pkg); - goto bomb; - } - /* make sure the pkg is verified */ - if (!verify(pkg)) { - warnx("Package %s will not be extracted", pkg); - goto bomb; - } + (void)unlink(filename); + fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, descr->perm); + if (fd == -1) { + warn("Can't open meta data file: %s", filename); + return -1; + } + len = strlen(*target); + do { + ret = write(fd, *target, len); + if (ret == -1) { + warn("Can't write meta data file: %s", + filename); + free(filename); + return -1; } - LFILE_ADD(&files, lfp, CONTENTS_FNAME); - } else { - /* some values for stdin */ - sb.st_size = 100000; /* Make up a plausible average size */ + len -= ret; + } while (ret > 0); + if (close(fd) == -1) { + warn("Can't close meta data file: %s", filename); + free(filename); + return -1; } - Home = make_playpen(playpen, sizeof(playpen), sb.st_size * 4); - if (!Home) - warnx("unable to make playpen for %ld bytes", - (long) (sb.st_size * 4)); - result = unpack(pkg, &files); - while ((lfp = TAILQ_FIRST(&files)) != NULL) { - TAILQ_REMOVE(&files, lfp, lf_link); - free(lfp); + free(filename); + } + + return 0; +} + +/* + * Helper function for extract_files. + */ +static int +copy_data_to_disk(struct archive *reader, struct archive *writer, + const char *filename) +{ + int r; + const void *buff; + size_t size; + off_t offset; + + for (;;) { + r = archive_read_data_block(reader, &buff, &size, &offset); + if (r == ARCHIVE_EOF) + return 0; + if (r != ARCHIVE_OK) { + warnx("Read error for %s: %s", filename, + archive_error_string(reader)); + return -1; } - if (result) { - warnx("unable to extract table of contents file from `%s' - not a package?", - pkg); - goto bomb; + r = archive_write_data_block(writer, buff, size, offset); + if (r != ARCHIVE_OK) { + warnx("Write error for %s: %s", filename, + archive_error_string(writer)); + return -1; } } +} - cfile = fopen(CONTENTS_FNAME, "r"); - if (!cfile) { - warnx("unable to open table of contents file `%s' - not a package?", - CONTENTS_FNAME); - goto bomb; - } - read_plist(&Plist, cfile); - fclose(cfile); +/* + * Extract package. + * Any misordered, missing or unlisted file in the package is an error. + */ - if (!IS_URL(pkg)) { - /* - * Apply a crude heuristic to see how much space the package will - * take up once it's unpacked. I've noticed that most packages - * compress an average of 75%, so multiply by 4 for good measure. - */ +static const int extract_flags = /* ARCHIVE_EXTRACT_OWNER | */ + ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_UNLINK | + ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_XATTR; - needed = 4 * (uint64_t) sb.st_size; - if (min_free(playpen) < needed) { - warnx("projected size of %" MY_PRIu64 " bytes exceeds available free space\n" - "in %s. Please set your PKG_TMPDIR variable to point\n" - "to a location with more free space and try again.", - needed, playpen); - goto bomb; - } +static int +extract_files(struct pkg_task *pkg) +{ + char cmd[MaxPathSize]; + const char *owner, *group, *permissions; + struct archive *writer; + int r; + plist_t *p; + const char *last_file; + char *fullpath; - /* Finally unpack the whole mess */ - if (unpack(pkg, NULL)) { - warnx("unable to extract `%s'!", pkg); - goto bomb; - } + if (Fake) + return 0; + + if (mkdir_p(pkg->install_prefix)) { + warn("Can't create prefix: %s", pkg->install_prefix); + return -1; } - /* Check for sanity */ - if (sanity_check(pkg)) - goto bomb; + if (chdir(pkg->install_prefix) == -1) { + warn("Can't change into prefix: %s", pkg->install_prefix); + return -1; + } - /* Read the OS, version and architecture from BUILD_INFO file */ - if (!read_buildinfo(buildinfo)) { - warn("can't read build information from %s", BUILD_INFO_FNAME); - if (!Force) { - warnx("aborting."); - goto bomb; - } + if (!NoRecord && !pkgdb_open(ReadWrite)) { + warn("Can't open pkgdb for writing"); + return -1; } - if (uname(&host_uname) < 0) { - warnx("uname() failed."); - if (!Force) { - warnx("aborting."); - goto bomb; - } - } else { - int status = Good; + writer = archive_write_disk_new(); + archive_write_disk_set_options(writer, extract_flags); + archive_write_disk_set_standard_lookup(writer); + + owner = NULL; + group = NULL; + permissions = NULL; + last_file = NULL; + + r = -1; + + for (p = pkg->plist.head; p != NULL; p = p->next) { + switch (p->type) { + case PLIST_FILE: + last_file = p->name; + if (pkg->entry == NULL) { + warnx("PLIST entry not in package (%s)", + archive_entry_pathname(pkg->entry)); + goto out; + } + if (strcmp(p->name, archive_entry_pathname(pkg->entry))) { + warnx("PLIST entry and package don't match (%s vs %s)", + p->name, archive_entry_pathname(pkg->entry)); + goto out; + } + if (asprintf(&fullpath, "%s/%s", pkg->install_prefix, p->name) == -1) { + warnx("asprintf failed"); + goto out; + } + pkgdb_store(fullpath, pkg->pkgname); + free(fullpath); + if (Verbose) + printf("%s", p->name); + break; - normalise_platform(&host_uname); + case PLIST_CMD: + if (format_cmd(cmd, sizeof(cmd), p->name, pkg->install_prefix, last_file)) + return -1; + printf("Executing '%s'\n", cmd); + if (!Fake && system(cmd)) + warnx("command '%s' failed", cmd); /* XXX bail out? */ + continue; - /* check that we have read some values from buildinfo */ - if (buildinfo[BI_OPSYS] == NULL) { - warnx("Missing operating system value from build information"); - status = Missing; - } - if (buildinfo[BI_MACHINE_ARCH] == NULL) { - warnx("Missing machine architecture value from build information"); - status = Missing; - } - if (buildinfo[BI_OS_VERSION] == NULL) { - warnx("Missing operating system version value from build information"); - status = Missing; + case PLIST_CHMOD: + permissions = p->name; + continue; + + case PLIST_CHOWN: + owner = p->name; + continue; + + case PLIST_CHGRP: + group = p->name; + continue; + + case PLIST_IGNORE: + p = p->next; + continue; + + default: + continue; } - if (status == Good) { - const char *effective_arch; - - if (OverrideMachine != NULL) - effective_arch = OverrideMachine; - else - effective_arch = MACHINE_ARCH; - - /* If either the OS or arch are different, bomb */ - if (strcmp(OPSYS_NAME, buildinfo[BI_OPSYS]) != 0) - status = Fatal; - if (strcmp(effective_arch, buildinfo[BI_MACHINE_ARCH]) != 0) - status = Fatal; - - /* If OS and arch are the same, warn if version differs */ - if (status == Good && - strcmp(host_uname.release, buildinfo[BI_OS_VERSION]) != 0) - status = Warning; - - if (status != Good) { - warnx("Warning: package `%s' was built for a different version of the OS:", pkg); - warnx("%s/%s %s (pkg) vs. %s/%s %s (this host)", - buildinfo[BI_OPSYS], - buildinfo[BI_MACHINE_ARCH], - buildinfo[BI_OS_VERSION], - OPSYS_NAME, - effective_arch, - host_uname.release); - } + r = archive_write_header(writer, pkg->entry); + if (r != ARCHIVE_OK) { + warnx("Failed to write %s: %s", + archive_entry_pathname(pkg->entry), + archive_error_string(writer)); + goto out; } - if (!Force && status == Fatal) { - warnx("aborting."); - goto bomb; + if (owner != NULL) + archive_entry_set_uname(pkg->entry, owner); + if (group != NULL) + archive_entry_set_uname(pkg->entry, group); + if (permissions != NULL) { + mode_t mode; + + mode = archive_entry_mode(pkg->entry); + mode = getmode(setmode(permissions), mode); + archive_entry_set_mode(pkg->entry, mode); } - } - /* Check if USE_ABI_DEPENDS or IGNORE_RECOMMENDED was set - * when this package was built. IGNORE_RECOMMENDED is historical. */ + r = copy_data_to_disk(pkg->archive, writer, + archive_entry_pathname(pkg->entry)); + if (r) + goto out; + if (Verbose) + printf("\n"); - if ((buildinfo[BI_USE_ABI_DEPENDS] != NULL && - strcasecmp(buildinfo[BI_USE_ABI_DEPENDS], "YES") != 0) || - (buildinfo[BI_IGNORE_RECOMMENDED] != NULL && - strcasecmp(buildinfo[BI_IGNORE_RECOMMENDED], "NO") != 0)) { - warnx("%s was built", pkg); - warnx("\tto ignore recommended ABI dependencies, this may cause problems!\n"); + r = archive_read_next_header(pkg->archive, &pkg->entry); + if (r == ARCHIVE_EOF) { + pkg->entry = NULL; + continue; + } + if (r != ARCHIVE_OK) { + warnx("Failed to read from archive: %s", + archive_error_string(pkg->archive)); + goto out; + } } - /* - * If we have a prefix, delete the first one we see and add this - * one in place of it. - */ - if (Prefix) { - delete_plist(&Plist, FALSE, PLIST_CWD, NULL); - add_plist_top(&Plist, PLIST_CWD, Prefix); + if (pkg->entry != NULL) { + warnx("Package contains entries not in PLIST: %s", + archive_entry_pathname(pkg->entry)); + goto out; } - /* Protect against old packages with bogus @name fields */ - p = find_plist(&Plist, PLIST_NAME); - if (p->name == NULL) { - warnx("PLIST contains no @name field"); - goto bomb; + r = 0; + +out: + if (!NoRecord) + pkgdb_close(); + archive_write_close(writer); + archive_write_finish(writer); + + return r; +} + +/* + * Register dependencies after sucessfully installing the package. + */ +static void +pkg_register_depends(struct pkg_task *pkg) +{ + int fd; + size_t text_len, i; + char *required_by, *text; + + if (Fake) + return; + + if (pkg->other_version != NULL) + return; /* XXX It's using the old dependencies. */ + + if (asprintf(&text, "%s\n", pkg->pkgname) == -1) + err(2, "asprintf failed"); + text_len = strlen(text); + + for (i = 0; i < pkg->dep_length; ++i) { + required_by = pkgdb_pkg_file(pkg->dependencies[i], REQUIRED_BY_FNAME); + + fd = open(required_by, O_WRONLY | O_APPEND | O_CREAT, 644); + if (fd == -1) + warn("can't open dependency file '%s'," + "registration is incomplete!", required_by); + else if (write(fd, text, text_len) != text_len) + warn("can't write to dependency file `%s'", required_by); + else if (close(fd) == -1) + warn("cannot close file %s", required_by); + + free(required_by); } - PkgName = p->name; - - if (fexists(VIEWS_FNAME)) - is_depoted_pkg = TRUE; - - /* - * Depoted packages' dbdir is the same as DEPOTBASE. Non-depoted - * packages' dbdir comes from the command-line or the environment. - */ - if (is_depoted_pkg) { - p = find_plist(&Plist, PLIST_CWD); - if (p == NULL) { - warn("no @cwd in +CONTENTS file?! aborting."); - goto bomb; + + free(text); +} + +/* + * Reduce the result from uname(3) to a canonical form. + */ +static void +normalise_platform(struct utsname *host_name) +{ +#ifdef NUMERIC_VERSION_ONLY + size_t span; + + span = strspn(host_name->release, "0123456789."); + host_name->release[span] = '\0'; +#endif +} + +/* + * Check build platform of the package against local host. + */ +static int +check_platform(struct pkg_task *pkg) +{ + struct utsname host_uname; + const char *effective_arch; + int fatal; + + if (uname(&host_uname) < 0) { + if (Force) { + warnx("uname() failed, continuing."); + return 0; + } else { + warnx("uname() failed, aborting."); + return -1; } - (void) strlcpy(dbdir, dirname_of(p->name), sizeof(dbdir)); - (void) strlcpy(LogDir, p->name, sizeof(LogDir)); - } else { - (void) strlcpy(dbdir, _pkgdb_getPKGDB_DIR(), sizeof(dbdir)); - (void) snprintf(LogDir, sizeof(LogDir), "%s/%s", dbdir, PkgName); } - /* Set environment variables expected by the +INSTALL script. */ - setenv(PKG_PREFIX_VNAME, (p = find_plist(&Plist, PLIST_CWD)) ? p->name : ".", 1); - setenv(PKG_METADATA_DIR_VNAME, LogDir, 1); + normalise_platform(&host_uname); + + if (OverrideMachine != NULL) + effective_arch = OverrideMachine; + else + effective_arch = MACHINE_ARCH; + + /* If either the OS or arch are different, bomb */ + if (strcmp(OPSYS_NAME, pkg->buildinfo[BI_OPSYS]) || + strcmp(effective_arch, pkg->buildinfo[BI_MACHINE_ARCH]) != 0) + fatal = 1; + else + fatal = 0; + + if (fatal || + strcmp(host_uname.release, pkg->buildinfo[BI_OS_VERSION]) != 0) { + warnx("Warning: package `%s' was built for a platform:", + pkg->pkgname); + warnx("%s/%s %s (pkg) vs. %s/%s %s (this host)", + pkg->buildinfo[BI_OPSYS], + pkg->buildinfo[BI_MACHINE_ARCH], + pkg->buildinfo[BI_OS_VERSION], + OPSYS_NAME, + effective_arch, + host_uname.release); + if (!Force && fatal) + return -1; + } + return 0; +} + +/* + * Run the install script. + */ +static int +run_install_script(struct pkg_task *pkg, const char *argument) +{ + int ret; + char *filename; + + if (pkg->meta_data.meta_install == NULL || NoInstall) + return 0; + + setenv(PKG_PREFIX_VNAME, pkg->install_prefix, 1); /* XXX or prefix? */ + setenv(PKG_METADATA_DIR_VNAME, pkg->logdir, 1); setenv(PKG_REFCOUNT_DBDIR_VNAME, pkgdb_refcount_dir(), 1); - - /* make sure dbdir actually exists! */ - if (!(isdir(dbdir) || islinktodir(dbdir))) { - if (fexec("mkdir", "-p", dbdir, NULL)) { - errx(EXIT_FAILURE, - "Database-dir %s cannot be generated, aborting.", - dbdir); - } + + filename = pkgdb_pkg_file(pkg->pkgname, INSTALL_FNAME); + if (Verbose) + printf("Running install with PRE-INSTALL for %s.\n", pkg->pkgname); + if (Fake) { + free(filename); + return 0; } - /* See if this package (exact version) is already registered */ - if (isdir(LogDir) && !Force) { - if (!Automatic && is_automatic_installed(PkgName)) { - if (mark_as_automatic_installed(PkgName, 0) == 0) - warnx("package `%s' was already installed as " - "dependency, now marked as installed " - "manually", PkgName); - } else { - warnx("package `%s' already recorded as installed", - PkgName); - } - goto success; /* close enough for government work */ + ret = 0; + + if (chdir(pkg->logdir) == -1) { + warn("Can't change to %s", pkg->logdir); + ret = -1; } - /* See if some other version of us is already installed */ - switch (pkg_do_installed(&replacing, replace_via, replace_to, is_depoted_pkg, dbdir)) { - case 0: - break; - case 1: - errc = 1; - goto success; - case -1: - goto bomb; + errno = 0; + if (ret == 0 && fexec(filename, pkg->pkgname, argument, (void *)NULL)) { + if (errno != 0) + warn("exec of install script failed"); + else + warnx("install script returned error status"); + ret = -1; } - /* See if there are conflicting packages installed */ - for (p = Plist.head; p; p = p->next) { - char *best_installed; + free(filename); + return ret; +} - if (p->type != PLIST_PKGCFL) +static int +check_explicit_conflict(struct pkg_task *pkg) +{ + char *installed, *installed_pattern; + plist_t *p; + int status; + + status = 0; + + for (p = pkg->plist.head; p != NULL; p = p->next) { + if (p->type == PLIST_IGNORE) { + p = p->next; continue; - if (Verbose) - printf("Package `%s' conflicts with `%s'.\n", PkgName, p->name); - best_installed = find_best_matching_installed_pkg(p->name); - if (best_installed) { + } else if (p->type != PLIST_PKGCFL) + continue; + installed = find_best_matching_installed_pkg(p->name); + if (installed) { warnx("Package `%s' conflicts with `%s', and `%s' is installed.", - PkgName, p->name, best_installed); - free(best_installed); - ++errc; + pkg->pkgname, p->name, installed); + free(installed); + status = -1; } } - /* See if any of the installed packages conflicts with this one. */ - { - char *inst_pkgname, *inst_pattern; + if (some_installed_package_conflicts_with(pkg->pkgname, + pkg->other_version, &installed, &installed_pattern)) { + warnx("Installed package `%s' conflicts with `%s' when trying to install `%s'.", + installed, installed_pattern, pkg->pkgname); + free(installed); + free(installed_pattern); + status = -1; + } - if (some_installed_package_conflicts_with(PkgName, &inst_pkgname, &inst_pattern)) { - warnx("Installed package `%s' conflicts with `%s' when trying to install `%s'.", - inst_pkgname, inst_pattern, PkgName); - free(inst_pkgname); - free(inst_pattern); - errc++; - } + return status; +} + +static int +check_implicit_conflict(struct pkg_task *pkg) +{ + plist_t *p; + char *fullpath, *existing; + int status; + + if (!pkgdb_open(ReadOnly)) { +#if notyet /* XXX empty pkgdb without database? */ + warn("Can't open pkgdb for reading"); + return -1; +#else + return 0; +#endif } - /* Quick pre-check if any conflicting dependencies are installed - * (e.g. version X is installed, but version Y is required) - */ - err_prescan=0; - for (p = Plist.head; p; p = p->next) { - char *best_installed; - - if (p->type != PLIST_PKGDEP) + status = 0; + + for (p = pkg->plist.head; p != NULL; p = p->next) { + if (p->type == PLIST_IGNORE) { + p = p->next; continue; - if (Verbose) - printf("Depends pre-scan: `%s' required.\n", p->name); - best_installed = find_best_matching_installed_pkg(p->name); - if (best_installed == NULL) { - /* - * required pkg not found. look if it's available with a more liberal - * pattern. If so, this will lead to problems later (check on "some - * other version of us is already installed" will fail, see above), - * and we better stop right now. - */ - char *s; - int skip = -1; - - /* doing this right required to parse the full version(s), - * do a 99% solution here for now */ - if (strchr(p->name, '{')) - continue; /* would remove trailing '}' else */ - - if ((s = strpbrk(p->name, "<>")) != NULL) { - skip = 0; - } else if (((s = strstr(p->name, "-[0-9]*")) != NULL) && - (*(s + sizeof("-[0-9]*") - 1) == '\0')) { - /* -[0-9]* already present so no need to */ - /* add it a second time */ - skip = -1; - } else if ((s = strrchr(p->name, '-')) != NULL) { - skip = 1; - } - - if (skip >= 0) { - char buf[MaxPathSize]; - - (void) snprintf(buf, sizeof(buf), - skip ? "%.*s[0-9]*" : "%.*s-[0-9]*", - (int)(s - p->name) + skip, p->name); - best_installed = find_best_matching_installed_pkg(buf); - if (best_installed) { - int done = 0; - - if (Replace > 1) - { - int errc0 = 0; - char tmp[MaxPathSize]; - - warnx("Attempting to update `%s' using binary package\n", p->name); - /* Yes, append .tgz after the version so the */ - /* pattern can match a filename. */ - snprintf(tmp, sizeof(tmp), "%s.tgz", p->name); - done = installprereq(tmp, &errc0, 2); - } - else if (Replace) - { - warnx("To perform necessary upgrades on required packages specify -u twice.\n"); - } - - if (!done) - { - warnx("pkg `%s' required, but `%s' found installed.", - p->name, best_installed); - if (Force) { - warnx("Proceeding anyway."); - } else { - err_prescan++; - } - } - free(best_installed); - } - } - } else { - free(best_installed); + } else if (p->type != PLIST_FILE) + continue; + + if (asprintf(&fullpath, "%s/%s", pkg->install_prefix, p->name) == -1) { + warnx("asprintf failed"); + status = -1; + break; + } + existing = pkgdb_retrieve(fullpath); + free(fullpath); + if (existing == NULL) + continue; + if (pkg->other_version != NULL && + strcmp(pkg->other_version, existing) == 0) + continue; + + warnx("Conflicting PLIST with %s: %s", existing, p->name); + if (!Force) { + status = -1; + if (!Verbose) + break; } } - if (err_prescan > 0) { - warnx("Please resolve this conflict!"); - errc += err_prescan; - goto success; /* close enough */ - } - - - /* Now check the packing list for dependencies */ - for (p = Plist.head; p; p = p->next) { - char *best_installed; - - if (p->type != PLIST_PKGDEP) + + pkgdb_close(); + return status; +} + +static int +check_dependencies(struct pkg_task *pkg) +{ + plist_t *p; + char *best_installed; + int status; + size_t i; + + status = 0; + + for (p = pkg->plist.head; p != NULL; p = p->next) { + if (p->type == PLIST_IGNORE) { + p = p->next; + continue; + } else if (p->type != PLIST_PKGDEP) continue; - if (Verbose) - printf("Package `%s' depends on `%s'.\n", PkgName, p->name); best_installed = find_best_matching_installed_pkg(p->name); if (best_installed == NULL) { - /* required pkg not found - need to pull in */ - - if (Fake) { - /* fake install (???) */ - if (Verbose) - printf("Package dependency %s for %s not installed%s\n", p->name, pkg, - Force ? " (proceeding anyway)" : "!"); - } else { - int done = 0; - int errc0 = 0; - - done = installprereq(p->name, &errc0, (Replace > 1) ? 2 : 0); - if (!done && !Force) { - errc += errc0; + /* XXX check cyclic dependencies? */ + if (Fake || NoRecord) { + if (!Force) { + warnx("Missing dependency %s\n", + p->name); + status = -1; + break; } + warnx("Missing dependency %s, continuing", + p->name); + continue; } - } else { - if (Verbose) - printf(" - %s already installed.\n", best_installed); + if (pkg_do(p->name, 1)) { + warnx("Can't install dependency %s", p->name); + status = -1; + break; + } + best_installed = find_best_matching_installed_pkg(p->name); + if (best_installed == NULL && Force) { + warnx("Missing dependency %s ignored", p->name); + continue; + } else if (best_installed == NULL) { + warnx("Just installed dependency %s disappeared", p->name); + status = -1; + break; + } + } + for (i = 0; i < pkg->dep_length; ++i) { + if (strcmp(best_installed, pkg->dependencies[i]) == 0) + break; + } + if (i < pkg->dep_length) { + /* Already used as dependency, so skip it. */ free(best_installed); + continue; + } + if (pkg->dep_length + 1 >= pkg->dep_allocated) { + char **tmp; + pkg->dep_allocated = 2 * pkg->dep_allocated + 1; + tmp = realloc(pkg->dependencies, + pkg->dep_allocated * sizeof(*tmp)); + if (tmp == NULL) { + warnx("realloc failed"); + free(pkg->dependencies); + pkg->dependencies = NULL; + pkg->dep_length = pkg->dep_allocated = 0; + free(best_installed); + return -1; + } + pkg->dependencies = tmp; } + pkg->dependencies[pkg->dep_length++] = best_installed; } - if (errc != 0) - goto bomb; + return status; +} - /* If we're really installing, and have an installation file, run it */ - if (!NoInstall && fexists(INSTALL_FNAME)) { - (void) fexec(CHMOD_CMD, "+x", INSTALL_FNAME, NULL); /* make sure */ - if (Verbose) - printf("Running install with PRE-INSTALL for %s.\n", PkgName); - errno = 0; - if (!Fake && fexec("./" INSTALL_FNAME, PkgName, "PRE-INSTALL", NULL)) { - if (errno != 0) - warn("exec of install script failed"); - else - warnx("install script returned error status"); - errc = 1; - goto success; /* nothing to uninstall yet */ - } +/* + * If this package uses pkg_views, register it in the default view. + */ +static void +pkg_register_views(struct pkg_task *pkg) +{ + if (Fake || NoView || pkg->meta_data.meta_views == NULL) + return; + + if (Verbose) { + printf("%s/pkg_view -d %s %s%s %s%s %sadd %s\n", + BINDIR, _pkgdb_getPKGDB_DIR(), + View ? "-w " : "", View ? View : "", + Viewbase ? "-W " : "", Viewbase ? Viewbase : "", + Verbose ? "-v " : "", pkg->pkgname); } - /* - * Now finally extract the entire show if we're not going direct. - * We need to reset the package dbdir so that extract_plist() - * updates the correct pkgdb.byfile.db database. - */ + fexec_skipempty(BINDIR "/pkg_view", "-d", _pkgdb_getPKGDB_DIR(), + View ? "-w " : "", View ? View : "", + Viewbase ? "-W " : "", Viewbase ? Viewbase : "", + Verbose ? "-v " : "", "add", pkg->pkgname, + (void *)NULL); +} + +static int +start_replacing(struct pkg_task *pkg) +{ + char *old_required_by, *new_required_by; + + old_required_by = pkgdb_pkg_file(pkg->other_version, + REQUIRED_BY_FNAME); + new_required_by = pkgdb_pkg_file(pkg->pkgname, + REQUIRED_BY_FNAME); + if (!Fake) { - _pkgdb_setPKGDB_DIR(dbdir); - if (!extract_plist(".", &Plist)) { - errc = 1; - goto fail; + if (rename(old_required_by, new_required_by) == -1 && + errno != ENOENT) { + warn("Can't move +REQUIRED_BY from %s to %s", + old_required_by, new_required_by); + return -1; } } - if (!Fake && fexists(MTREE_FNAME)) { - warnx("Mtree file ignored for package %s", PkgName); - } - - /* Run the installation script one last time? */ - if (!NoInstall && fexists(INSTALL_FNAME)) { - if (Verbose) - printf("Running install with POST-INSTALL for %s.\n", PkgName); - if (!Fake && fexec("./" INSTALL_FNAME, PkgName, "POST-INSTALL", NULL)) { - warnx("install script returned error status"); - errc = 1; - goto fail; - } + if (Verbose || Fake) { + printf("%s/pkg_delete -K %s -p %s '%s'\n", + BINDIR, _pkgdb_getPKGDB_DIR(), pkg->install_prefix, + pkg->other_version); } + if (!Fake) + fexec(BINDIR "/pkg_delete", "-K", _pkgdb_getPKGDB_DIR(), + "-p", pkg->install_prefix, + pkg->other_version, NULL); - /* Time to record the deed? */ - if (!NoRecord && !Fake) { - char contents[MaxPathSize]; + /* XXX Check return value and do what? */ + return 0; +} - if (!PkgName) { - warnx("no package name! can't record package, sorry"); - errc = 1; - goto success; /* well, partial anyway */ - } - (void) snprintf(LogDir, sizeof(LogDir), "%s/%s", dbdir, PkgName); - zapLogDir = 1; /* LogDir contains something valid now */ - if (Verbose) - printf("Attempting to record package into %s.\n", LogDir); - if (make_hierarchy(LogDir)) { - warnx("can't record package into '%s', you're on your own!", - LogDir); - memset(LogDir, 0, sizeof(LogDir)); - errc = 1; - goto success; /* close enough for government work */ - } - /* Make sure pkg_info can read the entry */ - (void) fexec(CHMOD_CMD, "a+rx", LogDir, NULL); - - /* Move all of the +-files into place */ - move_files(".", "+*", LogDir); - - /* Generate the +CONTENTS file in-place from the Plist */ - (void) snprintf(contents, sizeof(contents), "%s/%s", LogDir, CONTENTS_FNAME); - cfile = fopen(contents, "w"); - if (!cfile) { - warnx("can't open new contents file '%s'! can't register pkg", - contents); - goto success; /* can't log, but still keep pkg */ - } - write_plist(&Plist, cfile, NULL); - fclose(cfile); - - /* register dependencies */ - /* we could save some cycles here if we remembered what we - * installed above (in case we got a wildcard dependency) */ - /* XXX remembering in p->name would NOT be good! */ - for (p = Plist.head; p; p = p->next) { - if (p->type != PLIST_PKGDEP) - continue; - if (Verbose) - printf("Attempting to record dependency on package `%s'\n", p->name); - (void) snprintf(contents, sizeof(contents), "%s/%s", dbdir, - basename_of(p->name)); - if (ispkgpattern(p->name)) { - char *s; +/* + * Install a single package. + */ +static int +pkg_do(const char *pkgpath, int mark_automatic) +{ + int status; + void *archive_cookie; + struct pkg_task *pkg; - s = find_best_matching_installed_pkg(p->name); + if ((pkg = calloc(1, sizeof(*pkg))) == NULL) + err(2, "malloc failed"); - if (s == NULL) - errx(EXIT_FAILURE, "Where did our dependency go?!"); + status = -1; - (void) snprintf(contents, sizeof(contents), "%s/%s", dbdir, s); - free(s); - } - strlcat(contents, "/", sizeof(contents)); - strlcat(contents, REQUIRED_BY_FNAME, sizeof(contents)); - - cfile = fopen(contents, "a"); - if (!cfile) - warnx("can't open dependency file '%s'!\n" - "dependency registration is incomplete", contents); - else { - fprintf(cfile, "%s\n", PkgName); - if (fclose(cfile) == EOF) - warnx("cannot properly close file %s", contents); - } - } - if (Automatic) - mark_as_automatic_installed(PkgName, 1); - if (Verbose) - printf("Package %s registered in %s\n", PkgName, LogDir); + if ((pkg->archive = find_archive(pkgpath, &archive_cookie)) == NULL) { + warnx("no pkg found for '%s', sorry.", pkgpath); + goto clean_memory; } + if (read_meta_data(pkg)) + goto clean_memory; - if ((p = find_plist(&Plist, PLIST_DISPLAY)) != NULL) { - FILE *fp; - char buf[BUFSIZ]; - - (void) snprintf(buf, sizeof(buf), "%s/%s", LogDir, p->name); - fp = fopen(buf, "r"); - if (fp) { - putc('\n', stdout); - while (fgets(buf, sizeof(buf), fp)) - fputs(buf, stdout); - putc('\n', stdout); - (void) fclose(fp); - } else - warnx("cannot open %s as display file", buf); + /* Parse PLIST early, so that messages can use real package name. */ + if (pkg_parse_plist(pkg)) + goto clean_memory; + + if (pkg->meta_data.meta_mtree != NULL) + warnx("mtree specification in pkg `%s' ignored", pkg->pkgname); + + if (pkg->meta_data.meta_views != NULL) { + if ((pkg->logdir = strdup(pkg->install_prefix)) == NULL) + err(EXIT_FAILURE, "strdup failed"); + _pkgdb_setPKGDB_DIR(dirname_of(pkg->logdir)); + } else { + if (asprintf(&pkg->logdir, "%s/%s", _pkgdb_getPKGDB_DIR(), + pkg->pkgname) == -1) + err(EXIT_FAILURE, "asprintf failed"); } - /* Add the package to a default view. */ - if (!Fake && !NoView && is_depoted_pkg) { - if (Verbose) { - printf("%s/pkg_view -d %s %s%s %s%s %sadd %s\n", - BINDIR, dbdir, - View ? "-w " : "", View ? View : "", - Viewbase ? "-W " : "", Viewbase ? Viewbase : "", - Verbose ? "-v " : "", PkgName); + if (NoRecord && !Fake) { + const char *tmpdir; + + tmpdir = getenv("TMPDIR"); + if (tmpdir == NULL) + tmpdir = "/tmp"; + + free(pkg->logdir); + if (asprintf(&pkg->logdir, "%s/pkg_install.XXXXXX", tmpdir) == -1) + err(EXIT_FAILURE, "asprintf failed"); + /* XXX pkg_add -u... */ + if (mkdtemp(pkg->logdir) == NULL) { + warn("mkdtemp failed"); + goto clean_memory; } + } - fexec_skipempty(BINDIR "/pkg_view", "-d", dbdir, - View ? "-w " : "", View ? View : "", - Viewbase ? "-W " : "", Viewbase ? Viewbase : "", - Verbose ? "-v " : "", "add", PkgName, NULL); + if (check_already_installed(pkg) == 0) { + status = 0; + goto clean_memory; } - goto success; + if (read_buildinfo(pkg)) + goto clean_memory; -bomb: - errc = 1; - goto success; + if (check_platform(pkg)) + goto clean_memory; -fail: - /* Nuke the whole (installed) show, XXX but don't clean directories */ - if (!Fake) - delete_package(FALSE, FALSE, &Plist, FALSE); + if (check_other_installed(pkg)) + goto clean_memory; + + if (check_explicit_conflict(pkg)) + goto clean_memory; -success: - /* delete the packing list contents */ - free_plist(&Plist); - leave_playpen(Home); + if (check_implicit_conflict(pkg)) + goto clean_memory; - if (replacing) { + if (pkg->other_version != NULL) { /* - * Upgrade step 3/4: move back +REQUIRED_BY file - * (see also step 2/4) + * Replacing an existing package. + * Write meta-data, get rid of the old version, + * install/update dependencies and finally extract. */ - if (rename(replace_via, replace_to) != 0) - err(EXIT_FAILURE, "renaming \"%s\" to \"%s\" failed", replace_via, replace_to); - + if (write_meta_data(pkg)) + goto nuke_pkgdb; + + if (start_replacing(pkg)) + goto nuke_pkgdb; + + if (check_dependencies(pkg)) + goto nuke_pkgdb; + } else { /* - * Upgrade step 4/4: Fix pkgs that depend on us to - * depend on the new version instead of the old - * one by fixing @pkgdep lines in +CONTENTS files. - */ - /* TODO */ + * Normal installation. + * Install/update dependencies first and + * write the current package to disk afterwards. + */ + if (check_dependencies(pkg)) + goto clean_memory; + + if (write_meta_data(pkg)) + goto nuke_pkgdb; } - return errc; -} + if (run_install_script(pkg, "PRE-INSTALL")) + goto nuke_pkgdb; -void -cleanup(int signo) -{ - static int alreadyCleaning; - void (*oldint) (int); - void (*oldhup) (int); - int saved_errno; - - saved_errno = errno; - oldint = signal(SIGINT, SIG_IGN); - oldhup = signal(SIGHUP, SIG_IGN); - - if (!alreadyCleaning) { - alreadyCleaning = 1; - if (signo) - printf("Signal %d received, cleaning up.\n", signo); - if (!Fake && zapLogDir && LogDir[0]) - (void) fexec(REMOVE_CMD, "-fr", LogDir, NULL); - leave_playpen(Home); - if (signo) - exit(1); + if (extract_files(pkg)) + goto nuke_pkg; + + if (run_install_script(pkg, "POST-INSTALL")) + goto nuke_pkgdb; + + /* XXX keep +INSTALL_INFO for updates? */ + /* XXX keep +PRESERVE for updates? */ + if (mark_automatic) + mark_as_automatic_installed(pkg->pkgname, 1); + + pkg_register_depends(pkg); + + if (Verbose) + printf("Package %s registered in %s\n", pkg->pkgname, pkg->logdir); + + if (pkg->meta_data.meta_display != NULL) + fputs(pkg->meta_data.meta_display, stdout); + + pkg_register_views(pkg); + + status = 0; + goto clean_memory; + +nuke_pkg: + if (!Fake) { + if (pkg->other_version) { + warnx("Updating of %s to %s failed.", + pkg->other_version, pkg->pkgname); + warnx("Remember to run pkg_admin rebuild-tree after fixing this."); + } + delete_package(FALSE, FALSE, &pkg->plist, FALSE); + } + +nuke_pkgdb: + if (!Fake) { + (void) fexec(REMOVE_CMD, "-fr", pkg->logdir, (void *)NULL); + free(pkg->logdir); + pkg->logdir = NULL; + } + +clean_memory: + if (pkg->logdir != NULL && NoRecord && !Fake) + (void) fexec(REMOVE_CMD, "-fr", pkg->logdir, (void *)NULL); + free(pkg->logdir); + free_buildinfo(pkg); + free_plist(&pkg->plist); + free_meta_data(pkg); + if (pkg->archive) { + archive_read_close(pkg->archive); + close_archive(archive_cookie); } - signal(SIGINT, oldint); - signal(SIGHUP, oldhup); - errno = saved_errno; + free(pkg->other_version); + free(pkg); + return status; } int pkg_perform(lpkg_head_t *pkgs) { - int err_cnt = 0; + int errors = 0; lpkg_t *lpp; - signal(SIGINT, cleanup); - signal(SIGHUP, cleanup); - - TAILQ_INIT(&files); - while ((lpp = TAILQ_FIRST(pkgs)) != NULL) { path_prepend_from_pkgname(lpp->lp_name); - err_cnt += pkg_do(lpp->lp_name, pkgs); + if (pkg_do(lpp->lp_name, Automatic)) + ++errors; path_prepend_clear(); TAILQ_REMOVE(pkgs, lpp, lp_link); free_lpkg(lpp); } - - ftp_stop(); - - return err_cnt; + + return errors; } |