Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Merge branch 'master' of github.com:freebsd/pkg
Matthew Seaman committed 11 years ago
commit 9eecfc455c6420367b63a350705171d6d9f108fc
parent 1316949
34 files changed +1143 -828
modified README.md
@@ -471,5 +471,5 @@ If you hit a bug when using pkgng, you can always submit an issue in the [pkgng

[1]: https://github.com/freebsd/pkg
[2]: http://wiki.freebsd.org/pkgng
-
[3]: http://jenkins.unix-heaven.org/jenkins/
+
[3]: http://jenkins.mouf.net/job/pkg/
[4]: https://github.com/freebsd/pkg/issues
modified docs/pkg-lock.8
@@ -15,7 +15,7 @@
.\"     @(#)pkg.8
.\" $FreeBSD$
.\"
-
.Dd April 18, 2014
+
.Dd May 10, 2014
.Dt PKG-LOCK 8
.Os
.Sh NAME
@@ -74,6 +74,8 @@ The following options are supported by
.Bl -tag -width F1
.It Fl a
Lock or unlock all installed packages.
+
.It Fl l
+
List of all locked packages.
.It Fl C
Make the standard or the regular expression
.Fl ( x )
modified libpkg/Makefile.am
@@ -1,6 +1,7 @@
pkg_common_cflags=	-I$(top_srcdir)/libpkg \
			@LIBELF_INCLUDE@ \
			@LIBSBUF_INCLUDE@ \
+
			-I$(top_srcdir)/external/expat/lib \
			-I$(top_srcdir)/external/libucl/include \
			-I$(top_srcdir)/external/uthash \
			-I$(top_srcdir)/external/sqlite \
@@ -15,6 +16,7 @@ libpkg_la_SOURCES= pkg.c \
			packing.c \
			pkg_add.c \
			pkg_attributes.c \
+
			pkg_audit.c \
			pkg_config.c \
			pkg_conflicts.c \
			pkg_cudf.c \
@@ -47,6 +49,7 @@ libpkg_la_CFLAGS= $(pkg_common_cflags) -shared
libpkg_la_LIBADD=	$(top_builddir)/external/libucl.la \
			$(top_builddir)/external/libsqlite.la \
			$(top_builddir)/external/libyaml.la \
+
			$(top_builddir)/external/libexpat.la \
			@LIBELF_LIB@ \
			@LIBSBUF_LIB@ \
			-larchive \
@@ -63,6 +66,7 @@ libpkg_static_la_SOURCES= $(libpkg_la_SOURCES)
libpkg_static_la_CFLAGS=	$(pkg_common_cflags) -static
libpkg_static_la_LIBADD=	$(top_builddir)/external/libucl_static.la \
			$(top_builddir)/external/libsqlite_static.la \
+
			$(top_builddir)/external/libexpat_static.la \
			$(top_builddir)/external/libyaml_static.la

libpkg_static_la_LDFLAGS=	-all-static
modified libpkg/pkg.h.in
@@ -995,9 +995,8 @@ int pkgdb_open_all(struct pkgdb **db, pkgdb_t type, const char *reponame);
/**
 * Locking functions
 */
-
int pkgdb_obtain_lock(struct pkgdb *db, pkgdb_lock_t type, double delay, unsigned int retries);
-
int pkgdb_upgrade_lock(struct pkgdb *db, pkgdb_lock_t old_type, pkgdb_lock_t new_type,
-
		double delay, unsigned int retries);
+
int pkgdb_obtain_lock(struct pkgdb *db, pkgdb_lock_t type);
+
int pkgdb_upgrade_lock(struct pkgdb *db, pkgdb_lock_t old_type, pkgdb_lock_t new_type);
int pkgdb_release_lock(struct pkgdb *db, pkgdb_lock_t type);

/**
@@ -1714,4 +1713,44 @@ bool pkg_is_locked(const struct pkg * restrict p);
 */ 
#define PKG_FILE_CKSUM_CHARS 10

+
struct pkg_audit;
+

+
/**
+
 * Creates new pkg_audit structure
+
 */
+
struct pkg_audit * pkg_audit_new(void);
+

+
/**
+
 * Fetch and extract audit file from url `src` to the file `dest`
+
 * If no update is required then this function returns `EPKG_UPTODATE`
+
 * @return error code
+
 */
+
int pkg_audit_fetch(const char *src, const char *dest);
+

+
/**
+
 * Load audit file into memory
+
 * @return error code
+
 */
+
int pkg_audit_load(struct pkg_audit *audit, const char *fname);
+

+
/**
+
 * Process loaded audit structure.
+
 * Can and should be executed after cap_enter(3) or another sandboxing call
+
 * @return error code
+
 */
+
int pkg_audit_process(struct pkg_audit *audit);
+

+
/**
+
 * Check whether `pkg` is vulnerable against processed `audit` structure.
+
 * If a package is vulnerable, then `result` is set to sbuf describing the
+
 * vulnerability. If `quiet` is true, then this function produces reduced output
+
 * just returning a name of vulnerable package.
+
 * It's caller responsibility to free `result` after use
+
 * @return true and `*result` is set if a package is vulnerable
+
 */
+
bool pkg_audit_is_vulnerable(struct pkg_audit *audit, struct pkg *pkg,
+
		bool quiet, struct sbuf **result);
+
		
+
void pkg_audit_free (struct pkg_audit *audit);
+

#endif
added libpkg/pkg_audit.c
@@ -0,0 +1,870 @@
+
/*-
+
 * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
+
 * Copyright (c) 2014 Matthew Seaman <matthew@FreeBSD.org>
+
 * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.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
+
 *    in this position and unchanged.
+
 * 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(S) ``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(S) 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/param.h>
+
#include <sys/queue.h>
+
#include <sys/stat.h>
+
#include <sys/mman.h>
+

+
#define _WITH_GETLINE
+

+
#include <archive.h>
+
#include <archive.h>
+
#include <err.h>
+
#include <errno.h>
+
#include <fcntl.h>
+
#include <fnmatch.h>
+
#include <stdbool.h>
+
#include <stdio.h>
+
#include <string.h>
+
#include <unistd.h>
+
#include <sysexits.h>
+
#include <utlist.h>
+

+
#include <expat.h>
+

+
#include "pkg.h"
+
#include "private/pkg.h"
+
#include "private/event.h"
+

+
#define EQ 1
+
#define LT 2
+
#define LTE 3
+
#define GT 4
+
#define GTE 5
+

+
struct pkg_audit_version {
+
	char *version;
+
	int type;
+
};
+

+
struct pkg_audit_versions_range {
+
	struct pkg_audit_version v1;
+
	struct pkg_audit_version v2;
+
	struct pkg_audit_versions_range *next;
+
};
+

+
struct pkg_audit_cve {
+
	char *cvename;
+
	struct pkg_audit_cve *next;
+
};
+

+
struct pkg_audit_pkgname {
+
	char *pkgname;
+
	struct pkg_audit_pkgname *next;
+
};
+

+
struct pkg_audit_package {
+
	struct pkg_audit_pkgname *names;
+
	struct pkg_audit_versions_range *versions;
+
	struct pkg_audit_package *next;
+
};
+

+
struct pkg_audit_entry {
+
	const char *pkgname;
+
	struct pkg_audit_package *packages;
+
	struct pkg_audit_pkgname *names;
+
	struct pkg_audit_versions_range *versions;
+
	struct pkg_audit_cve *cve;
+
	char *url;
+
	char *desc;
+
	char *id;
+
	bool ref;
+
	struct pkg_audit_entry *next;
+
};
+

+
/*
+
 * The _sorted stuff.
+
 *
+
 * We are using the optimized search based on the following observations:
+
 *
+
 * - number of VuXML entries is more likely to be far greater than
+
 *   the number of installed ports; thus we should try to optimize
+
 *   the walk through all entries for a given port;
+
 *
+
 * - fnmatch() is good and fast, but if we will compare the audit entry
+
 *   name prefix without globbing characters to the prefix of port name
+
 *   of the same length and they are different, there is no point to
+
 *   check the rest;
+
 *
+
 * - (most important bit): if parsed VuXML entries are lexicographically
+
 *   sorted per the largest prefix with no globbing characters and we
+
 *   know how many succeeding entries have the same prefix we can
+
 *
+
 *   a. skip the rest of the entries once the non-globbing prefix is
+
 *      lexicographically larger than the port name prefix of the
+
 *      same length: all successive prefixes will be larger as well;
+
 *
+
 *   b. if we have non-globbing prefix that is lexicographically smaller
+
 *      than port name prefix, we can skip all succeeding entries with
+
 *      the same prefix; and as some port names tend to repeat due to
+
 *      multiple vulnerabilities, it could be a large win.
+
 */
+
struct pkg_audit_item {
+
	struct pkg_audit_entry *e;	/* Entry itself */
+
	size_t noglob_len;	/* Prefix without glob characters */
+
	size_t next_pfx_incr;	/* Index increment for the entry with
+
				   different prefix */
+
};
+

+
struct pkg_audit {
+
	struct pkg_audit_entry *entries;
+
	struct pkg_audit_item *items;
+
	bool parsed;
+
	bool loaded;
+
	void *map;
+
	size_t len;
+
};
+

+

+
/*
+
 * Another small optimization to skip the beginning of the
+
 * VuXML entry array, if possible.
+
 *
+
 * audit_entry_first_byte_idx[ch] represents the index
+
 * of the first VuXML entry in the sorted array that has
+
 * its non-globbing prefix that is started with the character
+
 * 'ch'.  It allows to skip entries from the beginning of the
+
 * VuXML array that aren't relevant for the checked port name.
+
 */
+
static size_t audit_entry_first_byte_idx[256];
+

+
static void
+
pkg_audit_free_entry(struct pkg_audit_entry *e)
+
{
+
	struct pkg_audit_package *ppkg, *ppkg_tmp;
+
	struct pkg_audit_versions_range *vers, *vers_tmp;
+
	struct pkg_audit_cve *cve, *cve_tmp;
+
	struct pkg_audit_pkgname *pname, *pname_tmp;
+

+
	if (!e->ref) {
+
		LL_FOREACH_SAFE(e->packages, ppkg, ppkg_tmp) {
+
			LL_FOREACH_SAFE(ppkg->versions, vers, vers_tmp) {
+
				if (vers->v1.version) {
+
					free(vers->v1.version);
+
				}
+
				if (vers->v2.version) {
+
					free(vers->v2.version);
+
				}
+
				free(vers);
+
			}
+

+
			LL_FOREACH_SAFE(ppkg->names, pname, pname_tmp) {
+
				if (pname->pkgname)
+
					free(pname->pkgname);
+
				free(pname);
+
			}
+
		}
+
		LL_FOREACH_SAFE(e->cve, cve, cve_tmp) {
+
			if (cve->cvename)
+
				free(cve->cvename);
+
			free(cve);
+
		}
+
		if (e->url)
+
			free(e->url);
+
		if (e->desc)
+
			free(e->desc);
+
		if (e->id)
+
			free(e->id);
+
	}
+
	free(e);
+
}
+

+
static void
+
pkg_audit_free_list(struct pkg_audit_entry *h)
+
{
+
	struct pkg_audit_entry *e;
+

+
	while (h) {
+
		e = h;
+
		h = h->next;
+
		pkg_audit_free_entry(e);
+
	}
+
}
+

+
struct pkg_audit_extract_cbdata {
+
	int out;
+
	const char *fname;
+
	const char *dest;
+
};
+

+
static int
+
pkg_audit_sandboxed_extract(int fd, void *ud)
+
{
+
	struct pkg_audit_extract_cbdata *cbdata = ud;
+
	int ret, rc = EPKG_OK;
+
	struct archive *a = NULL;
+
	struct archive_entry *ae = NULL;
+

+
	a = archive_read_new();
+
#if ARCHIVE_VERSION_NUMBER < 3000002
+
	archive_read_support_compression_all(a);
+
#else
+
	archive_read_support_filter_all(a);
+
#endif
+

+
	archive_read_support_format_raw(a);
+

+
	if (archive_read_open_fd(a, fd, 4096) != ARCHIVE_OK) {
+
		pkg_emit_error("archive_read_open_filename(%s) failed: %s",
+
				cbdata->fname, archive_error_string(a));
+
		rc = EPKG_FATAL;
+
	}
+
	else {
+
		while ((ret = archive_read_next_header(a, &ae)) == ARCHIVE_OK) {
+
			if (archive_read_data_into_fd(a, cbdata->out) != ARCHIVE_OK) {
+
				pkg_emit_error("archive_read_data_into_fd(%s) failed: %s",
+
						cbdata->dest, archive_error_string(a));
+
				break;
+
			}
+
		}
+
		archive_read_close(a);
+
		archive_read_free(a);
+
	}
+

+
	return (rc);
+
}
+

+
int
+
pkg_audit_fetch(const char *src, const char *dest)
+
{
+
	int fd = -1, outfd = -1;
+
	char tmp[MAXPATHLEN];
+
	const char *tmpdir;
+
	int retcode = EPKG_FATAL;
+
	time_t t = 0;
+
	struct stat st;
+
	struct pkg_audit_extract_cbdata cbdata;
+

+
	tmpdir = getenv("TMPDIR");
+
	if (tmpdir == NULL)
+
		tmpdir = "/tmp";
+
	strlcpy(tmp, tmpdir, sizeof(tmp));
+
	strlcat(tmp, "/vuln.xml.bz2.XXXX", sizeof(tmp));
+
	if (stat(dest, &st) != -1) {
+
		t = st.st_mtime;
+
	}
+
	switch (pkg_fetch_file(NULL, src, tmp, t)) {
+
	case EPKG_OK:
+
		break;
+
	case EPKG_UPTODATE:
+
		pkg_emit_notice("vulnxml file up-to-date");
+
		retcode = EPKG_OK;
+
		goto cleanup;
+
	default:
+
		pkg_emit_error("cannot fetch vulnxml file");
+
		goto cleanup;
+
	}
+

+
	/* Open input fd */
+
	fd = open(tmp, O_RDONLY);
+
	if (fd == -1) {
+
		retcode = EPKG_FATAL;
+
		goto cleanup;
+
	}
+
	/* Open out fd */
+
	outfd = open(dest, O_RDWR|O_CREAT|O_TRUNC,
+
			S_IRUSR|S_IRGRP|S_IROTH);
+
	if (outfd == -1) {
+
		pkg_emit_errno("pkg_audit_fetch", "open out fd");
+
		goto cleanup;
+
	}
+

+
	cbdata.fname = tmp;
+
	cbdata.out = outfd;
+
	cbdata.dest = dest;
+

+
	/* Call sandboxed */
+
	retcode = pkg_emit_sandbox_call(pkg_audit_sandboxed_extract, fd, &cbdata);
+

+
cleanup:
+
	unlink(tmp);
+

+
	if (fd != -1)
+
		close(fd);
+
	if (outfd != -1)
+
		close(outfd);
+

+
	return (retcode);
+
}
+

+
/*
+
 * Expand multiple names to a set of audit entries
+
 */
+
static void
+
pkg_audit_expand_entry(struct pkg_audit_entry *entry, struct pkg_audit_entry **head)
+
{
+
	struct pkg_audit_entry *n;
+
	struct pkg_audit_pkgname *ncur;
+
	struct pkg_audit_package *pcur;
+

+
	/* Set the name of the current entry */
+
	if (entry->packages == NULL || entry->packages->names == NULL) {
+
		pkg_audit_free_entry(entry);
+
		return;
+
	}
+

+
	LL_FOREACH(entry->packages, pcur) {
+
		LL_FOREACH(pcur->names, ncur) {
+
			n = calloc(1, sizeof(struct pkg_audit_entry));
+
			if (n == NULL)
+
				err(1, "calloc(audit_entry)");
+
			n->pkgname = ncur->pkgname;
+
			/* Set new entry as reference entry */
+
			n->ref = true;
+
			n->cve = entry->cve;
+
			n->desc = entry->desc;
+
			n->versions = pcur->versions;
+
			n->url = entry->url;
+
			n->id = entry->id;
+
			LL_PREPEND(*head, n);
+
		}
+
	}
+
	LL_PREPEND(*head, entry);
+
}
+

+
enum vulnxml_parse_state {
+
	VULNXML_PARSE_INIT = 0,
+
	VULNXML_PARSE_VULN,
+
	VULNXML_PARSE_TOPIC,
+
	VULNXML_PARSE_PACKAGE,
+
	VULNXML_PARSE_PACKAGE_NAME,
+
	VULNXML_PARSE_RANGE,
+
	VULNXML_PARSE_RANGE_GT,
+
	VULNXML_PARSE_RANGE_GE,
+
	VULNXML_PARSE_RANGE_LT,
+
	VULNXML_PARSE_RANGE_LE,
+
	VULNXML_PARSE_RANGE_EQ,
+
	VULNXML_PARSE_CVE
+
};
+

+
struct vulnxml_userdata {
+
	struct pkg_audit_entry *cur_entry;
+
	struct pkg_audit *audit;
+
	enum vulnxml_parse_state state;
+
	int range_num;
+
};
+

+
static void
+
vulnxml_start_element(void *data, const char *element, const char **attributes)
+
{
+
	struct vulnxml_userdata *ud = (struct vulnxml_userdata *)data;
+
	struct pkg_audit_versions_range *vers;
+
	struct pkg_audit_pkgname *name_entry;
+
	struct pkg_audit_package *pkg_entry;
+
	int i;
+

+
	if (ud->state == VULNXML_PARSE_INIT && strcasecmp(element, "vuln") == 0) {
+
		ud->cur_entry = calloc(1, sizeof(struct pkg_audit_entry));
+
		if (ud->cur_entry == NULL)
+
			err(1, "calloc(audit_entry)");
+
		for (i = 0; attributes[i]; i += 2) {
+
			if (strcasecmp(attributes[i], "vid") == 0) {
+
				ud->cur_entry->id = strdup(attributes[i + 1]);
+
				break;
+
			}
+
		}
+
		ud->cur_entry->next = ud->audit->entries;
+
		ud->state = VULNXML_PARSE_VULN;
+
	}
+
	else if (ud->state == VULNXML_PARSE_VULN && strcasecmp(element, "topic") == 0) {
+
		ud->state = VULNXML_PARSE_TOPIC;
+
	}
+
	else if (ud->state == VULNXML_PARSE_VULN && strcasecmp(element, "package") == 0) {
+
		pkg_entry = calloc(1, sizeof(struct pkg_audit_package));
+
		if (pkg_entry == NULL)
+
			err(1, "calloc(audit_package_entry)");
+
		LL_PREPEND(ud->cur_entry->packages, pkg_entry);
+
		ud->state = VULNXML_PARSE_PACKAGE;
+
	}
+
	else if (ud->state == VULNXML_PARSE_VULN && strcasecmp(element, "cvename") == 0) {
+
		ud->state = VULNXML_PARSE_CVE;
+
	}
+
	else if (ud->state == VULNXML_PARSE_PACKAGE && strcasecmp(element, "name") == 0) {
+
		ud->state = VULNXML_PARSE_PACKAGE_NAME;
+
		name_entry = calloc(1, sizeof(struct pkg_audit_pkgname));
+
		if (name_entry == NULL)
+
			err(1, "calloc(audit_pkgname_entry)");
+
		LL_PREPEND(ud->cur_entry->packages->names, name_entry);
+
	}
+
	else if (ud->state == VULNXML_PARSE_PACKAGE && strcasecmp(element, "range") == 0) {
+
		ud->state = VULNXML_PARSE_RANGE;
+
		vers = calloc(1, sizeof(struct pkg_audit_versions_range));
+
		if (vers == NULL)
+
			err(1, "calloc(audit_versions)");
+
		LL_PREPEND(ud->cur_entry->packages->versions, vers);
+
		ud->range_num = 0;
+
	}
+
	else if (ud->state == VULNXML_PARSE_RANGE && strcasecmp(element, "gt") == 0) {
+
		ud->range_num ++;
+
		ud->state = VULNXML_PARSE_RANGE_GT;
+
	}
+
	else if (ud->state == VULNXML_PARSE_RANGE && strcasecmp(element, "ge") == 0) {
+
		ud->range_num ++;
+
		ud->state = VULNXML_PARSE_RANGE_GE;
+
	}
+
	else if (ud->state == VULNXML_PARSE_RANGE && strcasecmp(element, "lt") == 0) {
+
		ud->range_num ++;
+
		ud->state = VULNXML_PARSE_RANGE_LT;
+
	}
+
	else if (ud->state == VULNXML_PARSE_RANGE && strcasecmp(element, "le") == 0) {
+
		ud->range_num ++;
+
		ud->state = VULNXML_PARSE_RANGE_LE;
+
	}
+
	else if (ud->state == VULNXML_PARSE_RANGE && strcasecmp(element, "eq") == 0) {
+
		ud->range_num ++;
+
		ud->state = VULNXML_PARSE_RANGE_EQ;
+
	}
+
}
+

+
static void
+
vulnxml_end_element(void *data, const char *element)
+
{
+
	struct vulnxml_userdata *ud = (struct vulnxml_userdata *)data;
+

+
	if (ud->state == VULNXML_PARSE_VULN && strcasecmp(element, "vuln") == 0) {
+
		pkg_audit_expand_entry(ud->cur_entry, &ud->audit->entries);
+
		ud->state = VULNXML_PARSE_INIT;
+
	}
+
	else if (ud->state == VULNXML_PARSE_TOPIC && strcasecmp(element, "topic") == 0) {
+
		ud->state = VULNXML_PARSE_VULN;
+
	}
+
	else if (ud->state == VULNXML_PARSE_CVE && strcasecmp(element, "cvename") == 0) {
+
		ud->state = VULNXML_PARSE_VULN;
+
	}
+
	else if (ud->state == VULNXML_PARSE_PACKAGE && strcasecmp(element, "package") == 0) {
+
		ud->state = VULNXML_PARSE_VULN;
+
	}
+
	else if (ud->state == VULNXML_PARSE_PACKAGE_NAME && strcasecmp(element, "name") == 0) {
+
		ud->state = VULNXML_PARSE_PACKAGE;
+
	}
+
	else if (ud->state == VULNXML_PARSE_RANGE && strcasecmp(element, "range") == 0) {
+
		ud->state = VULNXML_PARSE_PACKAGE;
+
	}
+
	else if (ud->state == VULNXML_PARSE_RANGE_GT && strcasecmp(element, "gt") == 0) {
+
		ud->state = VULNXML_PARSE_RANGE;
+
	}
+
	else if (ud->state == VULNXML_PARSE_RANGE_GE && strcasecmp(element, "ge") == 0) {
+
		ud->state = VULNXML_PARSE_RANGE;
+
	}
+
	else if (ud->state == VULNXML_PARSE_RANGE_LT && strcasecmp(element, "lt") == 0) {
+
		ud->state = VULNXML_PARSE_RANGE;
+
	}
+
	else if (ud->state == VULNXML_PARSE_RANGE_LE && strcasecmp(element, "le") == 0) {
+
		ud->state = VULNXML_PARSE_RANGE;
+
	}
+
	else if (ud->state == VULNXML_PARSE_RANGE_EQ && strcasecmp(element, "eq") == 0) {
+
		ud->state = VULNXML_PARSE_RANGE;
+
	}
+
}
+

+
static void
+
vulnxml_handle_data(void *data, const char *content, int length)
+
{
+
	struct vulnxml_userdata *ud = (struct vulnxml_userdata *)data;
+
	struct pkg_audit_versions_range *vers;
+
	struct pkg_audit_cve *cve;
+
	struct pkg_audit_entry *entry;
+
	int range_type = -1;
+

+
	switch(ud->state) {
+
	case VULNXML_PARSE_INIT:
+
	case VULNXML_PARSE_VULN:
+
	case VULNXML_PARSE_PACKAGE:
+
	case VULNXML_PARSE_RANGE:
+
		/* On these states we do not need any data */
+
		break;
+
	case VULNXML_PARSE_TOPIC:
+
		ud->cur_entry->desc = strndup(content, length);
+
		break;
+
	case VULNXML_PARSE_PACKAGE_NAME:
+
		ud->cur_entry->packages->names->pkgname = strndup(content, length);
+
		break;
+
	case VULNXML_PARSE_RANGE_GT:
+
		range_type = GT;
+
		break;
+
	case VULNXML_PARSE_RANGE_GE:
+
		range_type = GTE;
+
		break;
+
	case VULNXML_PARSE_RANGE_LT:
+
		range_type = LT;
+
		break;
+
	case VULNXML_PARSE_RANGE_LE:
+
		range_type = LTE;
+
		break;
+
	case VULNXML_PARSE_RANGE_EQ:
+
		range_type = EQ;
+
		break;
+
	case VULNXML_PARSE_CVE:
+
		entry = ud->cur_entry;
+
		cve = malloc(sizeof(struct pkg_audit_cve));
+
		cve->cvename = strndup(content, length);
+
		LL_PREPEND(entry->cve, cve);
+
		break;
+
	}
+

+
	if (range_type > 0) {
+
		vers = ud->cur_entry->packages->versions;
+
		if (ud->range_num == 1) {
+
			vers->v1.version = strndup(content, length);
+
			vers->v1.type = range_type;
+
		}
+
		else if (ud->range_num == 2) {
+
			vers->v2.version = strndup(content, length);
+
			vers->v2.type = range_type;
+
		}
+
	}
+
}
+

+
static int
+
pkg_audit_parse_vulnxml(struct pkg_audit *audit)
+
{
+
	XML_Parser parser;
+
	struct vulnxml_userdata ud;
+
	int ret = EPKG_OK;
+

+
	parser = XML_ParserCreate(NULL);
+
	XML_SetElementHandler(parser, vulnxml_start_element, vulnxml_end_element);
+
	XML_SetCharacterDataHandler(parser, vulnxml_handle_data);
+
	XML_SetUserData(parser, &ud);
+

+
	ud.cur_entry = NULL;
+
	ud.audit = audit;
+
	ud.range_num = 0;
+
	ud.state = VULNXML_PARSE_INIT;
+

+
	if (XML_Parse(parser, audit->map, audit->len, XML_TRUE) == XML_STATUS_ERROR) {
+
		pkg_emit_error("vulnxml parsing error: %s",
+
				XML_ErrorString(XML_GetErrorCode(parser)));
+
		ret = EPKG_FATAL;
+
	}
+

+
	XML_ParserFree(parser);
+

+
	return (ret);
+
}
+

+
/*
+
 * Returns the length of the largest prefix without globbing
+
 * characters, as per fnmatch().
+
 */
+
static size_t
+
pkg_audit_str_noglob_len(const char *s)
+
{
+
	size_t n;
+

+
	for (n = 0; s[n] && s[n] != '*' && s[n] != '?' &&
+
	    s[n] != '[' && s[n] != '{' && s[n] != '\\'; n++);
+

+
	return (n);
+
}
+

+
/*
+
 * Helper for quicksort that lexicographically orders prefixes.
+
 */
+
static int
+
pkg_audit_entry_cmp(const void *a, const void *b)
+
{
+
	const struct pkg_audit_item *e1, *e2;
+
	size_t min_len;
+
	int result;
+

+
	e1 = (const struct pkg_audit_item *)a;
+
	e2 = (const struct pkg_audit_item *)b;
+

+
	min_len = (e1->noglob_len < e2->noglob_len ?
+
	    e1->noglob_len : e2->noglob_len);
+
	result = strncmp(e1->e->pkgname, e2->e->pkgname, min_len);
+
	/*
+
	 * Additional check to see if some word is a prefix of an
+
	 * another one and, thus, should go before the former.
+
	 */
+
	if (result == 0) {
+
		if (e1->noglob_len < e2->noglob_len)
+
			result = -1;
+
		else if (e1->noglob_len > e2->noglob_len)
+
			result = 1;
+
	}
+

+
	return (result);
+
}
+

+
/*
+
 * Sorts VuXML entries and calculates increments to jump to the
+
 * next distinct prefix.
+
 */
+
static struct pkg_audit_item *
+
pkg_audit_preprocess(struct pkg_audit_entry *h)
+
{
+
	struct pkg_audit_entry *e;
+
	struct pkg_audit_item *ret;
+
	size_t i, n, tofill;
+

+
	n = 0;
+
	LL_FOREACH(h, e)
+
		n++;
+

+
	ret = (struct pkg_audit_item *)calloc(n + 1, sizeof(ret[0]));
+
	if (ret == NULL)
+
		err(1, "calloc(audit_entry_sorted*)");
+
	bzero((void *)ret, (n + 1) * sizeof(ret[0]));
+

+
	n = 0;
+
	LL_FOREACH(h, e) {
+
		if (e->pkgname != NULL) {
+
			ret[n].e = e;
+
			ret[n].noglob_len = pkg_audit_str_noglob_len(e->pkgname);
+
			ret[n].next_pfx_incr = 1;
+
			n++;
+
		}
+
	}
+

+
	qsort(ret, n, sizeof(*ret), pkg_audit_entry_cmp);
+

+
	/*
+
	 * Determining jump indexes to the next different prefix.
+
	 * Only non-1 increments are calculated there.
+
	 *
+
	 * Due to the current usage that picks only increment for the
+
	 * first of the non-unique prefixes in a row, we could
+
	 * calculate only that one and skip calculations for the
+
	 * succeeding, but for the uniformity and clarity we're
+
	 * calculating 'em all.
+
	 */
+
	for (n = 1, tofill = 0; ret[n].e; n++) {
+
		if (ret[n - 1].noglob_len != ret[n].noglob_len) {
+
			struct pkg_audit_item *base;
+

+
			base = ret + n - tofill;
+
			for (i = 0; tofill > 1; i++, tofill--)
+
				base[i].next_pfx_incr = tofill;
+
			tofill = 1;
+
		} else if (strcmp(ret[n - 1].e->pkgname,
+
		    ret[n].e->pkgname) == 0) {
+
			tofill++;
+
		} else {
+
			tofill = 1;
+
		}
+
	}
+

+
	/* Calculate jump indexes for the first byte of the package name */
+
	bzero(audit_entry_first_byte_idx, sizeof(audit_entry_first_byte_idx));
+
	for (n = 1, i = 0; n < 256; n++) {
+
		while (ret[i].e != NULL &&
+
		    (size_t)(ret[i].e->pkgname[0]) < n)
+
			i++;
+
		audit_entry_first_byte_idx[n] = i;
+
	}
+

+
	return (ret);
+
}
+

+
static bool
+
pkg_audit_version_match(const char *pkgversion, struct pkg_audit_version *v)
+
{
+
	bool res = false;
+

+
	/*
+
	 * Return true so it is easier for the caller to handle case where there is
+
	 * only one version to match: the missing one will always match.
+
	 */
+
	if (v->version == NULL)
+
		return (true);
+

+
	switch (pkg_version_cmp(pkgversion, v->version)) {
+
	case -1:
+
		if (v->type == LT || v->type == LTE)
+
			res = true;
+
		break;
+
	case 0:
+
		if (v->type == EQ || v->type == LTE || v->type == GTE)
+
			res = true;
+
		break;
+
	case 1:
+
		if (v->type == GT || v->type == GTE)
+
			res = true;
+
		break;
+
	}
+
	return (res);
+
}
+

+
bool
+
pkg_audit_is_vulnerable(struct pkg_audit *audit, struct pkg *pkg,
+
		bool quiet, struct sbuf **result)
+
{
+
	struct pkg_audit_entry *e;
+
	struct pkg_audit_versions_range *vers;
+
	struct pkg_audit_cve *cve;
+
	const char *pkgname;
+
	const char *pkgversion;
+
	struct sbuf *sb;
+
	struct pkg_audit_item *a;
+
	bool res = false, res1, res2;
+

+
	if (!audit->parsed)
+
		return false;
+

+
	pkg_get(pkg,
+
		PKG_NAME, &pkgname,
+
		PKG_VERSION, &pkgversion
+
	);
+

+
	a = audit->items;
+
	a += audit_entry_first_byte_idx[(size_t)pkgname[0]];
+
	sb = sbuf_new_auto();
+

+
	for (; (e = a->e) != NULL; a += a->next_pfx_incr) {
+
		int cmp;
+
		size_t i;
+

+
		/*
+
		 * Audit entries are sorted, so if we had found one
+
		 * that is lexicographically greater than our name,
+
		 * it and the rest won't match our name.
+
		 */
+
		cmp = strncmp(pkgname, e->pkgname, a->noglob_len);
+
		if (cmp > 0)
+
			continue;
+
		else if (cmp < 0)
+
			break;
+

+
		for (i = 0; i < a->next_pfx_incr; i++) {
+
			e = a[i].e;
+
			if (fnmatch(e->pkgname, pkgname, 0) != 0)
+
				continue;
+

+
			LL_FOREACH(e->versions, vers) {
+
				res1 = pkg_audit_version_match(pkgversion, &vers->v1);
+
				res2 = pkg_audit_version_match(pkgversion, &vers->v2);
+
				if (res1 && res2) {
+

+
					res = true;
+
					if (quiet) {
+
						sbuf_printf(sb, "%s-%s\n", pkgname, pkgversion);
+
						sbuf_finish(sb);
+
						*result = sb;
+
						return (res);
+
					} else {
+
						sbuf_printf(sb, "%s-%s is vulnerable:\n", pkgname, pkgversion);
+
						sbuf_printf(sb, "%s\n", e->desc);
+
						/* XXX: for vulnxml we should use more clever approach indeed */
+
						if (e->cve) {
+
							cve = e->cve;
+
							while (cve) {
+
								sbuf_printf(sb, "CVE: %s\n", cve->cvename);
+
								cve = cve->next;
+
							}
+
						}
+
						if (e->url)
+
							sbuf_printf(sb, "WWW: %s\n\n", e->url);
+
						else if (e->id)
+
							sbuf_printf(sb, "WWW: http://portaudit.FreeBSD.org/%s.html\n\n", e->id);
+
					}
+
					break;
+
				}
+
			}
+
		}
+
	}
+

+
	if (res) {
+
		sbuf_finish(sb);
+
		*result = sb;
+
	}
+
	else {
+
		sbuf_delete(sb);
+
	}
+

+
	return (res);
+
}
+

+
struct pkg_audit *
+
pkg_audit_new(void)
+
{
+
	struct pkg_audit *audit;
+

+
	audit = calloc(1, sizeof(struct pkg_audit));
+

+
	return (audit);
+
}
+

+
int
+
pkg_audit_load(struct pkg_audit *audit, const char *fname)
+
{
+
	int fd;
+
	void *mem;
+
	struct stat st;
+

+
	if (stat(fname, &st) == -1)
+
		return (EPKG_FATAL);
+

+
	if ((fd = open(fname, O_RDONLY)) == -1)
+
		return (EPKG_FATAL);
+

+
	if ((mem = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
+
		close(fd);
+
		return (EPKG_FATAL);
+
	}
+
	close(fd);
+

+
	audit->map = mem;
+
	audit->len = st.st_size;
+
	audit->loaded = true;
+

+
	return (EPKG_OK);
+
}
+

+
/* This can and should be executed after cap_enter(3) */
+
int
+
pkg_audit_process(struct pkg_audit *audit)
+
{
+
	if (!audit->loaded)
+
		return (EPKG_FATAL);
+

+
	if (pkg_audit_parse_vulnxml(audit) == EPKG_FATAL)
+
		return (EPKG_FATAL);
+

+
	audit->items = pkg_audit_preprocess(audit->entries);
+
	audit->parsed = true;
+

+
	return (EPKG_OK);
+
}
+

+
void
+
pkg_audit_free (struct pkg_audit *audit)
+
{
+
	if (audit != NULL) {
+
		if (audit->parsed) {
+
			pkg_audit_free_list(audit->entries);
+
			free(audit->items);
+
		}
+
		if (audit->loaded) {
+
			munmap(audit->map, audit->len);
+
		}
+
		free(audit);
+
	}
+
}
modified libpkg/pkg_config.c
@@ -294,6 +294,18 @@ static struct config_entry c[] = {
		"NO",
		"Match package names case sensitively",
	},
+
	{
+
		PKG_INT,
+
		"LOCK_WAIT",
+
		"1",
+
		"Wait time to regain a lock if it is not available"
+
	},
+
	{
+
		PKG_INT,
+
		"LOCK_RETRIES",
+
		"5",
+
		"Retries performed to obtain a lock"
+
	}
};

static bool parsed = false;
modified libpkg/pkg_jobs.c
@@ -2021,7 +2021,7 @@ pkg_jobs_execute(struct pkg_jobs *j)

	/* XXX: get rid of hardcoded values */
	retcode = pkgdb_upgrade_lock(j->db, PKGDB_LOCK_ADVISORY,
-
			PKGDB_LOCK_EXCLUSIVE, 0.5, 20);
+
			PKGDB_LOCK_EXCLUSIVE);
	if (retcode != EPKG_OK)
		return (retcode);

modified libpkg/pkgdb.c
@@ -4175,13 +4175,15 @@ pkgdb_check_lock_pid(struct pkgdb *db)
		if (pid != lpid) {
			if (kill((pid_t)pid, 0) == -1) {
				pkg_debug(1, "found stale pid %lld in lock database, my pid is: %lld",
-
						pid, lpid);
+
						(long long)pid, (long long)lpid);
				if (pkgdb_remove_lock_pid(db, pid) != EPKG_OK){
					sqlite3_finalize(stmt);
					return (EPKG_FATAL);
				}
			}
			else {
+
				pkg_emit_notice("process with pid %lld still holds the lock",
+
						(long long int)pid);
				found ++;
			}
		}
@@ -4209,15 +4211,24 @@ pkgdb_reset_lock(struct pkgdb *db)
}

static int
-
pkgdb_try_lock(struct pkgdb *db, const char *lock_sql,
-
		double delay, unsigned int retries, pkgdb_lock_t type,
+
pkgdb_try_lock(struct pkgdb *db, const char *lock_sql, pkgdb_lock_t type,
		bool upgrade)
{
	unsigned int tries = 0;
	struct timespec ts;
	int ret = EPKG_END;
+
	const pkg_object *timeout, *max_tries;
+
	int64_t num_timeout = 1, num_maxtries = 1;

-
	while (tries <= retries) {
+
	timeout = pkg_config_get("LOCK_WAIT");
+
	max_tries = pkg_config_get("LOCK_RETRIES");
+

+
	if (timeout)
+
		num_timeout = pkg_object_int(timeout);
+
	if (max_tries)
+
		num_maxtries = pkg_object_int(max_tries);
+

+
	while (tries <= num_maxtries) {
		ret = sqlite3_exec(db->sqlite, lock_sql, NULL, NULL, NULL);
		if (ret != SQLITE_OK) {
			if (ret == SQLITE_READONLY && type == PKGDB_LOCK_READONLY) {
@@ -4240,15 +4251,15 @@ pkgdb_try_lock(struct pkgdb *db, const char *lock_sql,
					 * hence switch upgrade to retain
					 */
					pkgdb_remove_lock_pid(db, (int64_t)getpid());
-
					return pkgdb_obtain_lock(db, type, delay, retries - tries);
+
					return pkgdb_obtain_lock(db, type);
				}
				continue;
			}
-
			else if (delay > 0) {
-
				ts.tv_sec = (int)delay;
-
				ts.tv_nsec = (delay - (int)delay) * 1000000000.;
+
			else if (num_timeout > 0) {
+
				ts.tv_sec = (int)num_timeout;
+
				ts.tv_nsec = (num_timeout - (int)num_timeout) * 1000000000.;
				pkg_debug(1, "waiting for database lock for %d times, "
-
						"next try in %.2f seconds", tries, delay);
+
						"next try in %.2f seconds", tries, num_timeout);
				(void)nanosleep(&ts, NULL);
			}
			else {
@@ -4270,8 +4281,7 @@ pkgdb_try_lock(struct pkgdb *db, const char *lock_sql,
}

int
-
pkgdb_obtain_lock(struct pkgdb *db, pkgdb_lock_t type,
-
		double delay, unsigned int retries)
+
pkgdb_obtain_lock(struct pkgdb *db, pkgdb_lock_t type)
{
	int ret;
	const char table_sql[] = ""
@@ -4323,14 +4333,13 @@ pkgdb_obtain_lock(struct pkgdb *db, pkgdb_lock_t type,
		break;
	}

-
	ret = pkgdb_try_lock(db, lock_sql, delay, retries, type, false);
+
	ret = pkgdb_try_lock(db, lock_sql, type, false);

	return (ret);
}

int
-
pkgdb_upgrade_lock(struct pkgdb *db, pkgdb_lock_t old_type, pkgdb_lock_t new_type,
-
		double delay, unsigned int retries)
+
pkgdb_upgrade_lock(struct pkgdb *db, pkgdb_lock_t old_type, pkgdb_lock_t new_type)
{
	const char advisory_exclusive_lock_sql[] = ""
		"UPDATE pkg_lock SET exclusive=1,advisory=1 WHERE exclusive=0 AND advisory=1 AND read=0;";
@@ -4340,7 +4349,7 @@ pkgdb_upgrade_lock(struct pkgdb *db, pkgdb_lock_t old_type, pkgdb_lock_t new_typ

	if (old_type == PKGDB_LOCK_ADVISORY && new_type == PKGDB_LOCK_EXCLUSIVE) {
		pkg_debug(1, "want to upgrade advisory to exclusive lock");
-
		ret = pkgdb_try_lock(db, advisory_exclusive_lock_sql, delay, retries,
+
		ret = pkgdb_try_lock(db, advisory_exclusive_lock_sql,
				new_type, true);
	}

modified libpkg/private/pkg.h
@@ -530,7 +530,9 @@ int do_extract_mtree(char *mtree, const char *prefix);

int pkg_repo_update_binary_pkgs(struct pkg_repo *repo, bool force);

-
bool ucl_object_emit_sbuf(ucl_object_t *obj, enum ucl_emitter emit_type, struct sbuf **buf);
-
bool ucl_object_emit_file(ucl_object_t *obj, enum ucl_emitter emit_type, FILE *);
+
bool ucl_object_emit_sbuf(const ucl_object_t *obj, enum ucl_emitter emit_type,
+
    struct sbuf **buf);
+
bool ucl_object_emit_file(const ucl_object_t *obj, enum ucl_emitter emit_type,
+
    FILE *);

#endif
modified libpkg/utils.c
@@ -805,7 +805,8 @@ ucl_sbuf_append_double(double val, void *data)
}

bool
-
ucl_object_emit_file(ucl_object_t *obj, enum ucl_emitter emit_type, FILE *out)
+
ucl_object_emit_file(const ucl_object_t *obj, enum ucl_emitter emit_type,
+
    FILE *out)
{
	struct ucl_emitter_functions func = {
		.ucl_emitter_append_character = ucl_file_append_character,
@@ -814,12 +815,10 @@ ucl_object_emit_file(ucl_object_t *obj, enum ucl_emitter emit_type, FILE *out)
		.ucl_emitter_append_double = ucl_file_append_double
	};

-
	func.ud = out;
-

	if (obj == NULL)
		return (false);

-
	func.ud = NULL;
+
	func.ud = out;

	return (ucl_object_emit_full(obj, emit_type, &func));

@@ -827,7 +826,7 @@ ucl_object_emit_file(ucl_object_t *obj, enum ucl_emitter emit_type, FILE *out)
}

bool
-
ucl_object_emit_sbuf(ucl_object_t *obj, enum ucl_emitter emit_type,
+
ucl_object_emit_sbuf(const ucl_object_t *obj, enum ucl_emitter emit_type,
                     struct sbuf **buf)
{
	bool ret = false;
modified src/Makefile.am
@@ -37,7 +37,6 @@ pkg_SOURCES= add.c \
			which.c
			
pkg_LDADD=	$(top_builddir)/libpkg/libpkg.la \
-
			$(top_builddir)/external/libexpat.la \
			@LIBJAIL_LIB@
pkg_CFLAGS=		-I$(top_srcdir)/libpkg \
			-I$(top_srcdir)/external/uthash \
modified src/add.c
@@ -132,7 +132,7 @@ exec_add(int argc, char **argv)
	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
		return (EX_IOERR);

-
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE, 0, 0) != EPKG_OK) {
+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get an exclusive lock on a database, it is locked by another process");
		return (EX_TEMPFAIL);
modified src/annotate.c
@@ -298,7 +298,7 @@ exec_annotate(int argc, char **argv)
		return (EX_IOERR);
	}

-
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE, 0, 0) != EPKG_OK) {
+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get an exclusive lock on a database, it is locked by another process");
		return (EX_TEMPFAIL);
modified src/audit.c
@@ -44,102 +44,9 @@
#include <sysexits.h>
#include <utlist.h>

-
#include <expat.h>
-

#include <pkg.h>
#include "pkgcli.h"

-
#define EQ 1
-
#define LT 2
-
#define LTE 3
-
#define GT 4
-
#define GTE 5
-
struct version_entry {
-
	char *version;
-
	int type;
-
};
-

-
struct audit_versions {
-
	struct version_entry v1;
-
	struct version_entry v2;
-
	struct audit_versions *next;
-
};
-

-
struct audit_cve {
-
	char *cvename;
-
	struct audit_cve *next;
-
};
-

-
struct audit_pkgname_entry {
-
	char *pkgname;
-
	struct audit_pkgname_entry *next;
-
};
-

-
struct audit_package_entry {
-
	struct audit_pkgname_entry *names;
-
	struct audit_versions *versions;
-
	struct audit_package_entry *next;
-
};
-

-
struct audit_entry {
-
	const char *pkgname;
-
	struct audit_package_entry *packages;
-
	struct audit_pkgname_entry *names;
-
	struct audit_versions *versions;
-
	struct audit_cve *cve;
-
	char *url;
-
	char *desc;
-
	char *id;
-
	bool ref;
-
	struct audit_entry *next;
-
};
-

-
/*
-
 * The _sorted stuff.
-
 *
-
 * We are using the optimized search based on the following observations:
-
 *
-
 * - number of VuXML entries is more likely to be far greater than
-
 *   the number of installed ports; thus we should try to optimize
-
 *   the walk through all entries for a given port;
-
 *
-
 * - fnmatch() is good and fast, but if we will compare the audit entry
-
 *   name prefix without globbing characters to the prefix of port name
-
 *   of the same length and they are different, there is no point to
-
 *   check the rest;
-
 *
-
 * - (most important bit): if parsed VuXML entries are lexicographically
-
 *   sorted per the largest prefix with no globbing characters and we
-
 *   know how many succeeding entries have the same prefix we can
-
 *
-
 *   a. skip the rest of the entries once the non-globbing prefix is
-
 *      lexicographically larger than the port name prefix of the
-
 *      same length: all successive prefixes will be larger as well;
-
 *
-
 *   b. if we have non-globbing prefix that is lexicographically smaller
-
 *      than port name prefix, we can skip all succeeding entries with
-
 *      the same prefix; and as some port names tend to repeat due to
-
 *      multiple vulnerabilities, it could be a large win.
-
 */
-
struct audit_entry_sorted {
-
	struct audit_entry *e;	/* Entry itself */
-
	size_t noglob_len;	/* Prefix without glob characters */
-
	size_t next_pfx_incr;	/* Index increment for the entry with
-
				   different prefix */
-
};
-

-
/*
-
 * Another small optimization to skip the beginning of the
-
 * VuXML entry array, if possible.
-
 *
-
 * audit_entry_first_byte_idx[ch] represents the index
-
 * of the first VuXML entry in the sorted array that has
-
 * its non-globbing prefix that is started with the character
-
 * 'ch'.  It allows to skip entries from the beginning of the
-
 * VuXML array that aren't relevant for the checked port name.
-
 */
-
static size_t audit_entry_first_byte_idx[256];
-

void
usage_audit(void)
{
@@ -147,626 +54,29 @@ usage_audit(void)
	fprintf(stderr, "For more information see 'pkg help audit'.\n");
}

-
static void
-
free_audit_entry(struct audit_entry *e)
-
{
-
	struct audit_package_entry *ppkg, *ppkg_tmp;
-
	struct audit_versions *vers, *vers_tmp;
-
	struct audit_cve *cve, *cve_tmp;
-
	struct audit_pkgname_entry *pname, *pname_tmp;
-

-
	if (!e->ref) {
-
		LL_FOREACH_SAFE(e->packages, ppkg, ppkg_tmp) {
-
			LL_FOREACH_SAFE(ppkg->versions, vers, vers_tmp) {
-
				if (vers->v1.version) {
-
					free(vers->v1.version);
-
				}
-
				if (vers->v2.version) {
-
					free(vers->v2.version);
-
				}
-
				free(vers);
-
			}
-

-
			LL_FOREACH_SAFE(ppkg->names, pname, pname_tmp) {
-
				if (pname->pkgname)
-
					free(pname->pkgname);
-
				free(pname);
-
			}
-
		}
-
		LL_FOREACH_SAFE(e->cve, cve, cve_tmp) {
-
			if (cve->cvename)
-
				free(cve->cvename);
-
			free(cve);
-
		}
-
		if (e->url)
-
			free(e->url);
-
		if (e->desc)
-
			free(e->desc);
-
		if (e->id)
-
			free(e->id);
-
	}
-
	free(e);
-
}
-

-
static int
-
fetch_and_extract(const char *src, const char *dest)
-
{
-
	struct archive *a = NULL;
-
	struct archive_entry *ae = NULL;
-
	int fd = -1;
-
	char tmp[MAXPATHLEN];
-
	const char *tmpdir;
-
	int retcode = EPKG_FATAL;
-
	int ret;
-
	time_t t = 0;
-
	struct stat st;
-

-
	tmpdir = getenv("TMPDIR");
-
	if (tmpdir == NULL)
-
		tmpdir = "/tmp";
-
	strlcpy(tmp, tmpdir, sizeof(tmp));
-
	strlcat(tmp, "/vuln.xml.bz2.XXXX", sizeof(tmp));
-
	if (stat(dest, &st) != -1) {
-
		t = st.st_mtime;
-
	}
-
	switch (pkg_fetch_file(NULL, src, tmp, t)) {
-
	case EPKG_OK:
-
		break;
-
	case EPKG_UPTODATE:
-
		printf("Vulnxml file up-to-date.\n");
-
		retcode = EPKG_OK;
-
		goto cleanup;
-
	default:
-
		warnx("Cannot fetch vulnxml file!");
-
		goto cleanup;
-
	}
-

-
	a = archive_read_new();
-
#if ARCHIVE_VERSION_NUMBER < 3000002
-
	archive_read_support_compression_all(a);
-
#else
-
	archive_read_support_filter_all(a);
-
#endif
-

-
	archive_read_support_format_raw(a);
-

-
	if (archive_read_open_filename(a, tmp, 4096) != ARCHIVE_OK) {
-
		warnx("archive_read_open_filename(%s): %s",
-
				tmp, archive_error_string(a));
-
		goto cleanup;
-
	}
-

-
	while ((ret = archive_read_next_header(a, &ae)) == ARCHIVE_OK) {
-
		fd = open(dest, O_RDWR|O_CREAT|O_TRUNC,
-
				S_IRUSR|S_IRGRP|S_IROTH);
-
		if (fd < 0) {
-
			warn("open(%s)", dest);
-
			goto cleanup;
-
		}
-

-
		if (archive_read_data_into_fd(a, fd) != ARCHIVE_OK) {
-
			warnx("archive_read_data_into_fd(%s): %s",
-
					dest, archive_error_string(a));
-
			goto cleanup;
-
		}
-
	}
-

-
	retcode = EPKG_OK;
-

-
	cleanup:
-
	unlink(tmp);
-
	if (a != NULL) {
-
		archive_read_close(a);
-
		archive_read_free(a);
-
	}
-
	if (fd >= 0)
-
		close(fd);
-

-
	return (retcode);
-
}
-

-
/*
-
 * Expand multiple names to a set of audit entries
-
 */
-
static void
-
audit_expand_entries(struct audit_entry *entry, struct audit_entry **head)
-
{
-
	struct audit_entry *n;
-
	struct audit_pkgname_entry *ncur;
-
	struct audit_package_entry *pcur;
-

-
	/* Set the name of the current entry */
-
	if (entry->packages == NULL || entry->packages->names == NULL) {
-
		free_audit_entry(entry);
-
		return;
-
	}
-

-
	LL_FOREACH(entry->packages, pcur) {
-
		LL_FOREACH(pcur->names, ncur) {
-
			n = calloc(1, sizeof(struct audit_entry));
-
			if (n == NULL)
-
				err(1, "calloc(audit_entry)");
-
			n->pkgname = ncur->pkgname;
-
			/* Set new entry as reference entry */
-
			n->ref = true;
-
			n->cve = entry->cve;
-
			n->desc = entry->desc;
-
			n->versions = pcur->versions;
-
			n->url = entry->url;
-
			n->id = entry->id;
-
			LL_PREPEND(*head, n);
-
		}
-
	}
-
	LL_PREPEND(*head, entry);
-
}
-

-
enum vulnxml_parse_state {
-
	VULNXML_PARSE_INIT = 0,
-
	VULNXML_PARSE_VULN,
-
	VULNXML_PARSE_TOPIC,
-
	VULNXML_PARSE_PACKAGE,
-
	VULNXML_PARSE_PACKAGE_NAME,
-
	VULNXML_PARSE_RANGE,
-
	VULNXML_PARSE_RANGE_GT,
-
	VULNXML_PARSE_RANGE_GE,
-
	VULNXML_PARSE_RANGE_LT,
-
	VULNXML_PARSE_RANGE_LE,
-
	VULNXML_PARSE_RANGE_EQ,
-
	VULNXML_PARSE_CVE
-
};
-

-
struct vulnxml_userdata {
-
	struct audit_entry *h;
-
	struct audit_entry *cur_entry;
-
	enum vulnxml_parse_state state;
-
	int range_num;
+
struct pkg_check_entry {
+
	struct pkg *pkg;
+
	struct pkg_check_entry *next;
};

static void
-
vulnxml_start_element(void *data, const char *element, const char **attributes)
+
add_to_check(struct pkg_check_entry **head, struct pkg *pkg)
{
-
	struct vulnxml_userdata *ud = (struct vulnxml_userdata *)data;
-
	struct audit_versions *vers;
-
	struct audit_pkgname_entry *name_entry;
-
	struct audit_package_entry *pkg_entry;
-
	int i;
+
	struct pkg_check_entry *e;

-
	if (ud->state == VULNXML_PARSE_INIT && strcasecmp(element, "vuln") == 0) {
-
		ud->cur_entry = calloc(1, sizeof(struct audit_entry));
-
		if (ud->cur_entry == NULL)
-
			err(1, "calloc(audit_entry)");
-
		for (i = 0; attributes[i]; i += 2) {
-
			if (strcasecmp(attributes[i], "vid") == 0) {
-
				ud->cur_entry->id = strdup(attributes[i + 1]);
-
				break;
-
			}
-
		}
-
		ud->cur_entry->next = ud->h;
-
		ud->state = VULNXML_PARSE_VULN;
-
	}
-
	else if (ud->state == VULNXML_PARSE_VULN && strcasecmp(element, "topic") == 0) {
-
		ud->state = VULNXML_PARSE_TOPIC;
-
	}
-
	else if (ud->state == VULNXML_PARSE_VULN && strcasecmp(element, "package") == 0) {
-
		pkg_entry = calloc(1, sizeof(struct audit_package_entry));
-
		if (pkg_entry == NULL)
-
			err(1, "calloc(audit_package_entry)");
-
		LL_PREPEND(ud->cur_entry->packages, pkg_entry);
-
		ud->state = VULNXML_PARSE_PACKAGE;
-
	}
-
	else if (ud->state == VULNXML_PARSE_VULN && strcasecmp(element, "cvename") == 0) {
-
		ud->state = VULNXML_PARSE_CVE;
-
	}
-
	else if (ud->state == VULNXML_PARSE_PACKAGE && strcasecmp(element, "name") == 0) {
-
		ud->state = VULNXML_PARSE_PACKAGE_NAME;
-
		name_entry = calloc(1, sizeof(struct audit_pkgname_entry));
-
		if (name_entry == NULL)
-
			err(1, "calloc(audit_pkgname_entry)");
-
		LL_PREPEND(ud->cur_entry->packages->names, name_entry);
-
	}
-
	else if (ud->state == VULNXML_PARSE_PACKAGE && strcasecmp(element, "range") == 0) {
-
		ud->state = VULNXML_PARSE_RANGE;
-
		vers = calloc(1, sizeof(struct audit_versions));
-
		if (vers == NULL)
-
			err(1, "calloc(audit_versions)");
-
		LL_PREPEND(ud->cur_entry->packages->versions, vers);
-
		ud->range_num = 0;
-
	}
-
	else if (ud->state == VULNXML_PARSE_RANGE && strcasecmp(element, "gt") == 0) {
-
		ud->range_num ++;
-
		ud->state = VULNXML_PARSE_RANGE_GT;
-
	}
-
	else if (ud->state == VULNXML_PARSE_RANGE && strcasecmp(element, "ge") == 0) {
-
		ud->range_num ++;
-
		ud->state = VULNXML_PARSE_RANGE_GE;
-
	}
-
	else if (ud->state == VULNXML_PARSE_RANGE && strcasecmp(element, "lt") == 0) {
-
		ud->range_num ++;
-
		ud->state = VULNXML_PARSE_RANGE_LT;
-
	}
-
	else if (ud->state == VULNXML_PARSE_RANGE && strcasecmp(element, "le") == 0) {
-
		ud->range_num ++;
-
		ud->state = VULNXML_PARSE_RANGE_LE;
-
	}
-
	else if (ud->state == VULNXML_PARSE_RANGE && strcasecmp(element, "eq") == 0) {
-
		ud->range_num ++;
-
		ud->state = VULNXML_PARSE_RANGE_EQ;
-
	}
-
}
-

-
static void
-
vulnxml_end_element(void *data, const char *element)
-
{
-
	struct vulnxml_userdata *ud = (struct vulnxml_userdata *)data;
-

-
	if (ud->state == VULNXML_PARSE_VULN && strcasecmp(element, "vuln") == 0) {
-
		audit_expand_entries(ud->cur_entry, &ud->h);
-
		ud->state = VULNXML_PARSE_INIT;
-
	}
-
	else if (ud->state == VULNXML_PARSE_TOPIC && strcasecmp(element, "topic") == 0) {
-
		ud->state = VULNXML_PARSE_VULN;
-
	}
-
	else if (ud->state == VULNXML_PARSE_CVE && strcasecmp(element, "cvename") == 0) {
-
		ud->state = VULNXML_PARSE_VULN;
-
	}
-
	else if (ud->state == VULNXML_PARSE_PACKAGE && strcasecmp(element, "package") == 0) {
-
		ud->state = VULNXML_PARSE_VULN;
-
	}
-
	else if (ud->state == VULNXML_PARSE_PACKAGE_NAME && strcasecmp(element, "name") == 0) {
-
		ud->state = VULNXML_PARSE_PACKAGE;
-
	}
-
	else if (ud->state == VULNXML_PARSE_RANGE && strcasecmp(element, "range") == 0) {
-
		ud->state = VULNXML_PARSE_PACKAGE;
-
	}
-
	else if (ud->state == VULNXML_PARSE_RANGE_GT && strcasecmp(element, "gt") == 0) {
-
		ud->state = VULNXML_PARSE_RANGE;
-
	}
-
	else if (ud->state == VULNXML_PARSE_RANGE_GE && strcasecmp(element, "ge") == 0) {
-
		ud->state = VULNXML_PARSE_RANGE;
-
	}
-
	else if (ud->state == VULNXML_PARSE_RANGE_LT && strcasecmp(element, "lt") == 0) {
-
		ud->state = VULNXML_PARSE_RANGE;
-
	}
-
	else if (ud->state == VULNXML_PARSE_RANGE_LE && strcasecmp(element, "le") == 0) {
-
		ud->state = VULNXML_PARSE_RANGE;
-
	}
-
	else if (ud->state == VULNXML_PARSE_RANGE_EQ && strcasecmp(element, "eq") == 0) {
-
		ud->state = VULNXML_PARSE_RANGE;
-
	}
-
}
-

-
static void
-
vulnxml_handle_data(void *data, const char *content, int length)
-
{
-
	struct vulnxml_userdata *ud = (struct vulnxml_userdata *)data;
-
	struct audit_versions *vers;
-
	struct audit_cve *cve;
-
	struct audit_entry *entry;
-
	int range_type = -1;
-

-
	switch(ud->state) {
-
	case VULNXML_PARSE_INIT:
-
	case VULNXML_PARSE_VULN:
-
	case VULNXML_PARSE_PACKAGE:
-
	case VULNXML_PARSE_RANGE:
-
		/* On these states we do not need any data */
-
		break;
-
	case VULNXML_PARSE_TOPIC:
-
		ud->cur_entry->desc = strndup(content, length);
-
		break;
-
	case VULNXML_PARSE_PACKAGE_NAME:
-
		ud->cur_entry->packages->names->pkgname = strndup(content, length);
-
		break;
-
	case VULNXML_PARSE_RANGE_GT:
-
		range_type = GT;
-
		break;
-
	case VULNXML_PARSE_RANGE_GE:
-
		range_type = GTE;
-
		break;
-
	case VULNXML_PARSE_RANGE_LT:
-
		range_type = LT;
-
		break;
-
	case VULNXML_PARSE_RANGE_LE:
-
		range_type = LTE;
-
		break;
-
	case VULNXML_PARSE_RANGE_EQ:
-
		range_type = EQ;
-
		break;
-
	case VULNXML_PARSE_CVE:
-
		entry = ud->cur_entry;
-
		cve = malloc(sizeof(struct audit_cve));
-
		cve->cvename = strndup(content, length);
-
		LL_PREPEND(entry->cve, cve);
-
		break;
-
	}
-

-
	if (range_type > 0) {
-
		vers = ud->cur_entry->packages->versions;
-
		if (ud->range_num == 1) {
-
			vers->v1.version = strndup(content, length);
-
			vers->v1.type = range_type;
-
		}
-
		else if (ud->range_num == 2) {
-
			vers->v2.version = strndup(content, length);
-
			vers->v2.type = range_type;
-
		}
-
	}
-
}
-

-
static int
-
parse_db_vulnxml(const char *path, struct audit_entry **h)
-
{
-
	int fd;
-
	void *mem;
-
	struct stat st;
-
	XML_Parser parser;
-
	struct vulnxml_userdata ud;
-
	int ret = EPKG_OK;
-

-
	if (stat(path, &st) == -1)
-
		return (EPKG_FATAL);
-

-
	if ((fd = open(path, O_RDONLY)) == -1)
-
		return (EPKG_FATAL);
-

-
	if ((mem = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
-
		close(fd);
-
		return (EPKG_FATAL);
-
	}
-
	close(fd);
-

-
	parser = XML_ParserCreate(NULL);
-
	XML_SetElementHandler(parser, vulnxml_start_element, vulnxml_end_element);
-
	XML_SetCharacterDataHandler(parser, vulnxml_handle_data);
-
	XML_SetUserData(parser, &ud);
-

-
	ud.cur_entry = NULL;
-
	ud.h = *h;
-
	ud.range_num = 0;
-
	ud.state = VULNXML_PARSE_INIT;
-

-
	if (XML_Parse(parser, mem, st.st_size, XML_TRUE) == XML_STATUS_ERROR) {
-
	    warnx("vulnxml parsing error: %s", XML_ErrorString(XML_GetErrorCode(parser)));
-
	}
-

-
	XML_ParserFree(parser);
-
	munmap(mem, st.st_size);
-

-
	*h = ud.h;
-

-
	return (ret);
-
}
-

-
/*
-
 * Returns the length of the largest prefix without globbing
-
 * characters, as per fnmatch().
-
 */
-
static size_t
-
str_noglob_len(const char *s)
-
{
-
	size_t n;
-

-
	for (n = 0; s[n] && s[n] != '*' && s[n] != '?' &&
-
	    s[n] != '[' && s[n] != '{' && s[n] != '\\'; n++);
-

-
	return n;
-
}
-

-
/*
-
 * Helper for quicksort that lexicographically orders prefixes.
-
 */
-
static int
-
audit_entry_compare(const void *a, const void *b)
-
{
-
	const struct audit_entry_sorted *e1, *e2;
-
	size_t min_len;
-
	int result;
-

-
	e1 = (const struct audit_entry_sorted *)a;
-
	e2 = (const struct audit_entry_sorted *)b;
-

-
	min_len = (e1->noglob_len < e2->noglob_len ?
-
	    e1->noglob_len : e2->noglob_len);
-
	result = strncmp(e1->e->pkgname, e2->e->pkgname, min_len);
-
	/*
-
	 * Additional check to see if some word is a prefix of an
-
	 * another one and, thus, should go before the former.
-
	 */
-
	if (result == 0) {
-
		if (e1->noglob_len < e2->noglob_len)
-
			result = -1;
-
		else if (e1->noglob_len > e2->noglob_len)
-
			result = 1;
-
	}
-

-
	return (result);
-
}
-

-
/*
-
 * Sorts VuXML entries and calculates increments to jump to the
-
 * next distinct prefix.
-
 */
-
static struct audit_entry_sorted *
-
preprocess_db(struct audit_entry *h)
-
{
-
	struct audit_entry *e;
-
	struct audit_entry_sorted *ret;
-
	size_t i, n, tofill;
-

-
	n = 0;
-
	LL_FOREACH(h, e)
-
		n++;
-

-
	ret = (struct audit_entry_sorted *)calloc(n + 1, sizeof(ret[0]));
-
	if (ret == NULL)
-
		err(1, "calloc(audit_entry_sorted*)");
-
	bzero((void *)ret, (n + 1) * sizeof(ret[0]));
-

-
	n = 0;
-
	LL_FOREACH(h, e) {
-
		if (e->pkgname != NULL) {
-
			ret[n].e = e;
-
			ret[n].noglob_len = str_noglob_len(e->pkgname);
-
			ret[n].next_pfx_incr = 1;
-
			n++;
-
		}
-
	}
-

-
	qsort(ret, n, sizeof(*ret), audit_entry_compare);
-

-
	/*
-
	 * Determining jump indexes to the next different prefix.
-
	 * Only non-1 increments are calculated there.
-
	 *
-
	 * Due to the current usage that picks only increment for the
-
	 * first of the non-unique prefixes in a row, we could
-
	 * calculate only that one and skip calculations for the
-
	 * succeeding, but for the uniformity and clarity we're
-
	 * calculating 'em all.
-
	 */
-
	for (n = 1, tofill = 0; ret[n].e; n++) {
-
		if (ret[n - 1].noglob_len != ret[n].noglob_len) {
-
			struct audit_entry_sorted *base;
-

-
			base = ret + n - tofill;
-
			for (i = 0; tofill > 1; i++, tofill--)
-
				base[i].next_pfx_incr = tofill;
-
			tofill = 1;
-
		} else if (strcmp(ret[n - 1].e->pkgname,
-
		    ret[n].e->pkgname) == 0) {
-
			tofill++;
-
		} else {
-
			tofill = 1;
-
		}
-
	}
-

-
	/* Calculate jump indexes for the first byte of the package name */
-
	bzero(audit_entry_first_byte_idx, sizeof(audit_entry_first_byte_idx));
-
	for (n = 1, i = 0; n < 256; n++) {
-
		while (ret[i].e != NULL &&
-
		    (size_t)(ret[i].e->pkgname[0]) < n)
-
			i++;
-
		audit_entry_first_byte_idx[n] = i;
-
	}
-

-
	return (ret);
-
}
-

-
static bool
-
match_version(const char *pkgversion, struct version_entry *v)
-
{
-
	bool res = false;
-

-
	/*
-
	 * Return true so it is easier for the caller to handle case where there is
-
	 * only one version to match: the missing one will always match.
-
	 */
-
	if (v->version == NULL)
-
		return true;
-

-
	switch (pkg_version_cmp(pkgversion, v->version)) {
-
	case -1:
-
		if (v->type == LT || v->type == LTE)
-
			res = true;
-
		break;
-
	case 0:
-
		if (v->type == EQ || v->type == LTE || v->type == GTE)
-
			res = true;
-
		break;
-
	case 1:
-
		if (v->type == GT || v->type == GTE)
-
			res = true;
-
		break;
-
	}
-
	return res;
-
}
-

-
static bool
-
is_vulnerable(struct audit_entry_sorted *a, struct pkg *pkg)
-
{
-
	struct audit_entry *e;
-
	struct audit_versions *vers;
-
	struct audit_cve *cve;
-
	const char *pkgname;
-
	const char *pkgversion;
-
	bool res = false, res1, res2;
-

-
	pkg_get(pkg,
-
		PKG_NAME, &pkgname,
-
		PKG_VERSION, &pkgversion
-
	);
-

-
	a += audit_entry_first_byte_idx[(size_t)pkgname[0]];
-
	for (; (e = a->e) != NULL; a += a->next_pfx_incr) {
-
		int cmp;
-
		size_t i;
-

-
		/*
-
		 * Audit entries are sorted, so if we had found one
-
		 * that is lexicographically greater than our name,
-
		 * it and the rest won't match our name.
-
		 */
-
		cmp = strncmp(pkgname, e->pkgname, a->noglob_len);
-
		if (cmp > 0)
-
			continue;
-
		else if (cmp < 0)
-
			break;
-

-
		for (i = 0; i < a->next_pfx_incr; i++) {
-
			e = a[i].e;
-
			if (fnmatch(e->pkgname, pkgname, 0) != 0)
-
				continue;
-

-
			LL_FOREACH(e->versions, vers) {
-
				res1 = match_version(pkgversion, &vers->v1);
-
				res2 = match_version(pkgversion, &vers->v2);
-
				if (res1 && res2) {
-
					res = true;
-
					if (quiet) {
-
						printf("%s-%s\n", pkgname, pkgversion);
-
						return res; /* avoid reporting the same pkg multiple times */
-
					} else {
-
						printf("%s-%s is vulnerable:\n", pkgname, pkgversion);
-
						printf("%s\n", e->desc);
-
						/* XXX: for vulnxml we should use more clever approach indeed */
-
						if (e->cve) {
-
							cve = e->cve;
-
							while (cve) {
-
								printf("CVE: %s\n", cve->cvename);
-
								cve = cve->next;
-
							}
-
						}
-
						if (e->url)
-
							printf("WWW: %s\n\n", e->url);
-
						else if (e->id)
-
							printf("WWW: http://portaudit.FreeBSD.org/%s.html\n\n", e->id);
-
					}
-
					break;
-
				}
-
			}
-
		}
-
	}
-

-
	return res;
-
}
-

-
static void
-
free_audit_list(struct audit_entry *h)
-
{
-
	struct audit_entry *e;
-

-
	while (h) {
-
		e = h;
-
		h = h->next;
-
		free_audit_entry(e);
+
	e = malloc(sizeof(*e));
+
	if (e == NULL) {
+
		warnx("malloc failed for pkg_check_entry");
+
		exit(EXIT_FAILURE);
	}
+
	e->pkg = pkg;
+
	LL_PREPEND(*head, e);
}

int
exec_audit(int argc, char **argv)
{
-
	struct audit_entry		*h = NULL;
-
	struct audit_entry_sorted	*cooked_audit_entries = NULL;
+
	struct pkg_audit	*audit;
	struct pkgdb			*db = NULL;
	struct pkgdb_it			*it = NULL;
	struct pkg			*pkg = NULL;
@@ -778,8 +88,10 @@ exec_audit(int argc, char **argv)
	unsigned int			 vuln = 0;
	bool				 fetch = false;
	int				 ch;
-
	int				 ret = EX_OK, res;
+
	int				 ret = EX_OK;
	const char			*portaudit_site = NULL;
+
	struct sbuf			*sb;
+
	struct pkg_check_entry *check = NULL, *cur;

	db_dir = pkg_object_string(pkg_config_get("PKG_DBDIR"));
	snprintf(audit_file_buf, sizeof(audit_file_buf), "%s/vuln.xml", db_dir);
@@ -803,13 +115,27 @@ exec_audit(int argc, char **argv)
	argc -= optind;
	argv += optind;

+
	audit = pkg_audit_new();
+

	if (fetch == true) {
		portaudit_site = pkg_object_string(pkg_config_get("VULNXML_SITE"));
-
		if (fetch_and_extract(portaudit_site, audit_file) != EPKG_OK) {
+
		if (pkg_audit_fetch(portaudit_site, audit_file) != EPKG_OK) {
			return (EX_IOERR);
		}
	}

+
	if (pkg_audit_load(audit, audit_file) != EPKG_OK) {
+
		if (errno == ENOENT)
+
			warnx("vulnxml file %s does not exist. "
+
					"Try running 'pkg audit -F' first",
+
					audit_file);
+
		else
+
			warn("unable to open vulnxml file %s",
+
					audit_file);
+

+
		return (EX_DATAERR);
+
	}
+

	if (argc > 2) {
		usage_audit();
		return (EX_USAGE);
@@ -827,83 +153,85 @@ exec_audit(int argc, char **argv)
		pkg_set(pkg,
		    PKG_NAME, name,
		    PKG_VERSION, version);
-
		res = parse_db_vulnxml(audit_file, &h);
-
		if (res != EPKG_OK) {
-
			if (errno == ENOENT)
-
				warnx("vulnxml file %s does not exist. "
-
				      "Try running 'pkg audit -F' first",
-
				      audit_file);
-
			else
-
				warn("unable to open vulnxml file %s",
-
				     audit_file);
-
			ret = EX_DATAERR;
-
			goto cleanup;
-
		}
-
		cooked_audit_entries = preprocess_db(h);
-
		is_vulnerable(cooked_audit_entries, pkg);
-
		goto cleanup;
+
		add_to_check(&check, pkg);
	}
+
	else {

-
	/*
-
	 * if the database doesn't exist it just means there are no
-
	 * packages to audit.
-
	 */
+
		/*
+
		 * if the database doesn't exist it just means there are no
+
		 * packages to audit.
+
		 */

-
	ret = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_LOCAL);
-
	if (ret == EPKG_ENODB) 
-
		return (EX_OK);
-
	else if (ret == EPKG_ENOACCESS) {
-
		warnx("Insufficient privileges to read the package database");
-
		return (EX_NOPERM);
-
	} else if (ret != EPKG_OK) {
-
		warnx("Error accessing the package database");
-
		return (EX_IOERR);
-
	}
+
		ret = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_LOCAL);
+
		if (ret == EPKG_ENODB)
+
			return (EX_OK);
+
		else if (ret == EPKG_ENOACCESS) {
+
			warnx("Insufficient privileges to read the package database");
+
			return (EX_NOPERM);
+
		} else if (ret != EPKG_OK) {
+
			warnx("Error accessing the package database");
+
			return (EX_IOERR);
+
		}

-
	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
-
		return (EX_IOERR);
+
		if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
+
			return (EX_IOERR);

-
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY, 0, 0) != EPKG_OK) {
-
		pkgdb_close(db);
-
		warnx("Cannot get a read lock on a database, it is locked by another process");
-
		return (EX_TEMPFAIL);
-
	}
+
		if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
+
			pkgdb_close(db);
+
			warnx("Cannot get a read lock on a database, it is locked by another process");
+
			return (EX_TEMPFAIL);
+
		}

-
	if ((it = pkgdb_query(db, NULL, MATCH_ALL)) == NULL)
-
	{
-
		warnx("Error accessing the package database");
-
		ret = EX_IOERR;
-
		goto cleanup;
+
		if ((it = pkgdb_query(db, NULL, MATCH_ALL)) == NULL) {
+
			warnx("Error accessing the package database");
+
			ret = EX_IOERR;
+
		}
+
		else {
+
			while ((ret = pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC)) == EPKG_OK) {
+
				add_to_check(&check, pkg);
+
				pkg = NULL;
+
			}
+
			ret = EX_OK;
+
		}
+
		if (db != NULL) {
+
			if (it != NULL)
+
				pkgdb_it_free(it);
+
			pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
+
			pkgdb_close(db);
+
		}
+
		if (ret != EX_OK)
+
			return (ret);
	}

-
	res = parse_db_vulnxml(audit_file, &h);
-
	if (res != EPKG_OK) {
-
		if (errno == ENOENT)
-
			warnx("unable to open vulnxml file, try running 'pkg audit -F' first");
-
		else
-
			warn("unable to open vulnxml file %s", audit_file);
-
		ret = EX_DATAERR;
-
		goto cleanup;
+
	/* Now we have vulnxml loaded and check list formed */
+
#ifdef HAVE_CAPSICUM
+
	if (cap_enter() < 0 && errno != ENOSYS) {
+
		warn("cap_enter() failed");
+
		return (EPKG_FATAL);
	}
-
	cooked_audit_entries = preprocess_db(h);
+
#endif

-
	while ((ret = pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC)) == EPKG_OK)
-
		if (is_vulnerable(cooked_audit_entries, pkg))
-
			vuln++;
+
	if (pkg_audit_process(audit) == EPKG_OK) {
+
		LL_FOREACH(check, cur) {
+
			if (pkg_audit_is_vulnerable(audit, cur->pkg, quiet, &sb)) {
+
				vuln ++;
+
				printf("%s", sbuf_data(sb));
+
				sbuf_delete(sb);
+
			}
+
		}

-
	if (ret == EPKG_END && vuln == 0)
-
		ret = EX_OK;
+
		if (ret == EPKG_END && vuln == 0)
+
			ret = EX_OK;

-
	if (!quiet)
-
		printf("%u problem(s) in the installed packages found.\n", vuln);
+
		if (!quiet)
+
			printf("%u problem(s) in the installed packages found.\n", vuln);
+
	}
+
	else {
+
		warnx("cannot process vulnxml");
+
		ret = EX_SOFTWARE;
+
	}

-
cleanup:
-
	pkgdb_it_free(it);
-
	if (db != NULL)
-
		pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
-
	pkgdb_close(db);
-
	pkg_free(pkg);
-
	free_audit_list(h);
+
	pkg_audit_free(audit);

	return (ret);
}
modified src/autoremove.c
@@ -101,7 +101,7 @@ exec_autoremove(__unused int argc, __unused char **argv)
		return (EX_IOERR);
	}

-
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_ADVISORY, 0, 0) != EPKG_OK) {
+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_ADVISORY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get an advisory lock on a database, it is locked by another process");
		return (EX_TEMPFAIL);
modified src/check.c
@@ -149,7 +149,7 @@ fix_deps(struct pkgdb *db, struct deps_head *dh, int nbpkgs, bool yes)
		return (EPKG_ENODB);
	}

-
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_ADVISORY, 0, 0) != EPKG_OK) {
+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_ADVISORY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get an advisory lock on a database, it is locked by another process");
		return (EX_TEMPFAIL);
@@ -354,7 +354,7 @@ exec_check(int argc, char **argv)
	if (ret != EPKG_OK)
		return (EX_IOERR);

-
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_ADVISORY, 0, 0) != EPKG_OK) {
+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_ADVISORY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get an advisory lock on a database, it is locked by another process");
		return (EX_TEMPFAIL);
@@ -386,7 +386,7 @@ exec_check(int argc, char **argv)
			}
			if (recompute) {
				if (pkgdb_upgrade_lock(db, PKGDB_LOCK_ADVISORY,
-
						PKGDB_LOCK_EXCLUSIVE, 0.5, 20) == EPKG_OK) {
+
						PKGDB_LOCK_EXCLUSIVE) == EPKG_OK) {
					if (verbose)
						pkg_printf("Recomputing size and checksums: %n\n", pkg);
					if (pkg_recompute(db, pkg) != EPKG_OK) {
@@ -400,7 +400,7 @@ exec_check(int argc, char **argv)
			}
			if (reanalyse_shlibs) {
				if (pkgdb_upgrade_lock(db, PKGDB_LOCK_ADVISORY,
-
						PKGDB_LOCK_EXCLUSIVE, 0.5, 20) == EPKG_OK) {
+
						PKGDB_LOCK_EXCLUSIVE) == EPKG_OK) {
					if (verbose)
						pkg_printf("Reanalyzing files for shlibs: %n\n", pkg);
					if (pkgdb_reanalyse_shlibs(db, pkg) != EPKG_OK) {
@@ -419,7 +419,7 @@ exec_check(int argc, char **argv)
			printf("\n>>> Missing package dependencies were detected.\n");
			printf(">>> Found %d issue(s) in the package database.\n\n", nbpkgs);
			if (pkgdb_upgrade_lock(db, PKGDB_LOCK_ADVISORY,
-
					PKGDB_LOCK_EXCLUSIVE, 0.5, 20) == EPKG_OK) {
+
					PKGDB_LOCK_EXCLUSIVE) == EPKG_OK) {
				ret = fix_deps(db, &dh, nbpkgs, yes);
				if (ret == EPKG_OK)
					check_summary(db, &dh);
modified src/clean.c
@@ -222,7 +222,7 @@ exec_clean(int argc, char **argv)
	if (pkgdb_open(&db, PKGDB_REMOTE) != EPKG_OK)
		return (EX_IOERR);

-
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY, 0, 0) != EPKG_OK) {
+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get a read lock on a database, it is locked by another process");
		return (EX_TEMPFAIL);
modified src/convert.c
@@ -72,7 +72,7 @@ convert_to_old(const char *pkg_add_dbdir, bool dry_run)
	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
		return (EX_IOERR);

-
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE, 0, 0) != EPKG_OK) {
+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get an exclusive lock on a database, it is locked by another process");
		return (EX_TEMPFAIL);
modified src/create.c
@@ -91,7 +91,7 @@ pkg_create_matches(int argc, char **argv, match_t match, pkg_formats fmt,
		return (EX_IOERR);
	}
	/* XXX: get rid of hardcoded timeouts */
-
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY, 0, 0) != EPKG_OK) {
+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get a read lock on a database, it is locked by another process");
		return (EX_TEMPFAIL);
modified src/delete.c
@@ -139,7 +139,7 @@ exec_delete(int argc, char **argv)
	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
		return (EX_IOERR);

-
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_ADVISORY, 0, 0) != EPKG_OK) {
+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_ADVISORY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get an advisory lock on a database, it is locked by another process");
		return (EX_TEMPFAIL);
modified src/fetch.c
@@ -160,7 +160,7 @@ exec_fetch(int argc, char **argv)
	if (pkgdb_open_all(&db, PKGDB_REMOTE, reponame) != EPKG_OK)
		return (EX_IOERR);

-
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY, 0, 0) != EPKG_OK) {
+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get a read lock on a database, it is locked by another process");
		return (EX_TEMPFAIL);
modified src/info.c
@@ -273,7 +273,7 @@ exec_info(int argc, char **argv)
	if (ret != EPKG_OK)
		return (EX_IOERR);

-
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY, 0, 0) != EPKG_OK) {
+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get a read lock on a database, it is locked by another process");
		return (EX_TEMPFAIL);
modified src/install.c
@@ -190,7 +190,7 @@ exec_install(int argc, char **argv)
	    reponame) != EPKG_OK)
		return (EX_IOERR);

-
	if (pkgdb_obtain_lock(db, lock_type, 0, 0) != EPKG_OK) {
+
	if (pkgdb_obtain_lock(db, lock_type) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get an advisory lock on a database, it is locked by another process");
		return (EX_TEMPFAIL);
modified src/lock.c
@@ -48,7 +48,7 @@ void
usage_lock(void)
{
	fprintf(stderr, "Usage: pkg lock [-qy] [-Cgix] <pkg-name>\n");
-
	fprintf(stderr, "       pkg lock [-qy] -a\n");
+
	fprintf(stderr, "       pkg lock [-qy] -al\n");
	fprintf(stderr, "       pkg unlock [-qy] [-Cgix] <pkg-name>\n");
	fprintf(stderr, "       pkg unlock [-qy] -a\n");
	fprintf(stderr, "For more information see 'pkg help lock'.\n");
@@ -106,6 +106,38 @@ exec_unlock(int argc, char **argv)
	return (exec_lock_unlock(argc, argv, UNLOCK));
}

+
static int 
+
list_locked()
+
{
+
        struct pkgdb *db = NULL;
+
        struct pkgdb_it *it = NULL;
+
        struct pkg *pkg = NULL;
+
        const char *pkg_name, *pkg_version;
+
 
+
	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
+
		return (EX_IOERR);
+
                        
+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
+
		pkgdb_close(db);
+
		warnx("Cannot get a read lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}
+

+
	if ((it = pkgdb_query(db, " where locked=1", MATCH_CONDITION)) == NULL) {
+
		pkgdb_close(db);
+
		return (EX_UNAVAILABLE);
+
	}
+

+
	while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) == EPKG_OK) {
+
		pkg_get(pkg, PKG_NAME, &pkg_name, PKG_VERSION, &pkg_version);
+
                printf("%s-%s\n", pkg_name, pkg_version);
+
	}
+
	pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
+
	pkgdb_close(db);
+
	return (EX_OK);
+

+
}
+

static int
exec_lock_unlock(int argc, char **argv, enum action action)
{
@@ -125,11 +157,20 @@ exec_lock_unlock(int argc, char **argv, enum action action)
                pkg_object_bool(pkg_config_get("CASE_SENSITIVE_MATCH"))
                );

-
	while ((ch = getopt(argc, argv, "aCgiqxy")) != -1) {
+
	while ((ch = getopt(argc, argv, "alCgiqxy")) != -1) {
		switch (ch) {
		case 'a':
			match = MATCH_ALL;
			break;
+
		case 'l':
+
			if (action == LOCK) {
+
				exitcode = list_locked();
+
				return exitcode;
+
			} else {
+
				warnx("list of locked packages useless with unlock command");
+
				return (EX_USAGE);
+
			}
+

		case 'C':
			pkgdb_set_case_sensitivity(true);
			break;
@@ -186,7 +227,7 @@ exec_lock_unlock(int argc, char **argv, enum action action)
	if (retcode != EPKG_OK)
		return (EX_IOERR);

-
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE, 0, 0) != EPKG_OK) {
+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get an exclusive lock on a database, it is locked by another process");
		return (EX_TEMPFAIL);
modified src/query.c
@@ -941,7 +941,7 @@ exec_query(int argc, char **argv)
	if (ret != EPKG_OK)
		return (EX_IOERR);

-
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY, 0, 0) != EPKG_OK) {
+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get an read lock on a database, it is locked by another process");
		return (EX_TEMPFAIL);
modified src/register.c
@@ -298,7 +298,7 @@ exec_register(int argc, char **argv)
		if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
			return (EX_IOERR);

-
		if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE, 0, 0) != EPKG_OK) {
+
		if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE) != EPKG_OK) {
			pkgdb_close(db);
			warnx("Cannot get an exclusive lock on a database, it is locked by another process");
			return (EX_TEMPFAIL);
modified src/search.c
@@ -370,11 +370,21 @@ exec_search(int argc, char **argv)
	}

	ret = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_REPO);
-
	if (ret == EPKG_ENOACCESS) {
+
	switch(ret) {
+
	case EPKG_ENOACCESS:
		warnx("Insufficient privileges to query the package database");
		return (EX_NOPERM);
-
	} else if (ret != EPKG_OK)
+
	case EPKG_ENODB:
+
		if (!auto_update) {
+
			warnx("Unable to open remote repository catalogues. Try running '%s update' first.", getprogname());
+
			return (EX_IOERR);
+
		}
+
		break;
+
	case EPKG_OK:
+
		break;
+
	default:
		return (EX_IOERR);
+
	}

	/* first update the remote repositories if needed */
	old_quiet = quiet;
modified src/set.c
@@ -157,7 +157,7 @@ exec_set(int argc, char **argv)
	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
		return (EX_IOERR);

-
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE, 0, 0) != EPKG_OK) {
+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get an exclusive lock on a database, it is locked by another process");
		return (EX_TEMPFAIL);
modified src/shlib.c
@@ -177,7 +177,7 @@ exec_shlib(int argc, char **argv)
	if (retcode != EPKG_OK)
		return (EX_IOERR);

-
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY, 0, 0) != EPKG_OK) {
+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get a read lock on a database, it is locked by another process");
		return (EX_TEMPFAIL);
modified src/stats.c
@@ -82,7 +82,7 @@ exec_stats(__unused int argc, __unused char **argv)
		return (EX_IOERR);
	}

-
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY, 0, 0) != EPKG_OK) {
+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get a read lock on a database, it is locked by another process");
		return (EX_TEMPFAIL);
modified src/updating.c
@@ -107,7 +107,7 @@ exec_updating(int argc, char **argv)
	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
		return (EX_IOERR);

-
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY, 0, 0) != EPKG_OK) {
+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get a read lock on a database, it is locked by another process");
		return (EX_TEMPFAIL);
modified src/upgrade.c
@@ -143,7 +143,7 @@ exec_upgrade(int argc, char **argv)
	if (pkgdb_open_all(&db, PKGDB_REMOTE, reponame) != EPKG_OK)
		return (EX_IOERR);

-
	if (pkgdb_obtain_lock(db, lock_type, 0, 0) != EPKG_OK) {
+
	if (pkgdb_obtain_lock(db, lock_type) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get an advisory lock on a database, it is locked by another process");
		return (EX_TEMPFAIL);
modified src/version.c
@@ -385,7 +385,7 @@ do_source_index(unsigned int opt, char limchar, char *pattern, match_t match,

	indexhead = hash_indexfile(indexfile);

-
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY, 0, 0) != EPKG_OK) {
+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
		pkgdb_close(db);
		free_index(indexhead);
		warnx("Cannot get a read lock on the database. "
@@ -454,7 +454,7 @@ do_source_remote(unsigned int opt, char limchar, char *pattern, match_t match,
	if (pkgdb_open_all(&db, PKGDB_REMOTE, reponame) != EPKG_OK)
		return (EX_IOERR);

-
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY, 0, 0) != EPKG_OK) {
+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get a read lock on a database. "
		      "It is locked by another process");
@@ -619,7 +619,7 @@ do_source_ports(unsigned int opt, char limchar, char *pattern, match_t match,
	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
		return (EX_IOERR);

-
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY, 0, 0) != EPKG_OK) {
+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get a read lock on a database. "
		      "It is locked by another process");
modified src/which.c
@@ -96,7 +96,7 @@ exec_which(int argc, char **argv)
		return (EX_IOERR);
	}

-
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY, 0, 0) != EPKG_OK) {
+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get a read lock on a database, it is locked by another process");
		return (EX_TEMPFAIL);