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 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.
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.
17 * @file peerstore/plugin_peerstore_flat.c
18 * @brief flat file-based peerstore backend
19 * @author Martin Schanzenbach
23 #include "gnunet_peerstore_plugin.h"
24 #include "gnunet_peerstore_service.h"
25 #include "peerstore.h"
28 * Context for all functions in this plugin.
34 * Configuration handle
36 const struct GNUNET_CONFIGURATION_Handle *cfg;
41 struct GNUNET_CONTAINER_MultiHashMap *hm;
46 GNUNET_PEERSTORE_Processor iter;
61 const struct GNUNET_PeerIdentity *iter_peer;
66 const char *iter_sub_system;
71 struct GNUNET_TIME_Absolute iter_now;
76 uint64_t deleted_entries;
91 int iter_result_found;
97 delete_entries (void *cls,
98 const struct GNUNET_HashCode *key,
101 struct Plugin *plugin = cls;
102 struct GNUNET_PEERSTORE_Record *entry = value;
103 if (0 != strcmp (plugin->iter_key, entry->key))
105 if (0 != memcmp (plugin->iter_peer,
107 sizeof (struct GNUNET_PeerIdentity)))
109 if (0 != strcmp (plugin->iter_sub_system, entry->sub_system))
112 GNUNET_CONTAINER_multihashmap_remove (plugin->hm, key, value);
113 plugin->deleted_entries++;
119 * Delete records with the given key
121 * @param cls closure (internal context for the plugin)
122 * @param sub_system name of sub system
123 * @param peer Peer identity (can be NULL)
124 * @param key entry key string (can be NULL)
125 * @return number of deleted records
128 peerstore_flat_delete_records (void *cls, const char *sub_system,
129 const struct GNUNET_PeerIdentity *peer,
132 struct Plugin *plugin = cls;
134 plugin->iter_sub_system = sub_system;
135 plugin->iter_peer = peer;
136 plugin->iter_key = key;
137 plugin->deleted_entries = 0;
139 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
142 return plugin->deleted_entries;
146 expire_entries (void *cls,
147 const struct GNUNET_HashCode *key,
150 struct Plugin *plugin = cls;
151 struct GNUNET_PEERSTORE_Record *entry = value;
153 if (entry->expiry.abs_value_us < plugin->iter_now.abs_value_us)
155 GNUNET_CONTAINER_multihashmap_remove (plugin->hm, key, value);
156 plugin->exp_changes++;
164 * Delete expired records (expiry < now)
166 * @param cls closure (internal context for the plugin)
167 * @param now time to use as reference
168 * @param cont continuation called with the number of records expired
169 * @param cont_cls continuation closure
170 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and cont is not
174 peerstore_flat_expire_records (void *cls, struct GNUNET_TIME_Absolute now,
175 GNUNET_PEERSTORE_Continuation cont,
178 struct Plugin *plugin = cls;
179 plugin->exp_changes = 0;
180 plugin->iter_now = now;
182 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
187 cont (cont_cls, plugin->exp_changes);
195 iterate_entries (void *cls,
196 const struct GNUNET_HashCode *key,
199 struct Plugin *plugin = cls;
200 struct GNUNET_PEERSTORE_Record *entry = value;
202 if ((NULL != plugin->iter_peer) &&
203 (0 != memcmp (plugin->iter_peer,
205 sizeof (struct GNUNET_PeerIdentity))))
209 if ((NULL != plugin->iter_key) &&
210 (0 != strcmp (plugin->iter_key,
215 if (NULL != plugin->iter)
216 plugin->iter (plugin->iter_cls, entry, NULL);
217 plugin->iter_result_found = GNUNET_YES;
222 * Iterate over the records given an optional peer id
225 * @param cls closure (internal context for the plugin)
226 * @param sub_system name of sub system
227 * @param peer Peer identity (can be NULL)
228 * @param key entry key string (can be NULL)
229 * @param iter function to call asynchronously with the results, terminated
231 * @param iter_cls closure for @a iter
232 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and iter is not
236 peerstore_flat_iterate_records (void *cls, const char *sub_system,
237 const struct GNUNET_PeerIdentity *peer,
239 GNUNET_PEERSTORE_Processor iter,
242 struct Plugin *plugin = cls;
244 plugin->iter_cls = iter_cls;
245 plugin->iter_peer = peer;
246 plugin->iter_sub_system = sub_system;
247 plugin->iter_key = key;
249 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
253 iter (iter_cls, NULL, NULL);
259 * Store a record in the peerstore.
260 * Key is the combination of sub system and peer identity.
261 * One key can store multiple values.
263 * @param cls closure (internal context for the plugin)
264 * @param sub_system name of the GNUnet sub system responsible
265 * @param peer peer identity
266 * @param key record key string
267 * @param value value to be stored
268 * @param size size of value to be stored
269 * @param expiry absolute time after which the record is (possibly) deleted
270 * @param options options related to the store operation
271 * @param cont continuation called when record is stored
272 * @param cont_cls continuation closure
273 * @return #GNUNET_OK on success, else #GNUNET_SYSERR and cont is not called
276 peerstore_flat_store_record (void *cls, const char *sub_system,
277 const struct GNUNET_PeerIdentity *peer,
278 const char *key, const void *value, size_t size,
279 struct GNUNET_TIME_Absolute expiry,
280 enum GNUNET_PEERSTORE_StoreOption options,
281 GNUNET_PEERSTORE_Continuation cont,
284 struct Plugin *plugin = cls;
285 struct GNUNET_HashCode hkey;
286 struct GNUNET_PEERSTORE_Record *entry;
290 entry = GNUNET_new (struct GNUNET_PEERSTORE_Record);
291 entry->sub_system = GNUNET_strdup (sub_system);
292 entry->key = GNUNET_strdup (key);
293 entry->value = GNUNET_malloc (size);
294 GNUNET_memcpy (entry->value, value, size);
295 entry->value_size = size;
297 entry->expiry = expiry;
299 peer_id = GNUNET_i2s (peer);
300 GNUNET_CRYPTO_hash (peer_id,
304 if (GNUNET_PEERSTORE_STOREOPTION_REPLACE == options)
306 peerstore_flat_delete_records (cls, sub_system, peer, key);
309 GNUNET_CONTAINER_multihashmap_put (plugin->hm,
312 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
315 cont (cont_cls, GNUNET_OK);
322 * Initialize the database connections and associated
323 * data structures (create tables and indices
324 * as needed as well).
326 * @param plugin the plugin context (state for this module)
327 * @return GNUNET_OK on success
330 database_setup (struct Plugin *plugin)
339 struct GNUNET_DISK_FileHandle *fh;
340 struct GNUNET_PEERSTORE_Record *entry;
341 struct GNUNET_HashCode hkey;
347 GNUNET_CONFIGURATION_get_value_filename (plugin->cfg, "peerstore-flat",
348 "FILENAME", &afsdir))
350 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "peerstore-flat",
352 return GNUNET_SYSERR;
354 if (GNUNET_OK != GNUNET_DISK_file_test (afsdir))
356 if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (afsdir))
359 GNUNET_free (afsdir);
360 return GNUNET_SYSERR;
363 /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
366 fh = GNUNET_DISK_file_open (afsdir,
367 GNUNET_DISK_OPEN_CREATE |
368 GNUNET_DISK_OPEN_READWRITE,
369 GNUNET_DISK_PERM_USER_WRITE |
370 GNUNET_DISK_PERM_USER_READ);
373 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
374 _("Unable to initialize file: %s.\n"),
376 return GNUNET_SYSERR;
379 /* Load data from file into hashmap */
380 plugin->hm = GNUNET_CONTAINER_multihashmap_create (10,
383 if (GNUNET_SYSERR == GNUNET_DISK_file_size (afsdir,
388 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
389 _("Unable to get filesize: %s.\n"),
391 return GNUNET_SYSERR;
394 buffer = GNUNET_malloc (size + 1);
396 if (GNUNET_SYSERR == GNUNET_DISK_file_read (fh,
400 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
401 _("Unable to read file: %s.\n"),
403 GNUNET_DISK_file_close (fh);
404 GNUNET_free (buffer);
405 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 = 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);
436 s = GNUNET_STRINGS_base64_decode (peer,
439 if (sizeof (struct GNUNET_PeerIdentity) == s)
440 GNUNET_memcpy (&entry->peer,
445 GNUNET_free_non_null (o);
447 entry->value_size = GNUNET_STRINGS_base64_decode (value,
449 (char**)&entry->value);
451 GNUNET_STRINGS_fancy_time_to_absolute (expiry,
454 GNUNET_free (entry->sub_system);
455 GNUNET_free (entry->key);
459 peer_id = GNUNET_i2s (&entry->peer);
460 GNUNET_CRYPTO_hash (peer_id,
464 GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (plugin->hm,
467 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
471 GNUNET_free (buffer);
476 store_and_free_entries (void *cls,
477 const struct GNUNET_HashCode *key,
480 struct GNUNET_DISK_FileHandle *fh = cls;
481 struct GNUNET_PEERSTORE_Record *entry = value;
487 GNUNET_STRINGS_base64_encode (entry->value,
490 expiry = GNUNET_STRINGS_absolute_time_to_string (entry->expiry);
491 GNUNET_STRINGS_base64_encode ((char*)&entry->peer,
492 sizeof (struct GNUNET_PeerIdentity),
494 GNUNET_asprintf (&line,
503 GNUNET_DISK_file_write (fh,
506 GNUNET_free (entry->sub_system);
507 GNUNET_free (entry->key);
508 GNUNET_free (entry->value);
516 * Shutdown database connection and associate data
518 * @param plugin the plugin context (state for this module)
521 database_shutdown (struct Plugin *plugin)
523 struct GNUNET_DISK_FileHandle *fh;
524 fh = GNUNET_DISK_file_open (plugin->fn,
525 GNUNET_DISK_OPEN_CREATE |
526 GNUNET_DISK_OPEN_TRUNCATE |
527 GNUNET_DISK_OPEN_READWRITE,
528 GNUNET_DISK_PERM_USER_WRITE |
529 GNUNET_DISK_PERM_USER_READ);
532 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
533 _("Unable to initialize file: %s.\n"),
537 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
538 &store_and_free_entries,
540 GNUNET_CONTAINER_multihashmap_destroy (plugin->hm);
541 GNUNET_DISK_file_close (fh);
546 * Entry point for the plugin.
548 * @param cls The struct GNUNET_CONFIGURATION_Handle.
549 * @return NULL on error, otherwise the plugin context
552 libgnunet_plugin_peerstore_flat_init (void *cls)
554 static struct Plugin plugin;
555 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
556 struct GNUNET_PEERSTORE_PluginFunctions *api;
558 if (NULL != plugin.cfg)
559 return NULL; /* can only initialize once! */
560 memset (&plugin, 0, sizeof (struct Plugin));
562 if (GNUNET_OK != database_setup (&plugin))
564 database_shutdown (&plugin);
567 api = GNUNET_new (struct GNUNET_PEERSTORE_PluginFunctions);
569 api->store_record = &peerstore_flat_store_record;
570 api->iterate_records = &peerstore_flat_iterate_records;
571 api->expire_records = &peerstore_flat_expire_records;
572 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Flat plugin is running\n");
578 * Exit point from the plugin.
580 * @param cls The plugin context (as returned by "init")
581 * @return Always NULL
584 libgnunet_plugin_peerstore_flat_done (void *cls)
586 struct GNUNET_PEERSTORE_PluginFunctions *api = cls;
587 struct Plugin *plugin = api->cls;
589 database_shutdown (plugin);
592 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Flat plugin is finished\n");
596 /* end of plugin_peerstore_sqlite.c */