aboutsummaryrefslogtreecommitdiff
path: root/src/peerstore/plugin_peerstore_flat.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/peerstore/plugin_peerstore_flat.c')
-rw-r--r--src/peerstore/plugin_peerstore_flat.c567
1 files changed, 567 insertions, 0 deletions
diff --git a/src/peerstore/plugin_peerstore_flat.c b/src/peerstore/plugin_peerstore_flat.c
new file mode 100644
index 000000000..ba5301d09
--- /dev/null
+++ b/src/peerstore/plugin_peerstore_flat.c
@@ -0,0 +1,567 @@
1/*
2 * This file is part of GNUnet
3 * Copyright (C) 2015 Christian Grothoff (and other contributing authors)
4 *
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.
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 * General Public License for more details.
14 *
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.
19 */
20
21/**
22 * @file peerstore/plugin_peerstore_flat.c
23 * @brief flat file-based peerstore backend
24 * @author Martin Schanzenbach
25 */
26
27#include "platform.h"
28#include "gnunet_peerstore_plugin.h"
29#include "gnunet_peerstore_service.h"
30#include "peerstore.h"
31
32/**
33 * Context for all functions in this plugin.
34 */
35struct Plugin
36{
37
38 /**
39 * Configuration handle
40 */
41 const struct GNUNET_CONFIGURATION_Handle *cfg;
42
43 /**
44 * HashMap
45 */
46 struct GNUNET_CONTAINER_MultiHashMap *hm;
47
48 /**
49 * Iterator
50 */
51 GNUNET_PEERSTORE_Processor iter;
52
53 /**
54 * Iterator cls
55 */
56 void *iter_cls;
57
58 /**
59 * iterator key
60 */
61 const char *iter_key;
62
63 /**
64 * Iterator peer
65 */
66 const struct GNUNET_PeerIdentity *iter_peer;
67
68 /**
69 * Iterator subsystem
70 */
71 const char *iter_sub_system;
72
73 /**
74 * Iterator time
75 */
76 struct GNUNET_TIME_Absolute iter_now;
77
78 /**
79 * Deleted entries
80 */
81 uint64_t deleted_entries;
82
83 /**
84 * Expired entries
85 */
86 uint64_t exp_changes;
87
88 /**
89 * Database filename.
90 */
91 char *fn;
92
93 /**
94 * Result found bool
95 */
96 int iter_result_found;
97
98};
99
100
101static int
102delete_entries (void *cls,
103 const struct GNUNET_HashCode *key,
104 void *value)
105{
106 struct Plugin *plugin = cls;
107 struct GNUNET_PEERSTORE_Record *entry = value;
108 if (0 != strcmp (plugin->iter_key, entry->key))
109 return GNUNET_YES;
110 if (0 != memcmp (plugin->iter_peer, entry->peer, sizeof (struct GNUNET_PeerIdentity)))
111 return GNUNET_YES;
112 if (0 != strcmp (plugin->iter_sub_system, entry->sub_system))
113 return GNUNET_YES;
114
115 GNUNET_CONTAINER_multihashmap_remove (plugin->hm, key, value);
116 plugin->deleted_entries++;
117 return GNUNET_YES;
118}
119
120
121/**
122 * Delete records with the given key
123 *
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
129 */
130static int
131peerstore_flat_delete_records (void *cls, const char *sub_system,
132 const struct GNUNET_PeerIdentity *peer,
133 const char *key)
134{
135 struct Plugin *plugin = cls;
136
137 plugin->iter_sub_system = sub_system;
138 plugin->iter_peer = peer;
139 plugin->iter_key = key;
140 plugin->deleted_entries = 0;
141
142 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
143 &delete_entries,
144 plugin);
145 return plugin->deleted_entries;
146}
147
148static int
149expire_entries (void *cls,
150 const struct GNUNET_HashCode *key,
151 void *value)
152{
153 struct Plugin *plugin = cls;
154 struct GNUNET_PEERSTORE_Record *entry = value;
155
156 if (entry->expiry->abs_value_us < plugin->iter_now.abs_value_us)
157 {
158 GNUNET_CONTAINER_multihashmap_remove (plugin->hm, key, value);
159 plugin->exp_changes++;
160 }
161 return GNUNET_YES;
162}
163
164
165
166/**
167 * Delete expired records (expiry < now)
168 *
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
174 * called
175 */
176static int
177peerstore_flat_expire_records (void *cls, struct GNUNET_TIME_Absolute now,
178 GNUNET_PEERSTORE_Continuation cont,
179 void *cont_cls)
180{
181 struct Plugin *plugin = cls;
182 plugin->exp_changes = 0;
183 plugin->iter_now = now;
184
185 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
186 &expire_entries,
187 plugin);
188 if (NULL != cont)
189 {
190 cont (cont_cls, plugin->exp_changes);
191 }
192 return GNUNET_OK;
193
194}
195
196
197static int
198iterate_entries (void *cls,
199 const struct GNUNET_HashCode *key,
200 void *value)
201{
202 struct Plugin *plugin = cls;
203 struct GNUNET_PEERSTORE_Record *entry = value;
204
205 if ((NULL != plugin->iter_peer) &&
206 (0 != memcmp (plugin->iter_peer,
207 entry->peer,
208 sizeof (struct GNUNET_PeerIdentity))))
209 {
210 return GNUNET_YES;
211 }
212 if ((NULL != plugin->iter_key) &&
213 (0 != strcmp (plugin->iter_key,
214 entry->key)))
215 {
216 return GNUNET_YES;
217 }
218 if (NULL != plugin->iter)
219 plugin->iter (plugin->iter_cls, entry, NULL);
220 plugin->iter_result_found = GNUNET_YES;
221 return GNUNET_YES;
222}
223
224/**
225 * Iterate over the records given an optional peer id
226 * and/or key.
227 *
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
233 * by a NULL result
234 * @param iter_cls closure for @a iter
235 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and iter is not
236 * called
237 */
238static int
239peerstore_flat_iterate_records (void *cls, const char *sub_system,
240 const struct GNUNET_PeerIdentity *peer,
241 const char *key,
242 GNUNET_PEERSTORE_Processor iter,
243 void *iter_cls)
244{
245 struct Plugin *plugin = cls;
246 plugin->iter = iter;
247 plugin->iter_cls = iter_cls;
248 plugin->iter_peer = peer;
249 plugin->iter_sub_system = sub_system;
250 plugin->iter_key = key;
251
252 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
253 &iterate_entries,
254 plugin);
255 if (NULL != iter)
256 iter (iter_cls, NULL, NULL);
257 return GNUNET_OK;
258}
259
260
261/**
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.
265 *
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
277 */
278static int
279peerstore_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,
285 void *cont_cls)
286{
287 struct Plugin *plugin = cls;
288 struct GNUNET_HashCode hkey;
289 struct GNUNET_PEERSTORE_Record *entry;
290 const char *peer_id;
291
292
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;
303
304 peer_id = GNUNET_i2s (peer);
305 GNUNET_CRYPTO_hash (peer_id,
306 strlen (peer_id),
307 &hkey);
308
309 if (GNUNET_PEERSTORE_STOREOPTION_REPLACE == options)
310 {
311 peerstore_flat_delete_records (cls, sub_system, peer, key);
312 }
313
314 GNUNET_CONTAINER_multihashmap_put (plugin->hm,
315 &hkey,
316 entry,
317 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
318 if (NULL != cont)
319 {
320 cont (cont_cls, GNUNET_OK);
321 }
322 return GNUNET_OK;
323}
324
325
326/**
327 * Initialize the database connections and associated
328 * data structures (create tables and indices
329 * as needed as well).
330 *
331 * @param plugin the plugin context (state for this module)
332 * @return GNUNET_OK on success
333 */
334static int
335database_setup (struct Plugin *plugin)
336{
337 char *afsdir;
338 char *key;
339 char *sub_system;
340 char *peer_id;
341 char *value;
342 char *expiry;
343 struct GNUNET_DISK_FileHandle *fh;
344 struct GNUNET_PEERSTORE_Record *entry;
345 size_t size;
346 char *buffer;
347 char *line;
348
349 if (GNUNET_OK !=
350 GNUNET_CONFIGURATION_get_value_filename (plugin->cfg, "peerstore-flat",
351 "FILENAME", &afsdir))
352 {
353 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "peerstore-flat",
354 "FILENAME");
355 return GNUNET_SYSERR;
356 }
357 if (GNUNET_OK != GNUNET_DISK_file_test (afsdir))
358 {
359 if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (afsdir))
360 {
361 GNUNET_break (0);
362 GNUNET_free (afsdir);
363 return GNUNET_SYSERR;
364 }
365 }
366 /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
367 plugin->fn = afsdir;
368
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);
374 if (NULL == fh)
375 {
376 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
377 _("Unable to initialize file: %s.\n"),
378 afsdir);
379 return GNUNET_SYSERR;
380 }
381
382 /* Load data from file into hashmap */
383 plugin->hm = GNUNET_CONTAINER_multihashmap_create (10,
384 GNUNET_NO);
385
386 if (GNUNET_SYSERR == GNUNET_DISK_file_size (afsdir,
387 &size,
388 GNUNET_YES,
389 GNUNET_YES))
390 {
391 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
392 _("Unable to get filesize: %s.\n"),
393 afsdir);
394 return GNUNET_SYSERR;
395 }
396
397 buffer = GNUNET_malloc (size);
398
399 if (GNUNET_SYSERR == GNUNET_DISK_file_read (fh,
400 buffer,
401 size))
402 {
403 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
404 _("Unable to read file: %s.\n"),
405 afsdir);
406 return GNUNET_SYSERR;
407 }
408
409 GNUNET_DISK_file_close (fh);
410 if (0 < size) {
411 line = strtok (buffer, "\n");
412 while (line != NULL) {
413 sub_system = strtok (line, ",");
414 if (NULL == sub_system)
415 break;
416 peer_id = strtok (NULL, ",");
417 if (NULL == peer_id)
418 break;
419 key = strtok (NULL, ",");
420 if (NULL == key)
421 break;
422 value = strtok (NULL, ",");
423 if (NULL == value)
424 break;
425 expiry = strtok (NULL, ",");
426 if (NULL == expiry)
427 break;
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,
432 strlen (peer_id),
433 (char**)&entry->peer);
434 entry->value_size = GNUNET_STRINGS_base64_decode (value,
435 strlen (value),
436 (char**)&entry->value);
437 GNUNET_STRINGS_fancy_time_to_absolute (expiry,
438 entry->expiry);
439
440 }
441 }
442 return GNUNET_OK;
443}
444
445static int
446store_and_free_entries (void *cls,
447 const struct GNUNET_HashCode *key,
448 void *value)
449{
450 struct GNUNET_DISK_FileHandle *fh = cls;
451 struct GNUNET_PEERSTORE_Record *entry = value;
452 char *line;
453 char *peer;
454 const char *expiry;
455 char *val;
456
457 GNUNET_STRINGS_base64_encode (entry->value,
458 entry->value_size,
459 &val);
460 expiry = GNUNET_STRINGS_absolute_time_to_string (*entry->expiry);
461 GNUNET_STRINGS_base64_encode ((char*)entry->peer,
462 sizeof (struct GNUNET_PeerIdentity),
463 &peer);
464 GNUNET_asprintf (&line,
465 "%s,%s,%s,%s,%s",
466 entry->sub_system,
467 peer,
468 entry->key,
469 val,
470 expiry);
471 GNUNET_free (val);
472 GNUNET_free (peer);
473 GNUNET_DISK_file_write (fh,
474 line,
475 strlen (line));
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);
481 GNUNET_free (entry);
482 return GNUNET_YES;
483
484}
485
486/**
487 * Shutdown database connection and associate data
488 * structures.
489 * @param plugin the plugin context (state for this module)
490 */
491static void
492database_shutdown (struct Plugin *plugin)
493{
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);
501 if (NULL == fh)
502 {
503 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
504 _("Unable to initialize file: %s.\n"),
505 plugin->fn);
506 return;
507 }
508 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
509 &store_and_free_entries,
510 fh);
511 GNUNET_CONTAINER_multihashmap_destroy (plugin->hm);
512 GNUNET_DISK_file_close (fh);
513}
514
515
516/**
517 * Entry point for the plugin.
518 *
519 * @param cls The struct GNUNET_CONFIGURATION_Handle.
520 * @return NULL on error, otherwise the plugin context
521 */
522void *
523libgnunet_plugin_peerstore_flat_init (void *cls)
524{
525 static struct Plugin plugin;
526 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
527 struct GNUNET_PEERSTORE_PluginFunctions *api;
528
529 if (NULL != plugin.cfg)
530 return NULL; /* can only initialize once! */
531 memset (&plugin, 0, sizeof (struct Plugin));
532 plugin.cfg = cfg;
533 if (GNUNET_OK != database_setup (&plugin))
534 {
535 database_shutdown (&plugin);
536 return NULL;
537 }
538 api = GNUNET_new (struct GNUNET_PEERSTORE_PluginFunctions);
539 api->cls = &plugin;
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");
544 return api;
545}
546
547
548/**
549 * Exit point from the plugin.
550 *
551 * @param cls The plugin context (as returned by "init")
552 * @return Always NULL
553 */
554void *
555libgnunet_plugin_peerstore_flat_done (void *cls)
556{
557 struct GNUNET_PEERSTORE_PluginFunctions *api = cls;
558 struct Plugin *plugin = api->cls;
559
560 database_shutdown (plugin);
561 plugin->cfg = NULL;
562 GNUNET_free (api);
563 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Flat plugin is finished\n");
564 return NULL;
565}
566
567/* end of plugin_peerstore_sqlite.c */