]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/nfsdcltrack/sqlite.c
nfsdcltrack: remove pointless sqlite_topdir variable
[nfs-utils.git] / utils / nfsdcltrack / sqlite.c
1 /*
2  * Copyright (C) 2011  Red Hat, Jeff Layton <jlayton@redhat.com>
3  *
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.
8  *
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.
13  *
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.
18  */
19
20 /*
21  * Explanation:
22  *
23  * This file contains the code to manage the sqlite backend database for the
24  * clstated upcall daemon.
25  *
26  * The main database is called main.sqlite and contains the following tables:
27  *
28  * parameters: simple key/value pairs for storing database info
29  *
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
32  *          established
33  *
34  * FIXME: should we also record the fsid being accessed?
35  */
36
37 #ifdef HAVE_CONFIG_H
38 #include "config.h"
39 #endif /* HAVE_CONFIG_H */
40
41 #include <dirent.h>
42 #include <errno.h>
43 #include <event.h>
44 #include <stdbool.h>
45 #include <string.h>
46 #include <sys/stat.h>
47 #include <sys/types.h>
48 #include <fcntl.h>
49 #include <unistd.h>
50 #include <sqlite3.h>
51 #include <linux/limits.h>
52
53 #include "xlog.h"
54
55 #define CLD_SQLITE_SCHEMA_VERSION 1
56
57 /* in milliseconds */
58 #define CLD_SQLITE_BUSY_TIMEOUT 10000
59
60 /* private data structures */
61
62 /* global variables */
63
64 /* reusable pathname and sql command buffer */
65 static char buf[PATH_MAX];
66
67 /* global database handle */
68 static sqlite3 *dbh;
69
70 /* forward declarations */
71
72 /* make a directory, ignoring EEXIST errors unless it's not a directory */
73 static int
74 mkdir_if_not_exist(const char *dirname)
75 {
76         int ret;
77         struct stat statbuf;
78
79         ret = mkdir(dirname, S_IRWXU);
80         if (ret && errno != EEXIST)
81                 return -errno;
82
83         ret = stat(dirname, &statbuf);
84         if (ret)
85                 return -errno;
86
87         if (!S_ISDIR(statbuf.st_mode))
88                 ret = -ENOTDIR;
89
90         return ret;
91 }
92
93 /*
94  * Open the "main" database, and attempt to initialize it by creating the
95  * parameters table and inserting the schema version into it. Ignore any errors
96  * from that, and then attempt to select the version out of it again. If the
97  * version appears wrong, then assume that the DB is corrupt or has been
98  * upgraded, and return an error. If all of that works, then attempt to create
99  * the "clients" table.
100  */
101 int
102 sqlite_maindb_init(const char *topdir)
103 {
104         int ret;
105         char *err = NULL;
106         sqlite3_stmt *stmt = NULL;
107
108         ret = mkdir_if_not_exist(topdir);
109         if (ret)
110                 return ret;
111
112         ret = snprintf(buf, PATH_MAX - 1, "%s/main.sqlite", topdir);
113         if (ret < 0)
114                 return ret;
115
116         buf[PATH_MAX - 1] = '\0';
117
118         ret = sqlite3_open(buf, &dbh);
119         if (ret != SQLITE_OK) {
120                 xlog(L_ERROR, "Unable to open main database: %d", ret);
121                 return ret;
122         }
123
124         ret = sqlite3_busy_timeout(dbh, CLD_SQLITE_BUSY_TIMEOUT);
125         if (ret != SQLITE_OK) {
126                 xlog(L_ERROR, "Unable to set sqlite busy timeout: %d", ret);
127                 goto out_err;
128         }
129
130         /* Try to create table */
131         ret = sqlite3_exec(dbh, "CREATE TABLE IF NOT EXISTS parameters "
132                                 "(key TEXT PRIMARY KEY, value TEXT);",
133                                 NULL, NULL, &err);
134         if (ret != SQLITE_OK) {
135                 xlog(L_ERROR, "Unable to create parameter table: %d", ret);
136                 goto out_err;
137         }
138
139         /* insert version into table -- ignore error if it fails */
140         ret = snprintf(buf, sizeof(buf),
141                        "INSERT OR IGNORE INTO parameters values (\"version\", "
142                        "\"%d\");", CLD_SQLITE_SCHEMA_VERSION);
143         if (ret < 0) {
144                 goto out_err;
145         } else if ((size_t)ret >= sizeof(buf)) {
146                 ret = -EINVAL;
147                 goto out_err;
148         }
149
150         ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err);
151         if (ret != SQLITE_OK) {
152                 xlog(L_ERROR, "Unable to insert into parameter table: %d",
153                                 ret);
154                 goto out_err;
155         }
156
157         ret = sqlite3_prepare_v2(dbh,
158                 "SELECT value FROM parameters WHERE key == \"version\";",
159                  -1, &stmt, NULL);
160         if (ret != SQLITE_OK) {
161                 xlog(L_ERROR, "Unable to prepare select statement: %d", ret);
162                 goto out_err;
163         }
164
165         /* check schema version */
166         ret = sqlite3_step(stmt);
167         if (ret != SQLITE_ROW) {
168                 xlog(L_ERROR, "Select statement execution failed: %s",
169                                 sqlite3_errmsg(dbh));
170                 goto out_err;
171         }
172
173         /* process SELECT result */
174         ret = sqlite3_column_int(stmt, 0);
175         if (ret != CLD_SQLITE_SCHEMA_VERSION) {
176                 xlog(L_ERROR, "Unsupported database schema version! "
177                         "Expected %d, got %d.",
178                         CLD_SQLITE_SCHEMA_VERSION, ret);
179                 ret = -EINVAL;
180                 goto out_err;
181         }
182
183         /* now create the "clients" table */
184         ret = sqlite3_exec(dbh, "CREATE TABLE IF NOT EXISTS clients "
185                                 "(id BLOB PRIMARY KEY, time INTEGER);",
186                                 NULL, NULL, &err);
187         if (ret != SQLITE_OK) {
188                 xlog(L_ERROR, "Unable to create clients table: %s", err);
189                 goto out_err;
190         }
191
192         sqlite3_free(err);
193         sqlite3_finalize(stmt);
194         return 0;
195
196 out_err:
197         if (err) {
198                 xlog(L_ERROR, "sqlite error: %s", err);
199                 sqlite3_free(err);
200         }
201         sqlite3_finalize(stmt);
202         sqlite3_close(dbh);
203         return ret;
204 }
205
206 /*
207  * Create a client record
208  *
209  * Returns a non-zero sqlite error code, or SQLITE_OK (aka 0)
210  */
211 int
212 sqlite_insert_client(const unsigned char *clname, const size_t namelen)
213 {
214         int ret;
215         sqlite3_stmt *stmt = NULL;
216
217         ret = sqlite3_prepare_v2(dbh, "INSERT OR REPLACE INTO clients VALUES "
218                                       "(?, strftime('%s', 'now'));", -1,
219                                         &stmt, NULL);
220         if (ret != SQLITE_OK) {
221                 xlog(L_ERROR, "%s: insert statement prepare failed: %s",
222                         __func__, sqlite3_errmsg(dbh));
223                 return ret;
224         }
225
226         ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
227                                 SQLITE_STATIC);
228         if (ret != SQLITE_OK) {
229                 xlog(L_ERROR, "%s: bind blob failed: %s", __func__,
230                                 sqlite3_errmsg(dbh));
231                 goto out_err;
232         }
233
234         ret = sqlite3_step(stmt);
235         if (ret == SQLITE_DONE)
236                 ret = SQLITE_OK;
237         else
238                 xlog(L_ERROR, "%s: unexpected return code from insert: %s",
239                                 __func__, sqlite3_errmsg(dbh));
240
241 out_err:
242         xlog(D_GENERAL, "%s: returning %d", __func__, ret);
243         sqlite3_finalize(stmt);
244         return ret;
245 }
246
247 /* Remove a client record */
248 int
249 sqlite_remove_client(const unsigned char *clname, const size_t namelen)
250 {
251         int ret;
252         sqlite3_stmt *stmt = NULL;
253
254         ret = sqlite3_prepare_v2(dbh, "DELETE FROM clients WHERE id==?", -1,
255                                  &stmt, NULL);
256         if (ret != SQLITE_OK) {
257                 xlog(L_ERROR, "%s: statement prepare failed: %s",
258                                 __func__, sqlite3_errmsg(dbh));
259                 goto out_err;
260         }
261
262         ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
263                                 SQLITE_STATIC);
264         if (ret != SQLITE_OK) {
265                 xlog(L_ERROR, "%s: bind blob failed: %s", __func__,
266                                 sqlite3_errmsg(dbh));
267                 goto out_err;
268         }
269
270         ret = sqlite3_step(stmt);
271         if (ret == SQLITE_DONE)
272                 ret = SQLITE_OK;
273         else
274                 xlog(L_ERROR, "%s: unexpected return code from delete: %d",
275                                 __func__, ret);
276
277 out_err:
278         xlog(D_GENERAL, "%s: returning %d", __func__, ret);
279         sqlite3_finalize(stmt);
280         return ret;
281 }
282
283 /*
284  * Is the given clname in the clients table? If so, then update its timestamp
285  * and return success. If the record isn't present, or the update fails, then
286  * return an error.
287  */
288 int
289 sqlite_check_client(const unsigned char *clname, const size_t namelen)
290 {
291         int ret;
292         sqlite3_stmt *stmt = NULL;
293
294         ret = sqlite3_prepare_v2(dbh, "SELECT count(*) FROM clients WHERE "
295                                       "id==?", -1, &stmt, NULL);
296         if (ret != SQLITE_OK) {
297                 xlog(L_ERROR, "%s: unable to prepare update statement: %s",
298                                 __func__, sqlite3_errmsg(dbh));
299                 goto out_err;
300         }
301
302         ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
303                                 SQLITE_STATIC);
304         if (ret != SQLITE_OK) {
305                 xlog(L_ERROR, "%s: bind blob failed: %s",
306                                 __func__, sqlite3_errmsg(dbh));
307                 goto out_err;
308         }
309
310         ret = sqlite3_step(stmt);
311         if (ret != SQLITE_ROW) {
312                 xlog(L_ERROR, "%s: unexpected return code from select: %d",
313                                 __func__, ret);
314                 goto out_err;
315         }
316
317         ret = sqlite3_column_int(stmt, 0);
318         xlog(D_GENERAL, "%s: select returned %d rows", __func__, ret);
319         if (ret != 1) {
320                 ret = -EACCES;
321                 goto out_err;
322         }
323
324         sqlite3_finalize(stmt);
325         stmt = NULL;
326         ret = sqlite3_prepare_v2(dbh, "UPDATE OR FAIL clients SET "
327                                       "time=strftime('%s', 'now') WHERE id==?",
328                                  -1, &stmt, NULL);
329         if (ret != SQLITE_OK) {
330                 xlog(L_ERROR, "%s: unable to prepare update statement: %s",
331                                 __func__, sqlite3_errmsg(dbh));
332                 goto out_err;
333         }
334
335         ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
336                                 SQLITE_STATIC);
337         if (ret != SQLITE_OK) {
338                 xlog(L_ERROR, "%s: bind blob failed: %s",
339                                 __func__, sqlite3_errmsg(dbh));
340                 goto out_err;
341         }
342
343         ret = sqlite3_step(stmt);
344         if (ret == SQLITE_DONE)
345                 ret = SQLITE_OK;
346         else
347                 xlog(L_ERROR, "%s: unexpected return code from update: %s",
348                                 __func__, sqlite3_errmsg(dbh));
349
350 out_err:
351         xlog(D_GENERAL, "%s: returning %d", __func__, ret);
352         sqlite3_finalize(stmt);
353         return ret;
354 }
355
356 /*
357  * remove any client records that were not reclaimed since grace_start.
358  */
359 int
360 sqlite_remove_unreclaimed(time_t grace_start)
361 {
362         int ret;
363         char *err = NULL;
364
365         ret = snprintf(buf, sizeof(buf), "DELETE FROM clients WHERE time < %ld",
366                         grace_start);
367         if (ret < 0) {
368                 return ret;
369         } else if ((size_t)ret >= sizeof(buf)) {
370                 ret = -EINVAL;
371                 return ret;
372         }
373
374         ret = sqlite3_exec(dbh, buf, NULL, NULL, &err);
375         if (ret != SQLITE_OK)
376                 xlog(L_ERROR, "%s: delete failed: %s", __func__, err);
377
378         xlog(D_GENERAL, "%s: returning %d", __func__, ret);
379         sqlite3_free(err);
380         return ret;
381 }