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),
263 GNUNET_SERVICE_client_continue (cls_record->client);
265 GNUNET_SERVICE_client_drop (cls_record->client);
266 PEERSTORE_destroy_record (cls_record);
270 env = PEERSTORE_create_record_mq_envelope (record->sub_system,
277 GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE_RECORD);
278 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (cls_record->client),
284 * Iterator over all watcher clients
285 * to notify them of a new record
287 * @param cls closure, a `struct GNUNET_PEERSTORE_Record *`
288 * @param key hash of record key
289 * @param value the watcher client, a `struct GNUNET_SERVICE_Client *`
290 * @return #GNUNET_YES to continue iterating
293 watch_notifier_it (void *cls,
294 const struct GNUNET_HashCode *key,
297 struct GNUNET_PEERSTORE_Record *record = cls;
298 struct GNUNET_SERVICE_Client *client = value;
299 struct GNUNET_MQ_Envelope *env;
301 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
302 "Found a watcher to update.\n");
303 env = PEERSTORE_create_record_mq_envelope (record->sub_system,
310 GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH_RECORD);
311 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
318 * Given a new record, notifies watchers
320 * @param record changed record to update watchers with
323 watch_notifier (struct GNUNET_PEERSTORE_Record *record)
325 struct GNUNET_HashCode keyhash;
327 PEERSTORE_hash_key (record->sub_system,
331 GNUNET_CONTAINER_multihashmap_get_multiple (watchers,
339 * Handle a watch cancel request from client
341 * @param cls identification of the client
342 * @param hm the actual message
345 handle_watch_cancel (void *cls,
346 const struct StoreKeyHashMessage *hm)
348 struct GNUNET_SERVICE_Client *client = cls;
350 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
351 "Received a watch cancel request.\n");
353 GNUNET_CONTAINER_multihashmap_remove (watchers,
358 GNUNET_SERVICE_client_drop (client);
362 GNUNET_SERVICE_client_continue (client);
367 * Handle a watch request from client
369 * @param cls identification of the client
370 * @param hm the actual message
373 handle_watch (void *cls,
374 const struct StoreKeyHashMessage *hm)
376 struct GNUNET_SERVICE_Client *client = cls;
378 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
379 "Received a watch request.\n");
380 num_clients--; /* do not count watchers */
381 GNUNET_SERVICE_client_mark_monitor (client);
382 GNUNET_CONTAINER_multihashmap_put (watchers,
385 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
386 GNUNET_SERVICE_client_continue (client);
391 * Check an iterate request from client
393 * @param cls client identification of the client
394 * @param srm the actual message
395 * @return #GNUNET_OK if @a srm is well-formed
398 check_iterate (void *cls,
399 const struct StoreRecordMessage *srm)
401 struct GNUNET_PEERSTORE_Record *record;
403 record = PEERSTORE_parse_record_message (srm);
407 return GNUNET_SYSERR;
409 if (NULL == record->sub_system)
412 PEERSTORE_destroy_record (record);
413 return GNUNET_SYSERR;
420 * Handle an iterate request from client
422 * @param cls identification of the client
423 * @param srm the actual message
426 handle_iterate (void *cls,
427 const struct StoreRecordMessage *srm)
429 struct GNUNET_SERVICE_Client *client = cls;
430 struct GNUNET_PEERSTORE_Record *record;
432 record = PEERSTORE_parse_record_message (srm);
433 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
434 "Iterate request: ss `%s', peer `%s', key `%s'\n",
436 (NULL == record->peer) ? "NULL" : GNUNET_i2s (record->peer),
437 (NULL == record->key) ? "NULL" : record->key);
438 record->client = client;
440 db->iterate_records (db->cls,
447 GNUNET_SERVICE_client_drop (client);
448 PEERSTORE_destroy_record (record);
454 * Continuation of store_record called by the peerstore plugin
457 * @param success result
460 store_record_continuation (void *cls,
463 struct GNUNET_PEERSTORE_Record *record = cls;
465 if (GNUNET_OK == success)
467 watch_notifier (record);
468 GNUNET_SERVICE_client_continue (record->client);
472 GNUNET_SERVICE_client_drop (record->client);
474 PEERSTORE_destroy_record (record);
479 * Check a store request from client
481 * @param cls client identification of the client
482 * @param srm the actual message
483 * @return #GNUNET_OK if @a srm is well-formed
486 check_store (void *cls,
487 const struct StoreRecordMessage *srm)
489 struct GNUNET_PEERSTORE_Record *record;
491 record = PEERSTORE_parse_record_message (srm);
495 return GNUNET_SYSERR;
497 if ( (NULL == record->sub_system) ||
498 (NULL == record->peer) ||
499 (NULL == record->key) )
502 PEERSTORE_destroy_record (record);
503 return GNUNET_SYSERR;
505 PEERSTORE_destroy_record (record);
511 * Handle a store request from client
513 * @param cls client identification of the client
514 * @param srm the actual message
517 handle_store (void *cls,
518 const struct StoreRecordMessage *srm)
520 struct GNUNET_SERVICE_Client *client = cls;
521 struct GNUNET_PEERSTORE_Record *record;
523 record = PEERSTORE_parse_record_message (srm);
524 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
525 "Received a store request. Sub system `%s' Peer `%s Key `%s' Options: %d.\n",
527 GNUNET_i2s (record->peer),
529 ntohl (srm->options));
530 record->client = client;
532 db->store_record (db->cls,
539 ntohl (srm->options),
540 &store_record_continuation,
543 PEERSTORE_destroy_record (record);
544 GNUNET_SERVICE_client_drop (client);
551 * Peerstore service runner.
554 * @param c configuration to use
555 * @param service the initialized service
559 const struct GNUNET_CONFIGURATION_Handle *c,
560 struct GNUNET_SERVICE_Handle *service)
564 in_shutdown = GNUNET_NO;
567 GNUNET_CONFIGURATION_get_value_string (cfg,
572 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
575 GNUNET_SCHEDULER_shutdown ();
578 GNUNET_asprintf (&db_lib_name,
579 "libgnunet_plugin_peerstore_%s",
581 db = GNUNET_PLUGIN_load (db_lib_name,
583 GNUNET_free (database);
586 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
587 _("Could not load database backend `%s'\n"),
589 GNUNET_SCHEDULER_shutdown ();
592 watchers = GNUNET_CONTAINER_multihashmap_create (10,
594 expire_task = GNUNET_SCHEDULER_add_now (&cleanup_expired_records,
596 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
602 * Define "main" method using service macro.
606 GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN,
609 &client_disconnect_cb,
611 GNUNET_MQ_hd_var_size (store,
612 GNUNET_MESSAGE_TYPE_PEERSTORE_STORE,
613 struct StoreRecordMessage,
615 GNUNET_MQ_hd_var_size (iterate,
616 GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE,
617 struct StoreRecordMessage,
619 GNUNET_MQ_hd_fixed_size (watch,
620 GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH,
621 struct StoreKeyHashMessage,
623 GNUNET_MQ_hd_fixed_size (watch_cancel,
624 GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH_CANCEL,
625 struct StoreKeyHashMessage,
627 GNUNET_MQ_handler_end ());
630 /* end of gnunet-service-peerstore.c */