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;
415 PEERSTORE_destroy_record (record);
421 * Handle an iterate request from client
423 * @param cls identification of the client
424 * @param srm the actual message
427 handle_iterate (void *cls,
428 const struct StoreRecordMessage *srm)
430 struct GNUNET_SERVICE_Client *client = cls;
431 struct GNUNET_PEERSTORE_Record *record;
433 record = PEERSTORE_parse_record_message (srm);
434 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
435 "Iterate request: ss `%s', peer `%s', key `%s'\n",
437 (NULL == record->peer) ? "NULL" : GNUNET_i2s (record->peer),
438 (NULL == record->key) ? "NULL" : record->key);
439 record->client = client;
441 db->iterate_records (db->cls,
448 GNUNET_SERVICE_client_drop (client);
449 PEERSTORE_destroy_record (record);
455 * Continuation of store_record called by the peerstore plugin
458 * @param success result
461 store_record_continuation (void *cls,
464 struct GNUNET_PEERSTORE_Record *record = cls;
466 if (GNUNET_OK == success)
468 watch_notifier (record);
469 GNUNET_SERVICE_client_continue (record->client);
473 GNUNET_SERVICE_client_drop (record->client);
475 PEERSTORE_destroy_record (record);
480 * Check a store request from client
482 * @param cls client identification of the client
483 * @param srm the actual message
484 * @return #GNUNET_OK if @a srm is well-formed
487 check_store (void *cls,
488 const struct StoreRecordMessage *srm)
490 struct GNUNET_PEERSTORE_Record *record;
492 record = PEERSTORE_parse_record_message (srm);
496 return GNUNET_SYSERR;
498 if ( (NULL == record->sub_system) ||
499 (NULL == record->peer) ||
500 (NULL == record->key) )
503 PEERSTORE_destroy_record (record);
504 return GNUNET_SYSERR;
506 PEERSTORE_destroy_record (record);
512 * Handle a store request from client
514 * @param cls client identification of the client
515 * @param srm the actual message
518 handle_store (void *cls,
519 const struct StoreRecordMessage *srm)
521 struct GNUNET_SERVICE_Client *client = cls;
522 struct GNUNET_PEERSTORE_Record *record;
524 record = PEERSTORE_parse_record_message (srm);
525 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
526 "Received a store request. Sub system `%s' Peer `%s Key `%s' Options: %u.\n",
528 GNUNET_i2s (record->peer),
530 (uint32_t) ntohl (srm->options));
531 record->client = client;
533 db->store_record (db->cls,
540 ntohl (srm->options),
541 &store_record_continuation,
544 PEERSTORE_destroy_record (record);
545 GNUNET_SERVICE_client_drop (client);
552 * Peerstore service runner.
555 * @param c configuration to use
556 * @param service the initialized service
560 const struct GNUNET_CONFIGURATION_Handle *c,
561 struct GNUNET_SERVICE_Handle *service)
565 in_shutdown = GNUNET_NO;
568 GNUNET_CONFIGURATION_get_value_string (cfg,
573 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
576 GNUNET_SCHEDULER_shutdown ();
579 GNUNET_asprintf (&db_lib_name,
580 "libgnunet_plugin_peerstore_%s",
582 db = GNUNET_PLUGIN_load (db_lib_name,
584 GNUNET_free (database);
587 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
588 _("Could not load database backend `%s'\n"),
590 GNUNET_SCHEDULER_shutdown ();
593 watchers = GNUNET_CONTAINER_multihashmap_create (10,
595 expire_task = GNUNET_SCHEDULER_add_now (&cleanup_expired_records,
597 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
603 * Define "main" method using service macro.
607 GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN,
610 &client_disconnect_cb,
612 GNUNET_MQ_hd_var_size (store,
613 GNUNET_MESSAGE_TYPE_PEERSTORE_STORE,
614 struct StoreRecordMessage,
616 GNUNET_MQ_hd_var_size (iterate,
617 GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE,
618 struct StoreRecordMessage,
620 GNUNET_MQ_hd_fixed_size (watch,
621 GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH,
622 struct StoreKeyHashMessage,
624 GNUNET_MQ_hd_fixed_size (watch_cancel,
625 GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH_CANCEL,
626 struct StoreKeyHashMessage,
628 GNUNET_MQ_handler_end ());
631 /* end of gnunet-service-peerstore.c */