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.
17 * @file peerstore/gnunet-service-peerstore.c
18 * @brief peerstore service implementation
19 * @author Omar Tarabai
22 #include "gnunet_util_lib.h"
23 #include "peerstore.h"
24 #include "gnunet_peerstore_plugin.h"
25 #include "peerstore_common.h"
29 * Interval for expired records cleanup (in seconds)
31 #define EXPIRED_RECORDS_CLEANUP_INTERVAL 300 /* 5mins */
36 static const struct GNUNET_CONFIGURATION_Handle *cfg;
39 * Database plugin library name
41 static char *db_lib_name;
46 static struct GNUNET_PEERSTORE_PluginFunctions *db;
49 * Hashmap with all watch requests
51 static struct GNUNET_CONTAINER_MultiHashMap *watchers;
54 * Task run to clean up expired records.
56 static struct GNUNET_SCHEDULER_Task *expire_task;
59 * Are we in the process of shutting down the service? #GNUNET_YES / #GNUNET_NO
61 static int in_shutdown;
64 * Number of connected clients.
66 static unsigned int num_clients;
70 * Perform the actual shutdown operations
75 if (NULL != db_lib_name)
78 GNUNET_PLUGIN_unload (db_lib_name,
80 GNUNET_free (db_lib_name);
85 GNUNET_CONTAINER_multihashmap_destroy (watchers);
88 if (NULL != expire_task)
90 GNUNET_SCHEDULER_cancel (expire_task);
93 GNUNET_SCHEDULER_shutdown ();
98 * Task run during shutdown.
103 shutdown_task (void *cls)
105 in_shutdown = GNUNET_YES;
106 if (0 == num_clients) /* Only when no connected clients. */
111 /* Forward declaration */
113 expire_records_continuation (void *cls,
118 * Deletes any expired records from storage
121 cleanup_expired_records (void *cls)
126 GNUNET_assert (NULL != db);
127 ret = db->expire_records (db->cls,
128 GNUNET_TIME_absolute_get (),
129 &expire_records_continuation,
131 if (GNUNET_OK != ret)
133 GNUNET_assert (NULL == expire_task);
134 expire_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
135 (GNUNET_TIME_UNIT_SECONDS,
136 EXPIRED_RECORDS_CLEANUP_INTERVAL),
137 &cleanup_expired_records,
144 * Continuation to expire_records called by the peerstore plugin
147 * @param success count of records deleted or #GNUNET_SYSERR
150 expire_records_continuation (void *cls,
154 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
155 "%d records expired.\n",
157 GNUNET_assert (NULL == expire_task);
158 expire_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
159 (GNUNET_TIME_UNIT_SECONDS,
160 EXPIRED_RECORDS_CLEANUP_INTERVAL),
161 &cleanup_expired_records,
167 * A client disconnected. Remove all of its data structure entries.
169 * @param cls closure, NULL
170 * @param client identification of the client
171 * @param mq the message queue
175 client_connect_cb (void *cls,
176 struct GNUNET_SERVICE_Client *client,
177 struct GNUNET_MQ_Handle *mq)
185 * Search for a disconnected client and remove it
187 * @param cls closuer, a `struct GNUNET_SERVICE_Client`
188 * @param key hash of record key
189 * @param value the watcher client, a `struct GNUNET_SERVICE_Client *`
190 * @return #GNUNET_OK to continue iterating
193 client_disconnect_it (void *cls,
194 const struct GNUNET_HashCode *key,
199 GNUNET_CONTAINER_multihashmap_remove (watchers,
209 * A client disconnected. Remove all of its data structure entries.
211 * @param cls closure, NULL
212 * @param client identification of the client
215 client_disconnect_cb (void *cls,
216 struct GNUNET_SERVICE_Client *client,
219 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
220 "A client disconnected, cleaning up.\n");
221 if (NULL != watchers)
222 GNUNET_CONTAINER_multihashmap_iterate (watchers,
223 &client_disconnect_it,
226 if ( (0 == num_clients) &&
233 * Function called by for each matching record.
236 * @param record peerstore record found
237 * @param emsg error message or NULL if no errors
238 * @return #GNUNET_YES to continue iteration
241 record_iterator (void *cls,
242 const struct GNUNET_PEERSTORE_Record *record,
245 struct GNUNET_PEERSTORE_Record *cls_record = cls;
246 struct GNUNET_MQ_Envelope *env;
250 /* No more records */
251 struct GNUNET_MessageHeader *endmsg;
253 env = GNUNET_MQ_msg (endmsg,
254 GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE_END);
255 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (cls_record->client),
259 GNUNET_SERVICE_client_continue (cls_record->client);
264 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
265 "Failed to iterate: %s\n",
267 GNUNET_SERVICE_client_drop (cls_record->client);
269 PEERSTORE_destroy_record (cls_record);
273 env = PEERSTORE_create_record_mq_envelope (record->sub_system,
280 GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE_RECORD);
281 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (cls_record->client),
287 * Iterator over all watcher clients
288 * to notify them of a new record
290 * @param cls closure, a `struct GNUNET_PEERSTORE_Record *`
291 * @param key hash of record key
292 * @param value the watcher client, a `struct GNUNET_SERVICE_Client *`
293 * @return #GNUNET_YES to continue iterating
296 watch_notifier_it (void *cls,
297 const struct GNUNET_HashCode *key,
300 struct GNUNET_PEERSTORE_Record *record = cls;
301 struct GNUNET_SERVICE_Client *client = value;
302 struct GNUNET_MQ_Envelope *env;
304 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
305 "Found a watcher to update.\n");
306 env = PEERSTORE_create_record_mq_envelope (record->sub_system,
313 GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH_RECORD);
314 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
321 * Given a new record, notifies watchers
323 * @param record changed record to update watchers with
326 watch_notifier (struct GNUNET_PEERSTORE_Record *record)
328 struct GNUNET_HashCode keyhash;
330 PEERSTORE_hash_key (record->sub_system,
334 GNUNET_CONTAINER_multihashmap_get_multiple (watchers,
342 * Handle a watch cancel request from client
344 * @param cls identification of the client
345 * @param hm the actual message
348 handle_watch_cancel (void *cls,
349 const struct StoreKeyHashMessage *hm)
351 struct GNUNET_SERVICE_Client *client = cls;
353 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
354 "Received a watch cancel request.\n");
356 GNUNET_CONTAINER_multihashmap_remove (watchers,
361 GNUNET_SERVICE_client_drop (client);
365 GNUNET_SERVICE_client_continue (client);
370 * Handle a watch request from client
372 * @param cls identification of the client
373 * @param hm the actual message
376 handle_watch (void *cls,
377 const struct StoreKeyHashMessage *hm)
379 struct GNUNET_SERVICE_Client *client = cls;
381 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
382 "Received a watch request.\n");
383 num_clients--; /* do not count watchers */
384 GNUNET_SERVICE_client_mark_monitor (client);
385 GNUNET_CONTAINER_multihashmap_put (watchers,
388 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
389 GNUNET_SERVICE_client_continue (client);
394 * Check an iterate request from client
396 * @param cls client identification of the client
397 * @param srm the actual message
398 * @return #GNUNET_OK if @a srm is well-formed
401 check_iterate (void *cls,
402 const struct StoreRecordMessage *srm)
404 struct GNUNET_PEERSTORE_Record *record;
406 record = PEERSTORE_parse_record_message (srm);
410 return GNUNET_SYSERR;
412 if (NULL == record->sub_system)
415 PEERSTORE_destroy_record (record);
416 return GNUNET_SYSERR;
418 PEERSTORE_destroy_record (record);
424 * Handle an iterate request from client
426 * @param cls identification of the client
427 * @param srm the actual message
430 handle_iterate (void *cls,
431 const struct StoreRecordMessage *srm)
433 struct GNUNET_SERVICE_Client *client = cls;
434 struct GNUNET_PEERSTORE_Record *record;
436 record = PEERSTORE_parse_record_message (srm);
437 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
438 "Iterate request: ss `%s', peer `%s', key `%s'\n",
440 GNUNET_i2s (&record->peer),
441 (NULL == record->key) ? "NULL" : record->key);
442 record->client = client;
444 db->iterate_records (db->cls,
446 (ntohs (srm->peer_set)) ? &record->peer : NULL,
452 GNUNET_SERVICE_client_drop (client);
453 PEERSTORE_destroy_record (record);
459 * Continuation of store_record called by the peerstore plugin
462 * @param success result
465 store_record_continuation (void *cls,
468 struct GNUNET_PEERSTORE_Record *record = cls;
470 if (GNUNET_OK == success)
472 watch_notifier (record);
473 GNUNET_SERVICE_client_continue (record->client);
478 GNUNET_SERVICE_client_drop (record->client);
480 PEERSTORE_destroy_record (record);
485 * Check a store request from client
487 * @param cls client identification of the client
488 * @param srm the actual message
489 * @return #GNUNET_OK if @a srm is well-formed
492 check_store (void *cls,
493 const struct StoreRecordMessage *srm)
495 struct GNUNET_PEERSTORE_Record *record;
497 record = PEERSTORE_parse_record_message (srm);
501 return GNUNET_SYSERR;
503 if ( (NULL == record->sub_system) ||
504 (NULL == record->key) )
507 PEERSTORE_destroy_record (record);
508 return GNUNET_SYSERR;
510 PEERSTORE_destroy_record (record);
516 * Handle a store request from client
518 * @param cls client identification of the client
519 * @param srm the actual message
522 handle_store (void *cls,
523 const struct StoreRecordMessage *srm)
525 struct GNUNET_SERVICE_Client *client = cls;
526 struct GNUNET_PEERSTORE_Record *record;
528 record = PEERSTORE_parse_record_message (srm);
529 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
530 "Received a store request. Sub system `%s' Peer `%s Key `%s' Options: %u.\n",
532 GNUNET_i2s (&record->peer),
534 (uint32_t) ntohl (srm->options));
535 record->client = client;
537 db->store_record (db->cls,
544 ntohl (srm->options),
545 &store_record_continuation,
549 PEERSTORE_destroy_record (record);
550 GNUNET_SERVICE_client_drop (client);
557 * Peerstore service runner.
560 * @param c configuration to use
561 * @param service the initialized service
565 const struct GNUNET_CONFIGURATION_Handle *c,
566 struct GNUNET_SERVICE_Handle *service)
570 in_shutdown = GNUNET_NO;
573 GNUNET_CONFIGURATION_get_value_string (cfg,
578 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
581 GNUNET_SCHEDULER_shutdown ();
584 GNUNET_asprintf (&db_lib_name,
585 "libgnunet_plugin_peerstore_%s",
587 db = GNUNET_PLUGIN_load (db_lib_name,
589 GNUNET_free (database);
592 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
593 _("Could not load database backend `%s'\n"),
595 GNUNET_SCHEDULER_shutdown ();
598 watchers = GNUNET_CONTAINER_multihashmap_create (10,
600 expire_task = GNUNET_SCHEDULER_add_now (&cleanup_expired_records,
602 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
608 * Define "main" method using service macro.
612 GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN,
615 &client_disconnect_cb,
617 GNUNET_MQ_hd_var_size (store,
618 GNUNET_MESSAGE_TYPE_PEERSTORE_STORE,
619 struct StoreRecordMessage,
621 GNUNET_MQ_hd_var_size (iterate,
622 GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE,
623 struct StoreRecordMessage,
625 GNUNET_MQ_hd_fixed_size (watch,
626 GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH,
627 struct StoreKeyHashMessage,
629 GNUNET_MQ_hd_fixed_size (watch_cancel,
630 GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH_CANCEL,
631 struct StoreKeyHashMessage,
633 GNUNET_MQ_handler_end ());
636 /* end of gnunet-service-peerstore.c */