Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Merge branch 'master' of git://github.com/wca/pkgng into wca
Baptiste Daroussin committed 14 years ago
commit 7fd834b03e0293a1048b84fc63d3a5bb061b64dd
parent 853612a
19 files changed +527 -158
modified libpkg/Makefile
@@ -13,7 +13,9 @@ SRCS= pkg.c \
		pkg_create.c \
		pkg_delete.c \
		pkg_elf.c \
+
		pkg_event.c \
		pkg_error.c \
+
		pkg_handle.c \
		pkg_jobs.c \
		pkg_manifest.c \
		pkg_ports.c \
modified libpkg/fetch.c
@@ -29,8 +29,9 @@ pkg_fetch_file(const char *url, const char *dest, void *data, fetch_cb cb)
	int retcode = EPKG_OK;

	if ((fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) {
-
		retcode = pkg_error_set(EPKG_FATAL, "open(%s): %s", dest,
-
								strerror(errno));
+
		pkg_emit_event(PKG_EVENT_IO_ERROR, /*argc*/3, "open", dest,
+
		    strerror(errno));
+
		retcode = EPKG_FATAL;
		goto cleanup;
	}

@@ -39,7 +40,9 @@ pkg_fetch_file(const char *url, const char *dest, void *data, fetch_cb cb)
		if (remote == NULL) {
			--retry;
			if (retry == 0) {
-
				retcode = pkg_error_set(EPKG_FATAL, "%s", fetchLastErrString);
+
				pkg_emit_event(PKG_EVENT_FETCH_ERROR,
+
				    /*argc*/1, fetchLastErrString);
+
				retcode = EPKG_FATAL;
				goto cleanup;
			}
			sleep(1);
@@ -52,8 +55,9 @@ pkg_fetch_file(const char *url, const char *dest, void *data, fetch_cb cb)
			break;

		if (write(fd, buf, r) != r) {
-
			retcode = pkg_error_set(EPKG_FATAL, "write(%s): %s", dest,
-
									strerror(errno));
+
			pkg_emit_event(PKG_EVENT_IO_ERROR, /*argc*/3, "write",
+
			    dest, strerror(errno));
+
			retcode = EPKG_FATAL;
			goto cleanup;
		}

@@ -67,7 +71,9 @@ pkg_fetch_file(const char *url, const char *dest, void *data, fetch_cb cb)
	}

	if (ferror(remote)) {
-
		retcode = pkg_error_set(EPKG_FATAL, "%s", fetchLastErrString);
+
		pkg_emit_event(PKG_EVENT_FETCH_ERROR, /*argc*/1,
+
		    fetchLastErrString);
+
		retcode = EPKG_FATAL;
		goto cleanup;
	}

@@ -104,7 +110,9 @@ pkg_fetch_buffer(const char *url, char **buffer, void *data, fetch_cb cb)
		if (remote == NULL) {
			--retry;
			if (retry == 0) {
-
				pkg_error_set(EPKG_FATAL, "%s", fetchLastErrString);
+
				pkg_emit_event(PKG_EVENT_FETCH_ERROR,
+
				    /*argc*/1, fetchLastErrString);
+
				retcode = EPKG_FATAL;
				goto cleanup;
			}
			sleep(1);
@@ -130,7 +138,8 @@ pkg_fetch_buffer(const char *url, char **buffer, void *data, fetch_cb cb)
	}

	if (ferror(remote)) {
-
		retcode = pkg_error_set(EPKG_FATAL, "%s", fetchLastErrString);
+
		pkg_emit_event(PKG_EVENT_FETCH_ERROR, /*argc*/1,
+
		    fetchLastErrString);
		goto cleanup;
	}

modified libpkg/packing.c
@@ -15,7 +15,6 @@
#include <stdlib.h>

#include <pkg.h>
-
#include <pkg_error.h>
#include <pkg_private.h>

static const char *packing_set_format(struct archive *a, pkg_formats format);
@@ -32,8 +31,10 @@ packing_init(struct packing **pack, const char *path, pkg_formats format)
	char archive_path[MAXPATHLEN];
	const char *ext;

-
	if ((*pack = calloc(1, sizeof(struct packing))) == NULL)
-
		return(pkg_error_set(EPKG_FATAL, "%s", strerror(errno)));
+
	if ((*pack = calloc(1, sizeof(struct packing))) == NULL) {
+
		pkg_emit_event(PKG_EVENT_MALLOC_ERROR, /*argc*/1,
+
		    strerror(errno));
+
	}

	(*pack)->aread = archive_read_disk_new();
	archive_read_disk_set_standard_lookup((*pack)->aread);
@@ -48,7 +49,7 @@ packing_init(struct packing **pack, const char *path, pkg_formats format)
			archive_read_finish((*pack)->aread);
			archive_write_finish((*pack)->awrite);
			archive_entry_free((*pack)->entry);
-
			return (pkg_error_set(EPKG_FATAL, "Unsupported format"));
+
			return EPKG_FATAL; /* error set by _set_format() */
		}
		snprintf(archive_path, sizeof(archive_path), "%s.%s", path, ext);

@@ -91,13 +92,14 @@ packing_append_file(struct packing *pack, const char *filepath, const char *newp
	archive_entry_clear(pack->entry);
	archive_entry_copy_sourcepath(pack->entry, filepath);

-
	if (archive_read_disk_entry_from_file(pack->aread, pack->entry, -1, NULL) !=
-
										  ARCHIVE_OK) {
-
		retcode = pkg_error_set(EPKG_FATAL,
-
								"archive_read_disk_entry_from_file(%s): %s",
-
								filepath, archive_error_string(pack->aread));
+
	retcode = archive_read_disk_entry_from_file(pack->aread, pack->entry, -1, NULL);
+
	if (retcode != ARCHIVE_OK) {
+
		pkg_emit_event(PKG_EVENT_ARCHIVE_ERROR, /*argc*/2,
+
		    filepath, pack->aread);
+
		retcode = EPKG_FATAL;
		goto cleanup;
	}
+
	retcode = EPKG_OK;

	lstat(filepath, &st);
	archive_entry_copy_stat(pack->entry, &st);
@@ -119,8 +121,9 @@ packing_append_file(struct packing *pack, const char *filepath, const char *newp

	if (archive_entry_size(pack->entry) > 0) {
		if ((fd = open(filepath, O_RDONLY)) < 0) {
-
			retcode = pkg_error_set(EPKG_FATAL, "open(%s): %s", filepath,
-
									 strerror(errno));
+
			pkg_emit_event(PKG_EVENT_IO_ERROR, /*argc*/3,
+
			    "open", filepath, strerror(errno));
+
			retcode = EPKG_FATAL;
			goto cleanup;
		}

@@ -152,7 +155,11 @@ packing_append_tree(struct packing *pack, const char *treepath, const char *newr
	sb = sbuf_new_auto();
	while ((fts_e = fts_read(fts)) != NULL) {
		switch(fts_e->fts_info) {
+
		case FTS_D:
+
		case FTS_DEFAULT:
		case FTS_F:
+
		case FTS_SL:
+
		case FTS_SLNONE:
			 /* Skip entries that are shorter than the tree itself */
			 if (fts_e->fts_pathlen <= treelen)
				  break;
@@ -164,6 +171,7 @@ packing_append_tree(struct packing *pack, const char *treepath, const char *newr
			 sbuf_finish(sb);
			 packing_append_file(pack, fts_e->fts_name, sbuf_get(sb));
			 break;
+
		case FTS_DC:
		case FTS_DNR:
		case FTS_ERR:
		case FTS_NS:
@@ -200,19 +208,22 @@ packing_set_format(struct archive *a, pkg_formats format)
			if (archive_write_set_compression_xz(a) == ARCHIVE_OK) {
				return ("txz");
			} else {
-
				warnx("xz compression is not supported, trying bzip2");
+
				pkg_emit_event(PKG_EVENT_ARCHIVE_COMP_UNSUP,
+
				    /*argc*/2, "xz", "bzip2");
			}
		case TBZ:
			if (archive_write_set_compression_bzip2(a) == ARCHIVE_OK) {
				return ("tbz");
			} else {
-
				warnx("bzip2 compression is not supported, trying gzip");
+
				pkg_emit_event(PKG_EVENT_ARCHIVE_COMP_UNSUP,
+
				    /*argc*/2, "bzip2", "gzip");
			}
		case TGZ:
			if (archive_write_set_compression_gzip(a) == ARCHIVE_OK) {
				return ("tgz");
			} else {
-
				warnx("gzip compression is not supported, trying plain tar");
+
				pkg_emit_event(PKG_EVENT_ARCHIVE_COMP_UNSUP,
+
				    /*argc*/2, "gzip", "plain tar");
			}
		case TAR:
			archive_write_set_compression_none(a);
modified libpkg/pkg.c
@@ -14,8 +14,11 @@
int
pkg_new(struct pkg **pkg, pkg_t type)
{
-
	if ((*pkg = calloc(1, sizeof(struct pkg))) == NULL)
-
		return(pkg_error_set(EPKG_FATAL, "%s", strerror(errno)));
+
	if ((*pkg = calloc(1, sizeof(struct pkg))) == NULL) {
+
		pkg_emit_event(PKG_EVENT_MALLOC_ERROR, /*argc*/1,
+
		    strerror(errno));
+
		return EPKG_FATAL;
+
	}

	struct _fields {
		int id;
@@ -594,7 +597,8 @@ pkg_addscript_file(struct pkg *pkg, const char *path)
			strcmp(filename, "+UPGRADE") == 0) {
		type = PKG_SCRIPT_UPGRADE;
	} else {
-
		return (pkg_error_set(EPKG_FATAL, "unknown script"));
+
		pkg_emit_event(PKG_EVENT_UNKNOWN_SCRIPT, /*argc*/1, filename);
+
		return EPKG_FATAL;
	}

	ret = pkg_addscript(pkg, data, type);
@@ -806,7 +810,9 @@ pkg_open2(struct pkg **pkg_p, struct archive **a, struct archive_entry **ae, con
	archive_read_support_format_tar(*a);

	if (archive_read_open_filename(*a, path, 4096) != ARCHIVE_OK) {
-
		retcode = pkg_error_set(EPKG_FATAL, "%s", archive_error_string(*a));
+
		pkg_emit_event(PKG_EVENT_ARCHIVE_ERROR, /*argc*/2,
+
		    archive_entry_pathname(*ae), *a);
+
		retcode = EPKG_FATAL;
		goto cleanup;
	}

@@ -866,8 +872,11 @@ pkg_open2(struct pkg **pkg_p, struct archive **a, struct archive_entry **ae, con
		}
	}

-
	if (ret != ARCHIVE_OK && ret != ARCHIVE_EOF)
-
		retcode = pkg_error_set(EPKG_FATAL, "%s", archive_error_string(*a));
+
	if (ret != ARCHIVE_OK && ret != ARCHIVE_EOF) {
+
		pkg_emit_event(PKG_EVENT_ARCHIVE_ERROR, /*argc*/2,
+
		    archive_entry_pathname(*ae), *a);
+
		retcode = EPKG_FATAL;
+
	}

	if (ret == ARCHIVE_EOF)
		retcode = EPKG_END;
@@ -893,7 +902,7 @@ pkg_copy_tree(struct pkg *pkg, const char *src, const char *dest)

	if (packing_init(&pack, dest, 0) != EPKG_OK) {
		/* TODO */
-
		return (pkg_error_set(EPKG_FATAL, "unable to create archive"));
+
		return EPKG_FATAL;
	}

	while (pkg_files(pkg, &file) == EPKG_OK) {
modified libpkg/pkg.h
@@ -1,6 +1,7 @@
#ifndef _PKG_H
#define _PKG_H

+
#include <stdarg.h>
#include <sys/types.h>
#include <openssl/pem.h>

@@ -608,5 +609,65 @@ int pkg_copy_tree(struct pkg *, const char *src, const char *dest);
/**
 * scripts handling
 */
+
int pkg_script_pre_install(struct pkg *);
+
int pkg_script_post_install(struct pkg *);
+
int pkg_script_pre_upgrade(struct pkg *);
+
int pkg_script_post_upgrade(struct pkg *);
+
int pkg_script_pre_deinstall(struct pkg *);
+
int pkg_script_post_deinstall(struct pkg *);
int pkg_script_run(struct pkg *, pkg_script_t type);
+

+
/**
+
 * Event type used to report progress or problems.
+
 */
+
typedef enum {
+
	/* informational */
+
	PKG_EVENT_INSTALL_BEGIN = 0,
+

+
	/* errors */
+
	PKG_EVENT_ARCHIVE_COMP_UNSUP = 65536,
+
	PKG_EVENT_ARCHIVE_ERROR,
+
	PKG_EVENT_ALREADY_INSTALLED,
+
	PKG_EVENT_CKSUM_ERROR,
+
	PKG_EVENT_CONFIG_KEY_NOTFOUND,
+
	PKG_EVENT_CREATE_DB_ERROR,
+
	PKG_EVENT_DELETE_DEP_EXISTS,
+
	PKG_EVENT_ERROR_INSTALLING_DEP,
+
	PKG_EVENT_FETCH_ERROR,
+
	PKG_EVENT_INVALID_DB_STATE,
+
	PKG_EVENT_IO_ERROR,
+
	PKG_EVENT_MALLOC_ERROR,
+
	PKG_EVENT_MISSING_DEP,
+
	PKG_EVENT_OPEN_DB_ERROR,
+
	PKG_EVENT_PARSE_ERROR,
+
	PKG_EVENT_REPO_KEY_UNAVAIL,
+
	PKG_EVENT_REPO_KEY_UNUSABLE,
+
	PKG_EVENT_SQLITE_CONSTRAINT,
+
	PKG_EVENT_SQLITE_ERROR,
+
	PKG_EVENT_UNKNOWN_SCRIPT,
+
} pkg_event_t;
+

+
/**
+
 * Package handle for global state information
+
 */
+

+
/**
+
 * Event callback mechanism.  Events will be reported using this callback,
+
 * providing an event identifier and up to two event-specific pointers.
+
 */
+
typedef int(*pkg_event_cb)(pkg_event_t, void **);
+

+
struct pkg_handle {
+
	pkg_event_cb event_cb;
+
};
+

+
struct pkg_handle *pkg_get_handle(void);
+
pkg_event_cb pkg_handle_get_event_callback(struct pkg_handle *);
+
void pkg_handle_set_event_callback(struct pkg_handle *, pkg_event_cb);
+

+
void __pkg_emit_event(struct pkg_handle *, pkg_event_t, int, ...);
+

+
#define	pkg_emit_event(ev, argc, argv...) \
+
	__pkg_emit_event(pkg_get_handle(), ev, argc, argv)
+

#endif
modified libpkg/pkg_add.c
@@ -41,7 +41,9 @@ do_extract(struct archive *a, struct archive_entry *ae)

	do {
		if (archive_read_extract(a, ae, EXTRACT_ARCHIVE_FLAGS) != ARCHIVE_OK) {
-
			retcode = pkg_error_set(EPKG_FATAL, "%s", archive_error_string(a));
+
			pkg_emit_event(PKG_EVENT_ARCHIVE_ERROR, /*argc*/2,
+
			    archive_entry_pathname(ae), a);
+
			retcode = EPKG_FATAL;
			break;
		}

@@ -57,14 +59,19 @@ do_extract(struct archive *a, struct archive_entry *ae)
		    && lstat(path, &st) == ENOENT) {
			archive_entry_set_pathname(ae, path);
			if (archive_read_extract(a, ae, EXTRACT_ARCHIVE_FLAGS) != ARCHIVE_OK) {
-
				retcode = pkg_error_set(EPKG_FATAL, "%s", archive_error_string(a));
+
				pkg_emit_event(PKG_EVENT_ARCHIVE_ERROR, /*argc*/2,
+
				    archive_entry_pathname(ae), a);
+
				retcode = EPKG_FATAL;
				break;
			}
		}
	} while ((ret = archive_read_next_header(a, &ae)) == ARCHIVE_OK);

-
	if (ret != ARCHIVE_EOF)
-
		retcode = pkg_error_set(EPKG_FATAL, "%s", archive_error_string(a));
+
	if (ret != ARCHIVE_EOF) {
+
		pkg_emit_event(PKG_EVENT_ARCHIVE_ERROR, /*argc*/2,
+
		    archive_entry_pathname(ae), a);
+
		retcode = EPKG_FATAL;
+
	}

	return (retcode);
}
@@ -112,10 +119,10 @@ pkg_add(struct pkgdb *db, const char *path, struct pkg **pkg_p)

	ret = pkgdb_it_next(it, &p, PKG_LOAD_BASIC);
	pkgdb_it_free(it);
-
	pkg_free(p);

	if (ret == EPKG_OK) {
-
		retcode = pkg_error_set(EPKG_INSTALLED, "package already installed");
+
		pkg_emit_event(PKG_EVENT_ALREADY_INSTALLED, /*argc*/1, p);
+
		retcode = EPKG_INSTALLED;
		goto cleanup;
	} else if (ret != EPKG_END) {
		retcode = ret;
@@ -140,16 +147,18 @@ pkg_add(struct pkgdb *db, const char *path, struct pkg **pkg_p)

			if (access(dpath, F_OK) == 0) {
				if (pkg_add(db, dpath, NULL) != EPKG_OK) {
-
					retcode = pkg_error_set(EPKG_FATAL, "error while "
-
											"installing %s (dependency): %s",
-
											dpath,
-
											pkg_error_string());
+
					/* XXX: maybe the pkg_error_string()
+
					 * should be handled in the event
+
					 * handler */
+
					pkg_emit_event(PKG_EVENT_ERROR_INSTALLING_DEP,
+
					    /*argc*/2, dpath, pkg_error_string());
+
					retcode = EPKG_FATAL;
					goto cleanup;
				}
			} else {
-
				retcode = pkg_error_set(EPKG_DEPENDENCY, "missing %s-%s dependency",
-
										pkg_dep_name(dep),
-
										pkg_dep_version(dep));
+
				retcode = EPKG_FATAL;
+
				pkg_emit_event(PKG_EVENT_MISSING_DEP, /*argc*/2,
+
				    pkg_dep_name(dep), pkg_dep_version(dep));
				goto cleanup;
			}
		}
@@ -161,6 +170,8 @@ pkg_add(struct pkgdb *db, const char *path, struct pkg **pkg_p)
	if (retcode != EPKG_OK || pkgdb_has_flag(db, PKGDB_FLAG_IN_FLIGHT) == 0)
		goto cleanup_reg;

+
	pkg_emit_event(PKG_EVENT_INSTALL_BEGIN, /*argc*/1, pkg);
+

	/*
	 * Execute pre-install scripts
	 */
@@ -188,6 +199,9 @@ pkg_add(struct pkgdb *db, const char *path, struct pkg **pkg_p)
	if (a != NULL)
		archive_read_finish(a);

+
	if (p != NULL)
+
		pkg_free(p);
+

	if (pkg_p != NULL)
		*pkg_p = (retcode == EPKG_OK) ? pkg : NULL;
	else
modified libpkg/pkg_config.c
@@ -68,6 +68,6 @@ pkg_config(const char *key)
		}
	}

-
	pkg_error_set(EPKG_FATAL, "unknown configuration key `%s'", key);
+
	pkg_emit_event(PKG_EVENT_CONFIG_KEY_NOTFOUND, /*argc*/1, key);
	return (NULL);
}
modified libpkg/pkg_create_repo.c
@@ -76,8 +76,11 @@ pkg_create_repo(char *path, void (progress)(struct pkg *pkg, void *data), void *
		"INSERT INTO deps (origin, name, version, package_id) "
		"VALUES (?1, ?2, ?3, ?4);";

-
	if (!is_dir(path))
-
		return (pkg_error_set(EPKG_FATAL, "%s is not a directory", path));
+
	if (!is_dir(path)) {
+
		pkg_emit_event(PKG_EVENT_CREATE_DB_ERROR, /*argc*/3,
+
		    path, "not a directory", NULL);
+
		return EPKG_FATAL;
+
	}

	repopath[0] = path;
	repopath[1] = NULL;
@@ -85,21 +88,25 @@ pkg_create_repo(char *path, void (progress)(struct pkg *pkg, void *data), void *
	snprintf(repodb, MAXPATHLEN, "%s/repo.sqlite", path);

	if (stat(repodb, &st) != -1)
-
		if (unlink(repodb) != 0)
-
			return (pkg_error_set(EPKG_FATAL, "can not unlink %s: %s", repodb,
-
								  strerror(errno)));
+
		if (unlink(repodb) != 0) {
+
			pkg_emit_event(PKG_EVENT_CREATE_DB_ERROR,
+
			    /*argc*/3, path, repodb, strerror(errno));
+
			return EPKG_FATAL;
+
		}

	if (sqlite3_open(repodb, &sqlite) != SQLITE_OK)
		return (EPKG_FATAL);

	if (sqlite3_exec(sqlite, initsql, NULL, NULL, &errmsg) != SQLITE_OK) {
-
		retcode = pkg_error_set(EPKG_FATAL, "%s", errmsg);
+
		pkg_emit_event(PKG_EVENT_SQLITE_ERROR, /*argc*/1, errmsg);
+
		retcode = EPKG_FATAL;
		goto cleanup;
	}

	if (sqlite3_exec(sqlite, "BEGIN TRANSACTION;", NULL, NULL, &errmsg) !=
		SQLITE_OK) {
-
		retcode = pkg_error_set(EPKG_FATAL, "%s", errmsg);
+
		pkg_emit_event(PKG_EVENT_SQLITE_ERROR, /*argc*/1, errmsg);
+
		retcode = EPKG_FATAL;
		goto cleanup;
	}

@@ -114,8 +121,9 @@ pkg_create_repo(char *path, void (progress)(struct pkg *pkg, void *data), void *
	}

	if ((fts = fts_open(repopath, FTS_PHYSICAL, NULL)) == NULL) {
-
		retcode = pkg_error_set(EPKG_FATAL, "can not open %s: %s", repopath,
-
								strerror(errno));
+
		pkg_emit_event(PKG_EVENT_OPEN_DB_ERROR, /*argc*/2,
+
		    repopath, strerror(errno));
+
		retcode = EPKG_FATAL;
		goto cleanup;
	}

@@ -143,8 +151,10 @@ pkg_create_repo(char *path, void (progress)(struct pkg *pkg, void *data), void *

		if (pkg_open(&pkg, ent->fts_accpath) != EPKG_OK) {
			if (progress != NULL) {
-
				pkg_error_set(EPKG_WARN, "can not open %s: %s", ent->fts_name,
-
							  pkg_error_string());
+
				pkg_emit_event(PKG_EVENT_OPEN_DB_ERROR,
+
				    /*argc*/2, ent->fts_name,
+
				    pkg_error_string());
+
				retcode = EPKG_WARN;
				progress(NULL, data);
			}
			continue;
@@ -191,8 +201,10 @@ pkg_create_repo(char *path, void (progress)(struct pkg *pkg, void *data), void *

	}

-
	if (sqlite3_exec(sqlite, "COMMIT;", NULL, NULL, &errmsg) != SQLITE_OK)
-
		retcode = pkg_error_set(EPKG_FATAL, "%s", errmsg);
+
	if (sqlite3_exec(sqlite, "COMMIT;", NULL, NULL, &errmsg) != SQLITE_OK) {
+
		pkg_emit_event(PKG_EVENT_SQLITE_ERROR, /*argc*/1, errmsg);
+
		retcode = EPKG_FATAL;
+
	}

	cleanup:
	if (fts != NULL)
@@ -256,8 +268,11 @@ pkg_finish_repo(char *path, pem_password_cb *password_cb, char *rsa_key_path)

	packing_init(&pack, repo_archive, TXZ);
	if (rsa_key_path != NULL) {
-
		if (access(rsa_key_path, R_OK) == -1)
-
			return pkg_error_set(EPKG_FATAL, "RSA key invalid: %s", strerror(errno));
+
		if (access(rsa_key_path, R_OK) == -1) {
+
			pkg_emit_event(PKG_EVENT_REPO_KEY_UNAVAIL, /*argc*/2,
+
			    rsa_key_path, strerror(errno));
+
			return EPKG_FATAL;
+
		}

		SSL_load_error_strings();

@@ -271,8 +286,11 @@ pkg_finish_repo(char *path, pem_password_cb *password_cb, char *rsa_key_path)

		sha256_file(repo_path, sha256);

-
		if (RSA_sign(NID_sha1, sha256, 65, sigret, &siglen, rsa) == 0)
-
			return pkg_error_set(EPKG_FATAL, "Unable to sign the repository");
+
		if (RSA_sign(NID_sha1, sha256, 65, sigret, &siglen, rsa) == 0) {
+
			pkg_emit_event(PKG_EVENT_REPO_KEY_UNUSABLE, /*argc*/2,
+
			    rsa_key_path, ERR_get_error()); /* XXX pass back RSA errors correctly */
+
			return EPKG_FATAL;
+
		}

		packing_append_buffer(pack, sigret, "signature", max_len);

modified libpkg/pkg_delete.c
@@ -50,7 +50,9 @@ pkg_delete(struct pkg *pkg, struct pkgdb *db, int force)
	if (rdep_msg != NULL) {
		if (!force) {
			sbuf_finish(rdep_msg);
-
			ret = pkg_error_set(EPKG_REQUIRED, "%s", sbuf_get(rdep_msg));
+
			pkg_emit_event(PKG_EVENT_DELETE_DEP_EXISTS, /*argc*/1,
+
			    sbuf_get(rdep_msg));
+
			ret = EPKG_REQUIRED;
			sbuf_free(rdep_msg);
			return ret;
		}
added libpkg/pkg_event.c
@@ -0,0 +1,148 @@
+
#include <assert.h>
+
#include <archive.h>
+
#include "pkg.h"
+
#include "pkg_error.h"
+

+
/* Guard-rail against incorrect number of arguments */
+
static void
+
pkg_event_argument_check(pkg_event_t ev, int argc)
+
{
+

+
	switch(ev) {
+
	case PKG_EVENT_ALREADY_INSTALLED:
+
	case PKG_EVENT_CKSUM_ERROR:
+
	case PKG_EVENT_CONFIG_KEY_NOTFOUND:
+
	case PKG_EVENT_DELETE_DEP_EXISTS:
+
	case PKG_EVENT_FETCH_ERROR:
+
	case PKG_EVENT_INSTALL_BEGIN:
+
	case PKG_EVENT_INVALID_DB_STATE:
+
	case PKG_EVENT_MALLOC_ERROR:
+
	case PKG_EVENT_UNKNOWN_SCRIPT:
+
	case PKG_EVENT_SQLITE_ERROR:
+
		assert(argc == 1);
+
		break;
+
	case PKG_EVENT_ARCHIVE_COMP_UNSUP:
+
	case PKG_EVENT_ARCHIVE_ERROR:
+
	case PKG_EVENT_ERROR_INSTALLING_DEP:
+
	case PKG_EVENT_MISSING_DEP:
+
	case PKG_EVENT_OPEN_DB_ERROR:
+
	case PKG_EVENT_PARSE_ERROR:
+
	case PKG_EVENT_REPO_KEY_UNAVAIL:
+
	case PKG_EVENT_REPO_KEY_UNUSABLE:
+
	case PKG_EVENT_SQLITE_CONSTRAINT:
+
		assert(argc == 2);
+
		break;
+
	case PKG_EVENT_CREATE_DB_ERROR:
+
	case PKG_EVENT_IO_ERROR:
+
		assert(argc == 3);
+
		break;
+
	default:
+
		break;
+
	}
+
}
+

+
/**
+
 * This function's purpose is to perform global event handling.
+
 */
+
static void
+
libpkg_handle_event(pkg_event_t ev, void **argv)
+
{
+
	switch(ev) {
+
	case PKG_EVENT_ALREADY_INSTALLED:
+
		pkg_error_set(EPKG_INSTALLED, "package '%s' already installed",
+
		    pkg_get(argv[0], PKG_NAME));
+
		break;
+
	case PKG_EVENT_ARCHIVE_COMP_UNSUP:
+
		break;
+
	case PKG_EVENT_ARCHIVE_ERROR:
+
		pkg_error_set(EPKG_FATAL, "archive error at %s: %s",
+
		    argv[0], archive_error_string(argv[1]));
+
		break;
+
	case PKG_EVENT_CKSUM_ERROR:
+
		pkg_error_set(EPKG_FATAL, "package '%s' failed checksum",
+
		    pkg_get(argv[0], PKG_NAME));
+
		break;
+
	case PKG_EVENT_CONFIG_KEY_NOTFOUND:
+
		pkg_error_set(EPKG_FATAL, "unknown configuration key `%s'", argv[0]);
+
		break;
+
	case PKG_EVENT_CREATE_DB_ERROR:
+
		if (argv[2] == NULL)
+
			pkg_error_set(EPKG_FATAL, "%s: %s", argv[0], argv[1]);
+
		else
+
			pkg_error_set(EPKG_FATAL, "%s(%s): %s", argv[0], argv[1], argv[2]);
+
		break;
+
	case PKG_EVENT_DELETE_DEP_EXISTS:
+
		pkg_error_set(EPKG_REQUIRED, "%s", argv[0]);
+
		break;
+
	case PKG_EVENT_ERROR_INSTALLING_DEP:
+
		pkg_error_set(EPKG_FATAL, "error while installing dependency %s: %s",
+
		    argv[0], argv[1]);
+
		break;
+
	case PKG_EVENT_FETCH_ERROR:
+
		pkg_error_set(EPKG_FATAL, "%s", argv[0]);
+
		break;
+
	case PKG_EVENT_INVALID_DB_STATE:
+
		pkg_error_set(EPKG_FATAL, "%s", argv[0]);
+
		break;
+
	case PKG_EVENT_IO_ERROR:
+
		pkg_error_set(EPKG_FATAL, "I/O error: %s(%s): %s",
+
		    /*call*/argv[0], /*arg*/argv[1], /*strerror*/argv[2]);
+
		break;
+
	case PKG_EVENT_MALLOC_ERROR:
+
		pkg_error_set(EPKG_FATAL, "allocation error: %s", argv[0]);
+
		break;
+
	case PKG_EVENT_MISSING_DEP:
+
		pkg_error_set(EPKG_DEPENDENCY, "missing %s-%s dependency",
+
		    argv[0], argv[1]);
+
		break;
+
	case PKG_EVENT_OPEN_DB_ERROR:
+
		pkg_error_set(EPKG_FATAL, "db open(%s) failed: %s", argv[0], argv[1]);
+
		break;
+
	case PKG_EVENT_PARSE_ERROR:
+
		pkg_error_set(EPKG_FATAL, "parse error(%s): %s", argv[0], argv[1]);
+
		break;
+
	case PKG_EVENT_REPO_KEY_UNAVAIL:
+
		pkg_error_set(EPKG_FATAL, "RSA key %s invalid: %s", argv[0], argv[1]);
+
		break;
+
	case PKG_EVENT_REPO_KEY_UNUSABLE:
+
		pkg_error_set(EPKG_FATAL, "RSA key %s unusable: %s", argv[0], argv[1]);
+
		break;
+
	case PKG_EVENT_SQLITE_CONSTRAINT:
+
		pkg_error_set(EPKG_FATAL, "constraint violation on %s: %s", argv[0], argv[1]);
+
		break;
+
	case PKG_EVENT_SQLITE_ERROR:
+
		pkg_error_set(EPKG_FATAL, "sqlite: %s", argv[0]);
+
		break;
+
	case PKG_EVENT_UNKNOWN_SCRIPT:
+
		pkg_error_set(EPKG_FATAL, "unknown script '%s'", argv[0]);
+
		break;
+
	default:
+
		break;
+
	}
+
}
+

+
void
+
__pkg_emit_event(struct pkg_handle *hdl, pkg_event_t ev, int argc, ...)
+
{
+
	va_list ap;
+
	void **argv;
+
	int i;
+

+
	if (hdl == NULL)
+
		return;
+

+
	pkg_event_argument_check(ev, argc);
+

+
	/* Generate the argument vector to pass in. */
+
	argv = calloc(argc, sizeof(void *));
+
	va_start(ap, argc);
+
	for (i = 0;i < argc; i++)
+
		argv[i] = va_arg(ap, void *);
+
	va_end(ap);
+

+
	libpkg_handle_event(ev, argv);
+

+
	if (hdl->event_cb != NULL)
+
		hdl->event_cb(ev, argv);
+
	free(argv);
+
}
added libpkg/pkg_handle.c
@@ -0,0 +1,21 @@
+
#include "pkg.h"
+

+
struct pkg_handle __pkg_handle_singleton;
+

+
struct pkg_handle *
+
pkg_get_handle(void)
+
{
+
	return &__pkg_handle_singleton;
+
}
+

+
void
+
pkg_handle_set_event_callback(struct pkg_handle *hdl, pkg_event_cb event_cb)
+
{
+
	hdl->event_cb = event_cb;
+
}
+

+
pkg_event_cb
+
pkg_handle_get_event_callback(struct pkg_handle *hdl)
+
{
+
	return hdl->event_cb;
+
}
modified libpkg/pkg_manifest.c
@@ -64,7 +64,6 @@ m_parse_set_string(struct pkg *pkg, char *buf, pkg_attr attr) {
		return (EPKG_FATAL);

	pkg_set(pkg, attr, buf);
-

	return (EPKG_OK);
}

@@ -131,14 +130,19 @@ m_parse_flatsize(struct pkg *pkg, char *buf)
	errno = 0;
	size = strtoimax(buf, NULL, 10);

-
	if (errno == EINVAL || errno == ERANGE)
-
		return (pkg_error_set(EPKG_FATAL, "m_parse_flatsize(): %s",
-
				strerror(errno)));
+
	if (errno == EINVAL || errno == ERANGE) {
+
		pkg_emit_event(PKG_EVENT_PARSE_ERROR, /*argc*/2,
+
		    "flatsize", strerror(errno));
+
		return EPKG_FATAL;
+
	}

	pkg_setflatsize(pkg, size);
	return (EPKG_OK);
}

+
/**
+
 * option: <key> <value>
+
 */
static int
m_parse_option(struct pkg *pkg, char *buf)
{
@@ -154,46 +158,43 @@ m_parse_option(struct pkg *pkg, char *buf)
	if (value == NULL)
		return (EPKG_FATAL);

-
	value[0] = '\0';
-
	value++;
+
	*value++ = '\0';

	pkg_addoption(pkg, buf, value);
-

	return (EPKG_OK);
}

+
/**
+
 * dep: <name> <origin> <version>
+
 */
static int
m_parse_dep(struct pkg *pkg, char *buf)
{
-
	char *buf_ptr;
-
	size_t next;
	const char *name, *origin, *version;

	while (isspace(*buf))
		buf++;

-
	buf_ptr = buf;
-

-
	if (split_chr(buf_ptr, ' ') != 2)
+
	if (split_chr(buf, ' ') != 2)
		return (EPKG_FATAL);

-
	next = strlen(buf_ptr);
-
	name = buf_ptr;
-

-
	buf_ptr += next + 1;
-
	next = strlen(buf_ptr);
-

-
	origin = buf_ptr;
+
	name = buf;
+
	while (*buf != '\0')
+
		buf++;

-
	buf_ptr += next + 1;
+
	origin = buf;
+
	while (*buf != '\0')
+
		buf++;

-
	version = buf_ptr;
+
	version = buf;

	pkg_adddep(pkg, name, origin, version);
-

	return (EPKG_OK);
}

+
/**
+
 * conflict: <pkgname>
+
 */
static int
m_parse_conflict(struct pkg *pkg, char *buf)
{
@@ -204,10 +205,12 @@ m_parse_conflict(struct pkg *pkg, char *buf)
		return (EPKG_FATAL);

	pkg_addconflict(pkg, buf);
-

	return (EPKG_OK);
}

+
/**
+
 * file: <sha256 checksum> <file path>
+
 */
static int
m_parse_file(struct pkg *pkg, char *buf)
{
@@ -217,30 +220,26 @@ m_parse_file(struct pkg *pkg, char *buf)
	while (isspace(*buf))
		buf++;

-
	while (isspace(buf[0]))
-
		buf++;
-

-
	if (buf[0] == '-')
-
		sha256 = NULL;
-
	else
-
		sha256 = buf;
+
	sha256 = (*buf != '-') ? buf : NULL;

-
	while (!isspace(buf[0]))
+
	while (!isspace(*buf))
		buf++;

-
	buf[0] = '\0';
-
	buf++;
-

-
	if (buf[0] != '/')
-
		return (EPKG_FATAL);
+
	if (*(buf + 1) != '/') {
+
		warnx("parse error: / expected near '%s'", buf + 1);
+
		return EPKG_FATAL;
+
	}

+
	*buf++ = '\0';
	path = buf;

	pkg_addfile(pkg, path, sha256);
-

	return (EPKG_OK);
}

+
/**
+
 * dir: <dir path>
+
 */
static int
m_parse_dir(struct pkg *pkg, char *buf)
{
modified libpkg/pkg_repo.c
@@ -35,8 +35,10 @@ pkg_repo_fetch(struct pkg *pkg, void *data, fetch_cb cb)
	checksum:
	retcode = sha256_file(dest, cksum);
	if (retcode == EPKG_OK)
-
		if (strcmp(cksum, pkg_get(pkg, PKG_CKSUM)))
-
			retcode = pkg_error_set(EPKG_FATAL, "failed checksum");
+
		if (strcmp(cksum, pkg_get(pkg, PKG_CKSUM))) {
+
			pkg_emit_event(PKG_EVENT_CKSUM_ERROR, /*argc*/1, pkg);
+
			retcode = EPKG_FATAL;
+
		}

	cleanup:
	if (retcode != EPKG_OK)
modified libpkg/pkg_util.c
@@ -70,23 +70,30 @@ file_to_buffer(const char *path, char **buffer, off_t *sz)
		return (ERROR_BAD_ARG("buffer"));

	if ((fd = open(path, O_RDONLY)) == -1) {
-
		return (pkg_error_set(EPKG_FATAL, "can not open %s: %s", path,
-
				strerror(errno)));
+
		pkg_emit_event(PKG_EVENT_IO_ERROR, /*argc*/3, "open",
+
		    path, strerror(errno));
+
		return EPKG_FATAL;
	}

	if (fstat(fd, &st) == -1) {
		close(fd);
-
		return (pkg_error_set(EPKG_FATAL, "fstat(): %s", strerror(errno)));
+
		pkg_emit_event(PKG_EVENT_IO_ERROR, /*argc*/3, "fstat",
+
		    path, strerror(errno));
+
		return EPKG_FATAL;
	}

	if ((*buffer = malloc(st.st_size + 1)) == NULL) {
		close(fd);
-
		return (pkg_error_set(EPKG_FATAL, "malloc(): %s", strerror(errno)));
+
		pkg_emit_event(PKG_EVENT_MALLOC_ERROR, /*argc*/1,
+
		    strerror(errno));
+
		return EPKG_FATAL;
	}

	if (read(fd, *buffer, st.st_size) == -1) {
		close(fd);
-
		return (pkg_error_set(EPKG_FATAL, "read(%s): %s", path, strerror(errno)));
+
		pkg_emit_event(PKG_EVENT_IO_ERROR, /*argc*/3, "read",
+
		    path, strerror(errno));
+
		return EPKG_FATAL;
	}

	close(fd);
@@ -206,9 +213,11 @@ sha256_file(const char *path, char out[65])
	size_t r = 0;
	SHA256_CTX sha256;

-
	if ((fp = fopen(path, "rb")) == NULL)
-
		return (pkg_error_set(EPKG_FATAL, "fopen(%s): %s", path,
-
							  strerror(errno)));
+
	if ((fp = fopen(path, "rb")) == NULL) {
+
		pkg_emit_event(PKG_EVENT_IO_ERROR, /*argc*/3, "open",
+
		    path, strerror(errno));
+
		return EPKG_FATAL;
+
	}

	SHA256_Init(&sha256);

@@ -218,9 +227,9 @@ sha256_file(const char *path, char out[65])
	if (ferror(fp) != 0) {
		fclose(fp);
		out[0] = '\0';
-
		return (pkg_error_set(EPKG_FATAL, "fread(%s): %s", path,
-
							  strerror(errno)));
-

+
		pkg_emit_event(PKG_EVENT_IO_ERROR, /*argc*/3, "read",
+
		    path, strerror(errno));
+
		return EPKG_FATAL;
	}

	fclose(fp);
modified libpkg/pkgdb.c
@@ -228,7 +228,7 @@ pkgdb_init(sqlite3 *sdb)
	;

	if (sqlite3_exec(sdb, sql, NULL, NULL, &errmsg) != SQLITE_OK) {
-
		pkg_error_set(EPKG_FATAL, "sqlite: %s", errmsg);
+
		pkg_emit_event(PKG_EVENT_SQLITE_ERROR, /*argc*/1, errmsg);
		sqlite3_free(errmsg);
		return (EPKG_FATAL);
	}
@@ -248,20 +248,27 @@ pkgdb_open(struct pkgdb **db, pkgdb_t type)

	dbdir = pkg_config("PKG_DBDIR");

-
	if ((*db = calloc(1, sizeof(struct pkgdb))) == NULL)
-
		return (pkg_error_set(EPKG_FATAL, "calloc(): %s", strerror(errno)));
+
	if ((*db = calloc(1, sizeof(struct pkgdb))) == NULL) {
+
		pkg_emit_event(PKG_EVENT_MALLOC_ERROR, /*argc*/1,
+
		    strerror(errno));
+
		return EPKG_FATAL;
+
	}

	(*db)->type = type;

	snprintf(localpath, sizeof(localpath), "%s/local.sqlite", dbdir);
	retcode = access(localpath, R_OK);
	if (retcode == -1) {
-
		if (errno != ENOENT)
-
			return (pkg_error_set(EPKG_FATAL, "%s: %s", localpath,
-
								  strerror(errno)));
-
		else if (eaccess(dbdir, W_OK) != 0)
-
			return (pkg_error_set(EPKG_FATAL, "can not initialize database in "
-
								 "%s: %s", dbdir, strerror(errno)));
+
		if (errno != ENOENT) {
+
			pkg_emit_event(PKG_EVENT_IO_ERROR, /*argc*/3, "access",
+
			    localpath, strerror(errno));
+
			return EPKG_FATAL;
+
		}
+
		else if (eaccess(dbdir, W_OK) != 0) {
+
			pkg_emit_event(PKG_EVENT_IO_ERROR, /*argc*/3, "eaccess",
+
			    dbdir, strerror(errno));
+
			return EPKG_FATAL;
+
		}
	}

	if (sqlite3_open(localpath, &(*db)->sqlite) != SQLITE_OK)
@@ -270,14 +277,16 @@ pkgdb_open(struct pkgdb **db, pkgdb_t type)
	if (type == PKGDB_REMOTE) {
		snprintf(remotepath, sizeof(remotepath), "%s/repo.sqlite", dbdir);

-
		if (access(remotepath, R_OK) != 0)
-
			return (pkg_error_set(EPKG_FATAL, "repo.sqlite: %s",
-
								  strerror(errno)));
+
		if (access(remotepath, R_OK) != 0) {
+
			pkg_emit_event(PKG_EVENT_IO_ERROR, /*argc*/3, "access",
+
			    remotepath, strerror(errno));
+
			return EPKG_FATAL;
+
		}

		sqlite3_snprintf(sizeof(sql), sql, "ATTACH \"%s\" as remote;", remotepath);

		if (sqlite3_exec((*db)->sqlite, sql, NULL, NULL, &errmsg) != SQLITE_OK) {
-
			pkg_error_set(EPKG_FATAL, "%s", errmsg);
+
			pkg_emit_event(PKG_EVENT_SQLITE_ERROR, /*argc*/1, errmsg);
			sqlite3_free(errmsg);
			return (EPKG_FATAL);
		}
@@ -303,7 +312,7 @@ pkgdb_open(struct pkgdb **db, pkgdb_t type)
	 */
	if (sqlite3_exec((*db)->sqlite, "PRAGMA foreign_keys = ON;", NULL, NULL,
		&errmsg) != SQLITE_OK) {
-
		pkg_error_set(EPKG_FATAL, "sqlite: %s", errmsg);
+
		pkg_emit_event(PKG_EVENT_SQLITE_ERROR, /*argc*/1, errmsg);
		sqlite3_free(errmsg);
		return (EPKG_FATAL);
	}
@@ -333,7 +342,8 @@ pkgdb_it_new(struct pkgdb *db, sqlite3_stmt *s, int type)
	struct pkgdb_it *it;

	if ((it = malloc(sizeof(struct pkgdb_it))) == NULL) {
-
		pkg_error_set(EPKG_FATAL, "malloc(): %s", strerror(errno));
+
		pkg_emit_event(PKG_EVENT_MALLOC_ERROR, /*argc*/1,
+
		    strerror(errno));
		sqlite3_finalize(s);
		return (NULL);
	}
@@ -956,14 +966,15 @@ pkgdb_register_pkg(struct pkgdb *db, struct pkg *pkg)
		"VALUES (?1, ?2);";

	if (pkgdb_has_flag(db, PKGDB_FLAG_IN_FLIGHT)) {
-
		pkg_error_set(EPKG_FATAL, "tried to register a package with an in-flight SQL command");
+
		pkg_emit_event(PKG_EVENT_INVALID_DB_STATE, /*argc*/1,
+
		    "tried to register a package with an in-flight SQL command");
		return (EPKG_FATAL);
	}

	s = db->sqlite;

	if (sqlite3_exec(s, sql_begin, NULL, NULL, &errmsg) != SQLITE_OK) {
-
		pkg_error_set(EPKG_FATAL, "sqlite: %s", errmsg);
+
		pkg_emit_event(PKG_EVENT_SQLITE_ERROR, /*argc*/1, errmsg);
		sqlite3_free(errmsg);
		return (EPKG_FATAL);
	}
@@ -992,10 +1003,11 @@ pkgdb_register_pkg(struct pkgdb *db, struct pkg *pkg)
	sqlite3_bind_int(stmt_pkg, 14, pkg_isautomatic(pkg));

	if ((ret = sqlite3_step(stmt_pkg)) != SQLITE_DONE) {
-
		if ( ret == SQLITE_CONSTRAINT)
-
			retcode = pkg_error_set(EPKG_FATAL, "constraint violation on "
-
					"pkg with %s", pkg_get(pkg, PKG_ORIGIN));
-
		else
+
		if ( ret == SQLITE_CONSTRAINT) {
+
			pkg_emit_event(PKG_EVENT_SQLITE_CONSTRAINT, /*argc*/2,
+
			    "pkg", pkg_get(pkg, PKG_ORIGIN));
+
			retcode = EPKG_FATAL;
+
		} else
			retcode = ERROR_SQLITE(s);
		goto cleanup;
	}
@@ -1035,10 +1047,11 @@ pkgdb_register_pkg(struct pkgdb *db, struct pkg *pkg)
		sqlite3_bind_int64(stmt_dep, 4, package_id);

		if ((ret = sqlite3_step(stmt_dep)) != SQLITE_DONE) {
-
			if ( ret == SQLITE_CONSTRAINT)
-
				retcode = pkg_error_set(EPKG_FATAL, "constraint violation on "
-
						"deps with %s", pkg_dep_origin(dep));
-
			else
+
			if ( ret == SQLITE_CONSTRAINT) {
+
				pkg_emit_event(PKG_EVENT_SQLITE_CONSTRAINT,
+
				    /*argc*/2, "deps", pkg_dep_origin(dep));
+
				retcode = EPKG_FATAL;
+
			} else
				retcode = ERROR_SQLITE(s);
			goto cleanup;
		}
@@ -1060,10 +1073,12 @@ pkgdb_register_pkg(struct pkgdb *db, struct pkg *pkg)
		sqlite3_bind_int64(stmt_conflict, 2, package_id);

		if ((ret = sqlite3_step(stmt_conflict)) != SQLITE_DONE) {
-
			if ( ret == SQLITE_CONSTRAINT)
-
				retcode = pkg_error_set(EPKG_FATAL, "constraint violation on "
-
						"conflicts with %s", pkg_conflict_glob(conflict));
-
			else
+
			if ( ret == SQLITE_CONSTRAINT) {
+
				pkg_emit_event(PKG_EVENT_SQLITE_CONSTRAINT,
+
				    /*argc*/2, "conflicts",
+
				    pkg_conflict_glob(conflict));
+
				retcode = EPKG_FATAL;
+
			} else
				retcode = ERROR_SQLITE(s);
			goto cleanup;
		}
@@ -1087,10 +1102,11 @@ pkgdb_register_pkg(struct pkgdb *db, struct pkg *pkg)
		sqlite3_bind_int64(stmt_file, 3, package_id);

		if ((ret = sqlite3_step(stmt_file)) != SQLITE_DONE) {
-
			if (ret == SQLITE_CONSTRAINT)
-
				retcode = pkg_error_set(EPKG_FATAL, "constraint violation on "
-
						"path with %s", pkg_file_path(file));
-
			else
+
			if (ret == SQLITE_CONSTRAINT) {
+
				pkg_emit_event(PKG_EVENT_SQLITE_CONSTRAINT,
+
				    /*argc*/2, "path", pkg_file_path(file));
+
				retcode = EPKG_FATAL;
+
			} else
				retcode = ERROR_SQLITE(s);
			goto cleanup;
		}
@@ -1111,10 +1127,11 @@ pkgdb_register_pkg(struct pkgdb *db, struct pkg *pkg)
		sqlite3_bind_text(stmt_dirs, 2, pkg_dir_path(dir), -1, SQLITE_STATIC);
			
		if ((ret = sqlite3_step(stmt_dirs)) != SQLITE_DONE) {
-
			if ( ret == SQLITE_CONSTRAINT)
-
				retcode = pkg_error_set(EPKG_FATAL, "constraint violation on "
-
						"dirs with %s", pkg_dir_path(dir));
-
			else
+
			if ( ret == SQLITE_CONSTRAINT) {
+
				pkg_emit_event(PKG_EVENT_SQLITE_CONSTRAINT,
+
				    /*argc*/2, "dirs", pkg_dir_path(dir));
+
				retcode = EPKG_FATAL;
+
			} else
				retcode = ERROR_SQLITE(s);
			goto cleanup;
		}
@@ -1136,10 +1153,12 @@ pkgdb_register_pkg(struct pkgdb *db, struct pkg *pkg)
		sqlite3_bind_int64(stmt_script, 3, package_id);

		if (sqlite3_step(stmt_script) != SQLITE_DONE) {
-
			if ( ret == SQLITE_CONSTRAINT)
-
				retcode = pkg_error_set(EPKG_FATAL, "constraint violation on "
-
						"scripts with %s", pkg_script_data(script));
-
			else
+
			if ( ret == SQLITE_CONSTRAINT) {
+
				pkg_emit_event(PKG_EVENT_SQLITE_CONSTRAINT,
+
				    /*argc*/2, "scripts",
+
				    pkg_script_data(script));
+
				retcode = EPKG_FATAL;
+
			} else
				retcode = ERROR_SQLITE(s);
			goto cleanup;
		}
@@ -1207,13 +1226,15 @@ pkgdb_register_finale(struct pkgdb *db, int retcode)
	char *errmsg;

	if (!pkgdb_has_flag(db, PKGDB_FLAG_IN_FLIGHT)) {
-
		ret = pkg_error_set(EPKG_FATAL, "database command not in flight");
-
		return ret;
+
		pkg_emit_event(PKG_EVENT_INVALID_DB_STATE, /*argc*/1,
+
		    "database command not in flight");
+
		return EPKG_FATAL;
	}

	command = (retcode == EPKG_OK) ? commands[0] : commands[1];
	if (sqlite3_exec(db->sqlite, command, NULL, NULL, &errmsg) != SQLITE_OK) {
-
		ret = pkg_error_set(EPKG_FATAL, "sqlite: %s", errmsg);
+
		pkg_emit_event(PKG_EVENT_SQLITE_ERROR, /*argc*/1, errmsg);
+
		ret = EPKG_FATAL;
		sqlite3_free(errmsg);
	}
	PKGDB_UNSET_FLAG(db, PKGDB_FLAG_IN_FLIGHT);
@@ -1302,7 +1323,8 @@ pkgdb_compact(struct pkgdb *db)
		return (EPKG_OK);

	if (sqlite3_exec(db->sqlite, "VACUUM;", NULL, NULL, &errmsg) != SQLITE_OK){
-
		retcode = pkg_error_set(EPKG_FATAL, "%s", errmsg);
+
		pkg_emit_event(PKG_EVENT_SQLITE_ERROR, /*argc*/1, errmsg);
+
		retcode = EPKG_FATAL;
		sqlite3_free(errmsg);
	}

@@ -1315,7 +1337,8 @@ pkgdb_query_upgrades(struct pkgdb *db)
	sqlite3_stmt *stmt;

	if (db->type != PKGDB_REMOTE) {
-
		pkg_error_set(EPKG_FATAL, "remote database not attached");
+
		pkg_emit_event(PKG_EVENT_INVALID_DB_STATE, /*argc*/1,
+
		    "remote database not attached");
		return (NULL);
	}

@@ -1343,7 +1366,8 @@ pkgdb_query_downgrades(struct pkgdb *db)
	sqlite3_stmt *stmt;

	if (db->type != PKGDB_REMOTE) {
-
		pkg_error_set(EPKG_FATAL, "remote database not attached");
+
		pkg_emit_event(PKG_EVENT_INVALID_DB_STATE, /*argc*/1,
+
		    "remote database not attached");
		return (NULL);
	}

modified pkg/Makefile
@@ -3,6 +3,7 @@ SRCS= add.c \
		autoremove.c \
		create.c \
		delete.c \
+
		event.c \
		info.c \
		main.c \
		register.c \
added pkg/event.c
@@ -0,0 +1,27 @@
+
#include <archive.h>
+
#include <err.h>
+

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

+
int
+
event_callback(pkg_event_t ev, void **argv)
+
{
+

+
	switch(ev) {
+
	case PKG_EVENT_INSTALL_BEGIN:
+
		printf("Installing %s\n", pkg_get((struct pkg *)argv[0], PKG_NAME));
+
		break;
+
	case PKG_EVENT_ARCHIVE_ERROR:
+
		fprintf(stderr, "archive error on %s: %s\n",
+
		    (const char *)argv[0], archive_error_string(argv[1]));
+
		break;
+
	case PKG_EVENT_ARCHIVE_COMP_UNSUP:
+
		warnx("%s is not supported, trying %s",
+
		    (const char *)argv[0], (const char *)argv[1]);
+
		break;
+
	default:
+
		break;
+
	}
+
	return 0;
+
}
added pkg/event.h
@@ -0,0 +1,6 @@
+
#ifndef __PKG_EVENT_H__
+
#define	__PKG_EVENT_H__
+

+
int event_callback(pkg_event_t ev, void **argv);
+

+
#endif
modified pkg/main.c
@@ -5,8 +5,10 @@
#include <string.h>
#include <sysexits.h>

+
#include "pkg.h"
#include "create.h"
#include "delete.h"
+
#include "event.h"
#include "info.h"
#include "which.h"
#include "add.h"
@@ -104,10 +106,14 @@ main(int argc, char **argv)
	struct commands *command = NULL;
	unsigned int ambiguous = 0;
	size_t len;
+
	struct pkg_handle *hdl;

	if (argc < 2)
		usage();

+
	hdl = pkg_get_handle();
+
	pkg_handle_set_event_callback(hdl, event_callback);
+

	len = strlen(argv[1]);
	for (i = 0; i < cmd_len; i++) {
		if (strncmp(argv[1], cmd[i].name, len) == 0) {