GNUnet  0.11.x
gnunet-service-peerstore.c
Go to the documentation of this file.
1 /*
2  This file is part of GNUnet.
3  Copyright (C) 2014, 2015, 2016 GNUnet e.V.
4 
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.
9 
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.
14 
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/>.
17 
18  SPDX-License-Identifier: AGPL3.0-or-later
19  */
20 
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "peerstore.h"
30 #include "peerstore_common.h"
31 
32 
36 #define EXPIRED_RECORDS_CLEANUP_INTERVAL 300 /* 5mins */
37 
41 static const struct GNUNET_CONFIGURATION_Handle *cfg;
42 
46 static char *db_lib_name;
47 
52 
57 
62 
66 static int in_shutdown;
67 
71 static unsigned int num_clients;
72 
73 
77 static void
79 {
81  "Shutting down peerstore, bye.\n");
82  if (NULL != db_lib_name)
83  {
86  db_lib_name = NULL;
87  }
88  if (NULL != watchers)
89  {
91  watchers = NULL;
92  }
93  if (NULL != expire_task)
94  {
95  GNUNET_SCHEDULER_cancel (expire_task);
96  expire_task = NULL;
97  }
99 }
100 
101 
107 static void
108 shutdown_task (void *cls)
109 {
111  "Priming PEERSTORE for shutdown.\n");
113  if (0 == num_clients) /* Only when no connected clients. */
114  do_shutdown ();
115 }
116 
117 
118 /* Forward declaration */
119 static void
120 expire_records_continuation (void *cls, int success);
121 
122 
126 static void
128 {
129  int ret;
130 
131  expire_task = NULL;
132  GNUNET_assert (NULL != db);
133  ret = db->expire_records (db->cls,
136  NULL);
137  if (GNUNET_OK != ret)
138  {
139  GNUNET_assert (NULL == expire_task);
140  expire_task = GNUNET_SCHEDULER_add_delayed (
144  NULL);
145  }
146 }
147 
148 
155 static void
156 expire_records_continuation (void *cls, int success)
157 {
158  if (success > 0)
159  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "%d records expired.\n", success);
160  GNUNET_assert (NULL == expire_task);
161  expire_task = GNUNET_SCHEDULER_add_delayed (
165  NULL);
166 }
167 
168 
177 static void *
178 client_connect_cb (void *cls,
179  struct GNUNET_SERVICE_Client *client,
180  struct GNUNET_MQ_Handle *mq)
181 {
182  num_clients++;
184  "A client connected (now %u)\n", num_clients);
185  return client;
186 }
187 
188 
197 static int
198 client_disconnect_it (void *cls, const struct GNUNET_HashCode *key, void *value)
199 {
200  if (value == cls)
201  {
203  GNUNET_CONTAINER_multihashmap_remove (watchers, key, value));
204  num_clients++; /* Watchers do not count */
205  }
206  return GNUNET_OK;
207 }
208 
209 
216 static void
218  struct GNUNET_SERVICE_Client *client,
219  void *app_cls)
220 {
221  num_clients--;
222  if (NULL != watchers)
225  client);
227  "A client disconnected (%u remaining).\n",
228  num_clients);
229  if ((0 == num_clients) && in_shutdown)
230  do_shutdown ();
231 }
232 
233 
242 static void
243 record_iterator (void *cls,
244  const struct GNUNET_PEERSTORE_Record *record,
245  const char *emsg)
246 {
247  struct GNUNET_PEERSTORE_Record *cls_record = cls;
248  struct GNUNET_MQ_Envelope *env;
249 
250  if (NULL == record)
251  {
252  /* No more records */
253  struct GNUNET_MessageHeader *endmsg;
254 
257  if (NULL == emsg)
258  {
260  }
261  else
262  {
263  GNUNET_break (0);
264  GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to iterate: %s\n", emsg);
265  GNUNET_SERVICE_client_drop (cls_record->client);
266  }
267  PEERSTORE_destroy_record (cls_record);
268  return;
269  }
270 
272  record->sub_system,
273  &record->peer,
274  record->key,
275  record->value,
276  record->value_size,
277  record->expiry,
278  0,
281 }
282 
283 
293 static int
294 watch_notifier_it (void *cls, const struct GNUNET_HashCode *key, void *value)
295 {
296  struct GNUNET_PEERSTORE_Record *record = cls;
297  struct GNUNET_SERVICE_Client *client = value;
298  struct GNUNET_MQ_Envelope *env;
299 
300  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found a watcher to update.\n");
302  record->sub_system,
303  &record->peer,
304  record->key,
305  record->value,
306  record->value_size,
307  record->expiry,
308  0,
311  return GNUNET_YES;
312 }
313 
314 
320 static void
322 {
323  struct GNUNET_HashCode keyhash;
324 
325  PEERSTORE_hash_key (record->sub_system, &record->peer, record->key, &keyhash);
327  &keyhash,
329  record);
330 }
331 
332 
339 static void
340 handle_watch_cancel (void *cls, const struct StoreKeyHashMessage *hm)
341 {
342  struct GNUNET_SERVICE_Client *client = cls;
343 
344  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received a watch cancel request.\n");
345  if (GNUNET_OK !=
346  GNUNET_CONTAINER_multihashmap_remove (watchers, &hm->keyhash, client))
347  {
348  GNUNET_break (0);
350  return;
351  }
352  num_clients++;
354 }
355 
356 
363 static void
364 handle_watch (void *cls, const struct StoreKeyHashMessage *hm)
365 {
366  struct GNUNET_SERVICE_Client *client = cls;
367 
368  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received a watch request.\n");
369  num_clients--; /* do not count watchers */
372  &hm->keyhash,
373  client,
376 }
377 
378 
386 static int
387 check_iterate (void *cls, const struct StoreRecordMessage *srm)
388 {
390 
391  record = PEERSTORE_parse_record_message (srm);
392  if (NULL == record)
393  {
394  GNUNET_break (0);
395  return GNUNET_SYSERR;
396  }
397  if (NULL == record->sub_system)
398  {
399  GNUNET_break (0);
400  PEERSTORE_destroy_record (record);
401  return GNUNET_SYSERR;
402  }
403  PEERSTORE_destroy_record (record);
404  return GNUNET_OK;
405 }
406 
407 
414 static void
415 handle_iterate (void *cls, const struct StoreRecordMessage *srm)
416 {
417  struct GNUNET_SERVICE_Client *client = cls;
419 
420  record = PEERSTORE_parse_record_message (srm);
422  "Iterate request: ss `%s', peer `%s', key `%s'\n",
423  record->sub_system,
424  GNUNET_i2s (&record->peer),
425  (NULL == record->key) ? "NULL" : record->key);
426  record->client = client;
427  if (GNUNET_OK !=
428  db->iterate_records (db->cls,
429  record->sub_system,
430  (ntohs (srm->peer_set)) ? &record->peer : NULL,
431  record->key,
433  record))
434  {
435  GNUNET_break (0);
437  PEERSTORE_destroy_record (record);
438  }
439 }
440 
441 
448 static void
449 store_record_continuation (void *cls, int success)
450 {
451  struct GNUNET_PEERSTORE_Record *record = cls;
452 
453  if (GNUNET_OK == success)
454  {
455  watch_notifier (record);
457  }
458  else
459  {
460  GNUNET_break (0);
462  }
463  PEERSTORE_destroy_record (record);
464 }
465 
466 
474 static int
475 check_store (void *cls, const struct StoreRecordMessage *srm)
476 {
478 
479  record = PEERSTORE_parse_record_message (srm);
480  if (NULL == record)
481  {
482  GNUNET_break (0);
483  return GNUNET_SYSERR;
484  }
485  if ((NULL == record->sub_system) || (NULL == record->key))
486  {
487  GNUNET_break (0);
488  PEERSTORE_destroy_record (record);
489  return GNUNET_SYSERR;
490  }
491  PEERSTORE_destroy_record (record);
492  return GNUNET_OK;
493 }
494 
495 
502 static void
503 handle_store (void *cls, const struct StoreRecordMessage *srm)
504 {
505  struct GNUNET_SERVICE_Client *client = cls;
507 
508  record = PEERSTORE_parse_record_message (srm);
509  GNUNET_log (
511  "Received a store request. Sub system `%s' Peer `%s Key `%s' Options: %u.\n",
512  record->sub_system,
513  GNUNET_i2s (&record->peer),
514  record->key,
515  (uint32_t) ntohl (srm->options));
516  record->client = client;
517  if (GNUNET_OK != db->store_record (db->cls,
518  record->sub_system,
519  &record->peer,
520  record->key,
521  record->value,
522  record->value_size,
523  record->expiry,
524  ntohl (srm->options),
526  record))
527  {
528  GNUNET_break (0);
529  PEERSTORE_destroy_record (record);
531  return;
532  }
533 }
534 
535 
543 static void
544 run (void *cls,
545  const struct GNUNET_CONFIGURATION_Handle *c,
547 {
548  char *database;
549 
551  num_clients = 0;
552  cfg = c;
554  "peerstore",
555  "DATABASE",
556  &database))
557  {
559  "peerstore",
560  "DATABASE");
562  return;
563  }
564  GNUNET_asprintf (&db_lib_name, "libgnunet_plugin_peerstore_%s", database);
565  db = GNUNET_PLUGIN_load (db_lib_name, (void *) cfg);
566  GNUNET_free (database);
567  if (NULL == db)
568  {
570  _ ("Could not load database backend `%s'\n"),
571  db_lib_name);
573  return;
574  }
576  expire_task = GNUNET_SCHEDULER_add_now (&cleanup_expired_records, NULL);
578 }
579 
580 
585  "peerstore",
587  &run,
590  NULL,
591  GNUNET_MQ_hd_var_size (store,
593  struct StoreRecordMessage,
594  NULL),
595  GNUNET_MQ_hd_var_size (iterate,
597  struct StoreRecordMessage,
598  NULL),
601  struct StoreKeyHashMessage,
602  NULL),
603  GNUNET_MQ_hd_fixed_size (watch_cancel,
605  struct StoreKeyHashMessage,
606  NULL),
608 
609 
610 /* end of gnunet-service-peerstore.c */
static unsigned int num_clients
Number of connected clients.
#define GNUNET_MESSAGE_TYPE_PEERSTORE_STORE
Store request message.
Helper peerstore functions.
static void record_iterator(void *cls, const struct GNUNET_PEERSTORE_Record *record, const char *emsg)
Function called by for each matching record.
static struct GNUNET_SERVICE_Handle * service
Handle to our service instance.
struct GNUNET_PEERSTORE_Record * PEERSTORE_parse_record_message(const struct StoreRecordMessage *srm)
Parses a message carrying a record.
static void cleanup_expired_records(void *cls)
Deletes any expired records from storage.
struct GNUNET_PeerIdentity peer
Peer Identity.
void PEERSTORE_destroy_record(struct GNUNET_PEERSTORE_Record *record)
Free any memory allocated for this record.
static void handle_watch(void *cls, const struct StoreKeyHashMessage *hm)
Handle a watch request from client.
Handle to a service.
Definition: service.c:116
static int client_disconnect_it(void *cls, const struct GNUNET_HashCode *key, void *value)
Search for a disconnected client and remove it.
struct GNUNET_MQ_Handle * GNUNET_SERVICE_client_get_mq(struct GNUNET_SERVICE_Client *c)
Obtain the message queue of c.
Definition: service.c:2438
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_shutdown(GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run on shutdown, that is when a CTRL-C signal is received, or when GNUNET_SCHEDULER_shutdown() is being invoked.
Definition: scheduler.c:1331
#define GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH_CANCEL
Watch cancel request.
size_t value_size
Size of value BLOB.
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
struct GNUNET_SERVICE_Client * client
Client from which this record originated.
#define GNUNET_TIME_UNIT_SECONDS
One second.
#define GNUNET_MQ_hd_fixed_size(name, code, str, ctx)
static int ret
Return value of the commandline.
Definition: gnunet-abd.c:81
#define GNUNET_MQ_msg(mvar, type)
Allocate a GNUNET_MQ_Envelope.
Definition: gnunet_mq_lib.h:67
#define GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE
Iteration request.
int(* store_record)(void *cls, const char *sub_system, const struct GNUNET_PeerIdentity *peer, const char *key, const void *value, size_t size, struct GNUNET_TIME_Absolute expiry, enum GNUNET_PEERSTORE_StoreOption options, GNUNET_PEERSTORE_Continuation cont, void *cont_cls)
Store a record in the peerstore.
void GNUNET_SCHEDULER_shutdown(void)
Request the shutdown of a scheduler.
Definition: scheduler.c:531
enum GNUNET_GenericReturnValue GNUNET_CONFIGURATION_get_value_string(const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section, const char *option, char **value)
Get a configuration value that should be a string.
void * cls
Closure to pass to all plugin functions.
Internal representation of the hash map.
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur...
static void * client_connect_cb(void *cls, struct GNUNET_SERVICE_Client *client, struct GNUNET_MQ_Handle *mq)
A client disconnected.
#define GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH_RECORD
Watch response.
#define _(String)
GNU gettext support macro.
Definition: platform.h:178
IPC messages.
void * GNUNET_PLUGIN_unload(const char *library_name, void *arg)
Unload plugin (runs the "done" callback and returns whatever "done" returned).
Definition: plugin.c:269
Handle to a client that is connected to a service.
Definition: service.c:250
#define GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE_RECORD
Iteration record message.
char * key
Record key string.
int(* expire_records)(void *cls, struct GNUNET_TIME_Absolute now, GNUNET_PEERSTORE_Continuation cont, void *cont_cls)
Delete expired records (expiry < now)
static struct GNUNET_SCHEDULER_Task * expire_task
Task run to clean up expired records.
static void handle_watch_cancel(void *cls, const struct StoreKeyHashMessage *hm)
Handle a watch cancel request from client.
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_delayed(struct GNUNET_TIME_Relative delay, GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run with a specified delay.
Definition: scheduler.c:1269
GNUNET_SERVICE_MAIN("peerstore", GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN, &run, &client_connect_cb, &client_disconnect_cb, NULL, GNUNET_MQ_hd_var_size(store, GNUNET_MESSAGE_TYPE_PEERSTORE_STORE, struct StoreRecordMessage, NULL), GNUNET_MQ_hd_var_size(iterate, GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE, struct StoreRecordMessage, NULL), GNUNET_MQ_hd_fixed_size(watch, GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH, struct StoreKeyHashMessage, NULL), GNUNET_MQ_hd_fixed_size(watch_cancel, GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH_CANCEL, struct StoreKeyHashMessage, NULL), GNUNET_MQ_handler_end())
Define "main" method using service macro.
void * value
Record value BLOB.
static void client_disconnect_cb(void *cls, struct GNUNET_SERVICE_Client *client, void *app_cls)
A client disconnected.
void GNUNET_log_config_missing(enum GNUNET_ErrorType kind, const char *section, const char *option)
Log error message about missing configuration option.
static char * value
Value of the record to add/remove.
#define GNUNET_MQ_hd_var_size(name, code, str, ctx)
uint16_t peer_set
GNUNET_YES if peer id value set, GNUNET_NO otherwise
Definition: peerstore.h:46
void GNUNET_CONTAINER_multihashmap_destroy(struct GNUNET_CONTAINER_MultiHashMap *map)
Destroy a hash map.
static void shutdown_task(void *cls)
Task run during shutdown.
Trigger a SOFT server shutdown on signals, allowing active non-monitor clients to complete their tran...
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_now(GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run as soon as possible.
Definition: scheduler.c:1296
int GNUNET_CONTAINER_multihashmap_remove(struct GNUNET_CONTAINER_MultiHashMap *map, const struct GNUNET_HashCode *key, const void *value)
Remove the given key-value pair from the map.
struct GNUNET_HashCode keyhash
Hash of a record key.
Definition: peerstore.h:104
struct GNUNET_TIME_Relative GNUNET_TIME_relative_multiply(struct GNUNET_TIME_Relative rel, unsigned long long factor)
Multiply relative time by a given factor.
Definition: time.c:311
static struct GNUNET_CONTAINER_MultiHashMap * watchers
Hashmap with all watch requests.
void GNUNET_SERVICE_client_mark_monitor(struct GNUNET_SERVICE_Client *c)
Set the &#39;monitor&#39; flag on this client.
Definition: service.c:2408
struct returned by the initialization function of the plugin
A 512-bit hashcode.
void PEERSTORE_hash_key(const char *sub_system, const struct GNUNET_PeerIdentity *peer, const char *key, struct GNUNET_HashCode *ret)
Creates a hash of the given key combination.
void GNUNET_SERVICE_client_drop(struct GNUNET_SERVICE_Client *c)
Ask the server to disconnect from the given client.
Definition: service.c:2325
#define GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH
Watch request.
static void do_shutdown()
Perform the actual shutdown operations.
struct GNUNET_TIME_Absolute GNUNET_TIME_absolute_get(void)
Get the current time.
Definition: time.c:86
int int GNUNET_asprintf(char **buf, const char *format,...) __attribute__((format(printf
Like asprintf, just portable.
struct GNUNET_HashCode key
The key used in the DHT.
struct GNUNET_MQ_Envelope * PEERSTORE_create_record_mq_envelope(const char *sub_system, const struct GNUNET_PeerIdentity *peer, const char *key, const void *value, size_t value_size, struct GNUNET_TIME_Absolute expiry, enum GNUNET_PEERSTORE_StoreOption options, uint16_t msg_type)
Creates a MQ envelope for a single record.
static int watch
Watch value continuously.
int(* iterate_records)(void *cls, const char *sub_system, const struct GNUNET_PeerIdentity *peer, const char *key, GNUNET_PEERSTORE_Processor iter, void *iter_cls)
Iterate over the records given an optional peer id and/or key.
static const struct GNUNET_CONFIGURATION_Handle * cfg
Our configuration.
static void handle_store(void *cls, const struct StoreRecordMessage *srm)
Handle a store request from client.
static void run(void *cls, const struct GNUNET_CONFIGURATION_Handle *c, struct GNUNET_SERVICE_Handle *service)
Peerstore service runner.
struct GNUNET_MQ_Envelope * env
Definition: 005.c:1
uint32_t options
Options, needed only in case of a store operation.
Definition: peerstore.h:80
int GNUNET_CONTAINER_multihashmap_put(struct GNUNET_CONTAINER_MultiHashMap *map, const struct GNUNET_HashCode *key, void *value, enum GNUNET_CONTAINER_MultiHashMapOption opt)
Store a key-value pair in the map.
#define GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE_END
Iteration end message.
static struct GNUNET_PEERSTORE_PluginFunctions * db
Database handle.
static int check_store(void *cls, const struct StoreRecordMessage *srm)
Check a store request from client.
Allow multiple values with the same key.
Handle to a message queue.
Definition: mq.c:85
static void handle_iterate(void *cls, const struct StoreRecordMessage *srm)
Handle an iterate request from client.
int GNUNET_CONTAINER_multihashmap_get_multiple(struct GNUNET_CONTAINER_MultiHashMap *map, const struct GNUNET_HashCode *key, GNUNET_CONTAINER_MulitHashMapIteratorCallback it, void *it_cls)
Iterate over all entries in the map that match a particular key.
#define EXPIRED_RECORDS_CLEANUP_INTERVAL
Interval for expired records cleanup (in seconds)
configuration data
Definition: configuration.c:84
static void store_record_continuation(void *cls, int success)
Continuation of store_record called by the peerstore plugin.
struct GNUNET_TIME_Absolute expiry
Expiry time of entry.
char * sub_system
Responsible sub system string.
static int check_iterate(void *cls, const struct StoreRecordMessage *srm)
Check an iterate request from client.
static int watch_notifier_it(void *cls, const struct GNUNET_HashCode *key, void *value)
Iterator over all watcher clients to notify them of a new record.
Single PEERSTORE record.
struct GNUNET_MQ_Handle * mq
Definition: 003.c:5
#define GNUNET_log(kind,...)
Entry in list of pending tasks.
Definition: scheduler.c:134
static void watch_notifier(struct GNUNET_PEERSTORE_Record *record)
Given a new record, notifies watchers.
Message carrying record key hash.
Definition: peerstore.h:89
static void record(void *cls, size_t data_size, const void *data)
Process recorded audio data.
struct GNUNET_CONTAINER_MultiHashMap * GNUNET_CONTAINER_multihashmap_create(unsigned int len, int do_not_copy_keys)
Create a multi hash map.
Message carrying a PEERSTORE record message.
Definition: peerstore.h:36
Header for all communications.
void * GNUNET_PLUGIN_load(const char *library_name, void *arg)
Setup plugin (runs the "init" callback and returns whatever "init" returned).
Definition: plugin.c:217
void GNUNET_MQ_send(struct GNUNET_MQ_Handle *mq, struct GNUNET_MQ_Envelope *ev)
Send a message with the given message queue.
Definition: mq.c:355
int GNUNET_CONTAINER_multihashmap_iterate(struct GNUNET_CONTAINER_MultiHashMap *map, GNUNET_CONTAINER_MulitHashMapIteratorCallback it, void *it_cls)
Iterate over all entries in the map.
static int in_shutdown
Are we in the process of shutting down the service? GNUNET_YES / GNUNET_NO.
const char * GNUNET_i2s(const struct GNUNET_PeerIdentity *pid)
Convert a peer identity to a string (for printing debug messages).
void GNUNET_SERVICE_client_continue(struct GNUNET_SERVICE_Client *c)
Continue receiving further messages from the given client.
Definition: service.c:2244
#define GNUNET_MQ_handler_end()
End-marker for the handlers array.
static void expire_records_continuation(void *cls, int success)
Continuation to expire_records called by the peerstore plugin.
#define GNUNET_free(ptr)
Wrapper around free.
void * GNUNET_SCHEDULER_cancel(struct GNUNET_SCHEDULER_Task *task)
Cancel the task with the specified identifier.
Definition: scheduler.c:972
static char * db_lib_name
Database plugin library name.