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