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 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.
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.
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/>.
20 * @file peerstore/gnunet-service-peerstore.c
21 * @brief peerstore service implementation
22 * @author Omar Tarabai
25 #include "gnunet_util_lib.h"
26 #include "peerstore.h"
27 #include "gnunet_peerstore_plugin.h"
28 #include "peerstore_common.h"
32 * Interval for expired records cleanup (in seconds)
34 #define EXPIRED_RECORDS_CLEANUP_INTERVAL 300 /* 5mins */
39 static const struct GNUNET_CONFIGURATION_Handle *cfg;
42 * Database plugin library name
44 static char *db_lib_name;
49 static struct GNUNET_PEERSTORE_PluginFunctions *db;
52 * Hashmap with all watch requests
54 static struct GNUNET_CONTAINER_MultiHashMap *watchers;
57 * Task run to clean up expired records.
59 static struct GNUNET_SCHEDULER_Task *expire_task;
62 * Are we in the process of shutting down the service? #GNUNET_YES / #GNUNET_NO
64 static int in_shutdown;
67 * Number of connected clients.
69 static unsigned int num_clients;
73 * Perform the actual shutdown operations
78 if (NULL != db_lib_name)
81 GNUNET_PLUGIN_unload (db_lib_name,
83 GNUNET_free (db_lib_name);
88 GNUNET_CONTAINER_multihashmap_destroy (watchers);
91 if (NULL != expire_task)
93 GNUNET_SCHEDULER_cancel (expire_task);
96 GNUNET_SCHEDULER_shutdown ();
101 * Task run during shutdown.
106 shutdown_task (void *cls)
108 in_shutdown = GNUNET_YES;
109 if (0 == num_clients) /* Only when no connected clients. */
114 /* Forward declaration */
116 expire_records_continuation (void *cls,
121 * Deletes any expired records from storage
124 cleanup_expired_records (void *cls)
129 GNUNET_assert (NULL != db);
130 ret = db->expire_records (db->cls,
131 GNUNET_TIME_absolute_get (),
132 &expire_records_continuation,
134 if (GNUNET_OK != ret)
136 GNUNET_assert (NULL == expire_task);
137 expire_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
138 (GNUNET_TIME_UNIT_SECONDS,
139 EXPIRED_RECORDS_CLEANUP_INTERVAL),
140 &cleanup_expired_records,
147 * Continuation to expire_records called by the peerstore plugin
150 * @param success count of records deleted or #GNUNET_SYSERR
153 expire_records_continuation (void *cls,
157 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
158 "%d records expired.\n",
160 GNUNET_assert (NULL == expire_task);
161 expire_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
162 (GNUNET_TIME_UNIT_SECONDS,
163 EXPIRED_RECORDS_CLEANUP_INTERVAL),
164 &cleanup_expired_records,
170 * A client disconnected. Remove all of its data structure entries.
172 * @param cls closure, NULL
173 * @param client identification of the client
174 * @param mq the message queue
178 client_connect_cb (void *cls,
179 struct GNUNET_SERVICE_Client *client,
180 struct GNUNET_MQ_Handle *mq)
188 * Search for a disconnected client and remove it
190 * @param cls closuer, a `struct GNUNET_SERVICE_Client`
191 * @param key hash of record key
192 * @param value the watcher client, a `struct GNUNET_SERVICE_Client *`
193 * @return #GNUNET_OK to continue iterating
196 client_disconnect_it (void *cls,
197 const struct GNUNET_HashCode *key,
202 GNUNET_CONTAINER_multihashmap_remove (watchers,
212 * A client disconnected. Remove all of its data structure entries.
214 * @param cls closure, NULL
215 * @param client identification of the client
218 client_disconnect_cb (void *cls,
219 struct GNUNET_SERVICE_Client *client,
222 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
223 "A client disconnected, cleaning up.\n");
224 if (NULL != watchers)
225 GNUNET_CONTAINER_multihashmap_iterate (watchers,
226 &client_disconnect_it,
229 if ( (0 == num_clients) &&
236 * Function called by for each matching record.
239 * @param record peerstore record found
240 * @param emsg error message or NULL if no errors
241 * @return #GNUNET_YES to continue iteration
244 record_iterator (void *cls,
245 const struct GNUNET_PEERSTORE_Record *record,
248 struct GNUNET_PEERSTORE_Record *cls_record = cls;
249 struct GNUNET_MQ_Envelope *env;
253 /* No more records */
254 struct GNUNET_MessageHeader *endmsg;
256 env = GNUNET_MQ_msg (endmsg,
257 GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE_END);
258 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (cls_record->client),
262 GNUNET_SERVICE_client_continue (cls_record->client);
267 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
268 "Failed to iterate: %s\n",
270 GNUNET_SERVICE_client_drop (cls_record->client);
272 PEERSTORE_destroy_record (cls_record);
276 env = PEERSTORE_create_record_mq_envelope (record->sub_system,
283 GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE_RECORD);
284 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (cls_record->client),
290 * Iterator over all watcher clients
291 * to notify them of a new record
293 * @param cls closure, a `struct GNUNET_PEERSTORE_Record *`
294 * @param key hash of record key
295 * @param value the watcher client, a `struct GNUNET_SERVICE_Client *`
296 * @return #GNUNET_YES to continue iterating
299 watch_notifier_it (void *cls,
300 const struct GNUNET_HashCode *key,
303 struct GNUNET_PEERSTORE_Record *record = cls;
304 struct GNUNET_SERVICE_Client *client = value;
305 struct GNUNET_MQ_Envelope *env;
307 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
308 "Found a watcher to update.\n");
309 env = PEERSTORE_create_record_mq_envelope (record->sub_system,
316 GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH_RECORD);
317 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
324 * Given a new record, notifies watchers
326 * @param record changed record to update watchers with
329 watch_notifier (struct GNUNET_PEERSTORE_Record *record)
331 struct GNUNET_HashCode keyhash;
333 PEERSTORE_hash_key (record->sub_system,
337 GNUNET_CONTAINER_multihashmap_get_multiple (watchers,
345 * Handle a watch cancel request from client
347 * @param cls identification of the client
348 * @param hm the actual message
351 handle_watch_cancel (void *cls,
352 const struct StoreKeyHashMessage *hm)
354 struct GNUNET_SERVICE_Client *client = cls;
356 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
357 "Received a watch cancel request.\n");
359 GNUNET_CONTAINER_multihashmap_remove (watchers,
364 GNUNET_SERVICE_client_drop (client);
368 GNUNET_SERVICE_client_continue (client);
373 * Handle a watch request from client
375 * @param cls identification of the client
376 * @param hm the actual message
379 handle_watch (void *cls,
380 const struct StoreKeyHashMessage *hm)
382 struct GNUNET_SERVICE_Client *client = cls;
384 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
385 "Received a watch request.\n");
386 num_clients--; /* do not count watchers */
387 GNUNET_SERVICE_client_mark_monitor (client);
388 GNUNET_CONTAINER_multihashmap_put (watchers,
391 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
392 GNUNET_SERVICE_client_continue (client);
397 * Check an iterate request from client
399 * @param cls client identification of the client
400 * @param srm the actual message
401 * @return #GNUNET_OK if @a srm is well-formed
404 check_iterate (void *cls,
405 const struct StoreRecordMessage *srm)
407 struct GNUNET_PEERSTORE_Record *record;
409 record = PEERSTORE_parse_record_message (srm);
413 return GNUNET_SYSERR;
415 if (NULL == record->sub_system)
418 PEERSTORE_destroy_record (record);
419 return GNUNET_SYSERR;
421 PEERSTORE_destroy_record (record);
427 * Handle an iterate request from client
429 * @param cls identification of the client
430 * @param srm the actual message
433 handle_iterate (void *cls,
434 const struct StoreRecordMessage *srm)
436 struct GNUNET_SERVICE_Client *client = cls;
437 struct GNUNET_PEERSTORE_Record *record;
439 record = PEERSTORE_parse_record_message (srm);
440 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
441 "Iterate request: ss `%s', peer `%s', key `%s'\n",
443 GNUNET_i2s (&record->peer),
444 (NULL == record->key) ? "NULL" : record->key);
445 record->client = client;
447 db->iterate_records (db->cls,
449 (ntohs (srm->peer_set)) ? &record->peer : NULL,
455 GNUNET_SERVICE_client_drop (client);
456 PEERSTORE_destroy_record (record);
462 * Continuation of store_record called by the peerstore plugin
465 * @param success result
468 store_record_continuation (void *cls,
471 struct GNUNET_PEERSTORE_Record *record = cls;
473 if (GNUNET_OK == success)
475 watch_notifier (record);
476 GNUNET_SERVICE_client_continue (record->client);
481 GNUNET_SERVICE_client_drop (record->client);
483 PEERSTORE_destroy_record (record);
488 * Check a store request from client
490 * @param cls client identification of the client
491 * @param srm the actual message
492 * @return #GNUNET_OK if @a srm is well-formed
495 check_store (void *cls,
496 const struct StoreRecordMessage *srm)
498 struct GNUNET_PEERSTORE_Record *record;
500 record = PEERSTORE_parse_record_message (srm);
504 return GNUNET_SYSERR;
506 if ( (NULL == record->sub_system) ||
507 (NULL == record->key) )
510 PEERSTORE_destroy_record (record);
511 return GNUNET_SYSERR;
513 PEERSTORE_destroy_record (record);
519 * Handle a store request from client
521 * @param cls client identification of the client
522 * @param srm the actual message
525 handle_store (void *cls,
526 const struct StoreRecordMessage *srm)
528 struct GNUNET_SERVICE_Client *client = cls;
529 struct GNUNET_PEERSTORE_Record *record;
531 record = PEERSTORE_parse_record_message (srm);
532 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
533 "Received a store request. Sub system `%s' Peer `%s Key `%s' Options: %u.\n",
535 GNUNET_i2s (&record->peer),
537 (uint32_t) ntohl (srm->options));
538 record->client = client;
540 db->store_record (db->cls,
547 ntohl (srm->options),
548 &store_record_continuation,
552 PEERSTORE_destroy_record (record);
553 GNUNET_SERVICE_client_drop (client);
560 * Peerstore service runner.
563 * @param c configuration to use
564 * @param service the initialized service
568 const struct GNUNET_CONFIGURATION_Handle *c,
569 struct GNUNET_SERVICE_Handle *service)
573 in_shutdown = GNUNET_NO;
576 GNUNET_CONFIGURATION_get_value_string (cfg,
581 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
584 GNUNET_SCHEDULER_shutdown ();
587 GNUNET_asprintf (&db_lib_name,
588 "libgnunet_plugin_peerstore_%s",
590 db = GNUNET_PLUGIN_load (db_lib_name,
592 GNUNET_free (database);
595 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
596 _("Could not load database backend `%s'\n"),
598 GNUNET_SCHEDULER_shutdown ();
601 watchers = GNUNET_CONTAINER_multihashmap_create (10,
603 expire_task = GNUNET_SCHEDULER_add_now (&cleanup_expired_records,
605 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
611 * Define "main" method using service macro.
615 GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN,
618 &client_disconnect_cb,
620 GNUNET_MQ_hd_var_size (store,
621 GNUNET_MESSAGE_TYPE_PEERSTORE_STORE,
622 struct StoreRecordMessage,
624 GNUNET_MQ_hd_var_size (iterate,
625 GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE,
626 struct StoreRecordMessage,
628 GNUNET_MQ_hd_fixed_size (watch,
629 GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH,
630 struct StoreKeyHashMessage,
632 GNUNET_MQ_hd_fixed_size (watch_cancel,
633 GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH_CANCEL,
634 struct StoreKeyHashMessage,
636 GNUNET_MQ_handler_end ());
639 /* end of gnunet-service-peerstore.c */