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 */
46 #include <sys/types.h>
50 #include <linux/limits.h>
54 #define CLD_SQLITE_SCHEMA_VERSION 1
56 #ifndef CLD_SQLITE_TOPDIR
57 #define CLD_SQLITE_TOPDIR NFS_STATEDIR "/nfsdcld"
61 #define CLD_SQLITE_BUSY_TIMEOUT 10000
63 /* private data structures */
65 /* global variables */
67 /* top level DB directory */
68 static char *sqlite_topdir;
70 /* reusable pathname and sql command buffer */
71 static char buf[PATH_MAX];
73 /* global database handle */
76 /* forward declarations */
78 /* make a directory, ignoring EEXIST errors unless it's not a directory */
80 mkdir_if_not_exist(char *dirname)
85 ret = mkdir(dirname, S_IRWXU);
86 if (ret && errno != EEXIST)
89 ret = stat(dirname, &statbuf);
93 if (!S_ISDIR(statbuf.st_mode))
100 * Open the "main" database, and attempt to initialize it by creating the
101 * parameters table and inserting the schema version into it. Ignore any errors
102 * from that, and then attempt to select the version out of it again. If the
103 * version appears wrong, then assume that the DB is corrupt or has been
104 * upgraded, and return an error. If all of that works, then attempt to create
105 * the "clients" table.
108 sqlite_maindb_init(char *topdir)
112 sqlite3_stmt *stmt = NULL;
114 sqlite_topdir = topdir ? topdir : CLD_SQLITE_TOPDIR;
116 ret = mkdir_if_not_exist(sqlite_topdir);
120 ret = snprintf(buf, PATH_MAX - 1, "%s/main.sqlite", sqlite_topdir);
124 buf[PATH_MAX - 1] = '\0';
126 ret = sqlite3_open(buf, &dbh);
127 if (ret != SQLITE_OK) {
128 xlog(L_ERROR, "Unable to open main database: %d", ret);
132 ret = sqlite3_busy_timeout(dbh, CLD_SQLITE_BUSY_TIMEOUT);
133 if (ret != SQLITE_OK) {
134 xlog(L_ERROR, "Unable to set sqlite busy timeout: %d", ret);
138 /* Try to create table */
139 ret = sqlite3_exec(dbh, "CREATE TABLE IF NOT EXISTS parameters "
140 "(key TEXT PRIMARY KEY, value TEXT);",
142 if (ret != SQLITE_OK) {
143 xlog(L_ERROR, "Unable to create parameter table: %d", ret);
147 /* insert version into table -- ignore error if it fails */
148 ret = snprintf(buf, sizeof(buf),
149 "INSERT OR IGNORE INTO parameters values (\"version\", "
150 "\"%d\");", CLD_SQLITE_SCHEMA_VERSION);
153 } else if ((size_t)ret >= sizeof(buf)) {
158 ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err);
159 if (ret != SQLITE_OK) {
160 xlog(L_ERROR, "Unable to insert into parameter table: %d",
165 ret = sqlite3_prepare_v2(dbh,
166 "SELECT value FROM parameters WHERE key == \"version\";",
168 if (ret != SQLITE_OK) {
169 xlog(L_ERROR, "Unable to prepare select statement: %d", ret);
173 /* check schema version */
174 ret = sqlite3_step(stmt);
175 if (ret != SQLITE_ROW) {
176 xlog(L_ERROR, "Select statement execution failed: %s",
177 sqlite3_errmsg(dbh));
181 /* process SELECT result */
182 ret = sqlite3_column_int(stmt, 0);
183 if (ret != CLD_SQLITE_SCHEMA_VERSION) {
184 xlog(L_ERROR, "Unsupported database schema version! "
185 "Expected %d, got %d.",
186 CLD_SQLITE_SCHEMA_VERSION, ret);
191 /* now create the "clients" table */
192 ret = sqlite3_exec(dbh, "CREATE TABLE IF NOT EXISTS clients "
193 "(id BLOB PRIMARY KEY, time INTEGER);",
195 if (ret != SQLITE_OK) {
196 xlog(L_ERROR, "Unable to create clients table: %s", err);
201 sqlite3_finalize(stmt);
206 xlog(L_ERROR, "sqlite error: %s", err);
209 sqlite3_finalize(stmt);
215 * Create a client record
217 * Returns a non-zero sqlite error code, or SQLITE_OK (aka 0)
220 sqlite_insert_client(const unsigned char *clname, const size_t namelen)
223 sqlite3_stmt *stmt = NULL;
225 ret = sqlite3_prepare_v2(dbh, "INSERT OR REPLACE INTO clients VALUES "
226 "(?, strftime('%s', 'now'));", -1,
228 if (ret != SQLITE_OK) {
229 xlog(L_ERROR, "%s: insert statement prepare failed: %s",
230 __func__, sqlite3_errmsg(dbh));
234 ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
236 if (ret != SQLITE_OK) {
237 xlog(L_ERROR, "%s: bind blob failed: %s", __func__,
238 sqlite3_errmsg(dbh));
242 ret = sqlite3_step(stmt);
243 if (ret == SQLITE_DONE)
246 xlog(L_ERROR, "%s: unexpected return code from insert: %s",
247 __func__, sqlite3_errmsg(dbh));
250 xlog(D_GENERAL, "%s: returning %d", __func__, ret);
251 sqlite3_finalize(stmt);
255 /* Remove a client record */
257 sqlite_remove_client(const unsigned char *clname, const size_t namelen)
260 sqlite3_stmt *stmt = NULL;
262 ret = sqlite3_prepare_v2(dbh, "DELETE FROM clients WHERE id==?", -1,
264 if (ret != SQLITE_OK) {
265 xlog(L_ERROR, "%s: statement prepare failed: %s",
266 __func__, sqlite3_errmsg(dbh));
270 ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
272 if (ret != SQLITE_OK) {
273 xlog(L_ERROR, "%s: bind blob failed: %s", __func__,
274 sqlite3_errmsg(dbh));
278 ret = sqlite3_step(stmt);
279 if (ret == SQLITE_DONE)
282 xlog(L_ERROR, "%s: unexpected return code from delete: %d",
286 xlog(D_GENERAL, "%s: returning %d", __func__, ret);
287 sqlite3_finalize(stmt);
292 * Is the given clname in the clients table? If so, then update its timestamp
293 * and return success. If the record isn't present, or the update fails, then
297 sqlite_check_client(const unsigned char *clname, const size_t namelen)
300 sqlite3_stmt *stmt = NULL;
302 ret = sqlite3_prepare_v2(dbh, "SELECT count(*) FROM clients WHERE "
303 "id==?", -1, &stmt, NULL);
304 if (ret != SQLITE_OK) {
305 xlog(L_ERROR, "%s: unable to prepare update statement: %s",
306 __func__, sqlite3_errmsg(dbh));
310 ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
312 if (ret != SQLITE_OK) {
313 xlog(L_ERROR, "%s: bind blob failed: %s",
314 __func__, sqlite3_errmsg(dbh));
318 ret = sqlite3_step(stmt);
319 if (ret != SQLITE_ROW) {
320 xlog(L_ERROR, "%s: unexpected return code from select: %d",
325 ret = sqlite3_column_int(stmt, 0);
326 xlog(D_GENERAL, "%s: select returned %d rows", ret);
332 sqlite3_finalize(stmt);
334 ret = sqlite3_prepare_v2(dbh, "UPDATE OR FAIL clients SET "
335 "time=strftime('%s', 'now') WHERE id==?",
337 if (ret != SQLITE_OK) {
338 xlog(L_ERROR, "%s: unable to prepare update statement: %s",
339 __func__, sqlite3_errmsg(dbh));
343 ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
345 if (ret != SQLITE_OK) {
346 xlog(L_ERROR, "%s: bind blob failed: %s",
347 __func__, sqlite3_errmsg(dbh));
351 ret = sqlite3_step(stmt);
352 if (ret == SQLITE_DONE)
355 xlog(L_ERROR, "%s: unexpected return code from update: %s",
356 __func__, sqlite3_errmsg(dbh));
359 xlog(D_GENERAL, "%s: returning %d", __func__, ret);
360 sqlite3_finalize(stmt);