2 * Copyright (C) 2011 Red Hat, Jeff Layton <jlayton@redhat.com>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
23 * This file contains the code to manage the sqlite backend database for the
24 * clstated upcall daemon.
26 * The main database is called main.sqlite and contains the following tables:
28 * parameters: simple key/value pairs for storing database info
30 * clients: one column containing a BLOB with the as sent by the client
31 * and a timestamp (in epoch seconds) of when the record was
34 * FIXME: should we also record the fsid being accessed?
39 #endif /* HAVE_CONFIG_H */
47 #include <sys/types.h>
51 #include <linux/limits.h>
55 #define CLD_SQLITE_SCHEMA_VERSION 1
57 #ifndef CLD_SQLITE_TOPDIR
58 #define CLD_SQLITE_TOPDIR NFS_STATEDIR "/nfsdcld"
62 #define CLD_SQLITE_BUSY_TIMEOUT 10000
64 /* private data structures */
66 /* global variables */
68 /* top level DB directory */
69 static char *sqlite_topdir;
71 /* reusable pathname and sql command buffer */
72 static char buf[PATH_MAX];
74 /* global database handle */
77 /* forward declarations */
79 /* make a directory, ignoring EEXIST errors unless it's not a directory */
81 mkdir_if_not_exist(char *dirname)
86 ret = mkdir(dirname, S_IRWXU);
87 if (ret && errno != EEXIST)
90 ret = stat(dirname, &statbuf);
94 if (!S_ISDIR(statbuf.st_mode))
101 * Open the "main" database, and attempt to initialize it by creating the
102 * parameters table and inserting the schema version into it. Ignore any errors
103 * from that, and then attempt to select the version out of it again. If the
104 * version appears wrong, then assume that the DB is corrupt or has been
105 * upgraded, and return an error. If all of that works, then attempt to create
106 * the "clients" table.
109 sqlite_maindb_init(char *topdir)
113 sqlite3_stmt *stmt = NULL;
115 sqlite_topdir = topdir ? topdir : CLD_SQLITE_TOPDIR;
117 ret = mkdir_if_not_exist(sqlite_topdir);
121 ret = snprintf(buf, PATH_MAX - 1, "%s/main.sqlite", sqlite_topdir);
125 buf[PATH_MAX - 1] = '\0';
127 ret = sqlite3_open(buf, &dbh);
128 if (ret != SQLITE_OK) {
129 xlog(L_ERROR, "Unable to open main database: %d", ret);
133 ret = sqlite3_busy_timeout(dbh, CLD_SQLITE_BUSY_TIMEOUT);
134 if (ret != SQLITE_OK) {
135 xlog(L_ERROR, "Unable to set sqlite busy timeout: %d", ret);
139 /* Try to create table */
140 ret = sqlite3_exec(dbh, "CREATE TABLE IF NOT EXISTS parameters "
141 "(key TEXT PRIMARY KEY, value TEXT);",
143 if (ret != SQLITE_OK) {
144 xlog(L_ERROR, "Unable to create parameter table: %d", ret);
148 /* insert version into table -- ignore error if it fails */
149 ret = snprintf(buf, sizeof(buf),
150 "INSERT OR IGNORE INTO parameters values (\"version\", "
151 "\"%d\");", CLD_SQLITE_SCHEMA_VERSION);
154 } else if ((size_t)ret >= sizeof(buf)) {
159 ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err);
160 if (ret != SQLITE_OK) {
161 xlog(L_ERROR, "Unable to insert into parameter table: %d",
166 ret = sqlite3_prepare_v2(dbh,
167 "SELECT value FROM parameters WHERE key == \"version\";",
169 if (ret != SQLITE_OK) {
170 xlog(L_ERROR, "Unable to prepare select statement: %d", ret);
174 /* check schema version */
175 ret = sqlite3_step(stmt);
176 if (ret != SQLITE_ROW) {
177 xlog(L_ERROR, "Select statement execution failed: %s",
178 sqlite3_errmsg(dbh));
182 /* process SELECT result */
183 ret = sqlite3_column_int(stmt, 0);
184 if (ret != CLD_SQLITE_SCHEMA_VERSION) {
185 xlog(L_ERROR, "Unsupported database schema version! "
186 "Expected %d, got %d.",
187 CLD_SQLITE_SCHEMA_VERSION, ret);
192 /* now create the "clients" table */
193 ret = sqlite3_exec(dbh, "CREATE TABLE IF NOT EXISTS clients "
194 "(id BLOB PRIMARY KEY, time INTEGER);",
196 if (ret != SQLITE_OK) {
197 xlog(L_ERROR, "Unable to create clients table: %s", err);
202 sqlite3_finalize(stmt);
207 xlog(L_ERROR, "sqlite error: %s", err);
210 sqlite3_finalize(stmt);
216 * Create a client record
218 * Returns a non-zero sqlite error code, or SQLITE_OK (aka 0)
221 sqlite_insert_client(const unsigned char *clname, const size_t namelen)
224 sqlite3_stmt *stmt = NULL;
226 ret = sqlite3_prepare_v2(dbh, "INSERT OR REPLACE INTO clients VALUES "
227 "(?, strftime('%s', 'now'));", -1,
229 if (ret != SQLITE_OK) {
230 xlog(L_ERROR, "%s: insert statement prepare failed: %s",
231 __func__, sqlite3_errmsg(dbh));
235 ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
237 if (ret != SQLITE_OK) {
238 xlog(L_ERROR, "%s: bind blob failed: %s", __func__,
239 sqlite3_errmsg(dbh));
243 ret = sqlite3_step(stmt);
244 if (ret == SQLITE_DONE)
247 xlog(L_ERROR, "%s: unexpected return code from insert: %s",
248 __func__, sqlite3_errmsg(dbh));
251 xlog(D_GENERAL, "%s: returning %d", __func__, ret);
252 sqlite3_finalize(stmt);
256 /* Remove a client record */
258 sqlite_remove_client(const unsigned char *clname, const size_t namelen)
261 sqlite3_stmt *stmt = NULL;
263 ret = sqlite3_prepare_v2(dbh, "DELETE FROM clients WHERE id==?", -1,
265 if (ret != SQLITE_OK) {
266 xlog(L_ERROR, "%s: statement prepare failed: %s",
267 __func__, sqlite3_errmsg(dbh));
271 ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
273 if (ret != SQLITE_OK) {
274 xlog(L_ERROR, "%s: bind blob failed: %s", __func__,
275 sqlite3_errmsg(dbh));
279 ret = sqlite3_step(stmt);
280 if (ret == SQLITE_DONE)
283 xlog(L_ERROR, "%s: unexpected return code from delete: %d",
287 xlog(D_GENERAL, "%s: returning %d", __func__, ret);
288 sqlite3_finalize(stmt);
293 * Is the given clname in the clients table? If so, then update its timestamp
294 * and return success. If the record isn't present, or the update fails, then
298 sqlite_check_client(const unsigned char *clname, const size_t namelen)
301 sqlite3_stmt *stmt = NULL;
303 ret = sqlite3_prepare_v2(dbh, "SELECT count(*) FROM clients WHERE "
304 "id==?", -1, &stmt, NULL);
305 if (ret != SQLITE_OK) {
306 xlog(L_ERROR, "%s: unable to prepare update statement: %s",
307 __func__, sqlite3_errmsg(dbh));
311 ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
313 if (ret != SQLITE_OK) {
314 xlog(L_ERROR, "%s: bind blob failed: %s",
315 __func__, sqlite3_errmsg(dbh));
319 ret = sqlite3_step(stmt);
320 if (ret != SQLITE_ROW) {
321 xlog(L_ERROR, "%s: unexpected return code from select: %d",
326 ret = sqlite3_column_int(stmt, 0);
327 xlog(D_GENERAL, "%s: select returned %d rows", ret);
333 sqlite3_finalize(stmt);
335 ret = sqlite3_prepare_v2(dbh, "UPDATE OR FAIL clients SET "
336 "time=strftime('%s', 'now') WHERE id==?",
338 if (ret != SQLITE_OK) {
339 xlog(L_ERROR, "%s: unable to prepare update statement: %s",
340 __func__, sqlite3_errmsg(dbh));
344 ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
346 if (ret != SQLITE_OK) {
347 xlog(L_ERROR, "%s: bind blob failed: %s",
348 __func__, sqlite3_errmsg(dbh));
352 ret = sqlite3_step(stmt);
353 if (ret == SQLITE_DONE)
356 xlog(L_ERROR, "%s: unexpected return code from update: %s",
357 __func__, sqlite3_errmsg(dbh));
360 xlog(D_GENERAL, "%s: returning %d", __func__, ret);
361 sqlite3_finalize(stmt);
366 * remove any client records that were not reclaimed since grace_start.
369 sqlite_remove_unreclaimed(time_t grace_start)
374 ret = snprintf(buf, sizeof(buf), "DELETE FROM clients WHERE time < %ld",
378 } else if ((size_t)ret >= sizeof(buf)) {
383 ret = sqlite3_exec(dbh, buf, NULL, NULL, &err);
384 if (ret != SQLITE_OK)
385 xlog(L_ERROR, "%s: delete failed: %s", __func__, err);
387 xlog(D_GENERAL, "%s: returning %d", __func__, ret);