aboutsummaryrefslogtreecommitdiff
path: root/src/peerstore/gnunet-service-peerstore.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/peerstore/gnunet-service-peerstore.c')
-rw-r--r--src/peerstore/gnunet-service-peerstore.c610
1 files changed, 0 insertions, 610 deletions
diff --git a/src/peerstore/gnunet-service-peerstore.c b/src/peerstore/gnunet-service-peerstore.c
deleted file mode 100644
index 959d088f9..000000000
--- a/src/peerstore/gnunet-service-peerstore.c
+++ /dev/null
@@ -1,610 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2014, 2015, 2016 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/gnunet-service-peerstore.c
23 * @brief peerstore service implementation
24 * @author Omar Tarabai
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "peerstore.h"
29#include "gnunet_peerstore_plugin.h"
30#include "peerstore_common.h"
31
32
33/**
34 * Interval for expired records cleanup (in seconds)
35 */
36#define EXPIRED_RECORDS_CLEANUP_INTERVAL 300 /* 5mins */
37
38/**
39 * Our configuration.
40 */
41static const struct GNUNET_CONFIGURATION_Handle *cfg;
42
43/**
44 * Database plugin library name
45 */
46static char *db_lib_name;
47
48/**
49 * Database handle
50 */
51static struct GNUNET_PEERSTORE_PluginFunctions *db;
52
53/**
54 * Hashmap with all watch requests
55 */
56static struct GNUNET_CONTAINER_MultiHashMap *watchers;
57
58/**
59 * Task run to clean up expired records.
60 */
61static struct GNUNET_SCHEDULER_Task *expire_task;
62
63/**
64 * Are we in the process of shutting down the service? #GNUNET_YES / #GNUNET_NO
65 */
66static int in_shutdown;
67
68/**
69 * Number of connected clients.
70 */
71static unsigned int num_clients;
72
73
74/**
75 * Perform the actual shutdown operations
76 */
77static void
78do_shutdown ()
79{
80 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
81 "Shutting down peerstore, bye.\n");
82 if (NULL != db_lib_name)
83 {
84 GNUNET_break (NULL == GNUNET_PLUGIN_unload (db_lib_name, db));
85 GNUNET_free (db_lib_name);
86 db_lib_name = NULL;
87 }
88 if (NULL != watchers)
89 {
90 GNUNET_CONTAINER_multihashmap_destroy (watchers);
91 watchers = NULL;
92 }
93 if (NULL != expire_task)
94 {
95 GNUNET_SCHEDULER_cancel (expire_task);
96 expire_task = NULL;
97 }
98 GNUNET_SCHEDULER_shutdown ();
99}
100
101
102/**
103 * Task run during shutdown.
104 *
105 * @param cls unused
106 */
107static void
108shutdown_task (void *cls)
109{
110 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
111 "Priming PEERSTORE for shutdown.\n");
112 in_shutdown = GNUNET_YES;
113 if (0 == num_clients) /* Only when no connected clients. */
114 do_shutdown ();
115}
116
117
118/* Forward declaration */
119static void
120expire_records_continuation (void *cls, int success);
121
122
123/**
124 * Deletes any expired records from storage
125 */
126static void
127cleanup_expired_records (void *cls)
128{
129 int ret;
130
131 expire_task = NULL;
132 GNUNET_assert (NULL != db);
133 ret = db->expire_records (db->cls,
134 GNUNET_TIME_absolute_get (),
135 &expire_records_continuation,
136 NULL);
137 if (GNUNET_OK != ret)
138 {
139 GNUNET_assert (NULL == expire_task);
140 expire_task = GNUNET_SCHEDULER_add_delayed (
141 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
142 EXPIRED_RECORDS_CLEANUP_INTERVAL),
143 &cleanup_expired_records,
144 NULL);
145 }
146}
147
148
149/**
150 * Continuation to expire_records called by the peerstore plugin
151 *
152 * @param cls unused
153 * @param success count of records deleted or #GNUNET_SYSERR
154 */
155static void
156expire_records_continuation (void *cls, int success)
157{
158 if (success > 0)
159 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "%d records expired.\n", success);
160 GNUNET_assert (NULL == expire_task);
161 expire_task = GNUNET_SCHEDULER_add_delayed (
162 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
163 EXPIRED_RECORDS_CLEANUP_INTERVAL),
164 &cleanup_expired_records,
165 NULL);
166}
167
168
169/**
170 * A client disconnected. Remove all of its data structure entries.
171 *
172 * @param cls closure, NULL
173 * @param client identification of the client
174 * @param mq the message queue
175 * @return
176 */
177static void *
178client_connect_cb (void *cls,
179 struct GNUNET_SERVICE_Client *client,
180 struct GNUNET_MQ_Handle *mq)
181{
182 num_clients++;
183 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
184 "A client connected (now %u)\n", num_clients);
185 return client;
186}
187
188
189/**
190 * Search for a disconnected client and remove it
191 *
192 * @param cls closuer, a `struct GNUNET_SERVICE_Client`
193 * @param key hash of record key
194 * @param value the watcher client, a `struct GNUNET_SERVICE_Client *`
195 * @return #GNUNET_OK to continue iterating
196 */
197static int
198client_disconnect_it (void *cls, const struct GNUNET_HashCode *key, void *value)
199{
200 if (value == cls)
201 {
202 GNUNET_assert (GNUNET_YES ==
203 GNUNET_CONTAINER_multihashmap_remove (watchers, key, value));
204 num_clients++; /* Watchers do not count */
205 }
206 return GNUNET_OK;
207}
208
209
210/**
211 * A client disconnected. Remove all of its data structure entries.
212 *
213 * @param cls closure, NULL
214 * @param client identification of the client
215 */
216static void
217client_disconnect_cb (void *cls,
218 struct GNUNET_SERVICE_Client *client,
219 void *app_cls)
220{
221 num_clients--;
222 if (NULL != watchers)
223 GNUNET_CONTAINER_multihashmap_iterate (watchers,
224 &client_disconnect_it,
225 client);
226 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
227 "A client disconnected (%u remaining).\n",
228 num_clients);
229 if ((0 == num_clients) && in_shutdown)
230 do_shutdown ();
231}
232
233
234/**
235 * Function called by for each matching record.
236 *
237 * @param cls closure
238 * @param record peerstore record found
239 * @param emsg error message or NULL if no errors
240 * @return #GNUNET_YES to continue iteration
241 */
242static void
243record_iterator (void *cls,
244 const struct GNUNET_PEERSTORE_Record *record,
245 const char *emsg)
246{
247 struct GNUNET_PEERSTORE_Record *cls_record = cls;
248 struct GNUNET_MQ_Envelope *env;
249
250 if (NULL == record)
251 {
252 /* No more records */
253 struct GNUNET_MessageHeader *endmsg;
254
255 env = GNUNET_MQ_msg (endmsg, GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE_END);
256 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (cls_record->client), env);
257 if (NULL == emsg)
258 {
259 GNUNET_SERVICE_client_continue (cls_record->client);
260 }
261 else
262 {
263 GNUNET_break (0);
264 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to iterate: %s\n", emsg);
265 GNUNET_SERVICE_client_drop (cls_record->client);
266 }
267 PEERSTORE_destroy_record (cls_record);
268 return;
269 }
270
271 env = PEERSTORE_create_record_mq_envelope (
272 record->sub_system,
273 &record->peer,
274 record->key,
275 record->value,
276 record->value_size,
277 record->expiry,
278 0,
279 GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE_RECORD);
280 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (cls_record->client), env);
281}
282
283
284/**
285 * Iterator over all watcher clients
286 * to notify them of a new record
287 *
288 * @param cls closure, a `struct GNUNET_PEERSTORE_Record *`
289 * @param key hash of record key
290 * @param value the watcher client, a `struct GNUNET_SERVICE_Client *`
291 * @return #GNUNET_YES to continue iterating
292 */
293static int
294watch_notifier_it (void *cls, const struct GNUNET_HashCode *key, void *value)
295{
296 struct GNUNET_PEERSTORE_Record *record = cls;
297 struct GNUNET_SERVICE_Client *client = value;
298 struct GNUNET_MQ_Envelope *env;
299
300 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found a watcher to update.\n");
301 env = PEERSTORE_create_record_mq_envelope (
302 record->sub_system,
303 &record->peer,
304 record->key,
305 record->value,
306 record->value_size,
307 record->expiry,
308 0,
309 GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH_RECORD);
310 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
311 return GNUNET_YES;
312}
313
314
315/**
316 * Given a new record, notifies watchers
317 *
318 * @param record changed record to update watchers with
319 */
320static void
321watch_notifier (struct GNUNET_PEERSTORE_Record *record)
322{
323 struct GNUNET_HashCode keyhash;
324
325 PEERSTORE_hash_key (record->sub_system, &record->peer, record->key, &keyhash);
326 GNUNET_CONTAINER_multihashmap_get_multiple (watchers,
327 &keyhash,
328 &watch_notifier_it,
329 record);
330}
331
332
333/**
334 * Handle a watch cancel request from client
335 *
336 * @param cls identification of the client
337 * @param hm the actual message
338 */
339static void
340handle_watch_cancel (void *cls, const struct StoreKeyHashMessage *hm)
341{
342 struct GNUNET_SERVICE_Client *client = cls;
343
344 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received a watch cancel request.\n");
345 if (GNUNET_OK !=
346 GNUNET_CONTAINER_multihashmap_remove (watchers, &hm->keyhash, client))
347 {
348 GNUNET_break (0);
349 GNUNET_SERVICE_client_drop (client);
350 return;
351 }
352 num_clients++;
353 GNUNET_SERVICE_client_continue (client);
354}
355
356
357/**
358 * Handle a watch request from client
359 *
360 * @param cls identification of the client
361 * @param hm the actual message
362 */
363static void
364handle_watch (void *cls, const struct StoreKeyHashMessage *hm)
365{
366 struct GNUNET_SERVICE_Client *client = cls;
367
368 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received a watch request.\n");
369 num_clients--; /* do not count watchers */
370 GNUNET_SERVICE_client_mark_monitor (client);
371 GNUNET_CONTAINER_multihashmap_put (watchers,
372 &hm->keyhash,
373 client,
374 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
375 GNUNET_SERVICE_client_continue (client);
376}
377
378
379/**
380 * Check an iterate request from client
381 *
382 * @param cls client identification of the client
383 * @param srm the actual message
384 * @return #GNUNET_OK if @a srm is well-formed
385 */
386static int
387check_iterate (void *cls, const struct StoreRecordMessage *srm)
388{
389 struct GNUNET_PEERSTORE_Record *record;
390
391 record = PEERSTORE_parse_record_message (srm);
392 if (NULL == record)
393 {
394 GNUNET_break (0);
395 return GNUNET_SYSERR;
396 }
397 if (NULL == record->sub_system)
398 {
399 GNUNET_break (0);
400 PEERSTORE_destroy_record (record);
401 return GNUNET_SYSERR;
402 }
403 PEERSTORE_destroy_record (record);
404 return GNUNET_OK;
405}
406
407
408/**
409 * Handle an iterate request from client
410 *
411 * @param cls identification of the client
412 * @param srm the actual message
413 */
414static void
415handle_iterate (void *cls, const struct StoreRecordMessage *srm)
416{
417 struct GNUNET_SERVICE_Client *client = cls;
418 struct GNUNET_PEERSTORE_Record *record;
419
420 record = PEERSTORE_parse_record_message (srm);
421 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
422 "Iterate request: ss `%s', peer `%s', key `%s'\n",
423 record->sub_system,
424 GNUNET_i2s (&record->peer),
425 (NULL == record->key) ? "NULL" : record->key);
426 record->client = client;
427 if (GNUNET_OK !=
428 db->iterate_records (db->cls,
429 record->sub_system,
430 (ntohs (srm->peer_set)) ? &record->peer : NULL,
431 record->key,
432 &record_iterator,
433 record))
434 {
435 GNUNET_break (0);
436 GNUNET_SERVICE_client_drop (client);
437 PEERSTORE_destroy_record (record);
438 }
439}
440
441
442/**
443 * Continuation of store_record called by the peerstore plugin
444 *
445 * @param cls closure
446 * @param success result
447 */
448static void
449store_record_continuation (void *cls, int success)
450{
451 struct GNUNET_PEERSTORE_Record *record = cls;
452
453 if (GNUNET_OK == success)
454 {
455 watch_notifier (record);
456 GNUNET_SERVICE_client_continue (record->client);
457 }
458 else
459 {
460 GNUNET_break (0);
461 GNUNET_SERVICE_client_drop (record->client);
462 }
463 PEERSTORE_destroy_record (record);
464}
465
466
467/**
468 * Check a store request from client
469 *
470 * @param cls client identification of the client
471 * @param srm the actual message
472 * @return #GNUNET_OK if @a srm is well-formed
473 */
474static int
475check_store (void *cls, const struct StoreRecordMessage *srm)
476{
477 struct GNUNET_PEERSTORE_Record *record;
478
479 record = PEERSTORE_parse_record_message (srm);
480 if (NULL == record)
481 {
482 GNUNET_break (0);
483 return GNUNET_SYSERR;
484 }
485 if ((NULL == record->sub_system) || (NULL == record->key))
486 {
487 GNUNET_break (0);
488 PEERSTORE_destroy_record (record);
489 return GNUNET_SYSERR;
490 }
491 PEERSTORE_destroy_record (record);
492 return GNUNET_OK;
493}
494
495
496/**
497 * Handle a store request from client
498 *
499 * @param cls client identification of the client
500 * @param srm the actual message
501 */
502static void
503handle_store (void *cls, const struct StoreRecordMessage *srm)
504{
505 struct GNUNET_SERVICE_Client *client = cls;
506 struct GNUNET_PEERSTORE_Record *record;
507
508 record = PEERSTORE_parse_record_message (srm);
509 GNUNET_log (
510 GNUNET_ERROR_TYPE_INFO,
511 "Received a store request. Sub system `%s' Peer `%s Key `%s' Options: %u.\n",
512 record->sub_system,
513 GNUNET_i2s (&record->peer),
514 record->key,
515 (uint32_t) ntohl (srm->options));
516 record->client = client;
517 if (GNUNET_OK != db->store_record (db->cls,
518 record->sub_system,
519 &record->peer,
520 record->key,
521 record->value,
522 record->value_size,
523 record->expiry,
524 ntohl (srm->options),
525 &store_record_continuation,
526 record))
527 {
528 GNUNET_break (0);
529 PEERSTORE_destroy_record (record);
530 GNUNET_SERVICE_client_drop (client);
531 return;
532 }
533}
534
535
536/**
537 * Peerstore service runner.
538 *
539 * @param cls closure
540 * @param c configuration to use
541 * @param service the initialized service
542 */
543static void
544run (void *cls,
545 const struct GNUNET_CONFIGURATION_Handle *c,
546 struct GNUNET_SERVICE_Handle *service)
547{
548 char *database;
549
550 in_shutdown = GNUNET_NO;
551 num_clients = 0;
552 cfg = c;
553 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
554 "peerstore",
555 "DATABASE",
556 &database))
557 {
558 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
559 "peerstore",
560 "DATABASE");
561 GNUNET_SCHEDULER_shutdown ();
562 return;
563 }
564 GNUNET_asprintf (&db_lib_name, "libgnunet_plugin_peerstore_%s", database);
565 db = GNUNET_PLUGIN_load (db_lib_name, (void *) cfg);
566 GNUNET_free (database);
567 if (NULL == db)
568 {
569 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
570 _ ("Could not load database backend `%s'\n"),
571 db_lib_name);
572 GNUNET_SCHEDULER_shutdown ();
573 return;
574 }
575 watchers = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
576 expire_task = GNUNET_SCHEDULER_add_now (&cleanup_expired_records, NULL);
577 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
578}
579
580
581/**
582 * Define "main" method using service macro.
583 */
584GNUNET_SERVICE_MAIN (
585 "peerstore",
586 GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN,
587 &run,
588 &client_connect_cb,
589 &client_disconnect_cb,
590 NULL,
591 GNUNET_MQ_hd_var_size (store,
592 GNUNET_MESSAGE_TYPE_PEERSTORE_STORE,
593 struct StoreRecordMessage,
594 NULL),
595 GNUNET_MQ_hd_var_size (iterate,
596 GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE,
597 struct StoreRecordMessage,
598 NULL),
599 GNUNET_MQ_hd_fixed_size (watch,
600 GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH,
601 struct StoreKeyHashMessage,
602 NULL),
603 GNUNET_MQ_hd_fixed_size (watch_cancel,
604 GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH_CANCEL,
605 struct StoreKeyHashMessage,
606 NULL),
607 GNUNET_MQ_handler_end ());
608
609
610/* end of gnunet-service-peerstore.c */