GNUnet  0.10.x
plugin_namecache_sqlite.c
Go to the documentation of this file.
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 
26 #include "platform.h"
27 #include "gnunet_sq_lib.h"
30 #include "gnunet_gnsrecord_lib.h"
31 #include "namecache.h"
32 #include <sqlite3.h>
33 
44 #define BUSY_TIMEOUT_MS 1000
45 
46 
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 
60 struct Plugin {
61  const struct GNUNET_CONFIGURATION_Handle *cfg;
62 
66  char *fn;
67 
71  sqlite3 *dbh;
72 
76  sqlite3_stmt *cache_block;
77 
81  sqlite3_stmt *delete_block;
82 
86  sqlite3_stmt *lookup_block;
87 
91  sqlite3_stmt *expire_blocks;
92 };
93 
94 
103 static int
105 {
106  struct GNUNET_SQ_ExecuteStatement es[] = {
107  GNUNET_SQ_make_try_execute("PRAGMA temp_store=MEMORY"),
108  GNUNET_SQ_make_try_execute("PRAGMA synchronous=NORMAL"),
109  GNUNET_SQ_make_try_execute("PRAGMA legacy_file_format=OFF"),
110  GNUNET_SQ_make_try_execute("PRAGMA auto_vacuum=INCREMENTAL"),
111  GNUNET_SQ_make_try_execute("PRAGMA encoding=\"UTF-8\""),
112  GNUNET_SQ_make_try_execute("PRAGMA locking_mode=EXCLUSIVE"),
113  GNUNET_SQ_make_try_execute("PRAGMA page_size=4092"),
114  GNUNET_SQ_make_try_execute("PRAGMA journal_mode=WAL"),
115  GNUNET_SQ_make_execute("CREATE TABLE IF NOT EXISTS ns096blocks ("
116  " query BLOB NOT NULL,"
117  " block BLOB NOT NULL,"
118  " expiration_time INT8 NOT NULL"
119  ")"),
120  GNUNET_SQ_make_execute("CREATE INDEX IF NOT EXISTS ir_query_hash "
121  "ON ns096blocks (query,expiration_time)"),
122  GNUNET_SQ_make_execute("CREATE INDEX IF NOT EXISTS ir_block_expiration "
123  "ON ns096blocks (expiration_time)"),
125  };
126  struct GNUNET_SQ_PrepareStatement ps[] = {
127  GNUNET_SQ_make_prepare("INSERT INTO ns096blocks (query,block,expiration_time) VALUES (?, ?, ?)",
128  &plugin->cache_block),
129  GNUNET_SQ_make_prepare("DELETE FROM ns096blocks WHERE expiration_time<?",
130  &plugin->expire_blocks),
131  GNUNET_SQ_make_prepare("DELETE FROM ns096blocks WHERE query=? AND expiration_time<=?",
132  &plugin->delete_block),
133  GNUNET_SQ_make_prepare("SELECT block FROM ns096blocks WHERE query=? "
134  "ORDER BY expiration_time DESC LIMIT 1",
135  &plugin->lookup_block),
137  };
138  char *afsdir;
139 
140  if (GNUNET_OK !=
142  "namecache-sqlite",
143  "FILENAME",
144  &afsdir))
145  {
147  "namecache-sqlite",
148  "FILENAME");
149  return GNUNET_SYSERR;
150  }
151  if (GNUNET_OK !=
152  GNUNET_DISK_file_test(afsdir))
153  {
154  if (GNUNET_OK !=
156  {
157  GNUNET_break(0);
158  GNUNET_free(afsdir);
159  return GNUNET_SYSERR;
160  }
161  }
162  /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
163  plugin->fn = afsdir;
164 
165  /* Open database and precompile statements */
166  if (SQLITE_OK !=
167  sqlite3_open(plugin->fn, &plugin->dbh))
168  {
170  _("Unable to initialize SQLite: %s.\n"),
171  sqlite3_errmsg(plugin->dbh));
172  return GNUNET_SYSERR;
173  }
174  if (GNUNET_OK !=
176  es))
177  {
178  GNUNET_break(0);
180  _("Failed to setup database at `%s'\n"),
181  plugin->fn);
182  return GNUNET_SYSERR;
183  }
184  GNUNET_break(SQLITE_OK ==
185  sqlite3_busy_timeout(plugin->dbh,
186  BUSY_TIMEOUT_MS));
187 
188  if (GNUNET_OK !=
189  GNUNET_SQ_prepare(plugin->dbh,
190  ps))
191  {
192  GNUNET_break(0);
194  _("Failed to setup database at `%s'\n"),
195  plugin->fn);
196  return GNUNET_SYSERR;
197  }
198 
199  return GNUNET_OK;
200 }
201 
202 
208 static void
210 {
211  int result;
212  sqlite3_stmt *stmt;
213 
214  if (NULL != plugin->cache_block)
215  sqlite3_finalize(plugin->cache_block);
216  if (NULL != plugin->lookup_block)
217  sqlite3_finalize(plugin->lookup_block);
218  if (NULL != plugin->expire_blocks)
219  sqlite3_finalize(plugin->expire_blocks);
220  if (NULL != plugin->delete_block)
221  sqlite3_finalize(plugin->delete_block);
222  result = sqlite3_close(plugin->dbh);
223  if (result == SQLITE_BUSY)
224  {
226  _("Tried to close sqlite without finalizing all prepared statements.\n"));
227  stmt = sqlite3_next_stmt(plugin->dbh,
228  NULL);
229  while (stmt != NULL)
230  {
232  "sqlite",
233  "Closing statement %p\n",
234  stmt);
235  result = sqlite3_finalize(stmt);
236  if (result != SQLITE_OK)
238  "sqlite",
239  "Failed to close statement %p: %d\n",
240  stmt,
241  result);
242  stmt = sqlite3_next_stmt(plugin->dbh,
243  NULL);
244  }
245  result = sqlite3_close(plugin->dbh);
246  }
247  if (SQLITE_OK != result)
248  LOG_SQLITE(plugin,
250  "sqlite3_close");
251 
252  GNUNET_free_non_null(plugin->fn);
253 }
254 
255 
261 static void
263 {
264  struct GNUNET_TIME_Absolute now;
265  struct GNUNET_SQ_QueryParam params[] = {
268  };
269  int n;
270 
271  now = GNUNET_TIME_absolute_get();
272  if (GNUNET_OK !=
274  params))
275  {
276  LOG_SQLITE(plugin,
278  "sqlite3_bind_XXXX");
279  GNUNET_SQ_reset(plugin->dbh,
280  plugin->expire_blocks);
281  return;
282  }
283  n = sqlite3_step(plugin->expire_blocks);
284  GNUNET_SQ_reset(plugin->dbh,
285  plugin->expire_blocks);
286  switch (n)
287  {
288  case SQLITE_DONE:
290  "sqlite",
291  "Records expired\n");
292  return;
293 
294  case SQLITE_BUSY:
295  LOG_SQLITE(plugin,
297  "sqlite3_step");
298  return;
299 
300  default:
301  LOG_SQLITE(plugin,
303  "sqlite3_step");
304  return;
305  }
306 }
307 
308 
316 static int
318  const struct GNUNET_GNSRECORD_Block *block)
319 {
320  static struct GNUNET_TIME_Absolute last_expire;
321  struct Plugin *plugin = cls;
322  struct GNUNET_HashCode query;
323  struct GNUNET_TIME_Absolute expiration;
324  size_t block_size = ntohl(block->purpose.size) +
325  sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey) +
326  sizeof(struct GNUNET_CRYPTO_EcdsaSignature);
327  struct GNUNET_SQ_QueryParam del_params[] = {
331  };
332  struct GNUNET_SQ_QueryParam ins_params[] = {
335  block_size),
338  };
339  int n;
340 
341  /* run expiration of old cache entries once per hour */
342  if (GNUNET_TIME_absolute_get_duration(last_expire).rel_value_us >
343  GNUNET_TIME_UNIT_HOURS.rel_value_us)
344  {
345  last_expire = GNUNET_TIME_absolute_get();
347  }
349  sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey),
350  &query);
351  expiration = GNUNET_TIME_absolute_ntoh(block->expiration_time);
353  "Caching new version of block %s (expires %s)\n",
354  GNUNET_h2s(&query),
356  if (block_size > 64 * 65536)
357  {
358  GNUNET_break(0);
359  return GNUNET_SYSERR;
360  }
361 
362  /* delete old version of the block */
363  if (GNUNET_OK !=
365  del_params))
366  {
368  "sqlite3_bind_XXXX");
369  GNUNET_SQ_reset(plugin->dbh,
370  plugin->delete_block);
371  return GNUNET_SYSERR;
372  }
373  n = sqlite3_step(plugin->delete_block);
374  switch (n)
375  {
376  case SQLITE_DONE:
378  "sqlite",
379  "Old block deleted\n");
380  break;
381 
382  case SQLITE_BUSY:
383  LOG_SQLITE(plugin,
385  "sqlite3_step");
386  break;
387 
388  default:
389  LOG_SQLITE(plugin,
391  "sqlite3_step");
392  break;
393  }
394  GNUNET_SQ_reset(plugin->dbh,
395  plugin->delete_block);
396 
397  /* insert new version of the block */
398  if (GNUNET_OK !=
399  GNUNET_SQ_bind(plugin->cache_block,
400  ins_params))
401  {
402  LOG_SQLITE(plugin,
404  "sqlite3_bind_XXXX");
405  GNUNET_SQ_reset(plugin->dbh,
406  plugin->cache_block);
407  return GNUNET_SYSERR;
408  }
410  "Caching block under derived key `%s'\n",
411  GNUNET_h2s_full(&query));
412  n = sqlite3_step(plugin->cache_block);
413  GNUNET_SQ_reset(plugin->dbh,
414  plugin->cache_block);
415  switch (n)
416  {
417  case SQLITE_DONE:
419  "Record stored\n");
420  return GNUNET_OK;
421 
422  case SQLITE_BUSY:
423  LOG_SQLITE(plugin,
425  "sqlite3_step");
426  return GNUNET_NO;
427 
428  default:
429  LOG_SQLITE(plugin,
431  "sqlite3_step");
432  return GNUNET_SYSERR;
433  }
434 }
435 
436 
447 static int
449  const struct GNUNET_HashCode *query,
451  void *iter_cls)
452 {
453  struct Plugin *plugin = cls;
454  int ret;
455  int sret;
456  size_t block_size;
457  const struct GNUNET_GNSRECORD_Block *block;
458  struct GNUNET_SQ_QueryParam params[] = {
461  };
462  struct GNUNET_SQ_ResultSpec rs[] = {
463  GNUNET_SQ_result_spec_variable_size((void **)&block,
464  &block_size),
466  };
467 
468  if (GNUNET_OK !=
470  params))
471  {
472  LOG_SQLITE(plugin,
474  "sqlite3_bind_XXXX");
475  GNUNET_SQ_reset(plugin->dbh,
476  plugin->lookup_block);
477  return GNUNET_SYSERR;
478  }
479  ret = GNUNET_NO;
480  if (SQLITE_ROW ==
481  (sret = sqlite3_step(plugin->lookup_block)))
482  {
483  if (GNUNET_OK !=
485  rs))
486  {
487  GNUNET_break(0);
488  ret = GNUNET_SYSERR;
489  }
490  else if ((block_size < sizeof(struct GNUNET_GNSRECORD_Block)) ||
491  (ntohl(block->purpose.size) +
492  sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey) +
493  sizeof(struct GNUNET_CRYPTO_EcdsaSignature) != block_size))
494  {
495  GNUNET_break(0);
497  ret = GNUNET_SYSERR;
498  }
499  else
500  {
502  "Found block under derived key `%s'\n",
503  GNUNET_h2s_full(query));
504  iter(iter_cls,
505  block);
507  ret = GNUNET_YES;
508  }
509  }
510  else
511  {
512  if (SQLITE_DONE != sret)
513  {
514  LOG_SQLITE(plugin,
516  "sqlite_step");
517  ret = GNUNET_SYSERR;
518  }
519  else
520  {
522  "No block found under derived key `%s'\n",
523  GNUNET_h2s_full(query));
524  }
525  }
526  GNUNET_SQ_reset(plugin->dbh,
527  plugin->lookup_block);
528  return ret;
529 }
530 
531 
538 void *
540 {
541  static struct Plugin plugin;
542  const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
544 
545  if (NULL != plugin.cfg)
546  return NULL; /* can only initialize once! */
547  memset(&plugin, 0, sizeof(struct Plugin));
548  plugin.cfg = cfg;
549  if (GNUNET_OK != database_setup(&plugin))
550  {
551  database_shutdown(&plugin);
552  return NULL;
553  }
555  api->cls = &plugin;
559  _("Sqlite database running\n"));
560  return api;
561 }
562 
563 
570 void *
572 {
574  struct Plugin *plugin = api->cls;
575 
576  database_shutdown(plugin);
577  plugin->cfg = NULL;
578  GNUNET_free(api);
580  "sqlite plugin is finished\n");
581  return NULL;
582 }
583 
584 /* end of plugin_namecache_sqlite.c */
int GNUNET_DISK_file_test(const char *fil)
Check that fil corresponds to a filename (of a file that exists and that is not a directory)...
Definition: disk.c:541
void(* GNUNET_NAMECACHE_BlockCallback)(void *cls, const struct GNUNET_GNSRECORD_Block *block)
Function called for matching blocks.
struct GNUNET_TIME_AbsoluteNBO expiration_time
Expiration time of the block.
void * cls
Closure to pass to all plugin functions.
Description of a DB result cell.
#define GNUNET_TIME_UNIT_HOURS
One hour.
struct GNUNET_TIME_Absolute GNUNET_TIME_absolute_ntoh(struct GNUNET_TIME_AbsoluteNBO a)
Convert absolute time from network byte order.
Definition: time.c:671
#define GNUNET_SQ_EXECUTE_STATEMENT_END
Terminator for executable statement list.
void * libgnunet_plugin_namecache_sqlite_init(void *cls)
Entry point for the plugin.
Information needed to run a list of SQL statements using GNUNET_SQ_exec_statements().
#define GNUNET_SQ_query_param_auto_from_type(x)
Generate fixed-size query parameter with size determined by variable type.
const struct GNUNET_CONFIGURATION_Handle * cfg
Our configuration.
#define LOG(kind,...)
int GNUNET_DISK_directory_create_for_file(const char *filename)
Create the directory structure for storing a file.
Definition: disk.c:681
#define GNUNET_SQ_result_spec_end
End of result parameter specification.
#define GNUNET_NO
Definition: gnunet_common.h:78
const char * GNUNET_h2s(const struct GNUNET_HashCode *hc)
Convert a hash value to a string (for printing debug messages).
#define GNUNET_OK
Named constants for return values.
Definition: gnunet_common.h:75
#define GNUNET_free_non_null(ptr)
Free the memory pointed to by ptr if ptr is not NULL.
#define GNUNET_new(type)
Allocate a struct or union of the given type.
sqlite3_stmt * expire_blocks
Precompiled SQL for removing expired blocks.
Information we have in an encrypted block with record data (i.e.
static int ret
Final status code.
Definition: gnunet-arm.c:89
Information needed to run a list of SQL statements using GNUNET_SQ_exec_statements().
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur...
struct GNUNET_BLOCK_PluginFunctions * api
Plugin API.
Definition: block.c:46
static int database_setup(struct Plugin *plugin)
Initialize the database connections and associated data structures (create tables and indices as need...
struct GNUNET_SQ_PrepareStatement GNUNET_SQ_make_prepare(const char *sql, sqlite3_stmt **pstmt)
Create a struct GNUNET_SQ_PrepareStatement
Definition: sq_prepare.c:37
#define _(String)
GNU gettext support macro.
Definition: platform.h:181
int GNUNET_SQ_extract_result(sqlite3_stmt *result, struct GNUNET_SQ_ResultSpec *rs)
Extract results from a query result according to the given specification.
Definition: sq.c:82
int(* cache_block)(void *cls, const struct GNUNET_GNSRECORD_Block *block)
Cache a block in the datastore.
static struct GNUNET_ATS_SolverFunctions * plugin
Our solver.
void * cls
Closure for conv and cleaner.
void GNUNET_SQ_cleanup_result(struct GNUNET_SQ_ResultSpec *rs)
Free all memory that was allocated in rs during GNUNET_SQ_extract_result().
Definition: sq.c:117
void GNUNET_log_config_missing(enum GNUNET_ErrorType kind, const char *section, const char *option)
Log error message about missing configuration option.
int GNUNET_SQ_exec_statements(sqlite3 *dbh, const struct GNUNET_SQ_ExecuteStatement *es)
Request execution of an array of statements es from Postgres.
Definition: sq_exec.c:77
int(* lookup_block)(void *cls, const struct GNUNET_HashCode *query, GNUNET_NAMECACHE_BlockCallback iter, void *iter_cls)
Get the block for a particular zone and label in the datastore.
void GNUNET_CRYPTO_hash(const void *block, size_t size, struct GNUNET_HashCode *ret)
Compute hash of a given block.
Definition: crypto_hash.c:44
sqlite3_stmt * delete_block
Precompiled SQL for deleting an older block.
static void namecache_sqlite_expire_blocks(struct Plugin *plugin)
Removes any expired block.
an ECC signature using ECDSA
struct GNUNET_SQ_ExecuteStatement GNUNET_SQ_make_execute(const char *sql)
Create a struct GNUNET_SQ_ExecuteStatement where errors are fatal.
Definition: sq_exec.c:36
struct GNUNET_SQ_ResultSpec GNUNET_SQ_result_spec_variable_size(void **dst, size_t *sptr)
Variable-size result expected.
static int result
Global testing status.
struct GNUNET_SQ_QueryParam GNUNET_SQ_query_param_absolute_time(const struct GNUNET_TIME_Absolute *x)
Generate query parameter for an absolute time value.
uint32_t size
How many bytes does this signature sign? (including this purpose header); in network byte order (!)...
A 512-bit hashcode.
int GNUNET_SQ_bind(sqlite3_stmt *stmt, const struct GNUNET_SQ_QueryParam *params)
Execute binding operations for a prepared statement.
Definition: sq.c:37
struct GNUNET_TIME_Absolute GNUNET_TIME_absolute_get(void)
Get the current time.
Definition: time.c:118
static int namecache_sqlite_cache_block(void *cls, const struct GNUNET_GNSRECORD_Block *block)
Cache a block in the datastore.
#define GNUNET_SYSERR
Definition: gnunet_common.h:76
#define BUSY_TIMEOUT_MS
After how many ms "busy" should a DB operation fail for good? A low value makes sure that we are more...
void * iter_cls
Iterator cls.
const char * GNUNET_h2s_full(const struct GNUNET_HashCode *hc)
Convert a hash value to a string (for printing debug messages).
sqlite3_stmt * cache_block
Precompiled SQL for caching a block.
helper functions for Sqlite3 DB interactions
Description of a DB query parameter.
Definition: gnunet_sq_lib.h:54
struct returned by the initialization function of the plugin
char * fn
Filename used for the DB.
#define GNUNET_SQ_PREPARE_END
Terminator for executable statement list.
void * libgnunet_plugin_namecache_sqlite_done(void *cls)
Exit point from the plugin.
void GNUNET_SQ_reset(sqlite3 *dbh, sqlite3_stmt *stmt)
Reset stmt and log error.
Definition: sq.c:132
configuration data
Definition: configuration.c:83
Handle for a plugin.
Definition: block.c:37
PGconn * dbh
Native Postgres database handle.
struct GNUNET_TIME_Relative GNUNET_TIME_absolute_get_duration(struct GNUNET_TIME_Absolute whence)
Get the duration of an operation as the difference of the current time and the given start time "henc...
Definition: time.c:373
int GNUNET_SQ_prepare(sqlite3 *dbh, const struct GNUNET_SQ_PrepareStatement *ps)
Prepare all statements given in the (NULL,NULL)-terminated array at ps.
Definition: sq_prepare.c:59
Public ECC key (always for Curve25519) encoded in a format suitable for network transmission and ECDS...
#define GNUNET_log(kind,...)
int GNUNET_CONFIGURATION_get_value_filename(const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section, const char *option, char **value)
Get a configuration value that should be the name of a file or directory.
struct GNUNET_SQ_QueryParam GNUNET_SQ_query_param_fixed_size(const void *ptr, size_t ptr_size)
Generate query parameter for a buffer ptr of ptr_size bytes.
Time for absolute times used by GNUnet, in microseconds.
#define GNUNET_YES
Definition: gnunet_common.h:77
#define GNUNET_SQ_query_param_end
End of query parameter specification.
Definition: gnunet_sq_lib.h:85
const char * GNUNET_STRINGS_absolute_time_to_string(struct GNUNET_TIME_Absolute t)
Like asctime, except for GNUnet time.
Definition: strings.c:741
#define GNUNET_log_from(kind, comp,...)
sqlite3_stmt * lookup_block
Precompiled SQL for looking up a block.
struct GNUNET_CRYPTO_EccSignaturePurpose purpose
Number of bytes signed; also specifies the number of bytes of encrypted data that follow...
GNUNET_PEERSTORE_Processor iter
Iterator.
common internal definitions for namecache service
#define LOG_SQLITE(db, level, cmd)
Log an error message at log-level &#39;level&#39; that indicates a failure of the command &#39;cmd&#39; on file &#39;file...
struct GNUNET_CRYPTO_EcdsaPublicKey derived_key
Derived key used for signing; hash of this is the query.
struct GNUNET_SQ_ExecuteStatement GNUNET_SQ_make_try_execute(const char *sql)
Create a struct GNUNET_SQ_ExecuteStatement where errors should be tolerated.
Definition: sq_exec.c:56
static int namecache_sqlite_lookup_block(void *cls, const struct GNUNET_HashCode *query, GNUNET_NAMECACHE_BlockCallback iter, void *iter_cls)
Get the block for a particular zone and label in the datastore.
#define GNUNET_free(ptr)
Wrapper around free.
static void database_shutdown(struct Plugin *plugin)
Shutdown database connection and associate data structures.