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 {
62 
63  const struct GNUNET_CONFIGURATION_Handle *cfg;
64 
68  char *fn;
69 
73  sqlite3 *dbh;
74 
78  sqlite3_stmt *cache_block;
79 
83  sqlite3_stmt *delete_block;
84 
88  sqlite3_stmt *lookup_block;
89 
93  sqlite3_stmt *expire_blocks;
94 
95 };
96 
97 
106 static int
108 {
109  struct GNUNET_SQ_ExecuteStatement es[] = {
110  GNUNET_SQ_make_try_execute ("PRAGMA temp_store=MEMORY"),
111  GNUNET_SQ_make_try_execute ("PRAGMA synchronous=NORMAL"),
112  GNUNET_SQ_make_try_execute ("PRAGMA legacy_file_format=OFF"),
113  GNUNET_SQ_make_try_execute ("PRAGMA auto_vacuum=INCREMENTAL"),
114  GNUNET_SQ_make_try_execute ("PRAGMA encoding=\"UTF-8\""),
115  GNUNET_SQ_make_try_execute ("PRAGMA locking_mode=EXCLUSIVE"),
116  GNUNET_SQ_make_try_execute ("PRAGMA page_size=4092"),
117  GNUNET_SQ_make_try_execute ("PRAGMA journal_mode=WAL"),
118  GNUNET_SQ_make_execute ("CREATE TABLE IF NOT EXISTS ns096blocks ("
119  " query BLOB NOT NULL,"
120  " block BLOB NOT NULL,"
121  " expiration_time INT8 NOT NULL"
122  ")"),
123  GNUNET_SQ_make_execute ("CREATE INDEX IF NOT EXISTS ir_query_hash "
124  "ON ns096blocks (query,expiration_time)"),
125  GNUNET_SQ_make_execute ("CREATE INDEX IF NOT EXISTS ir_block_expiration "
126  "ON ns096blocks (expiration_time)"),
128  };
129  struct GNUNET_SQ_PrepareStatement ps[] = {
130  GNUNET_SQ_make_prepare ("INSERT INTO ns096blocks (query,block,expiration_time) VALUES (?, ?, ?)",
131  &plugin->cache_block),
132  GNUNET_SQ_make_prepare ("DELETE FROM ns096blocks WHERE expiration_time<?",
133  &plugin->expire_blocks),
134  GNUNET_SQ_make_prepare ("DELETE FROM ns096blocks WHERE query=? AND expiration_time<=?",
135  &plugin->delete_block),
136  GNUNET_SQ_make_prepare ("SELECT block FROM ns096blocks WHERE query=? "
137  "ORDER BY expiration_time DESC LIMIT 1",
138  &plugin->lookup_block),
140  };
141  char *afsdir;
142 
143  if (GNUNET_OK !=
145  "namecache-sqlite",
146  "FILENAME",
147  &afsdir))
148  {
150  "namecache-sqlite",
151  "FILENAME");
152  return GNUNET_SYSERR;
153  }
154  if (GNUNET_OK !=
155  GNUNET_DISK_file_test (afsdir))
156  {
157  if (GNUNET_OK !=
159  {
160  GNUNET_break (0);
161  GNUNET_free (afsdir);
162  return GNUNET_SYSERR;
163  }
164  }
165  /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
166  plugin->fn = afsdir;
167 
168  /* Open database and precompile statements */
169  if (SQLITE_OK !=
170  sqlite3_open (plugin->fn, &plugin->dbh))
171  {
173  _("Unable to initialize SQLite: %s.\n"),
174  sqlite3_errmsg (plugin->dbh));
175  return GNUNET_SYSERR;
176  }
177  if (GNUNET_OK !=
179  es))
180  {
181  GNUNET_break (0);
183  _("Failed to setup database at `%s'\n"),
184  plugin->fn);
185  return GNUNET_SYSERR;
186  }
187  GNUNET_break (SQLITE_OK ==
188  sqlite3_busy_timeout (plugin->dbh,
189  BUSY_TIMEOUT_MS));
190 
191  if (GNUNET_OK !=
192  GNUNET_SQ_prepare (plugin->dbh,
193  ps))
194  {
195  GNUNET_break (0);
197  _("Failed to setup database at `%s'\n"),
198  plugin->fn);
199  return GNUNET_SYSERR;
200  }
201 
202  return GNUNET_OK;
203 }
204 
205 
211 static void
213 {
214  int result;
215  sqlite3_stmt *stmt;
216 
217  if (NULL != plugin->cache_block)
218  sqlite3_finalize (plugin->cache_block);
219  if (NULL != plugin->lookup_block)
220  sqlite3_finalize (plugin->lookup_block);
221  if (NULL != plugin->expire_blocks)
222  sqlite3_finalize (plugin->expire_blocks);
223  if (NULL != plugin->delete_block)
224  sqlite3_finalize (plugin->delete_block);
225  result = sqlite3_close (plugin->dbh);
226  if (result == SQLITE_BUSY)
227  {
229  _("Tried to close sqlite without finalizing all prepared statements.\n"));
230  stmt = sqlite3_next_stmt (plugin->dbh,
231  NULL);
232  while (stmt != NULL)
233  {
235  "sqlite",
236  "Closing statement %p\n",
237  stmt);
238  result = sqlite3_finalize (stmt);
239  if (result != SQLITE_OK)
241  "sqlite",
242  "Failed to close statement %p: %d\n",
243  stmt,
244  result);
245  stmt = sqlite3_next_stmt (plugin->dbh,
246  NULL);
247  }
248  result = sqlite3_close (plugin->dbh);
249  }
250  if (SQLITE_OK != result)
251  LOG_SQLITE (plugin,
253  "sqlite3_close");
254 
255  GNUNET_free_non_null (plugin->fn);
256 }
257 
258 
264 static void
266 {
267  struct GNUNET_TIME_Absolute now;
268  struct GNUNET_SQ_QueryParam params[] = {
271  };
272  int n;
273 
274  now = GNUNET_TIME_absolute_get ();
275  if (GNUNET_OK !=
276  GNUNET_SQ_bind (plugin->expire_blocks,
277  params))
278  {
279  LOG_SQLITE (plugin,
281  "sqlite3_bind_XXXX");
282  GNUNET_SQ_reset (plugin->dbh,
283  plugin->expire_blocks);
284  return;
285  }
286  n = sqlite3_step (plugin->expire_blocks);
287  GNUNET_SQ_reset (plugin->dbh,
288  plugin->expire_blocks);
289  switch (n)
290  {
291  case SQLITE_DONE:
293  "sqlite",
294  "Records expired\n");
295  return;
296  case SQLITE_BUSY:
297  LOG_SQLITE (plugin,
299  "sqlite3_step");
300  return;
301  default:
302  LOG_SQLITE (plugin,
304  "sqlite3_step");
305  return;
306  }
307 }
308 
309 
317 static int
319  const struct GNUNET_GNSRECORD_Block *block)
320 {
321  static struct GNUNET_TIME_Absolute last_expire;
322  struct Plugin *plugin = cls;
323  struct GNUNET_HashCode query;
324  struct GNUNET_TIME_Absolute expiration;
325  size_t block_size = ntohl (block->purpose.size) +
326  sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
327  sizeof (struct GNUNET_CRYPTO_EcdsaSignature);
328  struct GNUNET_SQ_QueryParam del_params[] = {
332  };
333  struct GNUNET_SQ_QueryParam ins_params[] = {
336  block_size),
339  };
340  int n;
341 
342  /* run expiration of old cache entries once per hour */
343  if (GNUNET_TIME_absolute_get_duration (last_expire).rel_value_us >
344  GNUNET_TIME_UNIT_HOURS.rel_value_us)
345  {
346  last_expire = GNUNET_TIME_absolute_get ();
348  }
350  sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
351  &query);
352  expiration = GNUNET_TIME_absolute_ntoh (block->expiration_time);
354  "Caching new version of block %s (expires %s)\n",
355  GNUNET_h2s (&query),
357  if (block_size > 64 * 65536)
358  {
359  GNUNET_break (0);
360  return GNUNET_SYSERR;
361  }
362 
363  /* delete old version of the block */
364  if (GNUNET_OK !=
365  GNUNET_SQ_bind (plugin->delete_block,
366  del_params))
367  {
369  "sqlite3_bind_XXXX");
370  GNUNET_SQ_reset (plugin->dbh,
371  plugin->delete_block);
372  return GNUNET_SYSERR;
373  }
374  n = sqlite3_step (plugin->delete_block);
375  switch (n)
376  {
377  case SQLITE_DONE:
379  "sqlite",
380  "Old block deleted\n");
381  break;
382  case SQLITE_BUSY:
383  LOG_SQLITE (plugin,
385  "sqlite3_step");
386  break;
387  default:
388  LOG_SQLITE (plugin,
390  "sqlite3_step");
391  break;
392  }
393  GNUNET_SQ_reset (plugin->dbh,
394  plugin->delete_block);
395 
396  /* insert new version of the block */
397  if (GNUNET_OK !=
398  GNUNET_SQ_bind (plugin->cache_block,
399  ins_params))
400  {
401  LOG_SQLITE (plugin,
403  "sqlite3_bind_XXXX");
404  GNUNET_SQ_reset (plugin->dbh,
405  plugin->cache_block);
406  return GNUNET_SYSERR;
407 
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  case SQLITE_BUSY:
422  LOG_SQLITE (plugin,
424  "sqlite3_step");
425  return GNUNET_NO;
426  default:
427  LOG_SQLITE (plugin,
429  "sqlite3_step");
430  return GNUNET_SYSERR;
431  }
432 }
433 
434 
445 static int
447  const struct GNUNET_HashCode *query,
449  void *iter_cls)
450 {
451  struct Plugin *plugin = cls;
452  int ret;
453  int sret;
454  size_t block_size;
455  const struct GNUNET_GNSRECORD_Block *block;
456  struct GNUNET_SQ_QueryParam params[] = {
459  };
460  struct GNUNET_SQ_ResultSpec rs[] = {
461  GNUNET_SQ_result_spec_variable_size ((void **) &block,
462  &block_size),
464  };
465 
466  if (GNUNET_OK !=
467  GNUNET_SQ_bind (plugin->lookup_block,
468  params))
469  {
470  LOG_SQLITE (plugin,
472  "sqlite3_bind_XXXX");
473  GNUNET_SQ_reset (plugin->dbh,
474  plugin->lookup_block);
475  return GNUNET_SYSERR;
476  }
477  ret = GNUNET_NO;
478  if (SQLITE_ROW ==
479  (sret = sqlite3_step (plugin->lookup_block)))
480  {
481  if (GNUNET_OK !=
483  rs))
484  {
485  GNUNET_break (0);
486  ret = GNUNET_SYSERR;
487  }
488  else if ( (block_size < sizeof (struct GNUNET_GNSRECORD_Block)) ||
489  (ntohl (block->purpose.size) +
490  sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
491  sizeof (struct GNUNET_CRYPTO_EcdsaSignature) != block_size) )
492  {
493  GNUNET_break (0);
495  ret = GNUNET_SYSERR;
496  }
497  else
498  {
500  "Found block under derived key `%s'\n",
501  GNUNET_h2s_full (query));
502  iter (iter_cls,
503  block);
505  ret = GNUNET_YES;
506  }
507  }
508  else
509  {
510  if (SQLITE_DONE != sret)
511  {
512  LOG_SQLITE (plugin,
514  "sqlite_step");
515  ret = GNUNET_SYSERR;
516  }
517  else
518  {
520  "No block found under derived key `%s'\n",
521  GNUNET_h2s_full (query));
522  }
523  }
524  GNUNET_SQ_reset (plugin->dbh,
525  plugin->lookup_block);
526  return ret;
527 }
528 
529 
536 void *
538 {
539  static struct Plugin plugin;
540  const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
542 
543  if (NULL != plugin.cfg)
544  return NULL; /* can only initialize once! */
545  memset (&plugin, 0, sizeof (struct Plugin));
546  plugin.cfg = cfg;
547  if (GNUNET_OK != database_setup (&plugin))
548  {
549  database_shutdown (&plugin);
550  return NULL;
551  }
553  api->cls = &plugin;
557  _("Sqlite database running\n"));
558  return api;
559 }
560 
561 
568 void *
570 {
572  struct Plugin *plugin = api->cls;
573 
574  database_shutdown (plugin);
575  plugin->cfg = NULL;
576  GNUNET_free (api);
578  "sqlite plugin is finished\n");
579  return NULL;
580 }
581 
582 /* 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:669
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:670
#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:833
#define GNUNET_SQ_result_spec_end
End of result parameter specification.
#define GNUNET_NO
Definition: gnunet_common.h:81
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:78
#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:47
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:208
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:79
#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:85
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:80
#define GNUNET_SQ_query_param_end
End of query parameter specification.
Definition: gnunet_sq_lib.h:87
const char * GNUNET_STRINGS_absolute_time_to_string(struct GNUNET_TIME_Absolute t)
Like asctime, except for GNUnet time.
Definition: strings.c:792
#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.