use NULL value in load_path_suffix to NOT load any files
[oweals/gnunet.git] / src / peerstore / plugin_peerstore_sqlite.c
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
21 /**
22  * @file peerstore/plugin_peerstore_sqlite.c
23  * @brief sqlite-based peerstore backend
24  * @author Omar Tarabai
25  * @author Christian Grothoff
26  */
27
28 #include "platform.h"
29 #include "gnunet_peerstore_plugin.h"
30 #include "gnunet_peerstore_service.h"
31 #include "gnunet_sq_lib.h"
32 #include "peerstore.h"
33 #include <sqlite3.h>
34
35 /**
36  * After how many ms "busy" should a DB operation fail for good?  A
37  * low value makes sure that we are more responsive to requests
38  * (especially PUTs).  A high value guarantees a higher success rate
39  * (SELECTs in iterate can take several seconds despite LIMIT=1).
40  *
41  * The default value of 1s should ensure that users do not experience
42  * huge latencies while at the same time allowing operations to
43  * succeed with reasonable probability.
44  */
45 #define BUSY_TIMEOUT_MS 1000
46
47 /**
48  * Log an error message at log-level 'level' that indicates
49  * a failure of the command 'cmd' on file 'filename'
50  * with the message given by strerror(errno).
51  */
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
63 /**
64  * Context for all functions in this plugin.
65  */
66 struct Plugin
67 {
68   /**
69    * Configuration handle
70    */
71   const struct GNUNET_CONFIGURATION_Handle *cfg;
72
73   /**
74    * Database filename.
75    */
76   char *fn;
77
78   /**
79    * Native SQLite database handle.
80    */
81   sqlite3 *dbh;
82
83   /**
84    * Precompiled SQL for inserting into peerstoredata
85    */
86   sqlite3_stmt *insert_peerstoredata;
87
88   /**
89    * Precompiled SQL for selecting from peerstoredata
90    */
91   sqlite3_stmt *select_peerstoredata;
92
93   /**
94    * Precompiled SQL for selecting from peerstoredata
95    */
96   sqlite3_stmt *select_peerstoredata_by_pid;
97
98   /**
99    * Precompiled SQL for selecting from peerstoredata
100    */
101   sqlite3_stmt *select_peerstoredata_by_key;
102
103   /**
104    * Precompiled SQL for selecting from peerstoredata
105    */
106   sqlite3_stmt *select_peerstoredata_by_all;
107
108   /**
109    * Precompiled SQL for deleting expired
110    * records from peerstoredata
111    */
112   sqlite3_stmt *expire_peerstoredata;
113
114   /**
115    * Precompiled SQL for deleting records
116    * with given key
117    */
118   sqlite3_stmt *delete_peerstoredata;
119 };
120
121
122 /**
123  * Delete records with the given key
124  *
125  * @param cls closure (internal context for the plugin)
126  * @param sub_system name of sub system
127  * @param peer Peer identity (can be NULL)
128  * @param key entry key string (can be NULL)
129  * @return number of deleted records, #GNUNE_SYSERR on error
130  */
131 static int
132 peerstore_sqlite_delete_records (void *cls,
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),
141     GNUNET_SQ_query_param_auto_from_type (peer),
142     GNUNET_SQ_query_param_string (key),
143     GNUNET_SQ_query_param_end
144   };
145   int ret;
146
147   if (GNUNET_OK !=
148       GNUNET_SQ_bind (stmt,
149                       params))
150   {
151     LOG_SQLITE (plugin,
152                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
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   {
161     LOG_SQLITE (plugin,
162                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
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
176 /**
177  * Delete expired records (expiry < now)
178  *
179  * @param cls closure (internal context for the plugin)
180  * @param now time to use as reference
181  * @param cont continuation called with the number of records expired
182  * @param cont_cls continuation closure
183  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and cont is not
184  * called
185  */
186 static int
187 peerstore_sqlite_expire_records (void *cls, struct GNUNET_TIME_Absolute now,
188                                  GNUNET_PEERSTORE_Continuation cont,
189                                  void *cont_cls)
190 {
191   struct Plugin *plugin = cls;
192   sqlite3_stmt *stmt = plugin->expire_peerstoredata;
193   struct GNUNET_SQ_QueryParam params[] = {
194     GNUNET_SQ_query_param_absolute_time (&now),
195     GNUNET_SQ_query_param_end
196   };
197
198   if (GNUNET_OK !=
199       GNUNET_SQ_bind (stmt,
200                       params))
201   {
202     LOG_SQLITE (plugin,
203                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
204                 "sqlite3_bind");
205     GNUNET_SQ_reset (plugin->dbh,
206                      stmt);
207     return GNUNET_SYSERR;
208   }
209   if (SQLITE_DONE != sqlite3_step (stmt))
210   {
211     LOG_SQLITE (plugin,
212                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
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
227 /**
228  * Iterate over the records given an optional peer id
229  * and/or key.
230  *
231  * @param cls closure (internal context for the plugin)
232  * @param sub_system name of sub system
233  * @param peer Peer identity (can be NULL)
234  * @param key entry key string (can be NULL)
235  * @param iter function to call asynchronously with the results, terminated
236  * by a NULL result
237  * @param iter_cls closure for @a iter
238  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and iter is not
239  * called
240  */
241 static int
242 peerstore_sqlite_iterate_records (void *cls,
243                                   const char *sub_system,
244                                   const struct GNUNET_PeerIdentity *peer,
245                                   const char *key,
246                                   GNUNET_PEERSTORE_Processor iter,
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
255   LOG (GNUNET_ERROR_TYPE_DEBUG,
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),
263         GNUNET_SQ_query_param_end
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),
274         GNUNET_SQ_query_param_string (key),
275         GNUNET_SQ_query_param_end
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),
289         GNUNET_SQ_query_param_auto_from_type (peer),
290         GNUNET_SQ_query_param_end
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),
301         GNUNET_SQ_query_param_auto_from_type (peer),
302         GNUNET_SQ_query_param_string (key),
303         GNUNET_SQ_query_param_end
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   {
314     LOG_SQLITE (plugin,
315                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
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   {
325     LOG (GNUNET_ERROR_TYPE_DEBUG,
326          "Returning a matched record.\n");
327     struct GNUNET_SQ_ResultSpec rs[] = {
328       GNUNET_SQ_result_spec_string (&rec.sub_system),
329       GNUNET_SQ_result_spec_auto_from_type (&rec.peer),
330       GNUNET_SQ_result_spec_string (&rec.key),
331       GNUNET_SQ_result_spec_variable_size (&rec.value, &rec.value_size),
332       GNUNET_SQ_result_spec_absolute_time (&rec.expiry),
333       GNUNET_SQ_result_spec_end
334     };
335
336     if (GNUNET_OK !=
337         GNUNET_SQ_extract_result (stmt,
338                                   rs))
339     {
340       GNUNET_break (0);
341       break;
342     }
343     if (NULL != iter)
344       iter (iter_cls,
345             &rec,
346             NULL);
347     GNUNET_SQ_cleanup_result (rs);
348   }
349   if (SQLITE_DONE != sret)
350   {
351     LOG_SQLITE (plugin,
352                 GNUNET_ERROR_TYPE_ERROR,
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
366 /**
367  * Store a record in the peerstore.
368  * Key is the combination of sub system and peer identity.
369  * One key can store multiple values.
370  *
371  * @param cls closure (internal context for the plugin)
372  * @param sub_system name of the GNUnet sub system responsible
373  * @param peer peer identity
374  * @param key record key string
375  * @param value value to be stored
376  * @param size size of value to be stored
377  * @param expiry absolute time after which the record is (possibly) deleted
378  * @param options options related to the store operation
379  * @param cont continuation called when record is stored
380  * @param cont_cls continuation closure
381  * @return #GNUNET_OK on success, else #GNUNET_SYSERR and cont is not called
382  */
383 static int
384 peerstore_sqlite_store_record (void *cls,
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,
391                                enum GNUNET_PEERSTORE_StoreOption options,
392                                GNUNET_PEERSTORE_Continuation cont,
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),
399     GNUNET_SQ_query_param_auto_from_type (peer),
400     GNUNET_SQ_query_param_string (key),
401     GNUNET_SQ_query_param_fixed_size (value, size),
402     GNUNET_SQ_query_param_absolute_time (&expiry),
403     GNUNET_SQ_query_param_end
404   };
405
406   if (GNUNET_PEERSTORE_STOREOPTION_REPLACE == options)
407   {
408     peerstore_sqlite_delete_records (cls,
409                                      sub_system,
410                                      peer,
411                                      key);
412   }
413   if (GNUNET_OK !=
414       GNUNET_SQ_bind (stmt,
415                       params))
416     LOG_SQLITE (plugin,
417                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
418                 "sqlite3_bind");
419   else if (SQLITE_DONE != sqlite3_step (stmt))
420   {
421     LOG_SQLITE (plugin,
422                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
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
434 /**
435  * @brief Prepare a SQL statement
436  *
437  * @param dbh handle to the database
438  * @param sql SQL statement, UTF-8 encoded
439  * @return 0 on success
440  */
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);
452   LOG (GNUNET_ERROR_TYPE_DEBUG,
453        "Executed `%s' / %d\n",
454        sql,
455        result);
456   if (SQLITE_OK != result)
457     LOG (GNUNET_ERROR_TYPE_ERROR,
458          _ ("Error executing SQL query: %s\n  %s\n"),
459          sqlite3_errmsg (dbh),
460          sql);
461   return result;
462 }
463
464
465 /**
466  * @brief Prepare a SQL statement
467  *
468  * @param dbh handle to the database
469  * @param sql SQL statement, UTF-8 encoded
470  * @param stmt set to the prepared statement
471  * @return 0 on success
472  */
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);
486   LOG (GNUNET_ERROR_TYPE_DEBUG,
487        "Prepared `%s' / %p: %d\n",
488        sql,
489        *stmt,
490        result);
491   if (SQLITE_OK != result)
492     LOG (GNUNET_ERROR_TYPE_ERROR,
493          _ ("Error preparing SQL query: %s\n  %s\n"),
494          sqlite3_errmsg (dbh),
495          sql);
496   return result;
497 }
498
499
500 /**
501  * Initialize the database connections and associated
502  * data structures (create tables and indices
503  * as needed as well).
504  *
505  * @param plugin the plugin context (state for this module)
506  * @return #GNUNET_OK on success
507  */
508 static int
509 database_setup (struct Plugin *plugin)
510 {
511   char *filename;
512
513   if (GNUNET_OK !=
514       GNUNET_CONFIGURATION_get_value_filename (plugin->cfg,
515                                                "peerstore-sqlite",
516                                                "FILENAME",
517                                                &filename))
518   {
519     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
520                                "peerstore-sqlite",
521                                "FILENAME");
522     return GNUNET_SYSERR;
523   }
524   if (GNUNET_OK != GNUNET_DISK_file_test (filename))
525   {
526     if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (filename))
527     {
528       GNUNET_break (0);
529       GNUNET_free (filename);
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   {
539     LOG (GNUNET_ERROR_TYPE_ERROR,
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,
557                         BUSY_TIMEOUT_MS);
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   {
574     LOG (GNUNET_ERROR_TYPE_ERROR,
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
617 /**
618  * Shutdown database connection and associate data
619  * structures.
620  * @param plugin the plugin context (state for this module)
621  */
622 static void
623 database_shutdown (struct Plugin *plugin)
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)
633       LOG (GNUNET_ERROR_TYPE_WARNING,
634            "Failed to close statement %p: %d\n",
635            stmt,
636            result);
637   }
638   if (SQLITE_OK != sqlite3_close (plugin->dbh))
639     LOG_SQLITE (plugin,
640                 GNUNET_ERROR_TYPE_ERROR,
641                 "sqlite3_close");
642   GNUNET_free_non_null (plugin->fn);
643 }
644
645
646 /**
647  * Entry point for the plugin.
648  *
649  * @param cls The struct GNUNET_CONFIGURATION_Handle.
650  * @return NULL on error, otherwise the plugin context
651  */
652 void *
653 libgnunet_plugin_peerstore_sqlite_init (void *cls)
654 {
655   static struct Plugin plugin;
656   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
657   struct GNUNET_PEERSTORE_PluginFunctions *api;
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   {
667     database_shutdown (&plugin);
668     return NULL;
669   }
670   api = GNUNET_new (struct GNUNET_PEERSTORE_PluginFunctions);
671   api->cls = &plugin;
672   api->store_record = &peerstore_sqlite_store_record;
673   api->iterate_records = &peerstore_sqlite_iterate_records;
674   api->expire_records = &peerstore_sqlite_expire_records;
675   LOG (GNUNET_ERROR_TYPE_DEBUG,
676        "Sqlite plugin is running\n");
677   return api;
678 }
679
680
681 /**
682  * Exit point from the plugin.
683  *
684  * @param cls The plugin context (as returned by "init")
685  * @return Always NULL
686  */
687 void *
688 libgnunet_plugin_peerstore_sqlite_done (void *cls)
689 {
690   struct GNUNET_PEERSTORE_PluginFunctions *api = cls;
691   struct Plugin *plugin = api->cls;
692
693   database_shutdown (plugin);
694   plugin->cfg = NULL;
695   GNUNET_free (api);
696   LOG (GNUNET_ERROR_TYPE_DEBUG,
697        "Sqlite plugin is finished\n");
698   return NULL;
699 }
700
701
702 /* end of plugin_peerstore_sqlite.c */