Merge branch 'master' of gnunet.org:gnunet
[oweals/gnunet.git] / src / namecache / plugin_namecache_sqlite.c
1  /*
2   * This file is part of GNUnet
3   * Copyright (C) 2009-2013 GNUnet e.V.
4   *
5   * GNUnet is free software; you can redistribute it and/or modify
6   * it under the terms of the GNU General Public License as published
7   * by the Free Software Foundation; either version 3, or (at your
8   * option) any later version.
9   *
10   * GNUnet is distributed in the hope that it will be useful, but
11   * WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   * General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with GNUnet; see the file COPYING.  If not, write to the
17   * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18   * Boston, MA 02110-1301, USA.
19   */
20
21 /**
22  * @file namecache/plugin_namecache_sqlite.c
23  * @brief sqlite-based namecache backend
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_sq_lib.h"
28 #include "gnunet_namecache_plugin.h"
29 #include "gnunet_namecache_service.h"
30 #include "gnunet_gnsrecord_lib.h"
31 #include "namecache.h"
32 #include <sqlite3.h>
33
34 /**
35  * After how many ms "busy" should a DB operation fail for good?  A
36  * low value makes sure that we are more responsive to requests
37  * (especially PUTs).  A high value guarantees a higher success rate
38  * (SELECTs in iterate can take several seconds despite LIMIT=1).
39  *
40  * The default value of 1s should ensure that users do not experience
41  * huge latencies while at the same time allowing operations to
42  * succeed with reasonable probability.
43  */
44 #define BUSY_TIMEOUT_MS 1000
45
46
47 /**
48  * Log an error message at log-level 'level' that indicates
49  * a failure of the command 'cmd' on file 'filename'
50  * with the message given by strerror(errno).
51  */
52 #define LOG_SQLITE(db, level, cmd) do { GNUNET_log_from (level, "namecache-sqlite", _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, sqlite3_errmsg(db->dbh)); } while(0)
53
54 #define LOG(kind,...) GNUNET_log_from (kind, "namecache-sqlite", __VA_ARGS__)
55
56
57 /**
58  * Context for all functions in this plugin.
59  */
60 struct Plugin
61 {
62
63   const struct GNUNET_CONFIGURATION_Handle *cfg;
64
65   /**
66    * Database filename.
67    */
68   char *fn;
69
70   /**
71    * Native SQLite database handle.
72    */
73   sqlite3 *dbh;
74
75   /**
76    * Precompiled SQL for caching a block
77    */
78   sqlite3_stmt *cache_block;
79
80   /**
81    * Precompiled SQL for deleting an older block
82    */
83   sqlite3_stmt *delete_block;
84
85   /**
86    * Precompiled SQL for looking up a block
87    */
88   sqlite3_stmt *lookup_block;
89
90   /**
91    * Precompiled SQL for removing expired blocks
92    */
93   sqlite3_stmt *expire_blocks;
94
95
96 };
97
98
99 /**
100  * @brief Prepare a SQL statement
101  *
102  * @param dbh handle to the database
103  * @param zSql SQL statement, UTF-8 encoded
104  * @param ppStmt set to the prepared statement
105  * @return 0 on success
106  */
107 static int
108 sq_prepare (sqlite3 *dbh,
109             const char *zSql,
110             sqlite3_stmt **ppStmt)
111 {
112   char *dummy;
113   int result;
114
115   result = sqlite3_prepare_v2 (dbh,
116                                zSql,
117                                strlen (zSql),
118                                ppStmt,
119                                (const char **) &dummy);
120   LOG (GNUNET_ERROR_TYPE_DEBUG,
121        "Prepared `%s' / %p: %d\n",
122        zSql,
123        *ppStmt,
124        result);
125   return result;
126 }
127
128
129 /**
130  * Create our database indices.
131  *
132  * @param dbh handle to the database
133  */
134 static void
135 create_indices (sqlite3 * dbh)
136 {
137   /* create indices */
138   if ( (SQLITE_OK !=
139         sqlite3_exec (dbh,
140                       "CREATE INDEX IF NOT EXISTS ir_query_hash ON ns096blocks (query,expiration_time)",
141                       NULL, NULL, NULL)) ||
142        (SQLITE_OK !=
143         sqlite3_exec (dbh,
144                       "CREATE INDEX IF NOT EXISTS ir_block_expiration ON ns096blocks (expiration_time)",
145                       NULL, NULL, NULL)) )
146     LOG (GNUNET_ERROR_TYPE_ERROR,
147          "Failed to create indices: %s\n",
148          sqlite3_errmsg (dbh));
149 }
150
151
152 #if 0
153 #define CHECK(a) GNUNET_break(a)
154 #define ENULL NULL
155 #else
156 #define ENULL &e
157 #define ENULL_DEFINED 1
158 #define CHECK(a) if (! (a)) { GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "%s\n", e); sqlite3_free(e); }
159 #endif
160
161
162 /**
163  * Initialize the database connections and associated
164  * data structures (create tables and indices
165  * as needed as well).
166  *
167  * @param plugin the plugin context (state for this module)
168  * @return #GNUNET_OK on success
169  */
170 static int
171 database_setup (struct Plugin *plugin)
172 {
173   sqlite3_stmt *stmt;
174   char *afsdir;
175 #if ENULL_DEFINED
176   char *e;
177 #endif
178
179   if (GNUNET_OK !=
180       GNUNET_CONFIGURATION_get_value_filename (plugin->cfg,
181                                                "namecache-sqlite",
182                                                "FILENAME",
183                                                &afsdir))
184   {
185     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
186                                "namecache-sqlite",
187                                "FILENAME");
188     return GNUNET_SYSERR;
189   }
190   if (GNUNET_OK !=
191       GNUNET_DISK_file_test (afsdir))
192   {
193     if (GNUNET_OK !=
194         GNUNET_DISK_directory_create_for_file (afsdir))
195     {
196       GNUNET_break (0);
197       GNUNET_free (afsdir);
198       return GNUNET_SYSERR;
199     }
200   }
201   /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
202   plugin->fn = afsdir;
203
204   /* Open database and precompile statements */
205   if (SQLITE_OK !=
206       sqlite3_open (plugin->fn, &plugin->dbh))
207   {
208     LOG (GNUNET_ERROR_TYPE_ERROR,
209          _("Unable to initialize SQLite: %s.\n"),
210          sqlite3_errmsg (plugin->dbh));
211     return GNUNET_SYSERR;
212   }
213   CHECK (SQLITE_OK ==
214          sqlite3_exec (plugin->dbh,
215                        "PRAGMA temp_store=MEMORY",
216                        NULL, NULL,
217                        ENULL));
218   CHECK (SQLITE_OK ==
219          sqlite3_exec (plugin->dbh,
220                        "PRAGMA synchronous=NORMAL",
221                        NULL, NULL,
222                        ENULL));
223   CHECK (SQLITE_OK ==
224          sqlite3_exec (plugin->dbh,
225                        "PRAGMA legacy_file_format=OFF",
226                        NULL, NULL,
227                        ENULL));
228   CHECK (SQLITE_OK ==
229          sqlite3_exec (plugin->dbh,
230                        "PRAGMA auto_vacuum=INCREMENTAL",
231                        NULL, NULL,
232                        ENULL));
233   CHECK (SQLITE_OK ==
234          sqlite3_exec (plugin->dbh,
235                        "PRAGMA encoding=\"UTF-8\"",
236                        NULL, NULL,
237                        ENULL));
238   CHECK (SQLITE_OK ==
239          sqlite3_exec (plugin->dbh,
240                        "PRAGMA locking_mode=EXCLUSIVE",
241                        NULL, NULL,
242                        ENULL));
243   CHECK (SQLITE_OK ==
244          sqlite3_exec (plugin->dbh,
245                        "PRAGMA page_size=4092",
246                        NULL, NULL,
247                        ENULL));
248
249   CHECK (SQLITE_OK ==
250          sqlite3_busy_timeout (plugin->dbh,
251                                BUSY_TIMEOUT_MS));
252
253
254   /* Create tables */
255   CHECK (SQLITE_OK ==
256          sq_prepare (plugin->dbh,
257                      "SELECT 1 FROM sqlite_master WHERE tbl_name = 'ns096blocks'",
258                      &stmt));
259   if ( (sqlite3_step (stmt) == SQLITE_DONE) &&
260        (SQLITE_OK !=
261         sqlite3_exec (plugin->dbh,
262                       "CREATE TABLE ns096blocks ("
263                       " query BLOB NOT NULL,"
264                       " block BLOB NOT NULL,"
265                       " expiration_time INT8 NOT NULL"
266                       ")",
267                       NULL, NULL, NULL)) )
268   {
269     LOG_SQLITE (plugin,
270                 GNUNET_ERROR_TYPE_ERROR,
271                 "sqlite3_exec");
272     sqlite3_finalize (stmt);
273     return GNUNET_SYSERR;
274   }
275   sqlite3_finalize (stmt);
276   create_indices (plugin->dbh);
277
278   if ( (SQLITE_OK !=
279         sq_prepare (plugin->dbh,
280                     "INSERT INTO ns096blocks (query,block,expiration_time) VALUES (?, ?, ?)",
281                     &plugin->cache_block)) ||
282        (SQLITE_OK !=
283         sq_prepare (plugin->dbh,
284                     "DELETE FROM ns096blocks WHERE expiration_time<?",
285                     &plugin->expire_blocks)) ||
286        (SQLITE_OK !=
287         sq_prepare (plugin->dbh,
288                     "DELETE FROM ns096blocks WHERE query=? AND expiration_time<=?",
289                     &plugin->delete_block)) ||
290        (SQLITE_OK !=
291         sq_prepare (plugin->dbh,
292                     "SELECT block FROM ns096blocks WHERE query=? "
293                     "ORDER BY expiration_time DESC LIMIT 1",
294                     &plugin->lookup_block) )
295       )
296   {
297     LOG_SQLITE (plugin,
298                 GNUNET_ERROR_TYPE_ERROR,
299                 "precompiling");
300     return GNUNET_SYSERR;
301   }
302   return GNUNET_OK;
303 }
304
305
306 /**
307  * Shutdown database connection and associate data
308  * structures.
309  * @param plugin the plugin context (state for this module)
310  */
311 static void
312 database_shutdown (struct Plugin *plugin)
313 {
314   int result;
315   sqlite3_stmt *stmt;
316
317   if (NULL != plugin->cache_block)
318     sqlite3_finalize (plugin->cache_block);
319   if (NULL != plugin->lookup_block)
320     sqlite3_finalize (plugin->lookup_block);
321   if (NULL != plugin->expire_blocks)
322     sqlite3_finalize (plugin->expire_blocks);
323   if (NULL != plugin->delete_block)
324     sqlite3_finalize (plugin->delete_block);
325   result = sqlite3_close (plugin->dbh);
326   if (result == SQLITE_BUSY)
327   {
328     LOG (GNUNET_ERROR_TYPE_WARNING,
329          _("Tried to close sqlite without finalizing all prepared statements.\n"));
330     stmt = sqlite3_next_stmt (plugin->dbh, NULL);
331     while (stmt != NULL)
332     {
333       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "sqlite",
334                        "Closing statement %p\n", stmt);
335       result = sqlite3_finalize (stmt);
336       if (result != SQLITE_OK)
337         GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "sqlite",
338                          "Failed to close statement %p: %d\n", stmt, result);
339       stmt = sqlite3_next_stmt (plugin->dbh, NULL);
340     }
341     result = sqlite3_close (plugin->dbh);
342   }
343   if (SQLITE_OK != result)
344     LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR, "sqlite3_close");
345
346   GNUNET_free_non_null (plugin->fn);
347 }
348
349
350 /**
351  * Removes any expired block.
352  *
353  * @param plugin the plugin
354  */
355 static void
356 namecache_sqlite_expire_blocks (struct Plugin *plugin)
357 {
358   struct GNUNET_TIME_Absolute now;
359   struct GNUNET_SQ_QueryParam params[] = {
360     GNUNET_SQ_query_param_absolute_time (&now),
361     GNUNET_SQ_query_param_end
362   };
363   int n;
364
365   now = GNUNET_TIME_absolute_get ();
366   if (GNUNET_OK !=
367       GNUNET_SQ_bind (plugin->expire_blocks,
368                       params))
369   {
370     LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
371                 "sqlite3_bind_XXXX");
372     GNUNET_SQ_reset (plugin->dbh,
373                      plugin->expire_blocks);
374     return;
375   }
376   n = sqlite3_step (plugin->expire_blocks);
377   GNUNET_SQ_reset (plugin->dbh,
378                    plugin->expire_blocks);
379   switch (n)
380   {
381   case SQLITE_DONE:
382     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
383                      "sqlite",
384                      "Records expired\n");
385     return;
386   case SQLITE_BUSY:
387     LOG_SQLITE (plugin,
388                 GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
389                 "sqlite3_step");
390     return;
391   default:
392     LOG_SQLITE (plugin,
393                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
394                 "sqlite3_step");
395     return;
396   }
397 }
398
399
400 /**
401  * Cache a block in the datastore.
402  *
403  * @param cls closure (internal context for the plugin)
404  * @param block block to cache
405  * @return #GNUNET_OK on success, else #GNUNET_SYSERR
406  */
407 static int
408 namecache_sqlite_cache_block (void *cls,
409                               const struct GNUNET_GNSRECORD_Block *block)
410 {
411   struct Plugin *plugin = cls;
412   struct GNUNET_HashCode query;
413   struct GNUNET_TIME_Absolute expiration;
414   size_t block_size = ntohl (block->purpose.size) +
415     sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
416     sizeof (struct GNUNET_CRYPTO_EcdsaSignature);
417   struct GNUNET_SQ_QueryParam del_params[] = {
418     GNUNET_SQ_query_param_auto_from_type (&query),
419     GNUNET_SQ_query_param_absolute_time (&expiration),
420     GNUNET_SQ_query_param_end
421   };
422   struct GNUNET_SQ_QueryParam ins_params[] = {
423     GNUNET_SQ_query_param_auto_from_type (&query),
424     GNUNET_SQ_query_param_fixed_size (block,
425                                       block_size),
426     GNUNET_SQ_query_param_absolute_time (&expiration),
427     GNUNET_SQ_query_param_end
428   };
429   int n;
430
431   namecache_sqlite_expire_blocks (plugin);
432   GNUNET_CRYPTO_hash (&block->derived_key,
433                       sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
434                       &query);
435   expiration = GNUNET_TIME_absolute_ntoh (block->expiration_time);
436   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
437               "Caching new version of block %s (expires %s)\n",
438               GNUNET_h2s (&query),
439               GNUNET_STRINGS_absolute_time_to_string (expiration));
440   if (block_size > 64 * 65536)
441   {
442     GNUNET_break (0);
443     return GNUNET_SYSERR;
444   }
445
446   /* delete old version of the block */
447   if (GNUNET_OK !=
448       GNUNET_SQ_bind (plugin->delete_block,
449                       del_params))
450   {
451     LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
452                 "sqlite3_bind_XXXX");
453     GNUNET_SQ_reset (plugin->dbh,
454                      plugin->delete_block);
455     return GNUNET_SYSERR;
456   }
457   n = sqlite3_step (plugin->delete_block);
458   switch (n)
459   {
460   case SQLITE_DONE:
461     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
462                      "sqlite",
463                      "Old block deleted\n");
464     break;
465   case SQLITE_BUSY:
466     LOG_SQLITE (plugin,
467                 GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
468                 "sqlite3_step");
469     break;
470   default:
471     LOG_SQLITE (plugin,
472                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
473                 "sqlite3_step");
474     break;
475   }
476   GNUNET_SQ_reset (plugin->dbh,
477                    plugin->delete_block);
478
479   /* insert new version of the block */
480   if (GNUNET_OK !=
481       GNUNET_SQ_bind (plugin->cache_block,
482                       ins_params))
483   {
484     LOG_SQLITE (plugin,
485                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
486                 "sqlite3_bind_XXXX");
487     GNUNET_SQ_reset (plugin->dbh,
488                      plugin->cache_block);
489     return GNUNET_SYSERR;
490
491   }
492   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
493               "Caching block under derived key `%s'\n",
494               GNUNET_h2s_full (&query));
495   n = sqlite3_step (plugin->cache_block);
496   GNUNET_SQ_reset (plugin->dbh,
497                    plugin->cache_block);
498   switch (n)
499   {
500   case SQLITE_DONE:
501     LOG (GNUNET_ERROR_TYPE_DEBUG,
502          "Record stored\n");
503     return GNUNET_OK;
504   case SQLITE_BUSY:
505     LOG_SQLITE (plugin,
506                 GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
507                 "sqlite3_step");
508     return GNUNET_NO;
509   default:
510     LOG_SQLITE (plugin,
511                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
512                 "sqlite3_step");
513     return GNUNET_SYSERR;
514   }
515 }
516
517
518 /**
519  * Get the block for a particular zone and label in the
520  * datastore.  Will return at most one result to the iterator.
521  *
522  * @param cls closure (internal context for the plugin)
523  * @param query hash of public key derived from the zone and the label
524  * @param iter function to call with the result
525  * @param iter_cls closure for @a iter
526  * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
527  */
528 static int
529 namecache_sqlite_lookup_block (void *cls,
530                                const struct GNUNET_HashCode *query,
531                                GNUNET_NAMECACHE_BlockCallback iter,
532                                void *iter_cls)
533 {
534   struct Plugin *plugin = cls;
535   int ret;
536   int sret;
537   size_t block_size;
538   const struct GNUNET_GNSRECORD_Block *block;
539   struct GNUNET_SQ_QueryParam params[] = {
540     GNUNET_SQ_query_param_auto_from_type (query),
541     GNUNET_SQ_query_param_end
542   };
543   struct GNUNET_SQ_ResultSpec rs[] = {
544     GNUNET_SQ_result_spec_variable_size ((void **) &block,
545                                          &block_size),
546     GNUNET_SQ_result_spec_end
547   };
548
549   if (GNUNET_OK !=
550       GNUNET_SQ_bind (plugin->lookup_block,
551                       params))
552   {
553     LOG_SQLITE (plugin,
554                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
555                 "sqlite3_bind_XXXX");
556     GNUNET_SQ_reset (plugin->dbh,
557                      plugin->lookup_block);
558     return GNUNET_SYSERR;
559   }
560   ret = GNUNET_NO;
561   if (SQLITE_ROW ==
562       (sret = sqlite3_step (plugin->lookup_block)))
563   {
564     if (GNUNET_OK !=
565         GNUNET_SQ_extract_result (plugin->lookup_block,
566                                   rs))
567     {
568       GNUNET_break (0);
569       ret = GNUNET_SYSERR;
570     }
571     else if ( (block_size < sizeof (struct GNUNET_GNSRECORD_Block)) ||
572               (ntohl (block->purpose.size) +
573                sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
574                sizeof (struct GNUNET_CRYPTO_EcdsaSignature) != block_size) )
575     {
576       GNUNET_break (0);
577       GNUNET_SQ_cleanup_result (rs);
578       ret = GNUNET_SYSERR;
579     }
580     else
581     {
582       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
583                   "Found block under derived key `%s'\n",
584                   GNUNET_h2s_full (query));
585       iter (iter_cls,
586             block);
587       GNUNET_SQ_cleanup_result (rs);
588       ret = GNUNET_YES;
589     }
590   }
591   else
592   {
593     if (SQLITE_DONE != sret)
594     {
595       LOG_SQLITE (plugin,
596                   GNUNET_ERROR_TYPE_ERROR,
597                   "sqlite_step");
598       ret = GNUNET_SYSERR;
599     }
600     else
601     {
602       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
603                   "No block found under derived key `%s'\n",
604                   GNUNET_h2s_full (query));
605     }
606   }
607   GNUNET_SQ_reset (plugin->dbh,
608                    plugin->lookup_block);
609   return ret;
610 }
611
612
613 /**
614  * Entry point for the plugin.
615  *
616  * @param cls the "struct GNUNET_NAMECACHE_PluginEnvironment*"
617  * @return NULL on error, otherwise the plugin context
618  */
619 void *
620 libgnunet_plugin_namecache_sqlite_init (void *cls)
621 {
622   static struct Plugin plugin;
623   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
624   struct GNUNET_NAMECACHE_PluginFunctions *api;
625
626   if (NULL != plugin.cfg)
627     return NULL;                /* can only initialize once! */
628   memset (&plugin, 0, sizeof (struct Plugin));
629   plugin.cfg = cfg;
630   if (GNUNET_OK != database_setup (&plugin))
631   {
632     database_shutdown (&plugin);
633     return NULL;
634   }
635   api = GNUNET_new (struct GNUNET_NAMECACHE_PluginFunctions);
636   api->cls = &plugin;
637   api->cache_block = &namecache_sqlite_cache_block;
638   api->lookup_block = &namecache_sqlite_lookup_block;
639   LOG (GNUNET_ERROR_TYPE_INFO,
640        _("Sqlite database running\n"));
641   return api;
642 }
643
644
645 /**
646  * Exit point from the plugin.
647  *
648  * @param cls the plugin context (as returned by "init")
649  * @return always NULL
650  */
651 void *
652 libgnunet_plugin_namecache_sqlite_done (void *cls)
653 {
654   struct GNUNET_NAMECACHE_PluginFunctions *api = cls;
655   struct Plugin *plugin = api->cls;
656
657   database_shutdown (plugin);
658   plugin->cfg = NULL;
659   GNUNET_free (api);
660   LOG (GNUNET_ERROR_TYPE_DEBUG,
661        "sqlite plugin is finished\n");
662   return NULL;
663 }
664
665 /* end of plugin_namecache_sqlite.c */