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