Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Implement new database locking system in pkg.
Vsevolod Stakhov committed 12 years ago
commit 95ae8df33224f8fdb0f0413577cfe803f47e6a9d
parent 065865b
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);
}