./usr/man/man1/basename.1 minix-sys
./usr/man/man1/bdes.1 minix-sys
./usr/man/man1/bg.1 minix-sys
+./usr/man/man1/bpm.1 minix-sys
./usr/man/man1/break.1 minix-sys obsolete
./usr/man/man1/bsdtar.1 minix-sys
./usr/man/man1/bsfilt.1 minix-sys
./usr/man/man1/pathchk.1 minix-sys
./usr/man/man1/pax.1 minix-sys
./usr/man/man1/ping.1 minix-sys obsolete
+./usr/man/man1/pkg_add.1 minix-sys
+./usr/man/man1/pkg_admin.1 minix-sys
+./usr/man/man1/pkg_create.1 minix-sys
+./usr/man/man1/pkg_delete.1 minix-sys
+./usr/man/man1/pkg_info.1 minix-sys
./usr/man/man1/pkg_view.1 minix-sys obsolete
./usr/man/man1/playwave.1 minix-sys
./usr/man/man1/popd.1 minix-sys
./usr/preserve minix-sys
./usr/run minix-sys
./usr/sbin minix-sys
+./usr/sbin/audit-packages minix-sys
+./usr/sbin/bpm minix-sys
./usr/sbin/btrace minix-sys
./usr/sbin/chown minix-sys
./usr/sbin/chroot minix-sys
./usr/sbin/diskctl minix-sys
+./usr/sbin/download-vulnerability-list minix-sys
./usr/sbin/fbdctl minix-sys
./usr/sbin/group minix-sys
./usr/sbin/groupadd minix-sys
./usr/sbin/mkproto minix-sys
./usr/sbin/mtree minix-sys
./usr/sbin/newfs_mfs minix-sys
+./usr/sbin/pkg_add minix-sys
+./usr/sbin/pkg_admin minix-sys
+./usr/sbin/pkg_create minix-sys
+./usr/sbin/pkg_delete minix-sys
+./usr/sbin/pkg_info minix-sys
./usr/sbin/postinstall minix-sys
./usr/sbin/pwd_mkdb minix-sys
./usr/sbin/rdate minix-sys
SUBDIR+= atf
.endif
.if (${MKCRYPTO} != "no")
-#SUBDIR+= pkg_install
+SUBDIR+= pkg_install
.endif
# IP Filter
.if (${MKIPFILTER} != "no")
--- /dev/null
+# $NetBSD: Makefile,v 1.1 2008/09/30 19:19:56 joerg Exp $
+
+SUBDIR= lib .WAIT sbin
+
+.include <bsd.subdir.mk>
--- /dev/null
+# $NetBSD: Makefile.inc,v 1.4 2011/09/16 16:41:20 joerg Exp $
+
+DIST= ${NETBSDSRCDIR}/external/bsd/pkg_install/dist
+
+USE_FORT?=yes # network client
+
+CPPFLAGS+=-I${DIST}/lib
+CPPFLAGS+=-I${NETBSDSRCDIR}/external/bsd/pkg_install/lib
+CPPFLAGS+=-DHAVE_CONFIG_H -DNETBSD -DHAVE_SSL
+CPPFLAGS+=-DSYSCONFDIR='"/etc"'
+
+WARNS= 4
+CWARNFLAGS+= -Wno-missing-noreturn
--- /dev/null
+/* $NetBSD: add.h,v 1.1.1.7 2011/02/18 22:32:27 aymeric Exp $ */
+
+/* from FreeBSD Id: add.h,v 1.8 1997/02/22 16:09:15 peter Exp */
+
+/*
+ * 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
+ *
+ * Include and define various things wanted by the add command.
+ *
+ */
+
+#ifndef _INST_ADD_H_INCLUDE
+#define _INST_ADD_H_INCLUDE
+
+extern char *Destdir;
+extern char *OverrideMachine;
+extern char *Prefix;
+extern char *View;
+extern char *Viewbase;
+extern Boolean NoView;
+extern Boolean NoInstall;
+extern Boolean NoRecord;
+extern Boolean Force;
+extern Boolean Automatic;
+extern int LicenseCheck;
+extern int Replace;
+extern int ReplaceSame;
+
+extern Boolean ForceDepends;
+extern Boolean ForceDepending;
+
+int make_hierarchy(char *);
+void apply_perms(char *, char **, int);
+
+int pkg_perform(lpkg_head_t *);
+
+#endif /* _INST_ADD_H_INCLUDE */
--- /dev/null
+/* $NetBSD: main.c,v 1.1.1.10 2011/02/18 22:32:27 aymeric Exp $ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+__RCSID("$NetBSD: main.c,v 1.1.1.10 2011/02/18 22:32:27 aymeric Exp $");
+
+/*
+ *
+ * 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 add module.
+ *
+ */
+
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+#if HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#include "lib.h"
+#include "add.h"
+
+static char Options[] = "AC:DIK:LP:RVW:fhm:np:t:Uuvw:";
+
+char *Destdir = NULL;
+char *OverrideMachine = NULL;
+char *Prefix = NULL;
+char *View = NULL;
+char *Viewbase = NULL;
+Boolean NoView = FALSE;
+Boolean NoInstall = FALSE;
+Boolean NoRecord = FALSE;
+Boolean Automatic = FALSE;
+Boolean ForceDepends = FALSE;
+/*
+ * Normally, updating fails if the dependencies of a depending package
+ * are not satisfied by the package to be updated. ForceDepending
+ * turns that failure into a warning.
+ */
+Boolean ForceDepending = FALSE;
+
+int LicenseCheck = 0;
+int Replace = 0;
+int ReplaceSame = 0;
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr, "%s\n%s\n%s\n%s\n",
+ "usage: pkg_add [-AfhILnRuVv] [-C config] [-P destdir] [-K pkg_dbdir]",
+ " [-m machine] [-p prefix] [-s verification-type",
+ " [-W viewbase] [-w view]\n",
+ " [[ftp|http]://[user[:password]@]host[:port]][/path/]pkg-name ...");
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int ch, error=0;
+ lpkg_head_t pkgs;
+
+ setprogname(argv[0]);
+ while ((ch = getopt(argc, argv, Options)) != -1) {
+ switch (ch) {
+ case 'A':
+ Automatic = TRUE;
+ break;
+
+ case 'C':
+ config_file = optarg;
+ break;
+
+ case 'D':
+ ForceDepending = TRUE;
+ break;
+
+ case 'P':
+ Destdir = optarg;
+ break;
+
+ case 'f':
+ Force = TRUE;
+ ForceDepends = TRUE;
+ ForceDepending = TRUE;
+ break;
+
+ case 'I':
+ NoInstall = TRUE;
+ break;
+
+ case 'K':
+ pkgdb_set_dir(optarg, 3);
+ break;
+
+ case 'L':
+ NoView = TRUE;
+ break;
+
+ case 'R':
+ NoRecord = TRUE;
+ break;
+
+ case 'm':
+ OverrideMachine = optarg;
+ break;
+
+ case 'n':
+ Fake = TRUE;
+ Verbose = TRUE;
+ break;
+
+ case 'p':
+ Prefix = optarg;
+ break;
+
+ case 'U':
+ ReplaceSame = 1;
+ Replace = 1;
+ break;
+
+ case 'u':
+ Replace = 1;
+ break;
+
+ case 'V':
+ show_version();
+ /* NOTREACHED */
+
+ case 'v':
+ Verbose = TRUE;
+ break;
+
+ case 'W':
+ Viewbase = optarg;
+ break;
+
+ case 'w':
+ View = optarg;
+ break;
+
+ case 'h':
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ pkg_install_config();
+
+ if (Destdir != NULL) {
+ char *pkgdbdir;
+
+ pkgdbdir = xasprintf("%s/%s", Destdir, config_pkg_dbdir);
+ pkgdb_set_dir(pkgdbdir, 4);
+ free(pkgdbdir);
+ }
+
+ process_pkg_path();
+ TAILQ_INIT(&pkgs);
+
+ if (argc == 0) {
+ /* If no packages, yelp */
+ warnx("missing package name(s)");
+ usage();
+ }
+
+ if (strcasecmp(do_license_check, "no") == 0)
+ LicenseCheck = 0;
+ else if (strcasecmp(do_license_check, "yes") == 0)
+ LicenseCheck = 1;
+ else if (strcasecmp(do_license_check, "always") == 0)
+ LicenseCheck = 2;
+ else
+ errx(1, "Unknown value of the configuration variable"
+ "CHECK_LICENSE");
+
+ if (LicenseCheck)
+ load_license_lists();
+
+ /* Get all the remaining package names, if any */
+ for (; argc > 0; --argc, ++argv) {
+ lpkg_t *lpp;
+
+ if (IS_STDIN(*argv))
+ lpp = alloc_lpkg("-");
+ else
+ lpp = alloc_lpkg(*argv);
+
+ TAILQ_INSERT_TAIL(&pkgs, lpp, lp_link);
+ }
+
+ error += pkg_perform(&pkgs);
+ if (error != 0) {
+ warnx("%d package addition%s failed", error, error == 1 ? "" : "s");
+ exit(1);
+ }
+ exit(0);
+}
--- /dev/null
+/* $NetBSD: perform.c,v 1.4 2013/04/20 15:29:22 wiz Exp $ */
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+__RCSID("$NetBSD: perform.c,v 1.4 2013/04/20 15:29:22 wiz 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, 2009 Joerg Sonnenberger <joerg@NetBSD.org>
+ * Copyright (c) 2010 Thomas Klausner <wiz@NetBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 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 <sys/stat.h>
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+#include <errno.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <archive.h>
+#include <archive_entry.h>
+
+#include "lib.h"
+#include "add.h"
+#include "version.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 {
+ char *pkgname;
+
+ const char *prefix;
+ char *install_prefix;
+
+ char *logdir;
+ char *install_logdir;
+ char *install_logdir_real;
+ char *other_version;
+
+ package_t plist;
+
+ struct pkg_meta meta_data;
+
+ struct archive *archive;
+ struct archive_entry *entry;
+
+ char *buildinfo[BI_ENUM_COUNT];
+
+ size_t dep_length, dep_allocated;
+ char **dependencies;
+};
+
+static 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, 0 },
+};
+
+static int pkg_do(const char *, int, int);
+
+static int
+end_of_version(const char *opsys, const char *version_end)
+{
+ if (*version_end == '\0')
+ return 1;
+
+ if (strcmp(opsys, "NetBSD") == 0) {
+ if (strncmp(version_end, "_ALPHA", 6) == 0
+ || strncmp(version_end, "_BETA", 5) == 0
+ || strncmp(version_end, "_RC", 3) == 0
+ || strncmp(version_end, "_STABLE", 7) == 0
+ || strncmp(version_end, "_PATCH", 6) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+compatible_platform(const char *opsys, const char *host, const char *package)
+{
+ int i = 0;
+
+ /* returns 1 if host and package operating system match */
+ if (strcmp(host, package) == 0)
+ return 1;
+
+ /* accept, if host version is a minor release of package version */
+ if (strncmp(host, package, strlen(package)) == 0)
+ return 1;
+
+ /* find offset of first difference */
+ for (i=0; (host[i] != '\0') && (host[i] == package[i]);)
+ i++;
+
+ if (end_of_version(opsys, host+i) && end_of_version(opsys, package+i))
+ return 1;
+
+ return 0;
+}
+
+static int
+mkdir_p(const char *path)
+{
+ char *p, *cur_end;
+ int done, saved_errno;
+ struct stat sb;
+
+ /*
+ * Handle the easy case of direct success or
+ * pre-existing directory first.
+ */
+ if (mkdir(path, 0777) == 0)
+ return 0;
+ if (stat(path, &sb) == 0) {
+ if (S_ISDIR(sb.st_mode))
+ return 0;
+ errno = ENOTDIR;
+ return -1;
+ }
+
+ cur_end = p = xstrdup(path);
+
+ for (;;) {
+ /*
+ * First skip leading slashes either from / or
+ * from the last iteration.
+ */
+ cur_end += strspn(cur_end, "/");
+ /* Find end of actual directory name. */
+ cur_end += strcspn(cur_end, "/");
+
+ /*
+ * Remember if this is the last component and
+ * overwrite / if needed.
+ */
+ done = (*cur_end == '\0');
+ *cur_end = '\0';
+
+ if (mkdir(p, 0777) == -1) {
+ saved_errno = errno;
+ if (stat(p, &sb) == 0) {
+ if (S_ISDIR(sb.st_mode))
+ goto pass;
+ errno = ENOTDIR;
+ } else {
+ errno = saved_errno;
+ }
+ free(p);
+ return -1;
+ }
+pass:
+ if (done)
+ break;
+ *cur_end = '/';
+ }
+
+ free(p);
+ return 0;
+}
+
+/*
+ * Read meta data from archive.
+ * Bail out if a required entry is missing or entries are in the wrong order.
+ */
+static int
+read_meta_data(struct pkg_task *pkg)
+{
+ const struct pkg_meta_desc *descr, *last_descr;
+ const char *fname;
+ char **target;
+ int64_t size;
+ int r, found_required;
+
+ found_required = 0;
+
+ r = ARCHIVE_OK;
+ last_descr = 0;
+
+ if (pkg->entry != NULL)
+ goto skip_header;
+
+ for (;;) {
+ r = archive_read_next_header(pkg->archive, &pkg->entry);
+ if (r != ARCHIVE_OK)
+ break;
+skip_header:
+ fname = archive_entry_pathname(pkg->entry);
+
+ for (descr = pkg_meta_descriptors; descr->entry_filename;
+ ++descr) {
+ if (strcmp(descr->entry_filename, fname) == 0)
+ break;
+ }
+ if (descr->entry_filename == NULL)
+ break;
+
+ if (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;
+ }
+ *target = xmalloc(size + 1);
+ if (archive_read_data(pkg->archive, *target, size) != size) {
+ warnx("cannot read package meta data");
+ return -1;
+ }
+ (*target)[size] = '\0';
+ }
+
+ if (r != ARCHIVE_OK)
+ pkg->entry = NULL;
+ if (r == ARCHIVE_EOF)
+ r = ARCHIVE_OK;
+
+ for (descr = pkg_meta_descriptors; descr->entry_filename; ++descr) {
+ if (descr->required_file)
+ --found_required;
+ }
+
+ return !found_required && r == ARCHIVE_OK ? 0 : -1;
+}
+
+/*
+ * Free meta data.
+ */
+static void
+free_meta_data(struct pkg_task *pkg)
+{
+ const struct pkg_meta_desc *descr;
+ char **target;
+
+ for (descr = pkg_meta_descriptors; descr->entry_filename; ++descr) {
+ target = (char **)((char *)&pkg->meta_data +
+ descr->entry_offset);
+ free(*target);
+ *target = NULL;
+ }
+}
+
+/*
+ * Parse PLIST and populate pkg.
+ */
+static int
+pkg_parse_plist(struct pkg_task *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;
+ }
+ if (pkg->pkgname == NULL)
+ pkg->pkgname = xstrdup(p->name);
+ else if (strcmp(pkg->pkgname, p->name) != 0) {
+ warnx("Signature and PLIST differ on package name");
+ return -1;
+ }
+ if ((p = find_plist(&pkg->plist, PLIST_CWD)) == NULL) {
+ warnx("Invalid PLIST: missing @cwd");
+ return -1;
+ }
+
+ if (Prefix != NULL &&
+ strcmp(p->name, Prefix) != 0) {
+ size_t len;
+
+ delete_plist(&pkg->plist, FALSE, PLIST_CWD, NULL);
+ add_plist_top(&pkg->plist, PLIST_CWD, Prefix);
+ free(pkg->meta_data.meta_contents);
+ stringify_plist(&pkg->plist, &pkg->meta_data.meta_contents, &len,
+ Prefix);
+ pkg->prefix = Prefix;
+ } else
+ pkg->prefix = p->name;
+
+ if (Destdir != NULL)
+ pkg->install_prefix = xasprintf("%s/%s", Destdir, pkg->prefix);
+ else
+ pkg->install_prefix = xstrdup(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 = xmalloc(eol - key);
+ memcpy(val, key + 1, eol - key - 1);
+ val[eol - key - 1] = '\0';
+ return val;
+}
+
+static int
+check_already_installed(struct pkg_task *pkg)
+{
+ char *filename;
+ int fd;
+
+ filename = pkgdb_pkg_file(pkg->pkgname, CONTENTS_FNAME);
+ fd = open(filename, O_RDONLY);
+ free(filename);
+ if (fd == -1)
+ return 1;
+ close(fd);
+
+ if (ReplaceSame) {
+ struct stat sb;
+
+ pkg->install_logdir_real = pkg->install_logdir;
+ pkg->install_logdir = xasprintf("%s.xxxxxx", pkg->install_logdir);
+ if (stat(pkg->install_logdir, &sb) == 0) {
+ warnx("package `%s' already has a temporary update "
+ "directory `%s', remove it manually",
+ pkg->pkgname, pkg->install_logdir);
+ return -1;
+ }
+ return 1;
+ }
+
+ if (Force)
+ return 1;
+
+ /* We can only arrive here for explicitly requested packages. */
+ if (!Automatic && is_automatic_installed(pkg->pkgname)) {
+ if (Fake ||
+ mark_as_automatic_installed(pkg->pkgname, 0) == 0)
+ warnx("package `%s' was already installed as "
+ "dependency, now marked as installed "
+ "manually", pkg->pkgname);
+ } else {
+ warnx("package `%s' already recorded as installed",
+ pkg->pkgname);
+ }
+ return 0;
+
+}
+
+static int
+check_other_installed(struct pkg_task *pkg)
+{
+ FILE *f, *f_pkg;
+ size_t len;
+ char *pkgbase, *iter, *filename;
+ package_t plist;
+ plist_t *p;
+ int status;
+
+ if (pkg->install_logdir_real) {
+ pkg->other_version = xstrdup(pkg->pkgname);
+ return 0;
+ }
+
+ pkgbase = xstrdup(pkg->pkgname);
+
+ if ((iter = strrchr(pkgbase, '-')) == NULL) {
+ free(pkgbase);
+ warnx("Invalid package name %s", pkg->pkgname);
+ return -1;
+ }
+ *iter = '\0';
+ pkg->other_version = find_best_matching_installed_pkg(pkgbase);
+ free(pkgbase);
+ if (pkg->other_version == NULL)
+ return 0;
+
+ if (!Replace) {
+ /* XXX This is redundant to the implicit conflict check. */
+ warnx("A different version of %s is already installed: %s",
+ pkg->pkgname, pkg->other_version);
+ return -1;
+ }
+
+ filename = pkgdb_pkg_file(pkg->other_version, REQUIRED_BY_FNAME);
+ errno = 0;
+ f = fopen(filename, "r");
+ free(filename);
+ if (f == NULL) {
+ if (errno == ENOENT) {
+ /* No packages depend on this, so everything is well. */
+ return 0;
+ }
+ warnx("Can't open +REQUIRED_BY of %s", pkg->other_version);
+ return -1;
+ }
+
+ 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;
+ }
+ 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.
+ */
+ 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 (!ForceDepending)
+ status = -1;
+ break;
+ }
+ free_plist(&plist);
+ }
+
+ 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;
+
+ for (; data != NULL && *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);
+ else if (strncmp(data, "LICENSE=", 8) == 0)
+ pkg->buildinfo[BI_LICENSE] = dup_value(data, eol);
+ else if (strncmp(data, "PKGTOOLS_VERSION=", 17) == 0)
+ pkg->buildinfo[BI_PKGTOOLS_VERSION] = 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;
+ }
+
+ if ((pkg->buildinfo[BI_USE_ABI_DEPENDS] != NULL &&
+ strcasecmp(pkg->buildinfo[BI_USE_ABI_DEPENDS], "YES") != 0) ||
+ (pkg->buildinfo[BI_IGNORE_RECOMMENDED] != NULL &&
+ strcasecmp(pkg->buildinfo[BI_IGNORE_RECOMMENDED], "NO") != 0)) {
+ warnx("%s was built to ignore ABI dependencies", pkg->pkgname);
+ }
+
+ return 0;
+}
+
+/*
+ * 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;
+ }
+}
+
+/*
+ * Write meta data files to pkgdb after creating the directory.
+ */
+static int
+write_meta_data(struct pkg_task *pkg)
+{
+ const struct pkg_meta_desc *descr;
+ char *filename, **target;
+ size_t len;
+ ssize_t ret;
+ int fd;
+
+ if (Fake)
+ return 0;
+
+ if (mkdir_p(pkg->install_logdir)) {
+ warn("Can't create pkgdb entry: %s", pkg->install_logdir);
+ return -1;
+ }
+
+ for (descr = pkg_meta_descriptors; descr->entry_filename; ++descr) {
+ target = (char **)((char *)&pkg->meta_data +
+ descr->entry_offset);
+ if (*target == NULL)
+ continue;
+ filename = xasprintf("%s/%s", pkg->install_logdir,
+ descr->entry_filename);
+ (void)unlink(filename);
+ fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, descr->perm);
+ if (fd == -1) {
+ warn("Can't open meta data file: %s", filename);
+ return -1;
+ }
+ len = strlen(*target);
+ do {
+ ret = write(fd, *target, len);
+ if (ret == -1) {
+ warn("Can't write meta data file: %s",
+ filename);
+ free(filename);
+ close(fd);
+ return -1;
+ }
+ len -= ret;
+ } while (ret > 0);
+ if (close(fd) == -1) {
+ warn("Can't close meta data file: %s", filename);
+ free(filename);
+ return -1;
+ }
+ 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;
+ }
+ 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;
+ }
+ }
+}
+
+/*
+ * 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;
+
+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 (Fake)
+ return 0;
+
+ if (mkdir_p(pkg->install_prefix)) {
+ warn("Can't create prefix: %s", pkg->install_prefix);
+ return -1;
+ }
+
+ if (!NoRecord && !pkgdb_open(ReadWrite)) {
+ warn("Can't open pkgdb for writing");
+ return -1;
+ }
+
+ if (chdir(pkg->install_prefix) == -1) {
+ warn("Can't change into prefix: %s", pkg->install_prefix);
+ return -1;
+ }
+
+ 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;
+ }
+ fullpath = xasprintf("%s/%s", pkg->prefix, p->name);
+ pkgdb_store(fullpath, pkg->pkgname);
+ free(fullpath);
+ if (Verbose)
+ printf("%s", p->name);
+ break;
+
+ case PLIST_PKGDIR:
+ fullpath = xasprintf("%s/%s", pkg->prefix, p->name);
+ mkdir_p(fullpath);
+ free(fullpath);
+ add_pkgdir(pkg->pkgname, pkg->prefix, p->name);
+ continue;
+
+ case PLIST_CMD:
+ if (format_cmd(cmd, sizeof(cmd), p->name, pkg->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;
+
+ case PLIST_IGNORE:
+ p = p->next;
+ continue;
+
+ default:
+ continue;
+ }
+
+ r = archive_write_header(writer, pkg->entry);
+ if (r != ARCHIVE_OK) {
+ warnx("Failed to write %s for %s: %s",
+ archive_entry_pathname(pkg->entry),
+ pkg->pkgname,
+ archive_error_string(writer));
+ goto out;
+ }
+
+ 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);
+ }
+
+ 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 for %s: %s",
+ pkg->pkgname,
+ archive_error_string(pkg->archive));
+ goto out;
+ }
+ }
+
+ if (pkg->entry != NULL) {
+ warnx("Package contains entries not in PLIST: %s",
+ archive_entry_pathname(pkg->entry));
+ goto out;
+ }
+
+ 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;
+
+ text = xasprintf("%s\n", pkg->pkgname);
+ text_len = strlen(text);
+
+ for (i = 0; i < pkg->dep_length; ++i) {
+ required_by = pkgdb_pkg_file(pkg->dependencies[i], REQUIRED_BY_FNAME);
+
+ fd = open(required_by, O_WRONLY | O_APPEND | O_CREAT, 0644);
+ if (fd == -1) {
+ warn("can't open dependency file '%s',"
+ "registration is incomplete!", required_by);
+ } else if (write(fd, text, text_len) != (ssize_t)text_len) {
+ warn("can't write to dependency file `%s'", required_by);
+ close(fd);
+ } else if (close(fd) == -1)
+ warn("cannot close file %s", required_by);
+
+ free(required_by);
+ }
+
+ 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;
+ }
+ }
+
+ 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 ||
+ compatible_platform(OPSYS_NAME, host_uname.release,
+ pkg->buildinfo[BI_OS_VERSION]) != 1) {
+ warnx("Warning: package `%s' was built for a platform:",
+ pkg->pkgname);
+ warnx("%s/%s %s (pkg) vs. %s/%s %s (this host)",
+ pkg->buildinfo[BI_OPSYS],
+ pkg->buildinfo[BI_MACHINE_ARCH],
+ pkg->buildinfo[BI_OS_VERSION],
+ OPSYS_NAME,
+ effective_arch,
+ host_uname.release);
+ if (!Force && fatal)
+ return -1;
+ }
+ return 0;
+}
+
+static int
+check_pkgtools_version(struct pkg_task *pkg)
+{
+ const char *val = pkg->buildinfo[BI_PKGTOOLS_VERSION];
+ int version;
+
+ if (val == NULL) {
+ warnx("Warning: package `%s' lacks pkg_install version data",
+ pkg->pkgname);
+ return 0;
+ }
+
+ if (strlen(val) != 8 || strspn(val, "0123456789") != 8) {
+ warnx("Warning: package `%s' contains an invalid pkg_install version",
+ pkg->pkgname);
+ return Force ? 0 : -1;
+ }
+ version = atoi(val);
+ if (version > PKGTOOLS_VERSION) {
+ warnx("%s: package `%s' was built with a newer pkg_install version",
+ Force ? "Warning" : "Error", pkg->pkgname);
+ return Force ? 0 : -1;
+ }
+ return 0;
+}
+
+/*
+ * Run the install script.
+ */
+static int
+run_install_script(struct pkg_task *pkg, const char *argument)
+{
+ int ret;
+ char *filename;
+
+ if (pkg->meta_data.meta_install == NULL || NoInstall)
+ return 0;
+
+ if (Destdir != NULL)
+ setenv(PKG_DESTDIR_VNAME, Destdir, 1);
+ setenv(PKG_PREFIX_VNAME, pkg->prefix, 1);
+ setenv(PKG_METADATA_DIR_VNAME, pkg->logdir, 1);
+ setenv(PKG_REFCOUNT_DBDIR_VNAME, config_pkg_refcount_dbdir, 1);
+
+ if (Verbose)
+ printf("Running install with PRE-INSTALL for %s.\n", pkg->pkgname);
+ if (Fake)
+ return 0;
+
+ filename = pkgdb_pkg_file(pkg->pkgname, INSTALL_FNAME);
+
+ ret = 0;
+ errno = 0;
+ if (fcexec(pkg->install_logdir, filename, pkg->pkgname, argument,
+ (void *)NULL)) {
+ if (errno != 0)
+ warn("exec of install script failed");
+ else
+ warnx("install script returned error status");
+ ret = -1;
+ }
+ free(filename);
+
+ return ret;
+}
+
+struct find_conflict_data {
+ const char *pkg;
+ const char *old_pkg;
+ const char *pattern;
+};
+
+static int
+check_explicit_conflict_iter(const char *cur_pkg, void *cookie)
+{
+ struct find_conflict_data *data = cookie;
+
+ if (data->old_pkg && strcmp(data->old_pkg, cur_pkg) == 0)
+ return 0;
+
+ warnx("Package `%s' conflicts with `%s', and `%s' is installed.",
+ data->pkg, data->pattern, cur_pkg);
+
+ return 1;
+}
+
+static int
+check_explicit_conflict(struct pkg_task *pkg)
+{
+ struct find_conflict_data data;
+ char *installed, *installed_pattern;
+ plist_t *p;
+ int status;
+
+ status = 0;
+
+ for (p = pkg->plist.head; p != NULL; p = p->next) {
+ if (p->type == PLIST_IGNORE) {
+ p = p->next;
+ continue;
+ }
+ if (p->type != PLIST_PKGCFL)
+ continue;
+ data.pkg = pkg->pkgname;
+ data.old_pkg = pkg->other_version;
+ data.pattern = p->name;
+ status |= match_installed_pkgs(p->name,
+ check_explicit_conflict_iter, &data);
+ }
+
+ 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;
+ }
+
+ 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
+ }
+
+ 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;
+
+ fullpath = xasprintf("%s/%s", pkg->prefix, p->name);
+ existing = pkgdb_retrieve(fullpath);
+ free(fullpath);
+ if (existing == NULL)
+ continue;
+ if (pkg->other_version != NULL &&
+ strcmp(pkg->other_version, existing) == 0)
+ continue;
+
+ warnx("Conflicting PLIST with %s: %s", existing, p->name);
+ if (!Force) {
+ status = -1;
+ if (!Verbose)
+ break;
+ }
+ }
+
+ pkgdb_close();
+ return status;
+}
+
+static int
+check_dependencies(struct pkg_task *pkg)
+{
+ plist_t *p;
+ char *best_installed;
+ int status;
+ size_t i;
+
+ status = 0;
+
+ for (p = pkg->plist.head; p != NULL; p = p->next) {
+ if (p->type == PLIST_IGNORE) {
+ p = p->next;
+ continue;
+ } else if (p->type != PLIST_PKGDEP)
+ continue;
+
+ 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;
+ }
+ warnx("Missing dependency %s, continuing",
+ p->name);
+ continue;
+ }
+ if (pkg_do(p->name, 1, 0)) {
+ if (ForceDepends) {
+ warnx("Can't install dependency %s, "
+ "continuing", p->name);
+ continue;
+ } else {
+ warnx("Can't install dependency %s",
+ p->name);
+ status = -1;
+ break;
+ }
+ }
+ best_installed = find_best_matching_installed_pkg(p->name);
+ if (best_installed == NULL && ForceDepends) {
+ warnx("Missing dependency %s ignored", p->name);
+ continue;
+ } else if (best_installed == NULL) {
+ warnx("Just installed dependency %s disappeared", p->name);
+ status = -1;
+ break;
+ }
+ }
+ for (i = 0; i < pkg->dep_length; ++i) {
+ if (strcmp(best_installed, pkg->dependencies[i]) == 0)
+ break;
+ }
+ if (i < pkg->dep_length) {
+ /* Already used as dependency, so skip it. */
+ free(best_installed);
+ continue;
+ }
+ if (pkg->dep_length + 1 >= pkg->dep_allocated) {
+ char **tmp;
+ pkg->dep_allocated = 2 * pkg->dep_allocated + 1;
+ pkg->dependencies = xrealloc(pkg->dependencies,
+ pkg->dep_allocated * sizeof(*tmp));
+ }
+ pkg->dependencies[pkg->dep_length++] = best_installed;
+ }
+
+ return status;
+}
+
+/*
+ * 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_get_dir(),
+ View ? "-w " : "", View ? View : "",
+ Viewbase ? "-W " : "", Viewbase ? Viewbase : "",
+ Verbose ? "-v " : "", pkg->pkgname);
+ }
+
+ fexec_skipempty(BINDIR "/pkg_view", "-d", pkgdb_get_dir(),
+ View ? "-w " : "", View ? View : "",
+ Viewbase ? "-W " : "", Viewbase ? Viewbase : "",
+ Verbose ? "-v " : "", "add", pkg->pkgname,
+ (void *)NULL);
+}
+
+static int
+preserve_meta_data_file(struct pkg_task *pkg, const char *name)
+{
+ char *old_file, *new_file;
+ int rv;
+
+ if (Fake)
+ return 0;
+
+ old_file = pkgdb_pkg_file(pkg->other_version, name);
+ new_file = xasprintf("%s/%s", pkg->install_logdir, name);
+ rv = 0;
+ if (rename(old_file, new_file) == -1 && errno != ENOENT) {
+ warn("Can't move %s from %s to %s", name, old_file, new_file);
+ rv = -1;
+ }
+ free(old_file);
+ free(new_file);
+ return rv;
+}
+
+static int
+start_replacing(struct pkg_task *pkg)
+{
+ if (preserve_meta_data_file(pkg, REQUIRED_BY_FNAME))
+ return -1;
+
+ if (preserve_meta_data_file(pkg, PRESERVE_FNAME))
+ return -1;
+
+ if (pkg->meta_data.meta_installed_info == NULL &&
+ preserve_meta_data_file(pkg, INSTALLED_INFO_FNAME))
+ return -1;
+
+ if (Verbose || Fake) {
+ printf("%s/pkg_delete -K %s -p %s%s%s '%s'\n",
+ BINDIR, pkgdb_get_dir(), pkg->prefix,
+ Destdir ? " -P ": "", Destdir ? Destdir : "",
+ pkg->other_version);
+ }
+ if (!Fake)
+ fexec_skipempty(BINDIR "/pkg_delete", "-K", pkgdb_get_dir(),
+ "-p", pkg->prefix,
+ Destdir ? "-P": "", Destdir ? Destdir : "",
+ pkg->other_version, NULL);
+
+ /* XXX Check return value and do what? */
+ return 0;
+}
+
+static int check_input(const char *line, size_t len)
+{
+ if (line == NULL || len == 0)
+ return 1;
+ switch (*line) {
+ case 'Y':
+ case 'y':
+ case 'T':
+ case 't':
+ case '1':
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+static int
+check_signature(struct pkg_task *pkg, int invalid_sig)
+{
+ char *line;
+ size_t len;
+
+ if (strcasecmp(verified_installation, "never") == 0)
+ return 0;
+ if (strcasecmp(verified_installation, "always") == 0) {
+ if (invalid_sig)
+ warnx("No valid signature found, rejected");
+ return invalid_sig;
+ }
+ if (strcasecmp(verified_installation, "trusted") == 0) {
+ if (!invalid_sig)
+ return 0;
+ fprintf(stderr, "No valid signature found for %s.\n",
+ pkg->pkgname);
+ fprintf(stderr,
+ "Do you want to proceed with the installation [y/n]?\n");
+ line = fgetln(stdin, &len);
+ if (check_input(line, len)) {
+ fprintf(stderr, "Cancelling installation\n");
+ return 1;
+ }
+ return 0;
+ }
+ if (strcasecmp(verified_installation, "interactive") == 0) {
+ fprintf(stderr, "Do you want to proceed with "
+ "the installation of %s [y/n]?\n", pkg->pkgname);
+ line = fgetln(stdin, &len);
+ if (check_input(line, len)) {
+ fprintf(stderr, "Cancelling installation\n");
+ return 1;
+ }
+ return 0;
+ }
+ warnx("Unknown value of configuration variable VERIFIED_INSTALLATION");
+ return 1;
+}
+
+static int
+check_vulnerable(struct pkg_task *pkg)
+{
+ static struct pkg_vulnerabilities *pv;
+ int require_check;
+ char *line;
+ size_t len;
+
+ if (strcasecmp(check_vulnerabilities, "never") == 0)
+ return 0;
+ else if (strcasecmp(check_vulnerabilities, "always") == 0)
+ require_check = 1;
+ else if (strcasecmp(check_vulnerabilities, "interactive") == 0)
+ require_check = 0;
+ else {
+ warnx("Unknown value of the configuration variable"
+ "CHECK_VULNERABILITIES");
+ return 1;
+ }
+
+ if (pv == NULL) {
+ pv = read_pkg_vulnerabilities_file(pkg_vulnerabilities_file,
+ require_check, 0);
+ if (pv == NULL)
+ return require_check;
+ }
+
+ if (!audit_package(pv, pkg->pkgname, NULL, 2))
+ return 0;
+
+ if (require_check)
+ return 1;
+
+ fprintf(stderr, "Do you want to proceed with the installation of %s"
+ " [y/n]?\n", pkg->pkgname);
+ line = fgetln(stdin, &len);
+ if (check_input(line, len)) {
+ fprintf(stderr, "Cancelling installation\n");
+ return 1;
+ }
+ return 0;
+}
+
+static int
+check_license(struct pkg_task *pkg)
+{
+ if (LicenseCheck == 0)
+ return 0;
+
+ if ((pkg->buildinfo[BI_LICENSE] == NULL ||
+ *pkg->buildinfo[BI_LICENSE] == '\0')) {
+
+ if (LicenseCheck == 1)
+ return 0;
+ warnx("No LICENSE set for package `%s'", pkg->pkgname);
+ return 1;
+ }
+
+ switch (acceptable_license(pkg->buildinfo[BI_LICENSE])) {
+ case 0:
+ warnx("License `%s' of package `%s' is not acceptable",
+ pkg->buildinfo[BI_LICENSE], pkg->pkgname);
+ return 1;
+ case 1:
+ return 0;
+ default:
+ warnx("Invalid LICENSE for package `%s'", pkg->pkgname);
+ return 1;
+ }
+}
+
+/*
+ * Install a single package.
+ */
+static int
+pkg_do(const char *pkgpath, int mark_automatic, int top_level)
+{
+ char *archive_name;
+ int status, invalid_sig;
+ struct pkg_task *pkg;
+
+ pkg = xcalloc(1, sizeof(*pkg));
+
+ status = -1;
+
+ pkg->archive = find_archive(pkgpath, top_level, &archive_name);
+ if (pkg->archive == NULL) {
+ warnx("no pkg found for '%s', sorry.", pkgpath);
+ goto clean_find_archive;
+ }
+
+ invalid_sig = pkg_verify_signature(archive_name, &pkg->archive, &pkg->entry,
+ &pkg->pkgname);
+ free(archive_name);
+
+ if (pkg->archive == NULL)
+ goto clean_memory;
+
+ 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 (check_signature(pkg, invalid_sig))
+ goto clean_memory;
+
+ if (read_buildinfo(pkg))
+ goto clean_memory;
+
+ if (check_pkgtools_version(pkg))
+ goto clean_memory;
+
+ if (check_vulnerable(pkg))
+ goto clean_memory;
+
+ if (check_license(pkg))
+ goto clean_memory;
+
+ if (pkg->meta_data.meta_mtree != NULL)
+ warnx("mtree specification in pkg `%s' ignored", pkg->pkgname);
+
+ if (pkg->meta_data.meta_views != NULL) {
+ pkg->logdir = xstrdup(pkg->prefix);
+ pkgdb_set_dir(dirname_of(pkg->logdir), 4);
+ } else {
+ pkg->logdir = xasprintf("%s/%s", config_pkg_dbdir, pkg->pkgname);
+ }
+
+ if (Destdir != NULL)
+ pkg->install_logdir = xasprintf("%s/%s", Destdir, pkg->logdir);
+ else
+ pkg->install_logdir = xstrdup(pkg->logdir);
+
+ if (NoRecord && !Fake) {
+ const char *tmpdir;
+
+ tmpdir = getenv("TMPDIR");
+ if (tmpdir == NULL)
+ tmpdir = "/tmp";
+
+ free(pkg->install_logdir);
+ pkg->install_logdir = xasprintf("%s/pkg_install.XXXXXX", tmpdir);
+ /* XXX pkg_add -u... */
+ if (mkdtemp(pkg->install_logdir) == NULL) {
+ warn("mkdtemp failed");
+ goto clean_memory;
+ }
+ }
+
+ switch (check_already_installed(pkg)) {
+ case 0:
+ status = 0;
+ goto clean_memory;
+ case 1:
+ break;
+ case -1:
+ goto clean_memory;
+ }
+
+ if (check_platform(pkg))
+ goto clean_memory;
+
+ if (check_other_installed(pkg))
+ goto clean_memory;
+
+ if (check_explicit_conflict(pkg))
+ goto clean_memory;
+
+ if (check_implicit_conflict(pkg))
+ goto clean_memory;
+
+ if (pkg->other_version != NULL) {
+ /*
+ * Replacing an existing package.
+ * Write meta-data, get rid of the old version,
+ * install/update dependencies and finally extract.
+ */
+ if (write_meta_data(pkg))
+ goto nuke_pkgdb;
+
+ if (start_replacing(pkg))
+ goto nuke_pkgdb;
+
+ if (pkg->install_logdir_real) {
+ rename(pkg->install_logdir, pkg->install_logdir_real);
+ free(pkg->install_logdir);
+ pkg->install_logdir = pkg->install_logdir_real;
+ pkg->install_logdir_real = NULL;
+ }
+
+ if (check_dependencies(pkg))
+ goto nuke_pkgdb;
+ } else {
+ /*
+ * 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->install_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, &pkg->plist, FALSE, Destdir);
+ }
+
+nuke_pkgdb:
+ if (!Fake) {
+ if (recursive_remove(pkg->install_logdir, 1))
+ warn("Couldn't remove %s", pkg->install_logdir);
+ free(pkg->install_logdir_real);
+ free(pkg->install_logdir);
+ free(pkg->logdir);
+ pkg->install_logdir_real = NULL;
+ pkg->install_logdir = NULL;
+ pkg->logdir = NULL;
+ }
+
+clean_memory:
+ if (pkg->logdir != NULL && NoRecord && !Fake) {
+ if (recursive_remove(pkg->install_logdir, 1))
+ warn("Couldn't remove %s", pkg->install_logdir);
+ }
+ free(pkg->install_prefix);
+ free(pkg->install_logdir_real);
+ free(pkg->install_logdir);
+ free(pkg->logdir);
+ free_buildinfo(pkg);
+ free_plist(&pkg->plist);
+ free_meta_data(pkg);
+ if (pkg->archive)
+ archive_read_finish(pkg->archive);
+ free(pkg->other_version);
+ free(pkg->pkgname);
+clean_find_archive:
+ free(pkg);
+ return status;
+}
+
+int
+pkg_perform(lpkg_head_t *pkgs)
+{
+ int errors = 0;
+ lpkg_t *lpp;
+
+ while ((lpp = TAILQ_FIRST(pkgs)) != NULL) {
+ if (pkg_do(lpp->lp_name, Automatic, 1))
+ ++errors;
+ TAILQ_REMOVE(pkgs, lpp, lp_link);
+ free_lpkg(lpp);
+ }
+
+ return errors;
+}
--- /dev/null
+.\" $NetBSD: pkg_add.1,v 1.1.1.12 2011/02/18 22:32:28 aymeric Exp $
+.\"
+.\" FreeBSD install - a package for the installation and maintenance
+.\" 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
+.\"
+.\"
+.\" @(#)pkg_add.1
+.\"
+.Dd June 16, 2010
+.Dt PKG_ADD 1
+.Os
+.Sh NAME
+.Nm pkg_add
+.Nd a utility for installing and upgrading software package distributions
+.Sh SYNOPSIS
+.Nm
+.Op Fl AfILnRUuVv
+.Op Fl C Ar config
+.Op Fl K Ar pkg_dbdir
+.Op Fl m Ar machine
+.Op Fl P Ar destdir
+.Op Fl p Ar prefix
+.Op Fl W Ar viewbase
+.Op Fl w Ar view
+.Ar Oo Oo Li ftp|http Oc Ns Li :// Ns Oo Ar user Oc Ns \
+Oo Li \&: Ns Ar password Oc \
+Ns Li @ Oc Ns Ar host Ns Oo Li \&: Ns Ar port Oc Ns \
+Oo Li / Ns Ar path/ Oc Ns Ar pkg-name ...
+.Sh DESCRIPTION
+The
+.Nm
+command is used to extract and upgrade packages that have been
+previously created with the
+.Xr pkg_create 1
+command.
+Packages are prepared collections of pre-built binaries, documentation,
+configurations, installation instructions and/or other files.
+.Nm
+can recursively install other packages that the current package
+depends on or requires from both local disk and via FTP or HTTP.
+.Sh WARNING
+.Bf -emphasis
+Since the
+.Nm
+command may execute scripts or programs contained within a package file,
+your system may be susceptible to
+.Dq Trojan horses
+or other subtle
+attacks from miscreants who create dangerous package files.
+.Pp
+You are advised to verify the competence and identity of those who
+provide installable package files.
+For extra protection, use the digital signatures provided where possible
+(see the
+.Xr pkg_install.conf 5 ) ,
+or, failing that, use
+.Xr tar 1
+to extract the package file, and inspect its contents and scripts
+to ensure it poses no danger to your system's integrity.
+Pay particular attention to any
+.Pa +INSTALL
+or
+.Pa +DEINSTALL
+files, and inspect the
+.Pa +CONTENTS
+file for
+.Cm @cwd ,
+.Cm @mode
+(check for setuid),
+.Cm @dirrm ,
+.Cm @exec ,
+and
+.Cm @unexec
+directives, and/or use the
+.Xr pkg_info 1
+command to examine the package file.
+.Ef
+.Sh OPTIONS
+The following command line arguments are supported:
+.Bl -tag -width indent
+.It Ar pkg-name [ ... ]
+The named packages are installed.
+.Nm
+will first try to use
+.Ar pkg-name
+as full URL or path name without any wildcard processing.
+If that fails,
+.Nm
+will try to match packages using wildcard processing.
+If that fails as well and
+.Ar pkg-name
+does not contain any /, the entries of the
+.Dv PKG_PATH
+variable are searched using the wildcard processing rules.
+.It Fl A
+Mark package as installed automatically, as dependency of another
+package.
+You can use
+.Dl Ic pkg_admin set automatic=YES
+to mark packages this way after installation, and
+.Dl Ic pkg_admin unset automatic
+to remove the mark.
+If you
+.Nm
+a package without specifying
+.Fl A
+after it had already been automatically installed, the mark is
+removed.
+.It Fl C Ar config
+Read the configuration file from
+.Ar config
+instead of the system default.
+.It Fl f
+Force installation to proceed even if prerequisite packages are not
+installed or the install script fails.
+Although
+.Nm
+will still try to find and auto-install missing prerequisite packages,
+a failure to find one will not be fatal.
+This flag also overrides the fatal error when the operating system or
+architecture the package was built on differ from that of the host.
+.It Fl D
+Force updating even if the dependencies of depending packages are not
+satisfied by the new package.
+This is used by "make replace", after which one would typically
+replace the depending packages.
+.It Fl I
+If an installation script exists for a given package, do not execute it.
+.It Fl K Ar pkg_dbdir
+Override the value of the
+.Dv PKG_DBDIR
+configuration option with the value
+.Ar pkg_dbdir .
+.It Fl L
+Don't add the package to any views after installation.
+.It Fl m
+Override the machine architecture returned by uname with
+.Ar machine .
+.It Fl n
+Don't actually install a package, just report the steps that
+would be taken if it was.
+.It Fl P Ar destdir
+Prefix all file and directory names with
+.Ar destdir .
+For packages without install scripts this has the same behavior as
+using
+.Xr chroot 8 .
+.It Fl p Ar prefix
+Override the prefix stored in the package with
+.Ar prefix .
+.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 U
+Replace an already installed version from a package.
+Implies
+.Fl u .
+.It Fl u
+If the package that's being installed is already installed,
+an update is performed.
+Installed dependent packages are updated recursively, if they are too
+old to fulfill the dependencies of the to-be-installed version.
+See below for a more detailed description of the process.
+.It Fl V
+Print version number and exit.
+.It Fl v
+Turn on verbose output.
+.It Fl W Ar viewbase
+Passed down to
+.Xr pkg_view 1
+for managed views.
+.It Fl w Ar view
+Passed down to
+.Xr pkg_view 1
+for managed views.
+.El
+.Pp
+One or more
+.Ar pkg-name
+arguments may be specified, each being either a file containing the
+package (these usually ending with the
+.Dq .tgz
+suffix) or a
+URL pointing at a file available on an ftp or web site.
+Thus you may extract files directly from their anonymous ftp or WWW
+locations (e.g.,
+.Nm
+ftp://ftp.NetBSD.org/pub/pkgsrc/packages/NetBSD/i386/3.1_2007Q2/shells/bash-3.2.9.tgz
+or
+.Nm
+http://www.example.org/packages/screen-4.0.tbz).
+Note: For ftp transfers, if you wish to use
+.Bf -emphasis
+passive mode
+.Ef
+ftp in such transfers, set the variable
+.Bf -emphasis
+FTP_PASSIVE_MODE
+.Ef
+to some value in your environment.
+Otherwise, the more standard ACTIVE mode may be used.
+If
+.Nm
+consistently fails to fetch a package from a site known to work,
+it may be because you have a firewall that demands the usage of
+.Bf -emphasis
+passive mode
+.Ef
+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:
+.Bl -enum -offset indent
+.It
+A check is made to determine if the package or another version of it
+is already recorded as installed.
+If it is,
+installation is terminated if the
+.Fl u
+or
+.Fl U
+options are not given.
+.Pp
+If the same version is installed and
+.Fl U
+is not given, it is marked as manually installed and process stops.
+If the
+.Fl u
+option is given, it's assumed the package should be replaced by the
+new version instead.
+Before doing so, all packages that depend on the
+pkg being upgraded are checked if they also work with the new version.
+If that test is not successful, the dependent packages are updated first.
+The replacing is then prepared by moving an existing
+.Pa +REQUIRED_BY
+file aside (if it exists), and running
+.Xr pkg_delete 1
+on the installed package.
+Installation then proceeds as if the package
+was not installed, and restores the
+.Pa +REQUIRED_BY
+file afterwards.
+.It
+The package build information is extracted from the
+.Pa +BUILD_INFO
+file and compared against the result of
+.Xr uname 3 .
+If the operating system or architecture of the package differ from
+that of the host, installation is aborted.
+This behavior is overridable with the
+.Fl f
+flag.
+.It
+The package build information from
+.Pa +BUILD_INFO
+is then checked for
+.Ev USE_ABI_DEPENDS=NO
+(or
+.Ev IGNORE_RECOMMENDED ) .
+If the package was built with ABI dependency recommendations ignored,
+a warning will be issued.
+.It
+A check is made to determine if the package conflicts (from
+.Cm @pkgcfl
+directives, see
+.Xr pkg_create 1 )
+with an already recorded as installed package or if an installed package
+conflicts with the package.
+If it is, installation is terminated.
+.It
+The file list of the package is compared to the file lists of the
+installed packages.
+If there is any overlap, the installation is terminated.
+.It
+All package dependencies (from
+.Cm @pkgdep
+directives, see
+.Xr pkg_create 1 )
+are read from the packing list.
+If any of these required packages are not currently installed,
+an attempt is made to find and install it;
+if the missing package cannot be found or installed,
+the installation is terminated.
+.It
+If the package contains an
+.Ar install
+script, it is executed with the following arguments:
+.Bl -tag -width indentindent
+.It Ar pkg-name
+The name of the package being installed.
+.It Cm PRE-INSTALL
+Keyword denoting that the script is to perform any actions needed before
+the package is installed.
+.El
+.Pp
+If the
+.Ar install
+script exits with a non-zero status code, the installation is terminated.
+.It
+The files from the file list are extracted to the chosen prefix.
+.It
+If an
+.Ar install
+script exists for the package, it is executed with the following arguments:
+.Bl -tag -width indentindent
+.It Ar pkg_name
+The name of the package being installed.
+.It Cm POST-INSTALL
+Keyword denoting that the script is to perform any actions needed
+after the package has been installed.
+.El
+.It
+After installation is complete, a copy of the packing list,
+.Ar deinstall
+script, description, and display files are copied into
+.Pa \*[Lt]PKG_DBDIR\*[Gt]/\*[Lt]pkg-name\*[Gt]
+for subsequent possible use by
+.Xr pkg_delete 1 .
+Any package dependencies are recorded in the other packages'
+.Pa +REQUIRED_BY
+file.
+.It
+If the package is a depoted package, then add it to the registered
+by calling
+.Xr pkg_view 1
+accordingly.
+.It
+Finally, if we were upgrading a package, any
+.Pa +REQUIRED_BY
+file that was moved aside before upgrading was started is now moved
+back into place.
+.El
+.Pp
+The
+.Ar install
+script is called with the environment variable
+.Ev PKG_PREFIX
+set to the installation prefix (see the
+.Fl p
+option above).
+This allows a package author to write a script
+that reliably performs some action on the directory where the package
+is installed, even if the user might change it with the
+.Fl p
+flag to
+.Cm pkg_add .
+The scripts are also called with the
+.Ev PKG_METADATA_DIR
+environment variable set to the location of the
+.Pa +*
+meta-data files, and with the
+.Ev PKG_REFCOUNT_DBDIR
+environment variable set to the location of the package reference counts
+database directory.
+If the
+.Fl P
+flag was given to
+.Nm ,
+.Ev PKG_DESTDIR
+will be set to
+.Ar destdir .
+Additionally,
+.Ev PKG_METADATA_DIR
+and
+.Ev PKG_REFCOUNT_DBDIR
+are prefixed with
+.Ar destdir .
+.Sh ENVIRONMENT
+See
+.Xr pkg_install.conf 5
+for options, that can also be specified using the environment.
+Packages using views are also affected by the environment variables
+documented for
+.Xr pkg_view 1 .
+.Sh EXAMPLES
+In all cases,
+.Nm
+will try to install binary packages listed in dependencies list.
+.Pp
+You can specify a compiled binary package explicitly on the command line.
+.Bd -literal
+# pkg_add /usr/pkgsrc/packages/All/tcsh-6.14.00.tgz
+.Ed
+.Pp
+If you omit the version number,
+.Nm
+will install the latest version available.
+With
+.Fl v ,
+.Nm
+emits more messages to terminal.
+.Bd -literal
+# pkg_add -v /usr/pkgsrc/packages/All/unzip
+.Ed
+.Pp
+You can grab a compiled binary package from remote location by specifying
+a URL.
+The base URL can also be provided by the configuration variable,
+.Dv PKG_PATH .
+.Bd -literal
+# pkg_add -v ftp://ftp.NetBSD.org/pub/pkgsrc/packages/NetBSD/i386/3.1_2007Q2/All/firefox-2.0.0.4.tgz
+
+# export PKG_PATH=ftp://ftp.NetBSD.org/pub/pkgsrc/packages/NetBSD/i386/3.1_2007Q2/All
+# pkg_add -v firefox
+.Ed
+.Sh SEE ALSO
+.Xr pkg_admin 1 ,
+.Xr pkg_create 1 ,
+.Xr pkg_delete 1 ,
+.Xr pkg_info 1 ,
+.Xr pkg_install.conf 5 ,
+.Xr pkgsrc 7
+.Sh AUTHORS
+.Bl -tag -width indent -compact
+.It "Jordan Hubbard"
+Initial work and ongoing development.
+.It "John Kohl"
+.Nx
+refinements.
+.It "Hubert Feyrer"
+.Nx
+wildcard dependency processing, pkgdb, upgrading, etc.
+.It Thomas Klausner
+HTTP support.
+.It Joerg Sonnenberger
+Rewrote most of the code base to work without external commands.
+.El
+.Sh BUGS
+Package upgrading needs a lot more work to be really universal.
+.Pp
+Sure to be others.
--- /dev/null
+/*-
+ * Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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.
+ */
+
+extern int quiet;
+extern int verbose;
+
+void check(char **);
+
+void audit_pkgdb(int, char **);
+void audit_pkg(int, char **);
+void audit_batch(int, char **);
+void audit_history(int, char **);
+void check_pkg_vulnerabilities(int, char **);
+void fetch_pkg_vulnerabilities(int, char **);
+
+void usage(void);
--- /dev/null
+.\" $NetBSD: audit-packages.8,v 1.1.1.1 2010/04/23 20:54:06 joerg Exp $
+.\"
+.\" Copyright (c) 2010 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by 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.
+.\"
+.\" 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.
+.\"
+.Dd March 18, 2010
+.Dt AUDIT-PACKAGES 8
+.Os
+.Sh NAME
+.Nm audit-packages
+.Nd report vulnerabilities for the installed packages
+.Sh SYNOPSIS
+.Nm
+.Op Fl deqsVv
+.Op Fl c Ar config_file
+.Op Fl F Ar file
+.Op Fl g Ar file
+.Op Fl h Ar file
+.Op Fl K Ar pkg_dbdir
+.Op Fl n Ar package
+.Op Fl p Ar package
+.Op Fl Q Ar varname
+.Op Fl t Ar type
+.Sh DESCRIPTION
+.Nm
+is deprecated.
+Please use the
+.Cm audit ,
+.Cm audit-pkg ,
+.Cm audit-batch ,
+and
+.Cm fetch-pkg-vulnerabilities
+commands of
+.Xr pkg_admin 1
+instead.
+.Pp
+The
+.Nm
+script is installed for backwards compatibility only and will
+eventually be removed.
+.Sh SEE ALSO
+.Xr pkg_admin 1
--- /dev/null
+#!/bin/sh
+
+pkg_admin=@PKG_ADMIN@
+
+usage() {
+ echo 'Usage: audit-packages [-deqsVv] [-c config_file] [-F file]' >& $2
+ echo ' [-g file] [-h file]' >& $2
+ echo ' [-K pkg_dbdir] [-n package] [-p package]' >& $2
+ echo ' [-Q varname] [-t type]' >& $2
+ echo "Please use the audit, audit-pkg, audit-batch and fetch-pkg-vulnerabilities" >& $2
+ echo "commands of pkg_admin instead." >& $2
+ exit $1
+}
+
+do_pkgdb=
+do_eol=
+do_fetch=
+do_quiet=
+do_sign=
+do_verbose=
+do_check_file=
+do_check_pattern=
+do_check_installed=
+do_check_vul_file=
+do_limit_type=
+do_print_var=
+
+args=`getopt F:K:Q:Vc:deg:h:n:p:qst:v $*`
+if [ $? -ne 0 ]; then
+ usage 1 2
+fi
+
+set -- $args
+
+while [ $# -gt 0 ]; do
+ case "$1" in
+ -F)
+ do_check_file=$2
+ shift
+ ;;
+ -K)
+ do_pkgdb="$1 $2"
+ shift
+ ;;
+ -Q)
+ do_print_var="$2"
+ shift
+ ;;
+ -V)
+ exec ${pkg_admin} -V
+ ;;
+ -c)
+ echo "The audit-packages wrapper does not support -c" >&2
+ echo "Please use the audit, audit-pkg, audit-batch and fetch-pkg-vulnerabilities" >& 2
+ echo "commands of pkg_admin instead." >& 2
+ exit 1
+ ;;
+ -d)
+ do_fetch=1
+ ;;
+ -e)
+ do_eol=-e
+ ;;
+ -g)
+ echo "The audit-packages wrapper does not support -g" >&2
+ echo "Please switch to \`\`pkg_admin fetch-pkg-vulnerabilities''." >&2
+ exit 1
+ ;;
+ -h)
+ do_check_vul_file=$2
+ shift
+ ;;
+ -n)
+ do_check_pattern=$2
+ shift
+ ;;
+ -p)
+ do_check_installed=$2
+ shift
+ ;;
+ -q)
+ do_quiet=-q
+ ;;
+ -s)
+ do_sign=-s
+ ;;
+ -t)
+ do_limit_type="-t $2"
+ shift
+ ;;
+ -v)
+ do_verbose="$do_verbose -v"
+ ;;
+ esac
+ shift
+done
+
+if [ -n "${do_fetch}" ]; then
+ exec ${pkg_admin} ${do_pkgdb} fetch-pkg-vulnerabilities ${do_sign}
+fi
+
+if [ -n "${do_check_vul_file}" ]; then
+ exec ${pkg_admin} ${do_pkgdb} check-pkg-vulnerabilities ${do_sign} "${do_check_vul_file}"
+fi
+
+if [ -n "${do_print_var}" ]; then
+ exec ${pkg_admin} ${do_pkgdb} config-var "${do_print_var}"
+fi
+
+if [ -n "${do_check_file}" ]; then
+ if [ -n "${do_check_pattern}" -o -n "${do_check_installed}" ]; then
+ echo "Only one of -F, -n or -p is interpreted at a time." >& 2
+ usage 1 2
+ fi
+ exec ${pkg_admin} ${do_pkgdb} ${do_verbose} ${do_quiet} audit-pkg \
+ ${do_eol} ${do_limit_type} ${do_check_file}
+fi
+
+if [ -n "${do_check_pattern}" ]; then
+ if [ -n "${do_check_installed}" ]; then
+ echo "Only one of -F, -n or -p is interpreted at a time." >& 2
+ usage 1 2
+ fi
+ exec ${pkg_admin} ${do_pkgdb} ${do_verbose} ${do_quiet} audit-pkg \
+ ${do_eol} ${do_limit_type} ${do_check_pattern}
+fi
+
+# If do_check_installed is empty, all packages are checked.
+exec ${pkg_admin} ${do_pkgdb} ${do_verbose} ${do_quiet} audit \
+ ${do_eol} ${do_limit_type} ${do_check_installed}
--- /dev/null
+/* $NetBSD: audit.c,v 1.1.1.9 2011/02/18 22:32:28 aymeric Exp $ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+__RCSID("$NetBSD: audit.c,v 1.1.1.9 2011/02/18 22:32:28 aymeric Exp $");
+
+/*-
+ * Copyright (c) 2008 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.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_SIGNAL_H
+#include <signal.h>
+#endif
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef NETBSD
+#include <unistd.h>
+#else
+#include <nbcompat/unistd.h>
+#endif
+
+#include <fetch.h>
+
+#include "admin.h"
+#include "lib.h"
+
+static int check_signature = 0;
+static const char *limit_vul_types = NULL;
+static int update_pkg_vuln = 0;
+
+static struct pkg_vulnerabilities *pv;
+
+static const char audit_options[] = "est:";
+
+static void
+parse_options(int argc, char **argv, const char *options)
+{
+ int ch;
+
+ optreset = 1;
+ /*
+ * optind == 0 is interpreted as partial reset request
+ * by GNU getopt, so compensate against this and cleanup
+ * at the end.
+ */
+ optind = 1;
+ ++argc;
+ --argv;
+
+ while ((ch = getopt(argc, argv, options)) != -1) {
+ switch (ch) {
+ case 'e':
+ check_eol = "yes";
+ break;
+ case 's':
+ check_signature = 1;
+ break;
+ case 't':
+ limit_vul_types = optarg;
+ break;
+ case 'u':
+ update_pkg_vuln = 1;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+
+ --optind; /* See above comment. */
+}
+
+static int
+check_exact_pkg(const char *pkg)
+{
+ return audit_package(pv, pkg, limit_vul_types, quiet ? 0 : 1);
+}
+
+static int
+check_batch_exact_pkgs(const char *fname)
+{
+ FILE *f;
+ char buf[4096], *line, *eol;
+ int ret;
+
+ ret = 0;
+ if (strcmp(fname, "-") == 0)
+ f = stdin;
+ else {
+ f = fopen(fname, "r");
+ if (f == NULL)
+ err(EXIT_FAILURE, "Failed to open input file %s",
+ fname);
+ }
+ while ((line = fgets(buf, sizeof(buf), f)) != NULL) {
+ eol = line + strlen(line);
+ if (eol == line)
+ continue;
+ --eol;
+ if (*eol == '\n') {
+ if (eol == line)
+ continue;
+ *eol = '\0';
+ }
+ ret |= check_exact_pkg(line);
+ }
+ if (f != stdin)
+ fclose(f);
+
+ return ret;
+}
+
+static int
+check_one_installed_pkg(const char *pkg, void *cookie)
+{
+ int *ret = cookie;
+
+ *ret |= check_exact_pkg(pkg);
+ return 0;
+}
+
+static int
+check_installed_pattern(const char *pattern)
+{
+ int ret = 0;
+
+ match_installed_pkgs(pattern, check_one_installed_pkg, &ret);
+
+ return ret;
+}
+
+static void
+check_and_read_pkg_vulnerabilities(void)
+{
+ struct stat st;
+ time_t now;
+
+ if (pkg_vulnerabilities_file == NULL)
+ errx(EXIT_FAILURE, "PKG_VULNERABILITIES is not set");
+
+ if (verbose >= 1) {
+ if (stat(pkg_vulnerabilities_file, &st) == -1) {
+ if (errno == ENOENT)
+ errx(EXIT_FAILURE,
+ "pkg-vulnerabilities not found, run %s -d",
+ getprogname());
+ errx(EXIT_FAILURE, "pkg-vulnerabilities not readable");
+ }
+ now = time(NULL);
+ now -= st.st_mtime;
+ if (now < 0)
+ warnx("pkg-vulnerabilities is from the future");
+ else if (now > 86400 * 7)
+ warnx("pkg-vulnerabilities is out of date (%ld days old)",
+ (long)(now / 86400));
+ else if (verbose >= 2)
+ warnx("pkg-vulnerabilities is %ld day%s old",
+ (long)(now / 86400), now / 86400 == 1 ? "" : "s");
+ }
+
+ pv = read_pkg_vulnerabilities_file(pkg_vulnerabilities_file, 0, check_signature);
+}
+
+void
+audit_pkgdb(int argc, char **argv)
+{
+ int rv;
+
+ parse_options(argc, argv, audit_options);
+ argv += optind;
+
+ check_and_read_pkg_vulnerabilities();
+
+ rv = 0;
+ if (*argv == NULL)
+ rv |= check_installed_pattern("*");
+ else {
+ for (; *argv != NULL; ++argv)
+ rv |= check_installed_pattern(*argv);
+ }
+ free_pkg_vulnerabilities(pv);
+
+ if (rv == 0 && verbose >= 1)
+ fputs("No vulnerabilities found\n", stderr);
+ exit(rv ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+void
+audit_pkg(int argc, char **argv)
+{
+ int rv;
+
+ parse_options(argc, argv, audit_options);
+ argv += optind;
+
+ check_and_read_pkg_vulnerabilities();
+ rv = 0;
+ for (; *argv != NULL; ++argv)
+ rv |= check_exact_pkg(*argv);
+
+ free_pkg_vulnerabilities(pv);
+
+ if (rv == 0 && verbose >= 1)
+ fputs("No vulnerabilities found\n", stderr);
+ exit(rv ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+void
+audit_batch(int argc, char **argv)
+{
+ int rv;
+
+ parse_options(argc, argv, audit_options);
+ argv += optind;
+
+ check_and_read_pkg_vulnerabilities();
+ rv = 0;
+ for (; *argv != NULL; ++argv)
+ rv |= check_batch_exact_pkgs(*argv);
+ free_pkg_vulnerabilities(pv);
+
+ if (rv == 0 && verbose >= 1)
+ fputs("No vulnerabilities found\n", stderr);
+ exit(rv ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+void
+check_pkg_vulnerabilities(int argc, char **argv)
+{
+ parse_options(argc, argv, "s");
+ if (argc != optind + 1)
+ usage();
+
+ pv = read_pkg_vulnerabilities_file(argv[optind], 0, check_signature);
+ free_pkg_vulnerabilities(pv);
+}
+
+void
+fetch_pkg_vulnerabilities(int argc, char **argv)
+{
+ struct pkg_vulnerabilities *pv_check;
+ char *buf;
+ size_t buf_len, buf_fetched;
+ ssize_t cur_fetched;
+ struct url *url;
+ struct url_stat st;
+ fetchIO *f;
+ int fd;
+ struct stat sb;
+ char my_flags[20];
+ const char *flags;
+
+ parse_options(argc, argv, "su");
+ if (argc != optind)
+ usage();
+
+ if (verbose >= 2)
+ fprintf(stderr, "Fetching %s\n", pkg_vulnerabilities_url);
+
+ url = fetchParseURL(pkg_vulnerabilities_url);
+ if (url == NULL)
+ errx(EXIT_FAILURE,
+ "Could not parse location of pkg_vulnerabilities: %s",
+ fetchLastErrString);
+
+ flags = fetch_flags;
+ if (update_pkg_vuln) {
+ fd = open(pkg_vulnerabilities_file, O_RDONLY);
+ if (fd != -1 && fstat(fd, &sb) != -1) {
+ url->last_modified = sb.st_mtime;
+ snprintf(my_flags, sizeof(my_flags), "%si",
+ fetch_flags);
+ flags = my_flags;
+ } else
+ update_pkg_vuln = 0;
+ if (fd != -1)
+ close(fd);
+ }
+
+ f = fetchXGet(url, &st, flags);
+ if (f == NULL && update_pkg_vuln &&
+ fetchLastErrCode == FETCH_UNCHANGED) {
+ if (verbose >= 1)
+ fprintf(stderr, "%s is not newer\n",
+ pkg_vulnerabilities_url);
+ exit(EXIT_SUCCESS);
+ }
+
+ if (f == NULL)
+ errx(EXIT_FAILURE, "Could not fetch vulnerability file: %s",
+ fetchLastErrString);
+
+ if (st.size > SSIZE_MAX - 1)
+ errx(EXIT_FAILURE, "pkg-vulnerabilities is too large");
+
+ buf_len = st.size;
+ buf = xmalloc(buf_len + 1);
+ buf_fetched = 0;
+
+ while (buf_fetched < buf_len) {
+ cur_fetched = fetchIO_read(f, buf + buf_fetched,
+ buf_len - buf_fetched);
+ if (cur_fetched == 0)
+ errx(EXIT_FAILURE,
+ "Truncated pkg-vulnerabilities received");
+ else if (cur_fetched == -1)
+ errx(EXIT_FAILURE,
+ "IO error while fetching pkg-vulnerabilities: %s",
+ fetchLastErrString);
+ buf_fetched += cur_fetched;
+ }
+
+ buf[buf_len] = '\0';
+
+ pv_check = read_pkg_vulnerabilities_memory(buf, buf_len, check_signature);
+ free_pkg_vulnerabilities(pv_check);
+
+ fd = open(pkg_vulnerabilities_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (fd == -1)
+ err(EXIT_FAILURE, "Cannot create pkg-vulnerability file %s",
+ pkg_vulnerabilities_file);
+
+ if (write(fd, buf, buf_len) != (ssize_t)buf_len)
+ err(EXIT_FAILURE, "Cannot write pkg-vulnerability file");
+ if (close(fd) == -1)
+ err(EXIT_FAILURE, "Cannot close pkg-vulnerability file after write");
+
+ free(buf);
+
+ exit(EXIT_SUCCESS);
+}
+
+static int
+check_pkg_history_pattern(const char *pkg, const char *pattern)
+{
+ const char *delim, *end_base;
+
+ if (strpbrk(pattern, "*[") != NULL) {
+ end_base = NULL;
+ for (delim = pattern;
+ *delim != '\0' && *delim != '['; delim++) {
+ if (*delim == '-')
+ end_base = delim;
+ }
+
+ if (end_base == NULL)
+ errx(EXIT_FAILURE, "Missing - in wildcard pattern %s",
+ pattern);
+ if ((delim = strchr(pattern, '>')) != NULL ||
+ (delim = strchr(pattern, '<')) != NULL)
+ errx(EXIT_FAILURE,
+ "Mixed relational and wildcard patterns in %s",
+ pattern);
+ } else if ((delim = strchr(pattern, '>')) != NULL) {
+ end_base = delim;
+ if ((delim = strchr(pattern, '<')) != NULL && delim < end_base)
+ errx(EXIT_FAILURE, "Inverted operators in %s",
+ pattern);
+ } else if ((delim = strchr(pattern, '<')) != NULL) {
+ end_base = delim;
+ } else if ((end_base = strrchr(pattern, '-')) == NULL) {
+ errx(EXIT_FAILURE, "Missing - in absolute pattern %s",
+ pattern);
+ }
+
+ if (strncmp(pkg, pattern, end_base - pattern) != 0)
+ return 0;
+ if (pkg[end_base - pattern] != '\0')
+ return 0;
+
+ return 1;
+}
+
+static int
+check_pkg_history1(const char *pkg, const char *pattern)
+{
+ const char *open_brace, *close_brace, *inner_brace, *suffix, *iter;
+ size_t prefix_len, suffix_len, middle_len;
+ char *expanded_pkg;
+
+ open_brace = strchr(pattern, '{');
+ if (open_brace == NULL) {
+ if ((close_brace = strchr(pattern, '}')) != NULL)
+ errx(EXIT_FAILURE, "Unbalanced {} in pattern %s",
+ pattern);
+ return check_pkg_history_pattern(pkg, pattern);
+ }
+ close_brace = strchr(open_brace, '}');
+ if (strchr(pattern, '}') != close_brace)
+ errx(EXIT_FAILURE, "Unbalanced {} in pattern %s",
+ pattern);
+
+ while ((inner_brace = strchr(open_brace + 1, '{')) != NULL) {
+ if (inner_brace >= close_brace)
+ break;
+ open_brace = inner_brace;
+ }
+
+ expanded_pkg = xmalloc(strlen(pattern)); /* {} are going away... */
+
+ prefix_len = open_brace - pattern;
+ suffix = close_brace + 1;
+ suffix_len = strlen(suffix) + 1;
+ memcpy(expanded_pkg, pattern, prefix_len);
+
+ ++open_brace;
+
+ do {
+ iter = strchr(open_brace, ',');
+ if (iter == NULL || iter > close_brace)
+ iter = close_brace;
+
+ middle_len = iter - open_brace;
+ memcpy(expanded_pkg + prefix_len, open_brace, middle_len);
+ memcpy(expanded_pkg + prefix_len + middle_len, suffix,
+ suffix_len);
+ if (check_pkg_history1(pkg, expanded_pkg)) {
+ free(expanded_pkg);
+ return 1;
+ }
+ open_brace = iter + 1;
+ } while (iter < close_brace);
+
+ free(expanded_pkg);
+ return 0;
+}
+
+static void
+check_pkg_history(const char *pkg)
+{
+ size_t i;
+
+ for (i = 0; i < pv->entries; ++i) {
+ if (!quick_pkg_match(pv->vulnerability[i], pkg))
+ continue;
+ if (strcmp("eol", pv->classification[i]) == 0)
+ continue;
+ if (check_pkg_history1(pkg, pv->vulnerability[i]) == 0)
+ continue;
+
+ printf("%s %s %s\n", pv->vulnerability[i],
+ pv->classification[i], pv->advisory[i]);
+ }
+}
+
+void
+audit_history(int argc, char **argv)
+{
+ parse_options(argc, argv, "st:");
+ argv += optind;
+
+ check_and_read_pkg_vulnerabilities();
+ for (; *argv != NULL; ++argv)
+ check_pkg_history(*argv);
+
+ free_pkg_vulnerabilities(pv);
+ exit(EXIT_SUCCESS);
+}
--- /dev/null
+/* $NetBSD: check.c,v 1.1.1.4 2010/01/30 21:33:23 joerg Exp $ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+__RCSID("$NetBSD: check.c,v 1.1.1.4 2010/01/30 21:33:23 joerg Exp $");
+
+/*-
+ * 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>.
+ *
+ * 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.
+ *
+ * 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_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_DIRENT_H
+#include <dirent.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
+#ifndef NETBSD
+#include <nbcompat/md5.h>
+#else
+#include <md5.h>
+#endif
+#if HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "admin.h"
+#include "lib.h"
+
+static int checkpattern_fn(const char *, void *);
+
+/*
+ * Assumes CWD is in /var/db/pkg/<pkg>!
+ */
+static void
+check1pkg(const char *pkgdir, int *filecnt, int *pkgcnt)
+{
+ FILE *f;
+ plist_t *p;
+ package_t Plist;
+ char *PkgName, *dirp = NULL, *md5file;
+ char file[MaxPathSize];
+ char *content;
+
+ content = pkgdb_pkg_file(pkgdir, CONTENTS_FNAME);
+ f = fopen(content, "r");
+ if (f == NULL)
+ err(EXIT_FAILURE, "can't open %s", content);
+ free(content);
+
+ read_plist(&Plist, f);
+ p = find_plist(&Plist, PLIST_NAME);
+ if (p == NULL)
+ errx(EXIT_FAILURE, "Package %s has no @name, aborting.",
+ pkgdir);
+ PkgName = p->name;
+ for (p = Plist.head; p; p = p->next) {
+ switch (p->type) {
+ case PLIST_FILE:
+ if (dirp == NULL) {
+ warnx("dirp not initialized, please send-pr!");
+ abort();
+ }
+
+ (void) snprintf(file, sizeof(file), "%s/%s", dirp, p->name);
+
+ if (isfile(file) || islinktodir(file)) {
+ if (p->next && p->next->type == PLIST_COMMENT) {
+ if (strncmp(p->next->name, CHECKSUM_HEADER, ChecksumHeaderLen) == 0) {
+ if ((md5file = MD5File(file, NULL)) != NULL) {
+ /* Mismatch? */
+ if (strcmp(md5file, p->next->name + ChecksumHeaderLen) != 0)
+ printf("%s fails MD5 checksum\n", file);
+
+ free(md5file);
+ }
+ } else if (strncmp(p->next->name, SYMLINK_HEADER, SymlinkHeaderLen) == 0) {
+ char buf[MaxPathSize + SymlinkHeaderLen];
+ int cc;
+
+ (void) strlcpy(buf, SYMLINK_HEADER, sizeof(buf));
+ if ((cc = readlink(file, &buf[SymlinkHeaderLen],
+ sizeof(buf) - SymlinkHeaderLen - 1)) < 0) {
+ warnx("can't readlink `%s'", file);
+ } else {
+ buf[SymlinkHeaderLen + cc] = 0x0;
+ if (strcmp(buf, p->next->name) != 0) {
+ printf("symlink (%s) is not same as recorded value, %s: %s\n",
+ file, buf, p->next->name);
+ }
+ }
+ }
+ }
+
+ (*filecnt)++;
+ } else if (isbrokenlink(file)) {
+ warnx("%s: Symlink `%s' exists and is in %s but target does not exist!", PkgName, file, CONTENTS_FNAME);
+ } else {
+ warnx("%s: File `%s' is in %s but not on filesystem!", PkgName, file, CONTENTS_FNAME);
+ }
+ break;
+ case PLIST_CWD:
+ if (strcmp(p->name, ".") != 0)
+ dirp = p->name;
+ else
+ dirp = pkgdb_pkg_dir(pkgdir);
+ break;
+ case PLIST_IGNORE:
+ p = p->next;
+ break;
+ case PLIST_SHOW_ALL:
+ case PLIST_SRC:
+ case PLIST_CMD:
+ case PLIST_CHMOD:
+ case PLIST_CHOWN:
+ case PLIST_CHGRP:
+ case PLIST_COMMENT:
+ case PLIST_NAME:
+ case PLIST_UNEXEC:
+ case PLIST_DISPLAY:
+ case PLIST_PKGDEP:
+ case PLIST_DIR_RM:
+ case PLIST_OPTION:
+ case PLIST_PKGCFL:
+ case PLIST_BLDDEP:
+ case PLIST_PKGDIR:
+ break;
+ }
+ }
+ free_plist(&Plist);
+ fclose(f);
+ (*pkgcnt)++;
+}
+
+struct checkpattern_arg {
+ int filecnt;
+ int pkgcnt;
+ int got_match;
+};
+
+static int
+checkpattern_fn(const char *pkg, void *vp)
+{
+ struct checkpattern_arg *arg = vp;
+
+ check1pkg(pkg, &arg->filecnt, &arg->pkgcnt);
+ if (!quiet)
+ printf(".");
+
+ arg->got_match = 1;
+
+ return 0;
+}
+
+static void
+check_pkg(const char *pkg, int *filecnt, int *pkgcnt, int allow_unmatched)
+{
+ struct checkpattern_arg arg;
+ char *pattern;
+
+ arg.filecnt = *filecnt;
+ arg.pkgcnt = *pkgcnt;
+ arg.got_match = 0;
+
+ if (match_installed_pkgs(pkg, checkpattern_fn, &arg) == -1)
+ errx(EXIT_FAILURE, "Cannot process pkdbdb");
+ if (arg.got_match != 0) {
+ *filecnt = arg.filecnt;
+ *pkgcnt = arg.pkgcnt;
+ return;
+ }
+
+ if (ispkgpattern(pkg)) {
+ if (allow_unmatched)
+ return;
+ errx(EXIT_FAILURE, "No matching pkg for %s.", pkg);
+ }
+
+ pattern = xasprintf("%s-[0-9]*", pkg);
+
+ if (match_installed_pkgs(pattern, checkpattern_fn, &arg) == -1)
+ errx(EXIT_FAILURE, "Cannot process pkdbdb");
+
+ if (arg.got_match == 0)
+ errx(EXIT_FAILURE, "cannot find package %s", pkg);
+ free(pattern);
+
+ *filecnt = arg.filecnt;
+ *pkgcnt = arg.pkgcnt;
+}
+
+void
+check(char **argv)
+{
+ int filecnt, pkgcnt;
+
+ filecnt = 0;
+ pkgcnt = 0;
+ setbuf(stdout, NULL);
+
+ if (*argv == NULL) {
+ check_pkg("*", &filecnt, &pkgcnt, 1);
+ } else {
+ for (; *argv != NULL; ++argv)
+ check_pkg(*argv, &filecnt, &pkgcnt, 0);
+ }
+
+ printf("\n");
+ printf("Checked %d file%s from %d package%s.\n",
+ filecnt, (filecnt == 1) ? "" : "s",
+ pkgcnt, (pkgcnt == 1) ? "" : "s");
+}
--- /dev/null
+.\" $NetBSD: download-vulnerability-list.8,v 1.1.1.1 2010/04/23 20:54:06 joerg Exp $
+.\"
+.\" Copyright (c) 2010 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by 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.
+.\"
+.\" 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.
+.\"
+.Dd March 18, 2010
+.Dt DOWNLOAD-VULNERABILITY-LIST 8
+.Os
+.Sh NAME
+.Nm download-vulnerability-list
+.Nd download vulnerability list used for checking installed packages
+.Sh SYNOPSIS
+.Nm
+.Op Fl hs
+.Op Fl c Ar config_file
+.Sh DESCRIPTION
+.Nm
+is deprecated.
+Please use the
+.Cm fetch-pkg-vulnerabilities
+command of
+.Xr pkg_admin 1
+instead.
+.Pp
+The
+.Nm
+script is installed for backwards compatibility only and will
+eventually be removed.
+.Sh SEE ALSO
+.Xr pkg_admin 1
--- /dev/null
+#!/bin/sh
+
+pkg_admin=@PKG_ADMIN@
+
+usage() {
+ echo 'Usage: download-vulnerability-list [-hs] [-c config_file]' >& $2
+ echo "Please use \`\`pkg_admin fetch-pkg-vulnerabilities'' instead." >& $2
+ exit $1
+}
+
+do_sign=
+
+args=`getopt c:hs $*`
+if [ $? -ne 0 ]; then
+ usage 1 2
+fi
+
+set -- $args
+
+while [ $# -gt 0 ]; do
+ case "$1" in
+ -c)
+ echo "The download-vulnerability-list wrapper does not support -c" >&2
+ echo "Please switch to \`\`pkg_admin fetch-pkg-vulnerabilities''." >&2
+ exit 1
+ ;;
+ -h)
+ usage 0 1
+ ;;
+ -s)
+ do_sign=-s
+ ;;
+ esac
+ shift
+done
+
+exec ${pkg_admin} fetch-pkg-vulnerabilities ${do_sign}
--- /dev/null
+/* $NetBSD: main.c,v 1.1.1.15 2010/04/23 20:54:07 joerg Exp $ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+__RCSID("$NetBSD: main.c,v 1.1.1.15 2010/04/23 20:54:07 joerg Exp $");
+
+/*-
+ * Copyright (c) 1999-2009 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
+ * by Joerg Sonnenberger <joerg@NetBSD.org>.
+ *
+ * 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.
+ *
+ * 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_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_DIRENT_H
+#include <dirent.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
+#ifndef NETBSD
+#include <nbcompat/md5.h>
+#else
+#include <md5.h>
+#endif
+#if HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifndef BOOTSTRAP
+#include <archive.h>
+#include <fetch.h>
+#endif
+
+#include "admin.h"
+#include "lib.h"
+
+#define DEFAULT_SFX ".t[bg]z" /* default suffix for ls{all,best} */
+
+struct pkgdb_count {
+ size_t files;
+ size_t directories;
+ size_t packages;
+};
+
+static const char Options[] = "C:K:SVbd:qs:v";
+
+int quiet, verbose;
+
+static void set_unset_variable(char **, Boolean);
+
+/* print usage message and exit */
+void
+usage(void)
+{
+ (void) fprintf(stderr, "usage: %s [-bqSVv] [-C config] [-d lsdir] [-K pkg_dbdir] [-s sfx] command [args ...]\n"
+ "Where 'commands' and 'args' are:\n"
+ " rebuild - rebuild pkgdb from +CONTENTS files\n"
+ " rebuild-tree - rebuild +REQUIRED_BY files from forward deps\n"
+ " check [pkg ...] - check md5 checksum of installed files\n"
+ " add pkg ... - add pkg files to database\n"
+ " delete pkg ... - delete file entries for pkg in database\n"
+ " set variable=value pkg ... - set installation variable for package\n"
+ " unset variable pkg ... - unset installation variable for package\n"
+ " lsall /path/to/pkgpattern - list all pkgs matching the pattern\n"
+ " lsbest /path/to/pkgpattern - list pkgs matching the pattern best\n"
+ " dump - dump database\n"
+ " pmatch pattern pkg - returns true if pkg matches pattern, otherwise false\n"
+ " fetch-pkg-vulnerabilities [-s] - fetch new vulnerability file\n"
+ " check-pkg-vulnerabilities [-s] <file> - check syntax and checksums of the vulnerability file\n"
+ " audit [-es] [-t type] ... - check installed packages for vulnerabilities\n"
+ " audit-pkg [-es] [-t type] ... - check listed packages for vulnerabilities\n"
+ " audit-batch [-es] [-t type] ... - check packages in listed files for vulnerabilities\n"
+ " audit-history [-t type] ... - print all advisories for package names\n"
+ " check-license <condition> - check if condition is acceptable\n"
+ " check-single-license <license> - check if license is acceptable\n"
+ " config-var name - print current value of the configuration variable\n"
+ " check-signature ... - verify the signature of packages\n"
+ " x509-sign-package pkg spkg key cert - create X509 signature\n"
+ " gpg-sign-package pkg spkg - create GPG signature\n",
+ getprogname());
+ exit(EXIT_FAILURE);
+}
+
+/*
+ * add1pkg(<pkg>)
+ * adds the files listed in the +CONTENTS of <pkg> into the
+ * pkgdb.byfile.db database file in the current package dbdir. It
+ * returns the number of files added to the database file.
+ */
+static int
+add_pkg(const char *pkgdir, void *vp)
+{
+ FILE *f;
+ plist_t *p;
+ package_t Plist;
+ char *contents;
+ char *PkgName, *dirp;
+ char file[MaxPathSize];
+ struct pkgdb_count *count;
+
+ if (!pkgdb_open(ReadWrite))
+ err(EXIT_FAILURE, "cannot open pkgdb");
+
+ count = vp;
+ ++count->packages;
+
+ contents = pkgdb_pkg_file(pkgdir, CONTENTS_FNAME);
+ if ((f = fopen(contents, "r")) == NULL)
+ errx(EXIT_FAILURE, "%s: can't open `%s'", pkgdir, CONTENTS_FNAME);
+ free(contents);
+
+ read_plist(&Plist, f);
+ if ((p = find_plist(&Plist, PLIST_NAME)) == NULL) {
+ errx(EXIT_FAILURE, "Package `%s' has no @name, aborting.", pkgdir);
+ }
+
+ PkgName = p->name;
+ dirp = NULL;
+ for (p = Plist.head; p; p = p->next) {
+ switch(p->type) {
+ case PLIST_FILE:
+ if (dirp == NULL) {
+ errx(EXIT_FAILURE, "@cwd not yet found, please send-pr!");
+ }
+ (void) snprintf(file, sizeof(file), "%s/%s", dirp, p->name);
+ if (!(isfile(file) || islinktodir(file))) {
+ if (isbrokenlink(file)) {
+ warnx("%s: Symlink `%s' exists and is in %s but target does not exist!",
+ PkgName, file, CONTENTS_FNAME);
+ } else {
+ warnx("%s: File `%s' is in %s but not on filesystem!",
+ PkgName, file, CONTENTS_FNAME);
+ }
+ } else {
+ pkgdb_store(file, PkgName);
+ ++count->files;
+ }
+ break;
+ case PLIST_PKGDIR:
+ add_pkgdir(PkgName, dirp, p->name);
+ ++count->directories;
+ break;
+ case PLIST_CWD:
+ if (strcmp(p->name, ".") != 0)
+ dirp = p->name;
+ else
+ dirp = pkgdb_pkg_dir(pkgdir);
+ break;
+ case PLIST_IGNORE:
+ p = p->next;
+ break;
+ case PLIST_SHOW_ALL:
+ case PLIST_SRC:
+ case PLIST_CMD:
+ case PLIST_CHMOD:
+ case PLIST_CHOWN:
+ case PLIST_CHGRP:
+ case PLIST_COMMENT:
+ case PLIST_NAME:
+ case PLIST_UNEXEC:
+ case PLIST_DISPLAY:
+ case PLIST_PKGDEP:
+ case PLIST_DIR_RM:
+ case PLIST_OPTION:
+ case PLIST_PKGCFL:
+ case PLIST_BLDDEP:
+ break;
+ }
+ }
+ free_plist(&Plist);
+ fclose(f);
+ pkgdb_close();
+
+ return 0;
+}
+
+static void
+delete1pkg(const char *pkgdir)
+{
+ if (!pkgdb_open(ReadWrite))
+ err(EXIT_FAILURE, "cannot open pkgdb");
+ (void) pkgdb_remove_pkg(pkgdir);
+ pkgdb_close();
+}
+
+static void
+rebuild(void)
+{
+ char *cachename;
+ struct pkgdb_count count;
+
+ count.files = 0;
+ count.directories = 0;
+ count.packages = 0;
+
+ cachename = pkgdb_get_database();
+ if (unlink(cachename) != 0 && errno != ENOENT)
+ err(EXIT_FAILURE, "unlink %s", cachename);
+
+ setbuf(stdout, NULL);
+
+ iterate_pkg_db(add_pkg, &count);
+
+ printf("\n");
+ printf("Stored %" PRIzu " file%s and %zu explicit director%s"
+ " from %"PRIzu " package%s in %s.\n",
+ count.files, count.files == 1 ? "" : "s",
+ count.directories, count.directories == 1 ? "y" : "ies",
+ count.packages, count.packages == 1 ? "" : "s",
+ cachename);
+}
+
+static int
+lspattern(const char *pkg, void *vp)
+{
+ const char *dir = vp;
+ printf("%s/%s\n", dir, pkg);
+ return 0;
+}
+
+static int
+lsbasepattern(const char *pkg, void *vp)
+{
+ puts(pkg);
+ return 0;
+}
+
+static int
+remove_required_by(const char *pkgname, void *cookie)
+{
+ char *path;
+
+ path = pkgdb_pkg_file(pkgname, REQUIRED_BY_FNAME);
+
+ if (unlink(path) == -1 && errno != ENOENT)
+ err(EXIT_FAILURE, "Cannot remove %s", path);
+
+ free(path);
+
+ return 0;
+}
+
+static void
+add_required_by(const char *pattern, const char *required_by)
+{
+ char *best_installed, *path;
+ int fd;
+ size_t len;
+
+ best_installed = find_best_matching_installed_pkg(pattern);
+ if (best_installed == NULL) {
+ warnx("Dependency %s of %s unresolved", pattern, required_by);
+ return;
+ }
+
+ path = pkgdb_pkg_file(best_installed, REQUIRED_BY_FNAME);
+ free(best_installed);
+
+ if ((fd = open(path, O_WRONLY | O_APPEND | O_CREAT, 0644)) == -1)
+ errx(EXIT_FAILURE, "Cannot write to %s", path);
+ free(path);
+
+ len = strlen(required_by);
+ if (write(fd, required_by, len) != (ssize_t)len ||
+ write(fd, "\n", 1) != 1 ||
+ close(fd) == -1)
+ errx(EXIT_FAILURE, "Cannot write to %s", path);
+}
+
+
+static int
+add_depends_of(const char *pkgname, void *cookie)
+{
+ FILE *fp;
+ plist_t *p;
+ package_t plist;
+ char *path;
+
+ path = pkgdb_pkg_file(pkgname, CONTENTS_FNAME);
+ if ((fp = fopen(path, "r")) == NULL)
+ errx(EXIT_FAILURE, "Cannot read %s of package %s",
+ CONTENTS_FNAME, pkgname);
+ free(path);
+ read_plist(&plist, fp);
+ fclose(fp);
+
+ for (p = plist.head; p; p = p->next) {
+ if (p->type == PLIST_PKGDEP)
+ add_required_by(p->name, pkgname);
+ }
+
+ free_plist(&plist);
+
+ return 0;
+}
+
+static void
+rebuild_tree(void)
+{
+ if (iterate_pkg_db(remove_required_by, NULL) == -1)
+ errx(EXIT_FAILURE, "cannot iterate pkgdb");
+ if (iterate_pkg_db(add_depends_of, NULL) == -1)
+ errx(EXIT_FAILURE, "cannot iterate pkgdb");
+}
+
+int
+main(int argc, char *argv[])
+{
+ Boolean use_default_sfx = TRUE;
+ Boolean show_basename_only = FALSE;
+ char lsdir[MaxPathSize];
+ char sfx[MaxPathSize];
+ char *lsdirp = NULL;
+ int ch;
+
+ setprogname(argv[0]);
+
+ if (argc < 2)
+ usage();
+
+ while ((ch = getopt(argc, argv, Options)) != -1)
+ switch (ch) {
+ case 'C':
+ config_file = optarg;
+ break;
+
+ case 'K':
+ pkgdb_set_dir(optarg, 3);
+ break;
+
+ case 'S':
+ sfx[0] = 0x0;
+ use_default_sfx = FALSE;
+ break;
+
+ case 'V':
+ show_version();
+ /* NOTREACHED */
+
+ case 'b':
+ show_basename_only = TRUE;
+ break;
+
+ case 'd':
+ (void) strlcpy(lsdir, optarg, sizeof(lsdir));
+ lsdirp = lsdir;
+ break;
+
+ case 'q':
+ quiet = 1;
+ break;
+
+ case 's':
+ (void) strlcpy(sfx, optarg, sizeof(sfx));
+ use_default_sfx = FALSE;
+ break;
+
+ case 'v':
+ ++verbose;
+ break;
+
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc <= 0) {
+ usage();
+ }
+
+ /*
+ * config-var is reading the config file implicitly,
+ * so skip it here.
+ */
+ if (strcasecmp(argv[0], "config-var") != 0)
+ pkg_install_config();
+
+ if (use_default_sfx)
+ (void) strlcpy(sfx, DEFAULT_SFX, sizeof(sfx));
+
+ if (strcasecmp(argv[0], "pmatch") == 0) {
+
+ char *pattern, *pkg;
+
+ argv++; /* "pmatch" */
+
+ if (argv[0] == NULL || argv[1] == NULL) {
+ usage();
+ }
+
+ pattern = argv[0];
+ pkg = argv[1];
+
+ if (pkg_match(pattern, pkg)){
+ return 0;
+ } else {
+ return 1;
+ }
+
+ } else if (strcasecmp(argv[0], "rebuild") == 0) {
+
+ rebuild();
+ printf("Done.\n");
+
+
+ } else if (strcasecmp(argv[0], "rebuild-tree") == 0) {
+
+ rebuild_tree();
+ printf("Done.\n");
+
+ } else if (strcasecmp(argv[0], "check") == 0) {
+ argv++; /* "check" */
+
+ check(argv);
+
+ if (!quiet) {
+ printf("Done.\n");
+ }
+
+ } else if (strcasecmp(argv[0], "lsall") == 0) {
+ argv++; /* "lsall" */
+
+ while (*argv != NULL) {
+ /* args specified */
+ int rc;
+ const char *basep, *dir;
+
+ dir = lsdirp ? lsdirp : dirname_of(*argv);
+ basep = basename_of(*argv);
+
+ if (show_basename_only)
+ rc = match_local_files(dir, use_default_sfx, 1, basep, lsbasepattern, NULL);
+ else
+ rc = match_local_files(dir, use_default_sfx, 1, basep, lspattern, __UNCONST(dir));
+ if (rc == -1)
+ errx(EXIT_FAILURE, "Error from match_local_files(\"%s\", \"%s\", ...)",
+ dir, basep);
+
+ argv++;
+ }
+
+ } else if (strcasecmp(argv[0], "lsbest") == 0) {
+ argv++; /* "lsbest" */
+
+ while (*argv != NULL) {
+ /* args specified */
+ const char *basep, *dir;
+ char *p;
+
+ dir = lsdirp ? lsdirp : dirname_of(*argv);
+ basep = basename_of(*argv);
+
+ p = find_best_matching_file(dir, basep, use_default_sfx, 1);
+
+ if (p) {
+ if (show_basename_only)
+ printf("%s\n", p);
+ else
+ printf("%s/%s\n", dir, p);
+ free(p);
+ }
+
+ argv++;
+ }
+ } else if (strcasecmp(argv[0], "list") == 0 ||
+ strcasecmp(argv[0], "dump") == 0) {
+
+ pkgdb_dump();
+
+ } else if (strcasecmp(argv[0], "add") == 0) {
+ struct pkgdb_count count;
+
+ count.files = 0;
+ count.directories = 0;
+ count.packages = 0;
+
+ for (++argv; *argv != NULL; ++argv)
+ add_pkg(*argv, &count);
+ } else if (strcasecmp(argv[0], "delete") == 0) {
+ argv++; /* "delete" */
+ while (*argv != NULL) {
+ delete1pkg(*argv);
+ argv++;
+ }
+ } else if (strcasecmp(argv[0], "set") == 0) {
+ argv++; /* "set" */
+ set_unset_variable(argv, FALSE);
+ } else if (strcasecmp(argv[0], "unset") == 0) {
+ argv++; /* "unset" */
+ set_unset_variable(argv, TRUE);
+ } else if (strcasecmp(argv[0], "config-var") == 0) {
+ argv++;
+ if (argv == NULL || argv[1] != NULL)
+ errx(EXIT_FAILURE, "config-var takes exactly one argument");
+ pkg_install_show_variable(argv[0]);
+ } else if (strcasecmp(argv[0], "check-license") == 0) {
+ if (argv[1] == NULL)
+ errx(EXIT_FAILURE, "check-license takes exactly one argument");
+
+ load_license_lists();
+
+ switch (acceptable_pkg_license(argv[1])) {
+ case 0:
+ puts("no");
+ return 0;
+ case 1:
+ puts("yes");
+ return 0;
+ case -1:
+ errx(EXIT_FAILURE, "invalid license condition");
+ }
+ } else if (strcasecmp(argv[0], "check-single-license") == 0) {
+ if (argv[1] == NULL)
+ errx(EXIT_FAILURE, "check-license takes exactly one argument");
+ load_license_lists();
+
+ switch (acceptable_license(argv[1])) {
+ case 0:
+ puts("no");
+ return 0;
+ case 1:
+ puts("yes");
+ return 0;
+ case -1:
+ errx(EXIT_FAILURE, "invalid license");
+ }
+ }
+#ifndef BOOTSTRAP
+ else if (strcasecmp(argv[0], "findbest") == 0) {
+ struct url *url;
+ char *output;
+ int rc;
+
+ process_pkg_path();
+
+ rc = 0;
+ for (++argv; *argv != NULL; ++argv) {
+ url = find_best_package(NULL, *argv, 1);
+ if (url == NULL) {
+ rc = 1;
+ continue;
+ }
+ output = fetchStringifyURL(url);
+ puts(output);
+ fetchFreeURL(url);
+ free(output);
+ }
+
+ return rc;
+ } else if (strcasecmp(argv[0], "fetch-pkg-vulnerabilities") == 0) {
+ fetch_pkg_vulnerabilities(--argc, ++argv);
+ } else if (strcasecmp(argv[0], "check-pkg-vulnerabilities") == 0) {
+ check_pkg_vulnerabilities(--argc, ++argv);
+ } else if (strcasecmp(argv[0], "audit") == 0) {
+ audit_pkgdb(--argc, ++argv);
+ } else if (strcasecmp(argv[0], "audit-pkg") == 0) {
+ audit_pkg(--argc, ++argv);
+ } else if (strcasecmp(argv[0], "audit-batch") == 0) {
+ audit_batch(--argc, ++argv);
+ } else if (strcasecmp(argv[0], "audit-history") == 0) {
+ audit_history(--argc, ++argv);
+ } else if (strcasecmp(argv[0], "check-signature") == 0) {
+ struct archive *pkg;
+ int rc;
+
+ rc = 0;
+ for (--argc, ++argv; argc > 0; --argc, ++argv) {
+ char *archive_name;
+
+ pkg = open_archive(*argv, &archive_name);
+ if (pkg == NULL) {
+ warnx("%s could not be opened", *argv);
+ continue;
+ }
+ if (pkg_full_signature_check(archive_name, &pkg))
+ rc = 1;
+ free(archive_name);
+ if (!pkg)
+ archive_read_finish(pkg);
+ }
+ return rc;
+ } else if (strcasecmp(argv[0], "x509-sign-package") == 0) {
+#ifdef HAVE_SSL
+ --argc;
+ ++argv;
+ if (argc != 4)
+ errx(EXIT_FAILURE, "x509-sign-package takes exactly four arguments");
+ pkg_sign_x509(argv[0], argv[1], argv[2], argv[3]);
+#else
+ errx(EXIT_FAILURE, "OpenSSL support is not included");
+#endif
+ } else if (strcasecmp(argv[0], "gpg-sign-package") == 0) {
+ --argc;
+ ++argv;
+ if (argc != 2)
+ errx(EXIT_FAILURE, "gpg-sign-package takes exactly two arguments");
+ pkg_sign_gpg(argv[0], argv[1]);
+ }
+#endif
+ else {
+ usage();
+ }
+
+ return 0;
+}
+
+struct set_installed_info_arg {
+ char *variable;
+ char *value;
+ int got_match;
+};
+
+static int
+set_installed_info_var(const char *name, void *cookie)
+{
+ struct set_installed_info_arg *arg = cookie;
+ char *filename;
+ int retval;
+
+ filename = pkgdb_pkg_file(name, INSTALLED_INFO_FNAME);
+
+ retval = var_set(filename, arg->variable, arg->value);
+
+ free(filename);
+ arg->got_match = 1;
+
+ return retval;
+}
+
+static void
+set_unset_variable(char **argv, Boolean unset)
+{
+ struct set_installed_info_arg arg;
+ char *eq;
+ char *variable;
+ int ret = 0;
+
+ if (argv[0] == NULL || argv[1] == NULL)
+ usage();
+
+ variable = NULL;
+
+ if (unset) {
+ arg.variable = argv[0];
+ arg.value = NULL;
+ } else {
+ eq = NULL;
+ if ((eq=strchr(argv[0], '=')) == NULL)
+ usage();
+
+ variable = xmalloc(eq-argv[0]+1);
+ strlcpy(variable, argv[0], eq-argv[0]+1);
+
+ arg.variable = variable;
+ arg.value = eq+1;
+
+ if (strcmp(variable, AUTOMATIC_VARNAME) == 0 &&
+ strcasecmp(arg.value, "yes") != 0 &&
+ strcasecmp(arg.value, "no") != 0) {
+ errx(EXIT_FAILURE,
+ "unknown value `%s' for " AUTOMATIC_VARNAME,
+ arg.value);
+ }
+ }
+ if (strpbrk(arg.variable, "ABCDEFGHIJKLMNOPQRSTUVWXYZ") != NULL) {
+ free(variable);
+ errx(EXIT_FAILURE,
+ "variable name must not contain uppercase letters");
+ }
+
+ argv++;
+ while (*argv != NULL) {
+ arg.got_match = 0;
+ if (match_installed_pkgs(*argv, set_installed_info_var, &arg) == -1)
+ errx(EXIT_FAILURE, "Cannot process pkdbdb");
+ if (arg.got_match == 0) {
+ char *pattern;
+
+ if (ispkgpattern(*argv)) {
+ warnx("no matching pkg for `%s'", *argv);
+ ret++;
+ } else {
+ pattern = xasprintf("%s-[0-9]*", *argv);
+
+ if (match_installed_pkgs(pattern, set_installed_info_var, &arg) == -1)
+ errx(EXIT_FAILURE, "Cannot process pkdbdb");
+
+ if (arg.got_match == 0) {
+ warnx("cannot find package %s", *argv);
+ ++ret;
+ }
+ free(pattern);
+ }
+ }
+
+ argv++;
+ }
+
+ if (ret > 0)
+ exit(EXIT_FAILURE);
+
+ free(variable);
+
+ return;
+}
--- /dev/null
+.\" $NetBSD: pkg_admin.1,v 1.1.1.12 2013/04/20 15:26:52 wiz Exp $
+.\"
+.\" Copyright (c) 1999-2010 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Hubert Feyrer <hubert@feyrer.de>.
+.\"
+.\" 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.
+.\"
+.Dd December 14, 2012
+.Dt PKG_ADMIN 1
+.Os
+.Sh NAME
+.Nm pkg_admin
+.Nd perform various administrative tasks to the pkg system
+.Sh SYNOPSIS
+.Nm
+.Op Fl bqSVv
+.Op Fl C Ar config
+.Op Fl d Ar lsdir
+.Op Fl K Ar pkg_dbdir
+.Op Fl s Ar sfx_pattern
+.Ar command Op args ...
+.Sh DESCRIPTION
+This command performs various administrative tasks around the
+.Nx
+Packages System.
+.Sh OPTIONS
+The following command-line options are supported:
+.Bl -tag -width indent
+.It Fl b
+Print only the base names when matching package names for
+.Cm lsall
+and
+.Cm lsbest .
+.It Fl C Ar config
+Read the configuration file from
+.Ar config
+instead of the system default.
+.It Fl d Ar lsdir
+Set
+.Ar lsdir
+as the path to the directory in which to find matching package names for
+.Cm lsall
+and
+.Cm lsbest .
+.It Fl K Ar pkg_dbdir
+Override the value of the
+.Dv PKG_DBDIR
+configuration option with the value
+.Ar pkg_dbdir .
+.It Fl q
+Perform checks in a quiet manner.
+In normal operation,
+.Nm
+prints a
+.Sq \&.
+to standard output to indicate progress.
+This option suppresses this progress indicator.
+.It Fl S
+Set the shell glob pattern for package suffixes when matching package
+names for
+.Cm lsall
+and
+.Cm lsbest
+to be the null suffix.
+.It Fl s Ar sfx_pattern
+Set the shell glob pattern for package suffixes when matching package
+names for
+.Cm lsall
+and
+.Cm lsbest .
+The default pattern is ".t[bg]z".
+.It Fl V
+Print version number and exit.
+.It Fl v
+Be more verbose.
+.El
+.Pp
+The following commands are supported:
+.Bl -tag -width indent
+.It Cm add Ar pkg ...
+For each listed package, write the absolute pathnames of the files listed in
+its
+.Pa +CONTENTS
+file together with the package they belong to into the package database.
+This should be used only by
+.Xr pkg_view 1 .
+.It Cm audit Oo Fl es Oc Oo Fl t Ar type Oc Oo Ar pkg Oc ...
+Check the listed installed packages for vulnerabilities.
+If no package is given, check all installed packages.
+If
+.Fl e
+is given, override the
+.Dv CHECK_END_OF_LIFE
+option from
+.Xr pkg_install.conf 5
+with
+.Qq Li yes .
+If
+.Fl s
+is given, check the signature of the pkg-vulnerabilities file before using it.
+.Fl t
+restricts the reported vulnerabilities to type
+.Ar type .
+.It Cm audit-pkg Oo Fl es Oc Oo Fl t Ar type Oc Oo Ar pkg Oc ...
+Like
+.Cm audit ,
+but check only the given package names or patterns.
+.It Cm audit-batch Oo Fl es Oc Oo Fl t Ar type Oc Oo Ar pkg-list Oc ...
+Like
+.Cm audit-pkg ,
+but read the package names or patterns one per line from the given files.
+.It Cm audit-history Oo Fl s Oc Oo Fl t Ar type Oc Oo Ar pkgbase Oc ...
+Print all vulnerabilities for the given base package names.
+.It Cm check Op Ar pkg ...
+Use this command to check the files belonging to some or all of the
+packages installed on the local machine against the checksum
+which was recorded in the
+.Pa +CONTENTS
+files at package installation time.
+Symbolic links also have their integrity checked against the recorded
+value at package installation time.
+If no additional argument is given, the files of all installed packages
+are checked, else only the named packages will be checked (wildcards can
+be used here, see
+.Xr pkg_info 1 ) .
+.Pp
+The packages'
+.Pa +CONTENTS
+files will be parsed and the
+checksum will be checked for every file found.
+A warning message is printed if the expected checksum differs from the
+checksum of the file on disk.
+Symbolic links are also checked, ensuring that the targets on disk are
+the same as the contents recorded at package installation time.
+.It Cm check-license Ar condition
+Check if
+.Ar condition
+can be fulfilled with the currently set of accepted licenses.
+Prints either yes or no to stdout if the condition can be parsed,
+otherwise it exits with error.
+.It Cm check-pkg-vulnerabilities Oo Fl s Oc Ar file
+Check format and hashes in the pkg-vulnerabilities file
+.Ar file .
+If
+.Fl s
+is given, also check the embedded signature.
+.It Cm check-signature Ar file ...
+Reports if
+.Ar file
+is a correctly signed package.
+.It Cm check-single-license Ar license
+Check if
+.Ar license
+is a valid license name and if it is in the set of acceptable licenses.
+Prints either yes or no to stdout if the condition can be parsed,
+otherwise it exits with error.
+.It Cm config-var Ar variable
+Print the current value of
+.Ar variable
+as used after parsing the configuration file.
+.It Cm delete Ar pkg ...
+For each listed package, remove all file entries in the package database that
+belong to the package.
+This should be used only by
+.Xr pkg_view 1 .
+.It Cm dump
+Dump the contents of the package database, similar to
+.Cm pkg_info -F .
+Columns are printed for the key field used in the pkgdb - the filename -,
+and the data field - the package the file belongs to.
+.It Cm fetch-pkg-vulnerabilities Oo Fl su Oc
+Fetch a new pkg-vulnerabilities file, check the format and if
+.Fl s
+is given the signature.
+If all checks are passed, write it to pkgdb.
+If
+.Fl u
+is given, the fetch is conditional and the file transfer is only done if
+the remote version is newer than the one in pkgdb.
+.It Cm findbest Ar pattern ...
+Search the entries of
+.Dv PKG_PATH
+for packages matching
+.Ar pattern .
+Print the URL of the best matching package to stdout for each pattern.
+If a pattern is not matched, it is skipped and the command will return
+a failure.
+.It Cm lsall Ar /dir/pkgpattern
+.It Cm lsbest Ar /dir/pkgpattern
+List all/best package matching pattern in the given directory
+.Pa /dir .
+If the
+.Fl d
+flag is given, then that directory path overrides
+.Pa /dir .
+Can be used to work around limitations of /bin/sh and other
+filename globbing mechanisms.
+This option implements matching of
+pkg-wildcards against arbitrary files and directories, useful mainly in
+the build system itself.
+See
+.Xr pkg_info 1
+for a description of the pattern.
+.Pp
+Example:
+.Bd -literal
+yui# cd /usr/pkgsrc/packages/i386ELF/All/
+yui# ls unzip*
+unzip-5.40.tgz unzip-5.41.tgz
+yui# pkg_admin lsall 'unzip*'
+/usr/pkgsrc/packages/i386ELF/All/unzip-5.40.tgz
+/usr/pkgsrc/packages/i386ELF/All/unzip-5.41.tgz
+yui# pkg_admin lsall 'unzip\*[Ge]5.40'
+/usr/pkgsrc/packages/i386ELF/All/unzip-5.40.tgz
+/usr/pkgsrc/packages/i386ELF/All/unzip-5.41.tgz
+yui# pkg_admin lsall 'unzip\*[Ge]5.41'
+/usr/pkgsrc/packages/i386ELF/All/unzip-5.41.tgz
+yui# pkg_admin lsbest 'unzip\*[Ge]5.40'
+/usr/pkgsrc/packages/i386ELF/All/unzip-5.41.tgz
+yui# pkg_admin lsall /usr/pkgsrc/packages/i386ELF/All/'{mit,unproven}-pthread*'
+/usr/pkgsrc/packages/i386ELF/All/mit-pthreads-1.60b6.tgz
+/usr/pkgsrc/packages/i386ELF/All/unproven-pthreads-0.15.tgz
+.Ed
+.It Cm pmatch Ar pattern Ar pkg
+Returns true if
+.Ar pkg
+matches
+.Ar pattern ,
+otherwise returns false.
+.It Cm rebuild
+Rebuild the package database mapping from scratch, using the
+.Pa +CONTENTS
+files of the installed packages.
+This option is only intended for recovery after system crashes
+during package installation and removal.
+.It Cm rebuild-tree
+Rebuild the +REQUIRED_BY files from scratch by reresolving all dependencies.
+.Pp
+This option is intended to be used for fixing inconsistencies between
+the records of depending and depended-on packages, such as can arise
+by the use of
+.Cm pkg_delete -f .
+.It Cm set Ar variable=value pkg ...
+Set variable with information about the installed package.
+Use
+.Cm unset
+to remove a variable.
+.Pp
+Packages that are not installed directly by the user but pulled in as
+dependencies are marked by setting
+.Dq automatic=YES .
+.It Cm gpg-sign-package pkg spkg
+Sign the binary package
+.Ar pkg
+using GPG and write the result to
+.Ar spkg .
+.It Cm x509-sign-package pkg spkg key cert
+Sign the binary package
+.Ar pkg
+using the key
+.Ar key
+and the certificate
+.Ar cert ,
+using
+.Ar spkg
+as output file.
+.It Cm unset Ar variable pkg ...
+Remove an installation variable.
+.El
+.Sh ENVIRONMENT
+See
+.Xr pkg_install.conf 5
+for options, that can also be specified using the environment.
+.Sh FILES
+.Bl -tag -width /var/db/pkg/pkgdb.byfile.db -compact
+.It Pa /var/db/pkg/pkgdb.byfile.db
+.It Pa /var/db/pkg/\*[Lt]pkg\*[Gt]/+CONTENTS
+.El
+.Sh SEE ALSO
+.Xr pkg_add 1 ,
+.Xr pkg_create 1 ,
+.Xr pkg_delete 1 ,
+.Xr pkg_info 1 ,
+.Xr pkg_view 1 ,
+.Xr pkg_install.conf 5 ,
+.Xr pkgsrc 7
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Nx 1.4 .
+.Sh AUTHORS
+The
+.Nm
+command was written by Hubert Feyrer.
--- /dev/null
+.\" $NetBSD: bpm.1,v 1.4 2013/07/20 21:40:04 wiz Exp $ */
+.\"
+.\" Copyright (c) 2003,2009 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Alistair Crooks (agc@NetBSD.org)
+.\"
+.\" 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.
+.\"
+.\" 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.
+.\"
+.Dd August 3, 2007
+.Dt BPM 1
+.Os
+.Sh NAME
+.Nm bpm
+.Nd menu-based binary package manager
+.Sh SYNOPSIS
+.Nm
+.Op Fl hnVv
+.Op Fl b Ar baseURL
+.Op Fl m Ar machine
+.Op Fl r Ar release
+.Op Fl w Ar seconds
+.Sh DESCRIPTION
+The
+.Nm
+command is used to locate and install binary packages from any
+reachable URL.
+.Pp
+The following command-line options are supported:
+.Bl -tag -width indent
+.It Fl b Ar baseURL
+Specify a base URL from which to download binary packages.
+The default URL is
+.Pa ftp://ftp.NetBSD.org/pub/pkgsrc/packages .
+.It Fl h
+Print a help message and then exit.
+.It Fl m Ar machine
+Use
+.Ar machine
+as the machine architecture to be used, instead of that returned by
+.Xr uname 1 .
+.It Fl n
+Don't actually execute the commands to add the package.
+.It Fl r Ar release
+Use
+.Ar release
+as the operating system release to be used, instead of that returned by
+.Xr uname 1 .
+.It Fl V
+Print version number and exit.
+.It Fl v
+Turn on verbose output.
+.It Fl w Ar seconds
+The number of
+.Ar seconds
+to wait after displaying an error message and returning to
+normal menu operations.
+.El
+.Pp
+.Nm
+provides a menu-based binary package manager for
+.Nx .
+.Nm
+first connects to the URL using
+.Xr ftp 1 ,
+and displays a list of categories for which binary packages exist.
+If no categories are displayed, it could
+be that the machine architecture or operating system release string
+have been wrongly interpreted, and that it will be necessary to override
+this values by means of the command line options.
+Within a category, a list of packages will be displayed, and by selecting
+one using the number assigned to it, the package will be downloaded
+automatically, and installed, using the
+.Xr pkg_add 1
+utility.
+It is also possible to change the category currently being examined,
+and to quit from the utility, simply by selecting the appropriate choices
+on the menu.
+.Sh ENVIRONMENT
+The environment variables which govern the behavior of
+.Xr ftp 1
+and
+.Xr pkg_add 1
+are valid for
+.Nm .
+.Sh SEE ALSO
+.Xr ftp 1 ,
+.Xr pkg_add 1 ,
+.Xr uname 1
+.Sh AUTHORS
+The
+.Nm
+utility was written by
+.An Alistair Crooks Aq Mt agc@NetBSD.org .
--- /dev/null
+#! /bin/sh
+#
+# $NetBSD: bpm.sh.in,v 1.3 2012/02/21 18:36:16 wiz Exp $
+#
+# Copyright (c) 2003,2009 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# This code is derived from software contributed to The NetBSD Foundation
+# by Alistair Crooks (agc@NetBSD.org)
+#
+# 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.
+#
+# 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.
+#
+
+die()
+{
+ echo >&2 "$@"
+ exit 1
+}
+
+check_prog()
+{
+ _var="$1"; _name="$2"
+
+ eval _tmp=\"\$$_var\"
+ if [ "x$_tmp" != "x" ]; then
+ # Variable is already set (by the user, for example)
+ return 0
+ fi
+
+ for _d in `echo $PATH | tr ':' ' '`; do
+ if [ -x "$_d/$_name" ]; then
+ # Program found
+ eval $_var=\""$_d/$_name"\"
+ return 1
+ fi
+ done
+
+ die "$_name not found in path."
+}
+
+check_prog awkprog awk
+check_prog echoprog echo
+check_prog ftpprog ftp
+check_prog idprog id
+check_prog moreprog more
+check_prog pkg_addprog pkg_add
+check_prog rmprog rm
+check_prog sedprog sed
+check_prog suprog su
+check_prog unameprog uname
+
+# print version and exit
+version() {
+ $pkg_addprog -V
+ exit 0
+}
+
+# temporary files
+tmpcategories=/tmp/categories.$$
+tmppackages=/tmp/packages.$$
+
+# some base parameters
+base=ftp://ftp.NetBSD.org/pub/pkgsrc/packages
+release=`${unameprog} -r | ${sedprog} -e 's/_STABLE//'`
+machine=`${unameprog} -m`
+
+sleepsecs=1
+
+doit=""
+
+while [ $# -gt 0 ]; do
+ case $1 in
+ -V) version ;;
+ -b) base=$2; shift ;;
+ -h) ${echoprog} "$0 [-b BaseURL] [-h] [-m machine] [-n] [-r release] [-v] [-w secs]"; exit 0;;
+ -m) machine=$2; shift ;;
+ -n) doit=":" ;;
+ -r) release=$2; shift ;;
+ -v) set -x ;;
+ -w) sleepsecs=$2; shift ;;
+ *) break ;;
+ esac
+ shift
+done
+
+category=""
+
+while true; do
+ # if we don't have a packages file, then we need to choose a category
+ case "$category" in
+ "") # get possible categories
+ if [ ! -f $tmpcategories ]; then
+ ${echoprog} "Downloading package categories from ${base}..."
+ ${echoprog} "** QUIT" > $tmpcategories
+ ${echoprog} ls | ${ftpprog} ${base}/${release}/${machine}/ 2>/dev/null | \
+ ${awkprog} 'NF == 9 { if ($9 != "All") print $9 }' >> $tmpcategories
+ fi
+
+ # check for bad release numbering
+ # - it usually shows with 0 categories being displayed
+ ${awkprog} 'END { if (NR == 1) { print "\n\n\n*** No categories found - is the OS release set properly? ***\n\n\n" } }' < $tmpcategories
+
+ # display possible categories
+ ${awkprog} '{ print NR ". " $0 }' < $tmpcategories | ${moreprog}
+
+ # read a category number from the user
+ ${echoprog} -n "Please type the category number: "
+ read choice
+
+ # validate user's choice
+ case "$choice" in
+ 0|1) ${rmprog} -f $tmpcategories $tmppackages; exit 0 ;;
+ [2-9]|[0-9]*) category=`${awkprog} 'NR == '$choice' { print }' < $tmpcategories` ;;
+ *) category="" ;;
+ esac
+ case "$category" in
+ "") ${echoprog} "No such category \"$choice\""
+ sleep $sleepsecs
+ continue
+ ;;
+ esac
+
+ # get possible packages
+ ${echoprog} ""
+ ${echoprog} "Downloading package names from ${base}/${category}..."
+ ${echoprog} "** QUIT" > $tmppackages
+ ${echoprog} "** Change category" >> $tmppackages
+ ${echoprog} ls | ${ftpprog} ${base}/${release}/${machine}/${category}/ 2>/dev/null \
+ | ${awkprog} 'NF == 11 { print $9 }' >> $tmppackages
+ ;;
+ esac
+
+ # display possible packages
+ ${awkprog} '{ print NR ". " $0 }' < $tmppackages | ${moreprog}
+
+ # read a package number from the user
+ ${echoprog} -n "Please type the package number: "
+ read choice
+
+ # validate user's choice
+ case "$choice" in
+ 1) ${rmprog} -f $tmppackages $tmpcategories; exit 0 ;;
+ 2) category=""; continue ;; # no package to install - choose new category
+ [3-9]|[0-9]*) package=`${awkprog} 'NR == '$choice' { print }' < $tmppackages` ;;
+ *) package="" ;;
+ esac
+ case "$package" in
+ "") ${echoprog} "No such package \"$choice\""
+ sleep $sleepsecs
+ continue
+ ;;
+ esac
+
+ # check it's not already installed
+ pkgbase=`${echoprog} ${package} | ${sedprog} -e 's|-[0-9].*||'`
+ installed=`pkg_info -e $pkgbase`
+ case "$installed" in
+ "") ;;
+ *) ${echoprog} "$package selected, but $installed already installed"
+ sleep $sleepsecs
+ continue
+ ;;
+ esac
+
+ # Tell people what we're doing
+ ${echoprog} ""
+ ${echoprog} "Adding package ${base}/${release}/${machine}/${category}/${package}"
+
+ cmd="env PKG_PATH=${base}/${release}/${machine}/All ${pkg_addprog} ${package}"
+
+ # check if we need to become root for this
+ if [ `${idprog} -u` != 0 ]; then
+ ${echoprog} "Becoming root@`/bin/hostname` to add a binary package"
+ ${echoprog} -n "`${echoprog} ${suprog} | $awkprog '{ print $1 }'` "
+ $doit ${suprog} root -c "$cmd"
+ success=$?
+ else
+ $doit $cmd
+ success=$?
+ fi
+
+ # give feedback after adding the package
+ case $success in
+ 0) ${echoprog} "$package successfully installed" ;;
+ *) ${echoprog} "Problems when installing $package - please try again" ;;
+ esac
+
+ ${echoprog} ""
+ ${echoprog} -n "[Q]uit, [C]hange category, [I]nstall another package: "
+ read choice
+
+ case "$choice" in
+ [Qq]) break ;;
+ [Cc]) category="" ;;
+ [Ii]) ;;
+ esac
+done
+
+${rmprog} -f $tmpcategories $tmppackages
+
+exit 0
--- /dev/null
+/* $NetBSD: build.c,v 1.1.1.8 2010/04/23 20:54:07 joerg Exp $ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+__RCSID("$NetBSD: build.c,v 1.1.1.8 2010/04/23 20:54:07 joerg Exp $");
+
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * This code was developed as part of Google's Summer of Code 2007 program.
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * 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 main body of the create module.
+ *
+ */
+
+#include "lib.h"
+#include "create.h"
+
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+#if HAVE_GRP_H
+#include <grp.h>
+#endif
+#if HAVE_PWD_H
+#include <pwd.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include <archive.h>
+#include <archive_entry.h>
+
+static struct memory_file *contents_file;
+static struct memory_file *comment_file;
+static struct memory_file *desc_file;
+static struct memory_file *install_file;
+static struct memory_file *deinstall_file;
+static struct memory_file *display_file;
+static struct memory_file *build_version_file;
+static struct memory_file *build_info_file;
+static struct memory_file *size_pkg_file;
+static struct memory_file *size_all_file;
+static struct memory_file *preserve_file;
+static struct memory_file *views_file;
+
+static void
+write_meta_file(struct memory_file *file, struct archive *archive)
+{
+ struct archive_entry *entry;
+
+ entry = archive_entry_new();
+ archive_entry_set_pathname(entry, file->name);
+ archive_entry_copy_stat(entry, &file->st);
+
+ archive_entry_set_uname(entry, file->owner);
+ archive_entry_set_gname(entry, file->group);
+
+ if (archive_write_header(archive, entry))
+ errx(2, "cannot write to archive: %s", archive_error_string(archive));
+
+ archive_write_data(archive, file->data, file->len);
+
+ archive_entry_free(entry);
+}
+
+static void
+write_entry(struct archive *archive, struct archive_entry *entry)
+{
+ char buf[16384];
+ const char *name;
+ int fd;
+ off_t len;
+ ssize_t buf_len;
+
+ if (archive_entry_pathname(entry) == NULL) {
+ warnx("entry with NULL path");
+ return;
+ }
+
+ if (archive_write_header(archive, entry)) {
+ errx(2, "cannot write %s to archive: %s",
+ archive_entry_pathname(entry),
+ archive_error_string(archive));
+ }
+
+ /* Only regular files can have data. */
+ if (archive_entry_filetype(entry) != AE_IFREG ||
+ archive_entry_size(entry) == 0) {
+ archive_entry_free(entry);
+ return;
+ }
+
+ name = archive_entry_pathname(entry);
+
+ if ((fd = open(name, O_RDONLY)) == -1)
+ err(2, "cannot open data file %s", name);
+
+ len = archive_entry_size(entry);
+
+ while (len > 0) {
+ buf_len = (len > (off_t)sizeof(buf)) ? (ssize_t)sizeof(buf) : (ssize_t)len;
+
+ if ((buf_len = read(fd, buf, buf_len)) == 0)
+ break;
+ else if (buf_len < 0)
+ err(2, "cannot read from %s", name);
+
+ archive_write_data(archive, buf, (size_t)buf_len);
+ len -= buf_len;
+ }
+
+ close(fd);
+
+ archive_entry_free(entry);
+}
+
+static void
+write_normal_file(const char *name, struct archive *archive,
+ struct archive_entry_linkresolver *resolver,
+ const char *owner, const char *group)
+{
+ char buf[16384];
+ ssize_t buf_len;
+ struct archive_entry *entry, *sparse_entry;
+ struct stat st;
+
+ if (lstat(name, &st) == -1)
+ err(2, "lstat failed for file %s", name);
+
+ entry = archive_entry_new();
+ archive_entry_set_pathname(entry, name);
+ archive_entry_copy_stat(entry, &st);
+
+ if (owner != NULL) {
+ uid_t uid;
+
+ archive_entry_set_uname(entry, owner);
+ if (uid_from_user(owner, &uid) == -1)
+ errx(2, "user %s unknown", owner);
+ archive_entry_set_uid(entry, uid);
+ } else {
+ archive_entry_set_uname(entry, user_from_uid(st.st_uid, 1));
+ }
+
+ if (group != NULL) {
+ gid_t gid;
+
+ archive_entry_set_gname(entry, group);
+ if (gid_from_group(group, &gid) == -1)
+ errx(2, "group %s unknown", group);
+ archive_entry_set_gid(entry, gid);
+ } else {
+ archive_entry_set_gname(entry, group_from_gid(st.st_gid, 1));
+ }
+
+ if ((st.st_mode & S_IFMT) == S_IFLNK) {
+ buf_len = readlink(name, buf, sizeof buf);
+ if (buf_len < 0)
+ err(2, "cannot read symlink %s", name);
+ buf[buf_len] = '\0';
+ archive_entry_set_symlink(entry, buf);
+ }
+
+ archive_entry_linkify(resolver, &entry, &sparse_entry);
+
+ if (entry != NULL)
+ write_entry(archive, entry);
+ if (sparse_entry != NULL)
+ write_entry(archive, sparse_entry);
+}
+
+static void
+make_dist(const char *pkg, const char *suffix, const package_t *plist)
+{
+ char *archive_name;
+ const char *owner, *group;
+ const plist_t *p;
+ struct archive *archive;
+ struct archive_entry *entry, *sparse_entry;
+ struct archive_entry_linkresolver *resolver;
+ char *initial_cwd;
+
+ archive = archive_write_new();
+ archive_write_set_format_pax_restricted(archive);
+ if ((resolver = archive_entry_linkresolver_new()) == NULL)
+ errx(2, "cannot create link resolver");
+ archive_entry_linkresolver_set_strategy(resolver,
+ archive_format(archive));
+
+ if (CompressionType == NULL) {
+ if (strcmp(suffix, "tbz") == 0 ||
+ strcmp(suffix, "tar.bz2") == 0)
+ CompressionType = "bzip2";
+ else if (strcmp(suffix, "tgz") == 0 ||
+ strcmp(suffix, "tar.gz") == 0)
+ CompressionType = "gzip";
+ else
+ CompressionType = "none";
+ }
+
+ if (strcmp(CompressionType, "bzip2") == 0)
+ archive_write_set_compression_bzip2(archive);
+ else if (strcmp(CompressionType, "gzip") == 0)
+ archive_write_set_compression_gzip(archive);
+ else if (strcmp(CompressionType, "xz") == 0)
+ archive_write_set_compression_xz(archive);
+ else if (strcmp(CompressionType, "none") == 0)
+ archive_write_set_compression_none(archive);
+ else
+ errx(1, "Unspported compression type for -F: %s",
+ CompressionType);
+
+ archive_name = xasprintf("%s.%s", pkg, suffix);
+
+ if (archive_write_open_file(archive, archive_name))
+ errx(2, "cannot create archive: %s", archive_error_string(archive));
+
+ free(archive_name);
+
+ owner = DefaultOwner;
+ group = DefaultGroup;
+
+ write_meta_file(contents_file, archive);
+ write_meta_file(comment_file, archive);
+ write_meta_file(desc_file, archive);
+
+ if (Install)
+ write_meta_file(install_file, archive);
+ if (DeInstall)
+ write_meta_file(deinstall_file, archive);
+ if (Display)
+ write_meta_file(display_file, archive);
+ if (BuildVersion)
+ write_meta_file(build_version_file, archive);
+ if (BuildInfo)
+ write_meta_file(build_info_file, archive);
+ if (SizePkg)
+ write_meta_file(size_pkg_file, archive);
+ if (SizeAll)
+ write_meta_file(size_all_file, archive);
+ if (Preserve)
+ write_meta_file(preserve_file, archive);
+ if (create_views)
+ write_meta_file(views_file, archive);
+
+ initial_cwd = getcwd(NULL, 0);
+
+ for (p = plist->head; p; p = p->next) {
+ if (p->type == PLIST_FILE) {
+ write_normal_file(p->name, archive, resolver, owner, group);
+ } else if (p->type == PLIST_CWD) {
+ chdir(p->name);
+ } else if (p->type == PLIST_IGNORE) {
+ p = p->next;
+ } else if (p->type == PLIST_CHOWN) {
+ if (p->name != NULL)
+ owner = p->name;
+ else
+ owner = DefaultOwner;
+ } else if (p->type == PLIST_CHGRP) {
+ if (p->name != NULL)
+ group = p->name;
+ else
+ group = DefaultGroup;
+ }
+ }
+
+ entry = NULL;
+ archive_entry_linkify(resolver, &entry, &sparse_entry);
+ while (entry != NULL) {
+ write_entry(archive, entry);
+ entry = NULL;
+ archive_entry_linkify(resolver, &entry, &sparse_entry);
+ }
+
+ archive_entry_linkresolver_free(resolver);
+
+ if (archive_write_close(archive))
+ errx(2, "cannot finish archive: %s", archive_error_string(archive));
+ archive_write_finish(archive);
+
+ free(initial_cwd);
+}
+
+static struct memory_file *
+load_and_add(package_t *plist, const char *input_name,
+ const char *target_name, mode_t perm)
+{
+ struct memory_file *file;
+
+ file = load_memory_file(input_name, target_name, DefaultOwner,
+ DefaultGroup, perm);
+ add_plist(plist, PLIST_IGNORE, NULL);
+ add_plist(plist, PLIST_FILE, target_name);
+
+ return file;
+}
+
+static struct memory_file *
+make_and_add(package_t *plist, const char *target_name,
+ char *content, mode_t perm)
+{
+ struct memory_file *file;
+
+ file = make_memory_file(target_name, content, strlen(content),
+ DefaultOwner, DefaultGroup, perm);
+ add_plist(plist, PLIST_IGNORE, NULL);
+ add_plist(plist, PLIST_FILE, target_name);
+
+ return file;
+}
+
+int
+pkg_build(const char *pkg, const char *full_pkg, const char *suffix,
+ package_t *plist)
+{
+ char *plist_buf;
+ size_t plist_len;
+
+ /* Now put the release specific items in */
+ add_plist(plist, PLIST_CWD, ".");
+ comment_file = make_and_add(plist, COMMENT_FNAME, Comment, 0444);
+ desc_file = make_and_add(plist, DESC_FNAME, Desc, 0444);
+
+ if (Install) {
+ install_file = load_and_add(plist, Install, INSTALL_FNAME,
+ 0555);
+ }
+ if (DeInstall) {
+ deinstall_file = load_and_add(plist, DeInstall,
+ DEINSTALL_FNAME, 0555);
+ }
+ if (Display) {
+ display_file = load_and_add(plist, Display,
+ DISPLAY_FNAME, 0444);
+ add_plist(plist, PLIST_DISPLAY, DISPLAY_FNAME);
+ }
+ if (BuildVersion) {
+ build_version_file = load_and_add(plist, BuildVersion,
+ BUILD_VERSION_FNAME, 0444);
+ }
+ if (BuildInfo) {
+ build_info_file = load_and_add(plist, BuildInfo,
+ BUILD_INFO_FNAME, 0444);
+ }
+ if (SizePkg) {
+ size_pkg_file = load_and_add(plist, SizePkg,
+ SIZE_PKG_FNAME, 0444);
+ }
+ if (SizeAll) {
+ size_all_file = load_and_add(plist, SizeAll,
+ SIZE_ALL_FNAME, 0444);
+ }
+ if (Preserve) {
+ preserve_file = load_and_add(plist, Preserve,
+ PRESERVE_FNAME, 0444);
+ }
+ if (create_views)
+ views_file = make_and_add(plist, VIEWS_FNAME, xstrdup(""), 0444);
+
+ /* Finally, write out the packing list */
+ stringify_plist(plist, &plist_buf, &plist_len, realprefix);
+ contents_file = make_memory_file(CONTENTS_FNAME, plist_buf, plist_len,
+ DefaultOwner, DefaultGroup, 0644);
+
+ /* And stick it into a tar ball */
+ make_dist(pkg, suffix, plist);
+
+ return TRUE; /* Success */
+}
--- /dev/null
+/* $NetBSD: create.h,v 1.1.1.5 2009/11/05 18:39:02 joerg Exp $ */
+
+/* from FreeBSD Id: create.h,v 1.13 1997/10/08 07:46:19 charnier Exp */
+
+/*
+ * 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
+ *
+ * Include and define various things wanted by the create command.
+ *
+ */
+
+#ifndef _INST_CREATE_H_INCLUDE
+#define _INST_CREATE_H_INCLUDE
+
+struct memory_file {
+ struct stat st;
+ const char *name;
+ const char *owner;
+ const char *group;
+ mode_t mode;
+
+ char *data;
+ size_t len;
+};
+
+extern char *Prefix;
+extern char *Comment;
+extern char *Desc;
+extern char *Display;
+extern char *Install;
+extern char *DeInstall;
+extern char *Contents;
+extern char *Pkgdeps;
+extern char *BuildPkgdeps;
+extern char *Pkgcfl;
+extern char *BuildVersion;
+extern char *BuildInfo;
+extern char *SizePkg;
+extern char *SizeAll;
+extern char *Preserve;
+extern char *realprefix;
+extern char *DefaultOwner;
+extern char *DefaultGroup;
+extern const char *CompressionType;
+extern int PlistOnly;
+extern int RelativeLinks;
+extern int update_pkgdb;
+extern int create_views;
+
+void check_list(package_t *, const char *);
+void copy_plist(char *, package_t *);
+
+struct memory_file
+ *load_memory_file(const char *, const char *,
+ const char *, const char *, mode_t);
+struct memory_file
+ *make_memory_file(const char *, void *, size_t,
+ const char *, const char *, mode_t);
+void free_memory_file(struct memory_file *);
+
+int pkg_perform(const char *);
+int pkg_build(const char *, const char *, const char *, package_t *plist);
+
+#endif /* _INST_CREATE_H_INCLUDE */
--- /dev/null
+/* $NetBSD: main.c,v 1.1.1.7 2010/01/30 21:33:31 joerg Exp $ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+__RCSID("$NetBSD: main.c,v 1.1.1.7 2010/01/30 21:33:31 joerg Exp $");
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the create module.
+ *
+ */
+
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+#include "lib.h"
+#include "create.h"
+
+static const char Options[] = "B:C:D:EF:I:K:L:OP:S:T:UVb:c:d:f:g:i:k:ln:p:r:s:u:v";
+
+char *Prefix = NULL;
+char *Comment = NULL;
+char *Desc = NULL;
+char *Display = NULL;
+char *Install = NULL;
+char *DeInstall = NULL;
+char *Contents = NULL;
+char *Pkgdeps = NULL;
+char *BuildPkgdeps = NULL;
+char *Pkgcfl = NULL;
+char *BuildVersion = NULL;
+char *BuildInfo = NULL;
+char *SizePkg = NULL;
+char *SizeAll = NULL;
+char *Preserve = NULL;
+char *DefaultOwner = NULL;
+char *DefaultGroup = NULL;
+char *realprefix = NULL;
+const char *CompressionType = NULL;
+int update_pkgdb = 1;
+int create_views = 0;
+int PlistOnly = 0;
+int RelativeLinks = 0;
+Boolean File2Pkg = FALSE;
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: pkg_create [-ElOUVv] [-B build-info-file] [-b build-version-file]\n"
+ " [-C cpkgs] [-D displayfile] [-F compression] \n"
+ " [-I realprefix] [-i iscript]\n"
+ " [-K pkg_dbdir] [-k dscript]\n"
+ " [-n preserve-file] [-P dpkgs] [-p prefix] [-r rscript]\n"
+ " [-S size-all-file] [-s size-pkg-file]\n"
+ " [-T buildpkgs] [-u owner] [-g group]\n"
+ " -c comment -d description -f packlist\n"
+ " pkg-name\n");
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int ch;
+
+ setprogname(argv[0]);
+ while ((ch = getopt(argc, argv, Options)) != -1)
+ switch (ch) {
+ case 'v':
+ Verbose = TRUE;
+ break;
+
+ case 'E':
+ create_views = 1;
+ break;
+
+ case 'F':
+ CompressionType = optarg;
+ break;
+
+ case 'I':
+ realprefix = optarg;
+ break;
+
+ case 'O':
+ PlistOnly = 1;
+ break;
+
+ case 'U':
+ update_pkgdb = 0;
+ break;
+
+ case 'p':
+ Prefix = optarg;
+ break;
+
+ case 's':
+ SizePkg = optarg;
+ break;
+
+ case 'S':
+ SizeAll = optarg;
+ break;
+
+ case 'f':
+ Contents = optarg;
+ break;
+
+ case 'c':
+ Comment = optarg;
+ break;
+
+ case 'd':
+ Desc = optarg;
+ break;
+
+ case 'g':
+ DefaultGroup = optarg;
+ break;
+
+ case 'i':
+ Install = optarg;
+ break;
+
+ case 'K':
+ pkgdb_set_dir(optarg, 3);
+ break;
+
+ case 'k':
+ DeInstall = optarg;
+ break;
+
+ case 'l':
+ RelativeLinks = 1;
+ break;
+
+ case 'L':
+ warnx("Obsolete -L option ignored");
+ break;
+
+ case 'u':
+ DefaultOwner = optarg;
+ break;
+
+ case 'D':
+ Display = optarg;
+ break;
+
+ case 'n':
+ Preserve = optarg;
+ break;
+
+ case 'P':
+ Pkgdeps = optarg;
+ break;
+
+ case 'T':
+ BuildPkgdeps = optarg;
+ break;
+
+ case 'C':
+ Pkgcfl = optarg;
+ break;
+
+ case 'b':
+ BuildVersion = optarg;
+ break;
+
+ case 'B':
+ BuildInfo = optarg;
+ break;
+
+ case 'V':
+ show_version();
+ /* NOTREACHED */
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ pkg_install_config();
+
+ if (argc == 0) {
+ warnx("missing package name");
+ usage();
+ }
+ if (argc != 1) {
+ warnx("only one package name allowed");
+ usage();
+ }
+
+ if (pkg_perform(*argv))
+ return 0;
+ if (Verbose) {
+ if (PlistOnly)
+ warnx("package registration failed");
+ else
+ warnx("package creation failed");
+ }
+ return 1;
+}
--- /dev/null
+/* $NetBSD: perform.c,v 1.1.1.5 2009/11/05 18:39:02 joerg Exp $ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+__RCSID("$NetBSD: perform.c,v 1.1.1.5 2009/11/05 18:39:02 joerg Exp $");
+
+/*
+ * 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 main body of the create module.
+ *
+ */
+
+#include "lib.h"
+#include "create.h"
+
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+static void
+sanity_check(void)
+{
+ if (!Comment)
+ errx(2, "required package comment string is missing (-c comment)");
+ if (!Desc)
+ errx(2, "required package description string is missing (-d desc)");
+ if (!Contents)
+ errx(2, "required package contents list is missing (-f [-]file)");
+}
+
+static void
+register_depends(package_t *plist, char *deps, int build_only)
+{
+ char *cp;
+
+ if (Verbose && !PlistOnly) {
+ if (build_only)
+ printf("Registering build depends:");
+ else
+ printf("Registering depends:");
+ }
+ while (deps) {
+ cp = strsep(&deps, " \t\n");
+ if (*cp) {
+ char *best_installed;
+ best_installed = find_best_matching_installed_pkg(cp);
+ if (best_installed != NULL) {
+ add_plist(plist, PLIST_BLDDEP, best_installed);
+ if (Verbose && !PlistOnly && build_only)
+ printf(" %s", cp);
+ } else
+ warnx("No matching package installed for %s", cp);
+ free(best_installed);
+ if (!build_only) {
+ add_plist(plist, PLIST_PKGDEP, cp);
+ if (Verbose && !PlistOnly)
+ printf(" %s", cp);
+ }
+ }
+ }
+ if (Verbose && !PlistOnly)
+ printf(".\n");
+}
+
+/*
+ * Expect "fname" to point at a file, and read it into
+ * the buffer returned.
+ */
+static char *
+fileGetContents(char *fname)
+{
+ char *contents;
+ struct stat sb;
+ int fd;
+
+ if (stat(fname, &sb) == FAIL) {
+ errx(2, "can't stat '%s'", fname);
+ }
+
+ contents = xmalloc((size_t) (sb.st_size) + 1);
+ fd = open(fname, O_RDONLY, 0);
+ if (fd == FAIL) {
+ errx(2, "unable to open '%s' for reading", fname);
+ }
+ if (read(fd, contents, (size_t) sb.st_size) != (ssize_t) sb.st_size) {
+ 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
+get_dash_string(char **s)
+{
+ if (**s == '-')
+ *s = xstrdup(*s + 1);
+ else
+ *s = fileGetContents(*s);
+}
+
+int
+pkg_perform(const char *pkg)
+{
+ char *cp;
+ FILE *pkg_in;
+ package_t plist;
+ const char *full_pkg, *suffix;
+ char *allocated_pkg;
+ int retval;
+
+ /* Break the package name into base and desired suffix (if any) */
+ if ((cp = strrchr(pkg, '.')) != NULL) {
+ allocated_pkg = xmalloc(cp - pkg + 1);
+ memcpy(allocated_pkg, pkg, cp - pkg);
+ allocated_pkg[cp - pkg] = '\0';
+ suffix = cp + 1;
+ full_pkg = pkg;
+ pkg = allocated_pkg;
+ } else {
+ allocated_pkg = NULL;
+ full_pkg = pkg;
+ suffix = "tgz";
+ }
+
+ /* Preliminary setup */
+ sanity_check();
+ if (Verbose && !PlistOnly)
+ printf("Creating package %s\n", pkg);
+ get_dash_string(&Comment);
+ get_dash_string(&Desc);
+ if (IS_STDIN(Contents))
+ pkg_in = stdin;
+ else {
+ pkg_in = fopen(Contents, "r");
+ if (!pkg_in)
+ errx(2, "unable to open contents file '%s' for input", Contents);
+ }
+
+ plist.head = plist.tail = NULL;
+
+ /* Stick the dependencies, if any, at the top */
+ if (Pkgdeps)
+ register_depends(&plist, Pkgdeps, 0);
+
+ /*
+ * Put the build dependencies after the dependencies.
+ * This works due to the evaluation order in pkg_add.
+ */
+ if (BuildPkgdeps)
+ register_depends(&plist, BuildPkgdeps, 1);
+
+ /* Put the conflicts directly after the dependencies, if any */
+ if (Pkgcfl) {
+ if (Verbose && !PlistOnly)
+ printf("Registering conflicts:");
+ while (Pkgcfl) {
+ cp = strsep(&Pkgcfl, " \t\n");
+ if (*cp) {
+ add_plist(&plist, PLIST_PKGCFL, cp);
+ if (Verbose && !PlistOnly)
+ printf(" %s", cp);
+ }
+ }
+ if (Verbose && !PlistOnly)
+ printf(".\n");
+ }
+
+ /* Slurp in the packing list */
+ append_plist(&plist, pkg_in);
+
+ if (pkg_in != stdin)
+ fclose(pkg_in);
+
+ /* Prefix should override the packing list */
+ if (Prefix) {
+ delete_plist(&plist, FALSE, PLIST_CWD, NULL);
+ add_plist_top(&plist, PLIST_CWD, Prefix);
+ }
+ /*
+ * Run down the list and see if we've named it, if not stick in a name
+ * at the top.
+ */
+ if (find_plist(&plist, PLIST_NAME) == NULL) {
+ add_plist_top(&plist, PLIST_NAME, basename_of(pkg));
+ }
+
+ /* Make first "real contents" pass over it */
+ check_list(&plist, basename_of(pkg));
+
+ /*
+ * We're just here for to dump out a revised plist for the FreeBSD ports
+ * hack. It's not a real create in progress.
+ */
+ if (PlistOnly) {
+ write_plist(&plist, stdout, realprefix);
+ retval = TRUE;
+ } else {
+#ifdef BOOTSTRAP
+ warnx("Package building is not supported in bootstrap mode");
+ retval = FALSE;
+#else
+ retval = pkg_build(pkg, full_pkg, suffix, &plist);
+#endif
+ }
+
+ /* Cleanup */
+ free(Comment);
+ free(Desc);
+ free_plist(&plist);
+
+ free(allocated_pkg);
+
+ return retval;
+}
--- /dev/null
+.\" $NetBSD: pkg_create.1,v 1.1.1.8 2010/04/23 20:54:08 joerg Exp $
+.\"
+.\" FreeBSD install - a package for the installation and maintenance
+.\" 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
+.\"
+.\"
+.\" @(#)pkg_create.1
+.\" from FreeBSD Id: pkg_create.1,v 1.19 1997/05/02 22:00:05 max Exp
+.\"
+.\" hacked up by John Kohl for NetBSD--fixed a few bugs, extended keywords,
+.\" added dependency tracking, etc.
+.\"
+.\" [jkh] Took John's changes back and made some additional extensions for
+.\" better integration with FreeBSD's new ports collection.
+.\"
+.Dd January 20, 2010
+.Dt PKG_CREATE 1
+.Os
+.Sh NAME
+.Nm pkg_create
+.Nd a utility for creating software package distributions
+.Sh SYNOPSIS
+.Nm
+.Op Fl ElOUVv
+.Bk -words
+.Op Fl B Ar build-info-file
+.Ek
+.Bk -words
+.Op Fl b Ar build-version-file
+.Ek
+.Bk -words
+.Op Fl C Ar cpkgs
+.Ek
+.Bk -words
+.Op Fl D Ar displayfile
+.Ek
+.Bk -words
+.Op Fl F Ar compression
+.Ek
+.Bk -words
+.Op Fl g Ar group
+.Ek
+.Bk -words
+.Op Fl I Ar realprefix
+.Ek
+.Bk -words
+.Op Fl i Ar iscript
+.Ek
+.Bk -words
+.Op Fl K Ar pkg_dbdir
+.Ek
+.Bk -words
+.Op Fl k Ar dscript
+.Ek
+.Bk -words
+.Op Fl n Ar preserve-file
+.Ek
+.Bk -words
+.Op Fl P Ar dpkgs
+.Ek
+.Bk -words
+.Op Fl T Ar buildpkgs
+.Ek
+.Bk -words
+.Op Fl p Ar prefix
+.Ek
+.Bk -words
+.Op Fl S Ar size-all-file
+.Ek
+.Bk -words
+.Op Fl s Ar size-pkg-file
+.Ek
+.Bk -words
+.Op Fl t Ar template
+.Ek
+.Bk -words
+.Op Fl u Ar owner
+.Ek
+.Bk -words
+.Fl c Ar comment
+.Ek
+.Bk -words
+.Fl d Ar description
+.Ek
+.Bk -words
+.Fl f Ar packlist
+.Ek
+.Ar pkg-name
+.Sh DESCRIPTION
+The
+.Nm
+command is used to create packages that will subsequently be fed to
+one of the package extraction/info utilities.
+The input description and command line arguments for the creation of a
+package are not really meant to be human-generated, though it is easy
+enough to do so.
+It is more expected that you will use a front-end tool for
+the job rather than muddling through it yourself.
+Nonetheless, a short description of the input syntax is included in this
+document.
+.Sh OPTIONS
+The following command line options are supported:
+.Bl -tag -width indent
+.It Fl B Ar build-info-file
+Install the file
+.Ar build-info-file
+so that users of binary packages can see what
+.Xr make 1
+definitions
+were used to control the build when creating the
+binary package.
+This allows various build definitions to be retained in a binary package
+and viewed wherever it is installed, using
+.Xr pkg_info 1 .
+.It Fl b Ar build-version-file
+Install the file
+.Ar build-version-file
+so that users of binary packages can see what versions of
+the files used to control the build were used when creating the
+binary package.
+This allows some fine-grained version control information to be retained
+in a binary package and viewed wherever it is installed, using
+.Xr pkg_info 1 .
+.It Fl C Ar cpkgs
+Set the initial package conflict list to
+.Ar cpkgs .
+This is assumed to be a whitespace separated list of package names
+and is meant as a convenient shorthand for specifying multiple
+.Cm @pkgcfl
+directives in the packing list (see PACKING LIST DETAILS section below).
+.It Fl c Ar [-]desc
+Fetch package
+.Pq one line description
+from file
+.Ar desc
+or, if preceded by
+.Cm - ,
+the argument itself.
+This string should also give some idea of which version of the product
+(if any) the package represents.
+.It Fl D Ar displayfile
+Display the file after installing the package.
+Useful for things like legal notices on almost-free software, etc.
+.It Fl d Ar [-]desc
+Fetch long description for package from file
+.Ar desc
+or, if preceded by
+.Cm - ,
+the argument itself.
+.It Fl E
+Add an empty views file to the package.
+.It Fl F Ar compression
+Use
+.Ar compression
+as compression algorithm.
+This overrides the heuristic to guess the compression type from the
+output name.
+Currently supported values are bzip2, gzip, none and xz.
+.It Fl f Ar packlist
+Fetch
+.Pq packing list
+for package from the file
+.Ar packlist
+or
+.Cm stdin
+if
+.Ar packlist
+is a
+.Cm -
+(dash).
+.It Fl g Ar group
+Make
+.Ar group
+the default group ownership instead of extracting it from the file system.
+.It Fl I Ar realprefix
+Provide the real prefix, as opposed to the staging prefix, for use in
+staged installations of packages.
+.It Fl i Ar iscript
+Set
+.Ar iscript
+to be the install procedure for the package.
+This can be any executable program (or shell script).
+It will be invoked automatically when the package is later installed.
+.It Fl K Ar pkg_dbdir
+Override the value of the
+.Dv PKG_DBDIR
+configuration option with the value
+.Ar pkg_dbdir .
+.It Fl k Ar dscript
+Set
+.Ar dscript
+to be the de-install procedure for the package.
+This can be any executable program (or shell script).
+It will be invoked automatically
+when the package is later (if ever) de-installed.
+.It Fl l
+Check that any symbolic links which are to be placed in the package are
+relative to the current prefix.
+This means using
+.Xr unlink 2
+and
+.Xr symlink 2
+to remove and re-link
+any symbolic links which are targeted at full path names.
+.It Fl n Ar preserve-file
+The file is used to denote that the package should not be deleted.
+This is intended for use where the deletion of packages may present
+a bootstrap problem.
+.It Fl O
+Go into a
+.Pq packing list only
+mode.
+This is used to do
+.Pq fake pkg_add
+operations when a package is installed.
+In such cases, it is necessary to know what the final, adjusted packing
+list will look like.
+.It Fl P Ar dpkgs
+Set the initial package dependency list to
+.Ar dpkgs .
+This is assumed to be a whitespace separated list of package names
+and is meant as a convenient shorthand for specifying multiple
+.Cm @pkgdep
+directives in the packing list (see PACKING LIST DETAILS section below).
+In addition, the exact versions of the packages referred to in the
+.Ar dpkgs
+list will be added to the packing list in the form of
+.Cm @blddep
+directives.
+.It Fl T Ar buildpkgs
+The exact versions of the packages referred to in the
+.Ar buildpkgs
+list will be added to the packing list in the form of
+.Cm @blddep
+directives.
+This directives are stored after those created by the
+.Fl P
+option.
+.Ar buildpkgs
+is assumed to be a whitespace separated list of package names.
+.It Fl p Ar prefix
+Set
+.Ar prefix
+as the initial directory
+.Pq base
+to start from in selecting files for
+the package.
+.It Fl S Ar size-all-file
+Store the given file for later querying with the
+.Xr pkg_info 1
+.Fl S
+flag.
+The file is expected to contain the size (in bytes) of all files of
+this package plus any required packages added up and stored as a
+ASCII string, terminated by a newline.
+.It Fl s Ar size-pkg-file
+Store the given file for later querying with the
+.Xr pkg_info 1
+.Fl s
+flag.
+The file is expected to contain the size (in bytes) of all files of
+this package added up and stored as a ASCII string, terminated by a newline.
+.It Fl t Ar template
+Use
+.Ar template
+as the input to
+.Xr mktemp 3 .
+By default, this is the string
+.Pa /tmp/instmp.XXXXXX ,
+but it may be necessary to override it in the situation where
+space in your
+.Pa /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.
+.It Fl U
+Do not update the package file database with any file information.
+.It Fl u Ar owner
+Make
+.Ar owner
+the default owner instead of extracting it from the file system.
+.It Fl V
+Print version number and exit.
+.It Fl v
+Turn on verbose output.
+.El
+.Sh PACKING LIST DETAILS
+The
+.Pq packing list
+format (see
+.Fl f )
+is fairly simple, being
+nothing more than a single column of filenames to include in the
+package.
+However, since absolute pathnames are generally a bad idea
+for a package that could be installed potentially anywhere, there is
+another method of specifying where things are supposed to go
+and, optionally, what ownership and mode information they should be
+installed with.
+This is done by embedding specialized command sequences
+in the packing list.
+Briefly described, these sequences are:
+.Bl -tag -width indent -compact
+.It Cm @cwd Ar directory
+Set the internal directory pointer to point to
+.Ar directory .
+All subsequent filenames will be assumed relative to this directory.
+Note:
+.Cm @cd
+is also an alias for this command.
+.It Cm @src Ar directory
+This command is supported for compatibility only.
+It was formerly used to override
+.Cm @cwd
+during package creation.
+.It Cm @exec Ar command
+Execute
+.Ar command
+as part of the unpacking process.
+If
+.Ar command
+contains any of the following sequences somewhere in it, they will
+be expanded inline.
+For the following examples, assume that
+.Cm @cwd
+is set to
+.Pa /usr/local
+and the last extracted file was
+.Pa bin/emacs .
+.Bl -tag -width indent -compact
+.It Cm "\&%F"
+Expands to the last filename extracted (as specified), in the example case
+.Pa bin/emacs
+.It Cm "\&%D"
+Expand to the current directory prefix, as set with
+.Cm @cwd ,
+in the example case
+.Pa /usr/local .
+.It Cm "\&%B"
+Expand to the
+.Pq basename
+of the fully qualified filename, that
+is the current directory prefix, plus the last filespec, minus
+the trailing filename.
+In the example case, that would be
+.Pa /usr/local/bin .
+.It Cm "\&%f"
+Expand to the
+.Pq filename
+part of the fully qualified name, or
+the converse of
+.Cm \&%B ,
+being in the example case,
+.Pa emacs .
+.El
+.It Cm @unexec Ar command
+Execute
+.Ar command
+as part of the deinstallation process.
+Expansion of special
+.Cm \&%
+sequences is the same as for
+.Cm @exec .
+This command is not executed during the package add, as
+.Cm @exec
+is, but rather when the package is deleted.
+This is useful for deleting links and other ancillary files that were created
+as a result of adding the package, but not directly known to the package's
+table of contents (and hence not automatically removable).
+The advantage of using
+.Cm @unexec
+over a deinstallation script is that you can use the
+.Pq special sequence expansion
+to get at files regardless of where they've
+been potentially redirected (see
+.Fl p ) .
+.It Cm @mode Ar mode
+Set default permission for all subsequently extracted files to
+.Ar mode .
+Format is the same as that used by the
+.Cm chmod
+command (well, considering that it's later handed off to it, that's
+no surprise).
+Use without an arg to set back to default (extraction) permissions.
+.It Cm @option Ar option
+Set internal package options, the only currently supported one
+being
+.Ar preserve ,
+which tells pkg_add to move any existing files out of the way,
+preserving the previous contents (which are also resurrected on
+pkg_delete, so caveat emptor).
+.It Cm @owner Ar user
+Set default ownership for all subsequently extracted files to
+.Ar user .
+Use without an arg to set back to default (extraction)
+ownership.
+.It Cm @group Ar group
+Set default group ownership for all subsequently extracted files to
+.Ar group .
+Use without an arg to set back to default (extraction)
+group ownership.
+.It Cm @comment Ar string
+Embed a comment in the packing list.
+Useful in trying to document some particularly hairy sequence that
+may trip someone up later.
+.It Cm @ignore
+Used internally to tell extraction to ignore the next file (don't
+copy it anywhere), as it's used for some special purpose.
+.It Cm @name Ar name
+Set the name of the package.
+This is mandatory and is usually put at the top.
+This name is potentially different than the name of the file it came in,
+and is used when keeping track of the package for later deinstallation.
+Note that
+.Nm
+will derive this field from the
+.Ar pkg-name
+and add it automatically if none is given.
+.It Cm @pkgdir Ar name
+Declare directory
+.Pa name
+as managed.
+If it does not exist at installation time, it is created.
+If this directory is no longer referenced by packages and the last
+file or directory in it is deleted, the directory is removed as well.
+.It Cm @dirrm Ar name
+This command is supported for compatibility only.
+If directory
+.Pa name
+exists, it will be deleted at deinstall time.
+.It Cm @display Ar name
+Declare
+.Pa name
+as the file to be displayed at install time (see
+.Fl D
+above).
+.It Cm @pkgdep Ar pkgname
+Declare a dependency on the
+.Ar pkgname
+package.
+The
+.Ar pkgname
+package must be installed before this package may be
+installed, and this package must be deinstalled before the
+.Ar pkgname
+package is deinstalled.
+Multiple
+.Cm @pkgdep
+directives may be used if the package depends on multiple other packages.
+.It Cm @blddep Ar pkgname
+Declare that this package was built with the exact version
+of
+.Ar pkgname
+(since the
+.Cm @pkgdep
+directive may contain wildcards or relational
+package version information).
+.It Cm @pkgcfl Ar pkgcflname
+Declare a conflict with the
+.Ar pkgcflname
+package, as the two packages contain references to the same files,
+and so cannot co-exist on the same system.
+.El
+.Sh ENVIRONMENT
+See
+.Xr pkg_install.conf 5
+for options, that can also be specified using the environment.
+.Sh SEE ALSO
+.Xr pkg_add 1 ,
+.Xr pkg_admin 1 ,
+.Xr pkg_delete 1 ,
+.Xr pkg_info 1 ,
+.Xr pkg_install.conf 5
+.Xr pkgsrc 7
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Fx .
+.Sh AUTHORS
+.Bl -tag -width indent -compact
+.It Jordan Hubbard
+most of the work
+.It John Kohl
+refined it for
+.Nx
+.It Hubert Feyrer
+.Nx
+wildcard dependency processing, pkgdb, pkg size recording etc.
+.El
--- /dev/null
+/* $NetBSD: pl.c,v 1.1.1.4 2009/11/05 18:39:03 joerg Exp $ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+__RCSID("$NetBSD: pl.c,v 1.1.1.4 2009/11/05 18:39:03 joerg Exp $");
+
+/*
+ * 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 dealing with the packing list.
+ *
+ */
+
+#include "lib.h"
+#include "create.h"
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+#ifndef NETBSD
+#include <nbcompat/md5.h>
+#else
+#include <md5.h>
+#endif
+
+/*
+ * Check that any symbolic link is relative to the prefix
+ */
+static void
+CheckSymlink(char *name, char *prefix, size_t prefixcc)
+{
+ char newtgt[MaxPathSize];
+ char oldtgt[MaxPathSize];
+ char *slash;
+ int slashc;
+ int cc;
+ int i;
+
+ if ((cc = readlink(name, oldtgt, sizeof(oldtgt) - 1)) > 0) {
+ oldtgt[cc] = 0;
+ if (strncmp(oldtgt, prefix, prefixcc) == 0 && oldtgt[prefixcc] == '/') {
+ for (slashc = 0, slash = &name[prefixcc + 1]; (slash = strchr(slash, '/')) != (char *) NULL; slash++, slashc++) {
+ }
+ for (cc = i = 0; i < slashc; i++) {
+ strlcpy(&newtgt[cc], "../", sizeof(newtgt) - cc);
+ cc += 3;
+ }
+ strlcpy(&newtgt[cc], &oldtgt[prefixcc + 1], sizeof(newtgt) - cc);
+ (void) fprintf(stderr, "Full pathname symlink `%s' is target of `%s' - adjusting to `%s'\n", oldtgt, name, newtgt);
+ if (unlink(name) != 0) {
+ warn("can't unlink `%s'", name);
+ } else if (symlink(newtgt, name) != 0) {
+ warn("can't symlink `%s' called `%s'", newtgt, name);
+ }
+ }
+ }
+}
+
+/*
+ * Check a list for files that require preconversion
+ */
+void
+check_list(package_t *pkg, const char *PkgName)
+{
+ struct stat st;
+ plist_t *tmp;
+ plist_t *p;
+ char buf[ChecksumHeaderLen + LegibleChecksumLen];
+ char target[MaxPathSize + SymlinkHeaderLen];
+ char name[MaxPathSize];
+ char *cwd = NULL;
+ char *pkgname = NULL;
+ int cc;
+
+ /* Open Package Database for writing */
+ if (update_pkgdb && !pkgdb_open(ReadWrite))
+ err(EXIT_FAILURE, "can't open pkgdb");
+
+ for (p = pkg->head; p; p = p->next) {
+ switch (p->type) {
+ case PLIST_CWD:
+ cwd = p->name;
+ break;
+ case PLIST_NAME:
+ pkgname = p->name;
+ break;
+ case PLIST_IGNORE:
+ p = p->next;
+ break;
+ case PLIST_PKGDIR:
+ if (cwd == NULL)
+ errx(2, "@pkgdir without preceding @cwd found");
+ if (pkgname == NULL)
+ errx(2, "@pkgdir without preceding @name found");
+ if (update_pkgdb) {
+ add_pkgdir(pkgname, cwd, p->name);
+ /* mkdir_p(cwd, p->name); */
+ }
+ break;
+ case PLIST_FILE:
+ /*
+ * pkgdb handling - usually, we enter files
+ * into the pkgdb as soon as they hit the disk,
+ * but as they are present before pkg_create
+ * starts, it's ok to do this somewhere here
+ */
+ if (cwd == NULL)
+ errx(2, "file without preceding @cwd found");
+ if (update_pkgdb) {
+ char *s, t[MaxPathSize];
+
+ (void) snprintf(t, sizeof(t), "%s%s%s",
+ cwd,
+ (strcmp(cwd, "/") == 0) ? "" : "/",
+ p->name);
+
+ s = pkgdb_retrieve(t);
+ if (s && PlistOnly)
+ warnx("Overwriting %s - "
+ "pkg %s bogus/conflicting?", t, s);
+ else {
+ pkgdb_store(t, PkgName);
+ }
+ }
+
+ /* prepend DESTDIR if set? - HF */
+ (void) snprintf(name, sizeof(name), "%s%s%s",
+ cwd,
+ (strcmp(cwd, "/") == 0) ? "" : "/",
+ p->name);
+ if (lstat(name, &st) < 0) {
+ warnx("can't stat `%s'", name);
+ continue;
+ }
+ switch (st.st_mode & S_IFMT) {
+ case S_IFDIR:
+ warnx("Warning - directory `%s' in PLIST", name);
+ break;
+ case S_IFLNK:
+ if (RelativeLinks) {
+ CheckSymlink(name, cwd, strlen(cwd));
+ }
+ (void) strlcpy(target, SYMLINK_HEADER,
+ sizeof(target));
+ if ((cc = readlink(name, &target[SymlinkHeaderLen],
+ sizeof(target) - SymlinkHeaderLen - 1)) < 0) {
+ warnx("can't readlink `%s'", name);
+ continue;
+ }
+ target[SymlinkHeaderLen + cc] = 0x0;
+ tmp = new_plist_entry();
+ tmp->name = xstrdup(target);
+ tmp->type = PLIST_COMMENT;
+ tmp->next = p->next;
+ tmp->prev = p;
+ if (p == pkg->tail) {
+ pkg->tail = tmp;
+ }
+ p->next = tmp;
+ p = tmp;
+ break;
+ case S_IFCHR:
+ warnx("Warning - char special device `%s' in PLIST", name);
+ break;
+ case S_IFBLK:
+ warnx("Warning - block special device `%s' in PLIST", name);
+ break;
+ default:
+ (void) strlcpy(buf, CHECKSUM_HEADER,
+ sizeof(buf));
+ if (MD5File(name, &buf[ChecksumHeaderLen]) != (char *) NULL) {
+ tmp = new_plist_entry();
+ tmp->name = xstrdup(buf);
+ tmp->type = PLIST_COMMENT; /* PLIST_MD5 - HF */
+ tmp->next = p->next;
+ tmp->prev = p;
+ if (p == pkg->tail) {
+ pkg->tail = tmp;
+ }
+ p->next = tmp;
+ p = tmp;
+ }
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (update_pkgdb) {
+ pkgdb_close();
+ }
+}
--- /dev/null
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <nbcompat.h>
+
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+#if HAVE_PWD_H
+#include <grp.h>
+#endif
+#if HAVE_PWD_H
+#include <pwd.h>
+#endif
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+#if HAVE_TIME_H
+#include <time.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include "lib.h"
+#include "create.h"
+
+static void
+update_ids(struct memory_file *file)
+{
+ if (file->owner != NULL) {
+ uid_t uid;
+
+ if (uid_from_user(file->owner, &uid) == -1)
+ errx(2, "user %s unknown", file->owner);
+ file->st.st_uid = uid;
+ } else {
+ file->owner = user_from_uid(file->st.st_uid, 1);
+ }
+
+ if (file->group != NULL) {
+ gid_t gid;
+
+ if (gid_from_group(file->group, &gid) == -1)
+ errx(2, "group %s unknown", file->group);
+ file->group = file->group;
+ file->st.st_gid = gid;
+ } else {
+ file->group = group_from_gid(file->st.st_gid, 1);
+ }
+}
+
+struct memory_file *
+make_memory_file(const char *archive_name, void *data, size_t len,
+ const char *owner, const char *group, mode_t mode)
+{
+ struct memory_file *file;
+
+ file = xmalloc(sizeof(*file));
+ file->name = archive_name;
+ file->owner = owner;
+ file->group = group;
+ file->data = data;
+ file->len = len;
+
+ memset(&file->st, 0, sizeof(file->st));
+
+ file->st.st_atime = file->st.st_ctime = file->st.st_mtime = time(NULL);
+
+ file->st.st_nlink = 1;
+ file->st.st_size = len;
+ file->st.st_mode = mode | S_IFREG;
+
+ update_ids(file);
+
+ return file;
+}
+
+struct memory_file *
+load_memory_file(const char *disk_name,
+ const char *archive_name, const char *owner, const char *group,
+ mode_t mode)
+{
+ struct memory_file *file;
+ int fd;
+
+ file = xmalloc(sizeof(*file));
+ file->name = archive_name;
+ file->owner = owner;
+ file->group = group;
+ file->mode = mode;
+
+ fd = open(disk_name, O_RDONLY);
+ if (fd == -1)
+ err(2, "cannot open file %s", disk_name);
+ if (fstat(fd, &file->st) == -1)
+ err(2, "cannot stat file %s", disk_name);
+
+ update_ids(file);
+
+ if ((file->st.st_mode & S_IFMT) != S_IFREG)
+ errx(1, "meta data file %s is not regular file", disk_name);
+ if (file->st.st_size > SSIZE_MAX)
+ errx(2, "meta data file too large: %s", disk_name);
+ file->data = xmalloc(file->st.st_size);
+
+ if (read(fd, file->data, file->st.st_size) != file->st.st_size)
+ err(2, "cannot read file into memory %s", disk_name);
+
+ file->len = file->st.st_size;
+
+ close(fd);
+
+ return file;
+}
+
+void
+free_memory_file(struct memory_file *file)
+{
+ if (file != NULL) {
+ free(file->data);
+ free(file);
+ }
+}
--- /dev/null
+.\" $NetBSD: pkg_delete.1,v 1.1.1.8 2010/04/23 20:54:08 joerg Exp $
+.\"
+.\" FreeBSD install - a package for the installation and maintenance
+.\" 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
+.\"
+.\"
+.\" from FreeBSD: @(#)pkg_delete.1
+.\"
+.Dd January 20, 2010
+.Dt PKG_DELETE 1
+.Os
+.Sh NAME
+.Nm pkg_delete
+.Nd a utility for deleting previously installed software package distributions
+.Sh SYNOPSIS
+.Nm
+.Op Fl ADFfkNnORrVv
+.Op Fl K Ar pkg_dbdir
+.Op Fl P Ar destdir
+.Op Fl p Ar prefix
+.Ar pkg-name ...
+.Sh DESCRIPTION
+The
+.Nm
+command is used to delete packages that have been previously installed
+with the
+.Xr pkg_add 1
+command.
+The given packages are sorted, so that the dependencies needed by a
+package are deleted after the package.
+Before any action is executed,
+.Nm
+checks for packages that are marked as
+.Cm preserved
+or have depending packages left.
+If the
+.Fl k
+flag is given, preserved packages are skipped and not removed.
+Unless the
+.Fl f
+flag is given,
+.Nm
+stops on the first error.
+.Sh WARNING
+.Bf -emphasis
+Since the
+.Nm
+command may execute scripts or programs provided by a package file,
+your system may be susceptible to
+.Dq Trojan horses
+or other subtle
+attacks from miscreants who create dangerous package files.
+.Pp
+You are advised to verify the competence and identity of those who
+provide installable package files.
+For extra protection, examine all the package control files in the
+package record directory
+.Pa \*[Lt]PKG_DBDIR\*[Gt]/\*[Lt]pkg-name\*[Gt]/ ) .
+Pay particular
+attention to any
+.Pa +INSTALL
+or
+.Pa +DEINSTALL
+files, and inspect the
+.Pa +CONTENTS
+file for
+.Cm @cwd ,
+.Cm @mode
+(check for setuid),
+.Cm @dirrm ,
+.Cm @exec ,
+and
+.Cm @unexec
+directives, and/or use the
+.Xr pkg_info 1
+command to examine the installed package control files.
+.Ef
+.Sh OPTIONS
+The following command line options are supported:
+.Bl -tag -width indent
+.It Ar pkg-name ...
+The named packages are deinstalled, wildcards can be used, see
+.Xr pkg_info 1 .
+If no version is given, the one currently installed
+will be removed.
+If the
+.Fl F
+flag is given, one or more (absolute) filenames may be specified and
+the package database will be consulted for the package to which the
+given file belongs.
+These packages are then deinstalled.
+.It Fl A
+Recursively remove all automatically installed packages that were needed
+by the given packages and are no longer required.
+Does not remove manually installed packages; see also the
+.Fl R
+flag.
+.It Fl D
+If a deinstallation script exists for a given package, do not execute it.
+.It Fl F
+Any
+.Ar pkg-name
+given will be interpreted as pathname which is
+subsequently transformed in a (real) package name via the package
+database.
+That way, packages can be deleted by giving a filename
+instead of the package-name.
+.It Fl f
+Force removal of the package, even if a dependency is recorded or the
+deinstall script fails.
+This might break the package database; see
+.Xr pkg_admin 1
+on how to repair it.
+.It Fl ff
+Force removal of the package, even if the package is marked as a
+.Cm preserved
+package.
+Note that this is a dangerous operation.
+See also the
+.Fl k
+option.
+.It Fl K Ar pkg_dbdir
+Override the value of the
+.Dv PKG_DBDIR
+configuration option with the value
+.Ar pkg_dbdir .
+.It Fl k
+Silently skip all packages that are marked as
+.Cm preserved .
+.It Fl N
+Remove the package's registration and its entries from the package database,
+but leave the files installed.
+Don't run any deinstall scripts or
+.Cm @unexec
+lines either.
+.It Fl n
+Don't actually deinstall a package, just report the steps that
+would be taken.
+.It Fl O
+Only delete the package's entries from the package database; do not
+touch the package or its files itself.
+.It Fl P Ar destdir
+Prefix all file and directory names with
+.Ar destdir .
+For packages without install scripts this has the same behavior as
+using
+.Xr chroot 8 .
+.It Fl p Ar prefix
+Set
+.Ar prefix
+as the directory in which to delete files from any installed packages
+which do not explicitly set theirs.
+For most packages, the prefix will
+be set automatically to the installed location by
+.Xr pkg_add 1 .
+.It Fl R
+Recursively remove all packages that were needed by the given packages
+and are no longer required.
+This option overrides the
+.Fl A
+flag.
+.It Fl r
+Recursively remove all packages that require one of the packages given.
+.It Fl V
+Print version number and exit.
+.It Fl v
+Turn on verbose output.
+.El
+.Sh TECHNICAL DETAILS
+.Nm
+does pretty much what it says.
+It examines installed package records in
+.Pa \*[Lt]PKG_DBDIR\*[Gt]/\*[Lt]pkg-name\*[Gt] ,
+deletes the package contents, and finally removes the package records.
+.Pp
+If a package is required by other installed packages,
+.Nm
+will list those dependent packages and refuse to delete the package
+(unless the
+.Fl f
+option is given).
+.Pp
+If a package has been marked as a
+.Cm preserved
+package, it will not be able to be deleted
+(unless more than one occurrence of the
+.Fl f
+option is given).
+.Pp
+If a filename is given instead of a package name, the package of which
+the given file belongs to can be deleted if the
+.Fl F
+flag is given.
+The filename needs to be absolute, see the output produced by the
+.Xr pkg_info 1
+.Fl aF
+command.
+.Pp
+If a
+.Cm deinstall
+script exists for the package, it is executed before and after
+any files are removed.
+It is this script's responsibility to clean up any additional messy details
+around the package's installation, since all
+.Nm
+knows how to do is delete the files created in the original distribution.
+The
+.Ic deinstall
+script is called as:
+.Bd -filled -offset indent -compact
+.Cm deinstall
+.Aq Ar pkg-name
+.Ar VIEW-DEINSTALL
+.Ed
+before removing the package from a view, and as:
+.Bd -filled -offset indent -compact
+.Cm deinstall
+.Aq Ar pkg-name
+.Ar DEINSTALL
+.Ed
+before deleting all files and as:
+.Bd -filled -offset indent -compact
+.Cm deinstall
+.Aq Ar pkg-name
+.Ar POST-DEINSTALL
+.Ed
+after deleting them.
+Passing the keywords
+.Ar VIEW-DEINSTALL ,
+.Ar DEINSTALL ,
+and
+.Ar POST-DEINSTALL
+lets you potentially write only one program/script that handles all
+aspects of installation and deletion.
+.Pp
+All scripts are called with the environment variable
+.Ev PKG_PREFIX
+set to the installation prefix (see the
+.Fl p
+option above).
+This allows a package author to write a script
+that reliably performs some action on the directory where the package
+is installed, even if the user might have changed it by specifying the
+.Fl p
+option when running
+.Nm
+or
+.Xr pkg_add 1 .
+The scripts are also called with the
+.Ev PKG_METADATA_DIR
+environment variable set to the location of the
+.Pa +*
+meta-data files, and with the
+.Ev PKG_REFCOUNT_DBDIR
+environment variable set to the location of the package reference counts
+database directory.
+If the
+.Fl P
+flag was given to
+.Nm ,
+.Ev PKG_DESTDIR
+will be set to
+.Ar destdir .
+.Sh ENVIRONMENT
+See
+.Xr pkg_install.conf 5
+for options, that can also be specified using the environment.
+.Sh SEE ALSO
+.Xr pkg_add 1 ,
+.Xr pkg_admin 1 ,
+.Xr pkg_create 1 ,
+.Xr pkg_info 1 ,
+.Xr pkg_install.conf 5
+.Xr pkgsrc 7
+.Sh AUTHORS
+.Bl -tag -width indent -compact
+.It "Jordan Hubbard"
+most of the work
+.It "John Kohl"
+refined it for
+.Nx
+.It "Hubert Feyrer"
+.Nx
+wildcard dependency processing, pkgdb, recursive "down"
+delete, etc.
+.It Joerg Sonnenberger
+Rewrote most of the code to compute correct order of deinstallation
+and to improve error handling.
+.El
--- /dev/null
+/*-
+ * Copyright (c) 2009 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * Copyright (c) 2003 Johnny Lam <jlam@NetBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+__RCSID("$NetBSD: pkg_delete.c,v 1.1.1.8 2012/02/19 17:46:46 tron Exp $");
+
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "lib.h"
+
+static const char *pkgdb;
+static const char *destdir;
+static const char *prefix;
+
+static int keep_preserve;
+static int no_deinstall;
+static int find_by_filename;
+static int unregister_only;
+static int pkgdb_update_only;
+static int delete_recursive;
+static int delete_new_leaves;
+static int delete_automatic_leaves;
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: pkg_delete [-DFfkNnORrVv] [-K pkg_dbdir]"
+ " [-P destdir] [-p prefix] pkg-name ...\n");
+ exit(1);
+}
+
+static int
+add_by_filename(lpkg_head_t *pkgs, const char *filename)
+{
+ lpkg_t *lpp;
+ char *s;
+
+ if ((s = pkgdb_retrieve(filename)) == NULL) {
+ warnx("No matching package for file `%s' in pkgdb", filename);
+ return 1;
+ }
+
+ /* XXX Verify that pkgdb is consistent? Trust it for now... */
+
+ lpp = alloc_lpkg(s);
+ TAILQ_INSERT_TAIL(pkgs, lpp, lp_link);
+ return 0;
+}
+
+static int
+add_by_pattern(lpkg_head_t *pkgs, const char *pattern)
+{
+ switch (add_installed_pkgs_by_pattern(pattern, pkgs)) {
+ case 0:
+ warnx("No package matching `%s' found", pattern);
+ return 1;
+ case -1:
+ warnx("Error while iterating package database for `%s'",
+ pattern);
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * The argument is either a fixed package name or an absolute path.
+ * The latter is recognized for legacy compatibility and must point
+ * into the package database.
+ */
+static int
+add_by_pkgname(lpkg_head_t *pkgs, char *pkg)
+{
+ char *s;
+ lpkg_t *lpp;
+ size_t l;
+ const char *orig_pkg = pkg;
+
+ if (pkg[0] == '/') {
+ l = strlen(pkgdb);
+ if (strncmp(pkg, pkgdb, l) || pkg[l] != '/') {
+ warnx("Absolute path is not relative to "
+ "package database, skipping: %s", pkg);
+ return 1;
+ }
+ pkg += l + 1;
+ }
+ l = strcspn(pkg, "/");
+ if (pkg[l + strspn(pkg + l, "/")] != '\0') {
+ warnx("`%s' is not a package name, skipping", orig_pkg);
+ return 1;
+ }
+ pkg[l] = '\0';
+
+ s = pkgdb_pkg_file(pkg, CONTENTS_FNAME);
+ if (fexists(s)) {
+ free(s);
+ lpp = alloc_lpkg(pkg);
+ TAILQ_INSERT_TAIL(pkgs, lpp, lp_link);
+ return 0;
+ }
+ free(s);
+
+ switch (add_installed_pkgs_by_basename(pkg, pkgs)) {
+ case 0:
+ warnx("No matching package for basename `%s' of `%s'",
+ pkg, orig_pkg);
+ return 1;
+ case -1:
+ warnx("Error expanding basename `%s' of `%s'",
+ pkg, orig_pkg);
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Evaluate +REQUIRED_BY. This function is used for four different
+ * tasks:
+ * 0: check if no depending packages remain
+ * 1: like 0, but prepend the depending packages to pkgs if they exist
+ * 2: print remaining packages to stderr
+ * 3: check all and at least one depending packages have been removed
+ */
+static int
+process_required_by(const char *pkg, lpkg_head_t *pkgs,
+ lpkg_head_t *sorted_pkgs, int action)
+{
+ char line[MaxPathSize], *eol, *fname;
+ FILE *fp;
+ lpkg_t *lpp;
+ int got_match, got_miss;
+
+ fname = pkgdb_pkg_file(pkg, REQUIRED_BY_FNAME);
+ if (!fexists(fname)) {
+ free(fname);
+ return 0;
+ }
+
+ if ((fp = fopen(fname, "r")) == NULL) {
+ warn("Failed to open `%s'", fname);
+ free(fname);
+ return -1;
+ }
+ free(fname);
+
+ got_match = 0;
+ got_miss = 0;
+
+ while (fgets(line, sizeof(line), fp)) {
+ if ((eol = strrchr(line, '\n')) != NULL)
+ *eol = '\0';
+ TAILQ_FOREACH(lpp, sorted_pkgs, lp_link) {
+ if (strcmp(lpp->lp_name, line) == 0)
+ break;
+ }
+ if (lpp != NULL) {
+ got_match = 1;
+ continue;
+ }
+ got_miss = 1;
+ if (pkgs) {
+ TAILQ_FOREACH(lpp, pkgs, lp_link) {
+ if (strcmp(lpp->lp_name, line) == 0)
+ break;
+ }
+ if (lpp != NULL)
+ continue;
+ }
+ switch (action) {
+ case 0:
+ fclose(fp);
+ return 1;
+ case 1:
+ lpp = alloc_lpkg(line);
+ TAILQ_INSERT_HEAD(pkgs, lpp, lp_link);
+ break;
+ case 2:
+ fprintf(stderr, "\t%s\n", line);
+ break;
+ case 3:
+ fclose(fp);
+ return 0;
+ }
+ }
+
+ fclose(fp);
+
+ return (action == 3 ? got_match : got_miss);
+}
+
+/*
+ * Main function to order the patterns from the command line and
+ * add the subtrees for -r processing as needed.
+ *
+ * The first part ensures that all packages are listed at most once
+ * in pkgs. Afterwards the list is scanned for packages without depending
+ * packages. Each such package is moved to sorted_pkgs in order.
+ * If -r is given, all dependencies are inserted at the head of pkgs.
+ * The loop has to continue as long as progress is made. This can happen
+ * either because another package has been added to pkgs due to recursion
+ * (head of pkgs changed) or because a package has no more depending packages
+ * (tail of sorted_pkgs changed).
+ *
+ * If no progress is made, the remaining packages are moved to sorted_pkgs
+ * and an error is returned for the !Force case.
+ */
+static int
+sort_and_recurse(lpkg_head_t *pkgs, lpkg_head_t *sorted_pkgs)
+{
+ lpkg_t *lpp, *lpp2, *lpp_next, *lpp_old_tail, *lpp_old_head;
+ int rv;
+
+ TAILQ_FOREACH_SAFE(lpp, pkgs, lp_link, lpp_next) {
+ TAILQ_FOREACH(lpp2, pkgs, lp_link) {
+ if (lpp != lpp2 &&
+ strcmp(lpp->lp_name, lpp2->lp_name) == 0)
+ break;
+ }
+ if (lpp2 == NULL)
+ continue;
+ TAILQ_REMOVE(pkgs, lpp, lp_link);
+ free_lpkg(lpp);
+ }
+
+ while (!TAILQ_EMPTY(pkgs)) {
+ lpp_old_tail = TAILQ_LAST(sorted_pkgs, _lpkg_head_t);
+ lpp_old_head = TAILQ_FIRST(pkgs);
+
+ TAILQ_FOREACH_SAFE(lpp, pkgs, lp_link, lpp_next) {
+ rv = process_required_by(lpp->lp_name, pkgs,
+ sorted_pkgs, delete_recursive ? 1 : 0);
+ if (rv)
+ continue;
+ TAILQ_REMOVE(pkgs, lpp, lp_link);
+ TAILQ_INSERT_TAIL(sorted_pkgs, lpp, lp_link);
+ }
+
+ if (lpp_old_tail == TAILQ_LAST(sorted_pkgs, _lpkg_head_t) &&
+ lpp_old_head == TAILQ_FIRST(pkgs))
+ break;
+ }
+
+ if (TAILQ_EMPTY(pkgs))
+ return 0;
+
+ while (!TAILQ_EMPTY(pkgs)) {
+ lpp = TAILQ_FIRST(pkgs);
+ TAILQ_REMOVE(pkgs, lpp, lp_link);
+ fprintf(stderr,
+ "Package `%s' is still required by other packages:\n",
+ lpp->lp_name);
+ process_required_by(lpp->lp_name, NULL, sorted_pkgs, 2);
+ if (Force) {
+ TAILQ_INSERT_TAIL(sorted_pkgs, lpp, lp_link);
+ } else
+ free_lpkg(lpp);
+ }
+
+ return !Force;
+}
+
+struct find_leaves_data {
+ lpkg_head_t *pkgs;
+ int progress;
+};
+
+/*
+ * Iterator for finding leaf packages.
+ * Packages that are marked as not for deletion are not considered as
+ * leaves. For all other packages it is checked if at least one package
+ * that depended on them is to be removed AND no depending package remains.
+ * If that is the case, the package is appended to the sorted list.
+ * As this package can't have depending packages left, the topological order
+ * remains consistent.
+ */
+static int
+find_new_leaves_iter(const char *pkg, void *cookie)
+{
+ char *fname;
+ struct find_leaves_data *data = cookie;
+ lpkg_t *lpp;
+
+ fname = pkgdb_pkg_file(pkg, PRESERVE_FNAME);
+ if (fexists(fname)) {
+ free(fname);
+ return 0;
+ }
+ free(fname);
+
+ if (delete_automatic_leaves && !delete_new_leaves &&
+ !is_automatic_installed(pkg))
+ return 0;
+
+ /* Check whether this package is already on the list first. */
+ TAILQ_FOREACH(lpp, data->pkgs, lp_link) {
+ if (strcmp(lpp->lp_name, pkg) == 0)
+ return 0;
+ }
+
+ if (process_required_by(pkg, NULL, data->pkgs, 3) == 1) {
+ lpp = alloc_lpkg(pkg);
+ TAILQ_INSERT_TAIL(data->pkgs, lpp, lp_link);
+ data->progress = 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Iterate over all installed packages and look for new leaf packages.
+ * As long as the loop adds one new leaf package, processing continues.
+ */
+static void
+find_new_leaves(lpkg_head_t *pkgs)
+{
+ struct find_leaves_data data;
+
+ data.pkgs = pkgs;
+ do {
+ data.progress = 0;
+ iterate_pkg_db(find_new_leaves_iter, &data);
+ } while (data.progress);
+}
+
+/*
+ * Check that no entry on the package list is marked as not for deletion.
+ */
+static int
+find_preserve_pkgs(lpkg_head_t *pkgs)
+{
+ lpkg_t *lpp, *lpp_next;
+ char *fname;
+ int found_preserve;
+
+ found_preserve = 0;
+ TAILQ_FOREACH_SAFE(lpp, pkgs, lp_link, lpp_next) {
+ fname = pkgdb_pkg_file(lpp->lp_name, PRESERVE_FNAME);
+ if (!fexists(fname)) {
+ free(fname);
+ continue;
+ }
+ free(fname);
+ if (keep_preserve) {
+ TAILQ_REMOVE(pkgs, lpp, lp_link);
+ free_lpkg(lpp);
+ continue;
+ }
+ if (!found_preserve)
+ warnx("The following packages are marked as not "
+ "for deletion:");
+ found_preserve = 1;
+ fprintf(stderr, "\t%s\n", lpp->lp_name);
+ }
+ if (!found_preserve)
+ return 0;
+ if (Force == 0 || (!unregister_only && Force == 1))
+ return 1;
+ fprintf(stderr, "...but will delete them anyway\n");
+ return 0;
+}
+
+/*
+ * Remove package from view. This is calling pkg_deinstall again.
+ */
+static int
+remove_pkg_from_view(const char *pkg)
+{
+ char line[MaxPathSize], *fname, *eol;
+ FILE *fp;
+
+ fname = pkgdb_pkg_file(pkg, VIEWS_FNAME);
+ if (isemptyfile(fname)) {
+ free(fname);
+ return 0;
+ }
+ if ((fp = fopen(fname, "r")) == NULL) {
+ warn("Unable to open `%s', aborting", fname);
+ free(fname);
+ return 1;
+ }
+ free(fname);
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ if ((eol = strrchr(line, '\n')) != NULL)
+ *eol = '\0';
+ if (Verbose || Fake)
+ printf("Deleting package `%s' instance from `%s' view\n",
+ pkg, line);
+ if (Fake)
+ continue;
+ if (fexec_skipempty(BINDIR "/pkg_delete", "-K", line,
+ Fake ? "-n" : "",
+ (Force > 1) ? "-f" : "",
+ (Force > 0) ? "-f" : "",
+ pkg, NULL) != 0) {
+ warnx("Unable to delete package `%s' from view `%s'",
+ pkg, line);
+ fclose(fp);
+ return 1;
+ }
+ }
+ fclose(fp);
+ return 0;
+}
+
+/*
+ * Run the +DEINSTALL script. Depending on whether this is
+ * a depoted package and whether this pre- or post-deinstall phase,
+ * different arguments are passed down.
+ */
+static int
+run_deinstall_script(const char *pkg, int do_postdeinstall)
+{
+ const char *target, *text;
+ char *fname, *fname2, *pkgdir;
+ int rv;
+
+ fname = pkgdb_pkg_file(pkg, DEINSTALL_FNAME);
+ if (!fexists(fname)) {
+ free(fname);
+ return 0;
+ }
+
+ fname2 = pkgdb_pkg_file(pkg, DEPOT_FNAME);
+ if (fexists(fname2)) {
+ if (do_postdeinstall) {
+ free(fname);
+ free(fname2);
+ return 0;
+ }
+ target = "VIEW-DEINSTALL";
+ text = "view deinstall";
+ } else if (do_postdeinstall) {
+ target = "POST-DEINSTALL";
+ text = "post-deinstall";
+ } else {
+ target = "DEINSTALL";
+ text = "deinstall";
+ }
+ free(fname2);
+
+ if (Fake) {
+ printf("Would execute %s script with argument %s now\n",
+ text, target);
+ free(fname);
+ return 0;
+ }
+
+ pkgdir = pkgdb_pkg_dir(pkg);
+ if (chmod(fname, 0555))
+ warn("chmod of `%s' failed", fname);
+ rv = fcexec(pkgdir, fname, pkg, target, NULL);
+ if (rv)
+ warnx("%s script returned error status", text);
+ free(pkgdir);
+ free(fname);
+ return rv;
+}
+
+/*
+ * Copy lines from fname to fname_tmp, filtering out lines equal to text.
+ * Afterwards rename fname_tmp to fname;
+ */
+static int
+remove_line(const char *fname, const char *fname_tmp, const char *text)
+{
+ FILE *fp, *fp_out;
+ char line[MaxPathSize], *eol;
+ int rv;
+
+ if ((fp = fopen(fname, "r")) == NULL) {
+ warn("Unable to open `%s'", fname);
+ return 1;
+ }
+ if ((fp_out = fopen(fname_tmp, "w")) == NULL) {
+ warn("Unable to open `%s'", fname_tmp);
+ fclose(fp);
+ return 1;
+ }
+
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ if ((eol = strrchr(line, '\n')) != NULL)
+ *eol = '\0';
+ if (strcmp(line, text) == 0)
+ continue;
+ fprintf(fp_out, "%s\n", line);
+ }
+ fclose(fp);
+
+ if (fclose(fp_out) == EOF) {
+ remove(fname_tmp);
+ warnx("Failure while closing `%s' temp file", fname_tmp);
+ return 1;
+ }
+
+ if (rename(fname_tmp, fname) == -1) {
+ warn("Unable to rename `%s' to `%s'", fname_tmp, fname);
+ rv = 1;
+ } else
+ rv = 0;
+ remove(fname_tmp);
+
+ return rv;
+}
+
+/*
+ * Unregister the package from the depot it is registered in.
+ */
+static int
+remove_pkg_from_depot(const char *pkg)
+{
+ FILE *fp;
+ char line[MaxPathSize], *eol;
+ char *fname, *fname2;
+ int rv;
+
+ fname = pkgdb_pkg_file(pkg, DEPOT_FNAME);
+ if (isemptyfile(fname)) {
+ free(fname);
+ return 0;
+ }
+
+ if (Verbose)
+ printf("Attempting to remove the `%s' registration "
+ "on package `%s'\n", fname, pkg);
+
+ if (Fake) {
+ free(fname);
+ return 1;
+ }
+
+ if ((fp = fopen(fname, "r")) == NULL) {
+ warn("Unable to open `%s' file", fname);
+ free(fname);
+ return 1;
+ }
+ if (fgets(line, sizeof(line), fp) == NULL) {
+ fclose(fp);
+ warnx("Empty depot file `%s'", fname);
+ free(fname);
+ return 1;
+ }
+ if ((eol = strrchr(line, '\n')) != NULL)
+ *eol = '\0';
+ fclose(fp);
+ free(fname);
+
+ fname = pkgdb_pkg_file(pkg, VIEWS_FNAME);
+ fname2 = pkgdb_pkg_file(pkg, VIEWS_FNAME_TMP);
+ rv = remove_line(fname, fname2, line);
+ free(fname2);
+ free(fname);
+
+ return rv;
+}
+
+/*
+ * remove_depend is used as iterator function below.
+ * The passed-in package name should be removed from the
+ * +REQUIRED_BY list of the dependency. Such an entry
+ * can miss in a fully correct package database, if the pattern
+ * matches more than one package.
+ */
+static int
+remove_depend(const char *cur_pkg, void *cookie)
+{
+ const char *pkg = cookie;
+ char *fname, *fname2;
+ int rv;
+
+ fname = pkgdb_pkg_file(cur_pkg, REQUIRED_BY_FNAME);
+ if (isemptyfile(fname)) {
+ free(fname);
+ return 0;
+ }
+ fname2 = pkgdb_pkg_file(cur_pkg, REQUIRED_BY_FNAME_TMP);
+
+ rv = remove_line(fname, fname2, pkg);
+
+ free(fname2);
+ free(fname);
+
+ return rv;
+}
+
+static int
+remove_pkg(const char *pkg)
+{
+ FILE *fp;
+ char *fname, *pkgdir;
+ package_t plist;
+ plist_t *p;
+ int is_depoted_pkg, rv, late_error;
+
+ if (pkgdb_update_only)
+ return pkgdb_remove_pkg(pkg) ? 0 : 1;
+
+ fname = pkgdb_pkg_file(pkg, CONTENTS_FNAME);
+ if (!fexists(fname)) {
+ warnx("package `%s' is not installed, `%s' missing", pkg, fname);
+ free(fname);
+ return 1;
+ }
+ free(fname);
+
+ /* +REQUIRED_BY and +PRESERVE already checked */
+ if (remove_pkg_from_view(pkg))
+ return 1;
+
+ /*
+ * The views related code has bad error handling, if e.g.
+ * the deinstall script fails, the package remains unregistered.
+ */
+
+ fname = pkgdb_pkg_file(pkg, CONTENTS_FNAME);
+ if ((fp = fopen(fname, "r")) == NULL) {
+ warnx("Failed to open `%s'", fname);
+ free(fname);
+ return 1;
+ }
+ read_plist(&plist, fp);
+ fclose(fp);
+
+ /*
+ * If a prefix has been provided, remove the first @cwd and
+ * prepend that prefix. This allows removing packages without
+ * @cwd if really necessary. pkg_admin rebuild is likely needed
+ * afterwards though.
+ */
+ if (prefix) {
+ delete_plist(&plist, FALSE, PLIST_CWD, NULL);
+ add_plist_top(&plist, PLIST_CWD, prefix);
+ }
+ if ((p = find_plist(&plist, PLIST_CWD)) == NULL) {
+ warnx("Package `%s' doesn't have a prefix", pkg);
+ return 1;
+ }
+
+ if (find_plist(&plist, PLIST_NAME) == NULL) {
+ /* Cheat a bit to allow removal of such bad packages. */
+ warnx("Package `%s' doesn't have a name", pkg);
+ add_plist_top(&plist, PLIST_NAME, pkg);
+ }
+
+ setenv(PKG_REFCOUNT_DBDIR_VNAME, config_pkg_refcount_dbdir, 1);
+ fname = pkgdb_pkg_dir(pkg);
+ setenv(PKG_METADATA_DIR_VNAME, fname, 1);
+ free(fname);
+ setenv(PKG_PREFIX_VNAME, p->name, 1);
+
+ if (!no_deinstall && !unregister_only) {
+ if (run_deinstall_script(pkg, 0) && !Force)
+ return 1;
+ }
+
+ late_error = 0;
+
+ if (Fake)
+ printf("Attempting to delete package `%s'\n", pkg);
+ else if (delete_package(FALSE, &plist, unregister_only,
+ destdir) == FAIL) {
+ warnx("couldn't entirely delete package `%s'", pkg);
+ /*
+ * XXX It could be nice to error out here explicitly,
+ * XXX but this is problematic for missing or changed files.
+ * XXX At least the inability to remove files at all should
+ * XXX be handled though.
+ */
+ }
+
+ /*
+ * Past the point of no return. Files are gone, all that is left
+ * is cleaning up registered dependencies and removing the meta data.
+ * Errors in the remaining part are counted, but don't stop the
+ * processing.
+ */
+
+ fname = pkgdb_pkg_file(pkg, DEPOT_FNAME);
+ if (fexists(fname)) {
+ late_error |= remove_pkg_from_depot(pkg);
+ /* XXX error checking */
+ } else {
+ for (p = plist.head; p; p = p->next) {
+ if (p->type != PLIST_PKGDEP)
+ continue;
+ if (Verbose)
+ printf("Attempting to remove dependency "
+ "on package `%s'\n", p->name);
+ if (Fake)
+ continue;
+ match_installed_pkgs(p->name, remove_depend,
+ __UNCONST(pkg));
+ }
+ }
+ free(fname);
+
+ free_plist(&plist);
+
+ if (!no_deinstall && !unregister_only)
+ late_error |= run_deinstall_script(pkg, 1);
+
+ fname = pkgdb_pkg_file(pkg, VIEWS_FNAME);
+ if (fexists(fname))
+ is_depoted_pkg = TRUE;
+ else
+ is_depoted_pkg = FALSE;
+ free(fname);
+
+ if (Fake)
+ return 0;
+
+ /*
+ * Kill the pkgdb subdirectory. The files have been removed, so
+ * this is way beyond the point of no return.
+ */
+ pkgdir = pkgdb_pkg_dir(pkg);
+ (void) remove_files(pkgdir, "+*");
+ rv = 1;
+ if (isemptydir(pkgdir)&& rmdir(pkgdir) == 0)
+ rv = 0;
+ else if (is_depoted_pkg)
+ warnx("Depot directory `%s' is not empty", pkgdir);
+ else if (!Force)
+ warnx("Couldn't remove package directory in `%s'", pkgdir);
+ else if (recursive_remove(pkgdir, 1))
+ warn("Couldn't remove package directory `%s'", pkgdir);
+ else
+ warnx("Package directory `%s' forcefully removed", pkgdir);
+ free(pkgdir);
+
+ return rv | late_error;
+}
+
+int
+main(int argc, char *argv[])
+{
+ lpkg_head_t pkgs, sorted_pkgs;
+ int ch, r, has_error;
+ unsigned long bad_count;
+
+ TAILQ_INIT(&pkgs);
+ TAILQ_INIT(&sorted_pkgs);
+
+ setprogname(argv[0]);
+ while ((ch = getopt(argc, argv, "ADFfK:kNnOP:p:RrVv")) != -1) {
+ switch (ch) {
+ case 'A':
+ delete_automatic_leaves = 1;
+ break;
+ case 'D':
+ no_deinstall = 1;
+ break;
+ case 'F':
+ find_by_filename = 1;
+ break;
+ case 'f':
+ ++Force;
+ break;
+ case 'K':
+ pkgdb_set_dir(optarg, 3);
+ break;
+ case 'k':
+ keep_preserve = 1;
+ break;
+ case 'N':
+ unregister_only = 1;
+ break;
+ case 'n':
+ Fake = 1;
+ break;
+ case 'O':
+ pkgdb_update_only = 1;
+ break;
+ case 'P':
+ destdir = optarg;
+ break;
+ case 'p':
+ prefix = optarg;
+ break;
+ case 'R':
+ delete_new_leaves = 1;
+ break;
+ case 'r':
+ delete_recursive = 1;
+ break;
+ case 'V':
+ show_version();
+ /* NOTREACHED */
+ case 'v':
+ ++Verbose;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ pkg_install_config();
+
+ pkgdb = xstrdup(pkgdb_get_dir());
+
+ if (destdir != NULL) {
+ char *pkgdbdir;
+
+ pkgdbdir = xasprintf("%s/%s", destdir, pkgdb);
+ pkgdb_set_dir(pkgdbdir, 4);
+ free(pkgdbdir);
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0) {
+ if (find_by_filename)
+ warnx("Missing filename(s)");
+ else
+ warnx("Missing package name(s)");
+ usage();
+ }
+
+ if (Fake)
+ r = pkgdb_open(ReadOnly);
+ else
+ r = pkgdb_open(ReadWrite);
+
+ if (!r)
+ errx(EXIT_FAILURE, "Opening pkgdb failed");
+
+ /* First, process all command line options. */
+
+ has_error = 0;
+ for (; argc != 0; --argc, ++argv) {
+ if (find_by_filename)
+ has_error |= add_by_filename(&pkgs, *argv);
+ else if (ispkgpattern(*argv))
+ has_error |= add_by_pattern(&pkgs, *argv);
+ else
+ has_error |= add_by_pkgname(&pkgs, *argv);
+ }
+
+ if (has_error && !Force) {
+ pkgdb_close();
+ return EXIT_FAILURE;
+ }
+
+ /* Second, reorder and recursive if necessary. */
+
+ if (sort_and_recurse(&pkgs, &sorted_pkgs)) {
+ pkgdb_close();
+ return EXIT_FAILURE;
+ }
+
+ /* Third, add leaves if necessary. */
+
+ if (delete_new_leaves || delete_automatic_leaves)
+ find_new_leaves(&sorted_pkgs);
+
+ /*
+ * Now that all packages to remove are known, check
+ * if all are removable. After that, start the actual
+ * removal.
+ */
+
+ if (find_preserve_pkgs(&sorted_pkgs)) {
+ pkgdb_close();
+ return EXIT_FAILURE;
+ }
+
+ setenv(PKG_REFCOUNT_DBDIR_VNAME, pkgdb_refcount_dir(), 1);
+
+ bad_count = 0;
+ while (!TAILQ_EMPTY(&sorted_pkgs)) {
+ lpkg_t *lpp;
+
+ lpp = TAILQ_FIRST(&sorted_pkgs);
+ TAILQ_REMOVE(&sorted_pkgs, lpp, lp_link);
+ if (remove_pkg(lpp->lp_name)) {
+ ++bad_count;
+ if (!Force)
+ break;
+ }
+ free_lpkg(lpp);
+ }
+
+ pkgdb_close();
+
+ if (Force && bad_count && Verbose)
+ warnx("Removal of %lu packages failed", bad_count);
+
+ return bad_count > 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
--- /dev/null
+/* $NetBSD: info.h,v 1.1.1.5 2009/10/07 13:19:42 joerg Exp $ */
+
+/* from FreeBSD Id: info.h,v 1.10 1997/02/22 16:09:40 peter Exp */
+
+/*-
+ * Copyright (c) 2008 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 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.
+ */
+
+/*
+ * 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
+ * 23 August 1993
+ *
+ * Include and define various things wanted by the info command.
+ *
+ */
+
+#ifndef _INST_INFO_H_INCLUDE
+#define _INST_INFO_H_INCLUDE
+
+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;
+ int is_installed;
+};
+
+#ifndef MAXINDEXSIZE
+#define MAXINDEXSIZE 60
+#endif
+
+#ifndef MAXNAMESIZE
+#define MAXNAMESIZE 20
+#endif
+
+#define SHOW_COMMENT 0x00001
+#define SHOW_DESC 0x00002
+#define SHOW_PLIST 0x00004
+#define SHOW_INSTALL 0x00008
+#define SHOW_DEINSTALL 0x00010
+#define SHOW_PREFIX 0x00040
+#define SHOW_INDEX 0x00080
+#define SHOW_FILES 0x00100
+#define SHOW_DISPLAY 0x00200
+#define SHOW_REQBY 0x00400
+#define SHOW_MTREE 0x00800
+#define SHOW_BUILD_VERSION 0x01000
+#define SHOW_BUILD_INFO 0x02000
+#define SHOW_DEPENDS 0x04000
+#define SHOW_PKG_SIZE 0x08000
+#define SHOW_ALL_SIZE 0x10000
+#define SHOW_BLD_DEPENDS 0x20000
+#define SHOW_BI_VAR 0x40000
+#define SHOW_SUMMARY 0x80000
+#define SHOW_FULL_REQBY 0x100000
+
+enum which {
+ WHICH_ALL,
+ WHICH_USER,
+ WHICH_LIST
+};
+
+extern int Flags;
+extern enum which Which;
+extern Boolean File2Pkg;
+extern Boolean Quiet;
+extern const char *InfoPrefix;
+extern const char *BuildInfoVariable;
+extern lpkg_head_t pkgs;
+
+int CheckForPkg(const char *);
+int CheckForBestPkg(const char *);
+
+void show_file(const char *, const char *, Boolean);
+void show_var(const char *, const char *);
+void show_plist(const char *, package_t *, pl_ent_t);
+void show_files(const char *, package_t *);
+void show_depends(const char *, package_t *);
+void show_bld_depends(const char *, package_t *);
+void show_index(const char *, const char *);
+void show_summary(struct pkg_meta *, package_t *, const char *);
+void show_list(lpkg_head_t *, const char *);
+
+int pkg_perform(lpkg_head_t *);
+
+#endif /* _INST_INFO_H_INCLUDE */
--- /dev/null
+/* $NetBSD: main.c,v 1.1.1.9 2013/04/20 15:26:53 wiz Exp $ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+__RCSID("$NetBSD: main.c,v 1.1.1.9 2013/04/20 15:26:53 wiz Exp $");
+
+/*
+ *
+ * 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 add module.
+ *
+ */
+
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+
+#include "lib.h"
+#include "info.h"
+
+static const char Options[] = ".aBbcDde:E:fFhIiK:kLl:mNnpQ:qrRsSuvVX";
+
+int Flags = 0;
+enum which Which = WHICH_LIST;
+Boolean File2Pkg = FALSE;
+Boolean Quiet = FALSE;
+const char *InfoPrefix = "";
+const char *BuildInfoVariable = "";
+lpkg_head_t pkgs;
+
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n",
+ "usage: pkg_info [-BbcDdFfhIikLmNnpqRrSsVvX] [-E pkg-name] [-e pkg-name]",
+ " [-K pkg_dbdir] [-l prefix] pkg-name ...",
+ " pkg_info [-a | -u] [flags]",
+ " pkg_info [-Q variable] pkg-name ...");
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ char *CheckPkg = NULL;
+ char *BestCheckPkg = NULL;
+ lpkg_t *lpp;
+ int ch;
+ int rc;
+
+ setprogname(argv[0]);
+ while ((ch = getopt(argc, argv, Options)) != -1)
+ switch (ch) {
+ case '.': /* for backward compatibility */
+ break;
+
+ case 'a':
+ Which = WHICH_ALL;
+ break;
+
+ case 'B':
+ Flags |= SHOW_BUILD_INFO;
+ break;
+
+ case 'b':
+ Flags |= SHOW_BUILD_VERSION;
+ break;
+
+ case 'c':
+ Flags |= SHOW_COMMENT;
+ break;
+
+ case 'D':
+ Flags |= SHOW_DISPLAY;
+ break;
+
+ case 'd':
+ Flags |= SHOW_DESC;
+ break;
+
+ case 'E':
+ BestCheckPkg = optarg;
+ break;
+
+ case 'e':
+ CheckPkg = optarg;
+ break;
+
+ case 'f':
+ Flags |= SHOW_PLIST;
+ break;
+
+ case 'F':
+ File2Pkg = 1;
+ break;
+
+ case 'I':
+ Flags |= SHOW_INDEX;
+ break;
+
+ case 'i':
+ Flags |= SHOW_INSTALL;
+ break;
+
+ case 'K':
+ pkgdb_set_dir(optarg, 3);
+ break;
+
+ case 'k':
+ Flags |= SHOW_DEINSTALL;
+ break;
+
+ case 'L':
+ Flags |= SHOW_FILES;
+ break;
+
+ case 'l':
+ InfoPrefix = optarg;
+ break;
+
+ case 'm':
+ Flags |= SHOW_MTREE;
+ break;
+
+ case 'N':
+ Flags |= SHOW_BLD_DEPENDS;
+ break;
+
+ case 'n':
+ Flags |= SHOW_DEPENDS;
+ break;
+
+ case 'p':
+ Flags |= SHOW_PREFIX;
+ break;
+
+ case 'Q':
+ Flags |= SHOW_BI_VAR;
+ BuildInfoVariable = optarg;
+ break;
+
+ case 'q':
+ Quiet = TRUE;
+ break;
+
+ case 'r':
+ Flags |= SHOW_FULL_REQBY;
+ break;
+
+ case 'R':
+ Flags |= SHOW_REQBY;
+ break;
+
+ case 's':
+ Flags |= SHOW_PKG_SIZE;
+ break;
+
+ case 'S':
+ Flags |= SHOW_ALL_SIZE;
+ break;
+
+ case 'u':
+ Which = WHICH_USER;
+ break;
+
+ case 'v':
+ Verbose = TRUE;
+ /* Reasonable definition of 'everything' */
+ Flags = SHOW_COMMENT | SHOW_DESC | SHOW_PLIST | SHOW_INSTALL |
+ SHOW_DEINSTALL | SHOW_DISPLAY | SHOW_MTREE |
+ SHOW_REQBY | SHOW_BLD_DEPENDS | SHOW_DEPENDS | SHOW_PKG_SIZE | SHOW_ALL_SIZE;
+ break;
+
+ case 'V':
+ show_version();
+ /* NOTREACHED */
+
+ case 'X':
+ Flags |= SHOW_SUMMARY;
+ break;
+
+ case 'h':
+ case '?':
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ pkg_install_config();
+
+ if (argc == 0 && !Flags && !CheckPkg) {
+ /* No argument or relevant flags specified - assume -I */
+ Flags = SHOW_INDEX;
+ /* assume -a if neither -u nor -a is given */
+ if (Which == WHICH_LIST)
+ Which = WHICH_ALL;
+ }
+
+ if (CheckPkg != NULL && BestCheckPkg != NULL) {
+ warnx("-E and -e are mutally exlusive");
+ usage();
+ }
+
+ if (argc != 0 && CheckPkg != NULL) {
+ warnx("can't give any additional arguments to -e");
+ usage();
+ }
+
+ if (argc != 0 && BestCheckPkg != NULL) {
+ warnx("can't give any additional arguments to -E");
+ usage();
+ }
+
+ if (argc != 0 && Which != WHICH_LIST) {
+ warnx("can't use both -a/-u and package name");
+ usage();
+ }
+
+ /* Set some reasonable defaults */
+ if (!Flags)
+ Flags = SHOW_COMMENT | SHOW_DESC | SHOW_REQBY
+ | SHOW_DEPENDS | SHOW_DISPLAY;
+
+ /* -Fe /filename -> change CheckPkg to real packagename */
+ if (CheckPkg) {
+ if (File2Pkg) {
+ char *s;
+
+ if (!pkgdb_open(ReadOnly))
+ err(EXIT_FAILURE, "cannot open pkgdb");
+
+ s = pkgdb_retrieve(CheckPkg);
+
+ if (s == NULL)
+ errx(EXIT_FAILURE, "No matching pkg for %s.", CheckPkg);
+ CheckPkg = xstrdup(s);
+
+ pkgdb_close();
+ }
+ return CheckForPkg(CheckPkg);
+ }
+
+ if (BestCheckPkg)
+ return CheckForBestPkg(BestCheckPkg);
+
+ TAILQ_INIT(&pkgs);
+
+ /* Get all the remaining package names, if any */
+ if (File2Pkg && Which == WHICH_LIST)
+ if (!pkgdb_open(ReadOnly)) {
+ err(EXIT_FAILURE, "cannot open pkgdb");
+ }
+ while (*argv) {
+ /* pkgdb: if -F flag given, don't add pkgnames to the "pkgs"
+ * queue but rather resolve the given filenames to pkgnames
+ * using pkgdb_retrieve, then add them. */
+ if (File2Pkg) {
+ char *s;
+
+ s = pkgdb_retrieve(*argv);
+
+ if (s) {
+ lpp = alloc_lpkg(s);
+ TAILQ_INSERT_TAIL(&pkgs, lpp, lp_link);
+ } else
+ errx(EXIT_FAILURE, "No matching pkg for %s.", *argv);
+ } else {
+ if (ispkgpattern(*argv)) {
+ switch (add_installed_pkgs_by_pattern(*argv, &pkgs)) {
+ case 0:
+ errx(EXIT_FAILURE, "No matching pkg for %s.", *argv);
+ case -1:
+ errx(EXIT_FAILURE, "Error during search in pkgdb for %s", *argv);
+ }
+ } else {
+ const char *dbdir;
+
+ dbdir = pkgdb_get_dir();
+ if (**argv == '/' && strncmp(*argv, dbdir, strlen(dbdir)) == 0) {
+ *argv += strlen(dbdir) + 1;
+ if ((*argv)[strlen(*argv) - 1] == '/') {
+ (*argv)[strlen(*argv) - 1] = 0;
+ }
+ }
+ lpp = alloc_lpkg(*argv);
+ TAILQ_INSERT_TAIL(&pkgs, lpp, lp_link);
+ }
+ }
+ argv++;
+ }
+
+ if (File2Pkg)
+ pkgdb_close();
+
+ /* If no packages, yelp */
+ if (TAILQ_FIRST(&pkgs) == NULL && Which == WHICH_LIST && !CheckPkg)
+ warnx("missing package name(s)"), usage();
+
+ rc = pkg_perform(&pkgs);
+ exit(rc);
+ /* NOTREACHED */
+}
--- /dev/null
+/* $NetBSD: perform.c,v 1.1.1.13 2010/02/20 04:41:55 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
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+__RCSID("$NetBSD: perform.c,v 1.1.1.13 2010/02/20 04:41:55 joerg Exp $");
+
+/*-
+ * Copyright (c) 2008 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 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.
+ */
+
+/*
+ * 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
+ * 23 Aug 1993
+ *
+ * This is the main body of the info module.
+ *
+ */
+
+#include "lib.h"
+#include "info.h"
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifndef BOOTSTRAP
+#include <archive.h>
+#include <archive_entry.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_SIGNAL_H
+#include <signal.h>
+#endif
+#if HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+#if HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+#include <stddef.h>
+
+#define LOAD_CONTENTS (1 << 0)
+#define LOAD_COMMENT (1 << 1)
+#define LOAD_DESC (1 << 2)
+#define LOAD_INSTALL (1 << 3)
+#define LOAD_DEINSTALL (1 << 4)
+#define LOAD_DISPLAY (1 << 5)
+#define LOAD_MTREE (1 << 6)
+#define LOAD_BUILD_VERSION (1 << 7)
+#define LOAD_BUILD_INFO (1 << 8)
+#define LOAD_SIZE_PKG (1 << 9)
+#define LOAD_SIZE_ALL (1 << 10)
+#define LOAD_PRESERVE (1 << 11)
+#define LOAD_VIEWS (1 << 12)
+#define LOAD_REQUIRED_BY (1 << 13)
+#define LOAD_INSTALLED_INFO (1 << 14)
+
+static const struct pkg_meta_desc {
+ size_t entry_offset;
+ const char *entry_filename;
+ int entry_mask;
+ int required_file;
+} pkg_meta_descriptors[] = {
+ { offsetof(struct pkg_meta, meta_contents), CONTENTS_FNAME,
+ LOAD_CONTENTS, 1},
+ { offsetof(struct pkg_meta, meta_comment), COMMENT_FNAME,
+ LOAD_COMMENT, 1 },
+ { offsetof(struct pkg_meta, meta_desc), DESC_FNAME,
+ LOAD_DESC, 1 },
+ { offsetof(struct pkg_meta, meta_install), INSTALL_FNAME,
+ LOAD_INSTALL, 0 },
+ { offsetof(struct pkg_meta, meta_deinstall), DEINSTALL_FNAME,
+ LOAD_DEINSTALL, 0 },
+ { offsetof(struct pkg_meta, meta_display), DISPLAY_FNAME,
+ LOAD_DISPLAY, 0 },
+ { offsetof(struct pkg_meta, meta_mtree), MTREE_FNAME,
+ LOAD_MTREE, 0 },
+ { offsetof(struct pkg_meta, meta_build_version), BUILD_VERSION_FNAME,
+ LOAD_BUILD_VERSION, 0 },
+ { offsetof(struct pkg_meta, meta_build_info), BUILD_INFO_FNAME,
+ LOAD_BUILD_INFO, 0 },
+ { offsetof(struct pkg_meta, meta_size_pkg), SIZE_PKG_FNAME,
+ LOAD_SIZE_PKG, 0 },
+ { offsetof(struct pkg_meta, meta_size_all), SIZE_ALL_FNAME,
+ LOAD_SIZE_ALL, 0 },
+ { offsetof(struct pkg_meta, meta_preserve), PRESERVE_FNAME,
+ LOAD_PRESERVE, 0 },
+ { offsetof(struct pkg_meta, meta_views), VIEWS_FNAME,
+ LOAD_VIEWS, 0 },
+ { offsetof(struct pkg_meta, meta_required_by), REQUIRED_BY_FNAME,
+ LOAD_REQUIRED_BY, 0 },
+ { offsetof(struct pkg_meta, meta_installed_info), INSTALLED_INFO_FNAME,
+ LOAD_INSTALLED_INFO, 0 },
+ { 0, NULL, 0, 0 },
+};
+
+static int desired_meta_data;
+
+static void
+free_pkg_meta(struct pkg_meta *meta)
+{
+ const struct pkg_meta_desc *descr;
+
+ for (descr = pkg_meta_descriptors; descr->entry_filename; ++descr)
+ free(*(char **)((char *)meta + descr->entry_offset));
+
+ free(meta);
+}
+
+#ifndef BOOTSTRAP
+static struct pkg_meta *
+read_meta_data_from_archive(struct archive *archive,
+ struct archive_entry *entry)
+{
+ struct pkg_meta *meta;
+ const char *fname;
+ const struct pkg_meta_desc *descr, *last_descr;
+ char **target;
+ int64_t size;
+ int r, found_required;
+
+ found_required = 0;
+
+ meta = xcalloc(1, sizeof(*meta));
+
+ last_descr = 0;
+ if (entry != NULL) {
+ r = ARCHIVE_OK;
+ goto has_entry;
+ }
+
+ while ((r = archive_read_next_header(archive, &entry)) == ARCHIVE_OK) {
+has_entry:
+ fname = archive_entry_pathname(entry);
+
+ for (descr = pkg_meta_descriptors; descr->entry_filename;
+ ++descr) {
+ if (strcmp(descr->entry_filename, fname) == 0)
+ break;
+ }
+ if (descr->entry_filename == NULL)
+ break;
+
+ if (descr->required_file)
+ ++found_required;
+
+ target = (char **)((char *)meta + descr->entry_offset);
+ if (*target)
+ errx(2, "duplicate entry, package corrupt");
+ if (descr < last_descr)
+ warnx("misordered package, continuing");
+ else
+ last_descr = descr;
+
+ if ((descr->entry_mask & desired_meta_data) == 0) {
+ if (archive_read_data_skip(archive))
+ errx(2, "cannot read package meta data");
+ continue;
+ }
+
+ size = archive_entry_size(entry);
+ if (size > SSIZE_MAX - 1)
+ errx(2, "package meta data too large to process");
+ *target = xmalloc(size + 1);
+ if (archive_read_data(archive, *target, size) != size)
+ errx(2, "cannot read package meta data");
+ (*target)[size] = '\0';
+ }
+
+ for (descr = pkg_meta_descriptors; descr->entry_filename; ++descr) {
+ if (descr->required_file)
+ --found_required;
+ }
+
+ meta->is_installed = 0;
+ if (found_required != 0 || (r != ARCHIVE_OK && r != ARCHIVE_EOF)) {
+ free_pkg_meta(meta);
+ meta = NULL;
+ }
+
+ return meta;
+}
+#endif
+
+static struct pkg_meta *
+read_meta_data_from_pkgdb(const char *pkg)
+{
+ struct pkg_meta *meta;
+ const struct pkg_meta_desc *descr;
+ char **target;
+ char *fname;
+ int fd;
+ struct stat st;
+
+ meta = xcalloc(1, sizeof(*meta));
+
+ for (descr = pkg_meta_descriptors; descr->entry_filename; ++descr) {
+ if ((descr->entry_mask & desired_meta_data) == 0)
+ continue;
+
+ fname = pkgdb_pkg_file(pkg, descr->entry_filename);
+ fd = open(fname, O_RDONLY, 0);
+ free(fname);
+ if (fd == -1) {
+ if (errno == ENOENT && descr->required_file == 0)
+ continue;
+ err(2, "cannot read meta data file %s of package %s",
+ descr->entry_filename, pkg);
+ }
+ target = (char **)((char *)meta + descr->entry_offset);
+
+ if (fstat(fd, &st) == -1)
+ err(2, "cannot stat meta data");
+ if ((st.st_mode & S_IFMT) != S_IFREG)
+ errx(1, "meta data is not regular file");
+ if (st.st_size > SSIZE_MAX - 1)
+ err(2, "meta data file too large to process");
+ *target = xmalloc(st.st_size + 1);
+ if (read(fd, *target, st.st_size) != st.st_size)
+ err(2, "cannot read meta data");
+ (*target)[st.st_size] = '\0';
+ close(fd);
+ }
+
+ meta->is_installed = 1;
+
+ return meta;
+}
+
+static void
+build_full_reqby(lpkg_head_t *reqby, struct pkg_meta *meta, int limit)
+{
+ char *iter, *eol, *next;
+ lpkg_t *lpp;
+ struct pkg_meta *meta_dep;
+
+ if (limit == 65536)
+ errx(1, "Cycle in the dependency tree, bailing out");
+
+ if (meta->is_installed == 0 || meta->meta_required_by == NULL)
+ return;
+
+ for (iter = meta->meta_required_by; *iter != '\0'; iter = next) {
+ eol = iter + strcspn(iter, "\n");
+ if (*eol == '\n')
+ next = eol + 1;
+ else
+ next = eol;
+ if (iter == eol)
+ continue;
+ TAILQ_FOREACH(lpp, reqby, lp_link) {
+ if (strlen(lpp->lp_name) + iter != eol)
+ continue;
+ if (memcmp(lpp->lp_name, iter, eol - iter) == 0)
+ break;
+ }
+ if (lpp != NULL)
+ continue;
+ *eol = '\0';
+ lpp = alloc_lpkg(iter);
+ if (next != eol)
+ *eol = '\n';
+
+ meta_dep = read_meta_data_from_pkgdb(lpp->lp_name);
+ if (meta_dep == NULL)
+ continue;
+ build_full_reqby(reqby, meta_dep, limit + 1);
+ free_pkg_meta(meta_dep);
+
+ TAILQ_INSERT_HEAD(reqby, lpp, lp_link);
+ }
+}
+
+static lfile_head_t files;
+
+static int
+pkg_do(const char *pkg)
+{
+ struct pkg_meta *meta;
+ int code = 0;
+ const char *binpkgfile = NULL;
+ char *pkgdir;
+
+ if (IS_URL(pkg) || (fexists(pkg) && isfile(pkg))) {
+#ifdef BOOTSTRAP
+ errx(2, "Binary packages not supported during bootstrap");
+#else
+ struct archive *archive;
+ struct archive_entry *entry;
+ char *archive_name, *pkgname;
+
+ archive = open_archive(pkg, &archive_name);
+ if (archive == NULL) {
+ warnx("can't find package `%s', skipped", pkg);
+ return -1;
+ }
+ pkgname = NULL;
+ entry = NULL;
+ pkg_verify_signature(archive_name, &archive, &entry, &pkgname);
+ if (archive == NULL)
+ return -1;
+ free(pkgname);
+
+ meta = read_meta_data_from_archive(archive, entry);
+ archive_read_finish(archive);
+ if (!IS_URL(pkg))
+ binpkgfile = pkg;
+#endif
+ } else {
+ /*
+ * It's not an uninstalled package, try and find it among the
+ * installed
+ */
+ pkgdir = pkgdb_pkg_dir(pkg);
+ if (!fexists(pkgdir) || !(isdir(pkgdir) || islinktodir(pkgdir))) {
+ switch (add_installed_pkgs_by_basename(pkg, &pkgs)) {
+ case 1:
+ return 0;
+ case 0:
+ /* No match */
+ warnx("can't find package `%s'", pkg);
+ return 1;
+ case -1:
+ errx(EXIT_FAILURE, "Error during search in pkgdb for %s", pkg);
+ }
+ }
+ free(pkgdir);
+ meta = read_meta_data_from_pkgdb(pkg);
+ }
+
+ if (meta == NULL) {
+ warnx("invalid package `%s' skipped", pkg);
+ return 1;
+ }
+
+ /*
+ * Index is special info type that has to override all others to make
+ * any sense.
+ */
+ if (Flags & SHOW_INDEX) {
+ char tmp[MaxPathSize];
+
+ (void) snprintf(tmp, sizeof(tmp), "%-19s ", pkg);
+ show_index(meta->meta_comment, tmp);
+ } else if (Flags & SHOW_BI_VAR) {
+ if (strcspn(BuildInfoVariable, "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
+ == strlen(BuildInfoVariable)) {
+ if (meta->meta_installed_info)
+ show_var(meta->meta_installed_info, BuildInfoVariable);
+ } else {
+ if (meta->meta_build_info)
+ show_var(meta->meta_build_info, BuildInfoVariable);
+ else
+ warnx("Build information missing");
+ }
+ } else {
+ package_t plist;
+
+ /* Read the contents list */
+ parse_plist(&plist, meta->meta_contents);
+
+ /* Start showing the package contents */
+ if (!Quiet && !(Flags & SHOW_SUMMARY)) {
+ printf("%sInformation for %s:\n\n", InfoPrefix, pkg);
+ if (meta->meta_preserve) {
+ printf("*** PACKAGE MAY NOT BE DELETED ***\n");
+ }
+ }
+ if (Flags & SHOW_SUMMARY) {
+ show_summary(meta, &plist, binpkgfile);
+ }
+ if (Flags & SHOW_COMMENT) {
+ show_file(meta->meta_comment, "Comment:\n", TRUE);
+ }
+ if (Flags & SHOW_DEPENDS) {
+ show_depends("Requires:\n", &plist);
+ }
+ if (Flags & SHOW_BLD_DEPENDS) {
+ show_bld_depends("Built using:\n", &plist);
+ }
+ if ((Flags & SHOW_REQBY) && meta->meta_required_by) {
+ show_file(meta->meta_required_by, "Required by:\n", TRUE);
+ }
+ if ((Flags & SHOW_FULL_REQBY) && meta->is_installed) {
+ lpkg_head_t reqby;
+ TAILQ_INIT(&reqby);
+ build_full_reqby(&reqby, meta, 0);
+ show_list(&reqby, "Full required by list:\n");
+ }
+ if (Flags & SHOW_DESC) {
+ show_file(meta->meta_desc, "Description:\n", TRUE);
+ }
+ if ((Flags & SHOW_DISPLAY) && meta->meta_display) {
+ show_file(meta->meta_display, "Install notice:\n",
+ TRUE);
+ }
+ if (Flags & SHOW_PLIST) {
+ show_plist("Packing list:\n", &plist, PLIST_SHOW_ALL);
+ }
+ if ((Flags & SHOW_INSTALL) && meta->meta_install) {
+ show_file(meta->meta_install, "Install script:\n",
+ TRUE);
+ }
+ if ((Flags & SHOW_DEINSTALL) && meta->meta_deinstall) {
+ show_file(meta->meta_deinstall, "De-Install script:\n",
+ TRUE);
+ }
+ if ((Flags & SHOW_MTREE) && meta->meta_mtree) {
+ show_file(meta->meta_mtree, "mtree file:\n", TRUE);
+ }
+ if (Flags & SHOW_PREFIX) {
+ show_plist("Prefix(s):\n", &plist, PLIST_CWD);
+ }
+ if (Flags & SHOW_FILES) {
+ show_files("Files:\n", &plist);
+ }
+ if ((Flags & SHOW_BUILD_VERSION) && meta->meta_build_version) {
+ show_file(meta->meta_build_version, "Build version:\n",
+ TRUE);
+ }
+ if (Flags & SHOW_BUILD_INFO) {
+ if (meta->meta_build_info) {
+ show_file(meta->meta_build_info, "Build information:\n",
+ TRUE);
+ }
+ if (meta->meta_installed_info) {
+ show_file(meta->meta_installed_info, "Installed information:\n",
+ TRUE);
+ }
+ }
+ if ((Flags & SHOW_PKG_SIZE) && meta->meta_size_pkg) {
+ show_file(meta->meta_size_pkg, "Size of this package in bytes: ",
+ TRUE);
+ }
+ if ((Flags & SHOW_ALL_SIZE) && meta->meta_size_all) {
+ show_file(meta->meta_size_all, "Size in bytes including required pkgs: ",
+ TRUE);
+ }
+ if (!Quiet && !(Flags & SHOW_SUMMARY)) {
+ if (meta->meta_preserve) {
+ printf("*** PACKAGE MAY NOT BE DELETED ***\n\n");
+ }
+ puts(InfoPrefix);
+ }
+ free_plist(&plist);
+ }
+ free_pkg_meta(meta);
+ return code;
+}
+
+struct print_matching_arg {
+ const char *pattern;
+ int got_match;
+};
+
+static int
+print_matching_pkg(const char *pkgname, void *cookie)
+{
+ struct print_matching_arg *arg= cookie;
+
+ if (pkg_match(arg->pattern, pkgname)) {
+ if (!Quiet)
+ puts(pkgname);
+ arg->got_match = 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Returns 0 if at least one package matching pkgname.
+ * Returns 1 otherwise.
+ *
+ * If -q was not specified, print all matching packages to stdout.
+ */
+int
+CheckForPkg(const char *pkgname)
+{
+ struct print_matching_arg arg;
+
+ arg.pattern = pkgname;
+ arg.got_match = 0;
+
+ if (iterate_pkg_db(print_matching_pkg, &arg) == -1) {
+ warnx("cannot iterate pkgdb");
+ return 1;
+ }
+
+ if (arg.got_match == 0 && !ispkgpattern(pkgname)) {
+ char *pattern;
+
+ pattern = xasprintf("%s-[0-9]*", pkgname);
+
+ arg.pattern = pattern;
+ arg.got_match = 0;
+
+ if (iterate_pkg_db(print_matching_pkg, &arg) == -1) {
+ free(pattern);
+ warnx("cannot iterate pkgdb");
+ return 1;
+ }
+ free(pattern);
+ }
+
+ if (arg.got_match)
+ return 0;
+ else
+ return 1;
+}
+
+/*
+ * Returns 0 if at least one package matching pkgname.
+ * Returns 1 otherwise.
+ *
+ * If -q was not specified, print best match to stdout.
+ */
+int
+CheckForBestPkg(const char *pkgname)
+{
+ char *pattern, *best_match;
+
+ best_match = find_best_matching_installed_pkg(pkgname);
+ if (best_match == NULL) {
+ if (ispkgpattern(pkgname))
+ return 1;
+
+ pattern = xasprintf("%s-[0-9]*", pkgname);
+ best_match = find_best_matching_installed_pkg(pattern);
+ free(pattern);
+ }
+
+ if (best_match == NULL)
+ return 1;
+ if (!Quiet)
+ puts(best_match);
+ free(best_match);
+ return 0;
+}
+
+static int
+perform_single_pkg(const char *pkg, void *cookie)
+{
+ int *err_cnt = cookie;
+
+ if (Which == WHICH_ALL || !is_automatic_installed(pkg))
+ *err_cnt += pkg_do(pkg);
+
+ return 0;
+}
+
+int
+pkg_perform(lpkg_head_t *pkghead)
+{
+ int err_cnt = 0;
+
+ TAILQ_INIT(&files);
+
+ desired_meta_data = 0;
+ if ((Flags & (SHOW_INDEX | SHOW_BI_VAR)) == 0)
+ desired_meta_data |= LOAD_PRESERVE;
+ if ((Flags & (SHOW_INDEX | SHOW_BI_VAR)) == 0)
+ desired_meta_data |= LOAD_CONTENTS;
+ if (Flags & (SHOW_COMMENT | SHOW_INDEX | SHOW_SUMMARY))
+ desired_meta_data |= LOAD_COMMENT;
+ if (Flags & (SHOW_BI_VAR | SHOW_BUILD_INFO | SHOW_SUMMARY))
+ desired_meta_data |= LOAD_BUILD_INFO | LOAD_INSTALLED_INFO;
+ if (Flags & (SHOW_SUMMARY | SHOW_PKG_SIZE))
+ desired_meta_data |= LOAD_SIZE_PKG;
+ if (Flags & SHOW_ALL_SIZE)
+ desired_meta_data |= LOAD_SIZE_ALL;
+ if (Flags & (SHOW_SUMMARY | SHOW_DESC))
+ desired_meta_data |= LOAD_DESC;
+ if (Flags & (SHOW_REQBY | SHOW_FULL_REQBY))
+ desired_meta_data |= LOAD_REQUIRED_BY;
+ if (Flags & SHOW_DISPLAY)
+ desired_meta_data |= LOAD_DISPLAY;
+ if (Flags & SHOW_INSTALL)
+ desired_meta_data |= LOAD_INSTALL;
+ if (Flags & SHOW_DEINSTALL)
+ desired_meta_data |= LOAD_DEINSTALL;
+ if (Flags & SHOW_MTREE)
+ desired_meta_data |= LOAD_MTREE;
+ if (Flags & SHOW_BUILD_VERSION)
+ desired_meta_data |= LOAD_BUILD_VERSION;
+
+ if (Which != WHICH_LIST) {
+ if (File2Pkg) {
+ /* Show all files with the package they belong to */
+ if (pkgdb_dump() == -1)
+ err_cnt = 1;
+ } else {
+ if (iterate_pkg_db(perform_single_pkg, &err_cnt) == -1)
+ err_cnt = 1;
+ }
+ } else {
+ /* Show info on individual pkg(s) */
+ lpkg_t *lpp;
+
+ while ((lpp = TAILQ_FIRST(pkghead)) != NULL) {
+ TAILQ_REMOVE(pkghead, lpp, lp_link);
+ err_cnt += pkg_do(lpp->lp_name);
+ free_lpkg(lpp);
+ }
+ }
+ return err_cnt;
+}
--- /dev/null
+.\" $NetBSD: pkg_info.1,v 1.1.1.6 2013/04/20 15:26:53 wiz Exp $
+.\"
+.\" FreeBSD install - a package for the installation and maintenance
+.\" 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
+.\"
+.\"
+.\" @(#)pkg_info.1
+.\"
+.Dd December 14, 2012
+.Dt PKG_INFO 1
+.Os
+.Sh NAME
+.Nm pkg_info
+.Nd a utility for displaying information on software packages
+.Sh SYNOPSIS
+.Nm
+.Op Fl BbcDdFfhIikLmNnpqRrSsVvX
+.Op Fl E Ar pkg-name
+.Op Fl e Ar pkg-name
+.Op Fl K Ar pkg_dbdir
+.Op Fl l Ar prefix
+.Ar pkg-name ...
+.Nm
+.Op Fl a | Fl u
+.Op flags
+.Nm
+.Op Fl Q Ar variable
+.Ar pkg-name ...
+.Sh DESCRIPTION
+The
+.Nm
+command is used to dump out information for packages, which may be either
+packed up in files or already installed on the system with the
+.Xr pkg_create 1
+command.
+.Pp
+The
+.Ar pkg-name
+may be the name of an installed package (with our without version), a
+pattern matching several installed packages (see the
+.Sx PACKAGE WILDCARDS
+section for a description of possible patterns),
+the pathname to a
+binary package, a filename belonging to an installed
+package (with
+.Fl F ) ,
+or a URL to an FTP-available package.
+.Pp
+The following command line options are available:
+.Bl -tag -width indent
+.It Fl a
+Show information for all currently installed packages.
+See also
+.Fl u .
+When neither
+.Fl a
+nor
+.Fl u
+is given, the former is assumed.
+.It Fl B
+Show some of the important definitions used when building
+the binary package (the
+.Dq Build information )
+for each package.
+Additionally, any installation information variables
+(lowercase) can be queried, too.
+In particular,
+.Ar automatic
+tells if a package was installed automatically
+as a dependency of another package.
+.It Fl b
+Show the
+.Nx
+RCS Id strings from the files used in the construction
+of the binary package (the
+.Dq Build version )
+for each package.
+These files are the package Makefile, any patch files, any checksum
+files, and the packing list file.
+.It Fl c
+Show the one-line comment field for each package.
+.It Fl D
+Show the install-message file (if any) for each package.
+.It Fl d
+Show the long-description field for each package.
+.It Fl E Ar pkg-name
+This option
+allows you to test for the existence of a given package.
+If a package identified by
+.Ar pkg-name
+is currently installed, return code is 0, otherwise 1.
+The name of the best matching package found installed is printed to
+stdout unless turned off using the
+.Fl q
+option.
+.Ar pkg-name
+can contain wildcards (see the
+.Sx PACKAGE WILDCARDS
+section below).
+.It Fl e Ar pkg-name
+This option
+allows you to test for the existence of a given package.
+If a package identified by
+.Ar pkg-name
+is currently installed, return code is 0, otherwise 1.
+The names of any package(s) found installed are printed to
+stdout unless turned off using the
+.Fl q
+option.
+.Ar pkg-name
+can contain wildcards (see the
+.Sx PACKAGE WILDCARDS
+section below).
+.It Fl F
+Interpret any
+.Ar pkg-name
+given as filename, and query information on the package that
+file belongs to.
+This can be used to query information on a per-file basis.
+See the
+.Sx TECHNICAL DETAILS
+section below for more information.
+.It Fl f
+Show the packing list instructions for each package.
+.It Fl h
+Print usage message and exit.
+.It Fl I
+Show the index entry for each package.
+This option is assumed when no arguments or relevant flags are specified.
+.It Fl i
+Show the install script (if any) for each package.
+.It Fl K Ar pkg_dbdir
+Override the value of the
+.Dv PKG_DBDIR
+configuration option with the value
+.Ar pkg_dbdir .
+.It Fl k
+Show the de-install script (if any) for each package.
+.It Fl L
+Show the files within each package.
+This is different from just viewing the packing list, since full pathnames
+for everything are generated.
+Files that were created dynamically during installation of the package
+are not listed.
+.It Fl l Ar prefix
+Prefix each information category header (see
+.Fl q )
+shown with
+.Ar prefix .
+This is primarily of use to front-end programs that want to request a
+lot of different information fields at once for a package, but don't
+necessary want the output intermingled in such a way that they can't
+organize it.
+This lets you add a special token to the start of each field.
+.It Fl m
+Show the mtree file (if any) for each package.
+.It Fl N
+Show which packages each package was built with (exact dependencies), if any.
+.It Fl n
+Show which packages each package needs (depends upon), if any.
+.It Fl p
+Show the installation prefix for each package.
+.It Fl Q Ar variable
+Show the definition of
+.Ar variable
+from the build information for each package.
+An empty string is returned if no such variable definition is found for
+the package(s).
+.It Fl q
+Be
+.Dq quiet
+in emitting report headers and such, just dump the
+raw info (basically, assume a non-human reading).
+.It Fl R
+For each package, show the packages that require it.
+.It Fl r
+For each package, show the packages that require it.
+Continue recursively to show all dependents.
+.It Fl S
+Show the size of this package and all the packages it requires,
+in bytes.
+.It Fl s
+Show the size of this package in bytes.
+The size is calculated by adding up the size of each file of the package.
+.It Fl u
+Show information for all user-installed packages:
+automatically installed packages (as dependencies
+of other packages) are not displayed.
+See also
+.Fl a .
+.It Fl V
+Print version number and exit.
+.It Fl v
+Turn on verbose output.
+.It Fl X
+Print summary information for each package.
+The summary format is
+described in
+.Xr pkg_summary 5 .
+Its primary use is to contain all information about the contents of a
+(remote) binary package repository needed by package managing software.
+.El
+.Sh TECHNICAL DETAILS
+Package info is either extracted from package files named on the
+command line, or from already installed package information
+in
+.Pa \*[Lt]PKG_DBDIR\*[Gt]/\*[Lt]pkg-name\*[Gt] .
+.Pp
+When the
+.Fl F
+option is used,
+a filename can be given instead of a package name to query
+information on the (installed) package that file belongs to.
+The filename is resolved to a package name using the package database.
+The filename must be absolute, as in the output of
+.Dl pkg_info -aF .
+For example,
+.Dl pkg_info -eF /path/to/file
+can be used to display the package the given file belongs to, and
+.Dl pkg_info -LF /path/to/file
+can be used to display all files belonging to the package the given
+file belongs to.
+.Sh PACKAGE WILDCARDS
+In the places where a package name/version is expected, e.g., for the
+.Fl e
+option, several forms can be used.
+Either use a package name with or without version, or specify a
+package wildcard that gets matched against all installed packages.
+.Pp
+Package wildcards use
+.Xr fnmatch 3 .
+In addition,
+.Xr csh 1
+style {,} alternates have been implemented.
+Package version numbers can also be matched in a relational manner
+using the
+.Dq \*[Ge] ,
+.Dq \*[Le] ,
+.Dq \*[Gt] ,
+and
+.Dq \*[Lt]
+operators.
+For example,
+.Dl pkg_info -e 'name\*[Ge]1.3'
+will match versions 1.3 and later of the
+.Dq name
+package.
+(Make sure to use shell quoting.)
+Additionally, ranges can be defined, by giving both a lower bound
+.Po with
+.Dq \*[Gt]
+or
+.Dq \*[Ge]
+.Pc
+as well as an upper bound
+.Po with
+.Dq \*[Lt]
+or
+. Dq \*[Le]
+.Pc .
+The lower bound has to come first.
+For example,
+.Dl pkg_info -e 'name\*[Ge]1.3\*[Lt]2.0'
+will match versions 1.3 (inclusive) to 2.0 (exclusive) of package
+.Dq name .
+.Pp
+The collating sequence of the various package version numbers is
+unusual, but strives to be consistent.
+The magic string
+.Dq alpha
+equates to
+.Dq alpha version ,
+and sorts before a beta version.
+The magic string
+.Dq beta
+equates to
+.Dq beta version ,
+and sorts before a release candidate.
+The magic string
+.Dq rc
+equates to
+.Dq release candidate ,
+and sorts before a release.
+The magic string
+.Dq pre ,
+short for
+.Dq pre-release ,
+is a synonym for
+.Dq rc .
+For example,
+.Dq name-1.3alpha2
+will sort before
+.Dq name-1.3beta1 ,
+and they both sort before
+.Dq name-1.3rc1 .
+Similarly,
+.Dq name-1.3rc3
+will sort before
+.Dq name-1.3 ,
+and after
+.Dq name-1.2.9 .
+The magic string
+.Dq pl
+equates to
+.Dq patch level ,
+and has the same value as a dot
+.Pq Sq \&.
+in the dewey-decimal ordering schemes,
+as does the underscore
+.Pq Sq _ .
+Additionally, alphabetic characters sort in the same place as
+their numeric counterparts, so that
+.Dq name-1.2e
+has the same sorting value as
+.Dq name-1.2.5 .
+.Sh ENVIRONMENT
+See
+.Xr pkg_install.conf 5
+for options, that can also be specified using the environment.
+.Sh SEE ALSO
+.Xr pkg_add 1 ,
+.Xr pkg_admin 1 ,
+.Xr pkg_create 1 ,
+.Xr pkg_delete 1 ,
+.Xr pkg_install.conf 5
+.Xr pkgsrc 7
+.Sh AUTHORS
+.Bl -tag -width indent -compact
+.It "Jordan Hubbard"
+most of the work
+.It "John Kohl"
+refined it for
+.Nx
+.It "Hubert Feyrer"
+.Nx
+wildcard dependency processing, pkgdb, depends displaying,
+pkg size display, and more.
+.El
--- /dev/null
+/* $NetBSD: show.c,v 1.3 2012/02/21 18:36:16 wiz Exp $ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+__RCSID("$NetBSD: show.c,v 1.3 2012/02/21 18:36:16 wiz Exp $");
+
+/*
+ * 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
+ * 23 Aug 1993
+ *
+ * Various display routines for the info module.
+ *
+ */
+/*-
+ * 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>.
+ *
+ * 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.
+ *
+ * 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_ERR_H
+#include <err.h>
+#endif
+
+#include "defs.h"
+#include "lib.h"
+#include "info.h"
+
+/* Structure to define entries for the "show table" */
+typedef struct show_t {
+ pl_ent_t sh_type; /* type of entry */
+ const char *sh_quiet; /* message when quiet */
+ const char *sh_verbose; /* message when verbose */
+} show_t;
+
+/*
+ * The entries in this table must be ordered the same as
+ * pl_ent_t constants
+ */
+static const show_t showv[] = {
+ {PLIST_FILE, "", "\tFile: "},
+ {PLIST_CWD, "@cwd ", "\tCWD to: "},
+ {PLIST_CMD, "@exec ", "\tEXEC ''"},
+ {PLIST_CHMOD, "@chmod ", "\tCHMOD to "},
+ {PLIST_CHOWN, "@chown ", "\tCHOWN to "},
+ {PLIST_CHGRP, "@chgrp ", "\tCHGRP to "},
+ {PLIST_COMMENT, "@comment ", "\tComment: "},
+ {PLIST_IGNORE, "@ignore", "Ignore next file:"},
+ {PLIST_NAME, "@name ", "\tPackage name: "},
+ {PLIST_UNEXEC, "@unexec ", "\tUNEXEC ''"},
+ {PLIST_SRC, "@src: ", "\tSRC to: "},
+ {PLIST_DISPLAY, "@display ", "\tInstall message file: "},
+ {PLIST_PKGDEP, "@pkgdep ", "\tPackage depends on: "},
+ {PLIST_DIR_RM, "@dirrm ", "\tObsolete deinstall directory removal hint: "},
+ {PLIST_OPTION, "@option ", "\tPackage has option: "},
+ {PLIST_PKGCFL, "@pkgcfl ", "\tPackage conflicts with: "},
+ {PLIST_BLDDEP, "@blddep ", "\tPackage depends exactly on: "},
+ {PLIST_PKGDIR, "@pkgdir ", "\tManaged directory: "},
+ {-1, NULL, NULL}
+};
+
+static int print_string_as_var(const char *, const char *);
+
+void
+show_file(const char *buf, const char *title, Boolean separator)
+{
+ size_t len;
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+
+ len = strlen(buf);
+ if (len == 0 || buf[len - 1] != '\n')
+ puts(buf);
+ else
+ fputs(buf, stdout);
+
+ if (!Quiet || separator)
+ printf("\n");
+}
+
+void
+show_var(const char *buf, const char *variable)
+{
+ char *value;
+
+ if (buf == NULL)
+ return;
+
+ if ((value = var_get_memory(buf, variable)) != NULL) {
+ (void) printf("%s\n", value);
+ free(value);
+ }
+}
+
+void
+show_index(const char *buf, const char *title)
+{
+ size_t len;
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+
+ len = strlen(buf);
+ if (len == 0 || buf[len - 1] != '\n')
+ puts(buf);
+ else
+ fputs(buf, stdout);
+}
+
+/*
+ * Show a packing list item type. If type is PLIST_SHOW_ALL, show all
+ */
+void
+show_plist(const char *title, package_t *plist, pl_ent_t type)
+{
+ plist_t *p;
+ Boolean ign;
+
+ if (!Quiet) {
+ printf("%s%s", InfoPrefix, title);
+ }
+ for (ign = FALSE, p = plist->head; p; p = p->next) {
+ if (p->type == type || type == PLIST_SHOW_ALL) {
+ switch (p->type) {
+ case PLIST_FILE:
+ printf("%s%s",
+ Quiet ? showv[p->type].sh_quiet :
+ showv[p->type].sh_verbose, p->name);
+ if (ign) {
+ if (!Quiet) {
+ printf(" (ignored)");
+ }
+ ign = FALSE;
+ }
+ break;
+ case PLIST_CHMOD:
+ case PLIST_CHOWN:
+ case PLIST_CHGRP:
+ printf("%s%s",
+ Quiet ? showv[p->type].sh_quiet :
+ showv[p->type].sh_verbose,
+ p->name ? p->name : "(clear default)");
+ break;
+ case PLIST_IGNORE:
+ printf("%s", Quiet ? showv[p->type].sh_quiet :
+ showv[p->type].sh_verbose);
+ ign = TRUE;
+ break;
+ case PLIST_CWD:
+ case PLIST_CMD:
+ case PLIST_SRC:
+ case PLIST_UNEXEC:
+ case PLIST_COMMENT:
+ case PLIST_NAME:
+ case PLIST_DISPLAY:
+ case PLIST_PKGDEP:
+ case PLIST_DIR_RM:
+ case PLIST_OPTION:
+ case PLIST_PKGCFL:
+ case PLIST_BLDDEP:
+ case PLIST_PKGDIR:
+ printf("%s%s",
+ Quiet ? showv[p->type].sh_quiet :
+ showv[p->type].sh_verbose,
+ p->name ? p->name : "(null)");
+ break;
+ default:
+ warnx("unknown command type %d (%s)", p->type, p->name);
+ }
+ (void) fputc('\n', stdout);
+ }
+ }
+}
+
+/*
+ * Show all files in the packing list (except ignored ones)
+ */
+void
+show_files(const char *title, package_t *plist)
+{
+ plist_t *p;
+ Boolean ign;
+ const char *dir = ".";
+
+ if (!Quiet) {
+ printf("%s%s", InfoPrefix, title);
+ }
+ for (ign = FALSE, p = plist->head; p; p = p->next) {
+ switch (p->type) {
+ case PLIST_FILE:
+ if (!ign) {
+ printf("%s%s%s\n", dir,
+ (strcmp(dir, "/") == 0) ? "" : "/", p->name);
+ }
+ ign = FALSE;
+ break;
+ case PLIST_CWD:
+ dir = p->name;
+ break;
+ case PLIST_IGNORE:
+ ign = TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/*
+ * Show dependencies (packages this pkg requires)
+ */
+void
+show_depends(const char *title, package_t *plist)
+{
+ plist_t *p;
+ int nodepends;
+
+ nodepends = 1;
+ for (p = plist->head; p && nodepends; p = p->next) {
+ switch (p->type) {
+ case PLIST_PKGDEP:
+ nodepends = 0;
+ break;
+ default:
+ break;
+ }
+ }
+ if (nodepends)
+ return;
+
+ if (!Quiet) {
+ printf("%s%s", InfoPrefix, title);
+ }
+ for (p = plist->head; p; p = p->next) {
+ switch (p->type) {
+ case PLIST_PKGDEP:
+ printf("%s\n", p->name);
+ break;
+ default:
+ break;
+ }
+ }
+
+ printf("\n");
+}
+
+/*
+ * Show exact dependencies (packages this pkg was built with)
+ */
+void
+show_bld_depends(const char *title, package_t *plist)
+{
+ plist_t *p;
+ int nodepends;
+
+ nodepends = 1;
+ for (p = plist->head; p && nodepends; p = p->next) {
+ switch (p->type) {
+ case PLIST_BLDDEP:
+ nodepends = 0;
+ break;
+ default:
+ break;
+ }
+ }
+ if (nodepends)
+ return;
+
+ if (!Quiet) {
+ printf("%s%s", InfoPrefix, title);
+ }
+ for (p = plist->head; p; p = p->next) {
+ switch (p->type) {
+ case PLIST_BLDDEP:
+ printf("%s\n", p->name);
+ break;
+ default:
+ break;
+ }
+ }
+
+ printf("\n");
+}
+
+
+/*
+ * Show entry for pkg_summary.txt file.
+ */
+void
+show_summary(struct pkg_meta *meta, package_t *plist, const char *binpkgfile)
+{
+ static const char *bi_vars[] = {
+ "PKGPATH",
+ "CATEGORIES",
+ "PROVIDES",
+ "REQUIRES",
+ "PKG_OPTIONS",
+ "OPSYS",
+ "OS_VERSION",
+ "MACHINE_ARCH",
+ "LICENSE",
+ "HOMEPAGE",
+ "PKGTOOLS_VERSION",
+ "BUILD_DATE",
+ "PREV_PKGPATH",
+ "SUPERSEDES",
+ NULL
+ };
+
+ plist_t *p;
+ struct stat st;
+
+ for (p = plist->head; p; p = p->next) {
+ switch (p->type) {
+ case PLIST_NAME:
+ printf("PKGNAME=%s\n", p->name);
+ break;
+ case PLIST_PKGDEP:
+ printf("DEPENDS=%s\n", p->name);
+ break;
+ case PLIST_PKGCFL:
+ printf("CONFLICTS=%s\n", p->name);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ print_string_as_var("COMMENT", meta->meta_comment);
+ if (meta->meta_size_pkg)
+ print_string_as_var("SIZE_PKG", meta->meta_size_pkg);
+
+ if (meta->meta_build_info)
+ var_copy_list(meta->meta_build_info, bi_vars);
+ else
+ warnx("Build information missing");
+
+ if (binpkgfile != NULL && stat(binpkgfile, &st) == 0) {
+ const char *base;
+
+ base = strrchr(binpkgfile, '/');
+ if (base == NULL)
+ base = binpkgfile;
+ else
+ base++;
+ printf("FILE_NAME=%s\n", base);
+ printf("FILE_SIZE=%" MY_PRIu64 "\n", (uint64_t)st.st_size);
+ /* XXX: DIGETS */
+ }
+
+ print_string_as_var("DESCRIPTION", meta->meta_desc);
+ putc('\n', stdout);
+}
+
+/*
+ * Print the contents of file fname as value of variable var to stdout.
+ */
+static int
+print_string_as_var(const char *var, const char *str)
+{
+ const char *eol;
+
+ while ((eol = strchr(str, '\n')) != NULL) {
+ printf("%s=%.*s\n", var, (int)(eol - str), str);
+ str = eol + 1;
+ }
+ if (*str)
+ printf("%s=%s\n", var, str);
+
+ return 0;
+}
+
+void
+show_list(lpkg_head_t *pkghead, const char *title)
+{
+ lpkg_t *lpp;
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+
+ while ((lpp = TAILQ_FIRST(pkghead)) != NULL) {
+ TAILQ_REMOVE(pkghead, lpp, lp_link);
+ puts(lpp->lp_name);
+ free_lpkg(lpp);
+ }
+
+ if (!Quiet)
+ printf("\n");
+}
--- /dev/null
+/* $NetBSD: automatic.c,v 1.1.1.2 2009/02/02 20:44:05 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2005 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Dieter Baron 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. 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_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+__RCSID("$NetBSD: automatic.c,v 1.1.1.2 2009/02/02 20:44:05 joerg Exp $");
+
+#if HAVE_ASSERT_H
+#include <assert.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_STRING_H
+#include <string.h>
+#endif
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include "lib.h"
+
+Boolean
+is_automatic_installed(const char *pkg)
+{
+ char *filename, *value;
+ Boolean ret;
+
+ assert(pkg[0] != '/');
+
+ filename = pkgdb_pkg_file(pkg, INSTALLED_INFO_FNAME);
+
+ value = var_get(filename, AUTOMATIC_VARNAME);
+
+ if (value && strcasecmp(value, "yes") == 0)
+ ret = TRUE;
+ else
+ ret = FALSE;
+
+ free(value);
+ free(filename);
+
+ return ret;
+}
+
+int
+mark_as_automatic_installed(const char *pkg, int value)
+{
+ char *filename;
+ int retval;
+
+ assert(pkg[0] != '/');
+
+ filename = pkgdb_pkg_file(pkg, INSTALLED_INFO_FNAME);
+
+ retval = var_set(filename, AUTOMATIC_VARNAME, value ? "yes" : NULL);
+
+ free(filename);
+
+ return retval;
+}
--- /dev/null
+/* lib/config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define to 1 if you have the <assert.h> header file. */
+#undef HAVE_ASSERT_H
+
+/* Define to 1 if you have the <ctype.h> header file. */
+#undef HAVE_CTYPE_H
+
+/* Define to 1 if you have the <dirent.h> header file. */
+#undef HAVE_DIRENT_H
+
+/* Define to 1 if you have the <errno.h> header file. */
+#undef HAVE_ERRNO_H
+
+/* Define to 1 if you have the <err.h> header file. */
+#undef HAVE_ERR_H
+
+/* Define to 1 if you have the <fnctl.h> header file. */
+#undef HAVE_FNCTL_H
+
+/* Define to 1 if you have the <fnmatch.h> header file. */
+#undef HAVE_FNMATCH_H
+
+/* Define to 1 if you have the <glob.h> header file. */
+#undef HAVE_GLOB_H
+
+/* Define to 1 if you have the <grp.h> header file. */
+#undef HAVE_GRP_H
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the `db' library (-ldb). */
+#undef HAVE_LIBDB
+
+/* Define to 1 if you have the <limits.h> header file. */
+#undef HAVE_LIMITS_H
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the <pwd.h> header file. */
+#undef HAVE_PWD_H
+
+/* Define to 1 if you have the <signal.h> header file. */
+#undef HAVE_SIGNAL_H
+
+/* Define to 1 if you have the <stdarg.h> header file. */
+#undef HAVE_STDARG_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#undef HAVE_STDIO_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <sys/cdefs.h> header file. */
+#undef HAVE_SYS_CDEFS_H
+
+/* Define to 1 if you have the <sys/file.h> header file. */
+#undef HAVE_SYS_FILE_H
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#undef HAVE_SYS_IOCTL_H
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#undef HAVE_SYS_PARAM_H
+
+/* Define to 1 if you have the <sys/queue.h> header file. */
+#undef HAVE_SYS_QUEUE_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <sys/utsname.h> header file. */
+#undef HAVE_SYS_UTSNAME_H
+
+/* Define to 1 if you have the <sys/wait.h> header file. */
+#undef HAVE_SYS_WAIT_H
+
+/* Define to 1 if you have the <time.h> header file. */
+#undef HAVE_TIME_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 if you have the `vfork' function. */
+#undef HAVE_VFORK
+
+/* Define to 1 if you have the <vis.h> header file. */
+#undef HAVE_VIS_H
+
+/* Define to 1 if the `z' modifider for printf is missing. */
+#undef MISSING_SIZE_T_SUPPORT
+
+/* Defined when PRIu64 is missing or broken */
+#undef NEED_PRI_MACRO
+
+/* Defined when to retain only the numeric OS version */
+#undef NUMERIC_VERSION_ONLY
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* The size of `int', as computed by sizeof. */
+#undef SIZEOF_INT
+
+/* The size of `long', as computed by sizeof. */
+#undef SIZEOF_LONG
+
+/* The size of `long long', as computed by sizeof. */
+#undef SIZEOF_LONG_LONG
+
+/* The size of `size_t', as computed by sizeof. */
+#undef SIZEOF_SIZE_T
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+#undef _UINT32_T
+
+/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+#undef _UINT64_T
+
+/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+#undef _UINT8_T
+
+/* Define to the type of an unsigned integer type of width exactly 16 bits if
+ such a type exists and the standard includes do not define it. */
+#undef uint16_t
+
+/* Define to the type of an unsigned integer type of width exactly 32 bits if
+ such a type exists and the standard includes do not define it. */
+#undef uint32_t
+
+/* Define to the type of an unsigned integer type of width exactly 64 bits if
+ such a type exists and the standard includes do not define it. */
+#undef uint64_t
+
+/* Define to the type of an unsigned integer type of width exactly 8 bits if
+ such a type exists and the standard includes do not define it. */
+#undef uint8_t
+
+#if !HAVE_VFORK
+# define vfork fork
+#endif
+
+#ifndef MISSING_SIZE_T_SUPPORT
+# define PRIzu "zu"
+#elif SIZEOF_SIZE_T == SIZEOF_INT
+# define PRIzu "u"
+#elif SIZEOF_SIZE_T == SIZEOF_LONG
+# define PRIzu "lu"
+#elif SIZEOF_SIZE_T == SIZEOF_LONG_LONG
+# define PRIzu "llu"
+#else
+# errror "Unknown size_t size"
+#endif
+
--- /dev/null
+/* $NetBSD: conflicts.c,v 1.1.1.4 2010/01/30 21:33:43 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2007 Roland Illig <rillig@NetBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 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.
+ */
+
+/*
+ * XXX: Reading the +CONTENTS files of all installed packages is
+ * rather slow. Since this check is necessary to avoid conflicting
+ * packages, it should not be removed.
+ *
+ * TODO: Put all the information that is currently in the +CONTENTS
+ * files into one large file or another database.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <nbcompat.h>
+
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+__RCSID("$NetBSD: conflicts.c,v 1.1.1.4 2010/01/30 21:33:43 joerg Exp $");
+
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+
+#include "dewey.h"
+#include "lib.h"
+
+/**
+ * Data structure to keep the intermediate result of the conflict
+ * search. ''pkgname'' is the package in question. The first
+ * installed package that conflicts is filled into
+ * ''conflicting_pkgname''. The pattern that leads to the conflict is
+ * also filled in to help the user in deciding what to do with the
+ * conflict.
+ */
+struct package_conflict {
+ const char *pkgname;
+ const char *skip_pkgname;
+ char **conflicting_pkgname;
+ char **conflicting_pattern;
+};
+
+static FILE *
+fopen_contents(const char *pkgname, const char *mode)
+{
+ char *fname;
+ FILE *f;
+
+ fname = pkgdb_pkg_file(pkgname, CONTENTS_FNAME);
+ f = fopen(fname, mode);
+ if (f == NULL) {
+ err(EXIT_FAILURE, "%s", fname);
+ /* NOTREACHED */
+ }
+ free(fname);
+ return f;
+}
+
+
+static int
+check_package_conflict(const char *pkgname, void *v)
+{
+ struct package_conflict *conflict = v;
+ package_t pkg;
+ plist_t *p;
+ FILE *f;
+ int rv;
+
+ if (conflict->skip_pkgname != NULL &&
+ strcmp(conflict->skip_pkgname, pkgname) == 0)
+ return 0;
+
+ rv = 0;
+
+ f = fopen_contents(pkgname, "r");
+ read_plist(&pkg, f);
+ (void)fclose(f);
+
+ for (p = pkg.head; p; p = p->next) {
+ if (p->type != PLIST_PKGCFL)
+ continue;
+
+ if (pkg_match(p->name, conflict->pkgname) == 1) {
+ *(conflict->conflicting_pkgname) = xstrdup(pkgname);
+ *(conflict->conflicting_pattern) = xstrdup(p->name);
+ rv = 1 /* nonzero, stop iterating */;
+ break;
+ }
+ }
+
+ free_plist(&pkg);
+ return rv;
+}
+
+/**
+ * Checks if some installed package has a pkgcfl entry that matches
+ * PkgName. If such an entry is found, the package name is returned in
+ * inst_pkgname, the matching pattern in inst_pattern, and the function
+ * returns a non-zero value. Otherwise, zero is returned and the result
+ * variables are set to NULL.
+ */
+int
+some_installed_package_conflicts_with(const char *pkgname,
+ const char *skip_pkgname, char **inst_pkgname, char **inst_pattern)
+{
+ struct package_conflict cfl;
+ int rv;
+
+ cfl.pkgname = pkgname;
+ cfl.skip_pkgname = skip_pkgname;
+ *inst_pkgname = NULL;
+ *inst_pattern = NULL;
+ cfl.conflicting_pkgname = inst_pkgname;
+ cfl.conflicting_pattern = inst_pattern;
+ rv = iterate_pkg_db(check_package_conflict, &cfl);
+ if (rv == -1) {
+ errx(EXIT_FAILURE, "Couldn't read list of installed packages.");
+ /* NOTREACHED */
+ }
+ return *inst_pkgname != NULL;
+}
+
+#if 0
+int main(int argc, char **argv)
+{
+ char *pkg, *patt;
+
+ if (some_installed_package_conflicts_with(argv[1], &pkg, &patt))
+ printf("yes: package %s conflicts with %s, pattern %s\n", pkg, argv[1], patt);
+ else
+ printf("no\n");
+ return 0;
+}
+#endif
--- /dev/null
+/* $NetBSD: defs.h,v 1.3 2012/02/21 18:36:17 wiz Exp $ */
+
+/*-
+ * Copyright (c) 1999,2000,2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * 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.
+ *
+ * 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.
+ */
+#ifndef DEFS_H_
+#define DEFS_H_
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef MAX
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+/*
+ * Some systems such as OpenBSD-3.6 do not provide PRIu64.
+ * Others such as AIX-4.3.2 have a broken PRIu64 which includes
+ * a leading "%".
+ */
+#ifdef NEED_PRI_MACRO
+# if SIZEOF_INT == 8
+# define MY_PRIu64 "u"
+# elif SIZEOF_LONG == 8
+# define MY_PRIu64 "lu"
+# elif SIZEOF_LONG_LONG == 8
+# define MY_PRIu64 "llu"
+# else
+# error "unable to find a suitable PRIu64"
+# endif
+#else
+# define MY_PRIu64 PRIu64
+#endif
+
+#endif /* !DEFS_H_ */
--- /dev/null
+/* $NetBSD: dewey.c,v 1.3 2009/03/08 14:53:16 joerg Exp $ */
+
+/*
+ * Copyright © 2002 Alistair G. Crooks. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+
+#if HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "defs.h"
+#include "dewey.h"
+
+#define PKG_PATTERN_MAX 1024
+
+/* do not modify these values, or things will NOT work */
+enum {
+ Alpha = -3,
+ Beta = -2,
+ RC = -1,
+ Dot = 0,
+ Patch = 1
+};
+
+/* this struct defines a version number */
+typedef struct arr_t {
+ unsigned c; /* # of version numbers */
+ unsigned size; /* size of array */
+ int *v; /* array of decimal numbers */
+ int netbsd; /* any "nb" suffix */
+} arr_t;
+
+/* this struct describes a test */
+typedef struct test_t {
+ const char *s; /* string representation */
+ unsigned len; /* length of string */
+ int t; /* enumerated type of test */
+} test_t;
+
+
+/* the tests that are recognised. */
+ const test_t tests[] = {
+ { "<=", 2, DEWEY_LE },
+ { "<", 1, DEWEY_LT },
+ { ">=", 2, DEWEY_GE },
+ { ">", 1, DEWEY_GT },
+ { "==", 2, DEWEY_EQ },
+ { "!=", 2, DEWEY_NE },
+ { NULL, 0, 0 }
+};
+
+ const test_t modifiers[] = {
+ { "alpha", 5, Alpha },
+ { "beta", 4, Beta },
+ { "pre", 3, RC },
+ { "rc", 2, RC },
+ { "pl", 2, Dot },
+ { "_", 1, Dot },
+ { ".", 1, Dot },
+ { NULL, 0, 0 }
+};
+
+
+
+/* locate the test in the tests array */
+int
+dewey_mktest(int *op, const char *test)
+{
+ const test_t *tp;
+
+ for (tp = tests ; tp->s ; tp++) {
+ if (strncasecmp(test, tp->s, tp->len) == 0) {
+ *op = tp->t;
+ return tp->len;
+ }
+ }
+ return -1;
+}
+
+/*
+ * make a component of a version number.
+ * '.' encodes as Dot which is '0'
+ * '_' encodes as 'patch level', or 'Dot', which is 0.
+ * 'pl' encodes as 'patch level', or 'Dot', which is 0.
+ * 'alpha' encodes as 'alpha version', or Alpha, which is -3.
+ * 'beta' encodes as 'beta version', or Beta, which is -2.
+ * 'rc' encodes as 'release candidate', or RC, which is -1.
+ * 'nb' encodes as 'netbsd version', which is used after all other tests
+ */
+static int
+mkcomponent(arr_t *ap, const char *num)
+{
+ static const char alphas[] = "abcdefghijklmnopqrstuvwxyz";
+ const test_t *modp;
+ int n;
+ const char *cp;
+
+ if (ap->c == ap->size) {
+ if (ap->size == 0) {
+ ap->size = 62;
+ if ((ap->v = malloc(ap->size * sizeof(int))) == NULL)
+ err(EXIT_FAILURE, "mkver malloc failed");
+ } else {
+ ap->size *= 2;
+ if ((ap->v = realloc(ap->v, ap->size * sizeof(int)))
+ == NULL)
+ err(EXIT_FAILURE, "mkver realloc failed");
+ }
+ }
+ if (isdigit((unsigned char)*num)) {
+ for (cp = num, n = 0 ; isdigit((unsigned char)*num) ; num++) {
+ n = (n * 10) + (*num - '0');
+ }
+ ap->v[ap->c++] = n;
+ return (int)(num - cp);
+ }
+ for (modp = modifiers ; modp->s ; modp++) {
+ if (strncasecmp(num, modp->s, modp->len) == 0) {
+ ap->v[ap->c++] = modp->t;
+ return modp->len;
+ }
+ }
+ if (strncasecmp(num, "nb", 2) == 0) {
+ for (cp = num, num += 2, n = 0 ; isdigit((unsigned char)*num) ; num++) {
+ n = (n * 10) + (*num - '0');
+ }
+ ap->netbsd = n;
+ return (int)(num - cp);
+ }
+ if (isalpha((unsigned char)*num)) {
+ ap->v[ap->c++] = Dot;
+ cp = strchr(alphas, tolower((unsigned char)*num));
+ if (ap->c == ap->size) {
+ ap->size *= 2;
+ if ((ap->v = realloc(ap->v, ap->size * sizeof(int))) == NULL)
+ err(EXIT_FAILURE, "mkver realloc failed");
+ }
+ ap->v[ap->c++] = (int)(cp - alphas) + 1;
+ return 1;
+ }
+ return 1;
+}
+
+/* make a version number string into an array of comparable ints */
+static int
+mkversion(arr_t *ap, const char *num)
+{
+ ap->c = 0;
+ ap->size = 0;
+ ap->v = NULL;
+ ap->netbsd = 0;
+
+ while (*num) {
+ num += mkcomponent(ap, num);
+ }
+ return 1;
+}
+
+static void
+freeversion(arr_t *ap)
+{
+ free(ap->v);
+ ap->v = NULL;
+ ap->c = 0;
+ ap->size = 0;
+}
+
+#define DIGIT(v, c, n) (((n) < (c)) ? v[n] : 0)
+
+/* compare the result against the test we were expecting */
+static int
+result(int cmp, int tst)
+{
+ switch(tst) {
+ case DEWEY_LT:
+ return cmp < 0;
+ case DEWEY_LE:
+ return cmp <= 0;
+ case DEWEY_GT:
+ return cmp > 0;
+ case DEWEY_GE:
+ return cmp >= 0;
+ case DEWEY_EQ:
+ return cmp == 0;
+ case DEWEY_NE:
+ return cmp != 0;
+ default:
+ return 0;
+ }
+}
+
+/* do the test on the 2 vectors */
+static int
+vtest(arr_t *lhs, int tst, arr_t *rhs)
+{
+ int cmp;
+ unsigned int c, i;
+
+ for (i = 0, c = MAX(lhs->c, rhs->c) ; i < c ; i++) {
+ if ((cmp = DIGIT(lhs->v, lhs->c, i) - DIGIT(rhs->v, rhs->c, i)) != 0) {
+ return result(cmp, tst);
+ }
+ }
+ return result(lhs->netbsd - rhs->netbsd, tst);
+}
+
+/*
+ * Compare two dewey decimal numbers
+ */
+int
+dewey_cmp(const char *lhs, int op, const char *rhs)
+{
+ arr_t right;
+ arr_t left;
+ int retval;
+
+ if (!mkversion(&left, lhs))
+ return 0;
+ if (!mkversion(&right, rhs)) {
+ freeversion(&left);
+ return 0;
+ }
+ retval = vtest(&left, op, &right);
+ freeversion(&left);
+ freeversion(&right);
+ return retval;
+}
+
+/*
+ * Perform dewey match on "pkg" against "pattern".
+ * Return 1 on match, 0 on non-match, -1 on error.
+ */
+int
+dewey_match(const char *pattern, const char *pkg)
+{
+ const char *version;
+ const char *sep, *sep2;
+ int op, op2;
+ int n;
+
+ /* compare names */
+ if ((version=strrchr(pkg, '-')) == NULL) {
+ return 0;
+ }
+ if ((sep = strpbrk(pattern, "<>")) == NULL)
+ return -1;
+ /* compare name lengths */
+ if ((sep-pattern != version-pkg) ||
+ strncmp(pkg, pattern, (size_t)(version-pkg)) != 0)
+ return 0;
+ version++;
+
+ /* extract comparison operator */
+ if ((n = dewey_mktest(&op, sep)) < 0) {
+ return 0;
+ }
+ /* skip operator */
+ sep += n;
+
+ /* if greater than, look for less than */
+ sep2 = NULL;
+ if (op == DEWEY_GT || op == DEWEY_GE) {
+ if ((sep2 = strchr(sep, '<')) != NULL) {
+ if ((n = dewey_mktest(&op2, sep2)) < 0) {
+ return 0;
+ }
+ /* compare upper limit */
+ if (!dewey_cmp(version, op2, sep2+n))
+ return 0;
+ }
+ }
+
+ /* compare only pattern / lower limit */
+ if (sep2) {
+ char ver[PKG_PATTERN_MAX];
+
+ strlcpy(ver, sep, MIN((ssize_t)sizeof(ver), sep2-sep+1));
+ if (dewey_cmp(version, op, ver))
+ return 1;
+ }
+ else {
+ if (dewey_cmp(version, op, sep))
+ return 1;
+ }
+
+ return 0;
+}
+
--- /dev/null
+/* $NetBSD: dewey.h,v 1.1.1.1 2008/09/30 19:00:27 joerg Exp $ */
+
+#ifndef _INST_LIB_DEWEY_H_
+#define _INST_LIB_DEWEY_H_
+
+int dewey_cmp(const char *, int, const char *);
+int dewey_match(const char *, const char *);
+int dewey_mktest(int *, const char *);
+
+enum {
+ DEWEY_LT,
+ DEWEY_LE,
+ DEWEY_EQ,
+ DEWEY_GE,
+ DEWEY_GT,
+ DEWEY_NE
+};
+
+#endif /* _INST_LIB_DEWEY_H_ */
--- /dev/null
+/* $NetBSD: fexec.c,v 1.1.1.3 2009/08/06 16:55:26 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Matthias Scheler.
+ *
+ * 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.
+ *
+ * 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_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+#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_STDARG_H
+#include <stdarg.h>
+#endif
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "lib.h"
+
+__RCSID("$NetBSD: fexec.c,v 1.1.1.3 2009/08/06 16:55:26 joerg Exp $");
+
+static int vfcexec(const char *, int, const char *, va_list);
+
+/*
+ * fork, then change current working directory to path and
+ * execute the command and arguments in the argv array.
+ * wait for the command to finish, then return the exit status.
+ */
+int
+pfcexec(const char *path, const char *file, const char **argv)
+{
+ pid_t child;
+ int status;
+
+ child = vfork();
+ switch (child) {
+ case 0:
+ if ((path != NULL) && (chdir(path) < 0))
+ _exit(127);
+
+ (void)execvp(file, __UNCONST(argv));
+ _exit(127);
+ /* NOTREACHED */
+ case -1:
+ return -1;
+ }
+
+ while (waitpid(child, &status, 0) < 0) {
+ if (errno != EINTR)
+ return -1;
+ }
+
+ if (!WIFEXITED(status))
+ return -1;
+
+ return WEXITSTATUS(status);
+}
+
+static int
+vfcexec(const char *path, int skipempty, const char *arg, va_list ap)
+{
+ const char **argv;
+ size_t argv_size, argc;
+ int retval;
+
+ argv_size = 16;
+ argv = xcalloc(argv_size, sizeof(*argv));
+
+ argv[0] = arg;
+ argc = 1;
+
+ do {
+ if (argc == argv_size) {
+ argv_size *= 2;
+ argv = xrealloc(argv, argv_size * sizeof(*argv));
+ }
+ arg = va_arg(ap, const char *);
+ if (skipempty && arg && strlen(arg) == 0)
+ continue;
+ argv[argc++] = arg;
+ } while (arg != NULL);
+
+ retval = pfcexec(path, argv[0], argv);
+ free(argv);
+ return retval;
+}
+
+int
+fexec(const char *arg, ...)
+{
+ va_list ap;
+ int result;
+
+ va_start(ap, arg);
+ result = vfcexec(NULL, 0, arg, ap);
+ va_end(ap);
+
+ return result;
+}
+
+int
+fexec_skipempty(const char *arg, ...)
+{
+ va_list ap;
+ int result;
+
+ va_start(ap, arg);
+ result = vfcexec(NULL, 1, arg, ap);
+ va_end(ap);
+
+ return result;
+}
+
+int
+fcexec(const char *path, const char *arg, ...)
+{
+ va_list ap;
+ int result;
+
+ va_start(ap, arg);
+ result = vfcexec(path, 0, arg, ap);
+ va_end(ap);
+
+ return result;
+}
--- /dev/null
+/* $NetBSD: file.c,v 1.1.1.6 2011/02/18 22:32:30 aymeric 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_PARAM_H
+#include <sys/param.h>
+#endif
+#if HAVE_SYS_QUEUE_H
+#include <sys/queue.h>
+#endif
+__RCSID("$NetBSD: file.c,v 1.1.1.6 2011/02/18 22:32:30 aymeric Exp $");
+
+/*
+ * 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.
+ *
+ */
+
+#include "lib.h"
+
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#if HAVE_ASSERT_H
+#include <assert.h>
+#endif
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+#if HAVE_GLOB_H
+#include <glob.h>
+#endif
+#if HAVE_PWD_H
+#include <pwd.h>
+#endif
+#if HAVE_TIME_H
+#include <time.h>
+#endif
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+
+/*
+ * Quick check to see if a file (or dir ...) exists
+ */
+Boolean
+fexists(const char *fname)
+{
+ struct stat dummy;
+ if (!lstat(fname, &dummy))
+ return TRUE;
+ return FALSE;
+}
+
+/*
+ * Quick check to see if something is a directory
+ */
+Boolean
+isdir(const char *fname)
+{
+ struct stat sb;
+
+ if (lstat(fname, &sb) != FAIL && S_ISDIR(sb.st_mode))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/*
+ * Check if something is a link to a directory
+ */
+Boolean
+islinktodir(const char *fname)
+{
+ struct stat sb;
+
+ if (lstat(fname, &sb) != FAIL && S_ISLNK(sb.st_mode)) {
+ if (stat(fname, &sb) != FAIL && S_ISDIR(sb.st_mode))
+ return TRUE; /* link to dir! */
+ else
+ return FALSE; /* link to non-dir */
+ } else
+ return FALSE; /* non-link */
+}
+
+/*
+ * Check if something is a link that points to nonexistant target.
+ */
+Boolean
+isbrokenlink(const char *fname)
+{
+ struct stat sb;
+
+ if (lstat(fname, &sb) != FAIL && S_ISLNK(sb.st_mode)) {
+ if (stat(fname, &sb) != FAIL)
+ return FALSE; /* link target exists! */
+ else
+ return TRUE; /* link target missing*/
+ } else
+ return FALSE; /* non-link */
+}
+
+/*
+ * Check to see if file is a dir, and is empty
+ */
+Boolean
+isemptydir(const char *fname)
+{
+ if (isdir(fname) || islinktodir(fname)) {
+ DIR *dirp;
+ struct dirent *dp;
+
+ dirp = opendir(fname);
+ if (!dirp)
+ return FALSE; /* no perms, leave it alone */
+ for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
+ if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, "..")) {
+ closedir(dirp);
+ return FALSE;
+ }
+ }
+ (void) closedir(dirp);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * Check if something is a regular file
+ */
+Boolean
+isfile(const char *fname)
+{
+ struct stat sb;
+ if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode))
+ return TRUE;
+ return FALSE;
+}
+
+/*
+ * Check to see if file is a file and is empty. If nonexistent or not
+ * a file, say "it's empty", otherwise return TRUE if zero sized.
+ */
+Boolean
+isemptyfile(const char *fname)
+{
+ struct stat sb;
+ if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode)) {
+ if (sb.st_size != 0)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* This struct defines the leading part of a valid URL name */
+typedef struct url_t {
+ const char *u_s; /* the leading part of the URL */
+ int u_len; /* its length */
+} url_t;
+
+/* A table of valid leading strings for URLs */
+static const url_t urls[] = {
+#define STR_AND_SIZE(str) { str, sizeof(str) - 1 }
+ STR_AND_SIZE("file://"),
+ STR_AND_SIZE("ftp://"),
+ STR_AND_SIZE("http://"),
+ STR_AND_SIZE("https://"),
+#undef STR_AND_SIZE
+ {NULL, 0}
+};
+
+/*
+ * Returns length of leading part of any URL from urls table, or -1
+ */
+int
+URLlength(const char *fname)
+{
+ const url_t *up;
+ int i;
+
+ if (fname != (char *) NULL) {
+ for (i = 0; isspace((unsigned char) *fname); i++) {
+ fname++;
+ }
+ for (up = urls; up->u_s; up++) {
+ if (strncmp(fname, up->u_s, up->u_len) == 0) {
+ return i + up->u_len; /* ... + sizeof(up->u_s); - HF */
+ }
+ }
+ }
+ return -1;
+}
+
+/*
+ * Takes a filename and package name, returning (in "try") the canonical
+ * "preserve" name for it.
+ */
+Boolean
+make_preserve_name(char *try, size_t max, const char *name, const char *file)
+{
+ size_t len, i;
+
+ if ((len = strlen(file)) == 0)
+ return FALSE;
+ i = len - 1;
+ strncpy(try, file, max);
+ if (try[i] == '/') /* Catch trailing slash early and save checking in the loop */
+ --i;
+ for (; i; i--) {
+ if (try[i] == '/') {
+ try[i + 1] = '.';
+ strncpy(&try[i + 2], &file[i + 1], max - i - 2);
+ break;
+ }
+ }
+ if (!i) {
+ try[0] = '.';
+ strncpy(try + 1, file, max - 1);
+ }
+ /* I should probably be called rude names for these inline assignments */
+ strncat(try, ".", max -= strlen(try));
+ strncat(try, name, max -= strlen(name));
+ strncat(try, ".", max--);
+ strncat(try, "backup", max -= 6);
+ return TRUE;
+}
+
+void
+remove_files(const char *path, const char *pattern)
+{
+ char fpath[MaxPathSize];
+ glob_t globbed;
+ int i;
+ size_t j;
+
+ (void) snprintf(fpath, sizeof(fpath), "%s/%s", path, 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;
+ }
+
+ /* deleting globbed files */
+ for (j = 0; j < globbed.gl_pathc; j++)
+ if (unlink(globbed.gl_pathv[j]) < 0)
+ warn("can't delete ``%s''", globbed.gl_pathv[j]);
+
+ return;
+}
+
+/*
+ * Using fmt, replace all instances of:
+ *
+ * %F With the parameter "name"
+ * %D With the parameter "dir"
+ * %B Return the directory part ("base") of %D/%F
+ * %f Return the filename part of %D/%F
+ *
+ * Check that no overflows can occur.
+ */
+int
+format_cmd(char *buf, size_t size, const char *fmt, const char *dir, const char *name)
+{
+ size_t remaining, quoted;
+ char *bufp, *tmp;
+ char *cp;
+
+ for (bufp = buf, remaining = size; remaining > 1 && *fmt;) {
+ if (*fmt != '%') {
+ *bufp++ = *fmt++;
+ --remaining;
+ continue;
+ }
+
+ if (*++fmt != 'D' && name == NULL) {
+ warnx("no last file available for '%s' command", buf);
+ return -1;
+ }
+ switch (*fmt) {
+ case 'F':
+ quoted = shquote(name, bufp, remaining);
+ if (quoted >= remaining) {
+ warnx("overflow during quoting");
+ return -1;
+ }
+ bufp += quoted;
+ remaining -= quoted;
+ break;
+
+ case 'D':
+ quoted = shquote(dir, bufp, remaining);
+ if (quoted >= remaining) {
+ warnx("overflow during quoting");
+ return -1;
+ }
+ bufp += quoted;
+ remaining -= quoted;
+ break;
+
+ case 'B':
+ tmp = xasprintf("%s/%s", dir, name);
+ cp = strrchr(tmp, '/');
+ *cp = '\0';
+ quoted = shquote(tmp, bufp, remaining);
+ free(tmp);
+ if (quoted >= remaining) {
+ warnx("overflow during quoting");
+ return -1;
+ }
+ bufp += quoted;
+ remaining -= quoted;
+ break;
+
+ case 'f':
+ tmp = xasprintf("%s/%s", dir, name);
+ cp = strrchr(tmp, '/') + 1;
+ quoted = shquote(cp, bufp, remaining);
+ free(tmp);
+ if (quoted >= remaining) {
+ warnx("overflow during quoting");
+ return -1;
+ }
+ bufp += quoted;
+ remaining -= quoted;
+ break;
+
+ default:
+ if (remaining == 1) {
+ warnx("overflow during quoting");
+ return -1;
+ }
+ *bufp++ = '%';
+ *bufp++ = *fmt;
+ remaining -= 2;
+ break;
+ }
+ ++fmt;
+ }
+ *bufp = '\0';
+ return 0;
+}
--- /dev/null
+/* $NetBSD: global.c,v 1.1.1.2 2009/02/02 20:44:06 joerg Exp $ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+__RCSID("$NetBSD: global.c,v 1.1.1.2 2009/02/02 20:44:06 joerg Exp $");
+
+/*
+ * 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
+ *
+ * Semi-convenient place to stick some needed globals.
+ *
+ */
+
+#include "lib.h"
+
+/* These are global for all utils */
+Boolean Verbose = FALSE;
+Boolean Fake = FALSE;
+Boolean Force = FALSE;
--- /dev/null
+/* $NetBSD: gpgsig.c,v 1.1.1.2 2009/08/06 16:55:27 joerg Exp $ */
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+__RCSID("$NetBSD: gpgsig.c,v 1.1.1.2 2009/08/06 16:55:27 joerg Exp $");
+
+/*-
+ * Copyright (c) 2008 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 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/wait.h>
+#ifndef NETBSD
+#include <nbcompat/err.h>
+#else
+#include <err.h>
+#endif
+#ifndef NETBSD
+#include <nbcompat/stdlib.h>
+#else
+#include <stdlib.h>
+#endif
+
+#include "lib.h"
+
+static void
+verify_signature(const char *input, size_t input_len, const char *keyring,
+ const char *detached_signature)
+{
+ const char *argv[8], **argvp;
+ pid_t child;
+ int fd[2], status;
+
+ if (pipe(fd) == -1)
+ err(EXIT_FAILURE, "cannot create input pipes");
+
+ child = vfork();
+ if (child == -1)
+ err(EXIT_FAILURE, "cannot fork GPG process");
+ if (child == 0) {
+ close(fd[1]);
+ close(STDIN_FILENO);
+ if (dup2(fd[0], STDIN_FILENO) == -1) {
+ static const char err_msg[] =
+ "cannot redirect stdin of GPG process\n";
+ write(STDERR_FILENO, err_msg, sizeof(err_msg) - 1);
+ _exit(255);
+ }
+ close(fd[0]);
+ argvp = argv;
+ *argvp++ = gpg_cmd;
+ *argvp++ = "--verify";
+ if (keyring != NULL) {
+ *argvp++ = "--no-default-keyring";
+ *argvp++ = "--keyring";
+ *argvp++ = keyring;
+ }
+
+ if (detached_signature != NULL)
+ *argvp++ = detached_signature;
+ *argvp++ = "-";
+
+ *argvp = NULL;
+
+ execvp(gpg_cmd, __UNCONST(argv));
+ _exit(255);
+ }
+ close(fd[0]);
+ if (write(fd[1], input, input_len) != (ssize_t)input_len)
+ errx(EXIT_FAILURE, "Short read from GPG");
+ close(fd[1]);
+ waitpid(child, &status, 0);
+ if (status)
+ errx(EXIT_FAILURE, "GPG could not verify the signature");
+}
+
+int
+inline_gpg_verify(const char *content, size_t len, const char *keyring)
+{
+ verify_signature(content, len, keyring, NULL);
+
+ return 0;
+}
+
+int
+detached_gpg_verify(const char *content, size_t len,
+ const char *signature, size_t signature_len, const char *keyring)
+{
+ int fd;
+ const char *tmpdir;
+ char *tempsig;
+ ssize_t ret;
+
+ if (gpg_cmd == NULL) {
+ warnx("GPG variable not set, failing signature check");
+ return -1;
+ }
+
+ if ((tmpdir = getenv("TMPDIR")) == NULL)
+ tmpdir = "/tmp";
+ tempsig = xasprintf("%s/pkg_install.XXXXXX", tmpdir);
+
+ fd = mkstemp(tempsig);
+ if (fd == -1) {
+ warnx("Creating temporary file for GPG signature failed");
+ return -1;
+ }
+
+ while (signature_len) {
+ ret = write(fd, signature, signature_len);
+ if (ret == -1)
+ err(EXIT_FAILURE, "Write to GPG failed");
+ if (ret == 0)
+ errx(EXIT_FAILURE, "Short write to GPG");
+ signature_len -= ret;
+ signature += ret;
+ }
+
+ verify_signature(content, len, keyring, tempsig);
+
+ unlink(tempsig);
+ close(fd);
+ free(tempsig);
+
+ return 0;
+}
+
+int
+detached_gpg_sign(const char *content, size_t len, char **sig, size_t *sig_len,
+ const char *keyring, const char *user)
+{
+ const char *argv[12], **argvp;
+ pid_t child;
+ int fd_in[2], fd_out[2], status;
+ size_t allocated;
+ ssize_t ret;
+
+ if (gpg_cmd == NULL)
+ errx(EXIT_FAILURE, "GPG variable not set");
+
+ if (pipe(fd_in) == -1)
+ err(EXIT_FAILURE, "cannot create input pipes");
+ if (pipe(fd_out) == -1)
+ err(EXIT_FAILURE, "cannot create output pipes");
+
+ child = fork();
+ if (child == -1)
+ err(EXIT_FAILURE, "cannot fork GPG process");
+ if (child == 0) {
+ close(fd_in[1]);
+ close(STDIN_FILENO);
+ if (dup2(fd_in[0], STDIN_FILENO) == -1) {
+ static const char err_msg[] =
+ "cannot redirect stdin of GPG process\n";
+ write(STDERR_FILENO, err_msg, sizeof(err_msg) - 1);
+ _exit(255);
+ }
+ close(fd_in[0]);
+
+ close(fd_out[0]);
+ close(STDOUT_FILENO);
+ if (dup2(fd_out[1], STDOUT_FILENO) == -1) {
+ static const char err_msg[] =
+ "cannot redirect stdout of GPG process\n";
+ write(STDERR_FILENO, err_msg, sizeof(err_msg) - 1);
+ _exit(255);
+ }
+ close(fd_out[1]);
+
+ argvp = argv;
+ *argvp++ = gpg_cmd;
+ *argvp++ = "--detach-sign";
+ *argvp++ = "--armor";
+ *argvp++ = "--output";
+ *argvp++ = "-";
+ if (user != NULL) {
+ *argvp++ = "--local-user";
+ *argvp++ = user;
+ }
+ if (keyring != NULL) {
+ *argvp++ = "--no-default-keyring";
+ *argvp++ = "--secret-keyring";
+ *argvp++ = keyring;
+ }
+
+ *argvp++ = "-";
+ *argvp = NULL;
+
+ execvp(gpg_cmd, __UNCONST(argv));
+ _exit(255);
+ }
+ close(fd_in[0]);
+ if (write(fd_in[1], content, len) != (ssize_t)len)
+ errx(EXIT_FAILURE, "Short read from GPG");
+ close(fd_in[1]);
+
+ allocated = 1024;
+ *sig = xmalloc(allocated);
+ *sig_len = 0;
+
+ close(fd_out[1]);
+
+ while ((ret = read(fd_out[0], *sig + *sig_len,
+ allocated - *sig_len)) > 0) {
+ *sig_len += ret;
+ if (*sig_len == allocated) {
+ allocated *= 2;
+ *sig = xrealloc(*sig, allocated);
+ }
+ }
+
+ close(fd_out[0]);
+
+ waitpid(child, &status, 0);
+ if (status)
+ errx(EXIT_FAILURE, "GPG could not create signature");
+
+ return 0;
+}
--- /dev/null
+/* $NetBSD: iterate.c,v 1.1.1.4 2010/01/30 21:33:47 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <nbcompat.h>
+
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "lib.h"
+
+/*
+ * Generic iteration function:
+ * - get new entries from srciter, stop on NULL
+ * - call matchiter for those entries, stop on non-null return value.
+ */
+int
+iterate_pkg_generic_src(int (*matchiter)(const char *, void *),
+ void *match_cookie, const char *(*srciter)(void *), void *src_cookie)
+{
+ int retval;
+ const char *entry;
+
+ retval = 0;
+
+ while ((entry = (*srciter)(src_cookie)) != NULL) {
+ if ((retval = (*matchiter)(entry, match_cookie)) != 0)
+ break;
+ }
+
+ return retval;
+}
+
+struct pkg_dir_iter_arg {
+ DIR *dirp;
+ int filter_suffix;
+ int allow_nonfiles;
+};
+
+static const char *
+pkg_dir_iter(void *cookie)
+{
+ struct pkg_dir_iter_arg *arg = cookie;
+ struct dirent *dp;
+ size_t len;
+
+ while ((dp = readdir(arg->dirp)) != NULL) {
+#if defined(DT_UNKNOWN) && defined(DT_DIR)
+ if (arg->allow_nonfiles == 0 &&
+ dp->d_type != DT_UNKNOWN && dp->d_type != DT_REG)
+ continue;
+#endif
+ len = strlen(dp->d_name);
+ /* .tbz or .tgz suffix length + some prefix*/
+ if (len < 5)
+ continue;
+ if (arg->filter_suffix == 0 ||
+ memcmp(dp->d_name + len - 4, ".tgz", 4) == 0 ||
+ memcmp(dp->d_name + len - 4, ".tbz", 4) == 0)
+ return dp->d_name;
+ }
+ return NULL;
+}
+
+/*
+ * Call matchiter for every package in the directory.
+ */
+int
+iterate_local_pkg_dir(const char *dir, int filter_suffix, int allow_nonfiles,
+ int (*matchiter)(const char *, void *), void *cookie)
+{
+ struct pkg_dir_iter_arg arg;
+ int retval;
+
+ if ((arg.dirp = opendir(dir)) == NULL)
+ return -1;
+
+ arg.filter_suffix = filter_suffix;
+ arg.allow_nonfiles = allow_nonfiles;
+ retval = iterate_pkg_generic_src(matchiter, cookie, pkg_dir_iter, &arg);
+
+ if (closedir(arg.dirp) == -1)
+ return -1;
+ return retval;
+}
+
+static const char *
+pkg_db_iter(void *cookie)
+{
+ DIR *dirp = cookie;
+ struct dirent *dp;
+
+ while ((dp = readdir(dirp)) != NULL) {
+ if (strcmp(dp->d_name, ".") == 0)
+ continue;
+ if (strcmp(dp->d_name, "..") == 0)
+ continue;
+ if (strcmp(dp->d_name, "pkgdb.byfile.db") == 0)
+ continue;
+ if (strcmp(dp->d_name, ".cookie") == 0)
+ continue;
+ if (strcmp(dp->d_name, "pkg-vulnerabilities") == 0)
+ continue;
+#if defined(DT_UNKNOWN) && defined(DT_DIR)
+ if (dp->d_type != DT_UNKNOWN && dp->d_type != DT_DIR)
+ continue;
+#endif
+ return dp->d_name;
+ }
+ return NULL;
+}
+
+/*
+ * Call matchiter for every installed package.
+ */
+int
+iterate_pkg_db(int (*matchiter)(const char *, void *), void *cookie)
+{
+ DIR *dirp;
+ int retval;
+
+ if ((dirp = opendir(pkgdb_get_dir())) == NULL) {
+ if (errno == ENOENT)
+ return 0; /* No pkgdb directory == empty pkgdb */
+ return -1;
+ }
+
+ retval = iterate_pkg_generic_src(matchiter, cookie, pkg_db_iter, dirp);
+
+ if (closedir(dirp) == -1)
+ return -1;
+ return retval;
+}
+
+static int
+match_by_basename(const char *pkg, void *cookie)
+{
+ const char *target = cookie;
+ const char *pkg_version;
+
+ if ((pkg_version = strrchr(pkg, '-')) == NULL) {
+ warnx("Entry %s in pkgdb is not a valid package name", pkg);
+ return 0;
+ }
+ if (strncmp(pkg, target, pkg_version - pkg) == 0 &&
+ pkg + strlen(target) == pkg_version)
+ return 1;
+ else
+ return 0;
+}
+
+static int
+match_by_pattern(const char *pkg, void *cookie)
+{
+ const char *pattern = cookie;
+
+ return pkg_match(pattern, pkg);
+}
+
+struct add_matching_arg {
+ lpkg_head_t *pkghead;
+ int got_match;
+ int (*match_fn)(const char *pkg, void *cookie);
+ void *cookie;
+};
+
+static int
+match_and_add(const char *pkg, void *cookie)
+{
+ struct add_matching_arg *arg = cookie;
+ lpkg_t *lpp;
+
+ if ((*arg->match_fn)(pkg, arg->cookie) == 1) {
+ arg->got_match = 1;
+
+ lpp = alloc_lpkg(pkg);
+ TAILQ_INSERT_TAIL(arg->pkghead, lpp, lp_link);
+ }
+ return 0;
+}
+
+/*
+ * Find all installed packages with the given basename and add them
+ * to pkghead.
+ * Returns -1 on error, 0 if no match was found and 1 otherwise.
+ */
+int
+add_installed_pkgs_by_basename(const char *pkgbase, lpkg_head_t *pkghead)
+{
+ struct add_matching_arg arg;
+
+ arg.pkghead = pkghead;
+ arg.got_match = 0;
+ arg.match_fn = match_by_basename;
+ arg.cookie = __UNCONST(pkgbase);
+
+ if (iterate_pkg_db(match_and_add, &arg) == -1) {
+ warnx("could not process pkgdb");
+ return -1;
+ }
+ return arg.got_match;
+}
+
+/*
+ * Match all installed packages against pattern, add the matches to pkghead.
+ * Returns -1 on error, 0 if no match was found and 1 otherwise.
+ */
+int
+add_installed_pkgs_by_pattern(const char *pattern, lpkg_head_t *pkghead)
+{
+ struct add_matching_arg arg;
+
+ arg.pkghead = pkghead;
+ arg.got_match = 0;
+ arg.match_fn = match_by_pattern;
+ arg.cookie = __UNCONST(pattern);
+
+ if (iterate_pkg_db(match_and_add, &arg) == -1) {
+ warnx("could not process pkgdb");
+ return -1;
+ }
+ return arg.got_match;
+}
+
+struct best_installed_match_arg {
+ const char *pattern;
+ char *best_current_match;
+};
+
+static int
+match_best_installed(const char *pkg, void *cookie)
+{
+ struct best_installed_match_arg *arg = cookie;
+
+ switch (pkg_order(arg->pattern, pkg, arg->best_current_match)) {
+ case 0:
+ case 2:
+ /*
+ * Either current package doesn't match or
+ * the older match is better. Nothing to do.
+ */
+ break;
+ case 1:
+ /* Current package is better, remember it. */
+ free(arg->best_current_match);
+ arg->best_current_match = xstrdup(pkg);
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Returns a copy of the name of best matching package.
+ * If no package matched the pattern or an error occured, return NULL.
+ */
+char *
+find_best_matching_installed_pkg(const char *pattern)
+{
+ struct best_installed_match_arg arg;
+
+ arg.pattern = pattern;
+ arg.best_current_match = NULL;
+
+ if (iterate_pkg_db(match_best_installed, &arg) == -1) {
+ warnx("could not process pkgdb");
+ return NULL;
+ }
+
+ return arg.best_current_match;
+}
+
+struct call_matching_arg {
+ const char *pattern;
+ int (*call_fn)(const char *pkg, void *cookie);
+ void *cookie;
+};
+
+static int
+match_and_call(const char *pkg, void *cookie)
+{
+ struct call_matching_arg *arg = cookie;
+
+ if (pkg_match(arg->pattern, pkg) == 1) {
+ return (*arg->call_fn)(pkg, arg->cookie);
+ } else
+ return 0;
+}
+
+/*
+ * Find all packages that match the given pattern and call the function
+ * for each of them. Iteration stops if the callback return non-0.
+ * Returns -1 on error, 0 if the iteration finished or whatever the
+ * callback returned otherwise.
+ */
+int
+match_installed_pkgs(const char *pattern, int (*cb)(const char *, void *),
+ void *cookie)
+{
+ struct call_matching_arg arg;
+
+ arg.pattern = pattern;
+ arg.call_fn = cb;
+ arg.cookie = cookie;
+
+ return iterate_pkg_db(match_and_call, &arg);
+}
+
+struct best_file_match_arg {
+ const char *pattern;
+ char *best_current_match_filtered;
+ char *best_current_match;
+ int filter_suffix;
+};
+
+static int
+match_best_file(const char *filename, void *cookie)
+{
+ struct best_file_match_arg *arg = cookie;
+ const char *active_filename;
+ char *filtered_filename;
+
+ if (arg->filter_suffix) {
+ size_t len;
+
+ len = strlen(filename);
+ if (len < 5 ||
+ (memcmp(filename + len - 4, ".tgz", 4) != 0 &&
+ memcmp(filename + len - 4, ".tbz", 4) != 0)) {
+ warnx("filename %s does not contain a recognized suffix", filename);
+ return -1;
+ }
+ filtered_filename = xmalloc(len - 4 + 1);
+ memcpy(filtered_filename, filename, len - 4);
+ filtered_filename[len - 4] = '\0';
+ active_filename = filtered_filename;
+ } else {
+ filtered_filename = NULL;
+ active_filename = filename;
+ }
+
+ switch (pkg_order(arg->pattern, active_filename, arg->best_current_match_filtered)) {
+ case 0:
+ case 2:
+ /*
+ * Either current package doesn't match or
+ * the older match is better. Nothing to do.
+ */
+ free(filtered_filename);
+ return 0;
+ case 1:
+ /* Current package is better, remember it. */
+ free(arg->best_current_match);
+ free(arg->best_current_match_filtered);
+ arg->best_current_match = xstrdup(filename);
+ if (filtered_filename != NULL)
+ arg->best_current_match_filtered = filtered_filename;
+ else
+ arg->best_current_match_filtered = xstrdup(active_filename);
+ return 0;
+ default:
+ errx(EXIT_FAILURE, "Invalid error from pkg_order");
+ /* NOTREACHED */
+ }
+}
+
+/*
+ * Returns a copy of the name of best matching file.
+ * If no package matched the pattern or an error occured, return NULL.
+ */
+char *
+find_best_matching_file(const char *dir, const char *pattern, int filter_suffix, int allow_nonfiles)
+{
+ struct best_file_match_arg arg;
+
+ arg.filter_suffix = filter_suffix;
+ arg.pattern = pattern;
+ arg.best_current_match = NULL;
+ arg.best_current_match_filtered = NULL;
+
+ if (iterate_local_pkg_dir(dir, filter_suffix, allow_nonfiles, match_best_file, &arg) == -1) {
+ warnx("could not process directory");
+ return NULL;
+ }
+ free(arg.best_current_match_filtered);
+
+ return arg.best_current_match;
+}
+
+struct call_matching_file_arg {
+ const char *pattern;
+ int (*call_fn)(const char *pkg, void *cookie);
+ void *cookie;
+ int filter_suffix;
+};
+
+static int
+match_file_and_call(const char *filename, void *cookie)
+{
+ struct call_matching_file_arg *arg = cookie;
+ const char *active_filename;
+ char *filtered_filename;
+ int ret;
+
+ if (arg->filter_suffix) {
+ size_t len;
+
+ len = strlen(filename);
+ if (len < 5 ||
+ (memcmp(filename + len - 4, ".tgz", 4) != 0 &&
+ memcmp(filename + len - 4, ".tbz", 4) != 0)) {
+ warnx("filename %s does not contain a recognized suffix", filename);
+ return -1;
+ }
+ filtered_filename = xmalloc(len - 4 + 1);
+ memcpy(filtered_filename, filename, len - 4);
+ filtered_filename[len - 4] = '\0';
+ active_filename = filtered_filename;
+ } else {
+ filtered_filename = NULL;
+ active_filename = filename;
+ }
+
+ ret = pkg_match(arg->pattern, active_filename);
+ free(filtered_filename);
+
+ if (ret == 1)
+ return (*arg->call_fn)(filename, arg->cookie);
+ else
+ return 0;
+}
+
+/*
+ * Find all packages that match the given pattern and call the function
+ * for each of them. Iteration stops if the callback return non-0.
+ * Returns -1 on error, 0 if the iteration finished or whatever the
+ * callback returned otherwise.
+ */
+int
+match_local_files(const char *dir, int filter_suffix, int allow_nonfiles, const char *pattern,
+ int (*cb)(const char *, void *), void *cookie)
+{
+ struct call_matching_file_arg arg;
+
+ arg.pattern = pattern;
+ arg.call_fn = cb;
+ arg.cookie = cookie;
+ arg.filter_suffix = filter_suffix;
+
+ return iterate_local_pkg_dir(dir, filter_suffix, allow_nonfiles, match_file_and_call, &arg);
+}
--- /dev/null
+/* $NetBSD: lib.h,v 1.6 2010/06/26 00:17:13 joerg Exp $ */
+
+/* from FreeBSD Id: lib.h,v 1.25 1997/10/08 07:48:03 charnier Exp */
+
+/*
+ * 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
+ *
+ * Include and define various things wanted by the library routines.
+ *
+ */
+
+#ifndef _INST_LIB_LIB_H_
+#define _INST_LIB_LIB_H_
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#if HAVE_SYS_QUEUE_H
+#include <sys/queue.h>
+#endif
+
+#if HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+#if HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/* Macros */
+#ifndef __UNCONST
+#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+#endif
+
+#define SUCCESS (0)
+#define FAIL (-1)
+
+#ifndef TRUE
+#define TRUE (1)
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#ifndef OPSYS_NAME
+#define OPSYS_NAME "NetBSD"
+#endif
+
+#ifndef DEF_UMASK
+#define DEF_UMASK 022
+#endif
+
+#ifndef PATH_MAX
+# ifdef MAXPATHLEN
+# define PATH_MAX MAXPATHLEN
+# else
+# define PATH_MAX 1024
+# endif
+#endif
+
+enum {
+ MaxPathSize = PATH_MAX
+};
+
+/* The names of our "special" files */
+#define CONTENTS_FNAME "+CONTENTS"
+#define COMMENT_FNAME "+COMMENT"
+#define DESC_FNAME "+DESC"
+#define INSTALL_FNAME "+INSTALL"
+#define DEINSTALL_FNAME "+DEINSTALL"
+#define REQUIRED_BY_FNAME "+REQUIRED_BY"
+#define REQUIRED_BY_FNAME_TMP "+REQUIRED_BY.tmp"
+#define DISPLAY_FNAME "+DISPLAY"
+#define MTREE_FNAME "+MTREE_DIRS"
+#define BUILD_VERSION_FNAME "+BUILD_VERSION"
+#define BUILD_INFO_FNAME "+BUILD_INFO"
+#define INSTALLED_INFO_FNAME "+INSTALLED_INFO"
+#define SIZE_PKG_FNAME "+SIZE_PKG"
+#define SIZE_ALL_FNAME "+SIZE_ALL"
+#define PRESERVE_FNAME "+PRESERVE"
+#define VIEWS_FNAME "+VIEWS"
+#define VIEWS_FNAME_TMP "+VIEWS.tmp"
+#define DEPOT_FNAME "+DEPOT"
+
+/* The names of special variables */
+#define AUTOMATIC_VARNAME "automatic"
+
+/* Prefix for extended PLIST cmd */
+#define CMD_CHAR '@'
+
+/* The name of the "prefix" environment variable given to scripts */
+#define PKG_PREFIX_VNAME "PKG_PREFIX"
+
+/* The name of the "destdir" environment variable given to scripts */
+#define PKG_DESTDIR_VNAME "PKG_DESTDIR"
+
+/*
+ * The name of the "metadatadir" environment variable given to scripts.
+ * This variable holds the location of the +-files for this package.
+ */
+#define PKG_METADATA_DIR_VNAME "PKG_METADATA_DIR"
+
+/*
+ * The name of the environment variable holding the location to the
+ * reference-counts database directory.
+ */
+#define PKG_REFCOUNT_DBDIR_VNAME "PKG_REFCOUNT_DBDIR"
+
+#define PKG_PATTERN_MAX MaxPathSize /* max length of pattern, including nul */
+#define PKG_SUFFIX_MAX 10 /* max length of suffix, including nul */
+
+enum {
+ ReadWrite,
+ ReadOnly
+};
+
+
+/* Enumerated constants for plist entry types */
+typedef enum pl_ent_t {
+ PLIST_SHOW_ALL = -1,
+ PLIST_FILE, /* 0 */
+ PLIST_CWD, /* 1 */
+ PLIST_CMD, /* 2 */
+ PLIST_CHMOD, /* 3 */
+ PLIST_CHOWN, /* 4 */
+ PLIST_CHGRP, /* 5 */
+ PLIST_COMMENT, /* 6 */
+ PLIST_IGNORE, /* 7 */
+ PLIST_NAME, /* 8 */
+ PLIST_UNEXEC, /* 9 */
+ PLIST_SRC, /* 10 */
+ PLIST_DISPLAY, /* 11 */
+ PLIST_PKGDEP, /* 12 */
+ PLIST_DIR_RM, /* 13 */
+ PLIST_OPTION, /* 14 */
+ PLIST_PKGCFL, /* 15 */
+ PLIST_BLDDEP, /* 16 */
+ PLIST_PKGDIR /* 17 */
+} pl_ent_t;
+
+/* Enumerated constants for build info */
+typedef enum bi_ent_t {
+ BI_OPSYS, /* 0 */
+ BI_OS_VERSION, /* 1 */
+ BI_MACHINE_ARCH, /* 2 */
+ BI_IGNORE_RECOMMENDED, /* 3 */
+ BI_USE_ABI_DEPENDS, /* 4 */
+ BI_LICENSE, /* 5 */
+ BI_PKGTOOLS_VERSION, /* 6 */
+ BI_ENUM_COUNT /* 7 */
+} bi_ent_t;
+
+/* Types */
+typedef unsigned int Boolean;
+
+/* This structure describes a packing list entry */
+typedef struct plist_t {
+ struct plist_t *prev; /* previous entry */
+ struct plist_t *next; /* next entry */
+ char *name; /* name of entry */
+ Boolean marked; /* whether entry has been marked */
+ pl_ent_t type; /* type of entry */
+} plist_t;
+
+/* This structure describes a package's complete packing list */
+typedef struct package_t {
+ plist_t *head; /* head of list */
+ plist_t *tail; /* tail of list */
+} package_t;
+
+#define SYMLINK_HEADER "Symlink:"
+#define CHECKSUM_HEADER "MD5:"
+
+enum {
+ ChecksumHeaderLen = 4, /* strlen(CHECKSUM_HEADER) */
+ SymlinkHeaderLen = 8, /* strlen(SYMLINK_HEADER) */
+ ChecksumLen = 16,
+ LegibleChecksumLen = 33
+};
+
+/* List of files */
+typedef struct _lfile_t {
+ TAILQ_ENTRY(_lfile_t) lf_link;
+ char *lf_name;
+} lfile_t;
+TAILQ_HEAD(_lfile_head_t, _lfile_t);
+typedef struct _lfile_head_t lfile_head_t;
+#define LFILE_ADD(lfhead,lfp,str) do { \
+ lfp = xmalloc(sizeof(lfile_t)); \
+ lfp->lf_name = str; \
+ TAILQ_INSERT_TAIL(lfhead,lfp,lf_link); \
+ } while(0)
+
+/* List of packages */
+typedef struct _lpkg_t {
+ TAILQ_ENTRY(_lpkg_t) lp_link;
+ char *lp_name;
+} lpkg_t;
+TAILQ_HEAD(_lpkg_head_t, _lpkg_t);
+typedef struct _lpkg_head_t lpkg_head_t;
+
+struct pkg_vulnerabilities {
+ size_t entries;
+ char **vulnerability;
+ char **classification;
+ char **advisory;
+};
+
+/* If URLlength()>0, then there is a ftp:// or http:// in the string,
+ * and this must be an URL. Hide this behind a more obvious name. */
+#define IS_URL(str) (URLlength(str) > 0)
+
+#define IS_STDIN(str) ((str) != NULL && !strcmp((str), "-"))
+#define IS_FULLPATH(str) ((str) != NULL && (str)[0] == '/')
+
+/* Conflict handling (conflicts.c) */
+int some_installed_package_conflicts_with(const char *, const char *, char **, char **);
+
+
+/* Prototypes */
+/* Misc */
+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 **);
+
+/* variables file handling */
+
+char *var_get(const char *, const char *);
+char *var_get_memory(const char *, const char *);
+int var_set(const char *, const char *, const char *);
+int var_copy_list(const char *, const char **);
+
+/* automatically installed as dependency */
+
+Boolean is_automatic_installed(const char *);
+int mark_as_automatic_installed(const char *, int);
+
+/* String */
+const char *basename_of(const char *);
+const char *dirname_of(const char *);
+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 *);
+int quick_pkg_match(const char *, const char *);
+
+/* Iterator functions */
+int iterate_pkg_generic_src(int (*)(const char *, void *), void *,
+ const char *(*)(void *),void *);
+int iterate_local_pkg_dir(const char *, int, int, int (*)(const char *, void *),
+ void *);
+int iterate_pkg_db(int (*)(const char *, void *), void *);
+
+int add_installed_pkgs_by_basename(const char *, lpkg_head_t *);
+int add_installed_pkgs_by_pattern(const char *, lpkg_head_t *);
+char *find_best_matching_installed_pkg(const char *);
+char *find_best_matching_file(const char *, const char *, int, int);
+int match_installed_pkgs(const char *, int (*)(const char *, void *), void *);
+int match_local_files(const char *, int, int, const char *, int (*cb)(const char *, void *), void *);
+
+/* File */
+Boolean fexists(const char *);
+Boolean isdir(const char *);
+Boolean islinktodir(const char *);
+Boolean isemptydir(const char *);
+Boolean isemptyfile(const char *);
+Boolean isfile(const char *);
+Boolean isbrokenlink(const char *);
+Boolean isempty(const char *);
+int URLlength(const char *);
+Boolean make_preserve_name(char *, size_t, const char *, const char *);
+void remove_files(const char *, const char *);
+int format_cmd(char *, size_t, const char *, const char *, const char *);
+
+int recursive_remove(const char *, int);
+
+void add_pkgdir(const char *, const char *, const char *);
+void delete_pkgdir(const char *, const char *, const char *);
+int has_pkgdir(const char *);
+
+/* pkg_io.c: Local and remote archive handling */
+struct archive;
+struct archive_entry;
+
+struct archive *open_archive(const char *, char **);
+struct archive *find_archive(const char *, int, char **);
+void process_pkg_path(void);
+struct url *find_best_package(const char *, const char *, int);
+
+/* Packing list */
+plist_t *new_plist_entry(void);
+plist_t *last_plist(package_t *);
+plist_t *find_plist(package_t *, pl_ent_t);
+char *find_plist_option(package_t *, const char *);
+void plist_delete(package_t *, Boolean, pl_ent_t, char *);
+void free_plist(package_t *);
+void mark_plist(package_t *);
+void csum_plist_entry(char *, plist_t *);
+void add_plist(package_t *, pl_ent_t, const char *);
+void add_plist_top(package_t *, pl_ent_t, const char *);
+void delete_plist(package_t *, Boolean, pl_ent_t, char *);
+void write_plist(package_t *, FILE *, char *);
+void stringify_plist(package_t *, char **, size_t *, const char *);
+void parse_plist(package_t *, const char *);
+void read_plist(package_t *, FILE *);
+void append_plist(package_t *, FILE *);
+int delete_package(Boolean, package_t *, Boolean, const char *);
+
+/* Package Database */
+int pkgdb_open(int);
+void pkgdb_close(void);
+int pkgdb_store(const char *, const char *);
+char *pkgdb_retrieve(const char *);
+int pkgdb_dump(void);
+int pkgdb_remove(const char *);
+int pkgdb_remove_pkg(const char *);
+char *pkgdb_refcount_dir(void);
+char *pkgdb_get_database(void);
+const char *pkgdb_get_dir(void);
+/*
+ * Priorities:
+ * 0 builtin default
+ * 1 config file
+ * 2 environment
+ * 3 command line
+ * 4 destdir/views reset
+ */
+void pkgdb_set_dir(const char *, int);
+char *pkgdb_pkg_dir(const char *);
+char *pkgdb_pkg_file(const char *, const char *);
+
+/* List of packages functions */
+lpkg_t *alloc_lpkg(const char *);
+lpkg_t *find_on_queue(lpkg_head_t *, const char *);
+void free_lpkg(lpkg_t *);
+
+/* Read pkg_vulnerabilities from file */
+struct pkg_vulnerabilities *read_pkg_vulnerabilities_file(const char *, int, int);
+/* Read pkg_vulnerabilities from memory */
+struct pkg_vulnerabilities *read_pkg_vulnerabilities_memory(void *, size_t, int);
+void free_pkg_vulnerabilities(struct pkg_vulnerabilities *);
+int audit_package(struct pkg_vulnerabilities *, const char *, const char *,
+ int);
+
+/* Parse configuration file */
+void pkg_install_config(void);
+/* Print configuration variable */
+void pkg_install_show_variable(const char *);
+
+/* Package signature creation and validation */
+int pkg_verify_signature(const char *, struct archive **, struct archive_entry **, char **);
+int pkg_full_signature_check(const char *, struct archive **);
+#ifdef HAVE_SSL
+void pkg_sign_x509(const char *, const char *, const char *, const char *);
+#endif
+
+void pkg_sign_gpg(const char *, const char *);
+
+#ifdef HAVE_SSL
+/* PKCS7 signing/verification */
+int easy_pkcs7_verify(const char *, size_t, const char *, size_t,
+ const char *, int);
+int easy_pkcs7_sign(const char *, size_t, char **, size_t *, const char *,
+ const char *);
+#endif
+
+int inline_gpg_verify(const char *, size_t, const char *);
+int detached_gpg_verify(const char *, size_t, const char *, size_t,
+ const char *);
+int detached_gpg_sign(const char *, size_t, char **, size_t *, const char *,
+ const char *);
+
+/* License handling */
+int add_licenses(const char *);
+int acceptable_license(const char *);
+int acceptable_pkg_license(const char *);
+void load_license_lists(void);
+
+/* Helper functions for memory allocation */
+char *xstrdup(const char *);
+void *xrealloc(void *, size_t);
+void *xcalloc(size_t, size_t);
+void *xmalloc(size_t);
+char *xasprintf(const char *, ...);
+
+/* Externs */
+extern Boolean Verbose;
+extern Boolean Fake;
+extern Boolean Force;
+extern const char *cert_chain_file;
+extern const char *certs_packages;
+extern const char *certs_pkg_vulnerabilities;
+extern const char *check_eol;
+extern const char *check_vulnerabilities;
+extern const char *config_file;
+extern const char *config_pkg_dbdir;
+extern const char *config_pkg_path;
+extern const char *config_pkg_refcount_dbdir;
+extern const char *do_license_check;
+extern const char *verified_installation;
+extern const char *gpg_cmd;
+extern const char *gpg_keyring_pkgvuln;
+extern const char *gpg_keyring_sign;
+extern const char *gpg_keyring_verify;
+extern const char *gpg_sign_as;
+extern char fetch_flags[];
+
+extern const char *pkg_vulnerabilities_dir;
+extern const char *pkg_vulnerabilities_file;
+extern const char *pkg_vulnerabilities_url;
+extern const char *ignore_advisories;
+extern const char tnf_vulnerability_base[];
+
+extern const char *acceptable_licenses;
+extern const char *default_acceptable_licenses;
+
+#endif /* _INST_LIB_LIB_H_ */
--- /dev/null
+/* $NetBSD: license.c,v 1.4 2013/04/20 15:29:23 wiz Exp $ */
+
+/*-
+ * Copyright (c) 2009 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <nbcompat.h>
+
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#include "lib.h"
+
+#define HASH_SIZE 521
+
+const char *default_acceptable_licenses =
+ "apache-1.1 apache-2.0 "
+ "arphic-public "
+ "artistic artistic-2.0 "
+ "boost-license "
+ "cc-by-sa-v3.0 "
+ "cddl-1.0 "
+ "cpl-1.0 "
+ "epl-v1.0 "
+ "gnu-fdl-v1.1 gnu-fdl-v1.2 gnu-fdl-v1.3 "
+ "gnu-gpl-v1 "
+ "gnu-gpl-v2 gnu-lgpl-v2 gnu-lgpl-v2.1 "
+ "gnu-gpl-v3 gnu-lgpl-v3 "
+ "ibm-public-license-1.0 "
+ "ipafont "
+ "isc "
+ "lppl-1.3c "
+ "lucent "
+ "miros "
+ "mit "
+ "mpl-1.0 mpl-1.1 mpl-2.0 "
+ "mplusfont "
+ "ofl-v1.0 ofl-v1.1 "
+ "original-bsd modified-bsd 2-clause-bsd "
+ "php "
+ "png-license "
+ "postgresql-license "
+ "public-domain "
+ "python-software-foundation "
+ "qpl-v1.0 "
+ "sleepycat-public "
+ "unlicense "
+ "x11 "
+ "zlib "
+ "zpl";
+
+#ifdef DEBUG
+static size_t hash_collisions;
+#endif
+
+static char **license_hash[HASH_SIZE];
+static const char license_spaces[] = " \t\n";
+static const char license_chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.";
+
+static size_t
+hash_license(const char *license, size_t len)
+{
+ size_t hash;
+
+ for (hash = 0; *license && len; ++license, --len)
+ hash = *license + hash * 32;
+ return hash % HASH_SIZE;
+}
+
+static void
+add_license_internal(const char *license, size_t len)
+{
+ char *new_license;
+ size_t slot, i;
+
+ slot = hash_license(license, len);
+
+ new_license = malloc(len + 1);
+ memcpy(new_license, license, len);
+ new_license[len] = '\0';
+
+ if (license_hash[slot] == NULL) {
+ license_hash[slot] = calloc(sizeof(char *), 2);
+ license_hash[slot][0] = new_license;
+ } else {
+ for (i = 0; license_hash[slot][i]; ++i) {
+ if (!memcmp(license_hash[slot][i], license, len) &&
+ license_hash[slot][i][len] == '\0') {
+ free(new_license);
+ return;
+ }
+ }
+
+#ifdef DEBUG
+ ++hash_collisions;
+#endif
+
+ license_hash[slot] = realloc(license_hash[slot],
+ sizeof(char *) * (i + 2));
+ license_hash[slot][i] = new_license;
+ license_hash[slot][i + 1] = NULL;
+ }
+}
+
+int
+add_licenses(const char *line)
+{
+ const char *next;
+
+ if (line == NULL)
+ return 0;
+
+ for (line += strspn(line, license_spaces); line; ) {
+ next = line + strspn(line, license_chars);
+ if (next == line)
+ return *line ? -1 : 0;
+ add_license_internal(line, next - line);
+ line = next + strspn(next, license_spaces);
+ if (next == line)
+ return *line ? -1 : 0;
+ }
+ return 0;
+}
+
+static int
+acceptable_license_internal(const char *license, size_t len)
+{
+ size_t slot, i;
+
+ slot = hash_license(license, len);
+
+ if (license_hash[slot] == NULL)
+ return 0;
+
+ for (i = 0; license_hash[slot][i]; ++i) {
+ if (strncmp(license_hash[slot][i], license, len) == 0 &&
+ license_hash[slot][i][len] == '\0')
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+acceptable_license(const char *license)
+{
+ size_t len;
+
+ len = strlen(license);
+ if (strspn(license, license_chars) != len) {
+ warnx("Invalid character in license name at position %" PRIzu, len);
+ return -1;
+ }
+
+ return acceptable_license_internal(license, len);
+}
+
+static int
+acceptable_pkg_license_internal(const char **licensep, int toplevel, const char *start)
+{
+ const char *license = *licensep;
+ int need_parenthesis, is_true = 0;
+ int expr_type = 0; /* 0: unset, 1: or, 2: and */
+ size_t len;
+
+ license += strspn(license, license_spaces);
+
+ if (*license == '(' && !toplevel) {
+ need_parenthesis = 1;
+ ++license;
+ license += strspn(license, license_spaces);
+ } else {
+ need_parenthesis = 0;
+ }
+
+ for (;;) {
+ if (*license == '(') {
+ switch (acceptable_pkg_license_internal(&license, 0, start)) {
+ case -1:
+ return -1;
+ case 0:
+ if (expr_type == 2)
+ is_true = 0;
+ break;
+ case 1:
+ is_true = 1;
+ break;
+ }
+ license += strspn(license, license_spaces);
+ } else {
+ len = strspn(license, license_chars);
+ if (len == 0) {
+ warnx("Invalid character in license name at position %" PRIzu, license - start + 1);
+ return -1;
+ }
+
+ if (acceptable_license_internal(license, len)) {
+ if (expr_type != 2)
+ is_true = 1;
+ } else if (expr_type == 2) {
+ is_true = 0;
+ }
+
+ license += len;
+
+ len = strspn(license, license_spaces);
+ if (len == 0 && *license && *license != ')') {
+ warnx("Missing space at position %" PRIzu, license - start + 1);
+ return -1;
+ }
+ license += len;
+ }
+
+ if (*license == ')') {
+ if (!need_parenthesis) {
+ warnx("Missing open parenthesis at position %" PRIzu, license - start + 1);
+ return -1;
+ }
+ *licensep = license + 1;
+ return is_true;
+ }
+ if (*license == '\0') {
+ if (need_parenthesis) {
+ warnx("Unbalanced parenthesis at position %" PRIzu, license - start + 1);
+ return -1;
+ }
+ *licensep = license;
+ return is_true;
+ }
+
+ if (strncmp(license, "AND", 3) == 0) {
+ if (expr_type == 1) {
+ warnx("Invalid operator in OR expression at position %" PRIzu, license - start + 1);
+ return -1;
+ }
+ expr_type = 2;
+ license += 3;
+ } else if (strncmp(license, "OR", 2) == 0) {
+ if (expr_type == 2) {
+ warnx("Invalid operator in AND expression at position %" PRIzu, license - start + 1);
+ return -1;
+ }
+ expr_type = 1;
+ license += 2;
+ } else {
+ warnx("Invalid operator at position %" PRIzu, license - start + 1);
+ return -1;
+ }
+ len = strspn(license, license_spaces);
+ if (len == 0 && *license != '(') {
+ warnx("Missing space at position %" PRIzu, license - start + 1);
+ return -1;
+ }
+ license += len;
+ }
+}
+
+int
+acceptable_pkg_license(const char *license)
+{
+ int ret;
+
+ ret = acceptable_pkg_license_internal(&license, 1, license);
+ if (ret == -1)
+ return -1;
+ license += strspn(license, license_spaces);
+ if (*license) {
+ warnx("Trailing garbage in license specification");
+ return -1;
+ }
+ return ret;
+}
+
+void
+load_license_lists(void)
+{
+ if (add_licenses(getenv("PKGSRC_ACCEPTABLE_LICENSES")))
+ errx(EXIT_FAILURE, "syntax error in PKGSRC_ACCEPTABLE_LICENSES");
+ if (add_licenses(acceptable_licenses))
+ errx(EXIT_FAILURE, "syntax error in ACCEPTABLE_LICENSES");
+ if (add_licenses(getenv("PKGSRC_DEFAULT_ACCEPTABLE_LICENSES")))
+ errx(EXIT_FAILURE, "syntax error in PKGSRC_DEFAULT_ACCEPTABLE_LICENSES");
+ if (add_licenses(default_acceptable_licenses))
+ errx(EXIT_FAILURE, "syntax error in DEFAULT_ACCEPTABLE_LICENSES");
+}
--- /dev/null
+/* $NetBSD: lpkg.c,v 1.1.1.2 2009/02/02 20:44:06 joerg Exp $ */
+
+/*
+ * Copyright (c) 1999 Christian E. Hopps
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * Package-list auxiliary functions
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+#include "lib.h"
+
+/*
+ * Add a package to the (add/recursive delete) list
+ */
+lpkg_t *
+alloc_lpkg(const char *pkgname)
+{
+ lpkg_t *lpp;
+
+ lpp = xmalloc(sizeof(*lpp));
+ lpp->lp_name = xstrdup(pkgname);
+ return (lpp);
+}
+
+void
+free_lpkg(lpkg_t *lpp)
+{
+ free(lpp->lp_name);
+ free(lpp);
+}
+
+lpkg_t *
+find_on_queue(lpkg_head_t *qp, const char *name)
+{
+ lpkg_t *lpp;
+
+ for (lpp = TAILQ_FIRST(qp); lpp; lpp = TAILQ_NEXT(lpp, lp_link))
+ if (!strcmp(name, lpp->lp_name))
+ return (lpp);
+ return (0);
+}
--- /dev/null
+/* $NetBSD: opattern.c,v 1.1.1.3 2012/02/19 17:46:47 tron Exp $ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+__RCSID("$NetBSD: opattern.c,v 1.1.1.3 2012/02/19 17:46:47 tron Exp $");
+
+/*
+ * 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 string utilities.
+ *
+ */
+
+#if HAVE_ASSERT_H
+#include <assert.h>
+#endif
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+#if HAVE_FNMATCH_H
+#include <fnmatch.h>
+#endif
+#include "lib.h"
+#include "dewey.h"
+
+/* pull in definitions and macros for resizing arrays as we go */
+#include "defs.h"
+
+/*
+ * Perform alternate match on "pkg" against "pattern",
+ * calling pkg_match (recursively) to resolve any other patterns.
+ * Return 1 on match, 0 otherwise
+ */
+static int
+alternate_match(const char *pattern, const char *pkg)
+{
+ char *sep;
+ char buf[MaxPathSize];
+ char *last;
+ char *alt;
+ char *cp;
+ int cnt;
+ int found;
+
+ if ((sep = strchr(pattern, '{')) == (char *) NULL) {
+ errx(EXIT_FAILURE, "alternate_match(): '{' expected in `%s'", pattern);
+ }
+ (void) strncpy(buf, pattern, (size_t) (sep - pattern));
+ alt = &buf[sep - pattern];
+ last = (char *) NULL;
+ for (cnt = 0, cp = sep; *cp && last == (char *) NULL; cp++) {
+ if (*cp == '{') {
+ cnt++;
+ } else if (*cp == '}' && --cnt == 0 && last == (char *) NULL) {
+ last = cp + 1;
+ }
+ }
+ if (cnt != 0) {
+ errx(EXIT_FAILURE, "Malformed alternate `%s'", pattern);
+ }
+ for (found = 0, cp = sep + 1; *sep != '}'; cp = sep + 1) {
+ for (cnt = 0, sep = cp; cnt > 0 || (cnt == 0 && *sep != '}' && *sep != ','); sep++) {
+ if (*sep == '{') {
+ cnt++;
+ } else if (*sep == '}') {
+ cnt--;
+ }
+ }
+ (void) snprintf(alt, sizeof(buf) - (alt - buf), "%.*s%s", (int) (sep - cp), cp, last);
+ if (pkg_match(buf, pkg) == 1) {
+ found = 1;
+ }
+ }
+ return found;
+}
+
+/*
+ * Perform glob match on "pkg" against "pattern".
+ * Return 1 on match, 0 otherwise
+ */
+static int
+glob_match(const char *pattern, const char *pkg)
+{
+ return fnmatch(pattern, pkg, FNM_PERIOD) == 0;
+}
+
+/*
+ * Perform simple match on "pkg" against "pattern".
+ * Return 1 on match, 0 otherwise
+ */
+static int
+simple_match(const char *pattern, const char *pkg)
+{
+ return strcmp(pattern, pkg) == 0;
+}
+
+/*
+ * Performs a fast check if pattern can ever match pkg.
+ * Returns 1 if a match is possible and 0 otherwise.
+ */
+int
+quick_pkg_match(const char *pattern, const char *pkg)
+{
+#define simple(x) (isalnum((unsigned char)(x)) || (x) == '-')
+ if (!simple(pattern[0]))
+ return 1;
+ if (pattern[0] != pkg[0])
+ return 0;
+
+ if (!simple(pattern[1]))
+ return 1;
+ if (pattern[1] != pkg[1])
+ return 0;
+ return 1;
+#undef simple
+}
+
+/*
+ * Match pkg against pattern, return 1 if matching, 0 else
+ */
+int
+pkg_match(const char *pattern, const char *pkg)
+{
+ if (!quick_pkg_match(pattern, pkg))
+ return 0;
+
+ if (strchr(pattern, '{') != (char *) NULL) {
+ /* emulate csh-type alternates */
+ return alternate_match(pattern, pkg);
+ }
+ if (strpbrk(pattern, "<>") != (char *) NULL) {
+ int ret;
+
+ /* perform relational dewey match on version number */
+ ret = dewey_match(pattern, pkg);
+ if (ret < 0)
+ errx(EXIT_FAILURE, "dewey_match returned error");
+ return ret;
+ }
+ if (strpbrk(pattern, "*?[]") != (char *) NULL) {
+ /* glob match */
+ if (glob_match(pattern, pkg))
+ return 1;
+ }
+
+ /* no alternate, dewey or glob match -> simple compare */
+ if (simple_match(pattern, pkg))
+ return 1;
+
+ /* globbing patterns and simple matches may be specified with or
+ * without the version number, so check for both cases. */
+
+ {
+ char *pattern_ver;
+ int retval;
+
+ pattern_ver = xasprintf("%s-[0-9]*", pattern);
+ retval = glob_match(pattern_ver, pkg);
+ free(pattern_ver);
+ return retval;
+ }
+}
+
+int
+pkg_order(const char *pattern, const char *first_pkg, const char *second_pkg)
+{
+ const char *first_version;
+ const char *second_version;
+
+ if (first_pkg == NULL && second_pkg == NULL)
+ return 0;
+
+ if (first_pkg == NULL)
+ return pkg_match(pattern, second_pkg) ? 2 : 0;
+ if (second_pkg == NULL)
+ return pkg_match(pattern, first_pkg) ? 1 : 0;
+
+ first_version = strrchr(first_pkg, '-');
+ second_version = strrchr(second_pkg, '-');
+
+ if (first_version == NULL || !pkg_match(pattern, first_pkg))
+ return pkg_match(pattern, second_pkg) ? 2 : 0;
+
+ if (second_version == NULL || !pkg_match(pattern, second_pkg))
+ return pkg_match(pattern, first_pkg) ? 1 : 0;
+
+ if (dewey_cmp(first_version + 1, DEWEY_GT, second_version + 1))
+ return 1;
+ else if (dewey_cmp(first_version + 1, DEWEY_LT, second_version + 1))
+ return 2;
+ else if (strcmp(first_pkg, second_pkg) < 0)
+ return 1;
+ else
+ return 2;
+}
--- /dev/null
+/* $NetBSD: parse-config.c,v 1.1.1.11 2010/06/26 00:14:31 joerg Exp $ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+__RCSID("$NetBSD: parse-config.c,v 1.1.1.11 2010/06/26 00:14:31 joerg Exp $");
+
+/*-
+ * Copyright (c) 2008, 2009 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+#include <errno.h>
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifndef BOOTSTRAP
+#include <fetch.h>
+#endif
+
+#include "lib.h"
+
+static int cache_connections = 16;
+static int cache_connections_host = 4;
+
+const char *config_file = SYSCONFDIR"/pkg_install.conf";
+
+char fetch_flags[10] = ""; /* Workaround Mac OS X linker issues with BSS */
+static const char *active_ftp;
+static const char *verbose_netio;
+static const char *ignore_proxy;
+const char *cache_index = "yes";
+const char *cert_chain_file;
+const char *certs_packages;
+const char *certs_pkg_vulnerabilities;
+const char *check_eol = "yes";
+const char *check_vulnerabilities;
+static const char *config_cache_connections;
+static const char *config_cache_connections_host;
+const char *config_pkg_dbdir;
+const char *config_pkg_path;
+const char *config_pkg_refcount_dbdir;
+const char *do_license_check;
+const char *verified_installation;
+const char *gpg_cmd;
+const char *gpg_keyring_pkgvuln;
+const char *gpg_keyring_sign;
+const char *gpg_keyring_verify;
+const char *gpg_sign_as;
+const char *pkg_vulnerabilities_dir;
+const char *pkg_vulnerabilities_file;
+const char *pkg_vulnerabilities_url;
+const char *ignore_advisories = NULL;
+const char tnf_vulnerability_base[] = "http://ftp.NetBSD.org/pub/NetBSD/packages/vulns";
+const char *acceptable_licenses = NULL;
+
+static struct config_variable {
+ const char *name;
+ const char **var;
+} config_variables[] = {
+ { "ACCEPTABLE_LICENSES", &acceptable_licenses },
+ { "ACTIVE_FTP", &active_ftp },
+ { "CACHE_INDEX", &cache_index },
+ { "CACHE_CONNECTIONS", &config_cache_connections },
+ { "CACHE_CONNECTIONS_HOST", &config_cache_connections_host },
+ { "CERTIFICATE_ANCHOR_PKGS", &certs_packages },
+ { "CERTIFICATE_ANCHOR_PKGVULN", &certs_pkg_vulnerabilities },
+ { "CERTIFICATE_CHAIN", &cert_chain_file },
+ { "CHECK_LICENSE", &do_license_check },
+ { "CHECK_END_OF_LIFE", &check_eol },
+ { "CHECK_VULNERABILITIES", &check_vulnerabilities },
+ { "DEFAULT_ACCEPTABLE_LICENSES", &default_acceptable_licenses },
+ { "GPG", &gpg_cmd },
+ { "GPG_KEYRING_PKGVULN", &gpg_keyring_pkgvuln },
+ { "GPG_KEYRING_SIGN", &gpg_keyring_sign },
+ { "GPG_KEYRING_VERIFY", &gpg_keyring_verify },
+ { "GPG_SIGN_AS", &gpg_sign_as },
+ { "IGNORE_PROXY", &ignore_proxy },
+ { "IGNORE_URL", &ignore_advisories },
+ { "PKG_DBDIR", &config_pkg_dbdir },
+ { "PKG_PATH", &config_pkg_path },
+ { "PKG_REFCOUNT_DBDIR", &config_pkg_refcount_dbdir },
+ { "PKGVULNDIR", &pkg_vulnerabilities_dir },
+ { "PKGVULNURL", &pkg_vulnerabilities_url },
+ { "VERBOSE_NETIO", &verbose_netio },
+ { "VERIFIED_INSTALLATION", &verified_installation },
+ { NULL, NULL }, /* For use by pkg_install_show_variable */
+ { NULL, NULL }
+};
+
+char *config_tmp_variables[sizeof config_variables/sizeof config_variables[0]];
+
+static void
+parse_pkg_install_conf(void)
+{
+ struct config_variable *var;
+ FILE *fp;
+ char *line, *value;
+ size_t len, var_len, i;
+
+ fp = fopen(config_file, "r");
+ if (!fp) {
+ if (errno != ENOENT)
+ warn("Can't open '%s' for reading", config_file);
+ return;
+ }
+
+ while ((line = fgetln(fp, &len)) != (char *) NULL) {
+ if (line[len - 1] == '\n')
+ --len;
+ for (i = 0; (var = &config_variables[i])->name != NULL; ++i) {
+ var_len = strlen(var->name);
+ if (strncmp(var->name, line, var_len) != 0)
+ continue;
+ if (line[var_len] != '=')
+ continue;
+ line += var_len + 1;
+ len -= var_len + 1;
+ if (config_tmp_variables[i])
+ value = xasprintf("%s\n%.*s",
+ config_tmp_variables[i], (int)len, line);
+ else
+ value = xasprintf("%.*s", (int)len, line);
+ free(config_tmp_variables[i]);
+ config_tmp_variables[i] = value;
+ break;
+ }
+ }
+
+ for (i = 0; (var = &config_variables[i])->name != NULL; ++i) {
+ if (config_tmp_variables[i] == NULL)
+ continue;
+ *var->var = config_tmp_variables[i];
+ config_tmp_variables[i] = NULL;
+ }
+
+ fclose(fp);
+}
+
+void
+pkg_install_config(void)
+{
+ int do_cache_index;
+ char *value;
+
+ parse_pkg_install_conf();
+
+ if ((value = getenv("PKG_DBDIR")) != NULL)
+ pkgdb_set_dir(value, 2);
+ else if (config_pkg_dbdir != NULL)
+ pkgdb_set_dir(config_pkg_dbdir, 1);
+ config_pkg_dbdir = xstrdup(pkgdb_get_dir());
+
+ if ((value = getenv("PKG_REFCOUNT_DBDIR")) != NULL)
+ config_pkg_refcount_dbdir = value;
+ else if (config_pkg_refcount_dbdir == NULL)
+ config_pkg_refcount_dbdir = xasprintf("%s.refcount",
+ pkgdb_get_dir());
+
+ if (pkg_vulnerabilities_dir == NULL)
+ pkg_vulnerabilities_dir = pkgdb_get_dir();
+ pkg_vulnerabilities_file = xasprintf("%s/pkg-vulnerabilities",
+ pkg_vulnerabilities_dir);
+ if (pkg_vulnerabilities_url == NULL) {
+ pkg_vulnerabilities_url = xasprintf("%s/pkg-vulnerabilities.gz",
+ tnf_vulnerability_base);
+ }
+ if (verified_installation == NULL)
+ verified_installation = "never";
+
+ if (check_vulnerabilities == NULL)
+ check_vulnerabilities = "never";
+
+ if (do_license_check == NULL)
+ do_license_check = "no";
+
+ if ((value = getenv("PKG_PATH")) != NULL)
+ config_pkg_path = value;
+
+ if (strcasecmp(cache_index, "yes") == 0)
+ do_cache_index = 1;
+ else {
+ if (strcasecmp(cache_index, "no"))
+ warnx("Invalid value for configuration option "
+ "CACHE_INDEX");
+ do_cache_index = 0;
+ }
+
+ if (config_cache_connections && *config_cache_connections) {
+ long v = strtol(config_cache_connections, &value, 10);
+ if (*value == '\0') {
+ if (v >= INT_MAX || v < 0)
+ v = -1;
+ cache_connections = v;
+ }
+ }
+ config_cache_connections = xasprintf("%d", cache_connections);
+
+ if (config_cache_connections_host) {
+ long v = strtol(config_cache_connections_host, &value, 10);
+ if (*value == '\0') {
+ if (v >= INT_MAX || v < 0)
+ v = -1;
+ cache_connections_host = v;
+ }
+ }
+ config_cache_connections_host = xasprintf("%d", cache_connections_host);
+
+#ifndef BOOTSTRAP
+ fetchConnectionCacheInit(cache_connections, cache_connections_host);
+#endif
+
+ snprintf(fetch_flags, sizeof(fetch_flags), "%s%s%s%s",
+ (do_cache_index) ? "c" : "",
+ (verbose_netio && *verbose_netio) ? "v" : "",
+ (active_ftp && *active_ftp) ? "a" : "",
+ (ignore_proxy && *ignore_proxy) ? "d" : "");
+}
+
+void
+pkg_install_show_variable(const char *var_name)
+{
+ struct config_variable *var;
+ const char *tmp_value = NULL;
+
+ for (var = config_variables; var->name != NULL; ++var) {
+ if (strcmp(var->name, var_name) == 0)
+ break;
+ }
+ if (var->name == NULL) {
+ var->name = var_name;
+ var->var = &tmp_value;
+ }
+
+ pkg_install_config();
+
+ if (*var->var != NULL)
+ puts(*var->var);
+}
--- /dev/null
+/* $NetBSD: pkcs7.c,v 1.1.1.4 2009/08/06 16:55:27 joerg Exp $ */
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+__RCSID("$NetBSD: pkcs7.c,v 1.1.1.4 2009/08/06 16:55:27 joerg Exp $");
+
+/*-
+ * Copyright (c) 2004, 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Love Hörnquist Åstrand <lha@it.su.se>
+ *
+ * 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.
+ *
+ * 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_ERR_H
+#include <err.h>
+#endif
+
+#include <openssl/pkcs7.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/pem.h>
+#include <openssl/err.h>
+
+#include "lib.h"
+
+#ifndef NS_ANY_CA
+#define NS_ANY_CA (NS_SSL_CA|NS_SMIME_CA|NS_OBJSIGN_CA)
+#endif
+
+static const unsigned int pkg_key_usage = XKU_CODE_SIGN | XKU_SMIME;
+
+static int
+check_ca(X509 *cert)
+{
+ if ((cert->ex_flags & EXFLAG_KUSAGE) != 0 &&
+ (cert->ex_kusage & KU_KEY_CERT_SIGN) != KU_KEY_CERT_SIGN)
+ return 0;
+ if ((cert->ex_flags & EXFLAG_BCONS) != 0)
+ return (cert->ex_flags & EXFLAG_CA) == EXFLAG_CA;
+ if ((cert->ex_flags & (EXFLAG_V1|EXFLAG_SS)) == (EXFLAG_V1|EXFLAG_SS))
+ return 1;
+ if ((cert->ex_flags & EXFLAG_KUSAGE) != 0)
+ return 1;
+ if ((cert->ex_flags & EXFLAG_NSCERT) != 0 &&
+ (cert->ex_nscert & NS_ANY_CA) != 0)
+ return 1;
+ return 0;
+}
+
+static STACK_OF(X509) *
+file_to_certs(const char *file)
+{
+ unsigned long ret;
+ STACK_OF(X509) *certs;
+ FILE *f;
+
+ if ((f = fopen(file, "r")) == NULL) {
+ warn("open failed %s", file);
+ return NULL;
+ }
+
+ certs = sk_X509_new_null();
+ for (;;) {
+ X509 *cert;
+
+ cert = PEM_read_X509(f, NULL, NULL, NULL);
+ if (cert == NULL) {
+ ret = ERR_GET_REASON(ERR_peek_error());
+ if (ret == PEM_R_NO_START_LINE) {
+ /* End of file reached. no error */
+ ERR_clear_error();
+ break;
+ }
+ sk_X509_free(certs);
+ warnx("Can't read certificate in file: %s", file);
+ fclose(f);
+ return NULL;
+ }
+ sk_X509_insert(certs, cert, sk_X509_num(certs));
+ }
+
+ fclose(f);
+
+ if (sk_X509_num(certs) == 0) {
+ sk_X509_free(certs);
+ certs = NULL;
+ warnx("No certificate found in file %s", file);
+ }
+
+ return certs;
+}
+
+int
+easy_pkcs7_verify(const char *content, size_t len,
+ const char *signature, size_t signature_len,
+ const char *anchor, int is_pkg)
+{
+ STACK_OF(X509) *cert_chain, *signers;
+ X509_STORE *store;
+ BIO *sig, *in;
+ PKCS7 *p7;
+ int i, status;
+ X509_NAME *name;
+ char *subject;
+
+ OpenSSL_add_all_algorithms();
+ ERR_load_crypto_strings();
+
+ status = -1;
+
+ if (cert_chain_file)
+ cert_chain = file_to_certs(cert_chain_file);
+ else
+ cert_chain = NULL;
+
+ store = X509_STORE_new();
+ if (store == NULL) {
+ sk_X509_free(cert_chain);
+ warnx("Failed to create certificate store");
+ return -1;
+ }
+
+ X509_STORE_load_locations(store, anchor, NULL);
+
+ in = BIO_new_mem_buf(__UNCONST(content), len);
+ sig = BIO_new_mem_buf(__UNCONST(signature), signature_len);
+ signers = NULL;
+
+ p7 = PEM_read_bio_PKCS7(sig, NULL, NULL, NULL);
+ if (p7 == NULL) {
+ warnx("Failed to parse the signature");
+ goto cleanup;
+ }
+
+ if (PKCS7_verify(p7, cert_chain, store, in, NULL, 0) != 1) {
+ warnx("Failed to verify signature");
+ goto cleanup;
+ }
+
+ signers = PKCS7_get0_signers(p7, NULL, 0);
+ if (signers == NULL) {
+ warnx("Failed to get signers");
+ goto cleanup;
+ }
+
+ if (sk_X509_num(signers) == 0) {
+ warnx("No signers found");
+ goto cleanup;
+ }
+
+ for (i = 0; i < sk_X509_num(signers); i++) {
+ /* Compute ex_xkusage */
+ X509_check_purpose(sk_X509_value(signers, i), -1, -1);
+
+ if (check_ca(sk_X509_value(signers, i))) {
+ warnx("CA keys are not valid for signatures");
+ goto cleanup;
+ }
+ if (is_pkg) {
+ if (sk_X509_value(signers, i)->ex_xkusage != pkg_key_usage) {
+ warnx("Certificate must have CODE SIGNING "
+ "and EMAIL PROTECTION property");
+ goto cleanup;
+ }
+ } else {
+ if (sk_X509_value(signers, i)->ex_xkusage != 0) {
+ warnx("Certificate must not have any property");
+ goto cleanup;
+ }
+ }
+ }
+
+ printf("Sigature ok, signed by:\n");
+
+ for (i = 0; i < sk_X509_num(signers); i++) {
+ name = X509_get_subject_name(sk_X509_value(signers, i));
+ subject = X509_NAME_oneline(name, NULL, 0);
+
+ printf("\t%s\n", subject);
+
+ OPENSSL_free(subject);
+ }
+
+ status = 0;
+
+cleanup:
+ sk_X509_free(cert_chain);
+ sk_X509_free(signers);
+ X509_STORE_free(store);
+
+ PKCS7_free(p7);
+ BIO_free(in);
+ BIO_free(sig);
+
+ return status;
+}
+
+static int
+ssl_pass_cb(char *buf, int size, int rwflag, void *u)
+{
+
+ if (EVP_read_pw_string(buf, size, "Passphrase :", 0)) {
+#if OPENSSL_VERSION >= 0x0090608fL
+ OPENSSL_cleanse(buf, size);
+#else
+ memset(buf, 0, size);
+#endif
+ return 0;
+ }
+ return strlen(buf);
+}
+
+int
+easy_pkcs7_sign(const char *content, size_t len,
+ char **signature, size_t *signature_len,
+ const char *key_file, const char *cert_file)
+{
+ FILE *f;
+ X509 *certificate;
+ STACK_OF(X509) *c, *cert_chain;
+ EVP_PKEY *private_key;
+ char *tmp_sig;
+ BIO *out, *in;
+ PKCS7 *p7;
+ int status;
+
+ OpenSSL_add_all_algorithms();
+ ERR_load_crypto_strings();
+
+ status = -1;
+ private_key = NULL;
+ cert_chain = NULL;
+ in = NULL;
+
+ c = file_to_certs(cert_file);
+
+ if (sk_X509_num(c) != 1) {
+ warnx("More then one certificate in the certificate file");
+ goto cleanup;
+ }
+ certificate = sk_X509_value(c, 0);
+
+ /* Compute ex_kusage */
+ X509_check_purpose(certificate, -1, 0);
+
+ if (check_ca(certificate)) {
+ warnx("CA keys are not valid for signatures");
+ goto cleanup;
+ }
+
+ if (certificate->ex_xkusage != pkg_key_usage) {
+ warnx("Certificate must have CODE SIGNING "
+ "and EMAIL PROTECTION property");
+ goto cleanup;
+ }
+
+ if (cert_chain_file)
+ cert_chain = file_to_certs(cert_chain_file);
+
+ if ((f = fopen(key_file, "r")) == NULL) {
+ warn("Failed to open private key file %s", key_file);
+ goto cleanup;
+ }
+ private_key = PEM_read_PrivateKey(f, NULL, ssl_pass_cb, NULL);
+ fclose(f);
+ if (private_key == NULL) {
+ warnx("Can't read private key: %s", key_file);
+ goto cleanup;
+ }
+
+ if (X509_check_private_key(certificate, private_key) != 1) {
+ warnx("The private key %s doesn't match the certificate %s",
+ key_file, cert_file);
+ goto cleanup;
+ }
+
+ in = BIO_new_mem_buf(__UNCONST(content), len);
+
+ p7 = PKCS7_sign(certificate, private_key, cert_chain, in,
+ PKCS7_DETACHED|PKCS7_NOATTR|PKCS7_BINARY);
+ if (p7 == NULL) {
+ warnx("Failed to create signature structure");
+ goto cleanup;
+ }
+
+ out = BIO_new(BIO_s_mem());
+ PEM_write_bio_PKCS7(out, p7);
+ *signature_len = BIO_get_mem_data(out, &tmp_sig);
+ *signature = xmalloc(*signature_len);
+ memcpy(*signature, tmp_sig, *signature_len);
+ BIO_free_all(out);
+
+ PKCS7_free(p7);
+
+ status = 0;
+
+cleanup:
+ sk_X509_free(c);
+ sk_X509_free(cert_chain);
+ EVP_PKEY_free(private_key);
+ BIO_free(in);
+
+ return status;
+}
--- /dev/null
+.\" $NetBSD: pkg_install.conf.5.in,v 1.1.1.13 2013/04/20 15:26:53 wiz Exp $
+.\"
+.\" Copyright (c) 2008, 2009, 2012 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by 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.
+.\"
+.\" 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.
+.\"
+.Dd February 22, 2012
+.Dt PKG_INSTALL.CONF 5
+.Os
+.Sh NAME
+.Nm pkg_install.conf
+.Nd configuration file for package installation tools
+.Sh DESCRIPTION
+The file
+.Nm
+contains system defaults for the package installation tools
+as a list of variable-value pairs.
+Each line has the format
+.Ev VARIABLE=VALUE .
+If the value consists of more than one line, each line is prefixed with
+.Ev VARIABLE= .
+.Pp
+The current value of a variable can be checked by running
+.Dl Ic pkg_admin config-var VARIABLE
+.Pp
+Some variables are overriden by environmental variables of the same name.
+Those are marked by (*).
+.Pp
+The following variables are supported:
+.Bl -tag -width indent
+.It Dv ACCEPTABLE_LICENSES
+Space-separated list of licenses packages are allowed to carry.
+License names are case-sensitive.
+.It Dv ACTIVE_FTP
+Force the use of active FTP.
+.It Dv CACHE_INDEX
+Cache directory listenings in memory.
+This avoids retransfers of the large directory index for HTTP and is
+enabled by default.
+.It Dv CERTIFICATE_ANCHOR_PKGS
+Path to the file containing the certificates used for validating
+binary packages.
+A package is trusted when a certificate chain ends in one of the
+certificates contained in this file.
+The certificates must be PEM-encoded.
+.It Dv CERTIFICATE_ANCHOR_PKGVULN
+Analogous to
+.Dv CERTIFICATE_ANCHOR_PKGS .
+The
+.Pa pkg-vulnerabilities
+is trusted when a certificate chain ends in one of the certificates
+contained in this file.
+.It Dv CERTIFICATE_CHAIN
+Path to a file containing additional certificates that can be used
+for completing certificate chains when validating binary packages or
+pkg-vulnerabilities files.
+.It Dv CHECK_LICENSE
+Check the license conditions of packages before installing them.
+Supported values are:
+.Bl -tag -width interactiveXX
+.It Dv no
+The check is not performed.
+.It Dv yes
+The check is performed if the package has license conditions set.
+.It Dv always
+Passing the license check is required.
+Missing license conditions are considered an error.
+.El
+.It Dv CHECK_END_OF_LIFE
+During vulnerability checks, consider packages that have reached end-of-life
+as vulnerable.
+This option is enabled by default.
+.It Dv CHECK_VULNERABILITIES
+Check for vulnerabilities when installing packages.
+Supported values are:
+.Bl -tag -width interactiveXX
+.It Dv never
+No check is performed.
+.It Dv always
+Passing the vulnerability check is required.
+A missing pkg-vulnerabilities file is considered an error.
+.It Dv interactive
+The user is always asked to confirm installation of vulnerable packages.
+.El
+.It Dv CONFIG_CACHE_CONNECTIONS
+Limit the global connection cache to this value.
+For FTP, this is the number of sessions without active command.
+For HTTP, this is the number of connections open with keep-alive.
+.It Dv CONFIG_CACHE_CONNECTIONS_HOST
+Like
+.Dv CONFIG_CACHE_CONNECTIONS ,
+but limit the number of connections to the host as well.
+See
+.Xr fetch 3
+for further details
+.It Dv DEFAULT_ACCEPTABLE_LICENSES
+Space-separated list of common Free and Open Source licenses packages are
+allowed to carry.
+The default value contains all OSI approved licenses in pkgsrc on the date
+pkg_install was released.
+License names are case-sensitive.
+.It Dv GPG
+Path to
+.Xr gpg 1 ,
+which can be used to verify the signature in the
+.Pa pkg-vulnerabilities
+file when running
+.Dl Ic pkg_admin check-pkg-vulnerabilities -s
+or
+.Dl Ic pkg_admin fetch-pkg-vulnerabilities -s
+It can also be used to verify and sign binary packages.
+.It Dv GPG_KEYRING_PKGVULN
+Non-default keyring to use for verifying GPG signatures of
+.Pa pkg-vulnerabilities .
+.It Dv GPG_KEYRING_SIGN
+Non-default keyring to use for signing packages with GPG.
+.It Dv GPG_KEYRING_VERIFY
+Non-default keyring to use for verifying GPG signature of packages.
+.It Dv GPG_SIGN_AS
+User-id to use for signing packages.
+.It Dv IGNORE_PROXY
+Use direct connections and ignore
+.Ev FTP_PROXY
+and
+.Ev HTTP_PROXY .
+.It Dv IGNORE_URL
+One line per advisory which should be ignored when running
+.Dl Ic pkg_admin audit
+The URL from the
+.Pa pkg-vulnerabilities
+file should be used as value.
+.It Dv PKG_DBDIR (*)
+Location of the packages database.
+This option is always overriden by the argument of the
+.Fl K
+option.
+.It Dv PKG_PATH (*)
+Search path for packages.
+The entries are separated by semicolon.
+Each entry specifies a directory or URL to search for packages.
+.It Dv PKG_REFCOUNT_DBDIR (*)
+Location of the package reference counts database directory.
+The default value is
+.Pa ${PKG_DBDIR}.refcount .
+.It Dv PKGVULNDIR
+Directory name in which the
+.Pa pkg-vulnerabilities
+file resides.
+Default is
+.Pa ${PKG_DBDIR} .
+.It Dv PKGVULNURL
+URL which is used for updating the local
+.Pa pkg-vulnerabilities
+file when running
+.Dl Ic pkg_admin fetch-pkg-vulnerabilities
+The default location is ftp.NetBSD.org using HTTP.
+.Em Note :
+Usually, only the compression type should be changed.
+Currently supported are uncompressed files and files compressed by
+.Xr bzip2 1
+.Pq Pa .bz2
+or
+.Xr gzip 1
+.Pq Pa .gz .
+.It Dv VERBOSE_NETIO
+Log details of network IO to stderr.
+.It Dv VERIFIED_INSTALLATION
+Set trust level used when installation.
+Supported values are:
+.Bl -tag -width interactiveXX
+.It Dv never
+No signature checks are performed.
+.It Dv always
+A valid signature is required.
+If the binary package can not be verified, the installation is terminated
+.It Dv trusted
+A valid signature is required.
+If the binary package can not be verified, the user is asked interactively.
+.It Dv interactive
+The user is always asked interactively when installing a package.
+.El
+.El
+.Sh FILES
+.Bl -tag -width ".Pa @SYSCONFDIR@/pkg_install.conf"
+.It Pa @SYSCONFDIR@/pkg_install.conf
+Default location for the file described in this manual page.
+.El
+.Sh SEE ALSO
+.Xr pkg_add 1 ,
+.Xr pkg_admin 1 ,
+.Xr pkg_create 1 ,
+.Xr pkg_delete 1 ,
+.Xr pkg_info 1
--- /dev/null
+/* $NetBSD: pkg_io.c,v 1.1.1.9 2010/04/23 20:54:11 joerg Exp $ */
+/*-
+ * Copyright (c) 2008, 2009 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+__RCSID("$NetBSD: pkg_io.c,v 1.1.1.9 2010/04/23 20:54:11 joerg Exp $");
+
+#include <archive.h>
+#include <archive_entry.h>
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fetch.h>
+#include <stdlib.h>
+
+#include "lib.h"
+
+struct pkg_path {
+ TAILQ_ENTRY(pkg_path) pl_link;
+ char *pl_path;
+};
+
+static char *orig_cwd, *last_toplevel;
+static TAILQ_HEAD(, pkg_path) pkg_path = TAILQ_HEAD_INITIALIZER(pkg_path);
+
+struct fetch_archive {
+ struct url *url;
+ fetchIO *fetch;
+ char buffer[32768];
+ off_t size;
+ int restart;
+};
+
+static int
+fetch_archive_open(struct archive *a, void *client_data)
+{
+ struct fetch_archive *f = client_data;
+ struct url_stat us;
+
+ f->fetch = fetchXGet(f->url, &us, fetch_flags);
+ if (f->fetch == NULL)
+ return ENOENT;
+ f->size = us.size;
+ f->restart = 1;
+ f->url->offset = 0;
+ return 0;
+}
+
+static ssize_t
+fetch_archive_read(struct archive *a, void *client_data,
+ const void **buffer)
+{
+ struct fetch_archive *f = client_data;
+ struct url_stat us;
+ ssize_t rv;
+
+ *buffer = f->buffer;
+ rv = fetchIO_read(f->fetch, f->buffer, sizeof(f->buffer));
+ if (rv > 0) {
+ f->url->offset += rv;
+ return rv;
+ }
+ if (f->restart == 0)
+ return rv;
+ if (rv == 0) {
+ if (f->size == -1)
+ return 0;
+ if (f->url->offset == f->size)
+ return 0;
+ }
+ f->restart = 0;
+ if (1) {
+ char *url = fetchStringifyURL(f->url);
+ fprintf(stderr, "Trying to reconnect %s\n", url);
+ free(url);
+ }
+ fetchIO_close(f->fetch);
+ f->fetch = fetchXGet(f->url, &us, fetch_flags);
+ if (f->fetch == NULL)
+ return -1;
+ if (us.size != f->size)
+ return -1;
+ rv = fetchIO_read(f->fetch, f->buffer, sizeof(f->buffer));
+ if (rv > 0)
+ f->url->offset += rv;
+ return rv;
+}
+
+static int
+fetch_archive_close(struct archive *a, void *client_data)
+{
+ struct fetch_archive *f = client_data;
+
+ if (f->fetch != NULL)
+ fetchIO_close(f->fetch);
+ fetchFreeURL(f->url);
+ free(f);
+ return 0;
+}
+
+static struct archive *
+open_archive_by_url(struct url *url, char **archive_name)
+{
+ struct fetch_archive *f;
+ struct archive *a;
+
+ f = xmalloc(sizeof(*f));
+ f->url = fetchCopyURL(url);
+
+ *archive_name = fetchStringifyURL(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)) {
+ free(*archive_name);
+ *archive_name = NULL;
+ archive_read_finish(a);
+ return NULL;
+ }
+
+ return a;
+}
+
+struct archive *
+open_archive(const char *url, char **archive_name)
+{
+ struct url *u;
+ struct archive *a;
+
+ *archive_name = NULL;
+
+ 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;
+ }
+ *archive_name = xstrdup(url);
+ return a;
+ }
+
+ if ((u = fetchParseURL(url)) == NULL)
+ return NULL;
+
+ a = open_archive_by_url(u, archive_name);
+
+ fetchFreeURL(u);
+ return a;
+}
+
+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_int(struct url *url, const char *pattern,
+ struct url **best_url)
+{
+ char *cur_match, *url_pattern, *best_match = NULL;
+ struct url_list ue;
+ size_t i;
+
+ if (*best_url) {
+ if ((best_match = fetchUnquoteFilename(*best_url)) == NULL)
+ return -1;
+ } else
+ best_match = NULL;
+
+ if (best_match && strip_suffix(best_match) == 0) {
+ free(best_match);
+ return -1;
+ }
+
+ for (i = 0; pattern[i] != '\0'; ++i) {
+ if (!isalnum((unsigned char)(pattern[i])) &&
+ (pattern[i]) != '-')
+ break;
+ }
+ url_pattern = xasprintf("%*.*s*", (int)i, (int)i, pattern);
+
+ fetchInitURLList(&ue);
+ if (fetchList(&ue, url, url_pattern, fetch_flags)) {
+ char *base_url;
+ base_url = fetchStringifyURL(url);
+ warnx("Can't process %s/%s: %s", base_url, url_pattern,
+ fetchLastErrString);
+ free(base_url);
+ free(url_pattern);
+ fetchFreeURLList(&ue);
+ return -1;
+ }
+ free(url_pattern);
+
+ for (i = 0; i < ue.length; ++i) {
+ cur_match = fetchUnquoteFilename(ue.urls + i);
+
+ if (cur_match == NULL) {
+ free(best_match);
+ fetchFreeURLList(&ue);
+ return -1;
+ }
+ if (strip_suffix(cur_match) == 0) {
+ free(cur_match);
+ continue;
+ }
+ if (pkg_order(pattern, cur_match, best_match) == 1) {
+ if (*best_url)
+ fetchFreeURL(*best_url);
+ *best_url = fetchCopyURL(ue.urls + i);
+ free(best_match);
+ best_match = cur_match;
+ cur_match = NULL;
+ if (*best_url == NULL) {
+ free(best_match);
+ return -1;
+ }
+ }
+ free(cur_match);
+ }
+ free(best_match);
+ fetchFreeURLList(&ue);
+ return 0;
+}
+
+void
+process_pkg_path(void)
+{
+ char cwd[PATH_MAX];
+ int relative_path;
+ struct pkg_path *pl;
+ const char *start, *next;
+ size_t len;
+
+ if (getcwd(cwd, sizeof(cwd)) == NULL)
+ errx(EXIT_FAILURE, "getcwd failed");
+
+ orig_cwd = xstrdup(cwd);
+
+ if (config_pkg_path == NULL)
+ return;
+
+ for (start = config_pkg_path; *start; start = next) {
+ len = strcspn(start, ";");
+ if (*(next = start + len) != '\0')
+ ++next;
+
+ relative_path = !IS_FULLPATH(start) && !IS_URL(start);
+ pl = xmalloc(sizeof(*pl));
+ pl->pl_path = xasprintf("%s%s%*.*s",
+ relative_path ? cwd : "", len && relative_path ? "/" : "",
+ (int)len, (int)len, start);
+ TAILQ_INSERT_TAIL(&pkg_path, pl, pl_link);
+ }
+}
+
+struct url *
+find_best_package(const char *toplevel, const char *pattern, int do_path)
+{
+ struct url *url, *best_match = NULL;
+ struct pkg_path *pl;
+
+ if (toplevel) {
+ url = fetchParseURL(last_toplevel);
+ if (url != NULL) {
+ find_best_package_int(url, pattern, &best_match);
+ /* XXX Check return value and complain */
+ fetchFreeURL(url);
+ }
+ }
+ if (!do_path)
+ return best_match;
+
+ TAILQ_FOREACH(pl, &pkg_path, pl_link) {
+ url = fetchParseURL(pl->pl_path);
+ if (url != NULL) {
+ find_best_package_int(url, pattern, &best_match);
+ /* XXX Check return value and complain */
+ fetchFreeURL(url);
+ }
+ }
+
+ return best_match;
+}
+
+struct archive *
+find_archive(const char *fname, int top_level, char **archive_name)
+{
+ struct archive *a;
+ struct url *best_match;
+ char *full_fname, *last_slash;
+ int search_path;
+
+ search_path = 0;
+ if (IS_FULLPATH(fname) || IS_URL(fname)) {
+ full_fname = xstrdup(fname);
+ } else {
+ if (strchr(fname, '/') == NULL)
+ search_path = 1;
+ full_fname = xasprintf("%s/%s", orig_cwd, fname);
+ }
+
+ last_slash = strrchr(full_fname, '/');
+ if (top_level) {
+ free(last_toplevel);
+ *last_slash = '\0';
+ last_toplevel = xstrdup(full_fname);
+ *last_slash = '/';
+ }
+
+ a = open_archive(full_fname, archive_name);
+ if (a != NULL) {
+ free(full_fname);
+ return a;
+ }
+
+ fname = last_slash + 1;
+ *last_slash = '\0';
+
+ best_match = find_best_package(full_fname, fname, 0);
+
+ if (search_path && best_match == NULL)
+ best_match = find_best_package(last_toplevel, fname, 1);
+
+ free(full_fname);
+
+ if (best_match == NULL)
+ return NULL;
+ a = open_archive_by_url(best_match, archive_name);
+ fetchFreeURL(best_match);
+ return a;
+}
--- /dev/null
+/* $NetBSD: pkg_signature.c,v 1.2 2013/09/11 12:59:19 khorben Exp $ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+__RCSID("$NetBSD: pkg_signature.c,v 1.2 2013/09/11 12:59:19 khorben Exp $");
+
+/*-
+ * Copyright (c) 2008 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#include <ctype.h>
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#ifndef NETBSD
+#include <nbcompat/sha2.h>
+#else
+#include <sha2.h>
+#endif
+#include <signal.h>
+#ifdef NETBSD
+#include <unistd.h>
+#else
+#include <nbcompat/unistd.h>
+#endif
+
+#include <archive.h>
+#include <archive_entry.h>
+
+#include "lib.h"
+
+#define HASH_FNAME "+PKG_HASH"
+#define SIGNATURE_FNAME "+PKG_SIGNATURE"
+#define GPG_SIGNATURE_FNAME "+PKG_GPG_SIGNATURE"
+
+struct signature_archive {
+ struct archive *archive;
+ off_t pkg_size;
+ size_t sign_block_len, sign_block_number, sign_cur_block;
+ char **sign_blocks;
+ unsigned char *sign_buf;
+};
+
+static void
+hash_block(unsigned char *buf, size_t buf_len,
+ char hash[SHA512_DIGEST_STRING_LENGTH])
+{
+ unsigned char digest[SHA512_DIGEST_LENGTH];
+ SHA512_CTX hash_ctx;
+ int i;
+
+ SHA512_Init(&hash_ctx);
+ SHA512_Update(&hash_ctx, buf, buf_len);
+ SHA512_Final(digest, &hash_ctx);
+ for (i = 0; i < SHA512_DIGEST_LENGTH; ++i) {
+ unsigned char c;
+
+ c = digest[i] / 16;
+ if (c < 10)
+ hash[2 * i] = '0' + c;
+ else
+ hash[2 * i] = 'a' - 10 + c;
+
+ c = digest[i] % 16;
+ if (c < 10)
+ hash[2 * i + 1] = '0' + c;
+ else
+ hash[2 * i + 1] = 'a' - 10 + c;
+ }
+ hash[2 * i] = '\0';
+}
+
+static ssize_t
+verify_signature_read_cb(struct archive *archive, void *cookie, const void **buf)
+{
+ struct signature_archive *state = cookie;
+ char hash[SHA512_DIGEST_STRING_LENGTH];
+ ssize_t len, expected;
+
+ if (state->sign_cur_block >= state->sign_block_number)
+ return 0;
+
+ /* The following works for sign_block_len > 1 */
+ if (state->sign_cur_block + 1 == state->sign_block_number)
+ expected = state->pkg_size % state->sign_block_len;
+ else
+ expected = state->sign_block_len;
+
+ len = archive_read_data(state->archive, state->sign_buf, expected);
+ if (len != expected) {
+ warnx("Short read from package");
+ return -1;
+ }
+
+ hash_block(state->sign_buf, len, hash);
+
+ if (strcmp(hash, state->sign_blocks[state->sign_cur_block]) != 0) {
+ warnx("Invalid signature of block %llu",
+ (unsigned long long)state->sign_cur_block);
+ return -1;
+ }
+ ++state->sign_cur_block;
+ *buf = state->sign_buf;
+ return len;
+}
+
+static void
+free_signature_int(struct signature_archive *state)
+{
+ size_t i;
+
+ if (state->sign_blocks != NULL) {
+ for (i = 0; i < state->sign_block_number; ++i)
+ free(state->sign_blocks[i]);
+ }
+ free(state->sign_blocks);
+ free(state->sign_buf);
+ free(state);
+}
+
+static int
+verify_signature_close_cb(struct archive *archive, void *cookie)
+{
+ struct signature_archive *state = cookie;
+
+ archive_read_finish(state->archive);
+ free_signature_int(state);
+ return 0;
+}
+
+static int
+read_file_from_archive(const char *archive_name, struct archive *archive,
+ struct archive_entry **entry,
+ const char *fname, char **content, size_t *len)
+{
+ int r;
+
+ *content = NULL;
+ *len = 0;
+
+retry:
+ if (*entry == NULL &&
+ (r = archive_read_next_header(archive, entry)) != ARCHIVE_OK) {
+ if (r == ARCHIVE_FATAL) {
+ warnx("Cannot read from archive `%s': %s",
+ archive_name, archive_error_string(archive));
+ } else {
+ warnx("Premature end of archive `%s'", archive_name);
+ }
+ *entry = NULL;
+ return -1;
+ }
+ if (strcmp(archive_entry_pathname(*entry), "//") == 0) {
+ archive_read_data_skip(archive);
+ *entry = NULL;
+ goto retry;
+ }
+
+ if (strcmp(fname, archive_entry_pathname(*entry)) != 0)
+ return 1;
+
+ if (archive_entry_size(*entry) > SSIZE_MAX - 1) {
+ warnx("Signature of archive `%s' too large to process",
+ archive_name);
+ return 1;
+ }
+ *len = archive_entry_size(*entry);
+ *content = xmalloc(*len + 1);
+
+ if (archive_read_data(archive, *content, *len) != (ssize_t)*len) {
+ warnx("Cannot read complete %s from archive `%s'", fname,
+ archive_name);
+ free(*content);
+ *len = 0;
+ *content = NULL;
+ return 1;
+ }
+ (*content)[*len] = '\0';
+ *entry = NULL;
+
+ return 0;
+}
+
+static int
+parse_hash_file(const char *hash_file, char **pkgname,
+ struct signature_archive *state)
+{
+ static const char block1[] = "pkgsrc signature\n\nversion: 1\npkgname: ";
+ static const char block2[] = "algorithm: SHA512\nblock size: ";
+ static const char block3[] = "file size: ";
+ static const char block4[] = "end pkgsrc signature\n";
+ char *next;
+ size_t i, len;
+
+ *pkgname = NULL;
+
+ if (strncmp(hash_file, block1, strlen(block1)) != 0)
+ goto cleanup;
+ hash_file += strlen(block1);
+
+ len = strcspn(hash_file, "\n");
+ *pkgname = xmalloc(len + 1);
+ memcpy(*pkgname, hash_file, len);
+ (*pkgname)[len] = '\0';
+ for (i = 0; i < len; ++i) {
+ if (!isgraph((unsigned char)(*pkgname)[i]))
+ goto cleanup;
+ }
+ hash_file += len + 1;
+
+ if (strncmp(hash_file, block2, strlen(block2)) != 0)
+ goto cleanup;
+ hash_file += strlen(block2);
+
+ errno = 0;
+ if (!isdigit((unsigned char)*hash_file))
+ goto cleanup;
+ state->sign_block_len = strtoul(hash_file, &next, 10);
+ hash_file = next;
+
+ /* Assert sane minimum block size of 1KB */
+ if (*hash_file++ != '\n' || errno == ERANGE || state->sign_block_len < 1024)
+ goto cleanup;
+
+ if (strncmp(hash_file, block3, strlen(block3)) != 0)
+ goto cleanup;
+ hash_file += strlen(block3);
+
+ errno = 0;
+ if (!isdigit((unsigned char)*hash_file))
+ goto cleanup;
+ if (/* CONSTCOND */sizeof(off_t) >= sizeof(long long))
+ state->pkg_size = strtoll(hash_file, &next, 10);
+ else
+ state->pkg_size = strtol(hash_file, &next, 10);
+ hash_file = next;
+ if (*hash_file++ != '\n' || errno == ERANGE || state->pkg_size < 1)
+ goto cleanup;
+
+ if (*hash_file++ != '\n')
+ goto cleanup;
+
+ if (state->pkg_size / state->sign_block_len > SSIZE_MAX)
+ goto cleanup;
+ state->sign_block_number = (state->pkg_size +
+ state->sign_block_len - 1) / state->sign_block_len;
+
+ state->sign_buf = xmalloc(state->sign_block_len);
+ state->sign_blocks = xcalloc(state->sign_block_number, sizeof(char *));
+
+ for (i = 0; i < state->sign_block_number; ++i) {
+ len = strspn(hash_file, "01234567889abcdef");
+ if (len != SHA512_DIGEST_LENGTH * 2 || hash_file[len] != '\n')
+ goto cleanup_hashes;
+ state->sign_blocks[i] = xmalloc(len + 1);
+ memcpy(state->sign_blocks[i], hash_file, len);
+ state->sign_blocks[i][len] = '\0';
+ hash_file += len + 1;
+ }
+
+ if (strcmp(hash_file, block4) != 0)
+ goto cleanup_hashes;
+
+ return 0;
+
+cleanup_hashes:
+ for (i = 0; i < state->sign_block_number; ++i)
+ free(state->sign_blocks[i]);
+ free(state->sign_blocks);
+ state->sign_blocks = NULL;
+
+cleanup:
+ warnx("Unknown format of hash file");
+ free(*pkgname);
+ *pkgname = NULL;
+ return -1;
+}
+
+int
+pkg_verify_signature(const char *archive_name, struct archive **archive,
+ struct archive_entry **entry, char **pkgname)
+{
+ struct signature_archive *state;
+ struct archive_entry *my_entry;
+ struct archive *a;
+ char *hash_file, *signature_file;
+ size_t hash_len, signature_len;
+ int r, has_sig;
+
+ *pkgname = NULL;
+
+ state = xcalloc(sizeof(*state), 1);
+
+ r = read_file_from_archive(archive_name, *archive, entry, HASH_FNAME,
+ &hash_file, &hash_len);
+ if (r == -1) {
+ archive_read_finish(*archive);
+ *archive = NULL;
+ free(state);
+ goto no_valid_signature;
+ } else if (r == 1) {
+ free(state);
+ goto no_valid_signature;
+ }
+
+ if (parse_hash_file(hash_file, pkgname, state))
+ goto no_valid_signature;
+
+ r = read_file_from_archive(archive_name, *archive, entry, SIGNATURE_FNAME,
+ &signature_file, &signature_len);
+ if (r == -1) {
+ archive_read_finish(*archive);
+ *archive = NULL;
+ free(state);
+ free(hash_file);
+ goto no_valid_signature;
+ } else if (r != 0) {
+ if (*entry != NULL)
+ r = read_file_from_archive(archive_name, *archive,
+ entry, GPG_SIGNATURE_FNAME,
+ &signature_file, &signature_len);
+ if (r == -1) {
+ archive_read_finish(*archive);
+ *archive = NULL;
+ free(state);
+ free(hash_file);
+ goto no_valid_signature;
+ } else if (r != 0) {
+ free(hash_file);
+ free(state);
+ goto no_valid_signature;
+ }
+ has_sig = !detached_gpg_verify(hash_file, hash_len,
+ signature_file, signature_len, gpg_keyring_verify);
+
+ free(signature_file);
+ } else {
+#ifdef HAVE_SSL
+ has_sig = !easy_pkcs7_verify(hash_file, hash_len, signature_file,
+ signature_len, certs_packages, 1);
+
+ free(signature_file);
+#else
+ warnx("No OpenSSL support compiled in, skipping signature");
+ has_sig = 0;
+ free(signature_file);
+#endif
+ }
+
+ r = archive_read_next_header(*archive, &my_entry);
+ if (r != ARCHIVE_OK) {
+ warnx("Cannot read inner package: %s",
+ archive_error_string(*archive));
+ free_signature_int(state);
+ goto no_valid_signature;
+ }
+
+ if (archive_entry_size(my_entry) != state->pkg_size) {
+ warnx("Package size doesn't match signature");
+ free_signature_int(state);
+ goto no_valid_signature;
+ }
+
+ state->archive = *archive;
+
+ a = archive_read_new();
+ archive_read_support_compression_all(a);
+ archive_read_support_format_all(a);
+ if (archive_read_open(a, state, NULL, verify_signature_read_cb,
+ verify_signature_close_cb)) {
+ warnx("Can't open signed package file");
+ archive_read_finish(a);
+ goto no_valid_signature;
+ }
+ *archive = a;
+ *entry = NULL;
+
+ return has_sig ? 0 : -1;
+
+no_valid_signature:
+ return -1;
+}
+
+int
+pkg_full_signature_check(const char *archive_name, struct archive **archive)
+{
+ struct archive_entry *entry = NULL;
+ char *pkgname;
+ int r;
+
+ if (pkg_verify_signature(archive_name, archive, &entry, &pkgname))
+ return -1;
+ if (pkgname == NULL)
+ return 0;
+
+ /* XXX read PLIST and compare pkgname */
+ while ((r = archive_read_next_header(*archive, &entry)) == ARCHIVE_OK)
+ archive_read_data_skip(*archive);
+
+ free(pkgname);
+ return r == ARCHIVE_EOF ? 0 : -1;
+}
+
+static char *
+extract_pkgname(int fd)
+{
+ package_t plist;
+ plist_t *p;
+ struct archive *a;
+ struct archive_entry *entry;
+ char *buf;
+ ssize_t len;
+ int r;
+
+ a = archive_read_new();
+ archive_read_support_compression_all(a);
+ archive_read_support_format_all(a);
+ if (archive_read_open_fd(a, fd, 1024)) {
+ warnx("Cannot open binary package: %s",
+ archive_error_string(a));
+ archive_read_finish(a);
+ return NULL;
+ }
+
+ r = archive_read_next_header(a, &entry);
+ if (r != ARCHIVE_OK) {
+ warnx("Cannot extract package name: %s",
+ r == ARCHIVE_EOF ? "EOF" : archive_error_string(a));
+ archive_read_finish(a);
+ return NULL;
+ }
+ if (strcmp(archive_entry_pathname(entry), "+CONTENTS") != 0) {
+ warnx("Invalid binary package, doesn't start with +CONTENTS");
+ archive_read_finish(a);
+ return NULL;
+ }
+ if (archive_entry_size(entry) > SSIZE_MAX - 1) {
+ warnx("+CONTENTS too large to process");
+ archive_read_finish(a);
+ return NULL;
+ }
+
+ len = archive_entry_size(entry);
+ buf = xmalloc(len + 1);
+
+ if (archive_read_data(a, buf, len) != len) {
+ warnx("Short read when extracing +CONTENTS");
+ free(buf);
+ archive_read_finish(a);
+ return NULL;
+ }
+ buf[len] = '\0';
+
+ archive_read_finish(a);
+
+ parse_plist(&plist, buf);
+ free(buf);
+ p = find_plist(&plist, PLIST_NAME);
+ if (p != NULL) {
+ buf = xstrdup(p->name);
+ } else {
+ warnx("Invalid PLIST: missing @name");
+ buf = NULL;
+ }
+ free_plist(&plist);
+
+ if (lseek(fd, 0, SEEK_SET) != 0) {
+ warn("Cannot seek in archive");
+ free(buf);
+ return NULL;
+ }
+
+ return buf;
+}
+
+static const char hash_template[] =
+"pkgsrc signature\n"
+"\n"
+"version: 1\n"
+"pkgname: %s\n"
+"algorithm: SHA512\n"
+"block size: 65536\n"
+"file size: %lld\n"
+"\n";
+
+static const char hash_trailer[] = "end pkgsrc signature\n";
+
+#ifdef HAVE_SSL
+void
+pkg_sign_x509(const char *name, const char *output, const char *key_file, const char *cert_file)
+{
+ struct archive *pkg;
+ struct archive_entry *entry, *hash_entry, *sign_entry;
+ int fd;
+ struct stat sb;
+ char *hash_file, *signature_file, *tmp, *pkgname, hash[SHA512_DIGEST_STRING_LENGTH];
+ unsigned char block[65536];
+ off_t i, size;
+ size_t block_len, signature_len;
+
+ if ((fd = open(name, O_RDONLY)) == -1)
+ err(EXIT_FAILURE, "Cannot open binary package %s", name);
+ if (fstat(fd, &sb) == -1)
+ err(EXIT_FAILURE, "Cannot stat %s", name);
+
+ entry = archive_entry_new();
+ archive_entry_copy_stat(entry, &sb);
+
+ pkgname = extract_pkgname(fd);
+ hash_file = xasprintf(hash_template, pkgname,
+ (long long)archive_entry_size(entry));
+ free(pkgname);
+
+ for (i = 0; i < archive_entry_size(entry); i += block_len) {
+ if (i + (off_t)sizeof(block) < archive_entry_size(entry))
+ block_len = sizeof(block);
+ else
+ block_len = archive_entry_size(entry) % sizeof(block);
+ if (read(fd, block, block_len) != (ssize_t)block_len)
+ err(2, "short read");
+ hash_block(block, block_len, hash);
+ tmp = xasprintf("%s%s\n", hash_file, hash);
+ free(hash_file);
+ hash_file = tmp;
+ }
+ tmp = xasprintf("%s%s", hash_file, hash_trailer);
+ free(hash_file);
+ hash_file = tmp;
+
+ if (easy_pkcs7_sign(hash_file, strlen(hash_file), &signature_file,
+ &signature_len, key_file, cert_file))
+ err(EXIT_FAILURE, "Cannot sign hash file");
+
+ lseek(fd, 0, SEEK_SET);
+
+ sign_entry = archive_entry_clone(entry);
+ hash_entry = archive_entry_clone(entry);
+ pkgname = strrchr(name, '/');
+ archive_entry_set_pathname(entry, pkgname != NULL ? pkgname + 1 : name);
+ archive_entry_set_pathname(hash_entry, HASH_FNAME);
+ archive_entry_set_pathname(sign_entry, SIGNATURE_FNAME);
+ archive_entry_set_size(hash_entry, strlen(hash_file));
+ archive_entry_set_size(sign_entry, signature_len);
+
+ pkg = archive_write_new();
+ archive_write_set_compression_none(pkg);
+ archive_write_set_format_ar_bsd(pkg);
+ archive_write_open_filename(pkg, output);
+
+ archive_write_header(pkg, hash_entry);
+ archive_write_data(pkg, hash_file, strlen(hash_file));
+ archive_write_finish_entry(pkg);
+ archive_entry_free(hash_entry);
+
+ archive_write_header(pkg, sign_entry);
+ archive_write_data(pkg, signature_file, signature_len);
+ archive_write_finish_entry(pkg);
+ archive_entry_free(sign_entry);
+
+ size = archive_entry_size(entry);
+ archive_write_header(pkg, entry);
+
+ for (i = 0; i < size; i += block_len) {
+ if (i + (off_t)sizeof(block) < size)
+ block_len = sizeof(block);
+ else
+ block_len = size % sizeof(block);
+ if (read(fd, block, block_len) != (ssize_t)block_len)
+ err(2, "short read");
+ archive_write_data(pkg, block, block_len);
+ }
+ archive_write_finish_entry(pkg);
+ archive_entry_free(entry);
+
+ archive_write_finish(pkg);
+
+ close(fd);
+
+ exit(0);
+}
+#endif
+
+void
+pkg_sign_gpg(const char *name, const char *output)
+{
+ struct archive *pkg;
+ struct archive_entry *entry, *hash_entry, *sign_entry;
+ int fd;
+ struct stat sb;
+ char *hash_file, *signature_file, *tmp, *pkgname, hash[SHA512_DIGEST_STRING_LENGTH];
+ unsigned char block[65536];
+ off_t i, size;
+ size_t block_len, signature_len;
+
+ if ((fd = open(name, O_RDONLY)) == -1)
+ err(EXIT_FAILURE, "Cannot open binary package %s", name);
+ if (fstat(fd, &sb) == -1)
+ err(EXIT_FAILURE, "Cannot stat %s", name);
+
+ entry = archive_entry_new();
+ archive_entry_copy_stat(entry, &sb);
+
+ pkgname = extract_pkgname(fd);
+ hash_file = xasprintf(hash_template, pkgname,
+ (long long)archive_entry_size(entry));
+ free(pkgname);
+
+ for (i = 0; i < archive_entry_size(entry); i += block_len) {
+ if (i + (off_t)sizeof(block) < archive_entry_size(entry))
+ block_len = sizeof(block);
+ else
+ block_len = archive_entry_size(entry) % sizeof(block);
+ if (read(fd, block, block_len) != (ssize_t)block_len)
+ err(2, "short read");
+ hash_block(block, block_len, hash);
+ tmp = xasprintf("%s%s\n", hash_file, hash);
+ free(hash_file);
+ hash_file = tmp;
+ }
+ tmp = xasprintf("%s%s", hash_file, hash_trailer);
+ free(hash_file);
+ hash_file = tmp;
+
+ if (detached_gpg_sign(hash_file, strlen(hash_file), &signature_file,
+ &signature_len, gpg_keyring_sign, gpg_sign_as))
+ err(EXIT_FAILURE, "Cannot sign hash file");
+
+ lseek(fd, 0, SEEK_SET);
+
+ sign_entry = archive_entry_clone(entry);
+ hash_entry = archive_entry_clone(entry);
+ pkgname = strrchr(name, '/');
+ archive_entry_set_pathname(entry, pkgname != NULL ? pkgname + 1 : name);
+ archive_entry_set_pathname(hash_entry, HASH_FNAME);
+ archive_entry_set_pathname(sign_entry, GPG_SIGNATURE_FNAME);
+ archive_entry_set_size(hash_entry, strlen(hash_file));
+ archive_entry_set_size(sign_entry, signature_len);
+
+ pkg = archive_write_new();
+ archive_write_set_compression_none(pkg);
+ archive_write_set_format_ar_bsd(pkg);
+ archive_write_open_filename(pkg, output);
+
+ archive_write_header(pkg, hash_entry);
+ archive_write_data(pkg, hash_file, strlen(hash_file));
+ archive_write_finish_entry(pkg);
+ archive_entry_free(hash_entry);
+
+ archive_write_header(pkg, sign_entry);
+ archive_write_data(pkg, signature_file, signature_len);
+ archive_write_finish_entry(pkg);
+ archive_entry_free(sign_entry);
+
+ size = archive_entry_size(entry);
+ archive_write_header(pkg, entry);
+
+ for (i = 0; i < size; i += block_len) {
+ if (i + (off_t)sizeof(block) < size)
+ block_len = sizeof(block);
+ else
+ block_len = size % sizeof(block);
+ if (read(fd, block, block_len) != (ssize_t)block_len)
+ err(2, "short read");
+ archive_write_data(pkg, block, block_len);
+ }
+ archive_write_finish_entry(pkg);
+ archive_entry_free(entry);
+
+ archive_write_finish(pkg);
+
+ close(fd);
+
+ exit(0);
+}
--- /dev/null
+.\" $NetBSD: pkg_summary.5,v 1.4 2012/02/19 17:49:09 tron Exp $
+.\"
+.\" Copyright (c) 2006 The NetBSD Foundation
+.\"
+.\" 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. 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 AND ITS 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 REGENTS 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.
+.\"
+.Dd April 11, 2009
+.Dt PKG_SUMMARY 5
+.Os
+.Sh NAME
+.Nm pkg_summary
+.Nd summary of binary package repository
+.Sh DESCRIPTION
+The file
+.Nm
+contains information about each package in a binary package
+repository as a list of variable-value pairs.
+The variables describing different packages are separated by one empty
+line.
+Each line has the format
+.Ev VARIABLE=VALUE .
+If the value consists of more than one line, each line is prefixed with
+.Ev VARIABLE= .
+Multi-line variables are guaranteed to be in consecutive lines.
+.Pp
+The following variables are used:
+.Bl -tag -width indent
+.It Ev BUILD_DATE
+(required) The date and time when the package was built.
+.It Ev CATEGORIES
+(required) A list of categories which this package fits in, separated by
+space.
+.It Ev COMMENT
+(required) A one-line description of the package.
+.It Ev CONFLICTS
+(optional) A list of dewey patterns of packages the package conflicts
+with, one per line.
+If missing, this package has no conflicts.
+.It Ev DEPENDS
+(optional) A list of dewey patterns of packages the package depends
+on, one per line.
+If missing, this package has no dependencies.
+.It Ev DESCRIPTION
+(required) A more detailed description of the package.
+.\" DIGEST
+.It Ev FILE_CKSUM
+(optional) A checksum type supported by
+.Xr digest 1
+and checksum separated by space character.
+.It Ev FILE_NAME
+(optional) The name of the binary package file.
+If not given,
+.Pa PKGNAME.tgz
+can be assumed.
+.It Ev FILE_SIZE
+(optional) The size of the binary package file, in bytes.
+.It Ev HOMEPAGE
+(optional) A URL where more information about the package can be found.
+.It Ev LICENSE
+(optional) The type of license this package is distributed under.
+If empty or missing, it is OSI-approved.
+.It Ev MACHINE_ARCH
+(required) The architecture on which the package was compiled.
+.It Ev OPSYS
+(required) The operating system on which the package was compiled.
+.It Ev OS_VERSION
+(required) The version of the operating system on which the package
+was compiled.
+.It Ev PKG_OPTIONS
+(optional) Any options selected to compile this package.
+If missing, the package does not support options.
+.It Ev PKGNAME
+(required) The name of the package.
+.It Ev PKGPATH
+(required) The path of the package directory within pkgsrc.
+.It Ev PKGTOOLS_VERSION
+(required) The version of the package tools used to create the package.
+.It Ev PREV_PKGPATH
+(optional) The previous path of the package directory within pkgsrc when
+a package was moved.
+(See
+.Ev SUPERSEDES
+below for a renamed package.)
+.It Ev PROVIDES
+(optional) A list of shared libraries provided by the package,
+including major version number, one per line.
+If missing, this package does not provide shared libraries.
+.It Ev REQUIRES
+(optional) A list of shared libraries needed by the package, including
+major version number, one per line.
+If missing, this package does not require shared libraries.
+.It Ev SIZE_PKG
+(required) The size of the package when installed, in bytes.
+.It Ev SUPERSEDES
+(optional) A list of dewey patterns of previous packages this
+package replaces, one per line.
+This is used for package renaming.
+.El
+.Pp
+The
+.Nm pkg_summary
+file can be generated using the
+.Xr pkg_info 1
+.Fl X
+option.
+For example, the following will list this data for all installed packages:
+.Pp
+.Dl "pkg_info -X -a"
+.Sh SEE ALSO
+.Xr digest 1 ,
+.Xr pkg_info 1
+.Sh HISTORY
+The
+.Nm pkg_summary
+format was first officially documented in April 2006.
--- /dev/null
+/* $NetBSD: pkgdb.c,v 1.1.1.8 2010/04/23 20:54:11 joerg Exp $ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+__RCSID("$NetBSD: pkgdb.c,v 1.1.1.8 2010/04/23 20:54:11 joerg Exp $");
+
+/*-
+ * Copyright (c) 1999-2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Hubert Feyrer <hubert@feyrer.de>.
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+#ifdef NETBSD
+#include <db.h>
+#else
+#include <nbcompat/db.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_STDARG_H
+#include <stdarg.h>
+#endif
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "lib.h"
+
+#define PKGDB_FILE "pkgdb.byfile.db" /* indexed by filename */
+
+/*
+ * Where we put logging information by default if PKG_DBDIR is unset.
+ */
+#ifndef DEF_LOG_DIR
+#define DEF_LOG_DIR "/var/db/pkg"
+#endif
+
+/* just in case we change the environment variable name */
+#define PKG_DBDIR "PKG_DBDIR"
+
+static DB *pkgdbp;
+static char pkgdb_dir_default[] = DEF_LOG_DIR;
+static char *pkgdb_dir = pkgdb_dir_default;
+static int pkgdb_dir_prio = 0;
+
+/*
+ * Return name of cache file in the buffer that was passed.
+ */
+char *
+pkgdb_get_database(void)
+{
+ return xasprintf("%s/%s", pkgdb_get_dir(), PKGDB_FILE);
+}
+
+/*
+ * Open the pkg-database
+ * Return value:
+ * 1: everything ok
+ * 0: error
+ */
+int
+pkgdb_open(int mode)
+{
+ BTREEINFO info;
+ char *cachename;
+
+ /* try our btree format first */
+ info.flags = 0;
+ info.cachesize = 2*1024*1024;
+ info.maxkeypage = 0;
+ info.minkeypage = 0;
+ info.psize = 4096;
+ info.compare = NULL;
+ info.prefix = NULL;
+ info.lorder = 0;
+ cachename = pkgdb_get_database();
+ pkgdbp = (DB *) dbopen(cachename,
+ (mode == ReadOnly) ? O_RDONLY : O_RDWR | O_CREAT,
+ 0644, DB_BTREE, (void *) &info);
+ free(cachename);
+ return (pkgdbp != NULL);
+}
+
+/*
+ * Close the pkg database
+ */
+void
+pkgdb_close(void)
+{
+ if (pkgdbp != NULL) {
+ (void) (*pkgdbp->close) (pkgdbp);
+ pkgdbp = NULL;
+ }
+}
+
+/*
+ * Store value "val" with key "key" in database
+ * Return value is as from ypdb_store:
+ * 0: ok
+ * 1: key already present
+ * -1: some other error, see errno
+ */
+int
+pkgdb_store(const char *key, const char *val)
+{
+ DBT keyd, vald;
+
+ if (pkgdbp == NULL)
+ return -1;
+
+ keyd.data = __UNCONST(key);
+ keyd.size = strlen(key) + 1;
+ vald.data = __UNCONST(val);
+ vald.size = strlen(val) + 1;
+
+ if (keyd.size > MaxPathSize || vald.size > MaxPathSize)
+ return -1;
+
+ return (*pkgdbp->put) (pkgdbp, &keyd, &vald, R_NOOVERWRITE);
+}
+
+/*
+ * Recall value for given key
+ * Return value:
+ * NULL if some error occurred or value for key not found (check errno!)
+ * String for "value" else
+ */
+char *
+pkgdb_retrieve(const char *key)
+{
+ DBT keyd, vald;
+ int status;
+ char *eos;
+ static int corruption_warning;
+
+ if (pkgdbp == NULL)
+ return NULL;
+
+ keyd.data = __UNCONST(key);
+ keyd.size = strlen(key) + 1;
+ errno = 0; /* to be sure it's 0 if the key doesn't match anything */
+
+ vald.data = (void *)NULL;
+ vald.size = 0;
+ status = (*pkgdbp->get) (pkgdbp, &keyd, &vald, 0);
+ if (status)
+ return NULL;
+ eos = memchr(vald.data, 0, vald.size);
+ if (eos == NULL || eos + 1 != (char *)vald.data + vald.size) {
+ if (!corruption_warning) {
+ warnx("pkgdb corrupted, please run ``pkg_admin rebuild''");
+ corruption_warning = 1;
+ }
+ return NULL;
+ }
+
+ return vald.data;
+}
+
+/* dump contents of the database to stdout */
+int
+pkgdb_dump(void)
+{
+ DBT key;
+ DBT val;
+ int type;
+
+ if (pkgdb_open(ReadOnly)) {
+ for (type = R_FIRST ; (*pkgdbp->seq)(pkgdbp, &key, &val, type) == 0 ; type = R_NEXT) {
+ printf("file: %.*s pkg: %.*s\n",
+ (int) key.size, (char *) key.data,
+ (int) val.size, (char *) val.data);
+ }
+ pkgdb_close();
+ return 0;
+ } else
+ return -1;
+}
+
+/*
+ * Remove data set from pkgdb
+ * Return value as ypdb_delete:
+ * 0: everything ok
+ * 1: key not present
+ * -1: some error occurred (see errno)
+ */
+int
+pkgdb_remove(const char *key)
+{
+ DBT keyd;
+
+ if (pkgdbp == NULL)
+ return -1;
+
+ keyd.data = __UNCONST(key);
+ keyd.size = strlen(key) + 1;
+ if (keyd.size > MaxPathSize)
+ return -1;
+
+ return (*pkgdbp->del) (pkgdbp, &keyd, 0);
+}
+
+/*
+ * Remove any entry from the cache which has a data field of `pkg'.
+ * Return value:
+ * 1: everything ok
+ * 0: error
+ */
+int
+pkgdb_remove_pkg(const char *pkg)
+{
+ DBT data;
+ DBT key;
+ int type;
+ int ret;
+ size_t cc;
+ char *cachename;
+
+ if (pkgdbp == NULL) {
+ return 0;
+ }
+ cachename = pkgdb_get_database();
+ cc = strlen(pkg);
+ for (ret = 1, type = R_FIRST; (*pkgdbp->seq)(pkgdbp, &key, &data, type) == 0 ; type = R_NEXT) {
+ if ((cc + 1) == data.size && strncmp(data.data, pkg, cc) == 0) {
+ if (Verbose) {
+ printf("Removing file `%s' from %s\n", (char *)key.data, cachename);
+ }
+ switch ((*pkgdbp->del)(pkgdbp, &key, 0)) {
+ case -1:
+ warn("Error removing `%s' from %s", (char *)key.data, cachename);
+ ret = 0;
+ break;
+ case 1:
+ warn("Key `%s' not present in %s", (char *)key.data, cachename);
+ ret = 0;
+ break;
+
+ }
+ }
+ }
+ free(cachename);
+ return ret;
+}
+
+/*
+ * Return the location of the package reference counts database directory.
+ */
+char *
+pkgdb_refcount_dir(void)
+{
+ static char buf[MaxPathSize];
+ char *tmp;
+
+ if ((tmp = getenv(PKG_REFCOUNT_DBDIR_VNAME)) != NULL)
+ strlcpy(buf, tmp, sizeof(buf));
+ else
+ snprintf(buf, sizeof(buf), "%s.refcount", pkgdb_get_dir());
+ return buf;
+}
+
+/*
+ * Return directory where pkgdb is stored
+ */
+const char *
+pkgdb_get_dir(void)
+{
+
+ return pkgdb_dir;
+}
+
+/*
+ * Set the first place we look for where pkgdb is stored.
+ */
+void
+pkgdb_set_dir(const char *dir, int prio)
+{
+
+ if (prio < pkgdb_dir_prio)
+ return;
+
+ pkgdb_dir_prio = prio;
+
+ if (dir == pkgdb_dir)
+ return;
+ if (pkgdb_dir != pkgdb_dir_default)
+ free(pkgdb_dir);
+ pkgdb_dir = xstrdup(dir);
+}
+
+char *
+pkgdb_pkg_dir(const char *pkg)
+{
+ return xasprintf("%s/%s", pkgdb_get_dir(), pkg);
+}
+
+char *
+pkgdb_pkg_file(const char *pkg, const char *file)
+{
+ return xasprintf("%s/%s/%s", pkgdb_get_dir(), pkg, file);
+}
--- /dev/null
+.\" $NetBSD: pkgsrc.7,v 1.1.1.1 2008/09/30 19:00:27 joerg Exp $
+.\"
+.\" Copyright (c) 2007 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by 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.
+.\"
+.\" 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.
+.\"
+.Dd March 2, 2007
+.Dt PKGSRC 7
+.Os
+.Sh NAME
+.Nm pkgsrc
+.Nd NetBSD packages collection (framework for third-party software)
+.Sh DESCRIPTION
+The
+.Nx
+Packages Collection (pkgsrc) is a framework for building and
+maintaining third-party software on
+.Nx
+and other
+.Ux Ns -like
+systems.
+It is used to enable freely available software to be configured
+and built easily on supported platforms.
+.Pp
+Tools are available to install ready-to-use packages and to perform
+various administrative tasks for the package system.
+.Sh SEE ALSO
+.Xr pkg_add 1 ,
+.Xr pkg_delete 1 ,
+.Xr pkg_info 1 ,
+.Pa http://www.netbsd.org/docs/pkgsrc/
--- /dev/null
+/* $NetBSD: plist.c,v 1.1.1.5 2009/08/06 16:55:28 joerg Exp $ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+__RCSID("$NetBSD: plist.c,v 1.1.1.5 2009/08/06 16:55:28 joerg Exp $");
+
+/*
+ * 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
+ *
+ * General packing list routines.
+ *
+ */
+
+/*-
+ * Copyright (c) 2008, 2009 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 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 "lib.h"
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+#ifndef NETBSD
+#include <nbcompat/md5.h>
+#else
+#include <md5.h>
+#endif
+
+static int delete_with_parents(const char *, Boolean, Boolean);
+
+/* This struct defines a plist command type */
+typedef struct cmd_t {
+ const char *c_s; /* string to recognise */
+ pl_ent_t c_type; /* type of command */
+ int c_argc; /* # of arguments */
+ int c_subst; /* can substitute real prefix */
+} cmd_t;
+
+/* Commands to recognise */
+static const cmd_t cmdv[] = {
+ {"cwd", PLIST_CWD, 1, 1},
+ {"src", PLIST_SRC, 1, 1},
+ {"exec", PLIST_CMD, 1, 0},
+ {"unexec", PLIST_UNEXEC, 1, 0},
+ {"mode", PLIST_CHMOD, 1, 0},
+ {"owner", PLIST_CHOWN, 1, 0},
+ {"group", PLIST_CHGRP, 1, 0},
+ {"comment", PLIST_COMMENT, 1, 0},
+ {"ignore", PLIST_IGNORE, 0, 0},
+ {"name", PLIST_NAME, 1, 0},
+ {"display", PLIST_DISPLAY, 1, 0},
+ {"pkgdep", PLIST_PKGDEP, 1, 0},
+ {"pkgcfl", PLIST_PKGCFL, 1, 0},
+ {"pkgdir", PLIST_PKGDIR, 1, 0},
+ {"dirrm", PLIST_DIR_RM, 1, 0},
+ {"option", PLIST_OPTION, 1, 0},
+ {"blddep", PLIST_BLDDEP, 1, 0},
+ {NULL, FAIL, 0, 0}
+};
+
+/*
+ * Add an item to the end of a packing list
+ */
+void
+add_plist(package_t *p, pl_ent_t type, const char *arg)
+{
+ plist_t *tmp;
+
+ tmp = new_plist_entry();
+ tmp->name = (arg == NULL) ? NULL : xstrdup(arg);
+ tmp->type = type;
+ if (!p->head) {
+ p->head = p->tail = tmp;
+ } else {
+ tmp->prev = p->tail;
+ p->tail->next = tmp;
+ p->tail = tmp;
+ }
+}
+
+/*
+ * Add an item to the start of a packing list
+ */
+void
+add_plist_top(package_t *p, pl_ent_t type, const char *arg)
+{
+ plist_t *tmp;
+
+ tmp = new_plist_entry();
+ tmp->name = (arg == NULL) ? NULL : xstrdup(arg);
+ tmp->type = type;
+ if (!p->head) {
+ p->head = p->tail = tmp;
+ } else {
+ tmp->next = p->head;
+ p->head->prev = tmp;
+ p->head = tmp;
+ }
+}
+
+/*
+ * Return the last (most recent) entry in a packing list
+ */
+plist_t *
+last_plist(package_t *p)
+{
+ return p->tail;
+}
+
+/*
+ * Mark all items in a packing list to prevent iteration over them
+ */
+void
+mark_plist(package_t *pkg)
+{
+ plist_t *pp;
+
+ for (pp = pkg->head; pp; pp = pp->next) {
+ pp->marked = TRUE;
+ }
+}
+
+/*
+ * Find a given item in a packing list and, if so, return it (else NULL)
+ */
+plist_t *
+find_plist(package_t *pkg, pl_ent_t type)
+{
+ plist_t *pp;
+
+ for (pp = pkg->head; pp && pp->type != type; pp = pp->next) {
+ }
+ return pp;
+}
+
+/*
+ * Look for a specific boolean option argument in the list
+ */
+char *
+find_plist_option(package_t *pkg, const char *name)
+{
+ plist_t *p;
+
+ for (p = pkg->head; p; p = p->next) {
+ if (p->type == PLIST_OPTION
+ && strcmp(p->name, name) == 0) {
+ return p->name;
+ }
+ }
+
+ return (char *) NULL;
+}
+
+/*
+ * Delete plist item 'type' in the list (if 'name' is non-null, match it
+ * too.) If 'all' is set, delete all items, not just the first occurance.
+ */
+void
+delete_plist(package_t *pkg, Boolean all, pl_ent_t type, char *name)
+{
+ plist_t *p = pkg->head;
+
+ while (p) {
+ plist_t *pnext = p->next;
+
+ if (p->type == type && (!name || !strcmp(name, p->name))) {
+ free(p->name);
+ if (p->prev)
+ p->prev->next = pnext;
+ else
+ pkg->head = pnext;
+ if (pnext)
+ pnext->prev = p->prev;
+ else
+ pkg->tail = p->prev;
+ free(p);
+ if (!all)
+ return;
+ p = pnext;
+ } else
+ p = p->next;
+ }
+}
+
+/*
+ * Allocate a new packing list entry, and return a pointer to it.
+ */
+plist_t *
+new_plist_entry(void)
+{
+ return xcalloc(1, sizeof(plist_t));
+}
+
+/*
+ * Free an entire packing list
+ */
+void
+free_plist(package_t *pkg)
+{
+ plist_t *p = pkg->head;
+
+ while (p) {
+ plist_t *p1 = p->next;
+
+ free(p->name);
+ free(p);
+ p = p1;
+ }
+ pkg->head = pkg->tail = NULL;
+}
+
+/*
+ * For an ASCII string denoting a plist command, return its code and
+ * optionally its argument(s)
+ */
+static int
+plist_cmd(const char *s, char **arg)
+{
+ const cmd_t *cmdp;
+ const char *cp, *sp;
+ char *sp2;
+
+ sp = NULL; /* Older GCC can't detect that the loop is executed */
+
+ for (cmdp = cmdv; cmdp->c_s; ++cmdp) {
+ for (sp = s, cp = cmdp->c_s; *sp && *cp; ++cp, ++sp)
+ if (*sp != *cp)
+ break;
+ if (*cp == '\0')
+ break;
+ }
+
+ if (cmdp->c_s == NULL || arg == NULL)
+ return cmdp->c_type;
+
+ while (isspace((unsigned char)*sp))
+ ++sp;
+ *arg = xstrdup(sp);
+ if (*sp) {
+ sp2 = *arg + strlen(*arg) - 1;
+ /*
+ * The earlier loop ensured that at least one non-whitespace
+ * is in the string.
+ */
+ while (isspace((unsigned char)*sp2))
+ --sp2;
+ sp2[1] = '\0';
+ }
+ return cmdp->c_type;
+}
+
+/*
+ * Parse a packaging list from a memory buffer.
+ */
+void
+parse_plist(package_t *pkg, const char *buf)
+{
+ int cmd;
+ char *line, *cp;
+ const char *eol, *next;
+ size_t len;
+
+ pkg->head = NULL;
+ pkg->tail = NULL;
+
+ for (; *buf; buf = next) {
+ /* Until add_plist can deal with trailing whitespace. */
+ if ((eol = strchr(buf, '\n')) != NULL) {
+ next = eol + 1;
+ len = eol - buf;
+ } else {
+ len = strlen(buf);
+ next = buf + len;
+ }
+
+ while (len && isspace((unsigned char)buf[len - 1]))
+ --len;
+
+ if (len == 0)
+ continue;
+
+ line = xmalloc(len + 1);
+ memcpy(line, buf, len);
+ line[len] = '\0';
+
+ if (*(cp = line) == CMD_CHAR) {
+ if ((cmd = plist_cmd(line + 1, &cp)) == FAIL) {
+ warnx("Unrecognised PLIST command `%s'", line);
+ continue;
+ }
+ if (*cp == '\0') {
+ free(cp);
+ cp = NULL;
+ }
+ } else {
+ cmd = PLIST_FILE;
+ }
+ add_plist(pkg, cmd, cp);
+ free(cp);
+ }
+}
+
+/*
+ * Read a packing list from a file
+ */
+void
+append_plist(package_t *pkg, FILE * fp)
+{
+ char pline[MaxPathSize];
+ char *cp;
+ int cmd;
+ int len;
+ int free_cp;
+
+ while (fgets(pline, MaxPathSize, fp) != (char *) NULL) {
+ for (len = strlen(pline); len &&
+ isspace((unsigned char) pline[len - 1]);) {
+ pline[--len] = '\0';
+ }
+ if (len == 0) {
+ continue;
+ }
+ free_cp = 0;
+ if (*(cp = pline) == CMD_CHAR) {
+ if ((cmd = plist_cmd(pline + 1, &cp)) == FAIL) {
+ warnx("Unrecognised PLIST command `%s'", pline);
+ continue;
+ }
+ if (*cp == '\0') {
+ free(cp);
+ cp = NULL;
+ }
+ free_cp = 1;
+ } else {
+ cmd = PLIST_FILE;
+ }
+ add_plist(pkg, cmd, cp);
+ if (free_cp)
+ free(cp);
+ }
+}
+
+void
+read_plist(package_t *pkg, FILE * fp)
+{
+ pkg->head = NULL;
+ pkg->tail = NULL;
+
+ append_plist(pkg, fp);
+}
+
+/*
+ * Write a packing list to a file, converting commands to ASCII equivs
+ */
+void
+write_plist(package_t *pkg, FILE * fp, char *realprefix)
+{
+ plist_t *p;
+ const cmd_t *cmdp;
+
+ for (p = pkg->head; p; p = p->next) {
+ if (p->type == PLIST_FILE) {
+ /* Fast-track files - these are the most common */
+ (void) fprintf(fp, "%s\n", p->name);
+ continue;
+ }
+ for (cmdp = cmdv; cmdp->c_type != FAIL && cmdp->c_type != p->type; cmdp++) {
+ }
+ if (cmdp->c_type == FAIL) {
+ warnx("Unknown PLIST command type %d (%s)", p->type, p->name);
+ } else if (cmdp->c_argc == 0) {
+ (void) fprintf(fp, "%c%s\n", CMD_CHAR, cmdp->c_s);
+ } else if (cmdp->c_subst && realprefix) {
+ (void) fprintf(fp, "%c%s %s\n", CMD_CHAR, cmdp->c_s, realprefix);
+ } else {
+ (void) fprintf(fp, "%c%s %s\n", CMD_CHAR, cmdp->c_s,
+ (p->name) ? p->name : "");
+ }
+ }
+}
+
+/*
+ * Like write_plist, but compute memory string.
+ */
+void
+stringify_plist(package_t *pkg, char **real_buf, size_t *real_len,
+ const char *realprefix)
+{
+ plist_t *p;
+ const cmd_t *cmdp;
+ char *buf;
+ size_t len;
+ int item_len;
+
+ /* Pass One: compute output size only. */
+ len = 0;
+
+ for (p = pkg->head; p; p = p->next) {
+ if (p->type == PLIST_FILE) {
+ len += strlen(p->name) + 1;
+ continue;
+ }
+ for (cmdp = cmdv; cmdp->c_type != FAIL && cmdp->c_type != p->type; cmdp++) {
+ }
+ if (cmdp->c_type == FAIL)
+ continue;
+ if (cmdp->c_argc == 0)
+ len += 1 + strlen(cmdp->c_s) + 1;
+ else if (cmdp->c_subst && realprefix)
+ len += 1 + strlen(cmdp->c_s) + 1 + strlen(realprefix) + 1;
+ else
+ len += 1 + strlen(cmdp->c_s) + 1 + strlen(p->name ? p->name : "") + 1;
+ }
+
+ /* Pass Two: build actual string. */
+ buf = xmalloc(len + 1);
+ *real_buf = buf;
+ *real_len = len;
+ ++len;
+
+#define UPDATE_LEN \
+do { \
+ if (item_len < 0 || (size_t)item_len > len) \
+ errx(2, "Size computation failed, aborted."); \
+ buf += item_len; \
+ len -= item_len; \
+} while (/* CONSTCOND */0)
+
+ for (p = pkg->head; p; p = p->next) {
+ if (p->type == PLIST_FILE) {
+ /* Fast-track files - these are the most common */
+ item_len = snprintf(buf, len, "%s\n", p->name);
+ UPDATE_LEN;
+ continue;
+ }
+ for (cmdp = cmdv; cmdp->c_type != FAIL && cmdp->c_type != p->type; cmdp++) {
+ }
+ if (cmdp->c_type == FAIL) {
+ warnx("Unknown PLIST command type %d (%s)", p->type, p->name);
+ } else if (cmdp->c_argc == 0) {
+ item_len = snprintf(buf, len, "%c%s\n", CMD_CHAR, cmdp->c_s);
+ UPDATE_LEN;
+ } else if (cmdp->c_subst && realprefix) {
+ item_len = snprintf(buf, len, "%c%s %s\n", CMD_CHAR, cmdp->c_s, realprefix);
+ UPDATE_LEN;
+ } else {
+ item_len = snprintf(buf, len, "%c%s %s\n", CMD_CHAR, cmdp->c_s,
+ (p->name) ? p->name : "");
+ UPDATE_LEN;
+ }
+ }
+
+ if (len != 1)
+ errx(2, "Size computation failed, aborted.");
+}
+
+/*
+ * Delete the results of a package installation.
+ *
+ * This is here rather than in the pkg_delete code because pkg_add needs to
+ * run it too in cases of failure.
+ */
+int
+delete_package(Boolean ign_err, package_t *pkg, Boolean NoDeleteFiles,
+ const char *destdir)
+{
+ plist_t *p;
+ const char *last_file = "";
+ int fail = SUCCESS;
+ Boolean preserve;
+ char tmp[MaxPathSize];
+ const char *prefix = NULL, *name = NULL;
+
+ if (!pkgdb_open(ReadWrite)) {
+ err(EXIT_FAILURE, "cannot open pkgdb");
+ }
+
+ preserve = find_plist_option(pkg, "preserve") ? TRUE : FALSE;
+
+ for (p = pkg->head; p; p = p->next) {
+ switch (p->type) {
+ case PLIST_NAME:
+ name = p->name;
+ break;
+ case PLIST_CWD:
+ if (prefix == NULL)
+ prefix = p->name;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (name == NULL || prefix == NULL)
+ errx(EXIT_FAILURE, "broken PLIST");
+
+ /*
+ * Remove database entries first, directory removal is done
+ * in the main loop below.
+ */
+ for (p = pkg->head; p; p = p->next) {
+ if (p->type == PLIST_PKGDIR)
+ delete_pkgdir(name, prefix, p->name);
+ }
+
+ for (p = pkg->head; p; p = p->next) {
+ switch (p->type) {
+ case PLIST_NAME:
+ /* Handled already */
+ break;
+
+ case PLIST_PKGDIR:
+ case PLIST_DIR_RM:
+ (void) snprintf(tmp, sizeof(tmp), "%s/%s",
+ prefix, p->name);
+ if (has_pkgdir(tmp))
+ continue;
+ (void) snprintf(tmp, sizeof(tmp), "%s%s%s/%s",
+ destdir ? destdir : "", destdir ? "/" : "",
+ prefix, p->name);
+ if (!fexists(tmp)) {
+ if (p->type == PLIST_PKGDIR)
+ warnx("Directory `%s' disappeared, skipping", tmp);
+ } else if (!isdir(tmp)) {
+ warnx("attempting to delete a file `%s' as a directory\n"
+ "this packing list is incorrect - ignoring delete request", tmp);
+ } else if (delete_with_parents(tmp, ign_err, TRUE))
+ fail = FAIL;
+ break;
+
+ case PLIST_IGNORE:
+ p = p->next;
+ break;
+
+ case PLIST_UNEXEC:
+ if (NoDeleteFiles)
+ break;
+ format_cmd(tmp, sizeof(tmp), p->name, prefix, last_file);
+ printf("Executing `%s'\n", tmp);
+ if (!Fake && system(tmp)) {
+ warnx("unexec command for `%s' failed", tmp);
+ fail = FAIL;
+ }
+ break;
+
+ case PLIST_FILE:
+ last_file = p->name;
+ (void) snprintf(tmp, sizeof(tmp), "%s%s%s/%s",
+ destdir ? destdir : "", destdir ? "/" : "",
+ prefix, p->name);
+ if (isdir(tmp)) {
+ warnx("attempting to delete directory `%s' as a file\n"
+ "this packing list is incorrect - ignoring delete request", tmp);
+ } else {
+ int restored = 0; /* restored from preserve? */
+
+ if (p->next && p->next->type == PLIST_COMMENT) {
+ if (strncmp(p->next->name, CHECKSUM_HEADER, ChecksumHeaderLen) == 0) {
+ char *cp, buf[LegibleChecksumLen];
+
+ if ((cp = MD5File(tmp, buf)) != NULL) {
+ /* Mismatch? */
+ if (strcmp(cp, p->next->name + ChecksumHeaderLen) != 0) {
+ printf("original MD5 checksum failed, %s: %s\n",
+ Force ? "deleting anyway" : "not deleting", tmp);
+ if (!Force) {
+ fail = FAIL;
+ goto pkgdb_cleanup;
+ }
+ }
+ }
+ } else if (strncmp(p->next->name, SYMLINK_HEADER, SymlinkHeaderLen) == 0) {
+ char buf[MaxPathSize + SymlinkHeaderLen];
+ int cc;
+
+ (void) strlcpy(buf, SYMLINK_HEADER,
+ sizeof(buf));
+ if ((cc = readlink(tmp, &buf[SymlinkHeaderLen],
+ sizeof(buf) - SymlinkHeaderLen - 1)) < 0) {
+ warn("can't readlink `%s'", tmp);
+ goto pkgdb_cleanup;
+ }
+ buf[SymlinkHeaderLen + cc] = 0x0;
+ if (strcmp(buf, p->next->name) != 0) {
+ if ((cc = readlink(&buf[SymlinkHeaderLen], &buf[SymlinkHeaderLen],
+ sizeof(buf) - SymlinkHeaderLen)) < 0) {
+ printf("symlink %s is not same as recorded value, %s: %s\n",
+ buf, Force ? "deleting anyway" : "not deleting", tmp);
+ if (!Force) {
+ fail = FAIL;
+ goto pkgdb_cleanup;
+ }
+ }
+ buf[SymlinkHeaderLen + cc] = 0x0;
+ if (strcmp(buf, p->next->name) != 0) {
+ printf("symlink %s is not same as recorded value, %s: %s\n",
+ buf, Force ? "deleting anyway" : "not deleting", tmp);
+ if (!Force) {
+ fail = FAIL;
+ goto pkgdb_cleanup;
+ }
+ }
+ }
+ }
+ }
+ if (Verbose && !NoDeleteFiles)
+ printf("Delete file %s\n", tmp);
+ if (!Fake && !NoDeleteFiles) {
+ if (delete_with_parents(tmp, ign_err, FALSE))
+ fail = FAIL;
+ if (preserve && name) {
+ char tmp2[MaxPathSize];
+
+ if (make_preserve_name(tmp2, MaxPathSize, name, tmp)) {
+ if (fexists(tmp2)) {
+ if (rename(tmp2, tmp))
+ warn("preserve: unable to restore %s as %s",
+ tmp2, tmp);
+ else
+ restored = 1;
+ }
+ }
+ }
+ }
+
+pkgdb_cleanup:
+ if (!Fake) {
+ if (!restored) {
+ errno = 0;
+ if (pkgdb_remove(tmp) && errno)
+ perror("pkgdb_remove");
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ pkgdb_close();
+ return fail;
+}
+
+/*
+ * Selectively delete a hierarchy
+ * Returns 1 on error, 0 else.
+ */
+static int
+delete_with_parents(const char *fname, Boolean ign_err, Boolean ign_nonempty)
+{
+ char *cp, *cp2;
+
+ if (remove(fname)) {
+ if (!ign_err && (!ign_nonempty || errno != ENOTEMPTY))
+ warnx("Couldn't remove %s", fname);
+ return 0;
+ }
+ cp = xstrdup(fname);
+ while (*cp) {
+ if ((cp2 = strrchr(cp, '/')) != NULL)
+ *cp2 = '\0';
+ if (!isemptydir(cp))
+ break;
+ if (has_pkgdir(cp))
+ break;
+ if (rmdir(cp))
+ break;
+ }
+ free(cp);
+
+ return 0;
+}
+
+void
+add_pkgdir(const char *pkg, const char *prefix, const char *path)
+{
+ char *fullpath, *oldvalue, *newvalue;
+
+ fullpath = xasprintf("%s/%s", prefix, path);
+ oldvalue = pkgdb_retrieve(fullpath);
+ if (oldvalue) {
+ if (strncmp(oldvalue, "@pkgdir ", 8) != 0)
+ errx(EXIT_FAILURE, "Internal error while processing pkgdb, run pkg_admin rebuild");
+ newvalue = xasprintf("%s %s", oldvalue, pkg);
+ pkgdb_remove(fullpath);
+ } else {
+ newvalue = xasprintf("@pkgdir %s", pkg);
+ }
+ pkgdb_store(fullpath, newvalue);
+
+ free(fullpath);
+ free(newvalue);
+}
+
+void
+delete_pkgdir(const char *pkg, const char *prefix, const char *path)
+{
+ size_t pkg_len, len;
+ char *fullpath, *oldvalue, *newvalue, *iter;
+
+ fullpath = xasprintf("%s/%s", prefix, path);
+ oldvalue = pkgdb_retrieve(fullpath);
+ if (oldvalue && strncmp(oldvalue, "@pkgdir ", 8) == 0) {
+ newvalue = xstrdup(oldvalue);
+ iter = newvalue + 8;
+ pkg_len = strlen(pkg);
+ while (*iter) {
+ if (strncmp(iter, pkg, pkg_len) == 0 &&
+ (iter[pkg_len] == ' ' || iter[pkg_len] == '\0')) {
+ len = strlen(iter + pkg_len);
+ memmove(iter, iter + pkg_len + 1, len);
+ if (len == 0)
+ *iter = '\0';
+ } else {
+ iter += strcspn(iter, " ");
+ iter += strspn(iter, " ");
+ }
+ }
+ pkgdb_remove(fullpath);
+ if (iter != newvalue + 8)
+ pkgdb_store(fullpath, newvalue);
+ free(newvalue);
+ }
+ free(fullpath);
+}
+
+int
+has_pkgdir(const char *path)
+{
+ const char *value;
+
+ value = pkgdb_retrieve(path);
+
+ if (value && strncmp(value, "@pkgdir ", 8) == 0)
+ return 1;
+ else
+ return 0;
+}
--- /dev/null
+/* $NetBSD: remove.c,v 1.1.1.2 2009/08/06 16:55:29 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2008 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <nbcompat.h>
+
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+__RCSID("$NetBSD: remove.c,v 1.1.1.2 2009/08/06 16:55:29 joerg Exp $");
+
+#if HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+#include <errno.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "lib.h"
+
+static int
+safe_fchdir(int cwd)
+{
+ int tmp_errno, rv;
+
+ tmp_errno = errno;
+ rv = fchdir(cwd);
+ errno = tmp_errno;
+
+ return rv;
+}
+
+static int
+long_remove(const char **path_ptr, int missing_ok, int *did_chdir)
+{
+ char tmp_path[PATH_MAX + 1];
+ const char *path;
+ size_t i, len;
+ int rv;
+
+ path = *path_ptr;
+ len = strlen(path);
+ *did_chdir = 0;
+
+ while (len >= PATH_MAX) {
+ for (i = PATH_MAX - 1; i > 0; --i) {
+ if (path[i] == '/')
+ break;
+ }
+ if (i == 0) {
+ errno = ENAMETOOLONG;
+ return -1; /* Assumes PATH_MAX > NAME_MAX */
+ }
+ memcpy(tmp_path, path, i);
+ tmp_path[i] = '\0';
+ if (chdir(tmp_path))
+ return -1;
+ *did_chdir = 1;
+ path += i + 1;
+ len -= i + 1;
+ }
+
+ if (remove(path) == 0 || (errno == ENOENT && missing_ok))
+ rv = 0;
+ else
+ rv = -1;
+
+ *path_ptr = path;
+
+ return rv;
+}
+
+static int
+recursive_remove_internal(const char *path, int missing_ok, int cwd)
+{
+ DIR *dir;
+ struct dirent *de;
+ const char *sub_path;
+ char *subdir;
+ int did_chdir, rv;
+
+ /*
+ * If the argument is longer than PATH_MAX, long_remove
+ * will try to shorten it using chdir. So before returning,
+ * make sure to fchdir back to the original cwd.
+ */
+ sub_path = path;
+ if (long_remove(&sub_path, missing_ok, &did_chdir) == 0)
+ rv = 0;
+ else if (errno != ENOTEMPTY) /* Other errors are terminal. */
+ rv = -1;
+ else
+ rv = 1;
+
+ if (rv != 1) {
+ if (did_chdir && safe_fchdir(cwd) == -1 && rv == 0)
+ rv = -1;
+ return rv;
+ }
+
+ if ((dir = opendir(sub_path)) == NULL) {
+ if (errno == EMFILE)
+ warn("opendir failed");
+ return -1;
+ }
+
+ if (did_chdir && fchdir(cwd) == -1)
+ return -1;
+
+ rv = 0;
+
+ while ((de = readdir(dir)) != NULL) {
+ if (strcmp(de->d_name, ".") == 0)
+ continue;
+ if (strcmp(de->d_name, "..") == 0)
+ continue;
+ subdir = xasprintf("%s/%s", path, de->d_name);
+ rv = recursive_remove_internal(subdir, 1, cwd);
+ free(subdir);
+ }
+
+ closedir(dir);
+
+ safe_fchdir(cwd);
+
+ rv |= long_remove(&path, missing_ok, &did_chdir);
+
+ if (did_chdir && safe_fchdir(cwd) == -1 && rv == 0)
+ rv = -1;
+
+ return rv;
+}
+
+int
+recursive_remove(const char *path, int missing_ok)
+{
+ int orig_cwd, rv;
+
+ /* First try the easy case of regular file or empty directory. */
+ if (remove(path) == 0 || (errno == ENOENT && missing_ok))
+ return 0;
+
+ /*
+ * If the path is too long, long_remove will use chdir to shorten it,
+ * so remember the current directory first.
+ */
+ if ((orig_cwd = open(".", O_RDONLY)) == -1)
+ return -1;
+
+ rv = recursive_remove_internal(path, missing_ok, orig_cwd);
+
+ close(orig_cwd);
+ return rv;
+}
--- /dev/null
+/* $NetBSD: str.c,v 1.1.1.2 2009/02/02 20:44:08 joerg Exp $ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+__RCSID("$NetBSD: str.c,v 1.1.1.2 2009/02/02 20:44:08 joerg Exp $");
+
+/*
+ * 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 string utilities.
+ *
+ */
+
+#if HAVE_ASSERT_H
+#include <assert.h>
+#endif
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+#if HAVE_FNMATCH_H
+#include <fnmatch.h>
+#endif
+#include "lib.h"
+#include "dewey.h"
+
+/* pull in definitions and macros for resizing arrays as we go */
+#include "defs.h"
+
+/*
+ * Return the suffix portion of a path
+ */
+const char *
+suffix_of(const char *str)
+{
+ const char *dot;
+
+ return ((dot = strrchr(basename_of(str), '.')) == NULL) ? "" : dot + 1;
+}
+
+/*
+ * Return the filename portion of a path
+ */
+const char *
+basename_of(const char *str)
+{
+ const char *slash;
+
+ return ((slash = strrchr(str, '/')) == NULL) ? str : slash + 1;
+}
+
+/*
+ * Return the dirname portion of a path
+ */
+const char *
+dirname_of(const char *path)
+{
+ size_t cc;
+ char *s;
+ static char buf[MaxPathSize];
+
+ if ((s = strrchr(path, '/')) == NULL) {
+ return ".";
+ }
+ if (s == path) {
+ /* "/foo" -> return "/" */
+ return "/";
+ }
+ cc = (size_t) (s - path);
+ if (cc >= sizeof(buf))
+ errx(EXIT_FAILURE, "dirname_of: too long dirname: '%s'", path);
+ (void) memcpy(buf, path, cc);
+ buf[cc] = 0;
+ return buf;
+}
+
+/*
+ * Does the pkgname contain any of the special chars ("{[]?*<>")?
+ * If so, return 1, else 0
+ */
+int
+ispkgpattern(const char *pkg)
+{
+ return strpbrk(pkg, "<>[]?*{") != NULL;
+}
--- /dev/null
+/* $NetBSD: var.c,v 1.2 2013/05/16 19:19:44 martin Exp $ */
+
+/*-
+ * Copyright (c) 2005, 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Dieter Baron, Thomas Klausner, Johnny Lam, and Joerg Sonnenberger.
+ *
+ * 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. 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_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+__RCSID("$NetBSD: var.c,v 1.2 2013/05/16 19:19:44 martin Exp $");
+
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#include "lib.h"
+
+static const char *var_cmp(const char *, size_t, const char *, size_t);
+static void var_print(FILE *, const char *, const char *);
+
+/*
+ * Copy the specified varibales from the file fname to stdout.
+ */
+int
+var_copy_list(const char *buf, const char **variables)
+{
+ const char *eol, *next;
+ size_t len;
+ int i;
+
+ for (; *buf; buf = next) {
+ if ((eol = strchr(buf, '\n')) != NULL) {
+ next = eol + 1;
+ len = eol - buf;
+ } else {
+ next = eol;
+ len = strlen(buf);
+ }
+
+ for (i=0; variables[i]; i++) {
+ if (var_cmp(buf, len, variables[i],
+ strlen(variables[i])) != NULL) {
+ printf("%.*s\n", (int)len, buf);
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+ * Print the value of variable from the file fname to stdout.
+ */
+char *
+var_get(const char *fname, const char *variable)
+{
+ FILE *fp;
+ char *line;
+ size_t len;
+ size_t varlen;
+ char *value;
+ size_t valuelen;
+ size_t thislen;
+ const char *p;
+
+ varlen = strlen(variable);
+ if (varlen == 0)
+ return NULL;
+
+ fp = fopen(fname, "r");
+ if (!fp) {
+ if (errno != ENOENT)
+ warn("var_get: can't open '%s' for reading", fname);
+ return NULL;
+ }
+
+ value = NULL;
+ valuelen = 0;
+
+ while ((line = fgetln(fp, &len)) != (char *) NULL) {
+ if (line[len - 1] == '\n')
+ --len;
+ if ((p=var_cmp(line, len, variable, varlen)) == NULL)
+ continue;
+
+ thislen = line+len - p;
+ if (value) {
+ value = xrealloc(value, valuelen+thislen+2);
+ value[valuelen++] = '\n';
+ }
+ else {
+ value = xmalloc(thislen+1);
+ }
+ sprintf(value+valuelen, "%.*s", (int)thislen, p);
+ valuelen += thislen;
+ }
+ (void) fclose(fp);
+ return value;
+}
+
+/*
+ * Print the value of variable from the memory buffer to stdout.
+ */
+char *
+var_get_memory(const char *buf, const char *variable)
+{
+ const char *eol, *next, *data;
+ size_t len, varlen, thislen, valuelen;
+ char *value;
+
+ varlen = strlen(variable);
+ if (varlen == 0)
+ return NULL;
+
+ value = NULL;
+ valuelen = 0;
+
+ for (; buf && *buf; buf = next) {
+ if ((eol = strchr(buf, '\n')) != NULL) {
+ next = eol + 1;
+ len = eol - buf;
+ } else {
+ next = eol;
+ len = strlen(buf);
+ }
+ if ((data = var_cmp(buf, len, variable, varlen)) == NULL)
+ continue;
+
+ thislen = buf + len - data;
+ if (value) {
+ value = xrealloc(value, valuelen+thislen+2);
+ value[valuelen++] = '\n';
+ }
+ else {
+ value = xmalloc(thislen+1);
+ }
+ sprintf(value + valuelen, "%.*s", (int)thislen, data);
+ valuelen += thislen;
+ }
+ return value;
+}
+
+/*
+ * Add given variable with given value to file, overwriting any
+ * previous occurrence.
+ */
+int
+var_set(const char *fname, const char *variable, const char *value)
+{
+ FILE *fp;
+ FILE *fout;
+ char *tmpname;
+ int fd;
+ char *line;
+ size_t len;
+ size_t varlen;
+ Boolean done;
+ struct stat st;
+
+ varlen = strlen(variable);
+ if (varlen == 0)
+ return 0;
+
+ fp = fopen(fname, "r");
+ if (fp == NULL) {
+ if (errno != ENOENT) {
+ warn("var_set: can't open '%s' for reading", fname);
+ return -1;
+ }
+ if (value == NULL)
+ return 0; /* Nothing to do */
+ }
+
+ tmpname = xasprintf("%s.XXXXXX", fname);
+ if ((fd = mkstemp(tmpname)) < 0) {
+ free(tmpname);
+ if (fp != NULL)
+ fclose(fp);
+ warn("var_set: can't open temp file for '%s' for writing",
+ fname);
+ return -1;
+ }
+ if (chmod(tmpname, 0644) < 0) {
+ close(fd);
+ if (fp != NULL)
+ fclose(fp);
+ free(tmpname);
+ warn("var_set: can't set permissions for temp file for '%s'",
+ fname);
+ return -1;
+ }
+ if ((fout=fdopen(fd, "w")) == NULL) {
+ close(fd);
+ remove(tmpname);
+ free(tmpname);
+ if (fp != NULL)
+ fclose(fp);
+ warn("var_set: can't open temp file for '%s' for writing",
+ fname);
+ return -1;
+ }
+
+ done = FALSE;
+
+ if (fp) {
+ while ((line = fgetln(fp, &len)) != (char *) NULL) {
+ if (var_cmp(line, len, variable, varlen) == NULL)
+ fprintf(fout, "%.*s", (int)len, line);
+ else {
+ if (!done && value) {
+ var_print(fout, variable, value);
+ done = TRUE;
+ }
+ }
+ }
+ (void) fclose(fp);
+ }
+
+ if (!done && value)
+ var_print(fout, variable, value);
+
+ if (fclose(fout) < 0) {
+ free(tmpname);
+ warn("var_set: write error for '%s'", fname);
+ return -1;
+ }
+
+ if (stat(tmpname, &st) < 0) {
+ free(tmpname);
+ warn("var_set: cannot stat tempfile for '%s'", fname);
+ return -1;
+ }
+
+ if (st.st_size == 0) {
+ if (remove(tmpname) < 0) {
+ free(tmpname);
+ warn("var_set: cannot remove tempfile for '%s'",
+ fname);
+ return -1;
+ }
+ free(tmpname);
+ if (remove(fname) < 0) {
+ warn("var_set: cannot remove '%s'", fname);
+ return -1;
+ }
+ return 0;
+ }
+
+ if (rename(tmpname, fname) < 0) {
+ free(tmpname);
+ warn("var_set: cannot move tempfile to '%s'", fname);
+ return -1;
+ }
+ free(tmpname);
+ return 0;
+}
+
+/*
+ * Check if line contains variable var, return pointer to its value or NULL.
+ */
+static const char *
+var_cmp(const char *line, size_t linelen, const char *var, size_t varlen)
+{
+ /*
+ * We expect lines to look like one of the following
+ * forms:
+ * VAR=value
+ * VAR= value
+ * We print out the value of VAR, or nothing if it
+ * doesn't exist.
+ */
+ if (linelen < varlen+1)
+ return NULL;
+ if (strncmp(var, line, varlen) != 0)
+ return NULL;
+
+ line += varlen;
+ if (*line != '=')
+ return NULL;
+
+ ++line;
+ linelen -= varlen+1;
+ if (linelen > 0 && *line == ' ')
+ ++line;
+ return line;
+}
+
+/*
+ * Print given variable with value to file f.
+ */
+static void
+var_print(FILE *f, const char *variable, const char *value)
+{
+ const char *p;
+
+ while ((p=strchr(value, '\n')) != NULL) {
+ if (p != value)
+ fprintf(f, "%s=%.*s\n", variable, (int)(p-value), value);
+ value = p+1;
+ }
+
+ if (*value)
+ fprintf(f, "%s=%s\n", variable, value);
+}
--- /dev/null
+/* $NetBSD: version.c,v 1.1.1.3 2010/02/03 14:24:00 joerg Exp $ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+__RCSID("$NetBSD: version.c,v 1.1.1.3 2010/02/03 14:24:00 joerg Exp $");
+
+/*
+ * Copyright (c) 2001 Thomas Klausner. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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_STDIO_H
+#include <stdio.h>
+#endif
+
+#include "lib.h"
+#include "version.h"
+
+void
+show_version(void)
+{
+ printf("%d\n", PKGTOOLS_VERSION);
+ exit (0);
+}
+
--- /dev/null
+/* $NetBSD: version.h,v 1.10 2013/04/20 15:29:23 wiz Exp $ */
+
+/*
+ * Copyright (c) 2001 Thomas Klausner. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#ifndef _INST_LIB_VERSION_H_
+#define _INST_LIB_VERSION_H_
+
+#define PKGTOOLS_VERSION 20130131
+
+#endif /* _INST_LIB_VERSION_H_ */
--- /dev/null
+/* $NetBSD: vulnerabilities-file.c,v 1.1.1.5 2010/06/26 00:14:33 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2008, 2010 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <nbcompat.h>
+
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+__RCSID("$NetBSD: vulnerabilities-file.c,v 1.1.1.5 2010/06/26 00:14:33 joerg Exp $");
+
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#ifndef BOOTSTRAP
+#include <archive.h>
+#endif
+#include <ctype.h>
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef NETBSD
+#include <nbcompat/sha1.h>
+#include <nbcompat/sha2.h>
+#else
+#include <sha1.h>
+#include <sha2.h>
+#endif
+#include <unistd.h>
+
+#include "lib.h"
+
+static struct pkg_vulnerabilities *read_pkg_vulnerabilities_archive(struct archive *, int);
+static struct pkg_vulnerabilities *parse_pkg_vuln(const char *, size_t, int);
+
+static const char pgp_msg_start[] = "-----BEGIN PGP SIGNED MESSAGE-----\n";
+static const char pgp_msg_end[] = "-----BEGIN PGP SIGNATURE-----\n";
+static const char pkcs7_begin[] = "-----BEGIN PKCS7-----\n";
+static const char pkcs7_end[] = "-----END PKCS7-----\n";
+
+static void
+verify_signature_pkcs7(const char *input)
+{
+#ifdef HAVE_SSL
+ const char *begin_pkgvul, *end_pkgvul, *begin_sig, *end_sig;
+
+ if (strncmp(input, pgp_msg_start, strlen(pgp_msg_start)) == 0) {
+ begin_pkgvul = input + strlen(pgp_msg_start);
+ if ((end_pkgvul = strstr(begin_pkgvul, pgp_msg_end)) == NULL)
+ errx(EXIT_FAILURE, "Invalid PGP signature");
+ if ((begin_sig = strstr(end_pkgvul, pkcs7_begin)) == NULL)
+ errx(EXIT_FAILURE, "No PKCS7 signature");
+ } else {
+ begin_pkgvul = input;
+ if ((begin_sig = strstr(begin_pkgvul, pkcs7_begin)) == NULL)
+ errx(EXIT_FAILURE, "No PKCS7 signature");
+ end_pkgvul = begin_sig;
+ }
+ if ((end_sig = strstr(begin_sig, pkcs7_end)) == NULL)
+ errx(EXIT_FAILURE, "Invalid PKCS7 signature");
+ end_sig += strlen(pkcs7_end);
+
+ if (easy_pkcs7_verify(begin_pkgvul, end_pkgvul - begin_pkgvul,
+ begin_sig, end_sig - begin_sig, certs_pkg_vulnerabilities, 0))
+ errx(EXIT_FAILURE, "Unable to verify PKCS7 signature");
+#else
+ errx(EXIT_FAILURE, "OpenSSL support is not compiled in");
+#endif
+}
+
+static void
+verify_signature(const char *input, size_t input_len)
+{
+ if (gpg_cmd == NULL && certs_pkg_vulnerabilities == NULL)
+ errx(EXIT_FAILURE,
+ "At least GPG or CERTIFICATE_ANCHOR_PKGVULN "
+ "must be configured");
+ if (gpg_cmd != NULL)
+ inline_gpg_verify(input, input_len, gpg_keyring_pkgvuln);
+ if (certs_pkg_vulnerabilities != NULL)
+ verify_signature_pkcs7(input);
+}
+
+static void *
+sha512_hash_init(void)
+{
+ static SHA512_CTX hash_ctx;
+
+ SHA512_Init(&hash_ctx);
+ return &hash_ctx;
+}
+
+static void
+sha512_hash_update(void *ctx, const void *data, size_t len)
+{
+ SHA512_CTX *hash_ctx = ctx;
+
+ SHA512_Update(hash_ctx, data, len);
+}
+
+static const char *
+sha512_hash_finish(void *ctx)
+{
+ static char hash[SHA512_DIGEST_STRING_LENGTH];
+ unsigned char digest[SHA512_DIGEST_LENGTH];
+ SHA512_CTX *hash_ctx = ctx;
+ int i;
+
+ SHA512_Final(digest, hash_ctx);
+ for (i = 0; i < SHA512_DIGEST_LENGTH; ++i) {
+ unsigned char c;
+
+ c = digest[i] / 16;
+ if (c < 10)
+ hash[2 * i] = '0' + c;
+ else
+ hash[2 * i] = 'a' - 10 + c;
+
+ c = digest[i] % 16;
+ if (c < 10)
+ hash[2 * i + 1] = '0' + c;
+ else
+ hash[2 * i + 1] = 'a' - 10 + c;
+ }
+ hash[2 * i] = '\0';
+
+ return hash;
+}
+
+static void *
+sha1_hash_init(void)
+{
+ static SHA1_CTX hash_ctx;
+
+ SHA1Init(&hash_ctx);
+ return &hash_ctx;
+}
+
+static void
+sha1_hash_update(void *ctx, const void *data, size_t len)
+{
+ SHA1_CTX *hash_ctx = ctx;
+
+ SHA1Update(hash_ctx, data, len);
+}
+
+static const char *
+sha1_hash_finish(void *ctx)
+{
+ static char hash[SHA1_DIGEST_STRING_LENGTH];
+ SHA1_CTX *hash_ctx = ctx;
+
+ SHA1End(hash_ctx, hash);
+
+ return hash;
+}
+
+static const struct hash_algorithm {
+ const char *name;
+ size_t name_len;
+ void * (*init)(void);
+ void (*update)(void *, const void *, size_t);
+ const char * (* finish)(void *);
+} hash_algorithms[] = {
+ { "SHA512", 6, sha512_hash_init, sha512_hash_update,
+ sha512_hash_finish },
+ { "SHA1", 4, sha1_hash_init, sha1_hash_update,
+ sha1_hash_finish },
+ { NULL, 0, NULL, NULL, NULL }
+};
+
+static void
+verify_hash(const char *input, const char *hash_line)
+{
+ const struct hash_algorithm *hash;
+ void *ctx;
+ const char *last_start, *next, *hash_value;
+ int in_pgp_msg;
+
+ for (hash = hash_algorithms; hash->name != NULL; ++hash) {
+ if (strncmp(hash_line, hash->name, hash->name_len))
+ continue;
+ if (isspace((unsigned char)hash_line[hash->name_len]))
+ break;
+ }
+ if (hash->name == NULL) {
+ const char *end_name;
+ for (end_name = hash_line; *end_name != '\0'; ++end_name) {
+ if (!isalnum((unsigned char)*end_name))
+ break;
+ }
+ warnx("Unsupported hash algorithm: %.*s",
+ (int)(end_name - hash_line), hash_line);
+ return;
+ }
+
+ hash_line += hash->name_len;
+ if (!isspace((unsigned char)*hash_line))
+ errx(EXIT_FAILURE, "Invalid #CHECKSUM");
+ while (isspace((unsigned char)*hash_line) && *hash_line != '\n')
+ ++hash_line;
+
+ if (*hash_line == '\n')
+ errx(EXIT_FAILURE, "Invalid #CHECKSUM");
+
+ ctx = (*hash->init)();
+ if (strncmp(input, pgp_msg_start, strlen(pgp_msg_start)) == 0) {
+ input += strlen(pgp_msg_start);
+ in_pgp_msg = 1;
+ } else {
+ in_pgp_msg = 0;
+ }
+ for (last_start = input; *input != '\0'; input = next) {
+ if ((next = strchr(input, '\n')) == NULL)
+ errx(EXIT_FAILURE, "Missing newline in pkg-vulnerabilities");
+ ++next;
+ if (in_pgp_msg && strncmp(input, pgp_msg_end, strlen(pgp_msg_end)) == 0)
+ break;
+ if (!in_pgp_msg && strncmp(input, pkcs7_begin, strlen(pkcs7_begin)) == 0)
+ break;
+ if (*input == '\n' ||
+ strncmp(input, "Hash:", 5) == 0 ||
+ strncmp(input, "# $NetBSD", 9) == 0 ||
+ strncmp(input, "#CHECKSUM", 9) == 0) {
+ (*hash->update)(ctx, last_start, input - last_start);
+ last_start = next;
+ }
+ }
+ (*hash->update)(ctx, last_start, input - last_start);
+ hash_value = (*hash->finish)(ctx);
+ if (strncmp(hash_line, hash_value, strlen(hash_value)))
+ errx(EXIT_FAILURE, "%s hash doesn't match", hash->name);
+ hash_line += strlen(hash_value);
+
+ while (isspace((unsigned char)*hash_line) && *hash_line != '\n')
+ ++hash_line;
+
+ if (!isspace((unsigned char)*hash_line))
+ errx(EXIT_FAILURE, "Invalid #CHECKSUM");
+}
+
+static void
+add_vulnerability(struct pkg_vulnerabilities *pv, size_t *allocated, const char *line)
+{
+ size_t len_pattern, len_class, len_url;
+ const char *start_pattern, *start_class, *start_url;
+
+ start_pattern = line;
+
+ start_class = line;
+ while (*start_class != '\0' && !isspace((unsigned char)*start_class))
+ ++start_class;
+ len_pattern = start_class - line;
+
+ while (*start_class != '\n' && isspace((unsigned char)*start_class))
+ ++start_class;
+
+ if (*start_class == '0' || *start_class == '\n')
+ errx(EXIT_FAILURE, "Input error: missing classification");
+
+ start_url = start_class;
+ while (*start_url != '\0' && !isspace((unsigned char)*start_url))
+ ++start_url;
+ len_class = start_url - start_class;
+
+ while (*start_url != '\n' && isspace((unsigned char)*start_url))
+ ++start_url;
+
+ if (*start_url == '0' || *start_url == '\n')
+ errx(EXIT_FAILURE, "Input error: missing URL");
+
+ line = start_url;
+ while (*line != '\0' && !isspace((unsigned char)*line))
+ ++line;
+ len_url = line - start_url;
+
+ if (pv->entries == *allocated) {
+ if (*allocated == 0)
+ *allocated = 16;
+ else if (*allocated <= SSIZE_MAX / 2)
+ *allocated *= 2;
+ else
+ errx(EXIT_FAILURE, "Too many vulnerabilities");
+ pv->vulnerability = xrealloc(pv->vulnerability,
+ sizeof(char *) * *allocated);
+ pv->classification = xrealloc(pv->classification,
+ sizeof(char *) * *allocated);
+ pv->advisory = xrealloc(pv->advisory,
+ sizeof(char *) * *allocated);
+ }
+
+ pv->vulnerability[pv->entries] = xmalloc(len_pattern + 1);
+ memcpy(pv->vulnerability[pv->entries], start_pattern, len_pattern);
+ pv->vulnerability[pv->entries][len_pattern] = '\0';
+ pv->classification[pv->entries] = xmalloc(len_class + 1);
+ memcpy(pv->classification[pv->entries], start_class, len_class);
+ pv->classification[pv->entries][len_class] = '\0';
+ pv->advisory[pv->entries] = xmalloc(len_url + 1);
+ memcpy(pv->advisory[pv->entries], start_url, len_url);
+ pv->advisory[pv->entries][len_url] = '\0';
+
+ ++pv->entries;
+}
+
+struct pkg_vulnerabilities *
+read_pkg_vulnerabilities_memory(void *buf, size_t len, int check_sum)
+{
+#ifdef BOOTSTRAP
+ errx(EXIT_FAILURE, "Audit functions are unsupported during bootstrap");
+#else
+ struct archive *a;
+ struct pkg_vulnerabilities *pv;
+
+ if ((a = archive_read_new()) == NULL)
+ errx(EXIT_FAILURE, "memory allocation failed");
+
+ if (archive_read_support_compression_all(a) != ARCHIVE_OK ||
+ archive_read_support_format_raw(a) != ARCHIVE_OK ||
+ archive_read_open_memory(a, buf, len) != ARCHIVE_OK)
+ errx(EXIT_FAILURE, "Cannot open pkg_vulnerabilies buffer: %s",
+ archive_error_string(a));
+
+ pv = read_pkg_vulnerabilities_archive(a, check_sum);
+
+ return pv;
+#endif
+}
+
+struct pkg_vulnerabilities *
+read_pkg_vulnerabilities_file(const char *path, int ignore_missing, int check_sum)
+{
+#ifdef BOOTSTRAP
+ errx(EXIT_FAILURE, "Audit functions are unsupported during bootstrap");
+#else
+ struct archive *a;
+ struct pkg_vulnerabilities *pv;
+ int fd;
+
+ if ((fd = open(path, O_RDONLY)) == -1) {
+ if (errno == ENOENT && ignore_missing)
+ return NULL;
+ err(EXIT_FAILURE, "Cannot open %s", path);
+ }
+
+ if ((a = archive_read_new()) == NULL)
+ errx(EXIT_FAILURE, "memory allocation failed");
+
+ if (archive_read_support_compression_all(a) != ARCHIVE_OK ||
+ archive_read_support_format_raw(a) != ARCHIVE_OK ||
+ archive_read_open_fd(a, fd, 65536) != ARCHIVE_OK)
+ errx(EXIT_FAILURE, "Cannot open ``%s'': %s", path,
+ archive_error_string(a));
+
+ pv = read_pkg_vulnerabilities_archive(a, check_sum);
+ close(fd);
+
+ return pv;
+#endif
+}
+
+#ifndef BOOTSTRAP
+static struct pkg_vulnerabilities *
+read_pkg_vulnerabilities_archive(struct archive *a, int check_sum)
+{
+ struct archive_entry *ae;
+ struct pkg_vulnerabilities *pv;
+ char *buf;
+ size_t buf_len, off;
+ ssize_t r;
+
+ if (archive_read_next_header(a, &ae) != ARCHIVE_OK)
+ errx(EXIT_FAILURE, "Cannot read pkg_vulnerabilities: %s",
+ archive_error_string(a));
+
+ off = 0;
+ buf_len = 65536;
+ buf = xmalloc(buf_len + 1);
+
+ for (;;) {
+ r = archive_read_data(a, buf + off, buf_len - off);
+ if (r <= 0)
+ break;
+ off += r;
+ if (off == buf_len) {
+ buf_len *= 2;
+ if (buf_len < off)
+ errx(EXIT_FAILURE, "pkg_vulnerabilties too large");
+ buf = xrealloc(buf, buf_len + 1);
+ }
+ }
+
+ if (r != ARCHIVE_OK)
+ errx(EXIT_FAILURE, "Cannot read pkg_vulnerabilities: %s",
+ archive_error_string(a));
+
+ archive_read_close(a);
+
+ buf[off] = '\0';
+ pv = parse_pkg_vuln(buf, off, check_sum);
+ free(buf);
+ return pv;
+}
+
+static struct pkg_vulnerabilities *
+parse_pkg_vuln(const char *input, size_t input_len, int check_sum)
+{
+ struct pkg_vulnerabilities *pv;
+ long version;
+ char *end;
+ const char *iter, *next;
+ size_t allocated_vulns;
+ int in_pgp_msg;
+
+ pv = xmalloc(sizeof(*pv));
+
+ allocated_vulns = pv->entries = 0;
+ pv->vulnerability = NULL;
+ pv->classification = NULL;
+ pv->advisory = NULL;
+
+ if (strlen(input) != input_len)
+ errx(1, "Invalid input (NUL character found)");
+
+ if (check_sum)
+ verify_signature(input, input_len);
+
+ if (strncmp(input, pgp_msg_start, strlen(pgp_msg_start)) == 0) {
+ iter = input + strlen(pgp_msg_start);
+ in_pgp_msg = 1;
+ } else {
+ iter = input;
+ in_pgp_msg = 0;
+ }
+
+ for (; *iter; iter = next) {
+ if ((next = strchr(iter, '\n')) == NULL)
+ errx(EXIT_FAILURE, "Missing newline in pkg-vulnerabilities");
+ ++next;
+ if (*iter == '\0' || *iter == '\n')
+ continue;
+ if (strncmp(iter, "Hash:", 5) == 0)
+ continue;
+ if (strncmp(iter, "# $NetBSD", 9) == 0)
+ continue;
+ if (*iter == '#' && isspace((unsigned char)iter[1])) {
+ for (++iter; iter != next; ++iter) {
+ if (!isspace((unsigned char)*iter))
+ errx(EXIT_FAILURE, "Invalid header");
+ }
+ continue;
+ }
+
+ if (strncmp(iter, "#FORMAT", 7) != 0)
+ errx(EXIT_FAILURE, "Input header is malformed");
+
+ iter += 7;
+ if (!isspace((unsigned char)*iter))
+ errx(EXIT_FAILURE, "Invalid #FORMAT");
+ ++iter;
+ version = strtol(iter, &end, 10);
+ if (iter == end || version != 1 || *end != '.')
+ errx(EXIT_FAILURE, "Input #FORMAT");
+ iter = end + 1;
+ version = strtol(iter, &end, 10);
+ if (iter == end || version != 1 || *end != '.')
+ errx(EXIT_FAILURE, "Input #FORMAT");
+ iter = end + 1;
+ version = strtol(iter, &end, 10);
+ if (iter == end || version != 0)
+ errx(EXIT_FAILURE, "Input #FORMAT");
+ for (iter = end; iter != next; ++iter) {
+ if (!isspace((unsigned char)*iter))
+ errx(EXIT_FAILURE, "Input #FORMAT");
+ }
+ break;
+ }
+ if (*iter == '\0')
+ errx(EXIT_FAILURE, "Missing #CHECKSUM or content");
+
+ for (iter = next; *iter; iter = next) {
+ if ((next = strchr(iter, '\n')) == NULL)
+ errx(EXIT_FAILURE, "Missing newline in pkg-vulnerabilities");
+ ++next;
+ if (*iter == '\0' || *iter == '\n')
+ continue;
+ if (in_pgp_msg && strncmp(iter, pgp_msg_end, strlen(pgp_msg_end)) == 0)
+ break;
+ if (!in_pgp_msg && strncmp(iter, pkcs7_begin, strlen(pkcs7_begin)) == 0)
+ break;
+ if (*iter == '#' &&
+ (iter[1] == '\0' || iter[1] == '\n' || isspace((unsigned char)iter[1])))
+ continue;
+ if (strncmp(iter, "#CHECKSUM", 9) == 0) {
+ iter += 9;
+ if (!isspace((unsigned char)*iter))
+ errx(EXIT_FAILURE, "Invalid #CHECKSUM");
+ while (isspace((unsigned char)*iter))
+ ++iter;
+ verify_hash(input, iter);
+ continue;
+ }
+ if (*iter == '#') {
+ /*
+ * This should really be an error,
+ * but it is still used.
+ */
+ /* errx(EXIT_FAILURE, "Invalid data line starting with #"); */
+ continue;
+ }
+ add_vulnerability(pv, &allocated_vulns, iter);
+ }
+
+ if (pv->entries != allocated_vulns) {
+ pv->vulnerability = xrealloc(pv->vulnerability,
+ sizeof(char *) * pv->entries);
+ pv->classification = xrealloc(pv->classification,
+ sizeof(char *) * pv->entries);
+ pv->advisory = xrealloc(pv->advisory,
+ sizeof(char *) * pv->entries);
+ }
+
+ return pv;
+}
+#endif
+
+void
+free_pkg_vulnerabilities(struct pkg_vulnerabilities *pv)
+{
+ size_t i;
+
+ for (i = 0; i < pv->entries; ++i) {
+ free(pv->vulnerability[i]);
+ free(pv->classification[i]);
+ free(pv->advisory[i]);
+ }
+ free(pv->vulnerability);
+ free(pv->classification);
+ free(pv->advisory);
+ free(pv);
+}
+
+static int
+check_ignored_entry(struct pkg_vulnerabilities *pv, size_t i)
+{
+ const char *iter, *next;
+ size_t entry_len, url_len;
+
+ if (ignore_advisories == NULL)
+ return 0;
+
+ url_len = strlen(pv->advisory[i]);
+
+ for (iter = ignore_advisories; *iter; iter = next) {
+ if ((next = strchr(iter, '\n')) == NULL) {
+ entry_len = strlen(iter);
+ next = iter + entry_len;
+ } else {
+ entry_len = next - iter;
+ ++next;
+ }
+ if (url_len != entry_len)
+ continue;
+ if (strncmp(pv->advisory[i], iter, entry_len) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+int
+audit_package(struct pkg_vulnerabilities *pv, const char *pkgname,
+ const char *limit_vul_types, int output_type)
+{
+ FILE *output = output_type == 1 ? stdout : stderr;
+ size_t i;
+ int retval, do_eol;
+
+ retval = 0;
+
+ do_eol = (strcasecmp(check_eol, "yes") == 0);
+
+ for (i = 0; i < pv->entries; ++i) {
+ if (check_ignored_entry(pv, i))
+ continue;
+ if (limit_vul_types != NULL &&
+ strcmp(limit_vul_types, pv->classification[i]))
+ continue;
+ if (!pkg_match(pv->vulnerability[i], pkgname))
+ continue;
+ if (strcmp("eol", pv->classification[i]) == 0) {
+ if (!do_eol)
+ continue;
+ retval = 1;
+ if (output_type == 0) {
+ puts(pkgname);
+ continue;
+ }
+ fprintf(output,
+ "Package %s has reached end-of-life (eol), "
+ "see %s/eol-packages\n", pkgname,
+ tnf_vulnerability_base);
+ continue;
+ }
+ retval = 1;
+ if (output_type == 0) {
+ puts(pkgname);
+ } else {
+ fprintf(output,
+ "Package %s has a %s vulnerability, see %s\n",
+ pkgname, pv->classification[i], pv->advisory[i]);
+ }
+ }
+ return retval;
+}
--- /dev/null
+/* $NetBSD: xwrapper.c,v 1.1.1.1 2009/02/02 20:44:09 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2008 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+__RCSID("$NetBSD: xwrapper.c,v 1.1.1.1 2009/02/02 20:44:09 joerg Exp $");
+
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lib.h"
+
+char *
+xasprintf(const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+
+ va_start(ap, fmt);
+ if (vasprintf(&buf, fmt, ap) == -1)
+ err(1, "asprintf failed");
+ va_end(ap);
+ return buf;
+}
+
+void *
+xmalloc(size_t len)
+{
+ void *ptr;
+
+ if ((ptr = malloc(len)) == NULL)
+ err(1, "malloc failed");
+ return ptr;
+}
+
+void *
+xcalloc(size_t len, size_t n)
+{
+ void *ptr;
+
+ if ((ptr = calloc(len, n)) == NULL)
+ err(1, "calloc failed");
+ return ptr;
+}
+
+void *
+xrealloc(void *buf, size_t len)
+{
+ void *ptr;
+
+ if ((ptr = realloc(buf, len)) == NULL)
+ err(1, "realloc failed");
+ return ptr;
+}
+
+char *
+xstrdup(const char *str)
+{
+ char *buf;
+
+ if ((buf = strdup(str)) == NULL)
+ err(1, "strdup failed");
+ return buf;
+}
--- /dev/null
+# $NetBSD: pkgsrc.cnf,v 1.1.1.1 2009/02/02 20:44:09 joerg Exp $
+#
+# OpenSSL sample configuration file for use by pkgsrc.sh
+#
+
+# This definition stops the following lines choking if HOME isn't
+# defined.
+HOME = .
+RANDFILE = $ENV::HOME/.rnd
+
+####################################################################
+[ ca ]
+default_ca = CA_default # The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir = ./pkgsrc # Where everything is kept
+certs = $dir/certs # Where the issued certs are kept
+crl_dir = $dir/crl # Where the issued crl are kept
+database = $dir/index.txt # database index file.
+#unique_subject = no # Set to 'no' to allow creation of
+ # several ctificates with same subject.
+new_certs_dir = $dir/newcerts # default place for new certs.
+
+certificate = $dir/cacert.pem # The CA certificate
+serial = $dir/serial # The current serial number
+crlnumber = $dir/crlnumber # the current crl number
+ # must be commented out to leave a V1 CRL
+crl = $dir/crl.pem # The current CRL
+private_key = $dir/private/cakey.pem# The private key
+RANDFILE = $dir/private/.rand # private random number file
+
+# Comment out the following two lines for the "traditional"
+# (and highly broken) format.
+name_opt = ca_default # Subject Name options
+cert_opt = ca_default # Certificate field options
+
+# Extension copying option: use with caution.
+# copy_extensions = copy
+
+# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
+# so this is commented out by default to leave a V1 CRL.
+# crlnumber must also be commented out to leave a V1 CRL.
+# crl_extensions = crl_ext
+
+default_days = 365 # how long to certify for
+default_crl_days= 30 # how long before next CRL
+default_md = default # use public key default MD
+preserve = no # keep passed DN ordering
+
+# A few difference way of specifying how similar the request should look
+# For type CA, the listed attributes must be the same, and the optional
+# and supplied fields are just that :-)
+policy = policy_match
+
+# For the CA policy
+[ policy_match ]
+countryName = match
+stateOrProvinceName = match
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+# For the 'anything' policy
+# At this point in time, you must list all acceptable 'object'
+# types.
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+####################################################################
+[ req ]
+default_bits = 2048
+default_keyfile = privkey.pem
+default_md = sha1
+distinguished_name = req_distinguished_name
+x509_extensions = v3_ca # The extentions to add to the self signed cert
+
+string_mask = utf8only
+
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+countryName_default = AU
+countryName_min = 2
+countryName_max = 2
+
+stateOrProvinceName = State or Province Name (full name)
+stateOrProvinceName_default = Some-State
+
+localityName = Locality Name (eg, city)
+
+0.organizationName = Organization Name (eg, company)
+0.organizationName_default = Internet Widgits Pty Ltd
+
+# we can do this but it is not needed normally :-)
+#1.organizationName = Second Organization Name (eg, company)
+#1.organizationName_default = World Wide Web Pty Ltd
+
+organizationalUnitName = Organizational Unit Name (eg, section)
+#organizationalUnitName_default =
+
+commonName = Common Name (eg, YOUR name)
+commonName_max = 64
+
+emailAddress = Email Address
+emailAddress_max = 64
+
+[ pkgkey ]
+nsComment = "Certificate for binary pkgsrc packages"
+
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+
+subjectAltName=email:move
+
+extendedKeyUsage = codeSigning, emailProtection
+
+[ pkgsec ]
+nsComment = "Certificate for pkg-vulnerabilities"
+
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+
+subjectAltName=email:move
+
+[ v3_ca ]
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid:always,issuer:always
+basicConstraints = critical,CA:true
--- /dev/null
+#!/bin/sh
+#
+# $NetBSD: pkgsrc.sh,v 1.1.1.1 2009/02/02 20:44:09 joerg Exp $
+#
+
+CA="openssl ca -config pkgsrc.cnf"
+REQ="openssl req -config pkgsrc.cnf"
+
+set -e
+
+new_ca() {
+ if [ -f $1/serial ]; then
+ echo "CA already exists, exiting" >& 2
+ exit 1
+ fi
+
+ mkdir -p $1/certs $1/crl $1/newcerts $1/private
+ echo "00" > $1/serial
+ touch $1/index.txt
+
+ echo "Making CA certificate ..."
+ $REQ -new -keyout $1/private/cakey.pem \
+ -out $1/careq.pem
+ $CA -out $1/cacert.pem -batch \
+ -keyfile $1/private/cakey.pem -selfsign \
+ -infiles $1/careq.pem
+}
+
+new_pkgkey() {
+ $REQ -new -keyout pkgkey_key.pem -out pkgkey_req.pem
+ $CA -extensions pkgkey -policy policy_match -out pkgkey_cert.pem -infiles pkgkey_req.pem
+ rm pkgkey_req.pem
+ echo "Signed certificate is in pkgkey_cert.pem, key in pkgkey_key.pem"
+}
+
+new_pkgsec() {
+ $REQ -new -keyout pkgsec_key.pem -out pkgsec_req.pem
+ $CA -extensions pkgsec -policy policy_match -out pkgsec_cert.pem -infiles pkgsec_req.pem
+ rm pkgsec_req.pem
+ echo "Signed certificate is in pkgsec_cert.pem, key in pkgsec_key.pem"
+}
+
+usage() {
+ echo "$0:"
+ echo "setup - create new CA in ./pkgsrc for use by pkg_install"
+ echo "pkgkey - create and sign a certificate for binary packages"
+ echo "pkgsec - create and sign a certificate for pkg-vulnerabilities"
+}
+
+case "$1" in
+setup)
+ new_ca ./pkgsrc
+ ;;
+pkgkey)
+ new_pkgkey
+ ;;
+pkgsec)
+ new_pkgsec
+ ;;
+*)
+ usage
+ ;;
+esac
--- /dev/null
+Use of digital signatures in pkg_install
+----------------------------------------
+
+(1) pkg_vulnerabilities: list of known vulnerabilities, provided by
+ the pkgsrc security team and updated regulary
+(2) binary packages: check who provided binary packages
+
+For (1) gpg is currently the only choice. After pkgsrcCon (?) a PKCS7
+signature will be added as well. With the pkg_install-renovation branch,
+PKCS7 is the only supported verification mechanism for (2) and preferred
+for (1) once the infrastructure exists.
+
+PKCS7 is a format to use RSA public key cryptography with X509
+certificates. Those are commonly used for SSL. X509 implements a
+hierachical trust model. For this purpose it means that one or more
+certificates are installed and marked as trusted. A certificate used for
+signing a binary package or pkg_vulnerabilities will have to be included
+in the list to be trusted OR it must be itself signed by a trusted
+certificate. The original list is called the TRUST ANCHOR.
+
+Optionally, a second list of certificates can be provided to fill gaps.
+Let's assume A is a trust anchor and C is used to sign a package. C
+itself is not signed by A, so it won't be trusted. Instead, there's a
+third certificate B; and C includes a signature with B. The certificate
+chain file can now provide B signed by A. This gives a certificate chain
+of C -> B (included in the package) -> A (with the chain file) and the
+signature is valid and trusted.
+
+
+Practical implications for pkgsrc users:
+- get the pkgsrc-security certificate and point CERTIFICATE_ANCHOR_PKGVULN to it
+- get the certificate used by your bulk builder and point
+CERTIFICATE_ANCHOR_PKGS to it
+- at some later point a CA for pkgsrc might be created, in that case it
+will serve as certificate for both purposes; a list of all certificates
+will be provided in that case to point CERTIFICATE_CHAIN to.
+
+
+How to create your own keys:
+
+The pkgsrc.sh script and the corresponding pkgsrc.cnf file provide a working
+wrapper around the OpenSSL command line tool.
+
+The root certificate can be created by running "sh pkgsrc.sh setup",
+the output can found in the pkgsrc subdirectory of the current directory.
+The meta data is for human beings and displayed e.g. by pkg_add, but not
+relevant for cryptographic purposes. pkgsrc/newcerts/00.pem is the
+public key and can be used as trust anchor.
+A certificate for signing packages can be created by running
+"sh pkgsrc.sh pkgkey". The private key can be found in pkgkey_key.pem
+and the certificate in pkgkey_cert.pem.
+Similary, "sh pkgsrc.sh pkgsec" will create a certificate/key pair for
+signing pkg-vulnerabilities.
+
+How to verify a certificate:
+- decode the data with "openssl x509 -text -noout -in newcert.pem"
+- "Issuer" is vouching for the identity (and reliability) of "Subject"
+- "X509v3 Basic Constraints" should list "CA:FALSE" for all keys that are not allowed
+ to sign further keys.
--- /dev/null
+# $NetBSD: Makefile,v 1.9 2010/04/23 20:56:01 joerg Exp $
+# Original from FreeBSD, no rcs id.
+
+LIBISPRIVATE= yes
+
+LIB= install
+SRCS= automatic.c conflicts.c dewey.c \
+ fexec.c file.c global.c gpgsig.c iterate.c license.c lpkg.c \
+ opattern.c parse-config.c pkcs7.c pkg_signature.c \
+ pkgdb.c pkg_io.c plist.c remove.c \
+ str.c version.c var.c vulnerabilities-file.c xwrapper.c
+MAN= pkg_install.conf.5 pkg_summary.5
+
+version.o: version.h version.c
+
+.include <bsd.init.mk>
+
+pkg_install.conf.5: ${DIST}/lib/pkg_install.conf.5.in
+ ${TOOL_SED} -e 's,@SYSCONFDIR@,/etc,' \
+ ${DIST}/lib/pkg_install.conf.5.in > ${.TARGET}
+
+.PATH: ${DIST}/lib
+
+CLEANFILES+= pkg_install.conf.5
+
+CPPFLAGS+= -DNETBSD
+
+.include <bsd.lib.mk>
--- /dev/null
+/* lib/config.h. Generated from config.h.in by configure. */
+/* lib/config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define to 1 if you have the <assert.h> header file. */
+#define HAVE_ASSERT_H 1
+
+/* Define to 1 if you have the <ctype.h> header file. */
+#define HAVE_CTYPE_H 1
+
+/* Define to 1 if you have the <dirent.h> header file. */
+#define HAVE_DIRENT_H 1
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have the <err.h> header file. */
+#define HAVE_ERR_H 1
+
+/* Define to 1 if you have the <fnctl.h> header file. */
+/* #undef HAVE_FNCTL_H */
+
+/* Define to 1 if you have the <fnmatch.h> header file. */
+#define HAVE_FNMATCH_H 1
+
+/* Define to 1 if you have the <glob.h> header file. */
+#define HAVE_GLOB_H 1
+
+/* Define to 1 if you have the <grp.h> header file. */
+#define HAVE_GRP_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `db' library (-ldb). */
+/* #undef HAVE_LIBDB */
+
+/* Define to 1 if you have the <limits.h> header file. */
+#define HAVE_LIMITS_H 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <pwd.h> header file. */
+#define HAVE_PWD_H 1
+
+/* Define to 1 if you have the <signal.h> header file. */
+#define HAVE_SIGNAL_H 1
+
+/* Define to 1 if you have the <stdarg.h> header file. */
+#define HAVE_STDARG_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/cdefs.h> header file. */
+#define HAVE_SYS_CDEFS_H 1
+
+/* Define to 1 if you have the <sys/file.h> header file. */
+#define HAVE_SYS_FILE_H 1
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/queue.h> header file. */
+#define HAVE_SYS_QUEUE_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/utsname.h> header file. */
+#define HAVE_SYS_UTSNAME_H 1
+
+/* Define to 1 if you have the <sys/wait.h> header file. */
+#define HAVE_SYS_WAIT_H 1
+
+/* Define to 1 if you have the <time.h> header file. */
+#define HAVE_TIME_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the `vfork' function. */
+#define HAVE_VFORK 1
+
+/* Define to 1 if you have the <vis.h> header file. */
+#define HAVE_VIS_H 1
+
+/* Define to 1 if the `z' modifider for printf is missing. */
+/* #undef MISSING_SIZE_T_SUPPORT */
+
+/* Defined when PRIu64 is missing or broken */
+/* #undef NEED_PRI_MACRO */
+
+/* Defined when to retain only the numeric OS version */
+/* #undef NUMERIC_VERSION_ONLY */
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "joerg@NetBSD.org"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "pkg_install"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "pkg_install 20090911"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "pkg_install"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "20090911"
+
+/* The size of `int', as computed by sizeof. */
+#define SIZEOF_INT 4
+
+/* The size of `long', as computed by sizeof. */
+#define SIZEOF_LONG 8
+
+/* The size of `long long', as computed by sizeof. */
+#define SIZEOF_LONG_LONG 8
+
+/* The size of `size_t', as computed by sizeof. */
+#define SIZEOF_SIZE_T 8
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+/* #undef _UINT32_T */
+
+/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+/* #undef _UINT64_T */
+
+/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+/* #undef _UINT8_T */
+
+/* Define to the type of an unsigned integer type of width exactly 16 bits if
+ such a type exists and the standard includes do not define it. */
+/* #undef uint16_t */
+
+/* Define to the type of an unsigned integer type of width exactly 32 bits if
+ such a type exists and the standard includes do not define it. */
+/* #undef uint32_t */
+
+/* Define to the type of an unsigned integer type of width exactly 64 bits if
+ such a type exists and the standard includes do not define it. */
+/* #undef uint64_t */
+
+/* Define to the type of an unsigned integer type of width exactly 8 bits if
+ such a type exists and the standard includes do not define it. */
+/* #undef uint8_t */
+
+#if !HAVE_VFORK
+# define vfork fork
+#endif
+
+#ifndef MISSING_SIZE_T_SUPPORT
+# define PRIzu "zu"
+#elif SIZEOF_SIZE_T == SIZEOF_INT
+# define PRIzu "u"
+#elif SIZEOF_SIZE_T == SIZEOF_LONG
+# define PRIzu "lu"
+#elif SIZEOF_SIZE_T == SIZEOF_LONG_LONG
+# define PRIzu "llu"
+#else
+# errror "Unknown size_t size"
+#endif
+
--- /dev/null
+/* $NetBSD: nbcompat.h,v 1.1 2008/09/30 19:19:56 joerg Exp $ */
+
+/*
+ * In pkgsrc, libnbcompat is used to provide compatibility functions.
+ * This is not needed on native NetBSD, so provide an empty header.
+ */
+
+#include <sys/statvfs.h>
--- /dev/null
+#!/bin/sh
+# $NetBSD: prepare-import.sh,v 1.4 2013/04/20 15:30:34 wiz Exp $
+#
+# Copy new pkgsrc/pkgtools/pkg_install/files to dist.
+# Run this script and check for additional files and
+# directories to prune, only relevant content is included.
+
+set -e
+
+cd dist
+rm -f Makefile.in README config* install-sh tkpkg
+rm -f */Makefile.in */*.cat*
+rm -rf CVS */CVS view
--- /dev/null
+# $NetBSD: Makefile,v 1.3 2009/02/02 20:47:21 joerg Exp $
+
+SUBDIR= bpm pkg_add pkg_admin pkg_create \
+ pkg_delete pkg_info
+
+.include <bsd.subdir.mk>
\ No newline at end of file
--- /dev/null
+# $NetBSD: Makefile.inc,v 1.3 2010/11/05 09:09:01 he Exp $
+
+.include <bsd.own.mk>
+
+.include "${.PARSEDIR}/../Makefile.inc"
+
+LIBINSTALL != cd ${.PARSEDIR}/../lib && ${PRINTOBJDIR}
+
+BINDIR?= /usr/sbin
+CPPFLAGS+= -DBINDIR='"${BINDIR}"'
+
+DPADD+= ${LIBINSTALL}/libinstall.a
+LDADD+= -L${LIBINSTALL} -linstall -ltermcap
+
+DPADD+= ${LIBFETCH} ${LIBSSL} ${LIBCRYPTO}
+LDADD+= -lfetch -lssl -lcrypto
+
+DPADD+= ${LIBARCHIVE}
+LDADD+= -larchive
+
+DPADD+= ${LIBZ} ${LIBBZ2}
+LDADD+= -lz -lbz2
+
+DPADD+= ${LIBLZMA}
+LDADD+= -llzma
--- /dev/null
+# $NetBSD: Makefile,v 1.1 2008/09/30 19:19:56 joerg Exp $
+
+SCRIPTS= bpm.sh
+MAN= bpm.1
+
+.include <bsd.init.mk>
+
+.PATH: ${DIST}/bpm
+
+CLEANFILES+= bpm.sh
+
+bpm.sh: ${DIST}/bpm/bpm.sh.in
+ ${TOOL_CAT} ${DIST}/bpm/bpm.sh.in > ${.TARGET}
+
+.include <bsd.prog.mk>
--- /dev/null
+# $NetBSD: Makefile,v 1.2 2009/02/02 20:47:21 joerg Exp $
+# Original from FreeBSD, no rcs id.
+
+PROG= pkg_add
+SRCS= main.c perform.c
+
+.include <bsd.init.mk>
+
+.PATH: ${DIST}/add
+
+CPPFLAGS+= -DMACHINE_ARCH=\"${MACHINE_ARCH}\"
+
+.include <bsd.prog.mk>
--- /dev/null
+# $NetBSD: Makefile,v 1.2 2009/02/02 20:47:21 joerg Exp $
+
+PROG= pkg_admin
+SRCS= audit.c check.c main.c
+
+SCRIPTS= audit-packages.sh download-vulnerability-list.sh
+CLEANFILES= audit-packages.sh download-vulnerability-list.sh
+
+audit-packages.sh: audit-packages.sh.in
+ sed -e 's|@PKG_ADMIN@|/usr/sbin/pkg_admin|' \
+ ${DIST}/admin/audit-packages.sh.in > $@
+
+download-vulnerability-list.sh: download-vulnerability-list.sh.in
+ sed -e 's|@PKG_ADMIN@|/usr/sbin/pkg_admin|' \
+ ${DIST}/admin/download-vulnerability-list.sh.in > $@
+
+LINK_PKGVUL= yes
+
+.include <bsd.init.mk>
+
+.PATH: ${DIST}/admin
+.include <bsd.prog.mk>
--- /dev/null
+# $NetBSD: Makefile,v 1.2 2009/02/02 20:47:21 joerg Exp $
+# Original from FreeBSD, no rcs id.
+
+PROG= pkg_create
+SRCS= main.c perform.c pl.c util.c build.c
+
+.include <bsd.init.mk>
+
+.PATH: ${DIST}/create
+
+.include <bsd.prog.mk>
--- /dev/null
+# $NetBSD: Makefile,v 1.2 2009/02/25 21:23:17 joerg Exp $
+# Original from FreeBSD, no rcs id.
+
+PROG= pkg_delete
+
+.include <bsd.init.mk>
+
+.PATH: ${DIST}/delete
+
+.include <bsd.prog.mk>
--- /dev/null
+# $NetBSD: Makefile,v 1.3 2011/05/26 12:56:26 joerg Exp $
+# Original from FreeBSD, no rcs id
+
+PROG= pkg_info
+SRCS= main.c perform.c show.c
+
+.include <bsd.init.mk>
+
+.PATH: ${DIST}/info
+
+CWARNFLAGS.clang+= -Wno-format-security
+
+.include <bsd.prog.mk>