2 This file is part of GNUnet.
3 Copyright (C) 2014, 2015, 2016 GNUnet e.V.
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/gnunet-service-peerstore.c
23 * @brief peerstore service implementation
24 * @author Omar Tarabai
27 #include "gnunet_util_lib.h"
28 #include "peerstore.h"
29 #include "gnunet_peerstore_plugin.h"
30 #include "peerstore_common.h"
34 * Interval for expired records cleanup (in seconds)
36 #define EXPIRED_RECORDS_CLEANUP_INTERVAL 300 /* 5mins */
41 static const struct GNUNET_CONFIGURATION_Handle *cfg;
44 * Database plugin library name
46 static char *db_lib_name;
51 static struct GNUNET_PEERSTORE_PluginFunctions *db;
54 * Hashmap with all watch requests
56 static struct GNUNET_CONTAINER_MultiHashMap *watchers;
59 * Task run to clean up expired records.
61 static struct GNUNET_SCHEDULER_Task *expire_task;
64 * Are we in the process of shutting down the service? #GNUNET_YES / #GNUNET_NO
66 static int in_shutdown;
69 * Number of connected clients.
71 static unsigned int num_clients;
75 * Perform the actual shutdown operations
80 if (NULL != db_lib_name)
83 GNUNET_PLUGIN_unload (db_lib_name,
85 GNUNET_free (db_lib_name);
90 GNUNET_CONTAINER_multihashmap_destroy (watchers);
93 if (NULL != expire_task)
95 GNUNET_SCHEDULER_cancel (expire_task);
98 GNUNET_SCHEDULER_shutdown ();
103 * Task run during shutdown.
108 shutdown_task (void *cls)
110 in_shutdown = GNUNET_YES;
111 if (0 == num_clients) /* Only when no connected clients. */
116 /* Forward declaration */
118 expire_records_continuation (void *cls,
123 * Deletes any expired records from storage
126 cleanup_expired_records (void *cls)
131 GNUNET_assert (NULL != db);
132 ret = db->expire_records (db->cls,
133 GNUNET_TIME_absolute_get (),
134 &expire_records_continuation,
136 if (GNUNET_OK != ret)
138 GNUNET_assert (NULL == expire_task);
139 expire_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
140 (GNUNET_TIME_UNIT_SECONDS,
141 EXPIRED_RECORDS_CLEANUP_INTERVAL),
142 &cleanup_expired_records,
149 * Continuation to expire_records called by the peerstore plugin
152 * @param success count of records deleted or #GNUNET_SYSERR
155 expire_records_continuation (void *cls,
159 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
160 "%d records expired.\n",
162 GNUNET_assert (NULL == expire_task);
163 expire_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
164 (GNUNET_TIME_UNIT_SECONDS,
165 EXPIRED_RECORDS_CLEANUP_INTERVAL),
166 &cleanup_expired_records,
172 * A client disconnected. Remove all of its data structure entries.
174 * @param cls closure, NULL
175 * @param client identification of the client
176 * @param mq the message queue
180 client_connect_cb (void *cls,
181 struct GNUNET_SERVICE_Client *client,
182 struct GNUNET_MQ_Handle *mq)
190 * Search for a disconnected client and remove it
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
198 client_disconnect_it (void *cls,
199 const struct GNUNET_HashCode *key,
204 GNUNET_CONTAINER_multihashmap_remove (watchers,
214 * A client disconnected. Remove all of its data structure entries.
216 * @param cls closure, NULL
217 * @param client identification of the client
220 client_disconnect_cb (void *cls,
221 struct GNUNET_SERVICE_Client *client,
224 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
225 "A client disconnected, cleaning up.\n");
226 if (NULL != watchers)
227 GNUNET_CONTAINER_multihashmap_iterate (watchers,
228 &client_disconnect_it,
231 if ( (0 == num_clients) &&
238 * Function called by for each matching record.
241 * @param record peerstore record found
242 * @param emsg error message or NULL if no errors
243 * @return #GNUNET_YES to continue iteration
246 record_iterator (void *cls,
247 const struct GNUNET_PEERSTORE_Record *record,
250 struct GNUNET_PEERSTORE_Record *cls_record = cls;
251 struct GNUNET_MQ_Envelope *env;
255 /* No more records */
256 struct GNUNET_MessageHeader *endmsg;
258 env = GNUNET_MQ_msg (endmsg,
259 GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE_END);
260 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (cls_record->client),
264 GNUNET_SERVICE_client_continue (cls_record->client);
269 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
270 "Failed to iterate: %s\n",
272 GNUNET_SERVICE_client_drop (cls_record->client);
274 PEERSTORE_destroy_record (cls_record);
278 env = PEERSTORE_create_record_mq_envelope (record->sub_system,
285 GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE_RECORD);
286 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (cls_record->client),
292 * Iterator over all watcher clients
293 * to notify them of a new record
295 * @param cls closure, a `struct GNUNET_PEERSTORE_Record *`
296 * @param key hash of record key
297 * @param value the watcher client, a `struct GNUNET_SERVICE_Client *`
298 * @return #GNUNET_YES to continue iterating
301 watch_notifier_it (void *cls,
302 const struct GNUNET_HashCode *key,
305 struct GNUNET_PEERSTORE_Record *record = cls;
306 struct GNUNET_SERVICE_Client *client = value;
307 struct GNUNET_MQ_Envelope *env;
309 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
310 "Found a watcher to update.\n");
311 env = PEERSTORE_create_record_mq_envelope (record->sub_system,
318 GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH_RECORD);
319 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
326 * Given a new record, notifies watchers
328 * @param record changed record to update watchers with
331 watch_notifier (struct GNUNET_PEERSTORE_Record *record)
333 struct GNUNET_HashCode keyhash;
335 PEERSTORE_hash_key (record->sub_system,
339 GNUNET_CONTAINER_multihashmap_get_multiple (watchers,
347 * Handle a watch cancel request from client
349 * @param cls identification of the client
350 * @param hm the actual message
353 handle_watch_cancel (void *cls,
354 const struct StoreKeyHashMessage *hm)
356 struct GNUNET_SERVICE_Client *client = cls;
358 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
359 "Received a watch cancel request.\n");
361 GNUNET_CONTAINER_multihashmap_remove (watchers,
366 GNUNET_SERVICE_client_drop (client);
370 GNUNET_SERVICE_client_continue (client);
375 * Handle a watch request from client
377 * @param cls identification of the client
378 * @param hm the actual message
381 handle_watch (void *cls,
382 const struct StoreKeyHashMessage *hm)
384 struct GNUNET_SERVICE_Client *client = cls;
386 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
387 "Received a watch request.\n");
388 num_clients--; /* do not count watchers */
389 GNUNET_SERVICE_client_mark_monitor (client);
390 GNUNET_CONTAINER_multihashmap_put (watchers,
393 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
394 GNUNET_SERVICE_client_continue (client);
399 * Check an iterate request from client
401 * @param cls client identification of the client
402 * @param srm the actual message
403 * @return #GNUNET_OK if @a srm is well-formed
406 check_iterate (void *cls,
407 const struct StoreRecordMessage *srm)
409 struct GNUNET_PEERSTORE_Record *record;
411 record = PEERSTORE_parse_record_message (srm);
415 return GNUNET_SYSERR;
417 if (NULL == record->sub_system)
420 PEERSTORE_destroy_record (record);
421 return GNUNET_SYSERR;
423 PEERSTORE_destroy_record (record);
429 * Handle an iterate request from client
431 * @param cls identification of the client
432 * @param srm the actual message
435 handle_iterate (void *cls,
436 const struct StoreRecordMessage *srm)
438 struct GNUNET_SERVICE_Client *client = cls;
439 struct GNUNET_PEERSTORE_Record *record;
441 record = PEERSTORE_parse_record_message (srm);
442 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
443 "Iterate request: ss `%s', peer `%s', key `%s'\n",
445 GNUNET_i2s (&record->peer),
446 (NULL == record->key) ? "NULL" : record->key);
447 record->client = client;
449 db->iterate_records (db->cls,
451 (ntohs (srm->peer_set)) ? &record->peer : NULL,
457 GNUNET_SERVICE_client_drop (client);
458 PEERSTORE_destroy_record (record);
464 * Continuation of store_record called by the peerstore plugin
467 * @param success result
470 store_record_continuation (void *cls,
473 struct GNUNET_PEERSTORE_Record *record = cls;
475 if (GNUNET_OK == success)
477 watch_notifier (record);
478 GNUNET_SERVICE_client_continue (record->client);
483 GNUNET_SERVICE_client_drop (record->client);
485 PEERSTORE_destroy_record (record);
490 * Check a store request from client
492 * @param cls client identification of the client
493 * @param srm the actual message
494 * @return #GNUNET_OK if @a srm is well-formed
497 check_store (void *cls,
498 const struct StoreRecordMessage *srm)
500 struct GNUNET_PEERSTORE_Record *record;
502 record = PEERSTORE_parse_record_message (srm);
506 return GNUNET_SYSERR;
508 if ( (NULL == record->sub_system) ||
509 (NULL == record->key) )
512 PEERSTORE_destroy_record (record);
513 return GNUNET_SYSERR;
515 PEERSTORE_destroy_record (record);
521 * Handle a store request from client
523 * @param cls client identification of the client
524 * @param srm the actual message
527 handle_store (void *cls,
528 const struct StoreRecordMessage *srm)
530 struct GNUNET_SERVICE_Client *client = cls;
531 struct GNUNET_PEERSTORE_Record *record;
533 record = PEERSTORE_parse_record_message (srm);
534 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
535 "Received a store request. Sub system `%s' Peer `%s Key `%s' Options: %u.\n",
537 GNUNET_i2s (&record->peer),
539 (uint32_t) ntohl (srm->options));
540 record->client = client;
542 db->store_record (db->cls,
549 ntohl (srm->options),
550 &store_record_continuation,
554 PEERSTORE_destroy_record (record);
555 GNUNET_SERVICE_client_drop (client);
562 * Peerstore service runner.
565 * @param c configuration to use
566 * @param service the initialized service
570 const struct GNUNET_CONFIGURATION_Handle *c,
571 struct GNUNET_SERVICE_Handle *service)
575 in_shutdown = GNUNET_NO;
578 GNUNET_CONFIGURATION_get_value_string (cfg,
583 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
586 GNUNET_SCHEDULER_shutdown ();
589 GNUNET_asprintf (&db_lib_name,
590 "libgnunet_plugin_peerstore_%s",
592 db = GNUNET_PLUGIN_load (db_lib_name,
594 GNUNET_free (database);
597 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
598 _("Could not load database backend `%s'\n"),
600 GNUNET_SCHEDULER_shutdown ();
603 watchers = GNUNET_CONTAINER_multihashmap_create (10,
605 expire_task = GNUNET_SCHEDULER_add_now (&cleanup_expired_records,
607 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
613 * Define "main" method using service macro.
617 GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN,
620 &client_disconnect_cb,
622 GNUNET_MQ_hd_var_size (store,
623 GNUNET_MESSAGE_TYPE_PEERSTORE_STORE,
624 struct StoreRecordMessage,
626 GNUNET_MQ_hd_var_size (iterate,
627 GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE,
628 struct StoreRecordMessage,
630 GNUNET_MQ_hd_fixed_size (watch,
631 GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH,
632 struct StoreKeyHashMessage,
634 GNUNET_MQ_hd_fixed_size (watch_cancel,
635 GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH_CANCEL,
636 struct StoreKeyHashMessage,
638 GNUNET_MQ_handler_end ());
641 /* end of gnunet-service-peerstore.c */