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, entry->peer, sizeof (struct GNUNET_PeerIdentity)))
112 if (0 != strcmp (plugin->iter_sub_system, entry->sub_system))
115 GNUNET_CONTAINER_multihashmap_remove (plugin->hm, key, value);
116 plugin->deleted_entries++;
122 * Delete records with the given key
124 * @param cls closure (internal context for the plugin)
125 * @param sub_system name of sub system
126 * @param peer Peer identity (can be NULL)
127 * @param key entry key string (can be NULL)
128 * @return number of deleted records
131 peerstore_flat_delete_records (void *cls, const char *sub_system,
132 const struct GNUNET_PeerIdentity *peer,
135 struct Plugin *plugin = cls;
137 plugin->iter_sub_system = sub_system;
138 plugin->iter_peer = peer;
139 plugin->iter_key = key;
140 plugin->deleted_entries = 0;
142 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
145 return plugin->deleted_entries;
149 expire_entries (void *cls,
150 const struct GNUNET_HashCode *key,
153 struct Plugin *plugin = cls;
154 struct GNUNET_PEERSTORE_Record *entry = value;
156 if (entry->expiry->abs_value_us < plugin->iter_now.abs_value_us)
158 GNUNET_CONTAINER_multihashmap_remove (plugin->hm, key, value);
159 plugin->exp_changes++;
167 * Delete expired records (expiry < now)
169 * @param cls closure (internal context for the plugin)
170 * @param now time to use as reference
171 * @param cont continuation called with the number of records expired
172 * @param cont_cls continuation closure
173 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and cont is not
177 peerstore_flat_expire_records (void *cls, struct GNUNET_TIME_Absolute now,
178 GNUNET_PEERSTORE_Continuation cont,
181 struct Plugin *plugin = cls;
182 plugin->exp_changes = 0;
183 plugin->iter_now = now;
185 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
190 cont (cont_cls, plugin->exp_changes);
198 iterate_entries (void *cls,
199 const struct GNUNET_HashCode *key,
202 struct Plugin *plugin = cls;
203 struct GNUNET_PEERSTORE_Record *entry = value;
205 if ((NULL != plugin->iter_peer) &&
206 (0 != memcmp (plugin->iter_peer,
208 sizeof (struct GNUNET_PeerIdentity))))
212 if ((NULL != plugin->iter_key) &&
213 (0 != strcmp (plugin->iter_key,
218 if (NULL != plugin->iter)
219 plugin->iter (plugin->iter_cls, entry, NULL);
220 plugin->iter_result_found = GNUNET_YES;
225 * Iterate over the records given an optional peer id
228 * @param cls closure (internal context for the plugin)
229 * @param sub_system name of sub system
230 * @param peer Peer identity (can be NULL)
231 * @param key entry key string (can be NULL)
232 * @param iter function to call asynchronously with the results, terminated
234 * @param iter_cls closure for @a iter
235 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and iter is not
239 peerstore_flat_iterate_records (void *cls, const char *sub_system,
240 const struct GNUNET_PeerIdentity *peer,
242 GNUNET_PEERSTORE_Processor iter,
245 struct Plugin *plugin = cls;
247 plugin->iter_cls = iter_cls;
248 plugin->iter_peer = peer;
249 plugin->iter_sub_system = sub_system;
250 plugin->iter_key = key;
252 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
256 iter (iter_cls, NULL, NULL);
262 * Store a record in the peerstore.
263 * Key is the combination of sub system and peer identity.
264 * One key can store multiple values.
266 * @param cls closure (internal context for the plugin)
267 * @param sub_system name of the GNUnet sub system responsible
268 * @param peer peer identity
269 * @param key record key string
270 * @param value value to be stored
271 * @param size size of value to be stored
272 * @param expiry absolute time after which the record is (possibly) deleted
273 * @param options options related to the store operation
274 * @param cont continuation called when record is stored
275 * @param cont_cls continuation closure
276 * @return #GNUNET_OK on success, else #GNUNET_SYSERR and cont is not called
279 peerstore_flat_store_record (void *cls, const char *sub_system,
280 const struct GNUNET_PeerIdentity *peer,
281 const char *key, const void *value, size_t size,
282 struct GNUNET_TIME_Absolute expiry,
283 enum GNUNET_PEERSTORE_StoreOption options,
284 GNUNET_PEERSTORE_Continuation cont,
287 struct Plugin *plugin = cls;
288 struct GNUNET_HashCode hkey;
289 struct GNUNET_PEERSTORE_Record *entry;
293 entry = GNUNET_new (struct GNUNET_PEERSTORE_Record);
294 entry->sub_system = GNUNET_strdup (sub_system);
295 entry->key = GNUNET_strdup (key);
296 entry->value = GNUNET_malloc (size);
297 memcpy (entry->value, value, size);
298 entry->value_size = size;
299 entry->peer = GNUNET_new (struct GNUNET_PeerIdentity);
300 memcpy (entry->peer, peer, sizeof (struct GNUNET_PeerIdentity));
301 entry->expiry = GNUNET_new (struct GNUNET_TIME_Absolute);
302 entry->expiry->abs_value_us = expiry.abs_value_us;
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)
343 struct GNUNET_DISK_FileHandle *fh;
344 struct GNUNET_PEERSTORE_Record *entry;
350 GNUNET_CONFIGURATION_get_value_filename (plugin->cfg, "peerstore-flat",
351 "FILENAME", &afsdir))
353 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "peerstore-flat",
355 return GNUNET_SYSERR;
357 if (GNUNET_OK != GNUNET_DISK_file_test (afsdir))
359 if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (afsdir))
362 GNUNET_free (afsdir);
363 return GNUNET_SYSERR;
366 /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
369 fh = GNUNET_DISK_file_open (afsdir,
370 GNUNET_DISK_OPEN_CREATE |
371 GNUNET_DISK_OPEN_READWRITE,
372 GNUNET_DISK_PERM_USER_WRITE |
373 GNUNET_DISK_PERM_USER_READ);
376 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
377 _("Unable to initialize file: %s.\n"),
379 return GNUNET_SYSERR;
382 /* Load data from file into hashmap */
383 plugin->hm = GNUNET_CONTAINER_multihashmap_create (10,
386 if (GNUNET_SYSERR == GNUNET_DISK_file_size (afsdir,
391 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
392 _("Unable to get filesize: %s.\n"),
394 return GNUNET_SYSERR;
397 buffer = GNUNET_malloc (size);
399 if (GNUNET_SYSERR == GNUNET_DISK_file_read (fh,
403 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
404 _("Unable to read file: %s.\n"),
406 return GNUNET_SYSERR;
409 GNUNET_DISK_file_close (fh);
411 line = strtok (buffer, "\n");
412 while (line != NULL) {
413 sub_system = strtok (line, ",");
414 if (NULL == sub_system)
416 peer_id = strtok (NULL, ",");
419 key = strtok (NULL, ",");
422 value = strtok (NULL, ",");
425 expiry = strtok (NULL, ",");
428 entry = GNUNET_new (struct GNUNET_PEERSTORE_Record);
429 entry->sub_system = GNUNET_strdup (sub_system);
430 entry->key = GNUNET_strdup (key);
431 GNUNET_STRINGS_base64_decode (peer_id,
433 (char**)&entry->peer);
434 entry->value_size = GNUNET_STRINGS_base64_decode (value,
436 (char**)&entry->value);
437 GNUNET_STRINGS_fancy_time_to_absolute (expiry,
446 store_and_free_entries (void *cls,
447 const struct GNUNET_HashCode *key,
450 struct GNUNET_DISK_FileHandle *fh = cls;
451 struct GNUNET_PEERSTORE_Record *entry = value;
457 GNUNET_STRINGS_base64_encode (entry->value,
460 expiry = GNUNET_STRINGS_absolute_time_to_string (*entry->expiry);
461 GNUNET_STRINGS_base64_encode ((char*)entry->peer,
462 sizeof (struct GNUNET_PeerIdentity),
464 GNUNET_asprintf (&line,
473 GNUNET_DISK_file_write (fh,
476 GNUNET_free (entry->sub_system);
477 GNUNET_free (entry->peer);
478 GNUNET_free (entry->key);
479 GNUNET_free (entry->value);
480 GNUNET_free (entry->expiry);
487 * Shutdown database connection and associate data
489 * @param plugin the plugin context (state for this module)
492 database_shutdown (struct Plugin *plugin)
494 struct GNUNET_DISK_FileHandle *fh;
495 fh = GNUNET_DISK_file_open (plugin->fn,
496 GNUNET_DISK_OPEN_CREATE |
497 GNUNET_DISK_OPEN_TRUNCATE |
498 GNUNET_DISK_OPEN_READWRITE,
499 GNUNET_DISK_PERM_USER_WRITE |
500 GNUNET_DISK_PERM_USER_READ);
503 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
504 _("Unable to initialize file: %s.\n"),
508 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
509 &store_and_free_entries,
511 GNUNET_CONTAINER_multihashmap_destroy (plugin->hm);
512 GNUNET_DISK_file_close (fh);
517 * Entry point for the plugin.
519 * @param cls The struct GNUNET_CONFIGURATION_Handle.
520 * @return NULL on error, otherwise the plugin context
523 libgnunet_plugin_peerstore_flat_init (void *cls)
525 static struct Plugin plugin;
526 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
527 struct GNUNET_PEERSTORE_PluginFunctions *api;
529 if (NULL != plugin.cfg)
530 return NULL; /* can only initialize once! */
531 memset (&plugin, 0, sizeof (struct Plugin));
533 if (GNUNET_OK != database_setup (&plugin))
535 database_shutdown (&plugin);
538 api = GNUNET_new (struct GNUNET_PEERSTORE_PluginFunctions);
540 api->store_record = &peerstore_flat_store_record;
541 api->iterate_records = &peerstore_flat_iterate_records;
542 api->expire_records = &peerstore_flat_expire_records;
543 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Flat plugin is running\n");
549 * Exit point from the plugin.
551 * @param cls The plugin context (as returned by "init")
552 * @return Always NULL
555 libgnunet_plugin_peerstore_flat_done (void *cls)
557 struct GNUNET_PEERSTORE_PluginFunctions *api = cls;
558 struct Plugin *plugin = api->cls;
560 database_shutdown (plugin);
563 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Flat plugin is finished\n");
567 /* end of plugin_peerstore_sqlite.c */