summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--utils/nfsdcltrack/nfsdcltrack.c2
-rw-r--r--utils/nfsdcltrack/sqlite.c223
-rw-r--r--utils/nfsdcltrack/sqlite.h1
3 files changed, 141 insertions, 85 deletions
diff --git a/utils/nfsdcltrack/nfsdcltrack.c b/utils/nfsdcltrack/nfsdcltrack.c
index 4334340..f281361 100644
--- a/utils/nfsdcltrack/nfsdcltrack.c
+++ b/utils/nfsdcltrack/nfsdcltrack.c
@@ -241,7 +241,7 @@ cltrack_init(const char __attribute__((unused)) *unused)
}
/* set up storage db */
- ret = sqlite_maindb_init(storagedir);
+ ret = sqlite_prepare_dbh(storagedir);
if (ret) {
xlog(L_ERROR, "Failed to init database: %d", ret);
/*
diff --git a/utils/nfsdcltrack/sqlite.c b/utils/nfsdcltrack/sqlite.c
index 1c8d935..dde2f7f 100644
--- a/utils/nfsdcltrack/sqlite.c
+++ b/utils/nfsdcltrack/sqlite.c
@@ -88,135 +88,192 @@ mkdir_if_not_exist(const char *dirname)
return ret;
}
-/* Open the database and set up the database handle for it */
-int
-sqlite_prepare_dbh(const char *topdir)
+static int
+sqlite_query_schema_version(void)
{
int ret;
+ sqlite3_stmt *stmt = NULL;
- /* Do nothing if the database handle is already set up */
- if (dbh)
- return 0;
-
- ret = snprintf(buf, PATH_MAX - 1, "%s/main.sqlite", topdir);
- if (ret < 0)
- return ret;
-
- buf[PATH_MAX - 1] = '\0';
-
- ret = sqlite3_open(buf, &dbh);
+ /* prepare select query */
+ ret = sqlite3_prepare_v2(dbh,
+ "SELECT value FROM parameters WHERE key == \"version\";",
+ -1, &stmt, NULL);
if (ret != SQLITE_OK) {
- xlog(L_ERROR, "Unable to open main database: %d", ret);
- dbh = NULL;
- return ret;
+ xlog(L_ERROR, "Unable to prepare select statement: %s",
+ sqlite3_errstr(ret));
+ ret = 0;
+ goto out;
}
- ret = sqlite3_busy_timeout(dbh, CLTRACK_SQLITE_BUSY_TIMEOUT);
- if (ret != SQLITE_OK) {
- xlog(L_ERROR, "Unable to set sqlite busy timeout: %d", ret);
- sqlite3_close(dbh);
- dbh = NULL;
+ /* query schema version */
+ ret = sqlite3_step(stmt);
+ if (ret != SQLITE_ROW) {
+ xlog(L_ERROR, "Select statement execution failed: %s",
+ sqlite3_errstr(ret));
+ ret = 0;
+ goto out;
}
+ ret = sqlite3_column_int(stmt, 0);
+out:
+ sqlite3_finalize(stmt);
return ret;
}
/*
- * Open the "main" database, and attempt to initialize it by creating the
- * parameters table and inserting the schema version into it. Ignore any errors
- * from that, and then attempt to select the version out of it again. If the
- * version appears wrong, then assume that the DB is corrupt or has been
- * upgraded, and return an error. If all of that works, then attempt to create
- * the "clients" table.
+ * Start an exclusive transaction and recheck the DB schema version. If it's
+ * still zero (indicating a new database) then set it up. If that all works,
+ * then insert schema version into the parameters table and commit the
+ * transaction. On any error, rollback the transaction.
*/
int
-sqlite_maindb_init(const char *topdir)
+sqlite_maindb_init_v1(void)
{
int ret;
char *err = NULL;
- sqlite3_stmt *stmt = NULL;
- ret = mkdir_if_not_exist(topdir);
- if (ret)
+ /* Start a transaction */
+ ret = sqlite3_exec(dbh, "BEGIN EXCLUSIVE TRANSACTION;", NULL, NULL,
+ &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to begin transaction: %s", err);
return ret;
+ }
- ret = sqlite_prepare_dbh(topdir);
- if (ret)
- return ret;
+ /*
+ * Check schema version again. This time, under an exclusive
+ * transaction to guard against racing DB setup attempts
+ */
+ ret = sqlite_query_schema_version();
+ switch (ret) {
+ case 0:
+ /* Query failed again -- set up DB */
+ break;
+ case CLTRACK_SQLITE_LATEST_SCHEMA_VERSION:
+ /* Someone else raced in and set it up */
+ ret = 0;
+ goto rollback;
+ default:
+ /* Something went wrong -- fail! */
+ ret = -EINVAL;
+ goto rollback;
+ }
- /* Try to create table */
- ret = sqlite3_exec(dbh, "CREATE TABLE IF NOT EXISTS parameters "
+ ret = sqlite3_exec(dbh, "CREATE TABLE parameters "
"(key TEXT PRIMARY KEY, value TEXT);",
NULL, NULL, &err);
if (ret != SQLITE_OK) {
- xlog(L_ERROR, "Unable to create parameter table: %d", ret);
- goto out_err;
+ xlog(L_ERROR, "Unable to create parameter table: %s", err);
+ goto rollback;
}
- /* insert version into table -- ignore error if it fails */
- ret = snprintf(buf, sizeof(buf),
- "INSERT OR IGNORE INTO parameters values (\"version\", "
- "\"%d\");", CLTRACK_SQLITE_LATEST_SCHEMA_VERSION);
+ /* create the "clients" table */
+ ret = sqlite3_exec(dbh, "CREATE TABLE clients (id BLOB PRIMARY KEY, "
+ "time INTEGER);",
+ NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to create clients table: %s", err);
+ goto rollback;
+ }
+
+
+ /* insert version into parameters table */
+ ret = snprintf(buf, sizeof(buf), "INSERT OR FAIL INTO parameters "
+ "values (\"version\", \"%d\");",
+ CLTRACK_SQLITE_LATEST_SCHEMA_VERSION);
if (ret < 0) {
- goto out_err;
+ xlog(L_ERROR, "sprintf failed!");
+ goto rollback;
} else if ((size_t)ret >= sizeof(buf)) {
+ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret);
ret = -EINVAL;
- goto out_err;
+ goto rollback;
}
ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err);
if (ret != SQLITE_OK) {
- xlog(L_ERROR, "Unable to insert into parameter table: %d",
- ret);
- goto out_err;
+ xlog(L_ERROR, "Unable to insert into parameter table: %s", err);
+ goto rollback;
}
- ret = sqlite3_prepare_v2(dbh,
- "SELECT value FROM parameters WHERE key == \"version\";",
- -1, &stmt, NULL);
+ ret = sqlite3_exec(dbh, "COMMIT TRANSACTION;", NULL, NULL, &err);
if (ret != SQLITE_OK) {
- xlog(L_ERROR, "Unable to prepare select statement: %d", ret);
- goto out_err;
+ xlog(L_ERROR, "Unable to commit transaction: %s", err);
+ goto rollback;
}
+out:
+ sqlite3_free(err);
+ return ret;
- /* check schema version */
- ret = sqlite3_step(stmt);
- if (ret != SQLITE_ROW) {
- xlog(L_ERROR, "Select statement execution failed: %s",
- sqlite3_errmsg(dbh));
- goto out_err;
+rollback:
+ /* Attempt to rollback the transaction */
+ sqlite3_exec(dbh, "ROLLBACK TRANSACTION;", NULL, NULL, &err);
+ goto out;
+}
+
+/* Open the database and set up the database handle for it */
+int
+sqlite_prepare_dbh(const char *topdir)
+{
+ int ret;
+
+ /* Do nothing if the database handle is already set up */
+ if (dbh)
+ return 0;
+
+ ret = snprintf(buf, PATH_MAX - 1, "%s/main.sqlite", topdir);
+ if (ret < 0)
+ return ret;
+
+ buf[PATH_MAX - 1] = '\0';
+
+ /* open a new DB handle */
+ ret = sqlite3_open(buf, &dbh);
+ if (ret != SQLITE_OK) {
+ /* try to create the dir */
+ ret = mkdir_if_not_exist(topdir);
+ if (ret)
+ goto out_close;
+
+ /* retry open */
+ ret = sqlite3_open(buf, &dbh);
+ if (ret != SQLITE_OK)
+ goto out_close;
}
- /* process SELECT result */
- ret = sqlite3_column_int(stmt, 0);
- if (ret != CLTRACK_SQLITE_LATEST_SCHEMA_VERSION) {
+ /* set busy timeout */
+ ret = sqlite3_busy_timeout(dbh, CLTRACK_SQLITE_BUSY_TIMEOUT);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to set sqlite busy timeout: %s",
+ sqlite3_errstr(ret));
+ goto out_close;
+ }
+
+ ret = sqlite_query_schema_version();
+ switch (ret) {
+ case CLTRACK_SQLITE_LATEST_SCHEMA_VERSION:
+ /* DB is already set up. Do nothing */
+ ret = 0;
+ break;
+ case 0:
+ /* Query failed -- try to set up new DB */
+ ret = sqlite_maindb_init_v1();
+ if (ret)
+ goto out_close;
+ break;
+ default:
+ /* Unknown DB version -- downgrade? Fail */
xlog(L_ERROR, "Unsupported database schema version! "
"Expected %d, got %d.",
CLTRACK_SQLITE_LATEST_SCHEMA_VERSION, ret);
ret = -EINVAL;
- goto out_err;
+ goto out_close;
}
- /* now create the "clients" table */
- ret = sqlite3_exec(dbh, "CREATE TABLE IF NOT EXISTS clients "
- "(id BLOB PRIMARY KEY, time INTEGER);",
- NULL, NULL, &err);
- if (ret != SQLITE_OK) {
- xlog(L_ERROR, "Unable to create clients table: %s", err);
- goto out_err;
- }
-
- sqlite3_free(err);
- sqlite3_finalize(stmt);
- return 0;
-
-out_err:
- if (err) {
- xlog(L_ERROR, "sqlite error: %s", err);
- sqlite3_free(err);
- }
- sqlite3_finalize(stmt);
- sqlite3_close(dbh);
+ return ret;
+out_close:
+ sqlite3_close_v2(dbh);
+ dbh = NULL;
return ret;
}
diff --git a/utils/nfsdcltrack/sqlite.h b/utils/nfsdcltrack/sqlite.h
index ebf04c3..3713bfb 100644
--- a/utils/nfsdcltrack/sqlite.h
+++ b/utils/nfsdcltrack/sqlite.h
@@ -21,7 +21,6 @@
#define _SQLITE_H_
int sqlite_prepare_dbh(const char *topdir);
-int sqlite_maindb_init(const char *topdir);
int sqlite_insert_client(const unsigned char *clname, const size_t namelen);
int sqlite_remove_client(const unsigned char *clname, const size_t namelen);
int sqlite_check_client(const unsigned char *clname, const size_t namelen);