From ba8b7d38610a93992f56700fb6e51bd0bf6c32a7 Mon Sep 17 00:00:00 2001 From: joerg Date: Sat, 26 Apr 2008 14:56:33 +0000 Subject: Add a clean pkg_add implementation on top of libarchive and libfetch. Known regressions: - "pkg_add -" (aka reading from stdin) is currently not supported - "pkg_add -s" is not supported either - no progress reports for the downloads - binary packages with hardlinks created by pkg_create before pkg_install-20080422 will not extract correctly (libarchive issue) - no adhoc check for potential conflicts between dependencies and already installed packages Features: - Twice as fast for the typical case of /var/tmp and /usr/pkg on different filesystems - Standalone - implicit conflict detection before actual installation. --- pkgtools/pkg_install/files/add/Makefile.in | 8 +- pkgtools/pkg_install/files/add/add.h | 9 +- pkgtools/pkg_install/files/add/extract.c | 340 ----- pkgtools/pkg_install/files/add/futil.c | 151 --- pkgtools/pkg_install/files/add/main.c | 18 +- pkgtools/pkg_install/files/add/perform.c | 1897 +++++++++++++++------------ pkgtools/pkg_install/files/add/pkg_add.1 | 38 +- pkgtools/pkg_install/files/create/perform.c | 36 +- pkgtools/pkg_install/files/info/Makefile.in | 4 +- pkgtools/pkg_install/files/info/perform.c | 29 +- pkgtools/pkg_install/files/lib/Makefile.in | 8 +- pkgtools/pkg_install/files/lib/conflicts.c | 9 +- pkgtools/pkg_install/files/lib/file.c | 429 +----- pkgtools/pkg_install/files/lib/ftpio.c | 1259 ------------------ pkgtools/pkg_install/files/lib/lib.h | 90 +- pkgtools/pkg_install/files/lib/pen.c | 206 --- pkgtools/pkg_install/files/lib/pexec.c | 110 -- pkgtools/pkg_install/files/lib/pkg_io.c | 185 ++- pkgtools/pkg_install/files/lib/plist.c | 5 +- pkgtools/pkg_install/files/lib/str.c | 45 +- 20 files changed, 1289 insertions(+), 3587 deletions(-) delete mode 100644 pkgtools/pkg_install/files/add/extract.c delete mode 100644 pkgtools/pkg_install/files/add/futil.c delete mode 100644 pkgtools/pkg_install/files/lib/ftpio.c delete mode 100644 pkgtools/pkg_install/files/lib/pen.c delete mode 100644 pkgtools/pkg_install/files/lib/pexec.c (limited to 'pkgtools/pkg_install') diff --git a/pkgtools/pkg_install/files/add/Makefile.in b/pkgtools/pkg_install/files/add/Makefile.in index 07cff421baa..3cf26e92a48 100644 --- a/pkgtools/pkg_install/files/add/Makefile.in +++ b/pkgtools/pkg_install/files/add/Makefile.in @@ -1,4 +1,4 @@ -# $NetBSD: Makefile.in,v 1.16 2008/03/10 12:14:32 wiz Exp $ +# $NetBSD: Makefile.in,v 1.17 2008/04/26 14:56:33 joerg Exp $ srcdir= @srcdir@ @@ -13,9 +13,9 @@ cat1dir= $(mandir)/cat1 CC= @CC@ CCLD= $(CC) -LIBS= -linstall @LIBS@ +LIBS= -linstall -lfetch -larchive -lbz2 -lz @LIBS@ CPPFLAGS= @CPPFLAGS@ -I. -I$(srcdir) -I../lib -DEFS= @DEFS@ -DOPSYS_NAME=\"$(OPSYS)\" -DMACHINE_ARCH=\"$(MACHINE_ARCH)\" -DBINDIR=\"$(sbindir)\" -DTAR_CMD=\"@tar@\" -DPAX_CMD=\"@pax@\" +DEFS= @DEFS@ -DOPSYS_NAME=\"$(OPSYS)\" -DMACHINE_ARCH=\"$(MACHINE_ARCH)\" -DBINDIR=\"$(sbindir)\" CFLAGS= @CFLAGS@ LDFLAGS= @LDFLAGS@ -L../lib @@ -23,7 +23,7 @@ INSTALL= @INSTALL@ PROG= pkg_add -OBJS= main.o perform.o futil.o extract.o verify.o +OBJS= main.o perform.o verify.o all: $(PROG) diff --git a/pkgtools/pkg_install/files/add/add.h b/pkgtools/pkg_install/files/add/add.h index ca6ce6f3195..3f9002b5604 100644 --- a/pkgtools/pkg_install/files/add/add.h +++ b/pkgtools/pkg_install/files/add/add.h @@ -1,4 +1,4 @@ -/* $NetBSD: add.h,v 1.10 2007/09/29 13:24:32 rillig Exp $ */ +/* $NetBSD: add.h,v 1.11 2008/04/26 14:56:33 joerg Exp $ */ /* from FreeBSD Id: add.h,v 1.8 1997/02/22 16:09:15 peter Exp */ @@ -35,15 +35,8 @@ extern Boolean NoRecord; extern Boolean Force; extern Boolean Automatic; extern int Replace; -extern char *Mode; -extern char *Owner; -extern char *Group; -extern char *Directory; -extern char *PkgName; -extern char FirstPen[]; int make_hierarchy(char *); -int extract_plist(char *, package_t *); void apply_perms(char *, char **, int); int pkg_perform(lpkg_head_t *); diff --git a/pkgtools/pkg_install/files/add/extract.c b/pkgtools/pkg_install/files/add/extract.c deleted file mode 100644 index 9bfb3b678f6..00000000000 --- a/pkgtools/pkg_install/files/add/extract.c +++ /dev/null @@ -1,340 +0,0 @@ -/* $NetBSD: extract.c,v 1.16 2007/09/11 13:46:10 rillig Exp $ */ - -#if HAVE_CONFIG_H -#include "config.h" -#endif -#include -#if HAVE_SYS_CDEFS_H -#include -#endif -#if HAVE_SYS_QUEUE_H -#include -#endif -#ifndef lint -#if 0 -static const char *rcsid = "FreeBSD - Id: extract.c,v 1.17 1997/10/08 07:45:35 charnier Exp"; -#else -__RCSID("$NetBSD: extract.c,v 1.16 2007/09/11 13:46:10 rillig Exp $"); -#endif -#endif - -/* - * FreeBSD install - a package for the installation and maintainance - * of non-core utilities. - * - * 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 package extraction code for the add module. - * - */ - -#if HAVE_ERR_H -#include -#endif -#include "lib.h" -#include "add.h" - -lfile_head_t files; -lfile_head_t perms; - -/* - * Copy files from staging area to todir. - * This is only used when the files cannot be directory rename()ed. - */ -static void -pushout(char *todir) -{ - pipe_to_system_t *pipe_to; - char *file_args[4]; - char **perm_argv; - int perm_argc = 1; - lfile_t *lfp; - int count; - - /* set up arguments to run "pax -r -w -p e" */ - file_args[0] = (char *)strrchr(PAX_CMD, '/'); - if (file_args[0] == NULL) - file_args[0] = PAX_CMD; - else - file_args[0]++; - file_args[1] = "-rwpe"; - file_args[2] = todir; - file_args[3] = NULL; - - /* count entries for files */ - count = 0; - TAILQ_FOREACH(lfp, &files, lf_link) - count++; - - if (count > 0) { - /* open pipe, feed it files, close pipe */ - pipe_to = pipe_to_system_begin(PAX_CMD, file_args, NULL); - while ((lfp = TAILQ_FIRST(&files)) != NULL) { - fprintf(pipe_to->fp, "%s\n", lfp->lf_name); - TAILQ_REMOVE(&files, lfp, lf_link); - free(lfp); - } - pipe_to_system_end(pipe_to); - } - - /* count entries for permissions */ - count = 0; - TAILQ_FOREACH(lfp, &perms, lf_link) - count++; - - if (count > 0) { - perm_argv = malloc((count + 1) * sizeof(char *)); - perm_argc = 0; - TAILQ_FOREACH(lfp, &perms, lf_link) - perm_argv[perm_argc++] = lfp->lf_name; - perm_argv[perm_argc] = NULL; - apply_perms(todir, perm_argv, perm_argc); - - /* empty the perm list */ - while ((lfp = TAILQ_FIRST(&perms)) != NULL) { - TAILQ_REMOVE(&perms, lfp, lf_link); - free(lfp); - } - free(perm_argv); - } -} - -static void -rollback(char *name, char *home, plist_t *start, plist_t *stop) -{ - plist_t *q; - char try[MaxPathSize], bup[MaxPathSize], *dir; - - dir = home; - for (q = start; q != stop; q = q->next) { - if (q->type == PLIST_FILE) { - (void) snprintf(try, sizeof(try), "%s/%s", dir, q->name); - if (make_preserve_name(bup, sizeof(bup), name, try) && fexists(bup)) { -#if HAVE_CHFLAGS - (void) chflags(try, 0); -#endif - (void) unlink(try); - if (rename(bup, try)) - warnx("rollback: unable to rename %s back to %s", bup, try); - } - } else if (q->type == PLIST_CWD) { - if (strcmp(q->name, ".")) - dir = q->name; - else - dir = home; - } - } -} - - -/* - * Return 0 on error, 1 for success. - */ -int -extract_plist(char *home, package_t *pkg) -{ - plist_t *p = pkg->head; - char *last_file; - char *last_chdir; - Boolean preserve; - lfile_t *lfp; - - TAILQ_INIT(&files); - TAILQ_INIT(&perms); - - last_chdir = 0; - preserve = find_plist_option(pkg, "preserve") ? TRUE : FALSE; - - /* Reset the world */ - Owner = NULL; - Group = NULL; - Mode = NULL; - last_file = NULL; - Directory = home; - - if (!NoRecord) { - /* Open Package Database for writing */ - if (!pkgdb_open(ReadWrite)) { - cleanup(0); - err(EXIT_FAILURE, "can't open pkgdb"); - } - } - /* Do it */ - while (p) { - char cmd[MaxPathSize]; - - switch (p->type) { - case PLIST_NAME: - PkgName = p->name; - if (Verbose) - printf("extract: Package name is %s\n", p->name); - break; - - case PLIST_FILE: - last_file = p->name; - if (Verbose) - printf("extract: %s/%s\n", Directory, p->name); - if (!Fake) { - char try[MaxPathSize]; - - if (strrchr(p->name, '\'')) { - cleanup(0); - errx(2, "Bogus filename \"%s\"", p->name); - } - - /* first try to rename it into place */ - (void) snprintf(try, sizeof(try), "%s/%s", Directory, p->name); - if (fexists(try)) { -#if HAVE_CHFLAGS - (void) chflags(try, 0); /* XXX hack - if truly immutable, rename fails */ -#endif - if (preserve && PkgName) { - char pf[MaxPathSize]; - - if (make_preserve_name(pf, sizeof(pf), PkgName, try)) { - if (rename(try, pf)) { - warnx( - "unable to back up %s to %s, aborting pkg_add", - try, pf); - rollback(PkgName, home, pkg->head, p); - return 0; - } - } - } - } - if (rename(p->name, try) == 0) { - if (!NoRecord) { - /* note in pkgdb */ - char *s, t[MaxPathSize]; - int rc; - - (void) snprintf(t, sizeof(t), "%s/%s", Directory, p->name); - - s = pkgdb_retrieve(t); -#ifdef PKGDB_DEBUG - printf("pkgdb_retrieve(\"%s\")=\"%s\"\n", t, s); /* pkgdb-debug - HF */ -#endif - if (s) - warnx("Overwriting %s - pkg %s bogus/conflicting?", t, s); - else { - rc = pkgdb_store(t, PkgName); -#ifdef PKGDB_DEBUG - printf("pkgdb_store(\"%s\", \"%s\") = %d\n", t, PkgName, rc); /* pkgdb-debug - HF */ -#endif - - } - } - - /* try to add to list of perms to be changed and run in bulk. */ - if (p->name[0] == '/') - pushout(Directory); - - LFILE_ADD(&perms, lfp, p->name); - } else { - /* rename failed, try copying with a big tar command */ - if (last_chdir != Directory) { - if (last_chdir != NULL) - pushout(last_chdir); - last_chdir = Directory; - } else if (p->name[0] == '/') { - pushout(Directory); - } - - if (!NoRecord) { - /* note in pkgdb */ - /* XXX would be better to store in PUSHOUT, but - * that would probably affect too much code I prefer - * not to touch - HF */ - - char *s, t[MaxPathSize]; - int rc; - - LFILE_ADD(&files, lfp, p->name); - LFILE_ADD(&perms, lfp, p->name); - if (p->name[0] == '/') - errx(EXIT_FAILURE, "File names must not be absolute (%s).", p->name); - else { - (void) snprintf(t, sizeof(t), "%s/%s", Directory, p->name); - } - - s = pkgdb_retrieve(t); -#ifdef PKGDB_DEBUG - printf("pkgdb_retrieve(\"%s\")=\"%s\"\n", t, s); /* pkgdb-debug - HF */ -#endif - if (s) - warnx("Overwriting %s - pkg %s bogus/conflicting?", t, s); - else { - rc = pkgdb_store(t, PkgName); -#ifdef PKGDB_DEBUG - printf("pkgdb_store(\"%s\", \"%s\") = %d\n", t, PkgName, rc); /* pkgdb-debug - HF */ -#endif - } - } - } - } - break; - - case PLIST_CWD: - if (Verbose) - printf("extract: CWD to %s\n", p->name); - pushout(Directory); - if (strcmp(p->name, ".")) { - if (!Fake && make_hierarchy(p->name) == FAIL) { - cleanup(0); - errx(2, "unable to make directory '%s'", p->name); - } - Directory = p->name; - } else - Directory = home; - break; - - case PLIST_CMD: - format_cmd(cmd, sizeof(cmd), p->name, Directory, last_file); - pushout(Directory); - printf("Executing '%s'\n", cmd); - if (!Fake && system(cmd)) - warnx("command '%s' failed", cmd); - break; - - case PLIST_CHMOD: - pushout(Directory); - Mode = p->name; - break; - - case PLIST_CHOWN: - pushout(Directory); - Owner = p->name; - break; - - case PLIST_CHGRP: - pushout(Directory); - Group = p->name; - break; - - case PLIST_COMMENT: - break; - - case PLIST_IGNORE: - p = p->next; - break; - - default: - break; - } - p = p->next; - } - pushout(Directory); - if (!NoRecord) - pkgdb_close(); - return 1; -} diff --git a/pkgtools/pkg_install/files/add/futil.c b/pkgtools/pkg_install/files/add/futil.c deleted file mode 100644 index c314842b789..00000000000 --- a/pkgtools/pkg_install/files/add/futil.c +++ /dev/null @@ -1,151 +0,0 @@ -/* $NetBSD: futil.c,v 1.9 2005/12/06 01:08:09 ben Exp $ */ - -#if HAVE_CONFIG_H -#include "config.h" -#endif -#include -#if HAVE_SYS_CDEFS_H -#include -#endif -#ifndef lint -#if 0 -static const char *rcsid = "from FreeBSD Id: futil.c,v 1.7 1997/10/08 07:45:39 charnier Exp"; -#else -__RCSID("$NetBSD: futil.c,v 1.9 2005/12/06 01:08:09 ben Exp $"); -#endif -#endif - -/* - * FreeBSD install - a package for the installation and maintainance - * of non-core utilities. - * - * 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 - * - * Miscellaneous file access utilities. - * - */ - -#if HAVE_ERR_H -#include -#endif -#include "lib.h" -#include "add.h" - -/* - * Assuming dir is a desired directory name, make it and all intervening - * directories necessary. - */ -int -make_hierarchy(char *dir) -{ - char *cp1, *cp2; - char *argv[2]; - - argv[0] = dir; - argv[1] = NULL; - - if (dir[0] == '/') - cp1 = cp2 = dir + 1; - else - cp1 = cp2 = dir; - while (cp2) { - if ((cp2 = strchr(cp1, '/')) != NULL) - *cp2 = '\0'; - if (fexists(dir)) { - if (!(isdir(dir) || islinktodir(dir))) - return FAIL; - } else { - if (fexec("mkdir", dir, NULL)) - return FAIL; - apply_perms(NULL, argv, 1); - } - /* Put it back */ - if (cp2) { - *cp2 = '/'; - cp1 = cp2 + 1; - } - } - return SUCCESS; -} - -/* - * Using permission defaults, apply them as necessary - */ -void -apply_perms(char *dir, char **args, int nargs) -{ - char *cd_to; - char owner_group[128]; - const char **argv; - int i; - - argv = malloc((nargs + 4) * sizeof(char *)); - /* - * elements 0..2 are set later depending on Mode. - * args is a NULL terminated list of file names. - * by appending them to argv, argv becomes NULL terminated also. - */ - for (i = 0; i <= nargs; i++) - argv[i + 3] = args[i]; - - if (!dir || args[0][0] == '/') /* absolute path? */ - cd_to = "/"; - else - cd_to = dir; - - if (Mode) { - argv[0] = CHMOD_CMD; - argv[1] = "-R"; - argv[2] = Mode; - if (pfcexec(cd_to, argv[0], argv)) - warnx("couldn't change modes of '%s' ... to '%s'", - args[0], Mode); - } - if (Owner != NULL && Group != NULL) { - if (snprintf(owner_group, sizeof(owner_group), - "%s:%s", Owner, Group) > sizeof(owner_group)) { - warnx("'%s:%s' is too long (%lu max)", - Owner, Group, (unsigned long) sizeof(owner_group)); - free(argv); - return; - } - argv[0] = CHOWN_CMD; - argv[1] = "-R"; - argv[2] = owner_group; - if (pfcexec(cd_to, argv[0], argv)) - warnx("couldn't change owner/group of '%s' ... to '%s:%s'", - args[0], Owner, Group); - free(argv); - return; - } - if (Owner != NULL) { - argv[0] = CHOWN_CMD; - argv[1] = "-R"; - argv[2] = Owner; - if (pfcexec(cd_to, argv[0], argv)) - warnx("couldn't change owner of '%s' ... to '%s'", - args[0], Owner); - free(argv); - - return; - } - if (Group != NULL) { - argv[0] = CHGRP_CMD; - argv[1] = "-R"; - argv[2] = Group; - if (pfcexec(cd_to, argv[0], argv)) - warnx("couldn't change group of '%s' ... to '%s'", - args[0], Group); - } - free(argv); -} diff --git a/pkgtools/pkg_install/files/add/main.c b/pkgtools/pkg_install/files/add/main.c index 92335db7ddc..1e68005cff9 100644 --- a/pkgtools/pkg_install/files/add/main.c +++ b/pkgtools/pkg_install/files/add/main.c @@ -1,4 +1,4 @@ -/* $NetBSD: main.c,v 1.14 2008/03/01 19:06:10 rillig Exp $ */ +/* $NetBSD: main.c,v 1.15 2008/04/26 14:56:34 joerg Exp $ */ #if HAVE_CONFIG_H #include "config.h" @@ -11,7 +11,7 @@ #if 0 static char *rcsid = "from FreeBSD Id: main.c,v 1.16 1997/10/08 07:45:43 charnier Exp"; #else -__RCSID("$NetBSD: main.c,v 1.14 2008/03/01 19:06:10 rillig Exp $"); +__RCSID("$NetBSD: main.c,v 1.15 2008/04/26 14:56:34 joerg Exp $"); #endif #endif @@ -60,12 +60,6 @@ Boolean NoInstall = FALSE; Boolean NoRecord = FALSE; Boolean Automatic = FALSE; -char *Mode = NULL; -char *Owner = NULL; -char *Group = NULL; -char *PkgName = NULL; -char *Directory = NULL; -char FirstPen[MaxPathSize]; int Replace = 0; static void @@ -73,7 +67,7 @@ usage(void) { (void) fprintf(stderr, "%s\n%s\n%s\n", "usage: pkg_add [-AfhILnRuVv] [-K pkg_dbdir] [-m machine] [-p prefix]", - " [-s verification-type] [-t template] [-W viewbase] [-w view]", + " [-s verification-type] [-W viewbase] [-w view]", " [[ftp|http]://[user[:password]@]host[:port]][/path/]pkg-name ..."); exit(1); } @@ -126,13 +120,11 @@ main(int argc, char **argv) Prefix = optarg; break; +#if 0 case 's': set_verification(optarg); break; - - case 't': - strlcpy(FirstPen, optarg, sizeof(FirstPen)); - break; +#endif case 'u': Replace++; 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 #endif -#if HAVE_SYS_QUEUE_H -#include -#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 + * Copyright (c) 2005 Dieter Baron + * Copyright (c) 2007 Roland Illig + * Copyright (c) 2008 Joerg Sonnenberger + * 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 -#endif -#if HAVE_ERR_H +#include +#include +#include #include -#endif -#if HAVE_ERRNO_H #include -#endif -#include "defs.h" +#include +#include +#include + #include "lib.h" #include "add.h" -#include "verify.h" -#if HAVE_INTTYPES_H -#include -#endif -#if HAVE_SIGNAL_H -#include -#endif -#if HAVE_STRING_H -#include -#endif -#if HAVE_STDLIB_H -#include -#endif -#if HAVE_SYS_UTSNAME_H -#include -#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; } diff --git a/pkgtools/pkg_install/files/add/pkg_add.1 b/pkgtools/pkg_install/files/add/pkg_add.1 index 295d3fa0416..69df18cfb43 100644 --- a/pkgtools/pkg_install/files/add/pkg_add.1 +++ b/pkgtools/pkg_install/files/add/pkg_add.1 @@ -1,4 +1,4 @@ -.\" $NetBSD: pkg_add.1,v 1.28 2007/10/13 19:38:27 rillig Exp $ +.\" $NetBSD: pkg_add.1,v 1.29 2008/04/26 14:56:34 joerg Exp $ .\" .\" FreeBSD install - a package for the installation and maintenance .\" of non-core utilities. @@ -30,7 +30,6 @@ .Op Fl m Ar machine .Op Fl p Ar prefix .Op Fl s Ar verification-type -.Op Fl t Ar template .Op Fl W Ar viewbase .Op Fl w Ar view .Ar \fR[[ftp|http]://[\fIuser\fR[:\fIpassword]\fR@]\fIhost\fR[:\fIport\fR]][/\fIpath/\fR]pkg-name ... @@ -160,6 +159,8 @@ into .Ar pkgdb . .It Fl R Do not record the installation of a package. +This implies +.Fl I . This means that you cannot deinstall it later, so only use this option if you know what you are doing! .It Fl s Ar verification-type @@ -183,30 +184,6 @@ the verification feature when using to add a binary package via a URL - the package, and the related detached signature file, must be local for the verification to work. -.It Fl t Ar template -Use -.Ar template -as the input to -.Xr mktemp 3 -when creating a -.Dq staging area . -By default, this is the string -.Pa /var/tmp/instmp.XXXXXX , -but it may be necessary to override it in the situation where -space in your -.Pa /var/tmp -directory is limited. -Be sure to leave some number of -.Sq X -characters for -.Xr mktemp 3 -to fill in with a unique ID. -.Pp -You can get a performance boost by setting the staging area -.Ar template -to reside on the same disk partition as target directories for package -file installation; often this is -.Pa /usr . .It Fl u If the package that's being installed is already installed, either in the same or a different version, an update is performed. @@ -275,11 +252,10 @@ passive mode ftp. .Sh TECHNICAL DETAILS .Nm -extracts each package's -.Dq packing list -into a special staging directory in /var/tmp (or $PKG_TMPDIR if set) -and then runs through the following sequence to fully extract the contents -of the package: +extracts each package's meta data (including the +.Dq packing list ) +to memory and then runs through the following sequence to fully extract +the contents of the package: .Bl -enum -offset indent .It A check is made to determine if the package or another version of it diff --git a/pkgtools/pkg_install/files/create/perform.c b/pkgtools/pkg_install/files/create/perform.c index 51c84c04f27..7c8d1b29e5f 100644 --- a/pkgtools/pkg_install/files/create/perform.c +++ b/pkgtools/pkg_install/files/create/perform.c @@ -1,4 +1,4 @@ -/* $NetBSD: perform.c,v 1.19 2008/04/18 17:16:44 joerg Exp $ */ +/* $NetBSD: perform.c,v 1.20 2008/04/26 14:56:34 joerg Exp $ */ #if HAVE_CONFIG_H #include "config.h" @@ -11,7 +11,7 @@ #if 0 static const char *rcsid = "from FreeBSD Id: perform.c,v 1.38 1997/10/13 15:03:51 jkh Exp"; #else -__RCSID("$NetBSD: perform.c,v 1.19 2008/04/18 17:16:44 joerg Exp $"); +__RCSID("$NetBSD: perform.c,v 1.20 2008/04/26 14:56:34 joerg Exp $"); #endif #endif @@ -90,6 +90,38 @@ register_depends(package_t *plist, char *deps, int build_only) printf(".\n"); } +/* + * Expect "fname" to point at a file, and read it into + * the buffer returned. + */ +static char * +fileGetContents(char *fname) +{ + char *contents; + struct stat sb; + int fd; + + if (stat(fname, &sb) == FAIL) { + cleanup(0); + errx(2, "can't stat '%s'", fname); + } + + contents = (char *) malloc((size_t) (sb.st_size) + 1); + fd = open(fname, O_RDONLY, 0); + if (fd == FAIL) { + cleanup(0); + errx(2, "unable to open '%s' for reading", fname); + } + if (read(fd, contents, (size_t) sb.st_size) != (size_t) sb.st_size) { + cleanup(0); + errx(2, "short read on '%s' - did not get %lld bytes", + fname, (long long) sb.st_size); + } + close(fd); + contents[(size_t) sb.st_size] = '\0'; + return contents; +} + /* * Get a string parameter as a file spec or as a "contents follow -" spec */ diff --git a/pkgtools/pkg_install/files/info/Makefile.in b/pkgtools/pkg_install/files/info/Makefile.in index 5e1bbc856e5..fed22801e5a 100644 --- a/pkgtools/pkg_install/files/info/Makefile.in +++ b/pkgtools/pkg_install/files/info/Makefile.in @@ -1,4 +1,4 @@ -# $NetBSD: Makefile.in,v 1.14 2008/04/04 15:21:32 joerg Exp $ +# $NetBSD: Makefile.in,v 1.15 2008/04/26 14:56:34 joerg Exp $ srcdir= @srcdir@ @@ -16,7 +16,7 @@ BOOTSTRAP= @bootstrap@ CC= @CC@ CCLD= $(CC) .if empty(BOOTSTRAP) -LIBS= -linstall -larchive -lbz2 -lfetch -lz @LIBS@ +LIBS= -linstall -larchive -lfetch -lbz2 -lz @LIBS@ CPPFLAGS= @CPPFLAGS@ -I. -I$(srcdir) -I../lib .else LIBS= -linstall @LIBS@ diff --git a/pkgtools/pkg_install/files/info/perform.c b/pkgtools/pkg_install/files/info/perform.c index 528ec13c7a9..2acde1f1337 100644 --- a/pkgtools/pkg_install/files/info/perform.c +++ b/pkgtools/pkg_install/files/info/perform.c @@ -1,4 +1,4 @@ -/* $NetBSD: perform.c,v 1.46 2008/04/04 15:21:32 joerg Exp $ */ +/* $NetBSD: perform.c,v 1.47 2008/04/26 14:56:34 joerg Exp $ */ #if HAVE_CONFIG_H #include "config.h" @@ -17,7 +17,7 @@ #if 0 static const char *rcsid = "from FreeBSD Id: perform.c,v 1.23 1997/10/13 15:03:53 jkh Exp"; #else -__RCSID("$NetBSD: perform.c,v 1.46 2008/04/04 15:21:32 joerg Exp $"); +__RCSID("$NetBSD: perform.c,v 1.47 2008/04/26 14:56:34 joerg Exp $"); #endif #endif @@ -126,7 +126,7 @@ static const struct pkg_meta_desc { int entry_mask; int required_file; } pkg_meta_descriptors[] = { - { offsetof(struct pkg_meta, meta_contents), CONTENTS_FNAME , + { offsetof(struct pkg_meta, meta_contents), CONTENTS_FNAME, LOAD_CONTENTS, 1}, { offsetof(struct pkg_meta, meta_comment), COMMENT_FNAME, LOAD_COMMENT, 1 }, @@ -302,30 +302,19 @@ pkg_do(const char *pkg) int code = 0; const char *binpkgfile = NULL; - if (IS_URL(pkg)) { -#ifdef BOOTSTRAP - errx(2, "Remote access not supported during bootstrap"); -#else - struct archive *archive; - void *remote_archive_cookie; - - archive = open_remote_archive(pkg, &remote_archive_cookie); - - meta = read_meta_data_from_archive(archive); - close_remote_archive(remote_archive_cookie); -#endif - } else if (fexists(pkg) && isfile(pkg)) { + if (IS_URL(pkg) || (fexists(pkg) && isfile(pkg))) { #ifdef BOOTSTRAP errx(2, "Binary packages not supported during bootstrap"); #else struct archive *archive; - void *remote_archive_cookie; + void *archive_cookie; - archive = open_local_archive(pkg, &remote_archive_cookie); + archive = open_archive(pkg, &archive_cookie); meta = read_meta_data_from_archive(archive); - close_local_archive(remote_archive_cookie); - binpkgfile = pkg; + close_archive(archive_cookie); + if (!IS_URL(pkg)) + binpkgfile = pkg; #endif } else { /* diff --git a/pkgtools/pkg_install/files/lib/Makefile.in b/pkgtools/pkg_install/files/lib/Makefile.in index 95b846a5b83..8a10272a905 100644 --- a/pkgtools/pkg_install/files/lib/Makefile.in +++ b/pkgtools/pkg_install/files/lib/Makefile.in @@ -1,4 +1,4 @@ -# $NetBSD: Makefile.in,v 1.21 2008/04/06 17:47:27 joerg Exp $ +# $NetBSD: Makefile.in,v 1.22 2008/04/26 14:56:34 joerg Exp $ srcdir= @srcdir@ @@ -20,7 +20,7 @@ RANLIB= @RANLIB@ AR= @AR@ CC= @CC@ CPPFLAGS= @CPPFLAGS@ -I. -I$(srcdir) -DEFS= @DEFS@ -DDEF_LOG_DIR=\"$(pkgdbdir)\" -DTAR_CMD=\"$(tar)\" -DFTP_CMD=\"$(ftp)\" +DEFS= @DEFS@ -DDEF_LOG_DIR=\"$(pkgdbdir)\" CFLAGS= @CFLAGS@ INSTALL= @INSTALL@ @@ -28,8 +28,8 @@ INSTALL= @INSTALL@ LIB= libinstall.a OBJS= automatic.o conflicts.o decompress.o dewey.o fexec.o file.o \ - ftpio.o global.o iterate.o lpkg.o opattern.o \ - path.o pen.o pexec.o pkgdb.o plist.o \ + global.o iterate.o lpkg.o opattern.o \ + path.o pkgdb.o plist.o \ str.o var.o version.o vulnerabilities-file.o .if !empty(BOOTSTRAP) diff --git a/pkgtools/pkg_install/files/lib/conflicts.c b/pkgtools/pkg_install/files/lib/conflicts.c index 35dd204ccf7..01468ce0ff6 100644 --- a/pkgtools/pkg_install/files/lib/conflicts.c +++ b/pkgtools/pkg_install/files/lib/conflicts.c @@ -30,6 +30,7 @@ */ struct package_conflict { const char *pkgname; + const char *skip_pkgname; char **conflicting_pkgname; char **conflicting_pattern; }; @@ -70,6 +71,10 @@ check_package_conflict(const char *pkgname, void *v) FILE *f; int rv; + if (conflict->skip_pkgname != NULL && + strcmp(conflict->skip_pkgname, pkgname) == 0) + return 0; + rv = 0; pkg.head = NULL; pkg.tail = NULL; @@ -102,12 +107,14 @@ check_package_conflict(const char *pkgname, void *v) * variables are set to NULL. */ int -some_installed_package_conflicts_with(const char *pkgname, char **inst_pkgname, char **inst_pattern) +some_installed_package_conflicts_with(const char *pkgname, + const char *skip_pkgname, char **inst_pkgname, char **inst_pattern) { struct package_conflict cfl; int rv; cfl.pkgname = pkgname; + cfl.skip_pkgname = skip_pkgname; *inst_pkgname = NULL; *inst_pattern = NULL; cfl.conflicting_pkgname = inst_pkgname; diff --git a/pkgtools/pkg_install/files/lib/file.c b/pkgtools/pkg_install/files/lib/file.c index b683c205e6a..17322df70e3 100644 --- a/pkgtools/pkg_install/files/lib/file.c +++ b/pkgtools/pkg_install/files/lib/file.c @@ -1,4 +1,4 @@ -/* $NetBSD: file.c,v 1.23 2007/08/29 15:42:39 jlam Exp $ */ +/* $NetBSD: file.c,v 1.24 2008/04/26 14:56:34 joerg Exp $ */ #if HAVE_CONFIG_H #include "config.h" @@ -17,7 +17,7 @@ #if 0 static const char *rcsid = "from FreeBSD Id: file.c,v 1.29 1997/10/08 07:47:54 charnier Exp"; #else -__RCSID("$NetBSD: file.c,v 1.23 2007/08/29 15:42:39 jlam Exp $"); +__RCSID("$NetBSD: file.c,v 1.24 2008/04/26 14:56:34 joerg Exp $"); #endif #endif @@ -217,263 +217,6 @@ URLlength(const char *fname) return -1; } -/* - * Returns the host part of a URL - */ -const char * -fileURLHost(const char *fname, char *where, int max) -{ - const char *ret; - int i; - - assert(where != NULL); - assert(max > 0); - - if ((i = URLlength(fname)) < 0) { /* invalid URL? */ - errx(EXIT_FAILURE, "fileURLhost called with a bad URL: `%s'", fname); - } - fname += i; - /* Do we have a place to stick our work? */ - ret = where; - while (*fname && *fname != '/' && --max) - *where++ = *fname++; - *where = '\0'; - - return ret; -} - -/* - * Returns the filename part of a URL - */ -const char * -fileURLFilename(const char *fname, char *where, int max) -{ - const char *ret; - int i; - - assert(where != NULL); - assert(max > 0); - - if ((i = URLlength(fname)) < 0) { /* invalid URL? */ - errx(EXIT_FAILURE, "fileURLFilename called with a bad URL: `%s'", fname); - } - fname += i; - /* Do we have a place to stick our work? */ - ret = where; - while (*fname && *fname != '/') - ++fname; - if (*fname == '/') { - while (*fname && --max) - *where++ = *fname++; - } - *where = '\0'; - - return ret; -} - -/* - * Try and fetch a file by URL, returning the directory name for where - * it's unpacked, if successful. To be handed to leave_playpen() later. - */ -char * -fileGetURL(const char *spec) -{ - char host[MAXHOSTNAMELEN], file[MaxPathSize]; - const char *cp; - char *rp; - char pen[MaxPathSize]; - int rc; - - rp = NULL; - if (!IS_URL(spec)) { - errx(EXIT_FAILURE, "fileGetURL was called with non-URL arg '%s'", spec); - } - - /* Some sanity checks on the URL */ - cp = fileURLHost(spec, host, MAXHOSTNAMELEN); - if (!*cp) { - warnx("URL `%s' has bad host part!", spec); - return NULL; - } - cp = fileURLFilename(spec, file, MaxPathSize); - if (!*cp) { - warnx("URL `%s' has bad filename part!", spec); - return NULL; - } - - if (Verbose) - printf("Trying to fetch %s.\n", spec); - - pen[0] = '\0'; - rp = make_playpen(pen, sizeof(pen), 0); - if (rp == NULL) { - printf("Error: Unable to construct a new playpen for FTP!\n"); - return NULL; - } - - rp = strdup(pen); - rc = unpackURL(spec, pen); - if (rc < 0) { - leave_playpen(rp); /* Don't leave dir hang around! */ - - printf("Error on unpackURL('%s', '%s')\n", spec, pen); - return NULL; - } - return rp; -} - -static char * -resolvepattern1(const char *name) -{ - static char tmp[MaxPathSize]; - char *cp; - - if (IS_URL(name)) { - /* some package depends on a wildcard pkg */ - int rc; - - rc = expandURL(tmp, name); - if (rc < 0) { - return NULL; - } - if (Verbose) - printf("'%s' expanded to '%s'\n", name, tmp); - return tmp; /* return expanded URL w/ corrent pkg */ - } - else if (ispkgpattern(name)) { - cp = find_best_matching_file(dirname_of(name), basename_of(name), 1, 0); - if (cp) { - snprintf(tmp, sizeof(tmp), "%s/%s", dirname_of(name), cp); - free(cp); - return tmp; - } - } else { - if (isfile(name)) { - strlcpy(tmp, name, sizeof(tmp)); - return tmp; - } - } - - return NULL; -} - -static char * -resolvepattern(const char *name) -{ - char tmp[MaxPathSize]; - char *cp; - const char *suf; - - cp = resolvepattern1(name); - if (cp != NULL) - return cp; - - if (ispkgpattern(name)) - return NULL; - - suf = suffix_of(name); - if (!strcmp(suf, "tbz") || !strcmp(suf, "tgz")) - return NULL; - - /* add suffix and try */ - snprintf(tmp, sizeof(tmp), "%s.tbz", name); - cp = resolvepattern1(tmp); - if (cp != NULL) - return cp; - snprintf(tmp, sizeof(tmp), "%s.tgz", name); - cp = resolvepattern1(tmp); - if (cp != NULL) - return cp; - - /* add version number wildcard and try */ - snprintf(tmp, sizeof(tmp), "%s-[0-9]*", name); - return resolvepattern1(tmp); -} - -/* - * Look for filename/pattern "fname" in - * Returns a full path/URL where the pkg can be found - */ -char * -fileFindByPath(const char *fname) -{ - char tmp[MaxPathSize]; - struct path *path; - - /* - * 1. if fname is an absolute pathname or a URL, - * just use it. - */ - if (IS_FULLPATH(fname) || IS_URL(fname)) - return resolvepattern(fname); - - /* - * 2. otherwise, use PKG_PATH. - */ - TAILQ_FOREACH(path, &PkgPath, pl_entry) { - char *cp; - const char *cp2 = path->pl_path; - - if (Verbose) - printf("trying PKG_PATH %s\n", cp2); - - if (IS_FULLPATH(cp2) || IS_URL(cp2)) { - snprintf(tmp, sizeof(tmp), "%s/%s", cp2, fname); - } - else { - char cwdtmp[MaxPathSize]; - if (getcwd(cwdtmp, sizeof(cwdtmp)) == NULL) - errx(EXIT_FAILURE, "getcwd"); - snprintf(tmp, sizeof(tmp), "%s/%s/%s", cwdtmp, cp2, fname); - } - cp = resolvepattern(tmp); - if (cp) - return cp; - } - -#if 0 - /* - * 3. finally, search current directory. - */ - snprintf(tmp, sizeof(tmp), "./%s", fname); - return resolvepattern(tmp); -#else - return NULL; -#endif -} - -/* - * Expect "fname" to point at a file, and read it into - * the buffer returned. - */ -char * -fileGetContents(char *fname) -{ - char *contents; - struct stat sb; - int fd; - - if (stat(fname, &sb) == FAIL) { - cleanup(0); - errx(2, "can't stat '%s'", fname); - } - - contents = (char *) malloc((size_t) (sb.st_size) + 1); - fd = open(fname, O_RDONLY, 0); - if (fd == FAIL) { - cleanup(0); - errx(2, "unable to open '%s' for reading", fname); - } - if (read(fd, contents, (size_t) sb.st_size) != (size_t) sb.st_size) { - cleanup(0); - errx(2, "short read on '%s' - did not get %lld bytes", - fname, (long long) sb.st_size); - } - close(fd); - contents[(size_t) sb.st_size] = '\0'; - return contents; -} - /* * Takes a filename and package name, returning (in "try") the canonical * "preserve" name for it. @@ -509,97 +252,6 @@ make_preserve_name(char *try, size_t max, char *name, char *file) return TRUE; } -/* - * Write the contents of "str" to a file - */ -void -write_file(char *name, char *str) -{ - size_t len; - FILE *fp; - - if ((fp = fopen(name, "w")) == (FILE *) NULL) { - cleanup(0); - errx(2, "cannot fopen '%s' for writing", name); - } - len = strlen(str); - if (fwrite(str, 1, len, fp) != len) { - cleanup(0); - errx(2, "short fwrite on '%s', tried to write %ld bytes", - name, (long) len); - } - if (fclose(fp)) { - cleanup(0); - errx(2, "failure to fclose '%s'", name); - } -} - -void -copy_file(char *dir, char *fname, char *to) -{ - char fpath[MaxPathSize]; - - (void) snprintf(fpath, sizeof(fpath), "%s%s%s", - (fname[0] != '/') ? dir : "", - (fname[0] != '/') ? "/" : "", - fname); - if (fexec("cp", "-r", fpath, to, NULL)) { - cleanup(0); - errx(2, "could not perform 'cp -r %s %s'", fpath, to); - } -} - -void -move_file(char *dir, char *fname, char *to) -{ - char fpath[MaxPathSize]; - - (void) snprintf(fpath, sizeof(fpath), "%s%s%s", - (fname[0] != '/') ? dir : "", - (fname[0] != '/') ? "/" : "", - fname); - if (fexec("mv", fpath, to, NULL)) { - cleanup(0); - errx(2, "could not perform 'mv %s %s'", fpath, to); - } -} - -void -move_files(const char *dir, const char *pattern, const char *to) -{ - char fpath[MaxPathSize]; - glob_t globbed; - size_t i; - - (void) snprintf(fpath, sizeof(fpath), "%s/%s", dir, pattern); - if ((i=glob(fpath, GLOB_NOSORT, NULL, &globbed)) != 0) { - switch(i) { - case GLOB_NOMATCH: - warn("no files matching ``%s'' found", fpath); - break; - case GLOB_ABORTED: - warn("globbing aborted"); - break; - case GLOB_NOSPACE: - warn("out-of-memory during globbing"); - break; - default: - warn("unknown error during globbing"); - break; - } - return; - } - - /* Moving globbed files -- we just use mv(1) to do the job */ - for (i=0; i 0) - up_argv[++i] = "--fast-read"; - if (decompress_cmd != NULL) { - up_argv[++i] = "--use-compress-program"; - up_argv[++i] = (char *)decompress_cmd; - } - up_argv[++i] = "-xpf"; - up_argv[++i] = (char *)pkg; - if (count > 0) - TAILQ_FOREACH(lfp, filesp, lf_link) - up_argv[++i] = lfp->lf_name; - up_argv[++i] = NULL; - - if (Verbose) { - printf("running: %s", TAR_CMD); - for (i = 1; up_argv[i] != NULL; i++) - printf(" %s", up_argv[i]); - printf("\n"); - } - - result = pfcexec(NULL, TAR_CMD, (const char **)up_argv); - free(up_argv); - if (result != 0) { - warnx("extract of %s failed", pkg); - return 1; - } - - return 0; -} - /* * Using fmt, replace all instances of: * @@ -712,8 +296,8 @@ unpack(const char *pkg, const lfile_head_t *filesp) * * Check that no overflows can occur. */ -void -format_cmd(char *buf, size_t size, char *fmt, char *dir, char *name) +int +format_cmd(char *buf, size_t size, const char *fmt, const char *dir, const char *name) { char scratch[MaxPathSize * 2]; char *bufp; @@ -722,8 +306,8 @@ format_cmd(char *buf, size_t size, char *fmt, char *dir, char *name) for (bufp = buf; (int) (bufp - buf) < size && *fmt;) { if (*fmt == '%') { if (*++fmt != 'D' && name == NULL) { - cleanup(0); - errx(2, "no last file available for '%s' command", buf); + warnx("no last file available for '%s' command", buf); + return -1; } switch (*fmt) { case 'F': @@ -768,4 +352,5 @@ format_cmd(char *buf, size_t size, char *fmt, char *dir, char *name) } } *bufp = '\0'; + return 0; } diff --git a/pkgtools/pkg_install/files/lib/ftpio.c b/pkgtools/pkg_install/files/lib/ftpio.c deleted file mode 100644 index f23612ee391..00000000000 --- a/pkgtools/pkg_install/files/lib/ftpio.c +++ /dev/null @@ -1,1259 +0,0 @@ -/* $NetBSD: ftpio.c,v 1.26 2008/01/29 15:39:31 hubertf Exp $ */ - -#if HAVE_CONFIG_H -#include "config.h" -#endif -#include -#if HAVE_SYS_CDEFS_H -#include -#endif -#ifndef lint -__RCSID("$NetBSD: ftpio.c,v 1.26 2008/01/29 15:39:31 hubertf Exp $"); -#endif - -/*- - * Copyright (c) 1999-2008 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Hubert Feyrer and Thomas Klausner. - * - * 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. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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_SYS_TYPES_H -#include -#endif -#if HAVE_SYS_TIME_H -#include -#endif -#if HAVE_SYS_POLL_H -#include -#endif -#if HAVE_SYS_WAIT_H -#include -#endif -#if HAVE_SIGNAL_H -#include -#endif -#if HAVE_ASSERT_H -#include -#endif -#if HAVE_CTYPE_H -#include -#endif -#if HAVE_ERR_H -#include -#endif -#if HAVE_ERRNO_H -#include -#endif -#if HAVE_FCNTL_H -#include -#endif -#if HAVE_NETDB_H -#include -#endif -#if HAVE_REGEX_H -#include -#endif -#if HAVE_STRING_H -#include -#endif -#if HAVE_STDIO_H -#include -#endif -#if HAVE_STDLIB_H -#include -#endif -#if HAVE_TERMCAP_H -#include -#endif -#if HAVE_UNISTD_H -#include -#endif - -#include "../lib/lib.h" - -/* - * Names of environment variables used to pass things to - * subprocesses, for connection caching. - */ -#define PKG_FTPIO_COMMAND "PKG_FTPIO_COMMAND" -#define PKG_FTPIO_ANSWER "PKG_FTPIO_ANSWER" -#define PKG_FTPIO_CNT "PKG_FTPIO_CNT" -#define PKG_FTPIO_CURRENTHOST "PKG_FTPIO_CURRENTHOST" -#define PKG_FTPIO_CURRENTDIR "PKG_FTPIO_CURRENTDIR" - -#undef STANDALONE /* define for standalone debugging */ - -/* File descriptors */ -typedef struct { - int command; - int answer; -} fds; - - -static int needclose=0; -static int ftp_started=0; -static fds ftpio; -static int ftp_pid; -static char term[1024]; -static char bold_on[1024]; -static char bold_off[1024]; - -static char *ftp_expand_URL(const char *, char *); -static int hexvalue(char); -static char *http_expand_URL(const char *, char *); -static int http_extract_fn(char *, char *, size_t); -static void URL_decode(char *); - -/* - * expect "str" (a regular expression) on file descriptor "fd", storing - * the FTP return code of the command in the integer "ftprc". The "str" - * string is expected to match some FTP return codes after a '\n', e.g. - * "\n(550|226).*\n" - */ -static int -expect(int fd, const char *str, int *ftprc) -{ - int rc; - char buf[256]; - regex_t rstr; - int done; - struct pollfd set[1]; - int retval; - regmatch_t match; - int verbose_expect=0; - - if (regcomp(&rstr, str, REG_EXTENDED) != 0) - err(EXIT_FAILURE, "expect: regcomp() failed"); - - memset(buf, '\n', sizeof(buf)); - - done=0; - retval=0; - set[0].fd = fd; - set[0].events = POLLIN; - while(!done) { - rc = poll(set, 1, 60*60*1000); /* seconds until next message from tar */ - switch (rc) { - case -1: - if (errno == EINTR) - break; - warn("expect: poll() failed (probably ftp died because of bad args)"); - done = 1; - retval = -1; - break; - case 0: - warnx("expect: poll() timeout"); - /* need to send ftp coprocess SIGINT to make it stop - * downloading into dir that we'll blow away in a second */ - kill(ftp_pid, SIGINT); - - /* Wait until ftp coprocess is responsive again - * XXX Entering recursion here! - */ - rc = ftp_cmd("cd .\n", "\n(550|250).*\n"); - if (rc != 250) { - /* now we have a really good reason to bail out ;) */ - } - /* ftp is at command prompt again, and will wait for our - * next command. If we were downloading, we can now safely - * continue and remove the dir that the tar command was - * expanding to */ - - done = 1; /* hope that's ok */ - retval = -1; - break; - default: - if (set[0].revents & POLLHUP) { - done = 1; - retval = -1; - break; - } - - rc = read(fd, &buf[sizeof(buf) - 1], 1); - if (rc <= 0) { - done = 1; - retval = -1; - break; - } - - if (verbose_expect) - putchar(buf[sizeof(buf)-1]); - - if (regexec(&rstr, buf, 1, &match, 0) == 0) { - if (ftprc && isdigit((unsigned char)buf[match.rm_so+1])) - *ftprc = atoi(buf+match.rm_so+1); - - done=1; - retval=0; - } - - memmove(buf, buf+1, sizeof(buf)-1); /* yes, this is non-performant */ - break; - } - } - - return retval; -} - -/* - * send a certain ftp-command "cmd" to our FTP coprocess, and wait for - * "expectstr" to be returned. Return numeric FTP return code or -1 - * in case of an error (usually expect() timeout) - */ -int -ftp_cmd(const char *cmd, const char *expectstr) -{ - int rc=0, verbose_ftp=0; - int len; - - if (Verbose) - verbose_ftp=1; - - if (verbose_ftp) - fprintf(stderr, "\n%sftp> %s%s", bold_on, cmd, bold_off); - - fflush(stdout); - len = write(ftpio.command, cmd, strlen(cmd)); - if (len == strlen(cmd)) { - if (expectstr) { - /* set "rc" to the FTP error code: */ - if (expect(ftpio.answer, expectstr, &rc) == -1) - rc = -1; /* some error occurred */ - } - } else { - if (Verbose) - warn("short write"); - } - - return rc; -} - - -/* - * Really fire up FTP coprocess - */ -static int -setupCoproc(const char *base) -{ - int command_pipe[2]; - int answer_pipe[2]; - int rc1, rc2; - char buf[20]; - char *argv0 = (char *)strrchr(FTP_CMD, '/'); - if (argv0 == NULL) - argv0 = FTP_CMD; - else - argv0++; - - rc1 = pipe(command_pipe); - rc2 = pipe(answer_pipe); - - if(rc1==-1 || rc2==-1) { - warn("setupCoproc: pipe() failed"); - return -1; - } - - if (command_pipe[0] == -1 || command_pipe[1] == -1 || - answer_pipe[0] == -1 || answer_pipe[1] == -1 ) { - warn("setupCoproc: pipe() returned bogus descriptor"); - return -1; - } - - rc1 = fork(); - switch (rc1) { - case -1: - /* Error */ - - warn("setupCoproc: fork() failed"); - return -1; - break; - - case 0: - /* Child */ - - (void) close(command_pipe[1]); - rc1 = dup2(command_pipe[0], 0); - if (rc1 == -1) { - err(EXIT_FAILURE, "setupCoproc: dup2 failed (command_pipe[0])"); - } - (void) close(command_pipe[0]); - - (void) close(answer_pipe[0]); - rc1 = dup2(answer_pipe[1], 1); - if (rc1 == -1) { - err(EXIT_FAILURE, "setupCoproc: dup2 failed (answer_pipe[1])"); - } - (void) close(answer_pipe[1]); - - setbuf(stdout, NULL); - - if (Verbose) - fprintf(stderr, "%sftp -detv %s%s\n", bold_on, base, bold_off); - rc1 = execlp(FTP_CMD, argv0, "-detv", base, NULL); - warn("setupCoproc: execlp() failed"); - exit(1); - break; - default: - /* Parent */ - (void) close(command_pipe[0]); - (void) close(answer_pipe[1]); - - (void) snprintf(buf, sizeof(buf), "%d", command_pipe[1]); - setenv(PKG_FTPIO_COMMAND, buf, 1); - (void) snprintf(buf, sizeof(buf), "%d", answer_pipe[0]); - setenv(PKG_FTPIO_ANSWER, buf, 1); - - ftpio.command = command_pipe[1]; - ftpio.answer = answer_pipe[0]; - ftp_pid = rc1; /* to ^C transfers */ - - fcntl(ftpio.command, F_SETFL, O_NONBLOCK); - fcntl(ftpio.answer , F_SETFL, O_NONBLOCK); - - break; - } - - return 0; -} - - -/* - * Dummy signal handler to detect if the ftp(1) coprocess or - * and of the processes of the tar/gzip pipeline dies. - */ -static void -sigchld_handler (int n) -{ - /* Make poll(2) return EINTR */ -} - - -/* - * SIGPIPE only happens when there's something wrong with the FTP - * coprocess. In that case, set mark to not try to close shut down - * the coprocess. - */ -static void -sigpipe_handler(int n) -{ - /* aparently our ftp companion died */ - if (Verbose) - fprintf(stderr, "SIGPIPE!\n"); - needclose = 0; -} - - -/* - * Close the FTP coprocess' current connection, but - * keep the process itself alive. - */ -void -ftp_stop(void) -{ -#if defined(__svr4__) && defined(__sun__) - char env[BUFSIZ]; -#endif - const char *tmp1, *tmp2; - - if (!ftp_started) - return; - - tmp1=getenv(PKG_FTPIO_COMMAND); - tmp2=getenv(PKG_FTPIO_ANSWER); - - /* (Only) the last one closes the link */ - if (tmp1 != NULL && tmp2 != NULL) { - if (needclose) - ftp_cmd("close\n", "\n(221 .*|Not connected.)\n"); - - (void) close(ftpio.command); - (void) close(ftpio.answer); - } - -#if defined(__svr4__) && defined(__sun__) - (void) snprintf(env, sizeof(env), "%s=", PKG_FTPIO_COMMAND); - putenv(env); - (void) snprintf(env, sizeof(env), "%s=", PKG_FTPIO_ANSWER); - putenv(env); -#else - unsetenv(PKG_FTPIO_COMMAND); - unsetenv(PKG_FTPIO_ANSWER); -#endif -} - - -/* - * (Start and re-)Connect the FTP coprocess to some host/dir. - * If the requested host/dir is different than the one that the - * coprocess is currently at, close first. - */ -int -ftp_start(const char *base) -{ - const char *tmp1, *tmp2; - char *p; - int rc; - char newHost[MAXHOSTNAMELEN]; - const char *newDir; - const char *currentHost=getenv(PKG_FTPIO_CURRENTHOST); - const char *currentDir=getenv(PKG_FTPIO_CURRENTDIR); - int urllen; - - /* talk to termcap for bold on/off escape sequences */ - if (getenv("TERM") != NULL && tgetent(term, getenv("TERM")) > 0) { - p = bold_on; tgetstr("md", &p); - p = bold_off; tgetstr("me", &p); - } else { - bold_on[0] = '\0'; - bold_off[0] = '\0'; - } - - fileURLHost(base, newHost, sizeof(newHost)); - urllen = URLlength(base); - if (urllen < 0 || !(newDir = strchr(base + URLlength(base), '/'))) - errx(EXIT_FAILURE, "ftp_start: bad URL '%s'", base); - newDir++; - if (currentHost - && currentDir - && ( strcmp(newHost, currentHost) != 0 - || strcmp(newDir, currentDir) != 0)) { /* could handle new dir case better here, w/o reconnect */ - if (Verbose) { - printf("ftp_start: new host or dir, stopping previous connect...\n"); - printf("currentHost='%s', newHost='%s'\n", currentHost, newHost); - printf("currentDir='%s', newDir='%s'\n", currentDir, newDir); - } - - ftp_stop(); - - if (Verbose) - printf("ftp stopped\n"); - } - setenv(PKG_FTPIO_CURRENTHOST, newHost, 1); /* need to update this in the environment */ - setenv(PKG_FTPIO_CURRENTDIR, newDir, 1); /* for subprocesses to have this available */ - - tmp1=getenv(PKG_FTPIO_COMMAND); - tmp2=getenv(PKG_FTPIO_ANSWER); - if(tmp1==NULL || tmp2==NULL || *tmp1=='\0' || *tmp2=='\0') { - /* no FTP coprocess running yet */ - - if (Verbose) - printf("Spawning FTP coprocess\n"); - - rc = setupCoproc(base); - if (rc == -1) { - warnx("setupCoproc() failed"); - return -1; - } - - needclose=1; - signal(SIGPIPE, sigpipe_handler); - signal(SIGCHLD, sigchld_handler); - - if ((expect(ftpio.answer, "\n(221|250|221|550).*\n", &rc) != 0) - || rc != 250) { - warnx("expect1 failed, rc=%d", rc); - return -1; - } - - /* nbftp now issues a CWD for each part of the path - * and will return a code for each of them. No idea how to - * deal with that other than to issue a 'prompt off' to - * get something that we can wait for and that does NOT - * look like a CWD command's output */ - rc = ftp_cmd("prompt off\n", "\n(Interactive mode off|221).*\n"); - if ((rc == 221) || (rc == -1)) { - /* something is wrong */ - ftp_started=1; /* not really, but for ftp_stop() */ - ftp_stop(); - warnx("prompt failed - wrong dir?"); - return -1; - } - - ftp_started=1; - } else { - /* get FDs of our coprocess */ - - ftpio.command = dup(atoi(tmp1)); - if (ftpio.command == -1 ) { - warnx("command dup() failed, increase 'descriptors' limit"); - return -1; - } - ftpio.answer = dup(atoi(tmp2)); - if (ftpio.answer == -1 ) { - warnx("answer dup() failed, increase 'descriptors' limit"); - return -1; - } - - if (Verbose) - printf("Reusing FDs %s/%s for communication to FTP coprocess\n", tmp1, tmp2); - - fcntl(ftpio.command, F_SETFL, O_NONBLOCK); - fcntl(ftpio.answer , F_SETFL, O_NONBLOCK); - } - - return 0; -} - - -/* - * Expand the given wildcard URL "wildcardurl" if possible, and store the - * expanded value into "expandedurl". return 0 if successful, -1 else. - */ -int -expandURL(char *expandedurl, const char *wildcardurl) -{ - char *pattern; - char *bestmatch; - char base[MaxPathSize]; - - pattern=strrchr(wildcardurl, '/'); - if (pattern == NULL){ - warnx("expandURL: no '/' in URL %s?!", wildcardurl); - return -1; - } - if (pattern-strchr(wildcardurl, '/') < 2) { - /* only one or two slashes in total */ - warnx("expandURL: not enough '/' in URL %s", wildcardurl); - return -1; - } - (void) snprintf(base, sizeof(base), "%.*s/", - (int)(pattern-wildcardurl), wildcardurl); - pattern++; - - if (strncmp(wildcardurl, "ftp://", 6) == 0) - bestmatch=ftp_expand_URL(base, pattern); - else if (strncmp(wildcardurl, "http://", 7) == 0) - bestmatch=http_expand_URL(base, pattern); - else { - warnx("expandURL: unknown protocol in URL `%s'", wildcardurl); - return -1; - } - - /* no match found */ - if (bestmatch == NULL) - return -1; - - snprintf(expandedurl, MaxPathSize, "%s%s", base, bestmatch); - if (Verbose) - printf("best match: '%s'\n", expandedurl); - - return 0; -} - -/* for a given wildcard ftp:// URL, find the best matching pkg */ -static char * -ftp_expand_URL(const char *base, char *pattern) -{ - char *s, buf[MaxPathSize]; - char tmpname[MaxPathSize]; - char best[MaxPathSize]; - char s_best[MaxPathSize]; - int rc, got_list, tfd, retry_tbz; - - retry_tbz = 0; - best[0]='\0'; - s_best[0]='\0'; - - rc = ftp_start(base); - if (rc == -1) { - warnx("ftp_start() failed"); - return NULL; - } - - strlcpy(tmpname, "/var/tmp/pkg.XXXXXX", sizeof(tmpname)); - tfd=mkstemp(tmpname); - if (tfd == -1) { - warnx("Cannot generate temp file for ftp(1)'s nlist output"); - return NULL; - } - close(tfd); /* We don't need the file descriptor, but will use - the file in a second */ - - s=strpbrk(pattern, "<>[]?*{"); /* Could leave out "[]?*" here; - * ftp(1) is not that stupid */ - if (!s) { - /* This should only happen when getting here with (only) a package - * name specified to pkg_add, and PKG_PATH containing some URL. - */ - (void) snprintf(buf, sizeof(buf), "nlist %s %s\n", pattern, tmpname); - } else { - /* replace possible version(wildcard) given with "-*". - * we can't use the pkg wildcards here as dewey compare - * and alternates won't be handled by ftp(1); sort - * out later, using pkg_match() */ - if (retry_tbz) { -retry_with_tbz: - (void) snprintf(buf, sizeof(buf), "nlist %.*s*.tbz %s\n", - (int)(s-pattern), pattern, tmpname); - retry_tbz = 0; - } else { - (void) snprintf(buf, sizeof(buf), "nlist %.*s*.tgz %s\n", - (int)(s-pattern), pattern, tmpname); - retry_tbz = 1; - } - } - - rc = ftp_cmd(buf, "\n(550|450|226).*\n"); /* catch errors */ - if (rc != 226) - got_list = 0; - else - got_list = 1; - - /* Sync - don't remove */ - rc = ftp_cmd("cd .\n", "\n(550|250|257).*\n"); - if (rc != 250) { - warnx("chdir failed!"); - unlink(tmpname); /* remove clutter */ - return NULL; - } - - if (got_list == 1 && access(tmpname, R_OK)==0) { - FILE *f; - char filename[MaxPathSize]; - - f=fopen(tmpname, "r"); - if (f == NULL) { - warn("fopen"); - unlink(tmpname); /* remove clutter */ - return NULL; - } - /* The following loop is basically the same as the readdir() loop - * in findmatchingname() */ - while (fgets(filename, sizeof(filename), f)) { - - /* - * We need to strip off any .t[bg]z etc. - * suffix here - */ - - char s_filename[MaxPathSize]; - char s_pattern[MaxPathSize]; - - filename[strlen(filename)-1] = '\0'; - - strip_txz(s_filename, NULL, filename); - strip_txz(s_pattern, NULL, pattern); - - if (pkg_order(s_pattern, s_filename, - s_best[0] != '\0' ? s_best : NULL) == 1) { - strlcpy(s_best, s_filename, sizeof(s_best)); - strlcpy(best, filename, sizeof(best)); - } - } - (void) fclose(f); - } - - if (retry_tbz) - goto retry_with_tbz; - - if (best[0] == '\0' && Verbose) - warnx("nothing appropriate found"); - - unlink(tmpname); - - if (best[0] == '\0') - return NULL; - - return strdup(best); -} - -/* for a given wildcard http:// URL, find the best matching pkg */ -static char * -http_expand_URL(const char *base, char *pattern) -{ - char best[MaxPathSize]; - char s_best[MaxPathSize]; - char line[BUFSIZ]; - char filename[MaxPathSize]; - FILE *fp; - int pipefds[2]; - int state; - pid_t pid; - - *best = '\0'; - *s_best = '\0'; - - /* Set up a pipe for getting the file list */ - if (pipe(pipefds) == -1) { - warnx("cannot create pipe"); - return NULL; - } - if ((pid = fork()) == -1) { - warnx("cannot fork ftp process"); - return NULL; - } - if (pid == 0) { /* The child */ - if (dup2(pipefds[1], STDOUT_FILENO) == -1) { - warnx("dup2 failed before starting ftp"); - _exit(2); - } - close(pipefds[0]); - close(pipefds[1]); - /* get URL contents to stdout and thus to parent, - * silently */ - execlp("ftp", "ftp", "-V", "-o", "-", base, NULL); - warnx("failed to execute ftp"); - _exit(2); - } - - /* parent */ - close(pipefds[1]); - - if ((fp=fdopen(pipefds[0], "r")) == NULL) - warn("can't fdopen pipe end"); - else { - char s_pattern[MaxPathSize]; - int len, offset; - - /* strip of .t[bg]z for comparison */ - strip_txz(s_pattern, NULL, pattern); - - /* initialize http_extract_fn internal state */ - http_extract_fn(NULL, NULL, 0); - - /* read line from HTTP output and extract filenames */ - while (fgets(line, sizeof(line), fp) != NULL) { - len = offset = 0; - while ((len=http_extract_fn(line+offset, filename, - sizeof(filename))) > 0) { - char s_filename[MaxPathSize]; - - offset += len; - strip_txz(s_filename, NULL, filename); - - if (pkg_order(s_pattern, s_filename, - *s_best != '\0' ? s_best : NULL) == 1) { - strlcpy(best, filename, sizeof(best)); - strlcpy(s_best, s_filename, sizeof(best)); - } - } - } - - } - - fclose(fp); - - /* wait for child to exit */ - if (waitpid(pid, &state, 0) < 0) { - /* error has been reported by child */ - return NULL; - } - - if (best[0] == '\0') { - if (Verbose) - warnx("nothing appropriate found"); - return NULL; - } - - return strdup(best); - -} - -enum http_states { - ST_NONE, - ST_LT, ST_LTA, ST_TAGA, ST_H, ST_R, ST_E, ST_F, ST_HREF, - ST_TAG, ST_TAGAX -}; - -/* return any hrefs found */ -static int -http_extract_fn(char *input, char *outbuf, size_t outbuflen) -{ - /* partial copied hrefs from previous calls are saved here */ - static char tempbuf[MaxPathSize]; - /* fill state of tempbuf */ - static int tempbuffill = 0; - /* parsing state information */ - static enum http_states state; - /* currently in double quotes (in parsing) */ - static int dqflag; - char p; - int offset, found; - - if (outbuf == NULL) { - /* init */ - dqflag = tempbuffill = 0; - state = ST_NONE; - return 0; - } - - offset = 0; - found = 0; - while ((p=input[offset++]) != '\0') { - /* handle anything that's inside double quotes */ - if (dqflag) { - /* incomplete href */ - if (state == ST_HREF) { - /* check if space left in output - * buffer */ - if (tempbuffill >= sizeof(tempbuf)) { - warnx("href starting with `%.*s'" - " too long", 60, tempbuf); - /* ignore remainder */ - tempbuffill = 0; - /* need space before "href" - * can start again (invalidly, - * of course, but we don't - * care) */ - state = ST_TAGAX; - } - - /* href complete */ - if (p == '\"') { - /* complete */ - dqflag = 0; - tempbuf[tempbuffill++] = '\0'; - /* need space before "href" - * can start again (invalidly, - * of course, but we don't - * care) */ - state = ST_TAGAX; - found = 1; - break; - } else { - /* copy one more char */ - tempbuf[tempbuffill++] = p; - } - } else { - /* leaving double quotes */ - if (p == '\"') - dqflag = 0; - } - continue; - } - - /* - * entering double quotes? (only relevant inside a tag) - */ - if (state != ST_NONE && p == '\"') { - dqflag = 1; - continue; - } - - /* other cases */ - switch (state) { - case ST_NONE: - /* plain text, not in markup */ - if (p == '<') - state = ST_LT; - break; - case ST_LT: - /* in tag -- "<" already found */ - if (p == '>') - state = ST_NONE; - else if (p == 'a' || p == 'A') - state = ST_LTA; - else if (!isspace((unsigned char)p)) - state = ST_TAG; - break; - case ST_LTA: - /* in tag -- "') - state = ST_NONE; - else if (isspace((unsigned char)p)) - state = ST_TAGA; - else - state = ST_TAG; - break; - case ST_TAG: - /* in tag, but not "') - state = ST_NONE; - break; - case ST_TAGA: - /* in a-tag -- "') - state = ST_NONE; - else if (p == 'h' || p == 'H') - state = ST_H; - else if (!isspace((unsigned char)p)) - state = ST_TAGAX; - break; - case ST_TAGAX: - /* in unknown keyword in a-tag */ - if (p == '>') - state = ST_NONE; - else if (isspace((unsigned char)p)) - state = ST_TAGA; - break; - case ST_H: - /* in a-tag -- "') - state = ST_NONE; - else if (p == 'r' || p == 'R') - state = ST_R; - else if (isspace((unsigned char)p)) - state = ST_TAGA; - else - state = ST_TAGAX; - break; - case ST_R: - /* in a-tag -- "') - state = ST_NONE; - else if (p == 'e' || p == 'E') - state = ST_E; - else if (isspace((unsigned char)p)) - state = ST_TAGA; - else - state = ST_TAGAX; - break; - case ST_E: - /* in a-tag -- "') - state = ST_NONE; - else if (p == 'f' || p == 'F') - state = ST_F; - else if (isspace((unsigned char)p)) - state = ST_TAGA; - else - state = ST_TAGAX; - break; - case ST_F: - /* in a-tag -- "') - state = ST_NONE; - else if (p == '=') - state = ST_HREF; - else if (!isspace((unsigned char)p)) - state = ST_TAGAX; - break; - case ST_HREF: - /* in a-tag -- "0){ - (void) snprintf(buf, sizeof(buf),"%d", atoi(s)-1); - setenv(PKG_FTPIO_CNT, buf, 1); - - printf("%s>>> %s -v %s\n", s, argv0, argv[0]); - fexec(argv0, "-v", argv[0], NULL); - } - } - - printf("\n\n\n"); - argv++; - } - - ftp_stop(); - - return 0; -} - -void -cleanup(int i) -{ -} -#endif /* STANDALONE */ diff --git a/pkgtools/pkg_install/files/lib/lib.h b/pkgtools/pkg_install/files/lib/lib.h index bb52d0c2d59..e9ed05d68d7 100644 --- a/pkgtools/pkg_install/files/lib/lib.h +++ b/pkgtools/pkg_install/files/lib/lib.h @@ -1,4 +1,4 @@ -/* $NetBSD: lib.h,v 1.42 2008/04/18 17:16:44 joerg Exp $ */ +/* $NetBSD: lib.h,v 1.43 2008/04/26 14:56:34 joerg Exp $ */ /* from FreeBSD Id: lib.h,v 1.25 1997/10/08 07:48:03 charnier Exp */ @@ -86,48 +86,18 @@ #define DEF_UMASK 022 #endif +#define MKDIR_CMD "mkdir" + /* Usually "rm", but often "echo" during debugging! */ #define REMOVE_CMD "rm" /* Usually "rm", but often "echo" during debugging! */ #define RMDIR_CMD "rmdir" -/* Define tar as a string, in case it's called gtar or something */ -#ifndef TAR_CMD -#define TAR_CMD "tar" -#endif - -/* Define pax as a string, used to copy files from staging area */ -#ifndef PAX_CMD -#define PAX_CMD "pax" -#endif - -/* Define gzip and bzip2, used to unpack binary packages */ -#ifndef GZIP_CMD -#define GZIP_CMD "gzip" -#endif - -#ifndef BZIP2_CMD -#define BZIP2_CMD "bzip2" -#endif - -/* Define ftp as a string, in case the ftp client is called something else */ -#ifndef FTP_CMD -#define FTP_CMD "ftp" -#endif - -#ifndef CHOWN_CMD -#define CHOWN_CMD "chown" -#endif - #ifndef CHMOD_CMD #define CHMOD_CMD "chmod" #endif -#ifndef CHGRP_CMD -#define CHGRP_CMD "chgrp" -#endif - /* some operating systems don't have this */ #ifndef MAXPATHLEN #define MAXPATHLEN 1024 @@ -158,13 +128,8 @@ enum { /* The names of special variables */ #define AUTOMATIC_VARNAME "automatic" -/* - * files which we expect to be in every package, passed to - * tar --fast-read. - */ -#define ALL_FNAMES CONTENTS_FNAME" "COMMENT_FNAME" "DESC_FNAME" "MTREE_FNAME" "BUILD_VERSION_FNAME" "BUILD_INFO_FNAME" "SIZE_PKG_FNAME" "SIZE_ALL_FNAME - -#define CMD_CHAR '@' /* prefix for extended PLIST cmd */ +/* Prefix for extended PLIST cmd */ +#define CMD_CHAR '@' /* The name of the "prefix" environment variable given to scripts */ #define PKG_PREFIX_VNAME "PKG_PREFIX" @@ -273,14 +238,6 @@ typedef struct _lpkg_t { TAILQ_HEAD(_lpkg_head_t, _lpkg_t); typedef struct _lpkg_head_t lpkg_head_t; -/* This structure describes a pipe to a child process */ -typedef struct { - int fds[2]; /* pipe, 0=child stdin, 1=parent output */ - FILE *fp; /* output from parent process */ - pid_t pid; /* process id of child process */ - void (*cleanup)(void); /* called on non-zero child exit status */ -} pipe_to_system_t; - struct pkg_vulnerabilities { size_t entries; char **vulnerability; @@ -296,25 +253,17 @@ struct pkg_vulnerabilities { #define IS_FULLPATH(str) ((str) != NULL && (str)[0] == '/') /* Conflict handling (conflicts.c) */ -int some_installed_package_conflicts_with(const char *, char **, char **); +int some_installed_package_conflicts_with(const char *, const char *, char **, char **); /* Prototypes */ /* Misc */ void cleanup(int); -char *make_playpen(char *, size_t, size_t); -char *where_playpen(void); -void leave_playpen(char *); -uint64_t min_free(const char *); -void save_dirs(char **, char **); -void restore_dirs(char *, char *); void show_version(void); int fexec(const char *, ...); int fexec_skipempty(const char *, ...); int fcexec(const char *, const char *, ...); int pfcexec(const char *, const char *, const char **); -pipe_to_system_t *pipe_to_system_begin(const char *, char *const *, void (*)(void)); -int pipe_to_system_end(pipe_to_system_t *); /* variables file handling */ @@ -335,7 +284,6 @@ const char *suffix_of(const char *); int pkg_match(const char *, const char *); int pkg_order(const char *, const char *, const char *); int ispkgpattern(const char *); -void strip_txz(char *, char *, const char *); /* Iterator functions */ int iterate_pkg_generic_src(int (*)(const char *, void *), void *, @@ -361,35 +309,17 @@ Boolean isfile(const char *); Boolean isbrokenlink(const char *); Boolean isempty(const char *); int URLlength(const char *); -char *fileGetURL(const char *); -const char *fileURLFilename(const char *, char *, int); -const char *fileURLHost(const char *, char *, int); -char *fileFindByPath(const char *); -char *fileGetContents(char *); Boolean make_preserve_name(char *, size_t, char *, char *); -void write_file(char *, char *); -void copy_file(char *, char *, char *); -void move_file(char *, char *, char *); -void move_files(const char *, const char *, const char *); void remove_files(const char *, const char *); int delete_hierarchy(char *, Boolean, Boolean); -int unpack(const char *, const lfile_head_t *); -void format_cmd(char *, size_t, char *, char *, char *); - -/* ftpio.c: FTP handling */ -int expandURL(char *, const char *); -int unpackURL(const char *, const char *); -int ftp_cmd(const char *, const char *); -int ftp_start(const char *); -void ftp_stop(void); +int format_cmd(char *, size_t, const char *, const char *, const char *); /* pkg_io.c: Local and remote archive handling */ struct archive; -struct archive *open_remote_archive(const char *, void **); -void close_remote_archive(void *); -struct archive *open_local_archive(const char *, void **); -void close_local_archive(void *); +struct archive *open_archive(const char *, void **); +void close_archive(void *); +struct archive *find_archive(const char *, void **); /* Packing list */ plist_t *new_plist_entry(void); diff --git a/pkgtools/pkg_install/files/lib/pen.c b/pkgtools/pkg_install/files/lib/pen.c deleted file mode 100644 index dd73a0de2e5..00000000000 --- a/pkgtools/pkg_install/files/lib/pen.c +++ /dev/null @@ -1,206 +0,0 @@ -/* $NetBSD: pen.c,v 1.22 2008/02/04 14:28:27 joerg Exp $ */ - -#if HAVE_CONFIG_H -#include "config.h" -#endif -#include -#if HAVE_SYS_CDEFS_H -#include -#endif -#ifndef lint -#if 0 -static const char *rcsid = "from FreeBSD Id: pen.c,v 1.25 1997/10/08 07:48:12 charnier Exp"; -#else -__RCSID("$NetBSD: pen.c,v 1.22 2008/02/04 14:28:27 joerg Exp $"); -#endif -#endif - -/* - * FreeBSD install - a package for the installation and maintainance - * of non-core utilities. - * - * 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 - * - * Routines for managing the "play pen". - * - */ - -#if HAVE_ERR_H -#include -#endif -#include "lib.h" -#if HAVE_SYS_SIGNAL_H -#include -#endif - -/* For keeping track of where we are */ -static char Current[MaxPathSize]; -static char Previous[MaxPathSize]; -static int CurrentSet; /* rm -fr Current only if it's really set! */ - /* CurrentSet is set to 0 before strcpy()s - * to prevent rm'ing of a partial string - * when interrupted by ^C */ - -char * -where_playpen(void) -{ - return Current; -} - -/* - * Find a good place to play. - */ -static char * -find_play_pen(char *pen, size_t pensize, size_t sz) -{ - const char **cp; - struct stat sb; - char *r; - const char *tmpdir[] = { - "PKG_TMPDIR", - "TMPDIR", - "/var/tmp", - "/tmp", - "/usr/tmp", - NULL - }; - - if (pen == NULL) { - cleanup(0); - errx(2, "find_play_pen(): 'pen' variable is NULL\n" - "(this should not happen, please report!)"); - return NULL; - } - - if (pen[0] && (r = strrchr(pen, '/')) != NULL) { - *r = '\0'; - if (stat(pen, &sb) != FAIL && (min_free(pen) >= sz)) { - *r = '/'; - return pen; - } - } - - for (cp = tmpdir; *cp; cp++) { - const char *d = (**cp == '/') ? *cp : getenv(*cp); - - if (d == NULL || stat(d, &sb) == FAIL || min_free(d) < sz) - continue; - - (void)snprintf(pen, pensize, "%s/instmp.XXXXXX", d); - return pen; - } - - cleanup(0); - errx(2, "Can't find enough temporary space to extract the files.\n" - "Please set your PKG_TMPDIR environment variable to a location " - "with at least %zu bytes free", sz); - return NULL; -} - -/* - * Make a temporary directory to play in and chdir() to it, returning - * pathname of previous working directory. - */ -char * -make_playpen(char *pen, size_t pensize, size_t sz) -{ - if (!find_play_pen(pen, pensize, sz)) - return NULL; - - if (!mkdtemp(pen)) { - cleanup(0); - errx(2, "can't mkdtemp '%s'", pen); - } - - /* - * On at least NetBSD, the temporary directory may have a group - * that isn't in the group list of the current user. In that - * case, it is impossible to extract setgid binaries from the - * package, since chmod(2) doesn't allow to set the S_ISGID bit - * for a group that isn't yours. - */ - (void)chown(pen, -1, getegid()); - - if (Verbose) { - if (sz) - fprintf(stderr, - "Requested space: %lu bytes, free space: %lld bytes in %s\n", - (u_long) sz, (long long) min_free(pen), pen); - } - if (min_free(pen) < sz) { - rmdir(pen); - cleanup(0); - errx(2, "not enough free space to create '%s'.\n" - "Please set your PKG_TMPDIR environment variable to a location\n" - "with more space and\ntry the command again", pen); - } - if (Current[0]) - strlcpy(Previous, Current, sizeof(Previous)); - else if (!getcwd(Previous, MaxPathSize)) { - cleanup(0); - err(EXIT_FAILURE, "fatal error during execution: getcwd"); - } - if (chdir(pen) == FAIL) { - cleanup(0); - errx(2, "can't chdir to '%s'", pen); - } - CurrentSet = 0; strlcpy(Current, pen, sizeof(Current)); CurrentSet = 1; - - return Previous; -} - -/* - * Convenience routine for getting out of playpen - */ -void -leave_playpen(char *save) -{ - void (*oldsig) (int); - - /* Make us interruptable while we're cleaning up - just in case... */ - oldsig = signal(SIGINT, SIG_DFL); - if (Previous[0] && chdir(Previous) == FAIL) { - cleanup(0); - errx(2, "can't chdir back to '%s'", Previous); - } else if (CurrentSet && Current[0] && strcmp(Current, Previous)) { - if (strcmp(Current, "/") == 0) { - fprintf(stderr, "PANIC: About to rm -fr / (not doing so, aborting)\n"); - abort(); - } - if (fexec("rm", "-fr", Current, NULL)) - warnx("couldn't remove temporary dir '%s'", Current); - strlcpy(Current, Previous, sizeof(Current)); - } - if (save) - strlcpy(Previous, save, sizeof(Previous)); - else - Previous[0] = '\0'; - signal(SIGINT, oldsig); -} - -/* - * Return free disk space (in bytes) on given file system. - * Returns size in a uint64_t since off_t isn't 64 bits on all - * operating systems. - */ -uint64_t -min_free(const char *tmpdir) -{ - struct statvfs buf; - - if (statvfs(tmpdir, &buf) != 0) { - warn("statvfs"); - return 0; - } - return (uint64_t)buf.f_bavail * buf.f_bsize; -} diff --git a/pkgtools/pkg_install/files/lib/pexec.c b/pkgtools/pkg_install/files/lib/pexec.c deleted file mode 100644 index 1358aedb910..00000000000 --- a/pkgtools/pkg_install/files/lib/pexec.c +++ /dev/null @@ -1,110 +0,0 @@ -#if HAVE_CONFIG_H -#include "config.h" -#endif -#include -#if HAVE_SYS_WAIT_H -#include -#endif -#if HAVE_ERR_H -#include -#endif -#if HAVE_ERRNO_H -#include -#endif -#if HAVE_STDLIB_H -#include -#endif -#if HAVE_UNISTD_H -#include -#endif -#if HAVE_STDIO_H -#include -#endif - -#include "lib.h" - -/* - * If the supplied callback is not NULL, then call it. - */ -static void call_callback(void (*callback)(void)) -{ - if (callback != NULL) { - callback(); - } -} - -/* - * create pipe, fork and exec file with arguments in argv - * child takes stdin from pipe, set up fp for parent to - * output to pipe, and return this information. - */ -pipe_to_system_t *pipe_to_system_begin(const char *file, char *const argv[], - void (*cleanup_callback)(void)) -{ - pipe_to_system_t *retval; - - retval = malloc(sizeof(pipe_to_system_t)); - if (retval == NULL) { - call_callback(cleanup_callback); - errx(2, "can't get pipe space"); - } - - retval->cleanup = cleanup_callback; - - if (pipe(retval->fds) == -1) { - call_callback(retval->cleanup); - errx(2, "cannot create pipe"); - } - - retval->pid = fork(); - if (retval->pid == -1) { - call_callback(retval->cleanup); - errx(2, "cannot fork process for %s", file); - } - - if (retval->pid == 0) { /* The child */ - if (retval->fds[0] != 0) { - dup2(retval->fds[0], 0); - close(retval->fds[0]); - } - close(retval->fds[1]); - execvp(file, argv); - warn("failed to execute %s command", file); - _exit(2); - } - - /* Meanwhile, back in the parent process ... */ - close(retval->fds[0]); - retval->fp = fdopen(retval->fds[1], "w"); - if (retval->fp == NULL) { - call_callback(retval->cleanup); - errx(2, "fdopen failed"); - } - return retval; -} - -/* - * close pipe and wait for child to exit. on non-zero exit status, - * call cleanup callback. return exit status. - */ -int pipe_to_system_end(pipe_to_system_t *to_pipe) -{ - int status; - int wait_ret; - - fclose(to_pipe->fp); - do { - wait_ret = waitpid(to_pipe->pid, &status, 0); - } while (wait_ret == -1 && errno == EINTR); - - if (wait_ret < 0) { - call_callback(to_pipe->cleanup); - errx(2, "waitpid returned failure"); - } - if (!WIFEXITED(status)) { - call_callback(to_pipe->cleanup); - errx(2, "waitpid: process terminated abnormally"); - } - free(to_pipe); - return WEXITSTATUS(status); -} diff --git a/pkgtools/pkg_install/files/lib/pkg_io.c b/pkgtools/pkg_install/files/lib/pkg_io.c index 643262e0221..0a87bfc5a64 100644 --- a/pkgtools/pkg_install/files/lib/pkg_io.c +++ b/pkgtools/pkg_install/files/lib/pkg_io.c @@ -1,4 +1,4 @@ -/* $NetBSD: pkg_io.c,v 1.1 2008/04/04 15:21:32 joerg Exp $ */ +/* $NetBSD: pkg_io.c,v 1.2 2008/04/26 14:56:34 joerg Exp $ */ /*- * Copyright (c) 2008 Joerg Sonnenberger . * All rights reserved. @@ -36,7 +36,7 @@ #include #endif -__RCSID("$NetBSD: pkg_io.c,v 1.1 2008/04/04 15:21:32 joerg Exp $"); +__RCSID("$NetBSD: pkg_io.c,v 1.2 2008/04/26 14:56:34 joerg Exp $"); #include #include @@ -52,7 +52,7 @@ __RCSID("$NetBSD: pkg_io.c,v 1.1 2008/04/04 15:21:32 joerg Exp $"); #include "lib.h" struct fetch_archive { - const char *url; + struct url *url; fetchIO *fetch; char buffer[32768]; }; @@ -62,7 +62,7 @@ fetch_archive_open(struct archive *a, void *client_data) { struct fetch_archive *f = client_data; - f->fetch = fetchGetURL(f->url, ""); + f->fetch = fetchGet(f->url, ""); if (f->fetch == NULL) return ENOENT; return 0; @@ -88,52 +88,181 @@ fetch_archive_close(struct archive *a, void *client_data) return 0; } -struct archive * -open_remote_archive(const char *url, void **cookie) +static struct archive * +open_archive_by_url(struct url *url, void **cookie) { struct fetch_archive *f; - struct archive *archive; + struct archive *a; f = malloc(sizeof(*f)); if (f == NULL) err(2, "cannot allocate memory for remote archive"); f->url = url; - archive = archive_read_new(); - archive_read_support_compression_all(archive); - archive_read_support_format_all(archive); - if (archive_read_open(archive, f, fetch_archive_open, fetch_archive_read, - fetch_archive_close)) - errx(2, "cannot open archive: %s", archive_error_string(archive)); + a = archive_read_new(); + archive_read_support_compression_all(a); + archive_read_support_format_all(a); + if (archive_read_open(a, f, fetch_archive_open, fetch_archive_read, + fetch_archive_close)) { + archive_read_close(a); + free(f); + return NULL; + } *cookie = f; + return a; +} + +struct archive * +open_archive(const char *url, void **cookie) +{ + struct url *u; + struct archive *a; + + if (!IS_URL(url)) { + a = archive_read_new(); + archive_read_support_compression_all(a); + archive_read_support_format_all(a); + if (archive_read_open_filename(a, url, 1024)) { + archive_read_close(a); + return NULL; + } + *cookie = NULL; + return a; + } + + if ((u = fetchParseURL(url)) == NULL) + return NULL; - return archive; + a = open_archive_by_url(u, cookie); + + fetchFreeURL(u); + return a; } void -close_remote_archive(void *cookie) +close_archive(void *cookie) { free(cookie); } -struct archive * -open_local_archive(const char *path, void **cookie) +static int +strip_suffix(char *filename) { - struct archive *archive; + size_t len; - archive = archive_read_new(); - archive_read_support_compression_all(archive); - archive_read_support_format_all(archive); - if (archive_read_open_filename(archive, path, 1024)) - errx(2, "cannot open archive: %s", - archive_error_string(archive)); - *cookie = NULL; + len = strlen(filename); + if (len <= 4) + return 0; + if (strcmp(filename + len - 4, ".tgz") == 0 || + strcmp(filename + len - 4, ".tbz") == 0) { + filename[len - 4] = '\0'; + return 1; + } else + return 0; +} - return archive; +static int +find_best_package(struct url *url, const char *pattern, struct url **best_url) +{ + char *cur_match, *best_match = NULL; + struct url_list ue; + size_t i; + + if (*best_url) { + if ((best_match = fetchUnquoteFilename(*best_url)) == NULL) + return -1; + } else + best_match = NULL; + + if (best_match && strip_suffix(best_match) == 0) { + free(best_match); + return -1; + } + + fetchInitURLList(&ue); + if (fetchList(&ue, url, NULL, "")) { + fetchFreeURLList(&ue); + return -1; + } + for (i = 0; i < ue.length; ++i) { + cur_match = fetchUnquoteFilename(ue.urls + i); + + if (cur_match == NULL) { + free(best_match); + fetchFreeURLList(&ue); + return -1; + } + if (strip_suffix(cur_match) == 0) { + free(cur_match); + continue; + } + if (pkg_order(pattern, cur_match, best_match) == 1) { + if (*best_url) + fetchFreeURL(*best_url); + *best_url = fetchCopyURL(ue.urls + i); + free(best_match); + best_match = cur_match; + cur_match = NULL; + if (*best_url == NULL) { + free(best_match); + return -1; + } + } + free(cur_match); + } + free(best_match); + fetchFreeURLList(&ue); + return 0; } -void -close_local_archive(void *cookie) +struct archive * +find_archive(const char *fname, void **cookie) { + struct archive *a; + struct path *path; + const char *cur_path; + struct url *url, *best_match; + char tmp[MaxPathSize]; + + best_match = NULL; + + a = open_archive(fname, cookie); + if (a != NULL) + return a; + + if (strchr(fname, '/') != NULL) { + const char *last_slash; + + last_slash = strrchr(fname, '/'); + snprintf(tmp, sizeof(tmp), "%s%.*s", + IS_URL(fname) ? "" : "file://", + (int)(last_slash - fname + 1), fname); + url = fetchParseURL(tmp); + if (url == NULL) + return NULL; + fname = last_slash + 1; /* XXX fetchUnquoteFilename */ + find_best_package(url, fname, &best_match); + fetchFreeURL(url); + } else { + TAILQ_FOREACH(path, &PkgPath, pl_entry) { + cur_path = path->pl_path; + if (!IS_URL(cur_path)) { + snprintf(tmp, sizeof(tmp), "file://%s", cur_path); + cur_path = tmp; + } + url = fetchParseURL(cur_path); + if (url == NULL) + continue; + find_best_package(url, fname, &best_match); + /* XXX Check return value and complain */ + fetchFreeURL(url); + } + } + + if (best_match == NULL) + return NULL; + a = open_archive_by_url(best_match, cookie); + fetchFreeURL(best_match); + return a; } diff --git a/pkgtools/pkg_install/files/lib/plist.c b/pkgtools/pkg_install/files/lib/plist.c index 0c0f0064fd1..8539272be75 100644 --- a/pkgtools/pkg_install/files/lib/plist.c +++ b/pkgtools/pkg_install/files/lib/plist.c @@ -1,4 +1,4 @@ -/* $NetBSD: plist.c,v 1.17 2008/02/02 16:21:45 joerg Exp $ */ +/* $NetBSD: plist.c,v 1.18 2008/04/26 14:56:34 joerg Exp $ */ #if HAVE_CONFIG_H #include "config.h" @@ -11,7 +11,7 @@ #if 0 static const char *rcsid = "from FreeBSD Id: plist.c,v 1.24 1997/10/08 07:48:15 charnier Exp"; #else -__RCSID("$NetBSD: plist.c,v 1.17 2008/02/02 16:21:45 joerg Exp $"); +__RCSID("$NetBSD: plist.c,v 1.18 2008/04/26 14:56:34 joerg Exp $"); #endif #endif @@ -539,6 +539,7 @@ delete_package(Boolean ign_err, Boolean nukedirs, package_t *pkg, Boolean NoDele if (NoDeleteFiles) break; format_cmd(tmp, sizeof(tmp), p->name, Where, last_file); + /* XXX cleanup(0); */ printf("Executing `%s'\n", tmp); if (!Fake && system(tmp)) { warnx("unexec command for `%s' failed", tmp); diff --git a/pkgtools/pkg_install/files/lib/str.c b/pkgtools/pkg_install/files/lib/str.c index d8229cea050..be5a156319a 100644 --- a/pkgtools/pkg_install/files/lib/str.c +++ b/pkgtools/pkg_install/files/lib/str.c @@ -1,4 +1,4 @@ -/* $NetBSD: str.c,v 1.22 2008/04/18 17:16:44 joerg Exp $ */ +/* $NetBSD: str.c,v 1.23 2008/04/26 14:56:34 joerg Exp $ */ #if HAVE_CONFIG_H #include "config.h" @@ -11,7 +11,7 @@ #if 0 static const char *rcsid = "Id: str.c,v 1.5 1997/10/08 07:48:21 charnier Exp"; #else -__RCSID("$NetBSD: str.c,v 1.22 2008/04/18 17:16:44 joerg Exp $"); +__RCSID("$NetBSD: str.c,v 1.23 2008/04/26 14:56:34 joerg Exp $"); #endif #endif @@ -106,44 +106,3 @@ ispkgpattern(const char *pkg) { return strpbrk(pkg, "<>[]?*{") != NULL; } - -/* - * Strip off any .tgz, .tbz or .t[bg]z suffix from fname, - * and copy into buffer "buf", the suffix is stored in "sfx" - * if "sfx" is not NULL. If no suffix is found, "sfx" is set - * to an empty string. - */ -void -strip_txz(char *buf, char *sfx, const char *fname) -{ - static const char *const suffixes[] = { - ".tgz", ".tbz", ".t[bg]z", 0}; - const char *const *suffixp; - size_t len; - - len = strlen(fname); - assert(len < PKG_PATTERN_MAX); - - if (sfx) - sfx[0] = '\0'; - - for (suffixp = suffixes; *suffixp; suffixp++) { - size_t suffixlen = strlen(*suffixp); - - if (memcmp(&fname[len - suffixlen], *suffixp, suffixlen)) - continue; - - /* matched! */ - memcpy(buf, fname, len - suffixlen); - buf[len - suffixlen] = 0; - if (sfx) { - if (suffixlen >= PKG_SUFFIX_MAX) - errx(EXIT_FAILURE, "too long suffix '%s'", fname); - memcpy(sfx, *suffixp, suffixlen+1); - return; - } - } - - /* not found */ - memcpy(buf, fname, len+1); -} -- cgit v1.2.3