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
60 {
61 
65  const struct GNUNET_CONFIGURATION_Handle *cfg;
66 
70  char *fn;
71 
75  sqlite3 *dbh;
76 
80  sqlite3_stmt *insert_peerstoredata;
81 
85  sqlite3_stmt *select_peerstoredata;
86 
91 
96 
101 
106  sqlite3_stmt *expire_peerstoredata;
107 
112  sqlite3_stmt *delete_peerstoredata;
113 
114 };
115 
116 
126 static int
128  const char *sub_system,
129  const struct GNUNET_PeerIdentity *peer,
130  const char *key)
131 {
132  struct Plugin *plugin = cls;
133  sqlite3_stmt *stmt = plugin->delete_peerstoredata;
134  struct GNUNET_SQ_QueryParam params[] = {
135  GNUNET_SQ_query_param_string (sub_system),
139  };
140  int ret;
141 
142  if (GNUNET_OK !=
143  GNUNET_SQ_bind (stmt,
144  params))
145  {
146  LOG_SQLITE (plugin,
148  "sqlite3_bind");
149  GNUNET_SQ_reset (plugin->dbh,
150  stmt);
151  return GNUNET_SYSERR;
152  }
153  if (SQLITE_DONE !=
154  sqlite3_step (stmt))
155  {
156  LOG_SQLITE (plugin,
158  "sqlite3_step");
159  ret = GNUNET_SYSERR;
160  }
161  else
162  {
163  ret = sqlite3_changes (plugin->dbh);
164  }
165  GNUNET_SQ_reset (plugin->dbh,
166  stmt);
167  return ret;
168 }
169 
170 
181 static int
184  void *cont_cls)
185 {
186  struct Plugin *plugin = cls;
187  sqlite3_stmt *stmt = plugin->expire_peerstoredata;
188  struct GNUNET_SQ_QueryParam params[] = {
191  };
192 
193  if (GNUNET_OK !=
194  GNUNET_SQ_bind (stmt,
195  params))
196  {
197  LOG_SQLITE (plugin,
199  "sqlite3_bind");
200  GNUNET_SQ_reset (plugin->dbh,
201  stmt);
202  return GNUNET_SYSERR;
203  }
204  if (SQLITE_DONE != sqlite3_step (stmt))
205  {
206  LOG_SQLITE (plugin,
208  "sqlite3_step");
209  GNUNET_SQ_reset (plugin->dbh,
210  stmt);
211  return GNUNET_SYSERR;
212  }
213  if (NULL != cont)
214  cont (cont_cls,
215  sqlite3_changes (plugin->dbh));
216  GNUNET_SQ_reset (plugin->dbh,
217  stmt);
218  return GNUNET_OK;
219 }
220 
221 
236 static int
238  const char *sub_system,
239  const struct GNUNET_PeerIdentity *peer,
240  const char *key,
242  void *iter_cls)
243 {
244  struct Plugin *plugin = cls;
245  sqlite3_stmt *stmt;
246  int err = 0;
247  int sret;
248  struct GNUNET_PEERSTORE_Record rec;
249 
251  "Executing iterate request on sqlite db.\n");
252  if (NULL == peer)
253  {
254  if (NULL == key)
255  {
256  struct GNUNET_SQ_QueryParam params[] = {
257  GNUNET_SQ_query_param_string (sub_system),
259  };
260 
261  stmt = plugin->select_peerstoredata;
262  err = GNUNET_SQ_bind (stmt,
263  params);
264  }
265  else
266  {
267  struct GNUNET_SQ_QueryParam params[] = {
268  GNUNET_SQ_query_param_string (sub_system),
271  };
272 
273  stmt = plugin->select_peerstoredata_by_key;
274  err = GNUNET_SQ_bind (stmt,
275  params);
276  }
277  }
278  else
279  {
280  if (NULL == key)
281  {
282  struct GNUNET_SQ_QueryParam params[] = {
283  GNUNET_SQ_query_param_string (sub_system),
286  };
287 
288  stmt = plugin->select_peerstoredata_by_pid;
289  err = GNUNET_SQ_bind (stmt,
290  params);
291  }
292  else
293  {
294  struct GNUNET_SQ_QueryParam params[] = {
295  GNUNET_SQ_query_param_string (sub_system),
299  };
300 
301  stmt = plugin->select_peerstoredata_by_all;
302  err = GNUNET_SQ_bind (stmt,
303  params);
304  }
305  }
306 
307  if (GNUNET_OK != err)
308  {
309  LOG_SQLITE (plugin,
311  "sqlite3_bind_XXXX");
312  GNUNET_SQ_reset (plugin->dbh,
313  stmt);
314  return GNUNET_SYSERR;
315  }
316 
317  err = 0;
318  while (SQLITE_ROW == (sret = sqlite3_step (stmt)))
319  {
321  "Returning a matched record.\n");
322  struct GNUNET_SQ_ResultSpec rs[] = {
329  };
330 
331  if (GNUNET_OK !=
333  rs))
334  {
335  GNUNET_break (0);
336  break;
337  }
338  if (NULL != iter)
339  iter (iter_cls,
340  &rec,
341  NULL);
343  }
344  if (SQLITE_DONE != sret)
345  {
346  LOG_SQLITE (plugin,
348  "sqlite_step");
349  err = 1;
350  }
351  GNUNET_SQ_reset (plugin->dbh,
352  stmt);
353  if (NULL != iter)
354  iter (iter_cls,
355  NULL,
356  err ? "sqlite error" : NULL);
357  return GNUNET_OK;
358 }
359 
360 
378 static int
380  const char *sub_system,
381  const struct GNUNET_PeerIdentity *peer,
382  const char *key,
383  const void *value,
384  size_t size,
385  struct GNUNET_TIME_Absolute expiry,
388  void *cont_cls)
389 {
390  struct Plugin *plugin = cls;
391  sqlite3_stmt *stmt = plugin->insert_peerstoredata;
392  struct GNUNET_SQ_QueryParam params[] = {
393  GNUNET_SQ_query_param_string (sub_system),
396  GNUNET_SQ_query_param_fixed_size (value, size),
399  };
400 
402  {
404  sub_system,
405  peer,
406  key);
407  }
408  if (GNUNET_OK !=
409  GNUNET_SQ_bind (stmt,
410  params))
411  LOG_SQLITE (plugin,
413  "sqlite3_bind");
414  else if (SQLITE_DONE != sqlite3_step (stmt))
415  {
416  LOG_SQLITE (plugin,
418  "sqlite3_step");
419  }
420  GNUNET_SQ_reset (plugin->dbh,
421  stmt);
422  if (NULL != cont)
423  cont (cont_cls,
424  GNUNET_OK);
425  return GNUNET_OK;
426 }
427 
428 
436 static int
437 sql_exec (sqlite3 *dbh,
438  const char *sql)
439 {
440  int result;
441 
442  result = sqlite3_exec (dbh,
443  sql,
444  NULL,
445  NULL,
446  NULL);
448  "Executed `%s' / %d\n",
449  sql,
450  result);
451  if (SQLITE_OK != result)
453  _("Error executing SQL query: %s\n %s\n"),
454  sqlite3_errmsg (dbh),
455  sql);
456  return result;
457 }
458 
459 
468 static int
469 sql_prepare (sqlite3 *dbh,
470  const char *sql,
471  sqlite3_stmt ** stmt)
472 {
473  char *tail;
474  int result;
475 
476  result = sqlite3_prepare_v2 (dbh,
477  sql,
478  strlen (sql),
479  stmt,
480  (const char **) &tail);
482  "Prepared `%s' / %p: %d\n",
483  sql,
484  *stmt,
485  result);
486  if (SQLITE_OK != result)
488  _("Error preparing SQL query: %s\n %s\n"),
489  sqlite3_errmsg (dbh),
490  sql);
491  return result;
492 }
493 
494 
503 static int
505 {
506  char *filename;
507 
508  if (GNUNET_OK !=
510  "peerstore-sqlite",
511  "FILENAME",
512  &filename))
513  {
515  "peerstore-sqlite",
516  "FILENAME");
517  return GNUNET_SYSERR;
518  }
519  if (GNUNET_OK != GNUNET_DISK_file_test (filename))
520  {
522  {
523  GNUNET_break (0);
524  GNUNET_free (filename);
525  return GNUNET_SYSERR;
526  }
527  }
528  /* filename should be UTF-8-encoded. If it isn't, it's a bug */
529  plugin->fn = filename;
530  /* Open database and precompile statements */
531  if (SQLITE_OK != sqlite3_open (plugin->fn,
532  &plugin->dbh))
533  {
535  _("Unable to initialize SQLite: %s.\n"),
536  sqlite3_errmsg (plugin->dbh));
537  return GNUNET_SYSERR;
538  }
539  sql_exec (plugin->dbh,
540  "PRAGMA temp_store=MEMORY");
541  sql_exec (plugin->dbh,
542  "PRAGMA synchronous=OFF");
543  sql_exec (plugin->dbh,
544  "PRAGMA legacy_file_format=OFF");
545  sql_exec (plugin->dbh,
546  "PRAGMA auto_vacuum=INCREMENTAL");
547  sql_exec (plugin->dbh,
548  "PRAGMA encoding=\"UTF-8\"");
549  sql_exec (plugin->dbh,
550  "PRAGMA page_size=4096");
551  sqlite3_busy_timeout (plugin->dbh,
553  /* Create tables */
554  sql_exec (plugin->dbh,
555  "CREATE TABLE IF NOT EXISTS peerstoredata (\n"
556  " sub_system TEXT NOT NULL,\n"
557  " peer_id BLOB NOT NULL,\n"
558  " key TEXT NOT NULL,\n"
559  " value BLOB NULL,\n"
560  " expiry INT8 NOT NULL" ");");
561  /* Create Indices */
562  if (SQLITE_OK !=
563  sqlite3_exec (plugin->dbh,
564  "CREATE INDEX IF NOT EXISTS peerstoredata_key_index ON peerstoredata (sub_system, peer_id, key)",
565  NULL,
566  NULL,
567  NULL))
568  {
570  _("Unable to create indices: %s.\n"),
571  sqlite3_errmsg (plugin->dbh));
572  return GNUNET_SYSERR;
573  }
574  /* Prepare statements */
575 
576  sql_prepare (plugin->dbh,
577  "INSERT INTO peerstoredata (sub_system, peer_id, key, value, expiry)"
578  " VALUES (?,?,?,?,?);",
579  &plugin->insert_peerstoredata);
580  sql_prepare (plugin->dbh,
581  "SELECT sub_system,peer_id,key,value,expiry FROM peerstoredata"
582  " WHERE sub_system = ?",
583  &plugin->select_peerstoredata);
584  sql_prepare (plugin->dbh,
585  "SELECT sub_system,peer_id,key,value,expiry FROM peerstoredata"
586  " WHERE sub_system = ?"
587  " AND peer_id = ?",
588  &plugin->select_peerstoredata_by_pid);
589  sql_prepare (plugin->dbh,
590  "SELECT sub_system,peer_id,key,value,expiry FROM peerstoredata"
591  " WHERE sub_system = ?"
592  " AND key = ?",
593  &plugin->select_peerstoredata_by_key);
594  sql_prepare (plugin->dbh,
595  "SELECT sub_system,peer_id,key,value,expiry FROM peerstoredata"
596  " WHERE sub_system = ?"
597  " AND peer_id = ?" " AND key = ?",
598  &plugin->select_peerstoredata_by_all);
599  sql_prepare (plugin->dbh,
600  "DELETE FROM peerstoredata"
601  " WHERE expiry < ?",
602  &plugin->expire_peerstoredata);
603  sql_prepare (plugin->dbh,
604  "DELETE FROM peerstoredata"
605  " WHERE sub_system = ?"
606  " AND peer_id = ?" " AND key = ?",
607  &plugin->delete_peerstoredata);
608  return GNUNET_OK;
609 }
610 
611 
617 static void
619 {
620  int result;
621  sqlite3_stmt *stmt;
622 
623  while (NULL != (stmt = sqlite3_next_stmt (plugin->dbh,
624  NULL)))
625  {
626  result = sqlite3_finalize (stmt);
627  if (SQLITE_OK != result)
629  "Failed to close statement %p: %d\n",
630  stmt,
631  result);
632  }
633  if (SQLITE_OK != sqlite3_close (plugin->dbh))
634  LOG_SQLITE (plugin,
636  "sqlite3_close");
637  GNUNET_free_non_null (plugin->fn);
638 }
639 
640 
647 void *
649 {
650  static struct Plugin plugin;
651  const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
653 
654  if (NULL != plugin.cfg)
655  return NULL; /* can only initialize once! */
656  memset (&plugin,
657  0,
658  sizeof (struct Plugin));
659  plugin.cfg = cfg;
660  if (GNUNET_OK != database_setup (&plugin))
661  {
662  database_shutdown (&plugin);
663  return NULL;
664  }
666  api->cls = &plugin;
671  "Sqlite plugin is running\n");
672  return api;
673 }
674 
675 
682 void *
684 {
686  struct Plugin *plugin = api->cls;
687 
688  database_shutdown (plugin);
689  plugin->cfg = NULL;
690  GNUNET_free (api);
692  "Sqlite plugin is finished\n");
693  return NULL;
694 }
695 
696 /* 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:669
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:833
#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: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.
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:47
#define _(String)
GNU gettext support macro.
Definition: platform.h:208
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:79
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:85
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:87
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.