2 * This file is part of GNUnet
3 * Copyright (C) 2015 Christian Grothoff (and other contributing authors)
5 * GNUnet is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published
7 * by the Free Software Foundation; either version 3, or (at your
8 * option) any later version.
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 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GNUnet; see the file COPYING. If not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
22 * @file peerstore/plugin_peerstore_flat.c
23 * @brief flat file-based peerstore backend
24 * @author Martin Schanzenbach
28 #include "gnunet_peerstore_plugin.h"
29 #include "gnunet_peerstore_service.h"
30 #include "peerstore.h"
33 * Context for all functions in this plugin.
39 * Configuration handle
41 const struct GNUNET_CONFIGURATION_Handle *cfg;
46 struct GNUNET_CONTAINER_MultiHashMap *hm;
51 GNUNET_PEERSTORE_Processor iter;
66 const struct GNUNET_PeerIdentity *iter_peer;
71 const char *iter_sub_system;
76 struct GNUNET_TIME_Absolute iter_now;
81 uint64_t deleted_entries;
96 int iter_result_found;
102 delete_entries (void *cls,
103 const struct GNUNET_HashCode *key,
106 struct Plugin *plugin = cls;
107 struct GNUNET_PEERSTORE_Record *entry = value;
108 if (0 != strcmp (plugin->iter_key, entry->key))
110 if (0 != memcmp (plugin->iter_peer,
112 sizeof (struct GNUNET_PeerIdentity)))
114 if (0 != strcmp (plugin->iter_sub_system, entry->sub_system))
117 GNUNET_CONTAINER_multihashmap_remove (plugin->hm, key, value);
118 plugin->deleted_entries++;
124 * Delete records with the given key
126 * @param cls closure (internal context for the plugin)
127 * @param sub_system name of sub system
128 * @param peer Peer identity (can be NULL)
129 * @param key entry key string (can be NULL)
130 * @return number of deleted records
133 peerstore_flat_delete_records (void *cls, const char *sub_system,
134 const struct GNUNET_PeerIdentity *peer,
137 struct Plugin *plugin = cls;
139 plugin->iter_sub_system = sub_system;
140 plugin->iter_peer = peer;
141 plugin->iter_key = key;
142 plugin->deleted_entries = 0;
144 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
147 return plugin->deleted_entries;
151 expire_entries (void *cls,
152 const struct GNUNET_HashCode *key,
155 struct Plugin *plugin = cls;
156 struct GNUNET_PEERSTORE_Record *entry = value;
158 if (entry->expiry.abs_value_us < plugin->iter_now.abs_value_us)
160 GNUNET_CONTAINER_multihashmap_remove (plugin->hm, key, value);
161 plugin->exp_changes++;
169 * Delete expired records (expiry < now)
171 * @param cls closure (internal context for the plugin)
172 * @param now time to use as reference
173 * @param cont continuation called with the number of records expired
174 * @param cont_cls continuation closure
175 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and cont is not
179 peerstore_flat_expire_records (void *cls, struct GNUNET_TIME_Absolute now,
180 GNUNET_PEERSTORE_Continuation cont,
183 struct Plugin *plugin = cls;
184 plugin->exp_changes = 0;
185 plugin->iter_now = now;
187 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
192 cont (cont_cls, plugin->exp_changes);
200 iterate_entries (void *cls,
201 const struct GNUNET_HashCode *key,
204 struct Plugin *plugin = cls;
205 struct GNUNET_PEERSTORE_Record *entry = value;
207 if ((NULL != plugin->iter_peer) &&
208 (0 != memcmp (plugin->iter_peer,
210 sizeof (struct GNUNET_PeerIdentity))))
214 if ((NULL != plugin->iter_key) &&
215 (0 != strcmp (plugin->iter_key,
220 if (NULL != plugin->iter)
221 plugin->iter (plugin->iter_cls, entry, NULL);
222 plugin->iter_result_found = GNUNET_YES;
227 * Iterate over the records given an optional peer id
230 * @param cls closure (internal context for the plugin)
231 * @param sub_system name of sub system
232 * @param peer Peer identity (can be NULL)
233 * @param key entry key string (can be NULL)
234 * @param iter function to call asynchronously with the results, terminated
236 * @param iter_cls closure for @a iter
237 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and iter is not
241 peerstore_flat_iterate_records (void *cls, const char *sub_system,
242 const struct GNUNET_PeerIdentity *peer,
244 GNUNET_PEERSTORE_Processor iter,
247 struct Plugin *plugin = cls;
249 plugin->iter_cls = iter_cls;
250 plugin->iter_peer = peer;
251 plugin->iter_sub_system = sub_system;
252 plugin->iter_key = key;
254 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
258 iter (iter_cls, NULL, NULL);
264 * Store a record in the peerstore.
265 * Key is the combination of sub system and peer identity.
266 * One key can store multiple values.
268 * @param cls closure (internal context for the plugin)
269 * @param sub_system name of the GNUnet sub system responsible
270 * @param peer peer identity
271 * @param key record key string
272 * @param value value to be stored
273 * @param size size of value to be stored
274 * @param expiry absolute time after which the record is (possibly) deleted
275 * @param options options related to the store operation
276 * @param cont continuation called when record is stored
277 * @param cont_cls continuation closure
278 * @return #GNUNET_OK on success, else #GNUNET_SYSERR and cont is not called
281 peerstore_flat_store_record (void *cls, const char *sub_system,
282 const struct GNUNET_PeerIdentity *peer,
283 const char *key, const void *value, size_t size,
284 struct GNUNET_TIME_Absolute expiry,
285 enum GNUNET_PEERSTORE_StoreOption options,
286 GNUNET_PEERSTORE_Continuation cont,
289 struct Plugin *plugin = cls;
290 struct GNUNET_HashCode hkey;
291 struct GNUNET_PEERSTORE_Record *entry;
295 entry = GNUNET_new (struct GNUNET_PEERSTORE_Record);
296 entry->sub_system = GNUNET_strdup (sub_system);
297 entry->key = GNUNET_strdup (key);
298 entry->value = GNUNET_malloc (size);
299 GNUNET_memcpy (entry->value, value, size);
300 entry->value_size = size;
302 entry->expiry = expiry;
304 peer_id = GNUNET_i2s (peer);
305 GNUNET_CRYPTO_hash (peer_id,
309 if (GNUNET_PEERSTORE_STOREOPTION_REPLACE == options)
311 peerstore_flat_delete_records (cls, sub_system, peer, key);
314 GNUNET_CONTAINER_multihashmap_put (plugin->hm,
317 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
320 cont (cont_cls, GNUNET_OK);
327 * Initialize the database connections and associated
328 * data structures (create tables and indices
329 * as needed as well).
331 * @param plugin the plugin context (state for this module)
332 * @return GNUNET_OK on success
335 database_setup (struct Plugin *plugin)
344 struct GNUNET_DISK_FileHandle *fh;
345 struct GNUNET_PEERSTORE_Record *entry;
346 struct GNUNET_HashCode hkey;
352 GNUNET_CONFIGURATION_get_value_filename (plugin->cfg, "peerstore-flat",
353 "FILENAME", &afsdir))
355 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "peerstore-flat",
357 return GNUNET_SYSERR;
359 if (GNUNET_OK != GNUNET_DISK_file_test (afsdir))
361 if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (afsdir))
364 GNUNET_free (afsdir);
365 return GNUNET_SYSERR;
368 /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
371 fh = GNUNET_DISK_file_open (afsdir,
372 GNUNET_DISK_OPEN_CREATE |
373 GNUNET_DISK_OPEN_READWRITE,
374 GNUNET_DISK_PERM_USER_WRITE |
375 GNUNET_DISK_PERM_USER_READ);
378 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
379 _("Unable to initialize file: %s.\n"),
381 return GNUNET_SYSERR;
384 /* Load data from file into hashmap */
385 plugin->hm = GNUNET_CONTAINER_multihashmap_create (10,
388 if (GNUNET_SYSERR == GNUNET_DISK_file_size (afsdir,
393 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
394 _("Unable to get filesize: %s.\n"),
396 return GNUNET_SYSERR;
399 buffer = GNUNET_malloc (size + 1);
401 if (GNUNET_SYSERR == GNUNET_DISK_file_read (fh,
405 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
406 _("Unable to read file: %s.\n"),
408 GNUNET_DISK_file_close (fh);
409 GNUNET_free (buffer);
410 return GNUNET_SYSERR;
414 GNUNET_DISK_file_close (fh);
416 line = strtok (buffer, "\n");
417 while (line != NULL) {
418 sub_system = strtok (line, ",");
419 if (NULL == sub_system)
421 peer = strtok (NULL, ",");
424 key = strtok (NULL, ",");
427 value = strtok (NULL, ",");
430 expiry = strtok (NULL, ",");
433 entry = GNUNET_new (struct GNUNET_PEERSTORE_Record);
434 entry->sub_system = GNUNET_strdup (sub_system);
435 entry->key = GNUNET_strdup (key);
441 s = GNUNET_STRINGS_base64_decode (peer,
444 if (sizeof (struct GNUNET_PeerIdentity) == s)
445 GNUNET_memcpy (&entry->peer,
450 GNUNET_free_non_null (o);
452 entry->value_size = GNUNET_STRINGS_base64_decode (value,
454 (char**)&entry->value);
456 GNUNET_STRINGS_fancy_time_to_absolute (expiry,
459 GNUNET_free (entry->sub_system);
460 GNUNET_free (entry->key);
464 peer_id = GNUNET_i2s (&entry->peer);
465 GNUNET_CRYPTO_hash (peer_id,
469 GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (plugin->hm,
472 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
476 GNUNET_free (buffer);
481 store_and_free_entries (void *cls,
482 const struct GNUNET_HashCode *key,
485 struct GNUNET_DISK_FileHandle *fh = cls;
486 struct GNUNET_PEERSTORE_Record *entry = value;
492 GNUNET_STRINGS_base64_encode (entry->value,
495 expiry = GNUNET_STRINGS_absolute_time_to_string (entry->expiry);
496 GNUNET_STRINGS_base64_encode ((char*)&entry->peer,
497 sizeof (struct GNUNET_PeerIdentity),
499 GNUNET_asprintf (&line,
508 GNUNET_DISK_file_write (fh,
511 GNUNET_free (entry->sub_system);
512 GNUNET_free (entry->key);
513 GNUNET_free (entry->value);
521 * Shutdown database connection and associate data
523 * @param plugin the plugin context (state for this module)
526 database_shutdown (struct Plugin *plugin)
528 struct GNUNET_DISK_FileHandle *fh;
529 fh = GNUNET_DISK_file_open (plugin->fn,
530 GNUNET_DISK_OPEN_CREATE |
531 GNUNET_DISK_OPEN_TRUNCATE |
532 GNUNET_DISK_OPEN_READWRITE,
533 GNUNET_DISK_PERM_USER_WRITE |
534 GNUNET_DISK_PERM_USER_READ);
537 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
538 _("Unable to initialize file: %s.\n"),
542 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
543 &store_and_free_entries,
545 GNUNET_CONTAINER_multihashmap_destroy (plugin->hm);
546 GNUNET_DISK_file_close (fh);
551 * Entry point for the plugin.
553 * @param cls The struct GNUNET_CONFIGURATION_Handle.
554 * @return NULL on error, otherwise the plugin context
557 libgnunet_plugin_peerstore_flat_init (void *cls)
559 static struct Plugin plugin;
560 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
561 struct GNUNET_PEERSTORE_PluginFunctions *api;
563 if (NULL != plugin.cfg)
564 return NULL; /* can only initialize once! */
565 memset (&plugin, 0, sizeof (struct Plugin));
567 if (GNUNET_OK != database_setup (&plugin))
569 database_shutdown (&plugin);
572 api = GNUNET_new (struct GNUNET_PEERSTORE_PluginFunctions);
574 api->store_record = &peerstore_flat_store_record;
575 api->iterate_records = &peerstore_flat_iterate_records;
576 api->expire_records = &peerstore_flat_expire_records;
577 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Flat plugin is running\n");
583 * Exit point from the plugin.
585 * @param cls The plugin context (as returned by "init")
586 * @return Always NULL
589 libgnunet_plugin_peerstore_flat_done (void *cls)
591 struct GNUNET_PEERSTORE_PluginFunctions *api = cls;
592 struct Plugin *plugin = api->cls;
594 database_shutdown (plugin);
597 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Flat plugin is finished\n");
601 /* end of plugin_peerstore_sqlite.c */