summaryrefslogtreecommitdiff
path: root/pkgtools
diff options
context:
space:
mode:
authorjoerg <joerg@pkgsrc.org>2008-04-26 17:40:01 +0000
committerjoerg <joerg@pkgsrc.org>2008-04-26 17:40:01 +0000
commit451f0436c4ce0abb533c77aa0d0a86f84616a957 (patch)
treedce0a9974c00fc1ce3fa404ec789177cd84fcdea /pkgtools
parent60ab8afe98e38742fb58ef681551c50c6c57d6a6 (diff)
downloadpkgsrc-451f0436c4ce0abb533c77aa0d0a86f84616a957.tar.gz
Revert last change, it was not intended to go into HEAD.
Diffstat (limited to 'pkgtools')
-rw-r--r--pkgtools/pkg_install/files/add/Makefile.in8
-rw-r--r--pkgtools/pkg_install/files/add/add.h9
-rw-r--r--pkgtools/pkg_install/files/add/extract.c340
-rw-r--r--pkgtools/pkg_install/files/add/futil.c151
-rw-r--r--pkgtools/pkg_install/files/add/main.c18
-rw-r--r--pkgtools/pkg_install/files/add/perform.c1897
-rw-r--r--pkgtools/pkg_install/files/add/pkg_add.138
-rw-r--r--pkgtools/pkg_install/files/create/perform.c36
-rw-r--r--pkgtools/pkg_install/files/info/Makefile.in4
-rw-r--r--pkgtools/pkg_install/files/info/perform.c29
-rw-r--r--pkgtools/pkg_install/files/lib/Makefile.in8
-rw-r--r--pkgtools/pkg_install/files/lib/conflicts.c9
-rw-r--r--pkgtools/pkg_install/files/lib/file.c429
-rw-r--r--pkgtools/pkg_install/files/lib/ftpio.c1259
-rw-r--r--pkgtools/pkg_install/files/lib/lib.h90
-rw-r--r--pkgtools/pkg_install/files/lib/pen.c206
-rw-r--r--pkgtools/pkg_install/files/lib/pexec.c110
-rw-r--r--pkgtools/pkg_install/files/lib/pkg_io.c185
-rw-r--r--pkgtools/pkg_install/files/lib/plist.c5
-rw-r--r--pkgtools/pkg_install/files/lib/str.c45
20 files changed, 3587 insertions, 1289 deletions
diff --git a/pkgtools/pkg_install/files/add/Makefile.in b/pkgtools/pkg_install/files/add/Makefile.in
index 3cf26e92a48..39cdc1b6767 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.17 2008/04/26 14:56:33 joerg Exp $
+# $NetBSD: Makefile.in,v 1.18 2008/04/26 17:40:01 joerg Exp $
srcdir= @srcdir@
@@ -13,9 +13,9 @@ cat1dir= $(mandir)/cat1
CC= @CC@
CCLD= $(CC)
-LIBS= -linstall -lfetch -larchive -lbz2 -lz @LIBS@
+LIBS= -linstall @LIBS@
CPPFLAGS= @CPPFLAGS@ -I. -I$(srcdir) -I../lib
-DEFS= @DEFS@ -DOPSYS_NAME=\"$(OPSYS)\" -DMACHINE_ARCH=\"$(MACHINE_ARCH)\" -DBINDIR=\"$(sbindir)\"
+DEFS= @DEFS@ -DOPSYS_NAME=\"$(OPSYS)\" -DMACHINE_ARCH=\"$(MACHINE_ARCH)\" -DBINDIR=\"$(sbindir)\" -DTAR_CMD=\"@tar@\" -DPAX_CMD=\"@pax@\"
CFLAGS= @CFLAGS@
LDFLAGS= @LDFLAGS@ -L../lib
@@ -23,7 +23,7 @@ INSTALL= @INSTALL@
PROG= pkg_add
-OBJS= main.o perform.o verify.o
+OBJS= main.o perform.o futil.o extract.o verify.o
all: $(PROG)
diff --git a/pkgtools/pkg_install/files/add/add.h b/pkgtools/pkg_install/files/add/add.h
index 3f9002b5604..9fbba20125a 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.11 2008/04/26 14:56:33 joerg Exp $ */
+/* $NetBSD: add.h,v 1.12 2008/04/26 17:40:01 joerg Exp $ */
/* from FreeBSD Id: add.h,v 1.8 1997/02/22 16:09:15 peter Exp */
@@ -35,8 +35,15 @@ 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
new file mode 100644
index 00000000000..0a303e701dd
--- /dev/null
+++ b/pkgtools/pkg_install/files/add/extract.c
@@ -0,0 +1,340 @@
+/* $NetBSD: extract.c,v 1.18 2008/04/26 17:40:01 joerg Exp $ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+#if HAVE_SYS_QUEUE_H
+#include <sys/queue.h>
+#endif
+#ifndef lint
+#if 0
+static const char *rcsid = "FreeBSD - Id: extract.c,v 1.17 1997/10/08 07:45:35 charnier Exp";
+#else
+__RCSID("$NetBSD: extract.c,v 1.18 2008/04/26 17:40:01 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
+ *
+ * This is the package extraction code for the add module.
+ *
+ */
+
+#if HAVE_ERR_H
+#include <err.h>
+#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
new file mode 100644
index 00000000000..ee4c2984fd3
--- /dev/null
+++ b/pkgtools/pkg_install/files/add/futil.c
@@ -0,0 +1,151 @@
+/* $NetBSD: futil.c,v 1.11 2008/04/26 17:40:01 joerg Exp $ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#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.11 2008/04/26 17:40:01 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
+ *
+ * Miscellaneous file access utilities.
+ *
+ */
+
+#if HAVE_ERR_H
+#include <err.h>
+#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 1e68005cff9..0a722e9b168 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.15 2008/04/26 14:56:34 joerg Exp $ */
+/* $NetBSD: main.c,v 1.16 2008/04/26 17:40:01 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.15 2008/04/26 14:56:34 joerg Exp $");
+__RCSID("$NetBSD: main.c,v 1.16 2008/04/26 17:40:01 joerg Exp $");
#endif
#endif
@@ -60,6 +60,12 @@ 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
@@ -67,7 +73,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] [-W viewbase] [-w view]",
+ " [-s verification-type] [-t template] [-W viewbase] [-w view]",
" [[ftp|http]://[user[:password]@]host[:port]][/path/]pkg-name ...");
exit(1);
}
@@ -120,11 +126,13 @@ main(int argc, char **argv)
Prefix = optarg;
break;
-#if 0
case 's':
set_verification(optarg);
break;
-#endif
+
+ case 't':
+ strlcpy(FirstPen, optarg, sizeof(FirstPen));
+ break;
case 'u':
Replace++;
diff --git a/pkgtools/pkg_install/files/add/perform.c b/pkgtools/pkg_install/files/add/perform.c
index fa72d03576e..60696fb67ca 100644
--- a/pkgtools/pkg_install/files/add/perform.c
+++ b/pkgtools/pkg_install/files/add/perform.c
@@ -1,4 +1,5 @@
-/* $NetBSD: perform.c,v 1.71 2008/04/26 14:56:34 joerg Exp $ */
+/* $NetBSD: perform.c,v 1.72 2008/04/26 17:40:01 joerg Exp $ */
+
#if HAVE_CONFIG_H
#include "config.h"
#endif
@@ -6,1223 +7,1047 @@
#if HAVE_SYS_CDEFS_H
#include <sys/cdefs.h>
#endif
-__RCSID("$NetBSD: perform.c,v 1.71 2008/04/26 14:56:34 joerg Exp $");
-
-/*-
- * Copyright (c) 2003 Grant Beattie <grant@NetBSD.org>
- * Copyright (c) 2005 Dieter Baron <dillo@NetBSD.org>
- * Copyright (c) 2007 Roland Illig <rillig@NetBSD.org>
- * Copyright (c) 2008 Joerg Sonnenberger <joerg@NetBSD.org>
- * All rights reserved.
+#if HAVE_SYS_QUEUE_H
+#include <sys/queue.h>
+#endif
+#ifndef lint
+#if 0
+static const char *rcsid = "from FreeBSD Id: perform.c,v 1.44 1997/10/13 15:03:46 jkh Exp";
+#else
+__RCSID("$NetBSD: perform.c,v 1.72 2008/04/26 17:40:01 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.
+ * 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.
*
- * 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.
*/
-#include <sys/utsname.h>
-#include <archive.h>
-#include <archive_entry.h>
+#if HAVE_ASSERT_H
+#include <assert.h>
+#endif
+#if HAVE_ERR_H
#include <err.h>
+#endif
+#if HAVE_ERRNO_H
#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
+#endif
+#include "defs.h"
#include "lib.h"
#include "add.h"
+#include "verify.h"
-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;
-};
-
-struct pkg_task {
- const char *pkgname;
-
- const char *prefix;
- const char *install_prefix;
-
- char *logdir;
- char *other_version;
-
- package_t plist;
-
- struct pkg_meta meta_data;
+#if HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if HAVE_SYS_UTSNAME_H
+#include <sys/utsname.h>
+#endif
- struct archive *archive;
- struct archive_entry *entry;
+static char LogDir[MaxPathSize];
+static int zapLogDir; /* Should we delete LogDir? */
- char *buildinfo[BI_ENUM_COUNT];
+static package_t Plist;
+static char *Home;
- size_t dep_length, dep_allocated;
- char **dependencies;
-};
+static lfile_head_t files;
-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 },
+/* used in build information */
+enum {
+ Good,
+ Missing,
+ Warning,
+ Fatal
};
-static int pkg_do(const char *, int);
-
-static int
-mkdir_p(const char *path)
+static void
+normalise_platform(struct utsname *host_name)
{
- return fexec(MKDIR_CMD, "-p", path, (void *)NULL);
+#ifdef NUMERIC_VERSION_ONLY
+ size_t span;
+
+ span = strspn(host_name->release, "0123456789.");
+ host_name->release[span] = '\0';
+#endif
}
-/*
- * Read meta data from archive.
- * Bail out if a required entry is missing or entries are in the wrong order.
- */
+/* Read package build information */
static int
-read_meta_data(struct pkg_task *pkg)
+read_buildinfo(char **buildinfo)
{
- 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;
+ char *key;
+ char *line;
+ size_t len;
+ FILE *fp;
- if (descr->required_file)
- ++found_required;
-
- 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;
-
- 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';
+ if ((fp = fopen(BUILD_INFO_FNAME, "r")) == NULL) {
+ warnx("unable to open %s file.", BUILD_INFO_FNAME);
+ return 0;
}
- if (r != ARCHIVE_OK)
- pkg->entry = NULL;
+ while ((line = fgetln(fp, &len)) != NULL) {
+ if (line[len - 1] == '\n')
+ line[len - 1] = '\0';
- for (descr = pkg_meta_descriptors; descr->entry_filename; ++descr) {
- if (descr->required_file)
- --found_required;
- }
+ if ((key = strsep(&line, "=")) == NULL)
+ continue;
- return !found_required ? 0 : -1;
-}
+ /*
+ * 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);
-/*
- * 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;
+ /*
+ * 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);
+ }
+ }
+ (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 1;
}
-/*
- * Parse PLIST and populate pkg.
- */
static int
-pkg_parse_plist(struct pkg_task *pkg)
+sanity_check(const char *pkg)
{
- 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;
+ 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;
}
- /* 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;
+ return errc;
}
+/* install a pre-requisite package. Returns 1 if it installed it */
static int
-check_already_installed(struct pkg_task *pkg)
+installprereq(const char *name, int *errc, int doupdate)
{
- char *filename;
- int fd;
-
- if (Force)
- return -1;
-
- filename = pkgdb_pkg_file(pkg->pkgname, CONTENTS_FNAME);
- fd = open(filename, O_RDONLY);
- free(filename);
- if (fd == -1)
- return -1;
+ int ret;
+ ret = 0;
- /* 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);
+ 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);
} else {
- warnx("package `%s' already recorded as installed",
- pkg->pkgname);
+ ret = 1;
}
- return 0;
+ return ret;
}
static int
-check_other_installed(struct pkg_task *pkg)
+pkg_do_installed(int *replacing, char replace_via[MaxPathSize], char replace_to[MaxPathSize],
+ Boolean is_depoted_pkg, const char *dbdir)
{
- FILE *f, *f_pkg;
- size_t len;
- char *pkgbase, *iter, *filename;
- package_t plist;
- plist_t *p;
- int status;
+ char replace_from[MaxPathSize];
+ char *s;
+ char buf[MaxPathSize];
+ char *best_installed;
- if ((pkgbase = strdup(pkg->pkgname)) == NULL) {
- warnx("strdup failed");
- return -1;
- }
- 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;
+ const size_t replace_via_size = MaxPathSize;
+ const size_t replace_to_size = MaxPathSize;
- 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);
+ if ((s = strrchr(PkgName, '-')) == NULL) {
+ warnx("Package name %s does not contain a version, bailing out", PkgName);
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)
+ return 0;
- 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;
+ 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 */
}
- warnx("Can't open +REQUIRED_BY of %s", pkg->other_version);
- 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;
+ /* 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);
+
+ 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;
}
- 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.
+ 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())
*/
- 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;
+ 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;
+ }
+ }
+ }
}
- free_plist(&plist);
- }
+ fclose(rb);
- fclose(f);
-
- return status;
-}
-
-/*
- * 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;
+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);
- 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;
+ *replacing = 1;
}
- 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);
+ if (Verbose) {
+ printf("%s/pkg_delete -K %s '%s'\n",
+ BINDIR, dbdir, best_installed);
}
+ fexec(BINDIR "/pkg_delete", "-K", dbdir, best_installed, NULL);
- return 0;
-}
+ free(best_installed);
-/*
- * Free buildinfo.
- */
-static void
-free_buildinfo(struct pkg_task *pkg)
-{
- size_t i;
-
- for (i = 0; i < BI_ENUM_COUNT; ++i) {
- free(pkg->buildinfo[i]);
- pkg->buildinfo[i] = NULL;
- }
+ return 0;
}
/*
- * Write meta data files to pkgdb after creating the directory.
+ * Install a single package
+ * Returns 0 if everything is ok, >0 else
*/
static int
-write_meta_data(struct pkg_task *pkg)
+pkg_do(const char *pkg, lpkg_head_t *pkgs)
{
- const struct pkg_meta_desc *descr;
- char *filename, **target;
- size_t len;
- ssize_t ret;
- int fd;
-
- if (Fake)
- return 0;
-
- if (mkdir_p(pkg->logdir)) {
- warn("Can't create pkgdb entry: %s", pkg->logdir);
- return -1;
+ 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;
}
- 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;
+ pkg = tmppkg;
+
+ if (IS_URL(pkg)) {
+ Home = fileGetURL(pkg);
+ if (Home == NULL) {
+ warnx("unable to fetch `%s' by URL", pkg);
}
- (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;
+
+ /* make sure the pkg is verified */
+ if (!verify(pkg)) {
+ warnx("Package %s will not be extracted", pkg);
+ goto bomb;
}
- 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;
+ } 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;
+ }
}
- len -= ret;
- } while (ret > 0);
- if (close(fd) == -1) {
- warn("Can't close 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 */
}
- 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;
+ 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);
}
- 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;
+ if (result) {
+ warnx("unable to extract table of contents file from `%s' - not a package?",
+ pkg);
+ goto bomb;
}
}
-}
-/*
- * Extract package.
- * Any misordered, missing or unlisted file in the package is an error.
- */
-
-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;
+ 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);
-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;
+ 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.
+ */
- if (Fake)
- return 0;
+ 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;
+ }
- if (mkdir_p(pkg->install_prefix)) {
- warn("Can't create prefix: %s", pkg->install_prefix);
- return -1;
+ /* Finally unpack the whole mess */
+ if (unpack(pkg, NULL)) {
+ warnx("unable to extract `%s'!", pkg);
+ goto bomb;
+ }
}
- if (chdir(pkg->install_prefix) == -1) {
- warn("Can't change into prefix: %s", pkg->install_prefix);
- return -1;
- }
+ /* Check for sanity */
+ if (sanity_check(pkg))
+ goto bomb;
- if (!NoRecord && !pkgdb_open(ReadWrite)) {
- warn("Can't open pkgdb for writing");
- 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;
+ }
}
- 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;
-
- 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;
-
- case PLIST_CHMOD:
- permissions = p->name;
- continue;
-
- case PLIST_CHOWN:
- owner = p->name;
- continue;
-
- case PLIST_CHGRP:
- group = p->name;
- continue;
+ if (uname(&host_uname) < 0) {
+ warnx("uname() failed.");
+ if (!Force) {
+ warnx("aborting.");
+ goto bomb;
+ }
+ } else {
+ int status = Good;
- case PLIST_IGNORE:
- p = p->next;
- continue;
+ normalise_platform(&host_uname);
- default:
- 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;
}
-
- 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 (buildinfo[BI_MACHINE_ARCH] == NULL) {
+ warnx("Missing machine architecture value from build information");
+ status = Missing;
}
-
- 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);
+ if (buildinfo[BI_OS_VERSION] == NULL) {
+ warnx("Missing operating system version value from build information");
+ status = Missing;
}
- r = copy_data_to_disk(pkg->archive, writer,
- archive_entry_pathname(pkg->entry));
- if (r)
- goto out;
- if (Verbose)
- printf("\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 (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);
+ }
}
- }
- if (pkg->entry != NULL) {
- warnx("Package contains entries not in PLIST: %s",
- archive_entry_pathname(pkg->entry));
- goto out;
+ if (!Force && status == Fatal) {
+ warnx("aborting.");
+ 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);
+ /* Check if USE_ABI_DEPENDS or IGNORE_RECOMMENDED was set
+ * when this package was built. IGNORE_RECOMMENDED is historical. */
- 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);
+ 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");
}
- 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;
- }
+ /*
+ * 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);
}
- 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;
+ /* 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;
+ }
+ 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;
+ }
+ (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);
}
- 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);
+ /* 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);
setenv(PKG_REFCOUNT_DBDIR_VNAME, pkgdb_refcount_dir(), 1);
-
- 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;
+
+ /* 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);
+ }
}
- ret = 0;
-
- if (chdir(pkg->logdir) == -1) {
- warn("Can't change to %s", pkg->logdir);
- ret = -1;
+ /* 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 */
}
- 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 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;
}
- free(filename);
- return ret;
-}
+ /* See if there are conflicting packages installed */
+ for (p = Plist.head; p; p = p->next) {
+ char *best_installed;
-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;
- } else if (p->type != PLIST_PKGCFL)
+ if (p->type != PLIST_PKGCFL)
continue;
- installed = find_best_matching_installed_pkg(p->name);
- if (installed) {
+ if (Verbose)
+ printf("Package `%s' conflicts with `%s'.\n", PkgName, p->name);
+ best_installed = find_best_matching_installed_pkg(p->name);
+ if (best_installed) {
warnx("Package `%s' conflicts with `%s', and `%s' is installed.",
- pkg->pkgname, p->name, installed);
- free(installed);
- status = -1;
+ PkgName, p->name, best_installed);
+ free(best_installed);
+ ++errc;
}
}
- 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;
- }
+ /* See if any of the installed packages conflicts with this one. */
+ {
+ char *inst_pkgname, *inst_pattern;
- 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
+ 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++;
+ }
}
- 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_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)
+ /* 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)
continue;
-
- warnx("Conflicting PLIST with %s: %s", existing, p->name);
- if (!Force) {
- status = -1;
- if (!Verbose)
- break;
+ 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);
}
}
-
- 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)
+ 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)
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) {
- /* XXX check cyclic dependencies? */
- if (Fake || NoRecord) {
- if (!Force) {
- warnx("Missing dependency %s\n",
- p->name);
- status = -1;
- break;
+ /* 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;
}
- warnx("Missing dependency %s, continuing",
- p->name);
- continue;
- }
- 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. */
+ } else {
+ if (Verbose)
+ printf(" - %s already installed.\n", best_installed);
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;
}
- return status;
-}
+ if (errc != 0)
+ goto bomb;
-/*
- * 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);
+ /* 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 */
+ }
}
- 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);
-
+ /*
+ * 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.
+ */
if (!Fake) {
- 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;
+ _pkgdb_setPKGDB_DIR(dbdir);
+ if (!extract_plist(".", &Plist)) {
+ 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 && fexists(MTREE_FNAME)) {
+ warnx("Mtree file ignored for package %s", PkgName);
}
- if (!Fake)
- fexec(BINDIR "/pkg_delete", "-K", _pkgdb_getPKGDB_DIR(),
- "-p", pkg->install_prefix,
- pkg->other_version, NULL);
-
- /* XXX Check return value and do what? */
- return 0;
-}
-/*
- * Install a single package.
- */
-static int
-pkg_do(const char *pkgpath, int mark_automatic)
-{
- int status;
- void *archive_cookie;
- struct pkg_task *pkg;
-
- if ((pkg = calloc(1, sizeof(*pkg))) == NULL)
- err(2, "malloc failed");
-
- status = -1;
-
- if ((pkg->archive = find_archive(pkgpath, &archive_cookie)) == NULL) {
- warnx("no pkg found for '%s', sorry.", pkgpath);
- goto clean_memory;
+ /* 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 (read_meta_data(pkg))
- goto clean_memory;
-
- /* 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);
+ /* Time to record the deed? */
+ if (!NoRecord && !Fake) {
+ char contents[MaxPathSize];
- 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");
- }
+ 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;
- if (NoRecord && !Fake) {
- const char *tmpdir;
+ s = find_best_matching_installed_pkg(p->name);
- tmpdir = getenv("TMPDIR");
- if (tmpdir == NULL)
- tmpdir = "/tmp";
+ if (s == NULL)
+ errx(EXIT_FAILURE, "Where did our dependency go?!");
- 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;
+ (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 (check_already_installed(pkg) == 0) {
- status = 0;
- 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);
}
- if (read_buildinfo(pkg))
- goto clean_memory;
+ /* 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);
+ }
+
+ fexec_skipempty(BINDIR "/pkg_view", "-d", dbdir,
+ View ? "-w " : "", View ? View : "",
+ Viewbase ? "-W " : "", Viewbase ? Viewbase : "",
+ Verbose ? "-v " : "", "add", PkgName, NULL);
+ }
- if (check_platform(pkg))
- goto clean_memory;
+ goto success;
- if (check_other_installed(pkg))
- goto clean_memory;
+bomb:
+ errc = 1;
+ goto success;
- if (check_explicit_conflict(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_implicit_conflict(pkg))
- goto clean_memory;
+success:
+ /* delete the packing list contents */
+ free_plist(&Plist);
+ leave_playpen(Home);
- if (pkg->other_version != NULL) {
+ if (replacing) {
/*
- * Replacing an existing package.
- * Write meta-data, get rid of the old version,
- * install/update dependencies and finally extract.
+ * Upgrade step 3/4: move back +REQUIRED_BY file
+ * (see also step 2/4)
*/
- if (write_meta_data(pkg))
- goto nuke_pkgdb;
-
- if (start_replacing(pkg))
- goto nuke_pkgdb;
-
- if (check_dependencies(pkg))
- goto nuke_pkgdb;
- } else {
+ if (rename(replace_via, replace_to) != 0)
+ err(EXIT_FAILURE, "renaming \"%s\" to \"%s\" failed", replace_via, replace_to);
+
/*
- * 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;
- }
-
- if (run_install_script(pkg, "PRE-INSTALL"))
- goto nuke_pkgdb;
-
- 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);
+ * 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 */
}
-nuke_pkgdb:
- if (!Fake) {
- (void) fexec(REMOVE_CMD, "-fr", pkg->logdir, (void *)NULL);
- free(pkg->logdir);
- pkg->logdir = NULL;
- }
+ return errc;
+}
-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);
+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);
}
- free(pkg->other_version);
- free(pkg);
- return status;
+ signal(SIGINT, oldint);
+ signal(SIGHUP, oldhup);
+ errno = saved_errno;
}
int
pkg_perform(lpkg_head_t *pkgs)
{
- int errors = 0;
+ int err_cnt = 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);
- if (pkg_do(lpp->lp_name, Automatic))
- ++errors;
+ err_cnt += pkg_do(lpp->lp_name, pkgs);
path_prepend_clear();
TAILQ_REMOVE(pkgs, lpp, lp_link);
free_lpkg(lpp);
}
-
- return errors;
+
+ ftp_stop();
+
+ return err_cnt;
}
diff --git a/pkgtools/pkg_install/files/add/pkg_add.1 b/pkgtools/pkg_install/files/add/pkg_add.1
index 69df18cfb43..ced8fd613c9 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.29 2008/04/26 14:56:34 joerg Exp $
+.\" $NetBSD: pkg_add.1,v 1.30 2008/04/26 17:40:01 joerg Exp $
.\"
.\" FreeBSD install - a package for the installation and maintenance
.\" of non-core utilities.
@@ -30,6 +30,7 @@
.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 ...
@@ -159,8 +160,6 @@ 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
@@ -184,6 +183,30 @@ 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.
@@ -252,10 +275,11 @@ passive mode
ftp.
.Sh TECHNICAL DETAILS
.Nm
-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:
+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:
.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 7c8d1b29e5f..066a8898c21 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.20 2008/04/26 14:56:34 joerg Exp $ */
+/* $NetBSD: perform.c,v 1.21 2008/04/26 17:40:01 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.20 2008/04/26 14:56:34 joerg Exp $");
+__RCSID("$NetBSD: perform.c,v 1.21 2008/04/26 17:40:01 joerg Exp $");
#endif
#endif
@@ -91,38 +91,6 @@ register_depends(package_t *plist, char *deps, int build_only)
}
/*
- * 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
*/
static void
diff --git a/pkgtools/pkg_install/files/info/Makefile.in b/pkgtools/pkg_install/files/info/Makefile.in
index fed22801e5a..967ead0f550 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.15 2008/04/26 14:56:34 joerg Exp $
+# $NetBSD: Makefile.in,v 1.16 2008/04/26 17:40:01 joerg Exp $
srcdir= @srcdir@
@@ -16,7 +16,7 @@ BOOTSTRAP= @bootstrap@
CC= @CC@
CCLD= $(CC)
.if empty(BOOTSTRAP)
-LIBS= -linstall -larchive -lfetch -lbz2 -lz @LIBS@
+LIBS= -linstall -larchive -lbz2 -lfetch -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 2acde1f1337..cdf4b563f6b 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.47 2008/04/26 14:56:34 joerg Exp $ */
+/* $NetBSD: perform.c,v 1.48 2008/04/26 17:40:01 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.47 2008/04/26 14:56:34 joerg Exp $");
+__RCSID("$NetBSD: perform.c,v 1.48 2008/04/26 17:40:01 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,19 +302,30 @@ pkg_do(const char *pkg)
int code = 0;
const char *binpkgfile = NULL;
- if (IS_URL(pkg) || (fexists(pkg) && isfile(pkg))) {
+ 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)) {
#ifdef BOOTSTRAP
errx(2, "Binary packages not supported during bootstrap");
#else
struct archive *archive;
- void *archive_cookie;
+ void *remote_archive_cookie;
- archive = open_archive(pkg, &archive_cookie);
+ archive = open_local_archive(pkg, &remote_archive_cookie);
meta = read_meta_data_from_archive(archive);
- close_archive(archive_cookie);
- if (!IS_URL(pkg))
- binpkgfile = pkg;
+ close_local_archive(remote_archive_cookie);
+ binpkgfile = pkg;
#endif
} else {
/*
diff --git a/pkgtools/pkg_install/files/lib/Makefile.in b/pkgtools/pkg_install/files/lib/Makefile.in
index 8a10272a905..e513a89a7d4 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.22 2008/04/26 14:56:34 joerg Exp $
+# $NetBSD: Makefile.in,v 1.23 2008/04/26 17:40:01 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)\"
+DEFS= @DEFS@ -DDEF_LOG_DIR=\"$(pkgdbdir)\" -DTAR_CMD=\"$(tar)\" -DFTP_CMD=\"$(ftp)\"
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 \
- global.o iterate.o lpkg.o opattern.o \
- path.o pkgdb.o plist.o \
+ ftpio.o global.o iterate.o lpkg.o opattern.o \
+ path.o pen.o pexec.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 01468ce0ff6..35dd204ccf7 100644
--- a/pkgtools/pkg_install/files/lib/conflicts.c
+++ b/pkgtools/pkg_install/files/lib/conflicts.c
@@ -30,7 +30,6 @@
*/
struct package_conflict {
const char *pkgname;
- const char *skip_pkgname;
char **conflicting_pkgname;
char **conflicting_pattern;
};
@@ -71,10 +70,6 @@ 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;
@@ -107,14 +102,12 @@ check_package_conflict(const char *pkgname, void *v)
* variables are set to NULL.
*/
int
-some_installed_package_conflicts_with(const char *pkgname,
- const char *skip_pkgname, char **inst_pkgname, char **inst_pattern)
+some_installed_package_conflicts_with(const char *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 17322df70e3..47146a31cac 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.24 2008/04/26 14:56:34 joerg Exp $ */
+/* $NetBSD: file.c,v 1.25 2008/04/26 17:40:01 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.24 2008/04/26 14:56:34 joerg Exp $");
+__RCSID("$NetBSD: file.c,v 1.25 2008/04/26 17:40:01 joerg Exp $");
#endif
#endif
@@ -218,6 +218,263 @@ URLlength(const char *fname)
}
/*
+ * 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.
*/
@@ -252,6 +509,97 @@ 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<globbed.gl_pathc; i++)
+ if (fexec("mv", globbed.gl_pathv[i], to, NULL)) {
+ cleanup(0);
+ errx(2, "could not perform 'mv %s %s'", globbed.gl_pathv[i], to);
+ }
+
+ return;
+}
+
void
remove_files(const char *path, const char *pattern)
{
@@ -287,6 +635,74 @@ remove_files(const char *path, const char *pattern)
}
/*
+ * Unpack a tar file
+ */
+int
+unpack(const char *pkg, const lfile_head_t *filesp)
+{
+ const char *decompress_cmd = NULL;
+ const char *suf;
+ int count = 0;
+ lfile_t *lfp;
+ char **up_argv;
+ int up_argc = 7;
+ int i = 0;
+ int result;
+
+ if (filesp != NULL)
+ TAILQ_FOREACH(lfp, filesp, lf_link)
+ count++;
+ up_argc += count;
+ up_argv = malloc((count + up_argc + 1) * sizeof(char *));
+ if (!IS_STDIN(pkg)) {
+ suf = suffix_of(pkg);
+ if (!strcmp(suf, "tbz") || !strcmp(suf, "bz2"))
+ decompress_cmd = BZIP2_CMD;
+ else if (!strcmp(suf, "tgz") || !strcmp(suf, "gz"))
+ decompress_cmd = GZIP_CMD;
+ else if (!strcmp(suf, "tar"))
+ ; /* do nothing */
+ else
+ errx(EXIT_FAILURE, "don't know how to decompress %s, sorry", pkg);
+ } else
+ decompress_cmd = GZIP_CMD;
+
+ up_argv[i] = (char *)strrchr(TAR_CMD, '/');
+ if (up_argv[i] == NULL)
+ up_argv[i] = TAR_CMD;
+ else
+ up_argv[i]++; /* skip / character */
+ if (count > 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:
*
* %F With the parameter "name"
@@ -296,8 +712,8 @@ remove_files(const char *path, const char *pattern)
*
* Check that no overflows can occur.
*/
-int
-format_cmd(char *buf, size_t size, const char *fmt, const char *dir, const char *name)
+void
+format_cmd(char *buf, size_t size, char *fmt, char *dir, char *name)
{
char scratch[MaxPathSize * 2];
char *bufp;
@@ -306,8 +722,8 @@ format_cmd(char *buf, size_t size, const char *fmt, const char *dir, const char
for (bufp = buf; (int) (bufp - buf) < size && *fmt;) {
if (*fmt == '%') {
if (*++fmt != 'D' && name == NULL) {
- warnx("no last file available for '%s' command", buf);
- return -1;
+ cleanup(0);
+ errx(2, "no last file available for '%s' command", buf);
}
switch (*fmt) {
case 'F':
@@ -352,5 +768,4 @@ format_cmd(char *buf, size_t size, const char *fmt, const char *dir, const char
}
}
*bufp = '\0';
- return 0;
}
diff --git a/pkgtools/pkg_install/files/lib/ftpio.c b/pkgtools/pkg_install/files/lib/ftpio.c
new file mode 100644
index 00000000000..17142056b26
--- /dev/null
+++ b/pkgtools/pkg_install/files/lib/ftpio.c
@@ -0,0 +1,1259 @@
+/* $NetBSD: ftpio.c,v 1.28 2008/04/26 17:40:01 joerg Exp $ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+#ifndef lint
+__RCSID("$NetBSD: ftpio.c,v 1.28 2008/04/26 17:40:01 joerg 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 <hubert@feyrer.de> 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 <sys/types.h>
+#endif
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#if HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#endif
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#if HAVE_ASSERT_H
+#include <assert.h>
+#endif
+#if HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#if HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#if HAVE_REGEX_H
+#include <regex.h>
+#endif
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if HAVE_TERMCAP_H
+#include <termcap.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#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 -- "<a" already found */
+ if (p == '>')
+ state = ST_NONE;
+ else if (isspace((unsigned char)p))
+ state = ST_TAGA;
+ else
+ state = ST_TAG;
+ break;
+ case ST_TAG:
+ /* in tag, but not "<a" -- disregard */
+ if (p == '>')
+ state = ST_NONE;
+ break;
+ case ST_TAGA:
+ /* in a-tag -- "<a " already found */
+ if (p == '>')
+ 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 -- "<a h" already found */
+ if (p == '>')
+ 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 -- "<a hr" already found */
+ if (p == '>')
+ 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 -- "<a hre" already found */
+ if (p == '>')
+ 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 -- "<a href" already found */
+ if (p == '>')
+ 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 -- "<a href=" already found */
+ /* XXX: handle missing double quotes? */
+ if (p == '>')
+ state = ST_NONE;
+ /* skip spaces before URL */
+ else if (!isspace((unsigned char)p))
+ state = ST_TAGA;
+ break;
+ /* no default case by purpose */
+ }
+ }
+
+ if (p == '\0')
+ return -1;
+
+ if (found) {
+ char *q;
+
+ URL_decode(tempbuf);
+
+ /* strip path (XXX) */
+ if ((q=strrchr(tempbuf, '/')) == NULL)
+ q = tempbuf;
+
+ (void)strlcpy(outbuf, q, outbuflen);
+ tempbuffill = 0;
+ }
+
+ return offset;
+}
+
+
+static int
+hexvalue(char p)
+{
+ if (p >= '0' && p <= '9')
+ return (p-'0');
+ else if (p >= 'a' && p <= 'f')
+ return (p-'a'+10);
+ else if (p >= 'A' && p <= 'F')
+ return (p-'A'+10);
+ else
+ return -1;
+}
+
+/* fetch and extract URL url into directory path */
+static int
+http_fetch(const char *url, const char *path)
+{
+ int pipefds[2];
+ int stateftp, state;
+ pid_t pidftp, pid;
+
+ /* Set up a pipe for passing the fetched contents. */
+ if (pipe(pipefds) == -1) {
+ warn("cannot create pipe");
+ return -1;
+ }
+ /* fork ftp child */
+ if ((pidftp = fork()) == -1) {
+ warn("cannot fork process for ftp");
+ return -1;
+ }
+ if (pidftp == 0) {
+ /* child */
+ if (dup2(pipefds[1], STDOUT_FILENO) == -1) {
+ warn("dup2 failed before executing ftp");
+ _exit(2);
+ }
+ close(pipefds[0]);
+ close(pipefds[1]);
+ execlp(FTP_CMD, FTP_CMD, "-o", "-", url, NULL);
+ warnx("failed to execute ftp");
+ _exit(2);
+ }
+
+ /* fork unpack child */
+ if ((pid = fork()) == -1) {
+ warn("cannot fork unpack process");
+ return -1;
+ }
+ if (pid == 0) {
+ /* child */
+ if (dup2(pipefds[0], STDIN_FILENO) == -1) {
+ warn("dup2 failed before unpack");
+ _exit(2);
+ }
+ close(pipefds[0]);
+ close(pipefds[1]);
+ if ((path != NULL) && (chdir(path) < 0))
+ _exit(127);
+
+ if (unpack("-", NULL) != 0) {
+ warnx("unpack failed");
+ _exit(2);
+ }
+
+ _exit(0);
+ }
+
+ close(pipefds[0]);
+ close(pipefds[1]);
+
+ /* wait for unpack to exit */
+ while (waitpid(pid, &state, 0) < 0) {
+ if (errno != EINTR) {
+ (void)waitpid(pidftp, &stateftp, 0);
+ return -1;
+ }
+ }
+ while (waitpid(pidftp, &stateftp, 0) < 0) {
+ if (errno != EINTR) {
+ return -1;
+ }
+ }
+
+ if (!WIFEXITED(state) || !WIFEXITED(stateftp))
+ return -1;
+
+ if (WEXITSTATUS(state) != 0 || WEXITSTATUS(stateftp) != 0)
+ return -1;
+
+ return 0;
+}
+
+static void
+URL_decode(char *URL)
+{
+ char *in, *out;
+
+ in = out = URL;
+
+ while (*in != '\0') {
+ if (in[0] == '%' && in[1] != '\0' && in[2] != '\0') {
+ /* URL-decode character */
+ if (hexvalue(in[1]) != -1 && hexvalue(in[2]) != -1) {
+ *out++ = hexvalue(in[1])*16+hexvalue(in[2]);
+ }
+ /* skip invalid encoded signs too */
+ in += 3;
+ }
+ else
+ *out++ = *in++;
+ }
+
+ *out = '\0';
+
+ return;
+}
+/*
+ * extract the given (expanded) URL "url" to the given directory "dir"
+ * return -1 on error, 0 else;
+ */
+int
+unpackURL(const char *url, const char *dir)
+{
+ char *pkg;
+ int rc;
+ char base[MaxPathSize];
+ char pkg_path[MaxPathSize];
+
+ {
+ /* Verify if the URL is really ok */
+ char expnd[MaxPathSize];
+
+ rc=expandURL(expnd, url);
+ if (rc == -1) {
+ warnx("unpackURL: verification expandURL failed");
+ return -1;
+ }
+ if (strcmp(expnd, url) != 0) {
+ warnx("unpackURL: verification expandURL failed, '%s'!='%s'",
+ expnd, url);
+ return -1;
+ }
+ }
+
+ pkg=strrchr(url, '/');
+ if (pkg == NULL){
+ warnx("unpackURL: no '/' in URL %s?!", url);
+ return -1;
+ }
+ (void) snprintf(base, sizeof(base), "%.*s/", (int)(pkg-url), url);
+ (void) snprintf(pkg_path, sizeof(pkg_path), "%.*s",
+ (int)(pkg-url), url); /* no trailing '/' */
+ pkg++;
+
+ /* Leave a hint for any depending pkgs that may need it */
+ if (getenv("PKG_PATH") == NULL) {
+ setenv("PKG_PATH", pkg_path, 1);
+#if 0
+ path_create(pkg_path); /* XXX */
+#endif
+ if (Verbose)
+ printf("setenv PKG_PATH='%s'\n", pkg_path);
+ }
+
+ if (strncmp(url, "http://", 7) == 0)
+ return http_fetch(url, dir);
+
+ rc = ftp_start(base);
+ if (rc == -1) {
+ warnx("ftp_start() failed");
+ return -1; /* error */
+ }
+
+ {
+ char cmd[1024];
+ const char *decompress_cmd = NULL;
+ const char *suf;
+
+ if (Verbose)
+ printf("unpackURL '%s' to '%s'\n", url, dir);
+
+ suf = suffix_of(pkg);
+ if (!strcmp(suf, "tbz") || !strcmp(suf, "bz2"))
+ decompress_cmd = BZIP2_CMD;
+ else if (!strcmp(suf, "tgz") || !strcmp(suf, "gz"))
+ decompress_cmd = GZIP_CMD;
+ else if (!strcmp(suf, "tar"))
+ ; /* do nothing */
+ else
+ errx(EXIT_FAILURE, "don't know how to decompress %s, sorry", pkg);
+
+ /* yes, this is gross, but needed for borken ftp(1) */
+ (void) snprintf(cmd, sizeof(cmd), "get %s \"| ( cd %s; " TAR_CMD " %s %s -vvxp -f - | tee %s )\"\n",
+ pkg, dir,
+ decompress_cmd != NULL ? "--use-compress-program" : "",
+ decompress_cmd != NULL ? decompress_cmd : "",
+ Verbose ? "/dev/stderr" : "/dev/null");
+
+ rc = ftp_cmd(cmd, "\n(226|550).*\n");
+ if (rc != 226) {
+ warnx("Cannot fetch file (%d!=226)!", rc);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+#ifdef STANDALONE
+static void
+usage(void)
+{
+ errx(EXIT_FAILURE, "Usage: foo [-v] ftp://-pattern");
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ int rc, ch;
+ char *argv0 = argv[0];
+
+ while ((ch = getopt(argc, argv, "v")) != -1) {
+ switch (ch) {
+ case 'v':
+ Verbose=1;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc<1)
+ usage();
+
+ while(argv[0] != NULL) {
+ char newurl[MaxPathSize];
+
+ printf("Expand %s:\n", argv[0]);
+ rc = expandURL(newurl, argv[0]);
+ if (rc==-1)
+ warnx("Cannot expand %s", argv[0]);
+ else
+ printf("Expanded URL: %s\n", newurl);
+
+ /* test out connection caching */
+ if (1) {
+ char *s, buf[MaxPathSize];
+
+ if ((s=getenv(PKG_FTPIO_CNT)) && atoi(s)>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 e9ed05d68d7..35292dd327c 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.43 2008/04/26 14:56:34 joerg Exp $ */
+/* $NetBSD: lib.h,v 1.44 2008/04/26 17:40:01 joerg Exp $ */
/* from FreeBSD Id: lib.h,v 1.25 1997/10/08 07:48:03 charnier Exp */
@@ -86,18 +86,48 @@
#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
@@ -128,8 +158,13 @@ enum {
/* The names of special variables */
#define AUTOMATIC_VARNAME "automatic"
-/* Prefix for extended PLIST cmd */
-#define CMD_CHAR '@'
+/*
+ * 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 */
/* The name of the "prefix" environment variable given to scripts */
#define PKG_PREFIX_VNAME "PKG_PREFIX"
@@ -238,6 +273,14 @@ 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;
@@ -253,17 +296,25 @@ struct pkg_vulnerabilities {
#define IS_FULLPATH(str) ((str) != NULL && (str)[0] == '/')
/* Conflict handling (conflicts.c) */
-int some_installed_package_conflicts_with(const char *, const char *, char **, char **);
+int some_installed_package_conflicts_with(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 */
@@ -284,6 +335,7 @@ 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 *,
@@ -309,17 +361,35 @@ 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 format_cmd(char *, size_t, const char *, const char *, const char *);
+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);
/* pkg_io.c: Local and remote archive handling */
struct archive;
-struct archive *open_archive(const char *, void **);
-void close_archive(void *);
-struct archive *find_archive(const char *, void **);
+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 *);
/* 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
new file mode 100644
index 00000000000..65451e7e4e7
--- /dev/null
+++ b/pkgtools/pkg_install/files/lib/pen.c
@@ -0,0 +1,206 @@
+/* $NetBSD: pen.c,v 1.24 2008/04/26 17:40:01 joerg Exp $ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#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.24 2008/04/26 17:40:01 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 <err.h>
+#endif
+#include "lib.h"
+#if HAVE_SYS_SIGNAL_H
+#include <sys/signal.h>
+#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
new file mode 100644
index 00000000000..1358aedb910
--- /dev/null
+++ b/pkgtools/pkg_install/files/lib/pexec.c
@@ -0,0 +1,110 @@
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_STDIO_H
+#include <stdio.h>
+#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 0a87bfc5a64..e8496eb2c2f 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.2 2008/04/26 14:56:34 joerg Exp $ */
+/* $NetBSD: pkg_io.c,v 1.3 2008/04/26 17:40:01 joerg Exp $ */
/*-
* Copyright (c) 2008 Joerg Sonnenberger <joerg@NetBSD.org>.
* All rights reserved.
@@ -36,7 +36,7 @@
#include <sys/cdefs.h>
#endif
-__RCSID("$NetBSD: pkg_io.c,v 1.2 2008/04/26 14:56:34 joerg Exp $");
+__RCSID("$NetBSD: pkg_io.c,v 1.3 2008/04/26 17:40:01 joerg Exp $");
#include <archive.h>
#include <archive_entry.h>
@@ -52,7 +52,7 @@ __RCSID("$NetBSD: pkg_io.c,v 1.2 2008/04/26 14:56:34 joerg Exp $");
#include "lib.h"
struct fetch_archive {
- struct url *url;
+ const char *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 = fetchGet(f->url, "");
+ f->fetch = fetchGetURL(f->url, "");
if (f->fetch == NULL)
return ENOENT;
return 0;
@@ -88,181 +88,52 @@ fetch_archive_close(struct archive *a, void *client_data)
return 0;
}
-static struct archive *
-open_archive_by_url(struct url *url, void **cookie)
+struct archive *
+open_remote_archive(const char *url, void **cookie)
{
struct fetch_archive *f;
- struct archive *a;
+ struct archive *archive;
f = malloc(sizeof(*f));
if (f == NULL)
err(2, "cannot allocate memory for remote archive");
f->url = url;
- 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;
- }
+ 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));
*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;
- a = open_archive_by_url(u, cookie);
-
- fetchFreeURL(u);
- return a;
+ return archive;
}
void
-close_archive(void *cookie)
+close_remote_archive(void *cookie)
{
free(cookie);
}
-static int
-strip_suffix(char *filename)
-{
- size_t len;
-
- 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;
-}
-
-static int
-find_best_package(struct url *url, const char *pattern, struct url **best_url)
+struct archive *
+open_local_archive(const char *path, void **cookie)
{
- 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;
+ struct archive *archive;
- if (best_match && strip_suffix(best_match) == 0) {
- free(best_match);
- return -1;
- }
+ 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;
- 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;
+ return archive;
}
-struct archive *
-find_archive(const char *fname, void **cookie)
+void
+close_local_archive(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 8539272be75..0b24734012d 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.18 2008/04/26 14:56:34 joerg Exp $ */
+/* $NetBSD: plist.c,v 1.19 2008/04/26 17:40:01 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.18 2008/04/26 14:56:34 joerg Exp $");
+__RCSID("$NetBSD: plist.c,v 1.19 2008/04/26 17:40:01 joerg Exp $");
#endif
#endif
@@ -539,7 +539,6 @@ 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 be5a156319a..ccd00059b07 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.23 2008/04/26 14:56:34 joerg Exp $ */
+/* $NetBSD: str.c,v 1.24 2008/04/26 17:40:01 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.23 2008/04/26 14:56:34 joerg Exp $");
+__RCSID("$NetBSD: str.c,v 1.24 2008/04/26 17:40:01 joerg Exp $");
#endif
#endif
@@ -106,3 +106,44 @@ 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);
+}