2 This file is part of GNUnet.
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., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, 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"
33 * Connected client entry
40 struct ClientEntry *next;
45 struct ClientEntry *prev;
48 * Corresponding server handle.
50 struct GNUNET_SERVER_Client *client;
54 * Interval for expired records cleanup (in seconds)
56 #define EXPIRED_RECORDS_CLEANUP_INTERVAL 300 /* 5mins */
61 static const struct GNUNET_CONFIGURATION_Handle *cfg;
64 * Database plugin library name
66 static char *db_lib_name;
71 static struct GNUNET_PEERSTORE_PluginFunctions *db;
74 * Hashmap with all watch requests
76 static struct GNUNET_CONTAINER_MultiHashMap *watchers;
79 * Our notification context.
81 static struct GNUNET_SERVER_NotificationContext *nc;
84 * Head of linked list of connected clients
86 static struct ClientEntry *client_head;
89 * Tail of linked list of connected clients
91 static struct ClientEntry *client_tail;
94 * Are we in the process of shutting down the service? #GNUNET_YES / #GNUNET_NO
99 * Perform the actual shutdown operations
104 if (NULL != db_lib_name)
106 GNUNET_break (NULL == GNUNET_PLUGIN_unload (db_lib_name, db));
107 GNUNET_free (db_lib_name);
112 GNUNET_SERVER_notification_context_destroy (nc);
115 if (NULL != watchers)
117 GNUNET_CONTAINER_multihashmap_destroy (watchers);
120 GNUNET_SCHEDULER_shutdown ();
125 * Task run during shutdown.
131 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
133 in_shutdown = GNUNET_YES;
134 if (NULL == client_head) /* Only when no connected clients. */
140 * Deletes any expired records from storage
143 cleanup_expired_records (void *cls,
144 const struct GNUNET_SCHEDULER_TaskContext *tc)
148 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
150 GNUNET_assert (NULL != db);
151 deleted = db->expire_records (db->cls, GNUNET_TIME_absolute_get ());
153 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "%d records expired.\n", deleted);
154 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
155 (GNUNET_TIME_UNIT_SECONDS,
156 EXPIRED_RECORDS_CLEANUP_INTERVAL),
157 &cleanup_expired_records, NULL);
162 * Search for a disconnected client and remove it
164 * @param cls closuer, a 'struct GNUNET_PEERSTORE_Record *'
165 * @param key hash of record key
166 * @param value the watcher client, a 'struct GNUNET_SERVER_Client *'
167 * @return #GNUNET_OK to continue iterating
170 client_disconnect_it (void *cls, const struct GNUNET_HashCode *key, void *value)
173 GNUNET_CONTAINER_multihashmap_remove (watchers, key, value);
179 * A client disconnected. Remove all of its data structure entries.
181 * @param cls closure, NULL
182 * @param client identification of the client
185 handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
187 struct ClientEntry *ce;
189 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "A client disconnected, cleaning up.\n");
190 if (NULL != watchers)
191 GNUNET_CONTAINER_multihashmap_iterate (watchers, &client_disconnect_it,
196 if (ce->client == client)
198 GNUNET_CONTAINER_DLL_remove (client_head, client_tail, ce);
204 if (NULL == client_head && in_shutdown)
210 * Function called by for each matching record.
213 * @param record peerstore record found
214 * @param emsg error message or NULL if no errors
215 * @return #GNUNET_YES to continue iteration
218 record_iterator (void *cls, struct GNUNET_PEERSTORE_Record *record, char *emsg)
220 struct GNUNET_SERVER_Client *client = cls;
221 struct StoreRecordMessage *srm;
224 PEERSTORE_create_record_message (record->sub_system, record->peer,
225 record->key, record->value,
226 record->value_size, record->expiry,
227 GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE_RECORD);
228 GNUNET_SERVER_notification_context_unicast (nc, client,
229 (struct GNUNET_MessageHeader *)
237 * Iterator over all watcher clients
238 * to notify them of a new record
240 * @param cls closuer, a 'struct GNUNET_PEERSTORE_Record *'
241 * @param key hash of record key
242 * @param value the watcher client, a 'struct GNUNET_SERVER_Client *'
243 * @return #GNUNET_YES to continue iterating
246 watch_notifier_it (void *cls, const struct GNUNET_HashCode *key, void *value)
248 struct GNUNET_PEERSTORE_Record *record = cls;
249 struct GNUNET_SERVER_Client *client = value;
250 struct StoreRecordMessage *srm;
252 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found a watcher to update.\n");
254 PEERSTORE_create_record_message (record->sub_system, record->peer,
255 record->key, record->value,
256 record->value_size, record->expiry,
257 GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH_RECORD);
258 GNUNET_SERVER_notification_context_unicast (nc, client,
259 (const struct GNUNET_MessageHeader
267 * Given a new record, notifies watchers
269 * @param record changed record to update watchers with
272 watch_notifier (struct GNUNET_PEERSTORE_Record *record)
274 struct GNUNET_HashCode keyhash;
276 PEERSTORE_hash_key (record->sub_system, record->peer, record->key, &keyhash);
277 GNUNET_CONTAINER_multihashmap_get_multiple (watchers, &keyhash,
278 &watch_notifier_it, record);
283 * Handle a watch cancel request from client
286 * @param client identification of the client
287 * @param message the actual message
290 handle_watch_cancel (void *cls, struct GNUNET_SERVER_Client *client,
291 const struct GNUNET_MessageHeader *message)
293 struct StoreKeyHashMessage *hm;
295 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received a watch cancel request.\n");
296 hm = (struct StoreKeyHashMessage *) message;
297 GNUNET_CONTAINER_multihashmap_remove (watchers, &hm->keyhash, client);
298 GNUNET_SERVER_receive_done (client, GNUNET_OK);
303 * Handle a watch request from client
306 * @param client identification of the client
307 * @param message the actual message
310 handle_watch (void *cls, struct GNUNET_SERVER_Client *client,
311 const struct GNUNET_MessageHeader *message)
313 struct StoreKeyHashMessage *hm;
315 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received a watch request.\n");
316 hm = (struct StoreKeyHashMessage *) message;
317 GNUNET_SERVER_client_mark_monitor (client);
318 GNUNET_SERVER_notification_context_add (nc, client);
319 GNUNET_CONTAINER_multihashmap_put (watchers, &hm->keyhash, client,
320 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
321 GNUNET_SERVER_receive_done (client, GNUNET_OK);
326 * Handle an iterate request from client
329 * @param client identification of the client
330 * @param message the actual message
333 handle_iterate (void *cls, struct GNUNET_SERVER_Client *client,
334 const struct GNUNET_MessageHeader *message)
336 struct GNUNET_PEERSTORE_Record *record;
337 struct GNUNET_MessageHeader *endmsg;
339 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received an iterate request.\n");
340 record = PEERSTORE_parse_record_message (message);
343 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Malformed iterate request.\n"));
344 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
347 if (NULL == record->sub_system)
349 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
350 _("Sub system not supplied in client iterate request.\n"));
351 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
354 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
355 "Iterate request: ss `%s', peer `%s', key `%s'\n",
357 (NULL == record->peer) ? "NULL" : GNUNET_i2s (record->peer),
358 (NULL == record->key) ? "NULL" : record->key);
359 GNUNET_SERVER_notification_context_add (nc, client);
361 db->iterate_records (db->cls, record->sub_system, record->peer,
362 record->key, &record_iterator, client))
364 endmsg = GNUNET_new (struct GNUNET_MessageHeader);
366 endmsg->size = htons (sizeof (struct GNUNET_MessageHeader));
367 endmsg->type = htons (GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE_END);
368 GNUNET_SERVER_notification_context_unicast (nc, client, endmsg, GNUNET_NO);
369 GNUNET_free (endmsg);
370 GNUNET_SERVER_receive_done (client, GNUNET_OK);
374 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
376 PEERSTORE_destroy_record (record);
381 * Handle a store request from client
384 * @param client identification of the client
385 * @param message the actual message
388 handle_store (void *cls, struct GNUNET_SERVER_Client *client,
389 const struct GNUNET_MessageHeader *message)
391 struct GNUNET_PEERSTORE_Record *record;
392 struct StoreRecordMessage *srm;
394 record = PEERSTORE_parse_record_message (message);
397 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
398 _("Malformed store request from client\n"));
399 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
402 srm = (struct StoreRecordMessage *) message;
403 if (NULL == record->sub_system || NULL == record->peer || NULL == record->key)
405 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
406 _("Full key not supplied in client store request\n"));
407 PEERSTORE_destroy_record (record);
408 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
411 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
412 "Received a store request (size: %lu) for sub system `%s', peer `%s', key `%s'\n",
413 record->value_size, record->sub_system, GNUNET_i2s (record->peer),
416 db->store_record (db->cls, record->sub_system, record->peer, record->key,
417 record->value, record->value_size, *record->expiry,
420 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
421 _("Failed to store requested value, sqlite database error."));
422 PEERSTORE_destroy_record (record);
423 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
426 GNUNET_SERVER_receive_done (client, GNUNET_OK);
427 watch_notifier (record);
428 PEERSTORE_destroy_record (record);
433 * Creates an entry for a new client or returns it if it already exists.
435 * @param client Client handle
436 * @return Client entry struct
438 static struct ClientEntry *
439 make_client_entry (struct GNUNET_SERVER_Client *client)
441 struct ClientEntry *ce;
446 if (ce->client == client)
450 if (GNUNET_YES == in_shutdown)
452 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
455 ce = GNUNET_new (struct ClientEntry);
457 GNUNET_CONTAINER_DLL_insert (client_head, client_tail, ce);
463 * Callback on a new client connection
465 * @param cls closure (unused)
466 * @param client identification of the client
469 handle_client_connect (void *cls, struct GNUNET_SERVER_Client *client)
471 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New client connection created.\n");
472 make_client_entry (client);
477 * Peerstore service runner.
480 * @param server the initialized server
481 * @param c configuration to use
484 run (void *cls, struct GNUNET_SERVER_Handle *server,
485 const struct GNUNET_CONFIGURATION_Handle *c)
487 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
488 {&handle_store, NULL, GNUNET_MESSAGE_TYPE_PEERSTORE_STORE, 0},
489 {&handle_iterate, NULL, GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE, 0},
490 {&handle_watch, NULL, GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH,
491 sizeof (struct StoreKeyHashMessage)},
492 {&handle_watch_cancel, NULL, GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH_CANCEL,
493 sizeof (struct StoreKeyHashMessage)},
498 in_shutdown = GNUNET_NO;
501 GNUNET_CONFIGURATION_get_value_string (cfg, "peerstore", "DATABASE",
503 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("No database backend configured\n"));
507 GNUNET_asprintf (&db_lib_name, "libgnunet_plugin_peerstore_%s", database);
508 db = GNUNET_PLUGIN_load (db_lib_name, (void *) cfg);
509 GNUNET_free (database);
513 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
514 _("Could not load database backend `%s'\n"), db_lib_name);
515 GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
518 nc = GNUNET_SERVER_notification_context_create (server, 16);
519 watchers = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
520 GNUNET_SCHEDULER_add_now (&cleanup_expired_records, NULL);
521 GNUNET_SERVER_add_handlers (server, handlers);
522 GNUNET_SERVER_connect_notify (server, &handle_client_connect, NULL);
523 GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL);
524 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
530 * The main function for the peerstore service.
532 * @param argc number of arguments from the command line
533 * @param argv command line arguments
534 * @return 0 ok, 1 on error
537 main (int argc, char *const *argv)
540 GNUNET_SERVICE_run (argc, argv, "peerstore",
541 GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN, &run,
545 /* end of gnunet-service-peerstore.c */