GNUnet  0.20.0
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, \
53  "peerstore-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, "peerstore-sqlite", __VA_ARGS__)
62 
66 struct Plugin
67 {
71  const struct GNUNET_CONFIGURATION_Handle *cfg;
72 
76  char *fn;
77 
81  sqlite3 *dbh;
82 
86  sqlite3_stmt *insert_peerstoredata;
87 
91  sqlite3_stmt *select_peerstoredata;
92 
97 
102 
107 
112  sqlite3_stmt *expire_peerstoredata;
113 
118  sqlite3_stmt *delete_peerstoredata;
119 };
120 
121 
131 static int
133  const char *sub_system,
134  const struct GNUNET_PeerIdentity *peer,
135  const char *key)
136 {
137  struct Plugin *plugin = cls;
138  sqlite3_stmt *stmt = plugin->delete_peerstoredata;
139  struct GNUNET_SQ_QueryParam params[] = {
140  GNUNET_SQ_query_param_string (sub_system),
144  };
145  int ret;
146 
147  if (GNUNET_OK !=
148  GNUNET_SQ_bind (stmt,
149  params))
150  {
153  "sqlite3_bind");
154  GNUNET_SQ_reset (plugin->dbh,
155  stmt);
156  return GNUNET_SYSERR;
157  }
158  if (SQLITE_DONE !=
159  sqlite3_step (stmt))
160  {
163  "sqlite3_step");
164  ret = GNUNET_SYSERR;
165  }
166  else
167  {
168  ret = sqlite3_changes (plugin->dbh);
169  }
170  GNUNET_SQ_reset (plugin->dbh,
171  stmt);
172  return ret;
173 }
174 
175 
186 static int
189  void *cont_cls)
190 {
191  struct Plugin *plugin = cls;
192  sqlite3_stmt *stmt = plugin->expire_peerstoredata;
193  struct GNUNET_SQ_QueryParam params[] = {
196  };
197 
198  if (GNUNET_OK !=
199  GNUNET_SQ_bind (stmt,
200  params))
201  {
204  "sqlite3_bind");
205  GNUNET_SQ_reset (plugin->dbh,
206  stmt);
207  return GNUNET_SYSERR;
208  }
209  if (SQLITE_DONE != sqlite3_step (stmt))
210  {
213  "sqlite3_step");
214  GNUNET_SQ_reset (plugin->dbh,
215  stmt);
216  return GNUNET_SYSERR;
217  }
218  if (NULL != cont)
219  cont (cont_cls,
220  sqlite3_changes (plugin->dbh));
221  GNUNET_SQ_reset (plugin->dbh,
222  stmt);
223  return GNUNET_OK;
224 }
225 
226 
241 static int
243  const char *sub_system,
244  const struct GNUNET_PeerIdentity *peer,
245  const char *key,
247  void *iter_cls)
248 {
249  struct Plugin *plugin = cls;
250  sqlite3_stmt *stmt;
251  int err = 0;
252  int sret;
253  struct GNUNET_PEERSTORE_Record rec;
254 
256  "Executing iterate request on sqlite db.\n");
257  if (NULL == peer)
258  {
259  if (NULL == key)
260  {
261  struct GNUNET_SQ_QueryParam params[] = {
262  GNUNET_SQ_query_param_string (sub_system),
264  };
265 
266  stmt = plugin->select_peerstoredata;
267  err = GNUNET_SQ_bind (stmt,
268  params);
269  }
270  else
271  {
272  struct GNUNET_SQ_QueryParam params[] = {
273  GNUNET_SQ_query_param_string (sub_system),
276  };
277 
278  stmt = plugin->select_peerstoredata_by_key;
279  err = GNUNET_SQ_bind (stmt,
280  params);
281  }
282  }
283  else
284  {
285  if (NULL == key)
286  {
287  struct GNUNET_SQ_QueryParam params[] = {
288  GNUNET_SQ_query_param_string (sub_system),
291  };
292 
293  stmt = plugin->select_peerstoredata_by_pid;
294  err = GNUNET_SQ_bind (stmt,
295  params);
296  }
297  else
298  {
299  struct GNUNET_SQ_QueryParam params[] = {
300  GNUNET_SQ_query_param_string (sub_system),
304  };
305 
306  stmt = plugin->select_peerstoredata_by_all;
307  err = GNUNET_SQ_bind (stmt,
308  params);
309  }
310  }
311 
312  if (GNUNET_OK != err)
313  {
316  "sqlite3_bind_XXXX");
317  GNUNET_SQ_reset (plugin->dbh,
318  stmt);
319  return GNUNET_SYSERR;
320  }
321 
322  err = 0;
323  while (SQLITE_ROW == (sret = sqlite3_step (stmt)))
324  {
326  "Returning a matched record.\n");
327  struct GNUNET_SQ_ResultSpec rs[] = {
334  };
335 
336  if (GNUNET_OK !=
338  rs))
339  {
340  GNUNET_break (0);
341  break;
342  }
343  if (NULL != iter)
344  iter (iter_cls,
345  &rec,
346  NULL);
348  }
349  if (SQLITE_DONE != sret)
350  {
353  "sqlite_step");
354  err = 1;
355  }
356  GNUNET_SQ_reset (plugin->dbh,
357  stmt);
358  if (NULL != iter)
359  iter (iter_cls,
360  NULL,
361  err ? "sqlite error" : NULL);
362  return GNUNET_OK;
363 }
364 
365 
383 static int
385  const char *sub_system,
386  const struct GNUNET_PeerIdentity *peer,
387  const char *key,
388  const void *value,
389  size_t size,
390  struct GNUNET_TIME_Absolute expiry,
393  void *cont_cls)
394 {
395  struct Plugin *plugin = cls;
396  sqlite3_stmt *stmt = plugin->insert_peerstoredata;
397  struct GNUNET_SQ_QueryParam params[] = {
398  GNUNET_SQ_query_param_string (sub_system),
404  };
405 
407  {
409  sub_system,
410  peer,
411  key);
412  }
413  if (GNUNET_OK !=
414  GNUNET_SQ_bind (stmt,
415  params))
418  "sqlite3_bind");
419  else if (SQLITE_DONE != sqlite3_step (stmt))
420  {
423  "sqlite3_step");
424  }
425  GNUNET_SQ_reset (plugin->dbh,
426  stmt);
427  if (NULL != cont)
428  cont (cont_cls,
429  GNUNET_OK);
430  return GNUNET_OK;
431 }
432 
433 
441 static int
442 sql_exec (sqlite3 *dbh,
443  const char *sql)
444 {
445  int result;
446 
447  result = sqlite3_exec (dbh,
448  sql,
449  NULL,
450  NULL,
451  NULL);
453  "Executed `%s' / %d\n",
454  sql,
455  result);
456  if (SQLITE_OK != result)
458  _ ("Error executing SQL query: %s\n %s\n"),
459  sqlite3_errmsg (dbh),
460  sql);
461  return result;
462 }
463 
464 
473 static int
474 sql_prepare (sqlite3 *dbh,
475  const char *sql,
476  sqlite3_stmt **stmt)
477 {
478  char *tail;
479  int result;
480 
481  result = sqlite3_prepare_v2 (dbh,
482  sql,
483  strlen (sql),
484  stmt,
485  (const char **) &tail);
487  "Prepared `%s' / %p: %d\n",
488  sql,
489  *stmt,
490  result);
491  if (SQLITE_OK != result)
493  _ ("Error preparing SQL query: %s\n %s\n"),
494  sqlite3_errmsg (dbh),
495  sql);
496  return result;
497 }
498 
499 
508 static int
510 {
511  char *filename;
512 
513  if (GNUNET_OK !=
515  "peerstore-sqlite",
516  "FILENAME",
517  &filename))
518  {
520  "peerstore-sqlite",
521  "FILENAME");
522  return GNUNET_SYSERR;
523  }
525  {
527  {
528  GNUNET_break (0);
530  return GNUNET_SYSERR;
531  }
532  }
533  /* filename should be UTF-8-encoded. If it isn't, it's a bug */
534  plugin->fn = filename;
535  /* Open database and precompile statements */
536  if (SQLITE_OK != sqlite3_open (plugin->fn,
537  &plugin->dbh))
538  {
540  _ ("Unable to initialize SQLite: %s.\n"),
541  sqlite3_errmsg (plugin->dbh));
542  return GNUNET_SYSERR;
543  }
544  sql_exec (plugin->dbh,
545  "PRAGMA temp_store=MEMORY");
546  sql_exec (plugin->dbh,
547  "PRAGMA synchronous=OFF");
548  sql_exec (plugin->dbh,
549  "PRAGMA legacy_file_format=OFF");
550  sql_exec (plugin->dbh,
551  "PRAGMA auto_vacuum=INCREMENTAL");
552  sql_exec (plugin->dbh,
553  "PRAGMA encoding=\"UTF-8\"");
554  sql_exec (plugin->dbh,
555  "PRAGMA page_size=4096");
556  sqlite3_busy_timeout (plugin->dbh,
558  /* Create tables */
559  sql_exec (plugin->dbh,
560  "CREATE TABLE IF NOT EXISTS peerstoredata (\n"
561  " sub_system TEXT NOT NULL,\n"
562  " peer_id BLOB NOT NULL,\n"
563  " key TEXT NOT NULL,\n"
564  " value BLOB NULL,\n"
565  " expiry INT8 NOT NULL" ");");
566  /* Create Indices */
567  if (SQLITE_OK !=
568  sqlite3_exec (plugin->dbh,
569  "CREATE INDEX IF NOT EXISTS peerstoredata_key_index ON peerstoredata (sub_system, peer_id, key)",
570  NULL,
571  NULL,
572  NULL))
573  {
575  _ ("Unable to create indices: %s.\n"),
576  sqlite3_errmsg (plugin->dbh));
577  return GNUNET_SYSERR;
578  }
579  /* Prepare statements */
580 
581  sql_prepare (plugin->dbh,
582  "INSERT INTO peerstoredata (sub_system, peer_id, key, value, expiry)"
583  " VALUES (?,?,?,?,?);",
584  &plugin->insert_peerstoredata);
585  sql_prepare (plugin->dbh,
586  "SELECT sub_system,peer_id,key,value,expiry FROM peerstoredata"
587  " WHERE sub_system = ?",
588  &plugin->select_peerstoredata);
589  sql_prepare (plugin->dbh,
590  "SELECT sub_system,peer_id,key,value,expiry FROM peerstoredata"
591  " WHERE sub_system = ?"
592  " AND peer_id = ?",
593  &plugin->select_peerstoredata_by_pid);
594  sql_prepare (plugin->dbh,
595  "SELECT sub_system,peer_id,key,value,expiry FROM peerstoredata"
596  " WHERE sub_system = ?"
597  " AND key = ?",
598  &plugin->select_peerstoredata_by_key);
599  sql_prepare (plugin->dbh,
600  "SELECT sub_system,peer_id,key,value,expiry FROM peerstoredata"
601  " WHERE sub_system = ?"
602  " AND peer_id = ?" " AND key = ?",
603  &plugin->select_peerstoredata_by_all);
604  sql_prepare (plugin->dbh,
605  "DELETE FROM peerstoredata"
606  " WHERE expiry < ?",
607  &plugin->expire_peerstoredata);
608  sql_prepare (plugin->dbh,
609  "DELETE FROM peerstoredata"
610  " WHERE sub_system = ?"
611  " AND peer_id = ?" " AND key = ?",
612  &plugin->delete_peerstoredata);
613  return GNUNET_OK;
614 }
615 
616 
622 static void
624 {
625  int result;
626  sqlite3_stmt *stmt;
627 
628  while (NULL != (stmt = sqlite3_next_stmt (plugin->dbh,
629  NULL)))
630  {
631  result = sqlite3_finalize (stmt);
632  if (SQLITE_OK != result)
634  "Failed to close statement %p: %d\n",
635  stmt,
636  result);
637  }
638  if (SQLITE_OK != sqlite3_close (plugin->dbh))
641  "sqlite3_close");
642  GNUNET_free (plugin->fn);
643 }
644 
645 
652 void *
654 {
655  static struct Plugin plugin;
656  const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
658 
659  if (NULL != plugin.cfg)
660  return NULL; /* can only initialize once! */
661  memset (&plugin,
662  0,
663  sizeof(struct Plugin));
664  plugin.cfg = cfg;
665  if (GNUNET_OK != database_setup (&plugin))
666  {
668  return NULL;
669  }
671  api->cls = &plugin;
676  "Sqlite plugin is running\n");
677  return api;
678 }
679 
680 
687 void *
689 {
691  struct Plugin *plugin = api->cls;
692 
694  plugin->cfg = NULL;
695  GNUNET_free (api);
697  "Sqlite plugin is finished\n");
698  return NULL;
699 }
700 
701 
702 /* end of plugin_peerstore_sqlite.c */
struct GNUNET_GETOPT_CommandLineOption options[]
Definition: 002.c:5
static const struct GNUNET_CONFIGURATION_Handle * cfg
Configuration we are using.
Definition: gnunet-abd.c:36
static int ret
Return value of the commandline.
Definition: gnunet-abd.c:81
static struct PendingResolutions * tail
Tail of list of pending resolution requests.
Definition: gnunet-ats.c:235
struct TestcasePlugin * plugin
The process handle to the testbed service.
struct GNUNET_HashCode key
The key used in the DHT.
static char * filename
static char * value
Value of the record to add/remove.
static int result
Global testing status.
Plugin API for the peerstore database backend.
API to the peerstore service.
helper functions for Sqlite3 DB interactions
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.
enum GNUNET_GenericReturnValue 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:76
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:105
struct GNUNET_SQ_ResultSpec GNUNET_SQ_result_spec_string(char **dst)
0-terminated string expected.
struct GNUNET_SQ_ResultSpec GNUNET_SQ_result_spec_absolute_time(struct GNUNET_TIME_Absolute *at)
Absolute time expected.
struct GNUNET_SQ_QueryParam GNUNET_SQ_query_param_absolute_time(const struct GNUNET_TIME_Absolute *x)
Generate query parameter for an absolute time value.
#define GNUNET_SQ_result_spec_end
End of result parameter specification.
struct GNUNET_SQ_ResultSpec GNUNET_SQ_result_spec_variable_size(void **dst, size_t *sptr)
Variable-size result expected.
#define GNUNET_SQ_query_param_auto_from_type(x)
Generate fixed-size query parameter with size determined by variable type.
enum GNUNET_GenericReturnValue GNUNET_SQ_bind(sqlite3_stmt *stmt, const struct GNUNET_SQ_QueryParam *params)
Execute binding operations for a prepared statement.
Definition: sq.c:31
void GNUNET_SQ_reset(sqlite3 *dbh, sqlite3_stmt *stmt)
Reset stmt and log error.
Definition: sq.c:120
#define GNUNET_SQ_query_param_end
End of query parameter specification.
Definition: gnunet_sq_lib.h:87
struct GNUNET_SQ_QueryParam GNUNET_SQ_query_param_string(const char *ptr)
Generate query parameter for a string.
#define GNUNET_SQ_result_spec_auto_from_type(dst)
We expect a fixed-size result, with size determined by the type of * dst
enum GNUNET_GenericReturnValue 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.
enum GNUNET_GenericReturnValue 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:482
enum GNUNET_GenericReturnValue GNUNET_DISK_directory_create_for_file(const char *filename)
Create the directory structure for storing a file.
Definition: disk.c:582
@ GNUNET_OK
@ GNUNET_SYSERR
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur.
void GNUNET_log_config_missing(enum GNUNET_ErrorType kind, const char *section, const char *option)
Log error message about missing configuration option.
@ GNUNET_ERROR_TYPE_WARNING
@ GNUNET_ERROR_TYPE_ERROR
@ GNUNET_ERROR_TYPE_BULK
@ GNUNET_ERROR_TYPE_DEBUG
#define GNUNET_new(type)
Allocate a struct or union of the given type.
#define GNUNET_free(ptr)
Wrapper around free.
GNUNET_PEERSTORE_StoreOption
Options for storing values in PEERSTORE.
void(* GNUNET_PEERSTORE_Processor)(void *cls, const struct GNUNET_PEERSTORE_Record *record, const char *emsg)
Function called by PEERSTORE for each matching record.
void(* GNUNET_PEERSTORE_Continuation)(void *cls, int success)
Continuation called with a status result.
@ GNUNET_PEERSTORE_STOREOPTION_REPLACE
Delete any previous values for the given key before storing the given value.
static unsigned int size
Size of the "table".
Definition: peer.c:68
IPC messages.
#define _(String)
GNU gettext support macro.
Definition: platform.h:178
#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...
static int sql_prepare(sqlite3 *dbh, const char *sql, sqlite3_stmt **stmt)
Prepare a SQL statement.
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.
#define LOG_SQLITE(db, level, cmd)
Log an error message at log-level 'level' that indicates a failure of the command 'cmd' on file 'file...
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)
void * libgnunet_plugin_peerstore_sqlite_done(void *cls)
Exit point from the plugin.
static void database_shutdown(struct Plugin *plugin)
Shutdown database connection and associate data structures.
void * libgnunet_plugin_peerstore_sqlite_init(void *cls)
Entry point for the plugin.
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.
static int sql_exec(sqlite3 *dbh, const char *sql)
Prepare a SQL statement.
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 database_setup(struct Plugin *plugin)
Initialize the database connections and associated data structures (create tables and indices as need...
#define LOG(kind,...)
void * cls
Closure for all of the callbacks.
struct returned by the initialization function of the plugin
void * cls
Closure to pass to all plugin functions.
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.
int(* expire_records)(void *cls, struct GNUNET_TIME_Absolute now, GNUNET_PEERSTORE_Continuation cont, void *cont_cls)
Delete expired records (expiry < now)
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.
Single PEERSTORE record.
size_t value_size
Size of value BLOB.
struct GNUNET_PeerIdentity peer
Peer Identity.
void * value
Record value BLOB.
struct GNUNET_TIME_Absolute expiry
Expiry time of entry.
char * sub_system
Responsible sub system string.
char * key
Record key string.
The identity of the host (wraps the signing key of the peer).
Description of a DB query parameter.
Definition: gnunet_sq_lib.h:56
Description of a DB result cell.
void * cls
Closure for conv and cleaner.
Time for absolute times used by GNUnet, in microseconds.
Handle for a plugin.
Definition: block.c:38
struct GNUNET_BLOCK_PluginFunctions * api
Plugin API.
Definition: block.c:47
sqlite3_stmt * expire_peerstoredata
Precompiled SQL for deleting expired records from peerstoredata.
sqlite3_stmt * insert_peerstoredata
Precompiled SQL for inserting into peerstoredata.
char * fn
Filename used for the DB.
sqlite3_stmt * select_peerstoredata
Precompiled SQL for selecting from peerstoredata.
struct GNUNET_PQ_Context * dbh
Native Postgres database handle.
const struct GNUNET_CONFIGURATION_Handle * cfg
Our configuration.
sqlite3_stmt * select_peerstoredata_by_all
Precompiled SQL for selecting from peerstoredata.
sqlite3_stmt * delete_peerstoredata
Precompiled SQL for deleting records with given key.
sqlite3_stmt * select_peerstoredata_by_key
Precompiled SQL for selecting from peerstoredata.
sqlite3_stmt * select_peerstoredata_by_pid
Precompiled SQL for selecting from peerstoredata.
struct GNUNET_TESTBED_Peer * peer
The peer associated with this model.