remove 'illegal' (non-reentrant) log logic from signal handler
[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 it
6  * under the terms of the GNU Affero General Public License as published
7  * by the Free Software Foundation, either version 3 of the License,
8  * or (at your 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  * Affero General Public License for more details.
14  *
15  * You should have received a copy of the GNU Affero General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18     SPDX-License-Identifier: AGPL3.0-or-later
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, \
53                                                          "namecache-sqlite", _ ( \
54                                                            "`%s' failed at %s:%d with error: %s\n"), \
55                                                          cmd, \
56                                                          __FILE__, __LINE__, \
57                                                          sqlite3_errmsg ( \
58                                                            db->dbh)); \
59 } while (0)
60
61 #define LOG(kind, ...) GNUNET_log_from (kind, "namecache-sqlite", __VA_ARGS__)
62
63
64 /**
65  * Context for all functions in this plugin.
66  */
67 struct Plugin
68 {
69   const struct GNUNET_CONFIGURATION_Handle *cfg;
70
71   /**
72    * Database filename.
73    */
74   char *fn;
75
76   /**
77    * Native SQLite database handle.
78    */
79   sqlite3 *dbh;
80
81   /**
82    * Precompiled SQL for caching a block
83    */
84   sqlite3_stmt *cache_block;
85
86   /**
87    * Precompiled SQL for deleting an older block
88    */
89   sqlite3_stmt *delete_block;
90
91   /**
92    * Precompiled SQL for looking up a block
93    */
94   sqlite3_stmt *lookup_block;
95
96   /**
97    * Precompiled SQL for removing expired blocks
98    */
99   sqlite3_stmt *expire_blocks;
100 };
101
102
103 /**
104  * Initialize the database connections and associated
105  * data structures (create tables and indices
106  * as needed as well).
107  *
108  * @param plugin the plugin context (state for this module)
109  * @return #GNUNET_OK on success
110  */
111 static int
112 database_setup (struct Plugin *plugin)
113 {
114   struct GNUNET_SQ_ExecuteStatement es[] = {
115     GNUNET_SQ_make_try_execute ("PRAGMA temp_store=MEMORY"),
116     GNUNET_SQ_make_try_execute ("PRAGMA synchronous=NORMAL"),
117     GNUNET_SQ_make_try_execute ("PRAGMA legacy_file_format=OFF"),
118     GNUNET_SQ_make_try_execute ("PRAGMA auto_vacuum=INCREMENTAL"),
119     GNUNET_SQ_make_try_execute ("PRAGMA encoding=\"UTF-8\""),
120     GNUNET_SQ_make_try_execute ("PRAGMA locking_mode=EXCLUSIVE"),
121     GNUNET_SQ_make_try_execute ("PRAGMA page_size=4092"),
122     GNUNET_SQ_make_try_execute ("PRAGMA journal_mode=WAL"),
123     GNUNET_SQ_make_execute ("CREATE TABLE IF NOT EXISTS ns096blocks ("
124                             " query BLOB NOT NULL,"
125                             " block BLOB NOT NULL,"
126                             " expiration_time INT8 NOT NULL"
127                             ")"),
128     GNUNET_SQ_make_execute ("CREATE INDEX IF NOT EXISTS ir_query_hash "
129                             "ON ns096blocks (query,expiration_time)"),
130     GNUNET_SQ_make_execute ("CREATE INDEX IF NOT EXISTS ir_block_expiration "
131                             "ON ns096blocks (expiration_time)"),
132     GNUNET_SQ_EXECUTE_STATEMENT_END
133   };
134   struct GNUNET_SQ_PrepareStatement ps[] = {
135     GNUNET_SQ_make_prepare (
136       "INSERT INTO ns096blocks (query,block,expiration_time) VALUES (?, ?, ?)",
137       &plugin->cache_block),
138     GNUNET_SQ_make_prepare ("DELETE FROM ns096blocks WHERE expiration_time<?",
139                             &plugin->expire_blocks),
140     GNUNET_SQ_make_prepare (
141       "DELETE FROM ns096blocks WHERE query=? AND expiration_time<=?",
142       &plugin->delete_block),
143     GNUNET_SQ_make_prepare ("SELECT block FROM ns096blocks WHERE query=? "
144                             "ORDER BY expiration_time DESC LIMIT 1",
145                             &plugin->lookup_block),
146     GNUNET_SQ_PREPARE_END
147   };
148   char *afsdir;
149
150   if (GNUNET_OK !=
151       GNUNET_CONFIGURATION_get_value_filename (plugin->cfg,
152                                                "namecache-sqlite",
153                                                "FILENAME",
154                                                &afsdir))
155   {
156     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
157                                "namecache-sqlite",
158                                "FILENAME");
159     return GNUNET_SYSERR;
160   }
161   if (GNUNET_OK !=
162       GNUNET_DISK_file_test (afsdir))
163   {
164     if (GNUNET_OK !=
165         GNUNET_DISK_directory_create_for_file (afsdir))
166     {
167       GNUNET_break (0);
168       GNUNET_free (afsdir);
169       return GNUNET_SYSERR;
170     }
171   }
172   /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
173   plugin->fn = afsdir;
174
175   /* Open database and precompile statements */
176   if (SQLITE_OK !=
177       sqlite3_open (plugin->fn, &plugin->dbh))
178   {
179     LOG (GNUNET_ERROR_TYPE_ERROR,
180          _ ("Unable to initialize SQLite: %s.\n"),
181          sqlite3_errmsg (plugin->dbh));
182     return GNUNET_SYSERR;
183   }
184   if (GNUNET_OK !=
185       GNUNET_SQ_exec_statements (plugin->dbh,
186                                  es))
187   {
188     GNUNET_break (0);
189     LOG (GNUNET_ERROR_TYPE_ERROR,
190          _ ("Failed to setup database at `%s'\n"),
191          plugin->fn);
192     return GNUNET_SYSERR;
193   }
194   GNUNET_break (SQLITE_OK ==
195                 sqlite3_busy_timeout (plugin->dbh,
196                                       BUSY_TIMEOUT_MS));
197
198   if (GNUNET_OK !=
199       GNUNET_SQ_prepare (plugin->dbh,
200                          ps))
201   {
202     GNUNET_break (0);
203     LOG (GNUNET_ERROR_TYPE_ERROR,
204          _ ("Failed to setup database at `%s'\n"),
205          plugin->fn);
206     return GNUNET_SYSERR;
207   }
208
209   return GNUNET_OK;
210 }
211
212
213 /**
214  * Shutdown database connection and associate data
215  * structures.
216  * @param plugin the plugin context (state for this module)
217  */
218 static void
219 database_shutdown (struct Plugin *plugin)
220 {
221   int result;
222   sqlite3_stmt *stmt;
223
224   if (NULL != plugin->cache_block)
225     sqlite3_finalize (plugin->cache_block);
226   if (NULL != plugin->lookup_block)
227     sqlite3_finalize (plugin->lookup_block);
228   if (NULL != plugin->expire_blocks)
229     sqlite3_finalize (plugin->expire_blocks);
230   if (NULL != plugin->delete_block)
231     sqlite3_finalize (plugin->delete_block);
232   result = sqlite3_close (plugin->dbh);
233   if (result == SQLITE_BUSY)
234   {
235     LOG (GNUNET_ERROR_TYPE_WARNING,
236          _ (
237            "Tried to close sqlite without finalizing all prepared statements.\n"));
238     stmt = sqlite3_next_stmt (plugin->dbh,
239                               NULL);
240     while (stmt != NULL)
241     {
242       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
243                        "sqlite",
244                        "Closing statement %p\n",
245                        stmt);
246       result = sqlite3_finalize (stmt);
247       if (result != SQLITE_OK)
248         GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
249                          "sqlite",
250                          "Failed to close statement %p: %d\n",
251                          stmt,
252                          result);
253       stmt = sqlite3_next_stmt (plugin->dbh,
254                                 NULL);
255     }
256     result = sqlite3_close (plugin->dbh);
257   }
258   if (SQLITE_OK != result)
259     LOG_SQLITE (plugin,
260                 GNUNET_ERROR_TYPE_ERROR,
261                 "sqlite3_close");
262
263   GNUNET_free_non_null (plugin->fn);
264 }
265
266
267 /**
268  * Removes any expired block.
269  *
270  * @param plugin the plugin
271  */
272 static void
273 namecache_sqlite_expire_blocks (struct Plugin *plugin)
274 {
275   struct GNUNET_TIME_Absolute now;
276   struct GNUNET_SQ_QueryParam params[] = {
277     GNUNET_SQ_query_param_absolute_time (&now),
278     GNUNET_SQ_query_param_end
279   };
280   int n;
281
282   now = GNUNET_TIME_absolute_get ();
283   if (GNUNET_OK !=
284       GNUNET_SQ_bind (plugin->expire_blocks,
285                       params))
286   {
287     LOG_SQLITE (plugin,
288                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
289                 "sqlite3_bind_XXXX");
290     GNUNET_SQ_reset (plugin->dbh,
291                      plugin->expire_blocks);
292     return;
293   }
294   n = sqlite3_step (plugin->expire_blocks);
295   GNUNET_SQ_reset (plugin->dbh,
296                    plugin->expire_blocks);
297   switch (n)
298   {
299   case SQLITE_DONE:
300     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
301                      "sqlite",
302                      "Records expired\n");
303     return;
304
305   case SQLITE_BUSY:
306     LOG_SQLITE (plugin,
307                 GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
308                 "sqlite3_step");
309     return;
310
311   default:
312     LOG_SQLITE (plugin,
313                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
314                 "sqlite3_step");
315     return;
316   }
317 }
318
319
320 /**
321  * Cache a block in the datastore.
322  *
323  * @param cls closure (internal context for the plugin)
324  * @param block block to cache
325  * @return #GNUNET_OK on success, else #GNUNET_SYSERR
326  */
327 static int
328 namecache_sqlite_cache_block (void *cls,
329                               const struct GNUNET_GNSRECORD_Block *block)
330 {
331   static struct GNUNET_TIME_Absolute last_expire;
332   struct Plugin *plugin = cls;
333   struct GNUNET_HashCode query;
334   struct GNUNET_TIME_Absolute expiration;
335   size_t block_size = ntohl (block->purpose.size)
336                       + sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)
337                       + sizeof(struct GNUNET_CRYPTO_EcdsaSignature);
338   struct GNUNET_SQ_QueryParam del_params[] = {
339     GNUNET_SQ_query_param_auto_from_type (&query),
340     GNUNET_SQ_query_param_absolute_time (&expiration),
341     GNUNET_SQ_query_param_end
342   };
343   struct GNUNET_SQ_QueryParam ins_params[] = {
344     GNUNET_SQ_query_param_auto_from_type (&query),
345     GNUNET_SQ_query_param_fixed_size (block,
346                                       block_size),
347     GNUNET_SQ_query_param_absolute_time (&expiration),
348     GNUNET_SQ_query_param_end
349   };
350   int n;
351
352   /* run expiration of old cache entries once per hour */
353   if (GNUNET_TIME_absolute_get_duration (last_expire).rel_value_us >
354       GNUNET_TIME_UNIT_HOURS.rel_value_us)
355   {
356     last_expire = GNUNET_TIME_absolute_get ();
357     namecache_sqlite_expire_blocks (plugin);
358   }
359   GNUNET_CRYPTO_hash (&block->derived_key,
360                       sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey),
361                       &query);
362   expiration = GNUNET_TIME_absolute_ntoh (block->expiration_time);
363   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
364               "Caching new version of block %s (expires %s)\n",
365               GNUNET_h2s (&query),
366               GNUNET_STRINGS_absolute_time_to_string (expiration));
367   if (block_size > 64 * 65536)
368   {
369     GNUNET_break (0);
370     return GNUNET_SYSERR;
371   }
372
373   /* delete old version of the block */
374   if (GNUNET_OK !=
375       GNUNET_SQ_bind (plugin->delete_block,
376                       del_params))
377   {
378     LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
379                 "sqlite3_bind_XXXX");
380     GNUNET_SQ_reset (plugin->dbh,
381                      plugin->delete_block);
382     return GNUNET_SYSERR;
383   }
384   n = sqlite3_step (plugin->delete_block);
385   switch (n)
386   {
387   case SQLITE_DONE:
388     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
389                      "sqlite",
390                      "Old block deleted\n");
391     break;
392
393   case SQLITE_BUSY:
394     LOG_SQLITE (plugin,
395                 GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
396                 "sqlite3_step");
397     break;
398
399   default:
400     LOG_SQLITE (plugin,
401                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
402                 "sqlite3_step");
403     break;
404   }
405   GNUNET_SQ_reset (plugin->dbh,
406                    plugin->delete_block);
407
408   /* insert new version of the block */
409   if (GNUNET_OK !=
410       GNUNET_SQ_bind (plugin->cache_block,
411                       ins_params))
412   {
413     LOG_SQLITE (plugin,
414                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
415                 "sqlite3_bind_XXXX");
416     GNUNET_SQ_reset (plugin->dbh,
417                      plugin->cache_block);
418     return GNUNET_SYSERR;
419   }
420   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
421               "Caching block under derived key `%s'\n",
422               GNUNET_h2s_full (&query));
423   n = sqlite3_step (plugin->cache_block);
424   GNUNET_SQ_reset (plugin->dbh,
425                    plugin->cache_block);
426   switch (n)
427   {
428   case SQLITE_DONE:
429     LOG (GNUNET_ERROR_TYPE_DEBUG,
430          "Record stored\n");
431     return GNUNET_OK;
432
433   case SQLITE_BUSY:
434     LOG_SQLITE (plugin,
435                 GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
436                 "sqlite3_step");
437     return GNUNET_NO;
438
439   default:
440     LOG_SQLITE (plugin,
441                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
442                 "sqlite3_step");
443     return GNUNET_SYSERR;
444   }
445 }
446
447
448 /**
449  * Get the block for a particular zone and label in the
450  * datastore.  Will return at most one result to the iterator.
451  *
452  * @param cls closure (internal context for the plugin)
453  * @param query hash of public key derived from the zone and the label
454  * @param iter function to call with the result
455  * @param iter_cls closure for @a iter
456  * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
457  */
458 static int
459 namecache_sqlite_lookup_block (void *cls,
460                                const struct GNUNET_HashCode *query,
461                                GNUNET_NAMECACHE_BlockCallback iter,
462                                void *iter_cls)
463 {
464   struct Plugin *plugin = cls;
465   int ret;
466   int sret;
467   size_t block_size;
468   const struct GNUNET_GNSRECORD_Block *block;
469   struct GNUNET_SQ_QueryParam params[] = {
470     GNUNET_SQ_query_param_auto_from_type (query),
471     GNUNET_SQ_query_param_end
472   };
473   struct GNUNET_SQ_ResultSpec rs[] = {
474     GNUNET_SQ_result_spec_variable_size ((void **) &block,
475                                          &block_size),
476     GNUNET_SQ_result_spec_end
477   };
478
479   if (GNUNET_OK !=
480       GNUNET_SQ_bind (plugin->lookup_block,
481                       params))
482   {
483     LOG_SQLITE (plugin,
484                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
485                 "sqlite3_bind_XXXX");
486     GNUNET_SQ_reset (plugin->dbh,
487                      plugin->lookup_block);
488     return GNUNET_SYSERR;
489   }
490   ret = GNUNET_NO;
491   if (SQLITE_ROW ==
492       (sret = sqlite3_step (plugin->lookup_block)))
493   {
494     if (GNUNET_OK !=
495         GNUNET_SQ_extract_result (plugin->lookup_block,
496                                   rs))
497     {
498       GNUNET_break (0);
499       ret = GNUNET_SYSERR;
500     }
501     else if ((block_size < sizeof(struct GNUNET_GNSRECORD_Block)) ||
502              (ntohl (block->purpose.size)
503               + sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)
504               + sizeof(struct GNUNET_CRYPTO_EcdsaSignature) != block_size))
505     {
506       GNUNET_break (0);
507       GNUNET_SQ_cleanup_result (rs);
508       ret = GNUNET_SYSERR;
509     }
510     else
511     {
512       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
513                   "Found block under derived key `%s'\n",
514                   GNUNET_h2s_full (query));
515       iter (iter_cls,
516             block);
517       GNUNET_SQ_cleanup_result (rs);
518       ret = GNUNET_YES;
519     }
520   }
521   else
522   {
523     if (SQLITE_DONE != sret)
524     {
525       LOG_SQLITE (plugin,
526                   GNUNET_ERROR_TYPE_ERROR,
527                   "sqlite_step");
528       ret = GNUNET_SYSERR;
529     }
530     else
531     {
532       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
533                   "No block found under derived key `%s'\n",
534                   GNUNET_h2s_full (query));
535     }
536   }
537   GNUNET_SQ_reset (plugin->dbh,
538                    plugin->lookup_block);
539   return ret;
540 }
541
542
543 /**
544  * Entry point for the plugin.
545  *
546  * @param cls the "struct GNUNET_NAMECACHE_PluginEnvironment*"
547  * @return NULL on error, otherwise the plugin context
548  */
549 void *
550 libgnunet_plugin_namecache_sqlite_init (void *cls)
551 {
552   static struct Plugin plugin;
553   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
554   struct GNUNET_NAMECACHE_PluginFunctions *api;
555
556   if (NULL != plugin.cfg)
557     return NULL;                /* can only initialize once! */
558   memset (&plugin, 0, sizeof(struct Plugin));
559   plugin.cfg = cfg;
560   if (GNUNET_OK != database_setup (&plugin))
561   {
562     database_shutdown (&plugin);
563     return NULL;
564   }
565   api = GNUNET_new (struct GNUNET_NAMECACHE_PluginFunctions);
566   api->cls = &plugin;
567   api->cache_block = &namecache_sqlite_cache_block;
568   api->lookup_block = &namecache_sqlite_lookup_block;
569   LOG (GNUNET_ERROR_TYPE_INFO,
570        _ ("Sqlite database running\n"));
571   return api;
572 }
573
574
575 /**
576  * Exit point from the plugin.
577  *
578  * @param cls the plugin context (as returned by "init")
579  * @return always NULL
580  */
581 void *
582 libgnunet_plugin_namecache_sqlite_done (void *cls)
583 {
584   struct GNUNET_NAMECACHE_PluginFunctions *api = cls;
585   struct Plugin *plugin = api->cls;
586
587   database_shutdown (plugin);
588   plugin->cfg = NULL;
589   GNUNET_free (api);
590   LOG (GNUNET_ERROR_TYPE_DEBUG,
591        "sqlite plugin is finished\n");
592   return NULL;
593 }
594
595
596 /* end of plugin_namecache_sqlite.c */