GNUnet  0.10.x
plugin_peerstore_sqlite.c
Go to the documentation of this file.
1 /*
2  * This file is part of GNUnet
3  * Copyright (C) 2013, 2017 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 
28 #include "platform.h"
31 #include "gnunet_sq_lib.h"
32 #include "peerstore.h"
33 #include <sqlite3.h>
34 
45 #define BUSY_TIMEOUT_MS 1000
46 
52 #define LOG_SQLITE(db, level, cmd) do { GNUNET_log_from(level, "peerstore-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, "peerstore-sqlite", __VA_ARGS__)
55 
59 struct Plugin {
63  const struct GNUNET_CONFIGURATION_Handle *cfg;
64 
68  char *fn;
69 
73  sqlite3 *dbh;
74 
78  sqlite3_stmt *insert_peerstoredata;
79 
83  sqlite3_stmt *select_peerstoredata;
84 
89 
94 
99 
104  sqlite3_stmt *expire_peerstoredata;
105 
110  sqlite3_stmt *delete_peerstoredata;
111 };
112 
113 
123 static int
125  const char *sub_system,
126  const struct GNUNET_PeerIdentity *peer,
127  const char *key)
128 {
129  struct Plugin *plugin = cls;
130  sqlite3_stmt *stmt = plugin->delete_peerstoredata;
131  struct GNUNET_SQ_QueryParam params[] = {
132  GNUNET_SQ_query_param_string(sub_system),
136  };
137  int ret;
138 
139  if (GNUNET_OK !=
140  GNUNET_SQ_bind(stmt,
141  params))
142  {
143  LOG_SQLITE(plugin,
145  "sqlite3_bind");
146  GNUNET_SQ_reset(plugin->dbh,
147  stmt);
148  return GNUNET_SYSERR;
149  }
150  if (SQLITE_DONE !=
151  sqlite3_step(stmt))
152  {
153  LOG_SQLITE(plugin,
155  "sqlite3_step");
156  ret = GNUNET_SYSERR;
157  }
158  else
159  {
160  ret = sqlite3_changes(plugin->dbh);
161  }
162  GNUNET_SQ_reset(plugin->dbh,
163  stmt);
164  return ret;
165 }
166 
167 
178 static int
181  void *cont_cls)
182 {
183  struct Plugin *plugin = cls;
184  sqlite3_stmt *stmt = plugin->expire_peerstoredata;
185  struct GNUNET_SQ_QueryParam params[] = {
188  };
189 
190  if (GNUNET_OK !=
191  GNUNET_SQ_bind(stmt,
192  params))
193  {
194  LOG_SQLITE(plugin,
196  "sqlite3_bind");
197  GNUNET_SQ_reset(plugin->dbh,
198  stmt);
199  return GNUNET_SYSERR;
200  }
201  if (SQLITE_DONE != sqlite3_step(stmt))
202  {
203  LOG_SQLITE(plugin,
205  "sqlite3_step");
206  GNUNET_SQ_reset(plugin->dbh,
207  stmt);
208  return GNUNET_SYSERR;
209  }
210  if (NULL != cont)
211  cont(cont_cls,
212  sqlite3_changes(plugin->dbh));
213  GNUNET_SQ_reset(plugin->dbh,
214  stmt);
215  return GNUNET_OK;
216 }
217 
218 
233 static int
235  const char *sub_system,
236  const struct GNUNET_PeerIdentity *peer,
237  const char *key,
239  void *iter_cls)
240 {
241  struct Plugin *plugin = cls;
242  sqlite3_stmt *stmt;
243  int err = 0;
244  int sret;
245  struct GNUNET_PEERSTORE_Record rec;
246 
248  "Executing iterate request on sqlite db.\n");
249  if (NULL == peer)
250  {
251  if (NULL == key)
252  {
253  struct GNUNET_SQ_QueryParam params[] = {
254  GNUNET_SQ_query_param_string(sub_system),
256  };
257 
258  stmt = plugin->select_peerstoredata;
259  err = GNUNET_SQ_bind(stmt,
260  params);
261  }
262  else
263  {
264  struct GNUNET_SQ_QueryParam params[] = {
265  GNUNET_SQ_query_param_string(sub_system),
268  };
269 
270  stmt = plugin->select_peerstoredata_by_key;
271  err = GNUNET_SQ_bind(stmt,
272  params);
273  }
274  }
275  else
276  {
277  if (NULL == key)
278  {
279  struct GNUNET_SQ_QueryParam params[] = {
280  GNUNET_SQ_query_param_string(sub_system),
283  };
284 
285  stmt = plugin->select_peerstoredata_by_pid;
286  err = GNUNET_SQ_bind(stmt,
287  params);
288  }
289  else
290  {
291  struct GNUNET_SQ_QueryParam params[] = {
292  GNUNET_SQ_query_param_string(sub_system),
296  };
297 
298  stmt = plugin->select_peerstoredata_by_all;
299  err = GNUNET_SQ_bind(stmt,
300  params);
301  }
302  }
303 
304  if (GNUNET_OK != err)
305  {
306  LOG_SQLITE(plugin,
308  "sqlite3_bind_XXXX");
309  GNUNET_SQ_reset(plugin->dbh,
310  stmt);
311  return GNUNET_SYSERR;
312  }
313 
314  err = 0;
315  while (SQLITE_ROW == (sret = sqlite3_step(stmt)))
316  {
318  "Returning a matched record.\n");
319  struct GNUNET_SQ_ResultSpec rs[] = {
326  };
327 
328  if (GNUNET_OK !=
330  rs))
331  {
332  GNUNET_break(0);
333  break;
334  }
335  if (NULL != iter)
336  iter(iter_cls,
337  &rec,
338  NULL);
340  }
341  if (SQLITE_DONE != sret)
342  {
343  LOG_SQLITE(plugin,
345  "sqlite_step");
346  err = 1;
347  }
348  GNUNET_SQ_reset(plugin->dbh,
349  stmt);
350  if (NULL != iter)
351  iter(iter_cls,
352  NULL,
353  err ? "sqlite error" : NULL);
354  return GNUNET_OK;
355 }
356 
357 
375 static int
377  const char *sub_system,
378  const struct GNUNET_PeerIdentity *peer,
379  const char *key,
380  const void *value,
381  size_t size,
382  struct GNUNET_TIME_Absolute expiry,
385  void *cont_cls)
386 {
387  struct Plugin *plugin = cls;
388  sqlite3_stmt *stmt = plugin->insert_peerstoredata;
389  struct GNUNET_SQ_QueryParam params[] = {
390  GNUNET_SQ_query_param_string(sub_system),
396  };
397 
399  {
401  sub_system,
402  peer,
403  key);
404  }
405  if (GNUNET_OK !=
406  GNUNET_SQ_bind(stmt,
407  params))
408  LOG_SQLITE(plugin,
410  "sqlite3_bind");
411  else if (SQLITE_DONE != sqlite3_step(stmt))
412  {
413  LOG_SQLITE(plugin,
415  "sqlite3_step");
416  }
417  GNUNET_SQ_reset(plugin->dbh,
418  stmt);
419  if (NULL != cont)
420  cont(cont_cls,
421  GNUNET_OK);
422  return GNUNET_OK;
423 }
424 
425 
433 static int
434 sql_exec(sqlite3 *dbh,
435  const char *sql)
436 {
437  int result;
438 
439  result = sqlite3_exec(dbh,
440  sql,
441  NULL,
442  NULL,
443  NULL);
445  "Executed `%s' / %d\n",
446  sql,
447  result);
448  if (SQLITE_OK != result)
450  _("Error executing SQL query: %s\n %s\n"),
451  sqlite3_errmsg(dbh),
452  sql);
453  return result;
454 }
455 
456 
465 static int
466 sql_prepare(sqlite3 *dbh,
467  const char *sql,
468  sqlite3_stmt ** stmt)
469 {
470  char *tail;
471  int result;
472 
473  result = sqlite3_prepare_v2(dbh,
474  sql,
475  strlen(sql),
476  stmt,
477  (const char **)&tail);
479  "Prepared `%s' / %p: %d\n",
480  sql,
481  *stmt,
482  result);
483  if (SQLITE_OK != result)
485  _("Error preparing SQL query: %s\n %s\n"),
486  sqlite3_errmsg(dbh),
487  sql);
488  return result;
489 }
490 
491 
500 static int
502 {
503  char *filename;
504 
505  if (GNUNET_OK !=
507  "peerstore-sqlite",
508  "FILENAME",
509  &filename))
510  {
512  "peerstore-sqlite",
513  "FILENAME");
514  return GNUNET_SYSERR;
515  }
516  if (GNUNET_OK != GNUNET_DISK_file_test(filename))
517  {
519  {
520  GNUNET_break(0);
521  GNUNET_free(filename);
522  return GNUNET_SYSERR;
523  }
524  }
525  /* filename should be UTF-8-encoded. If it isn't, it's a bug */
526  plugin->fn = filename;
527  /* Open database and precompile statements */
528  if (SQLITE_OK != sqlite3_open(plugin->fn,
529  &plugin->dbh))
530  {
532  _("Unable to initialize SQLite: %s.\n"),
533  sqlite3_errmsg(plugin->dbh));
534  return GNUNET_SYSERR;
535  }
536  sql_exec(plugin->dbh,
537  "PRAGMA temp_store=MEMORY");
538  sql_exec(plugin->dbh,
539  "PRAGMA synchronous=OFF");
540  sql_exec(plugin->dbh,
541  "PRAGMA legacy_file_format=OFF");
542  sql_exec(plugin->dbh,
543  "PRAGMA auto_vacuum=INCREMENTAL");
544  sql_exec(plugin->dbh,
545  "PRAGMA encoding=\"UTF-8\"");
546  sql_exec(plugin->dbh,
547  "PRAGMA page_size=4096");
548  sqlite3_busy_timeout(plugin->dbh,
550  /* Create tables */
551  sql_exec(plugin->dbh,
552  "CREATE TABLE IF NOT EXISTS peerstoredata (\n"
553  " sub_system TEXT NOT NULL,\n"
554  " peer_id BLOB NOT NULL,\n"
555  " key TEXT NOT NULL,\n"
556  " value BLOB NULL,\n"
557  " expiry INT8 NOT NULL" ");");
558  /* Create Indices */
559  if (SQLITE_OK !=
560  sqlite3_exec(plugin->dbh,
561  "CREATE INDEX IF NOT EXISTS peerstoredata_key_index ON peerstoredata (sub_system, peer_id, key)",
562  NULL,
563  NULL,
564  NULL))
565  {
567  _("Unable to create indices: %s.\n"),
568  sqlite3_errmsg(plugin->dbh));
569  return GNUNET_SYSERR;
570  }
571  /* Prepare statements */
572 
573  sql_prepare(plugin->dbh,
574  "INSERT INTO peerstoredata (sub_system, peer_id, key, value, expiry)"
575  " VALUES (?,?,?,?,?);",
576  &plugin->insert_peerstoredata);
577  sql_prepare(plugin->dbh,
578  "SELECT sub_system,peer_id,key,value,expiry FROM peerstoredata"
579  " WHERE sub_system = ?",
580  &plugin->select_peerstoredata);
581  sql_prepare(plugin->dbh,
582  "SELECT sub_system,peer_id,key,value,expiry FROM peerstoredata"
583  " WHERE sub_system = ?"
584  " AND peer_id = ?",
585  &plugin->select_peerstoredata_by_pid);
586  sql_prepare(plugin->dbh,
587  "SELECT sub_system,peer_id,key,value,expiry FROM peerstoredata"
588  " WHERE sub_system = ?"
589  " AND key = ?",
590  &plugin->select_peerstoredata_by_key);
591  sql_prepare(plugin->dbh,
592  "SELECT sub_system,peer_id,key,value,expiry FROM peerstoredata"
593  " WHERE sub_system = ?"
594  " AND peer_id = ?" " AND key = ?",
595  &plugin->select_peerstoredata_by_all);
596  sql_prepare(plugin->dbh,
597  "DELETE FROM peerstoredata"
598  " WHERE expiry < ?",
599  &plugin->expire_peerstoredata);
600  sql_prepare(plugin->dbh,
601  "DELETE FROM peerstoredata"
602  " WHERE sub_system = ?"
603  " AND peer_id = ?" " AND key = ?",
604  &plugin->delete_peerstoredata);
605  return GNUNET_OK;
606 }
607 
608 
614 static void
616 {
617  int result;
618  sqlite3_stmt *stmt;
619 
620  while (NULL != (stmt = sqlite3_next_stmt(plugin->dbh,
621  NULL)))
622  {
623  result = sqlite3_finalize(stmt);
624  if (SQLITE_OK != result)
626  "Failed to close statement %p: %d\n",
627  stmt,
628  result);
629  }
630  if (SQLITE_OK != sqlite3_close(plugin->dbh))
631  LOG_SQLITE(plugin,
633  "sqlite3_close");
634  GNUNET_free_non_null(plugin->fn);
635 }
636 
637 
644 void *
646 {
647  static struct Plugin plugin;
648  const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
650 
651  if (NULL != plugin.cfg)
652  return NULL; /* can only initialize once! */
653  memset(&plugin,
654  0,
655  sizeof(struct Plugin));
656  plugin.cfg = cfg;
657  if (GNUNET_OK != database_setup(&plugin))
658  {
659  database_shutdown(&plugin);
660  return NULL;
661  }
663  api->cls = &plugin;
668  "Sqlite plugin is running\n");
669  return api;
670 }
671 
672 
679 void *
681 {
683  struct Plugin *plugin = api->cls;
684 
685  database_shutdown(plugin);
686  plugin->cfg = NULL;
687  GNUNET_free(api);
689  "Sqlite plugin is finished\n");
690  return NULL;
691 }
692 
693 /* end of plugin_peerstore_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
static int peerstore_sqlite_delete_records(void *cls, const char *sub_system, const struct GNUNET_PeerIdentity *peer, const char *key)
Delete records with the given key.
static int sql_prepare(sqlite3 *dbh, const char *sql, sqlite3_stmt **stmt)
Prepare a SQL statement.
void(* GNUNET_PEERSTORE_Processor)(void *cls, const struct GNUNET_PEERSTORE_Record *record, const char *emsg)
Function called by PEERSTORE for each matching record.
sqlite3_stmt * select_peerstoredata_by_key
Precompiled SQL for selecting from peerstoredata.
Description of a DB result cell.
sqlite3_stmt * select_peerstoredata
Precompiled SQL for selecting from peerstoredata.
struct GNUNET_PeerIdentity peer
Peer Identity.
sqlite3_stmt * delete_peerstoredata
Precompiled SQL for deleting records with given key.
Delete any previous values for the given key before storing the given value.
sqlite3_stmt * select_peerstoredata_by_pid
Precompiled SQL for selecting from peerstoredata.
size_t value_size
Size of value BLOB.
uint32_t options
Options for addresses.
#define GNUNET_SQ_query_param_auto_from_type(x)
Generate fixed-size query parameter with size determined by variable type.
static int sql_exec(sqlite3 *dbh, const char *sql)
Prepare a SQL statement.
char * key
TLS key.
const struct GNUNET_CONFIGURATION_Handle * cfg
Our configuration.
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.
sqlite3_stmt * expire_peerstoredata
Precompiled SQL for deleting expired records from peerstoredata.
#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.
static int peerstore_sqlite_expire_records(void *cls, struct GNUNET_TIME_Absolute now, GNUNET_PEERSTORE_Continuation cont, void *cont_cls)
Delete expired records (expiry < now)
int(* store_record)(void *cls, const char *sub_system, const struct GNUNET_PeerIdentity *peer, const char *key, const void *value, size_t size, struct GNUNET_TIME_Absolute expiry, enum GNUNET_PEERSTORE_StoreOption options, GNUNET_PEERSTORE_Continuation cont, void *cont_cls)
Store a record in the peerstore.
#define GNUNET_SQ_result_spec_auto_from_type(dst)
We expect a fixed-size result, with size determined by the type of * dst
static int ret
Final status code.
Definition: gnunet-arm.c:89
#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 * cls
Closure to pass to all plugin functions.
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur...
struct GNUNET_SQ_ResultSpec GNUNET_SQ_result_spec_absolute_time(struct GNUNET_TIME_Absolute *at)
Absolute time expected.
struct GNUNET_BLOCK_PluginFunctions * api
Plugin API.
Definition: block.c:46
#define _(String)
GNU gettext support macro.
Definition: platform.h:181
IPC messages.
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
char * key
Record key string.
int(* expire_records)(void *cls, struct GNUNET_TIME_Absolute now, GNUNET_PEERSTORE_Continuation cont, void *cont_cls)
Delete expired records (expiry < now)
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 * value
Record value BLOB.
void GNUNET_log_config_missing(enum GNUNET_ErrorType kind, const char *section, const char *option)
Log error message about missing configuration option.
static char * value
Value of the record to add/remove.
void * libgnunet_plugin_peerstore_sqlite_init(void *cls)
Entry point for the plugin.
#define LOG(kind,...)
static char * filename
GNUNET_PEERSTORE_StoreOption
Options for storing values in PEERSTORE.
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.
struct returned by the initialization function of the plugin
int GNUNET_SQ_bind(sqlite3_stmt *stmt, const struct GNUNET_SQ_QueryParam *params)
Execute binding operations for a prepared statement.
Definition: sq.c:37
#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_TESTBED_Peer * peer
The peer associated with this model.
#define GNUNET_SYSERR
Definition: gnunet_common.h:76
void(* GNUNET_PEERSTORE_Continuation)(void *cls, int success)
Continuation called with a status result.
void * iter_cls
Iterator cls.
int(* iterate_records)(void *cls, const char *sub_system, const struct GNUNET_PeerIdentity *peer, const char *key, GNUNET_PEERSTORE_Processor iter, void *iter_cls)
Iterate over the records given an optional peer id and/or key.
helper functions for Sqlite3 DB interactions
struct GNUNET_SQ_QueryParam GNUNET_SQ_query_param_string(const char *ptr)
Generate query parameter for a string.
Description of a DB query parameter.
Definition: gnunet_sq_lib.h:54
char * fn
Filename used for the DB.
static int peerstore_sqlite_store_record(void *cls, const char *sub_system, const struct GNUNET_PeerIdentity *peer, const char *key, const void *value, size_t size, struct GNUNET_TIME_Absolute expiry, enum GNUNET_PEERSTORE_StoreOption options, GNUNET_PEERSTORE_Continuation cont, void *cont_cls)
Store a record in the peerstore.
The identity of the host (wraps the signing key of the peer).
void GNUNET_SQ_reset(sqlite3 *dbh, sqlite3_stmt *stmt)
Reset stmt and log error.
Definition: sq.c:132
unsigned long long size
Size of all values we&#39;re storing.
configuration data
Definition: configuration.c:83
struct GNUNET_TIME_Absolute expiry
Expiry time of entry.
char * sub_system
Responsible sub system string.
Handle for a plugin.
Definition: block.c:37
PGconn * dbh
Native Postgres database handle.
sqlite3_stmt * select_peerstoredata_by_all
Precompiled SQL for selecting from peerstoredata.
Single PEERSTORE record.
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.
sqlite3_stmt * insert_peerstoredata
Precompiled SQL for inserting into peerstoredata.
static void database_shutdown(struct Plugin *plugin)
Shutdown database connection and associate data structures.
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_SQ_query_param_end
End of query parameter specification.
Definition: gnunet_sq_lib.h:85
struct GNUNET_ATS_Session * tail
Tail of linked list of open sessions.
void * libgnunet_plugin_peerstore_sqlite_done(void *cls)
Exit point from the plugin.
struct GNUNET_SQ_ResultSpec GNUNET_SQ_result_spec_string(char **dst)
0-terminated string expected.
static int peerstore_sqlite_iterate_records(void *cls, const char *sub_system, const struct GNUNET_PeerIdentity *peer, const char *key, GNUNET_PEERSTORE_Processor iter, void *iter_cls)
Iterate over the records given an optional peer id and/or key.
GNUNET_PEERSTORE_Processor iter
Iterator.
static int database_setup(struct Plugin *plugin)
Initialize the database connections and associated data structures (create tables and indices as need...
#define GNUNET_free(ptr)
Wrapper around free.