Radish alpha
H
HardenedBSD Package Manager
Radicle
Git (anonymous pull)
Log in to clone via SSH
Implement new database locking system in pkg.
Vsevolod Stakhov committed 12 years ago
commit 95ae8df33224f8fdb0f0413577cfe803f47e6a9d
parent 065865b3c9ca6025c4bf8380007d37f0cfc3569d
27 files changed +447 -110
modified libpkg/pkg.h.in
@@ -497,6 +497,17 @@ typedef enum {
	PKG_UPGRADE,
} pkg_change_t;

+
/**
+
 * Locking types for database:
+
 * `PKGDB_LOCK_READONLY`: lock for read only queries (can be nested)
+
 * `PKGDB_LOCK_ADVISORY`: write to DB inside a transaction (allows `PKGDB_LOCK_READONLY`)
+
 * `PKGDB_LOCK_EXCLUSIVE`: possibly destructive operations (does not allow other locks)
+
 */
+
typedef enum {
+
	PKGDB_LOCK_READONLY,
+
	PKGDB_LOCK_ADVISORY,
+
	PKGDB_LOCK_EXCLUSIVE
+
} pkgdb_lock_t;

#define PKG_OPEN_MANIFEST_ONLY 0x1
#define PKG_OPEN_MANIFEST_COMPACT (0x1 << 1)
@@ -1015,6 +1026,14 @@ int pkgdb_access(unsigned mode, unsigned database);
int pkgdb_open(struct pkgdb **db, pkgdb_t type);

/**
+
 * 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_release_lock(struct pkgdb *db, pkgdb_lock_t type);
+

+
/**
 * Close and free the struct pkgdb.
 */
void pkgdb_close(struct pkgdb *db);
modified libpkg/pkg_jobs.c
@@ -101,10 +101,6 @@ pkg_jobs_free(struct pkg_jobs *j)
	if (j == NULL)
		return;

-
	if ((j->flags & PKG_FLAG_DRY_RUN) == 0 &&
-
		j->type != PKG_JOBS_FETCH)
-
		pkgdb_release_lock(j->db);
-

	HASH_ITER(hh, j->request_add, req, tmp) {
		HASH_DEL(j->request_add, req);
		free(req);
@@ -976,20 +972,12 @@ jobs_sort_priority_inc(struct pkg_solved *r1, struct pkg_solved *r2)
int
pkg_jobs_solve(struct pkg_jobs *j)
{
-
	bool dry_run = false;
	int ret, pstatus;
	struct pkg_solve_problem *problem;
	const char *solver;
	FILE *spipe[2];
	pid_t pchild;

-
	if ((j->flags & PKG_FLAG_DRY_RUN) == PKG_FLAG_DRY_RUN)
-
		dry_run = true;
-

-
	if (!dry_run && pkgdb_obtain_lock(j->db) != EPKG_OK)
-
		return (EPKG_FATAL);
-

-

	switch (j->type) {
	case PKG_JOBS_AUTOREMOVE:
		ret =jobs_solve_autoremove(j);
@@ -1183,6 +1171,12 @@ pkg_jobs_install(struct pkg_jobs *j)
	
	pkg_config_bool(PKG_CONFIG_HANDLE_RC_SCRIPTS, &handle_rc);

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

	p = NULL;
	pkg_manifest_keys_new(&keys);
	/* Install */
@@ -1210,6 +1204,7 @@ pkg_jobs_install(struct pkg_jobs *j)

cleanup:
	pkgdb_transaction_commit(j->db->sqlite, "upgrade");
+
	pkgdb_release_lock(j->db, PKGDB_LOCK_EXCLUSIVE);
	pkg_manifest_keys_free(keys);

	return (retcode);
@@ -1233,6 +1228,10 @@ pkg_jobs_deinstall(struct pkg_jobs *j)
	if ((j->flags & PKG_FLAG_NOSCRIPT) == PKG_FLAG_NOSCRIPT)
		flags |= PKG_DELETE_NOSCRIPT;

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

	DL_FOREACH(j->jobs_delete, ps) {
		p = ps->pkg[0];
		pkg_get(p, PKG_NAME, &name);
@@ -1244,10 +1243,11 @@ pkg_jobs_deinstall(struct pkg_jobs *j)
		retcode = pkg_delete(p, j->db, flags);

		if (retcode != EPKG_OK)
-
			return (retcode);
+
			goto cleanup;
	}
-

-
	return (EPKG_OK);
+
cleanup:
+
	pkgdb_release_lock(j->db, PKGDB_LOCK_EXCLUSIVE);
+
	return (retcode);
}

int
modified libpkg/pkgdb.c
@@ -4160,37 +4160,146 @@ pkgshell_open(const char **reponame)
	*reponame = strdup(localpath);
}

+
static int
+
pkgdb_try_lock (struct pkgdb *db, const char *lock_sql,
+
		double delay, unsigned int retries)
+
{
+
	unsigned int tries = 0;
+
	struct timespec ts;
+
	int ret = EPKG_END;
+

+
	while (tries <= retries) {
+
		ret = sqlite3_exec(db->sqlite, lock_sql, NULL, NULL, NULL);
+
		if (ret != SQLITE_OK)
+
			return (EPKG_FATAL);
+

+
		if (sqlite3_changes(db->sqlite) == 0) {
+
			if (delay > 0) {
+
				ts.tv_sec = (int)delay;
+
				ts.tv_nsec = (delay - (int)delay) * 1000000000.;
+
				pkg_debug(1, "waiting for database lock for %d times, "
+
						"next try in %.2f seconds", tries, delay);
+
				(void)nanosleep(&ts, NULL);
+
			}
+
			else {
+
				break;
+
			}
+
		}
+
		else {
+
			ret = EPKG_OK;
+
			break;
+
		}
+
		tries ++;
+
	}
+

+
	return (ret);
+
}
+

int
-
pkgdb_obtain_lock(struct pkgdb *db)
+
pkgdb_obtain_lock(struct pkgdb *db, pkgdb_lock_t type,
+
		double delay, unsigned int retries)
{
	int ret;
+
	const char table_sql[] = ""
+
			"CREATE TABLE pkg_lock "
+
			"(exclusive INT(1), advisory INT(1), read INT(8));";
+
	const char init_sql[] = ""
+
			"INSERT INTO pkg_lock VALUES(0,0,0);";
+
	const char readonly_lock_sql[] = ""
+
			"UPDATE pkg_lock SET read=read+1 WHERE exclusive=0;";
+
	const char advisory_lock_sql[] = ""
+
			"UPDATE pkg_lock SET advisory=1 WHERE exclusive=0 AND advisory=0;";
+
	const char exclusive_lock_sql[] = ""
+
			"UPDATE pkg_lock SET exclusive=1 WHERE exclusive=0 AND advisory=0 AND read=0;";
+
	const char *lock_sql;

	assert(db != NULL);
-
	assert(db->lock_count >= 0);
-
	if (!db->lock_count) {
-
		ret = sql_exec(db->sqlite,
-
		    "PRAGMA main.locking_mode=EXCLUSIVE;BEGIN IMMEDIATE;COMMIT;");
-
		/* Set lock only if we actually were able to switch locking mode */
-
		if (ret == EPKG_OK)
-
			++db->lock_count;
-
		return (ret);
+

+
	ret = sqlite3_exec(db->sqlite, table_sql, NULL, NULL, NULL);
+
	if (ret == SQLITE_OK) {
+
		/* Need to initialize */
+
		ret = sqlite3_exec(db->sqlite, init_sql, NULL, NULL, NULL);
+
		if (ret != SQLITE_OK) {
+
			ERROR_SQLITE(db->sqlite);
+
			return (EPKG_FATAL);
+
		}
	}
-
	else
-
		return (EPKG_OK);
+

+
	switch (type) {
+
	case PKGDB_LOCK_READONLY:
+
		lock_sql = readonly_lock_sql;
+
		pkg_debug(1, "want to get a read only lock on a database");
+
		break;
+
	case PKGDB_LOCK_ADVISORY:
+
		lock_sql = advisory_lock_sql;
+
		pkg_debug(1, "want to get an advisory lock on a database");
+
		break;
+
	case PKGDB_LOCK_EXCLUSIVE:
+
		pkg_debug(1, "want to get an exclusive lock on a database");
+
		lock_sql = exclusive_lock_sql;
+
		break;
+
	}
+

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

+
	return (ret);
}

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

	assert(db != NULL);
-
	assert(db->lock_count >= 0);
-
	if (db->lock_count > 0)
-
		db->lock_count--;
-
	if (db->lock_count == 0)
-
		return sql_exec(db->sqlite,
-
		    "PRAGMA main.locking_mode=NORMAL;BEGIN IMMEDIATE;COMMIT;");
-
	else
-
		return (EPKG_OK);
+

+
	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);
+
	}
+

+
	return (ret);
+
}
+

+
int
+
pkgdb_release_lock(struct pkgdb *db, pkgdb_lock_t type)
+
{
+
	const char readonly_unlock_sql[] = ""
+
			"UPDATE pkg_lock SET read=read-1 WHERE read>0;";
+
	const char advisory_unlock_sql[] = ""
+
			"UPDATE pkg_lock SET advisory=0 WHERE advisory=1;";
+
	const char exclusive_unlock_sql[] = ""
+
			"UPDATE pkg_lock SET exclusive=0 WHERE exclusive=1;";
+
	const char *unlock_sql;
+
	int ret = EPKG_FATAL;
+

+
	assert(db != NULL);
+

+
	switch (type) {
+
	case PKGDB_LOCK_READONLY:
+
		unlock_sql = readonly_unlock_sql;
+
		pkg_debug(1, "release a read only lock on a database");
+
		break;
+
	case PKGDB_LOCK_ADVISORY:
+
		unlock_sql = advisory_unlock_sql;
+
		pkg_debug(1, "release an advisory lock on a database");
+
		break;
+
	case PKGDB_LOCK_EXCLUSIVE:
+
		pkg_debug(1, "release an exclusive lock on a database");
+
		unlock_sql = exclusive_unlock_sql;
+
		break;
+
	}
+

+
	ret = sqlite3_exec(db->sqlite, unlock_sql, NULL, NULL, NULL);
+
	if (ret != SQLITE_OK)
+
		return (EPKG_FATAL);
+

+
	if (sqlite3_changes(db->sqlite) == 0)
+
		return (EPKG_END);
+

+
	return (EPKG_OK);
}

int64_t
modified libpkg/private/pkgdb.h
@@ -65,9 +65,6 @@ int pkgdb_transaction_rollback(sqlite3 *sqlite, const char *savepoint);

struct pkgdb_it *pkgdb_it_new(struct pkgdb *db, sqlite3_stmt *s, int type, short flags);

-
int pkgdb_obtain_lock(struct pkgdb *db);
-
int pkgdb_release_lock(struct pkgdb *db);
-

void pkgshell_open(const char **r);

/**
modified pkg/add.c
@@ -113,6 +113,12 @@ 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) {
+
		pkgdb_close(db);
+
		warnx("Cannot get an exclusive lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}
+

	failedpkgs = sbuf_new_auto();
	pkg_manifest_keys_new(&keys);
	for (i = 0; i < argc; i++) {
@@ -157,7 +163,7 @@ exec_add(int argc, char **argv)

	}
	pkg_manifest_keys_free(keys);
-

+
	pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
	pkgdb_close(db);
	
	if(failedpkgcount > 0) {
modified pkg/annotate.c
@@ -283,8 +283,13 @@ exec_annotate(int argc, char **argv)

	retcode = pkgdb_open(&db, PKGDB_DEFAULT);
	if (retcode != EPKG_OK) {
-
		exitcode = EX_IOERR;
-
		goto cleanup;
+
		return (EX_IOERR);
+
	}
+

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

	if ((it = pkgdb_query(db, pkgname, match)) == NULL) {
@@ -324,8 +329,9 @@ cleanup:
		pkg_free(pkg);
	if (it != NULL)
		pkgdb_it_free(it);
-
	if (db != NULL)
-
		pkgdb_close(db);
+

+
	pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
+
	pkgdb_close(db);
	if (input != NULL)
		sbuf_delete(input);

modified pkg/audit.c
@@ -809,6 +809,12 @@ exec_audit(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) {
+
		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");
@@ -839,6 +845,7 @@ exec_audit(int argc, char **argv)

cleanup:
	pkgdb_it_free(it);
+
	pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
	pkgdb_close(db);
	pkg_free(pkg);
	free_audit_list(h);
modified pkg/autoremove.c
@@ -101,6 +101,11 @@ exec_autoremove(__unused int argc, __unused char **argv)
		return (EX_IOERR);
	}

+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_ADVISORY, 0, 0) != EPKG_OK) {
+
		pkgdb_close(db);
+
		warnx("Cannot get an advisory lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}
	/* Always force packages to be removed */
	if (pkg_jobs_new(&jobs, PKG_JOBS_AUTOREMOVE, db) != EPKG_OK) {
		pkgdb_close(db);
@@ -139,6 +144,7 @@ exec_autoremove(__unused int argc, __unused char **argv)

cleanup:
	pkg_jobs_free(jobs);
+
	pkgdb_release_lock(db, PKGDB_LOCK_ADVISORY);
	pkgdb_close(db);

	return (retcode);
modified pkg/check.c
@@ -339,11 +339,17 @@ 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) {
+
		pkgdb_close(db);
+
		warnx("Cannot get an advisory lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}
+

	i = 0;
	do {
		if ((it = pkgdb_query(db, argv[i], match)) == NULL) {
-
			pkgdb_close(db);
-
			return (EX_IOERR);
+
			rc = EX_IOERR;
+
			goto cleanup;
		}

		while (pkgdb_it_next(it, &pkg, flags) == EPKG_OK) {
@@ -364,18 +370,32 @@ exec_check(int argc, char **argv)
				}
			}
			if (recompute) {
-
				if (verbose)
-
					pkg_printf("Recomputing size and checksums: %n\n", pkg);
-
				if (pkg_recompute(db, pkg) != EPKG_OK) {
-
					rc = EX_DATAERR;
+
				if (pkgdb_upgrade_lock(db, PKGDB_LOCK_ADVISORY,
+
						PKGDB_LOCK_EXCLUSIVE, 0.5, 20) == EPKG_OK) {
+
					if (verbose)
+
						pkg_printf("Recomputing size and checksums: %n\n", pkg);
+
					if (pkg_recompute(db, pkg) != EPKG_OK) {
+
						rc = EX_DATAERR;
+
					}
+
					pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
+
				}
+
				else {
+
					rc = EX_TEMPFAIL;
				}
			}
			if (reanalyse_shlibs) {
-
				if (verbose)
-
					pkg_printf("Reanalyzing files for shlibs: %n\n", pkg);
-
				if (pkgdb_reanalyse_shlibs(db, pkg) != EPKG_OK) {
-
					pkg_printf("Failed to reanalyse for shlibs: %n\n", pkg);
-
					rc = EX_UNAVAILABLE;
+
				if (pkgdb_upgrade_lock(db, PKGDB_LOCK_ADVISORY,
+
						PKGDB_LOCK_EXCLUSIVE, 0.5, 20) == EPKG_OK) {
+
					if (verbose)
+
						pkg_printf("Reanalyzing files for shlibs: %n\n", pkg);
+
					if (pkgdb_reanalyse_shlibs(db, pkg) != EPKG_OK) {
+
						pkg_printf("Failed to reanalyse for shlibs: %n\n", pkg);
+
						rc = EX_UNAVAILABLE;
+
					}
+
					pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
+
				}
+
				else {
+
					rc = EX_TEMPFAIL;
				}
			}
		}
@@ -383,21 +403,32 @@ exec_check(int argc, char **argv)
		if (dcheck && nbpkgs > 0 && !noinstall) {
			printf("\n>>> Missing package dependencies were detected.\n");
			printf(">>> Found %d issue(s) in the package database.\n\n", nbpkgs);
-

-
			ret = fix_deps(db, &dh, nbpkgs, yes);
-
			if (ret == EPKG_OK)
-
				check_summary(db, &dh);
-
			else if (ret == EPKG_ENODB) {
-
				db = NULL;
-
				return (EX_IOERR);
+
			if (pkgdb_upgrade_lock(db, PKGDB_LOCK_ADVISORY,
+
					PKGDB_LOCK_EXCLUSIVE, 0.5, 20) == EPKG_OK) {
+
				ret = fix_deps(db, &dh, nbpkgs, yes);
+
				if (ret == EPKG_OK)
+
					check_summary(db, &dh);
+
				else if (ret == EPKG_ENODB) {
+
					db = NULL;
+
					rc = EX_IOERR;
+
				}
+
				pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
+
				if (rc == EX_IOERR)
+
					goto cleanup;
+
			}
+
			else {
+
				rc = EX_TEMPFAIL;
+
				goto cleanup;
			}
		}
		pkgdb_it_free(it);
		i++;
	} while (i < argc);

+
cleanup:
	deps_free(&dh);
	pkg_free(pkg);
+
	pkgdb_release_lock(db, PKGDB_LOCK_ADVISORY);
	pkgdb_close(db);

	return (rc);
modified pkg/clean.c
@@ -280,8 +280,13 @@ exec_clean(int argc, char **argv)

	retcode = EX_SOFTWARE;

-
	if (pkgdb_open(&db, PKGDB_REMOTE) != EPKG_OK) {
-
		goto cleanup;
+
	if (pkgdb_open(&db, PKGDB_REMOTE) != 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 ((fts = fts_open(paths, FTS_PHYSICAL, NULL)) == NULL) {
@@ -416,8 +421,9 @@ cleanup:
	pkg_free(p);
	if (fts != NULL)
		fts_close(fts);
-
	if (db != NULL)
-
		pkgdb_close(db);
+

+
	pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
+
	pkgdb_close(db);

	return (retcode);
}
modified pkg/convert.c
@@ -69,9 +69,13 @@ convert_to_old(const char *pkg_add_dbdir, bool dry_run)
	if (mkdir(pkg_add_dbdir, 0755) != 0 && errno != EEXIST)
		err(EX_CANTCREAT, "%s", pkg_add_dbdir);

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

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

	if ((it = pkgdb_query(db, NULL, MATCH_ALL)) == NULL) {
@@ -218,6 +222,7 @@ convert_to_old(const char *pkg_add_dbdir, bool dry_run)
cleanup:
	pkg_free(pkg);
	pkgdb_it_free(it);
+
	pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
	pkgdb_close(db);

	return (ret);
modified pkg/create.c
@@ -86,6 +86,12 @@ pkg_create_matches(int argc, char **argv, match_t match, pkg_formats fmt,
		pkgdb_close(db);
		return (EX_IOERR);
	}
+
	/* XXX: get rid of hardcoded timeouts */
+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE, 0.5, 20) != EPKG_OK) {
+
		pkgdb_close(db);
+
		warnx("Cannot get an exclusive lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}

	switch (fmt) {
	case TXZ:
@@ -154,6 +160,7 @@ pkg_create_matches(int argc, char **argv, match_t match, pkg_formats fmt,
	}

cleanup:
+
	pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
	pkgdb_close(db);

	return (retcode);
modified pkg/delete.c
@@ -130,6 +130,13 @@ 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) {
+
		pkgdb_close(db);
+
		warnx("Cannot get an advisory lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}
+

+

	if (pkg_jobs_new(&jobs, PKG_JOBS_DEINSTALL, db) != EPKG_OK) {
		pkgdb_close(db);
		return (EX_IOERR);
@@ -186,6 +193,7 @@ exec_delete(int argc, char **argv)
	retcode = EX_OK;

cleanup:
+
	pkgdb_release_lock(db, PKGDB_LOCK_ADVISORY);
	pkg_jobs_free(jobs);
	pkgdb_close(db);

modified pkg/fetch.c
@@ -151,6 +151,13 @@ exec_fetch(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) {
+
		pkgdb_close(db);
+
		warnx("Cannot get a read lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}
+

+

	if (pkg_jobs_new(&jobs, PKG_JOBS_FETCH, db) != EPKG_OK)
		goto cleanup;

@@ -183,6 +190,7 @@ exec_fetch(int argc, char **argv)

cleanup:
	pkg_jobs_free(jobs);
+
	pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
	pkgdb_close(db);

	return (retcode);
modified pkg/info.c
@@ -234,6 +234,12 @@ 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) {
+
		pkgdb_close(db);
+
		warnx("Cannot get a read lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}
+

	i = 0;
	do {
		gotone = false;
@@ -327,7 +333,7 @@ exec_info(int argc, char **argv)
		}

		if ((it = pkgdb_query(db, pkgname, match)) == NULL) {
-
			return (EX_IOERR);
+
			goto cleanup;
		}

		/* this is place for compatibility hacks */
@@ -423,7 +429,11 @@ exec_info(int argc, char **argv)
		i++;
	} while (i < argc);

-
	pkg_free(pkg);
+
cleanup:
+
	if (pkg != NULL)
+
		pkg_free(pkg);
+

+
	pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
	pkgdb_close(db);

	return (retcode);
modified pkg/install.c
@@ -158,6 +158,12 @@ exec_install(int argc, char **argv)
	if (pkgdb_open(&db, PKGDB_REMOTE) != EPKG_OK)
		return (EX_IOERR);

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

	if (pkg_jobs_new(&jobs, PKG_JOBS_INSTALL, db) != EPKG_OK)
		goto cleanup;

@@ -206,6 +212,7 @@ exec_install(int argc, char **argv)
	retcode = EX_OK;

cleanup:
+
	pkgdb_release_lock(db, PKGDB_LOCK_ADVISORY);
	pkg_jobs_free(jobs);
	pkgdb_close(db);

modified pkg/lock.c
@@ -178,6 +178,12 @@ 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) {
+
		pkgdb_close(db);
+
		warnx("Cannot get an exclusive lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}
+

	if ((it = pkgdb_query(db, pkgname, match)) == NULL) {
		exitcode = EX_IOERR;
		goto cleanup;
@@ -202,8 +208,9 @@ cleanup:
		pkg_free(pkg);
	if (it != NULL)
		pkgdb_it_free(it);
-
	if (db != NULL)
-
		pkgdb_close(db);
+

+
	pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
+
	pkgdb_close(db);

	return (exitcode);
}
modified pkg/query.c
@@ -882,12 +882,14 @@ exec_query(int argc, char **argv)
	/* Default to all packages if no pkg provided */
	if (argc == 1 && pkgname == NULL && condition == NULL && match == MATCH_EXACT) {
		match = MATCH_ALL;
-
	} else if ((argc == 1) ^ (match == MATCH_ALL) && pkgname == NULL && condition == NULL) {
+
	} else if (((argc == 1) ^ (match == MATCH_ALL)) && pkgname == NULL
+
			&& condition == NULL) {
		usage_query();
		return (EX_USAGE);
	}

-
	if (analyse_query_string(argv[0], accepted_query_flags, q_flags_len, &query_flags, &multiline) != EPKG_OK)
+
	if (analyse_query_string(argv[0], accepted_query_flags, q_flags_len,
+
			&query_flags, &multiline) != EPKG_OK)
		return (EX_USAGE);

	if (pkgname != NULL) {
@@ -926,6 +928,12 @@ 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) {
+
		pkgdb_close(db);
+
		warnx("Cannot get an read lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}
+

	if (match == MATCH_ALL || match == MATCH_CONDITION) {
		const char *condition_sql = NULL;
		if (match == MATCH_CONDITION && sqlcond)
@@ -945,8 +953,10 @@ exec_query(int argc, char **argv)
		for (i = 1; i < argc; i++) {
			pkgname = argv[i];

-
			if ((it = pkgdb_query(db, pkgname, match)) == NULL)
-
				return (EX_IOERR);
+
			if ((it = pkgdb_query(db, pkgname, match)) == NULL) {
+
				retcode = EX_IOERR;
+
				goto cleanup;
+
			}

			while ((ret = pkgdb_it_next(it, &pkg, query_flags)) == EPKG_OK) {
				nprinted++;
@@ -967,7 +977,11 @@ exec_query(int argc, char **argv)
		}
	}

-
	pkg_free(pkg);
+
cleanup:
+
	if (pkg != NULL)
+
		pkg_free(pkg);
+

+
	pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
	pkgdb_close(db);

	return (retcode);
modified pkg/register.c
@@ -162,7 +162,7 @@ exec_register(int argc, char **argv)
	}

	/*
-
         * Ideally, the +MANIFEST should be all that is necessary,
+
	 * Ideally, the +MANIFEST should be all that is necessary,
	 * since it can contain all of the meta-data supplied by the
	 * other files mentioned below.  These are here for backwards
	 * compatibility with the way the ports tree works with
@@ -273,12 +273,19 @@ exec_register(int argc, char **argv)
	}


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

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

	/*
-
         * testing_mode allows updating the local package database
+
	 * testing_mode allows updating the local package database
	 * without any check that the files etc. listed in the meta
	 * data actually exist on the system.  Inappropriate use of
	 * testing_mode can really screw things up.
@@ -316,8 +323,10 @@ exec_register(int argc, char **argv)
	if (!legacy && pkg_has_message(pkg))
		pkg_printf("%M\n", pkg);

-
	if (!old)
+
	if (!old) {
+
		pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
		pkgdb_close(db);
+
	}

	pkg_free(pkg);

modified pkg/set.c
@@ -147,12 +147,19 @@ 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) {
+
		pkgdb_close(db);
+
		warnx("Cannot get an exclusive lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}
+

 
	if (oldorigin != NULL) {
		match = MATCH_ALL;
		if ((it = pkgdb_query(db, oldorigin, MATCH_EXACT)) == NULL) {
-
			pkgdb_close(db);
-
			return (EX_IOERR);
+
			retcode = EX_IOERR;
+
			goto cleanup;
		}

		if (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) != EPKG_OK) {
@@ -173,8 +180,10 @@ exec_set(int argc, char **argv)
				    "[y/N]: ", oldorigin, neworigin);
		}
		if (pkg != NULL && yes) {
-
			if (pkgdb_set(db, pkg, PKG_SET_ORIGIN, neworigin) != EPKG_OK)
-
				return (EX_IOERR);
+
			if (pkgdb_set(db, pkg, PKG_SET_ORIGIN, neworigin) != EPKG_OK) {
+
				retcode = EX_IOERR;
+
				goto cleanup;
+
			}
		}
		pkgdb_it_free(it);
	}
@@ -183,9 +192,8 @@ exec_set(int argc, char **argv)
		bool save_yes = yes;

		if ((it = pkgdb_query(db, argv[i], match)) == NULL) {
-
			free(oldorigin);
-
			pkgdb_close(db);
-
			return (EX_IOERR);
+
			retcode = EX_IOERR;
+
			goto cleanup;
		}

		while (pkgdb_it_next(it, &pkg, loads) == EPKG_OK) {
@@ -210,8 +218,10 @@ exec_set(int argc, char **argv)
					 * Do not query user when he has already
					 * been queried.
					 */
-
					if (pkgdb_set(db, pkg, PKG_SET_DEPORIGIN, oldorigin, neworigin) != EPKG_OK)
-
						return (EX_IOERR);
+
					if (pkgdb_set(db, pkg, PKG_SET_DEPORIGIN, oldorigin, neworigin) != EPKG_OK) {
+
						retcode = EX_IOERR;
+
						goto cleanup;
+
					}
				}
			}
		}
@@ -219,9 +229,15 @@ exec_set(int argc, char **argv)
		i++;
	} while (i < argc);

-
	free(oldorigin);
-
	pkg_free(pkg);
+
cleanup:
+
	if (oldorigin)
+
		free(oldorigin);
+

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

+
	pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
	pkgdb_close(db);

-
	return (EX_OK);
+
	return (retcode);
}
modified pkg/shell.c
@@ -42,6 +42,7 @@ usage_shell(void)
int
exec_shell(int argc, char **argv)
{
+
	/* XXX: need exclusive lock here */
	pkgdb_cmd(argc, argv);
	return (EX_OK);
}
modified pkg/shlib.c
@@ -174,6 +174,14 @@ exec_shlib(int argc, char **argv)
	}

	retcode = pkgdb_open(&db, PKGDB_DEFAULT);
+
	if (retcode != 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 (retcode == EPKG_OK && !requires_only)
		retcode = pkgs_providing_lib(db, libname);
@@ -184,6 +192,7 @@ exec_shlib(int argc, char **argv)
	if (retcode != EPKG_OK)
		retcode = (EX_IOERR);
		
+
	pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
	pkgdb_close(db);
	return (retcode);
}
modified pkg/stats.c
@@ -29,6 +29,7 @@
#include <unistd.h>
#include <inttypes.h>
#include <libutil.h>
+
#include <err.h>

#include <pkg.h>

@@ -81,6 +82,12 @@ exec_stats(__unused int argc, __unused char **argv)
		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 (opt & STATS_LOCAL) {
		printf("Local package database:\n");
		printf("\tInstalled packages: %" PRId64 "\n", pkgdb_stats(db, PKG_STATS_LOCAL_COUNT));
@@ -111,6 +118,7 @@ exec_stats(__unused int argc, __unused char **argv)
		}
	}

+
	pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
	pkgdb_close(db);

	return (EX_OK);
modified pkg/updating.c
@@ -69,6 +69,7 @@ exec_updating(int argc, char **argv)
	struct pkgdb_it *it = NULL;
	const char *origin;
	FILE *fd;
+
	int retcode = EXIT_SUCCESS;

	while ((ch = getopt(argc, argv, "d:f:")) != -1) {
		switch (ch) {
@@ -94,10 +95,18 @@ 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) {
+
		pkgdb_close(db);
+
		warnx("Cannot get a read lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}
+

	SLIST_INIT(&origins);
	if (argc == 0) {
-
		if ((it = pkgdb_query(db, NULL, MATCH_ALL)) == NULL)
+
		if ((it = pkgdb_query(db, NULL, MATCH_ALL)) == NULL) {
+
			retcode = EX_UNAVAILABLE;
			goto cleanup;
+
		}

		while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) == EPKG_OK) {
			port = malloc(sizeof(struct installed_ports));
@@ -117,15 +126,17 @@ exec_updating(int argc, char **argv)
	if (updatingfile == NULL) {
		const char *portsdir;
		if (pkg_config_string(PKG_CONFIG_PORTSDIR, &portsdir) != EPKG_OK) {
-
			warnx("Cannot get portsdir config entry!");
-
			return (1);
+
			retcode = EX_CONFIG;
+
			goto cleanup;
		}
		asprintf(&updatingfile, "%s/UPDATING", portsdir);
	}

	fd = fopen(updatingfile, "r");
-
	if (fd == NULL)
-
		errx(EX_UNAVAILABLE, "Unable to open: %s", updatingfile);
+
	if (fd == NULL) {
+
		warnx("Unable to open: %s", updatingfile);
+
		goto cleanup;
+
	}

	while ((linelen = getline(&line, &linecap, fd)) > 0) {
		if (strspn(line, "0123456789:") == 9) {
@@ -156,10 +167,12 @@ exec_updating(int argc, char **argv)
		}
	}
	fclose(fd);
+

cleanup:
	pkgdb_it_free(it);
+
	pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
	pkgdb_close(db);
	pkg_free(pkg);

-
	return (EXIT_SUCCESS);
+
	return (retcode);
}
modified pkg/upgrade.c
@@ -132,6 +132,12 @@ exec_upgrade(int argc, char **argv)
	if (pkgdb_open(&db, PKGDB_REMOTE) != EPKG_OK)
		return (EX_IOERR);

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

	if (pkg_jobs_new(&jobs, PKG_JOBS_UPGRADE, db) != EPKG_OK)
		goto cleanup;

@@ -172,8 +178,9 @@ exec_upgrade(int argc, char **argv)

	retcode = EXIT_SUCCESS;

-
	cleanup:
+
cleanup:
	pkg_jobs_free(jobs);
+
	pkgdb_release_lock(db, PKGDB_LOCK_ADVISORY);
	pkgdb_close(db);

	if (!yes && newpkgversion)
modified pkg/version.c
@@ -172,6 +172,7 @@ exec_version(int argc, char **argv)
	char *pattern=NULL;
	struct stat sb;
	char portsdirmakefile[MAXPATHLEN];
+
	int retcode = EXIT_SUCCESS;

	pkg_config_bool(PKG_CONFIG_REPO_AUTOUPDATE, &auto_update);

@@ -325,7 +326,6 @@ exec_version(int argc, char **argv)
		   user is forced to have a repo.sqlite */
		if (opt & VERSION_SOURCE_REMOTE) {
			if (auto_update) {
-
				int retcode;

				retcode = pkgcli_update(false);
				if (retcode != EPKG_OK)
@@ -337,6 +337,12 @@ exec_version(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) {
+
			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, pattern, match)) == NULL)
			goto cleanup;

@@ -345,8 +351,11 @@ exec_version(int argc, char **argv)
			rel_major_ver = (int) strtol(u.release, NULL, 10);
			snprintf(indexpath, sizeof(indexpath), "%s/INDEX-%d", portsdir, rel_major_ver);
			indexfile = fopen(indexpath, "r");
-
			if (!indexfile)
-
				err(EX_SOFTWARE, "Unable to open %s!", indexpath);
+
			if (!indexfile) {
+
				warnx("Unable to open %s!", indexpath);
+
				retcode = EX_IOERR;
+
				goto cleanup;
+
			}

			while ((linelen = getline(&line, &linecap, indexfile)) > 0) {
				/* line is pkgname|portdir|... */
@@ -432,7 +441,8 @@ cleanup:
	pkg_free(pkg);
	pkg_free(pkg_remote);
	pkgdb_it_free(it);
+
	pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
	pkgdb_close(db);

-
	return (EX_OK);
+
	return (retcode);
}
modified pkg/which.c
@@ -33,6 +33,7 @@
#include <string.h>
#include <unistd.h>
#include <sysexits.h>
+
#include <err.h>

#include "pkgcli.h"

@@ -80,11 +81,6 @@ exec_which(int argc, char **argv)
		return (EX_USAGE);
	}

-
	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK) {
-
		pkgdb_close(db);
-
		return (EX_IOERR);
-
	}
-

	if (!glob)
		absolutepath(argv[0], pathabs, sizeof(pathabs));
	else {
@@ -92,8 +88,20 @@ exec_which(int argc, char **argv)
			return (EX_USAGE);
	}

-
	if ((it = pkgdb_query_which(db, pathabs, glob)) == NULL)
+
	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 ((it = pkgdb_query_which(db, pathabs, glob)) == NULL) {
+
		retcode = EX_IOERR;
+
		goto cleanup;
+
	}

	while ((ret = pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC)) == EPKG_OK) {
		retcode = EX_OK;
@@ -113,6 +121,9 @@ exec_which(int argc, char **argv)
	pkg_free(pkg);
	pkgdb_it_free(it);

+
cleanup:
+
	pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
	pkgdb_close(db);
+

	return (retcode);
}