fe7987632af37376f898b9e3a077088f23fb6589
[oweals/gnunet.git] / src / datastore / gnunet-service-datastore.c
1 /*
2      This file is part of GNUnet
3      (C) 2004, 2005, 2006, 2007, 2009 Christian Grothoff (and other contributing authors)
4
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 2, or (at your
8      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      General Public License for more details.
14
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.
19 */
20
21 /**
22  * @file datastore/gnunet-service-datastore.c
23  * @brief Management for the datastore for files stored on a GNUnet node
24  * @author Christian Grothoff
25  *
26  * TODO:
27  * 1) transmit and transmit flow-control (when do we signal client 'success'?
28  *    ALSO: async transmit will need to address ref-counting issues on client!
29  * 2) efficient "update" for client to raise priority / expiration
30  *    (not possible with current datastore API, but plugin API has support!);
31  *    [ maybe integrate desired priority/expiration updates directly
32  *      with 'GET' request? ]
33  * 3) semantics of "PUT" (plugin) if entry exists (should likely
34  *   be similar to "UPDATE" (need to specify in PLUGIN API!)
35  * 4) quota management code!
36  * 5) add bloomfilter for efficiency!
37  */
38
39 #include "platform.h"
40 #include "gnunet_util_lib.h"
41 #include "gnunet_protocols.h"
42 #include "plugin_datastore.h"
43 #include "datastore.h"
44
45
46 /**
47  * Our datastore plugin.
48  */
49 struct DatastorePlugin
50 {
51
52   /**
53    * API of the transport as returned by the plugin's
54    * initialization function.
55    */
56   struct GNUNET_DATASTORE_PluginFunctions *api;
57
58   /**
59    * Short name for the plugin (i.e. "sqlite").
60    */
61   char *short_name;
62
63   /**
64    * Name of the library (i.e. "gnunet_plugin_datastore_sqlite").
65    */
66   char *lib_name;
67
68   /**
69    * Environment this transport service is using
70    * for this plugin.
71    */
72   struct GNUNET_DATASTORE_PluginEnvironment env;
73
74 };
75
76
77 /**
78  * Our datastore plugin (NULL if not available).
79  */
80 static struct DatastorePlugin *plugin;
81
82
83 /**
84  * Transmit the given message to the client.
85  */
86 static void
87 transmit (struct GNUNET_SERVER_Client *client,
88           const struct GNUNET_MessageHeader *msg)
89 {
90   /* FIXME! */
91 }
92
93
94 /**
95  * Transmit the size of the current datastore to the client.
96  */
97 static void
98 transmit_size (struct GNUNET_SERVER_Client *client)
99 {
100   struct SizeMessage sm;
101   
102   sm.header.size = htons(sizeof(struct SizeMessage));
103   sm.header.type = htons(GNUNET_MESSAGE_TYPE_DATASTORE_SIZE);
104   sm.reserved = htonl(0);
105   sm.size = GNUNET_htonll(plugin->api->get_size (plugin->api->cls));
106   transmit (client, &sm.header);
107 }
108
109
110 /**
111  * Function that will transmit the given datastore entry
112  * to the client.
113  *
114  * @param cls closure, pointer to the client (of type GNUNET_SERVER_Client).
115  * @param key key for the content
116  * @param size number of bytes in data
117  * @param data content stored
118  * @param type type of the content
119  * @param priority priority of the content
120  * @param anonymity anonymity-level for the content
121  * @param expiration expiration time for the content
122  * @param uid unique identifier for the datum;
123  *        maybe 0 if no unique identifier is available
124  *
125  * @return GNUNET_SYSERR to abort the iteration, GNUNET_OK to continue,
126  *         GNUNET_NO to delete the item and continue (if supported)
127  */
128 static int
129 transmit_item (void *cls,
130                const GNUNET_HashCode * key,
131                uint32_t size,
132                const void *data,
133                uint32_t type,
134                uint32_t priority,
135                uint32_t anonymity,
136                struct GNUNET_TIME_Absolute
137                expiration, unsigned long long uid)
138 {
139   struct GNUNET_SERVER_Client *client = cls;
140   struct GNUNET_MessageHeader end;
141   struct DataMessage *dm;
142
143   if (key == NULL)
144     {
145       /* transmit 'DATA_END' */
146       end.size = htons(sizeof(struct GNUNET_MessageHeader));
147       end.type = htons(GNUNET_MESSAGE_TYPE_DATASTORE_DATA_END);
148       transmit (client, &end);
149       return GNUNET_OK;
150     }
151   /* FIXME: make use of 'uid' for efficient priority/expiration update! */
152   dm = GNUNET_malloc (sizeof(struct DataMessage) + size);
153   dm->header.size = htons(sizeof(struct DataMessage) + size);
154   dm->header.type = htons(GNUNET_MESSAGE_TYPE_DATASTORE_DATA);
155   dm->reserved = htonl(0);
156   dm->size = htonl(size);
157   dm->type = htonl(type);
158   dm->priority = htonl(priority);
159   dm->anonymity = htonl(anonymity);
160   dm->expiration = GNUNET_TIME_absolute_hton(expiration);
161   dm->key = *key;
162   memcpy (&dm[1], data, size);
163   transmit (client, &dm->header);
164   GNUNET_free (dm);
165   return GNUNET_OK;
166 }
167
168
169 /**
170  * Handle INIT-message.
171  *
172  * @param cls closure
173  * @param client identification of the client
174  * @param message the actual message
175  */
176 static void
177 handle_init (void *cls,
178              struct GNUNET_SERVER_Client *client,
179              const struct GNUNET_MessageHeader *message)
180 {
181   transmit_size (client);
182   GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
183 }
184
185
186 /**
187  * Check that the given message is a valid data message.
188  *
189  * @return NULL if the message is not well-formed, otherwise the message
190  */
191 static const struct DataMessage *
192 check_data (const struct GNUNET_MessageHeader *message)
193 {
194   uint16_t size;
195   uint32_t dsize;
196   const struct DataMessage *dm;
197
198   size = ntohs(message->size);
199   if (size < sizeof(struct DataMessage))
200     { 
201       GNUNET_break (0);
202       return NULL;
203     }
204   dm = (const struct DataMessage *) message;
205   dsize = ntohl(dm->size);
206   if (size != dsize + sizeof(struct DataMessage))
207     {
208       GNUNET_break (0);
209       return NULL;
210     }
211   if ( (ntohl(dm->type) == 0) ||
212        (ntohl(dm->reserved) != 0) )
213     {
214       GNUNET_break (0);
215       return NULL;
216     }
217   return dm;
218 }
219
220
221 /**
222  * Handle PUT-message.
223  *
224  * @param cls closure
225  * @param client identification of the client
226  * @param message the actual message
227  */
228 static void
229 handle_put (void *cls,
230             struct GNUNET_SERVER_Client *client,
231             const struct GNUNET_MessageHeader *message)
232 {
233   const struct DataMessage *dm = check_data (message);
234   if (dm == NULL)
235     {
236       GNUNET_break (0);
237       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
238       return;
239     }
240   plugin->api->put (plugin->api->cls,
241                     &dm->key,
242                     ntohl(dm->size),
243                     &dm[1],
244                     ntohl(dm->type),
245                     ntohl(dm->priority),
246                     ntohl(dm->anonymity),
247                     GNUNET_TIME_absolute_ntoh(dm->expiration));
248   transmit_size (client);
249   GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
250 }
251
252
253 /**
254  * Handle GET-message.
255  *
256  * @param cls closure
257  * @param client identification of the client
258  * @param message the actual message
259  */
260 static void
261 handle_get (void *cls,
262              struct GNUNET_SERVER_Client *client,
263              const struct GNUNET_MessageHeader *message)
264 {
265   const struct GetMessage *msg;
266   uint16_t size;
267
268   size = ntohs(message->size);
269   if ( (size != sizeof(struct GetMessage)) &&
270        (size != sizeof(struct GetMessage) - sizeof(GNUNET_HashCode)) )
271     {
272       GNUNET_break (0);
273       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
274       return;
275     }
276   msg = (const struct GetMessage*) message;
277   plugin->api->get (plugin->api->cls,
278                     ((size == sizeof(struct GetMessage)) ? &msg->key : NULL),
279                     NULL,
280                     ntohl(msg->type),
281                     &transmit_item,
282                     client);    
283   GNUNET_SERVER_receive_done (client, GNUNET_OK);
284 }
285
286
287 /**
288  * Handle GET_RANDOM-message.
289  *
290  * @param cls closure
291  * @param client identification of the client
292  * @param message the actual message
293  */
294 static void
295 handle_get_random (void *cls,
296                    struct GNUNET_SERVER_Client *client,
297                    const struct GNUNET_MessageHeader *message)
298 {
299   plugin->api->iter_migration_order (plugin->api->cls,
300                                      0,
301                                      &transmit_item,
302                                      client);  
303   GNUNET_SERVER_receive_done (client, GNUNET_OK);
304 }
305
306
307 /**
308  * Callback function that will cause the item that is passed
309  * in to be deleted (by returning GNUNET_NO).
310  */
311 static int
312 remove_callback (void *cls,
313                  const GNUNET_HashCode * key,
314                  uint32_t size,
315                  const void *data,
316                  uint32_t type,
317                  uint32_t priority,
318                  uint32_t anonymity,
319                  struct GNUNET_TIME_Absolute
320                  expiration, unsigned long long uid)
321 {
322   return GNUNET_NO;
323 }
324
325
326 /**
327  * Handle REMOVE-message.
328  *
329  * @param cls closure
330  * @param client identification of the client
331  * @param message the actual message
332  */
333 static void
334 handle_remove (void *cls,
335              struct GNUNET_SERVER_Client *client,
336              const struct GNUNET_MessageHeader *message)
337 {
338   const struct DataMessage *dm = check_data (message);
339   GNUNET_HashCode vhash;
340
341   if (dm == NULL)
342     {
343       GNUNET_break (0);
344       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
345       return;
346     }
347   GNUNET_CRYPTO_hash (&dm[1],
348                       ntohl(dm->size),
349                       &vhash);
350   plugin->api->get (plugin->api->cls,
351                     &dm->key,
352                     &vhash,
353                     ntohl(dm->type),
354                     &remove_callback,
355                     NULL);
356   transmit_size (client);
357   GNUNET_SERVER_receive_done (client, GNUNET_OK);
358 }
359
360
361 /**
362  * Handle DROP-message.
363  *
364  * @param cls closure
365  * @param client identification of the client
366  * @param message the actual message
367  */
368 static void
369 handle_drop (void *cls,
370              struct GNUNET_SERVER_Client *client,
371              const struct GNUNET_MessageHeader *message)
372 {
373   plugin->api->drop (plugin->api->cls);
374   GNUNET_SERVER_receive_done (client, GNUNET_OK);
375 }
376
377
378 /**
379  * List of handlers for the messages understood by this
380  * service.
381  */
382 static struct GNUNET_SERVER_MessageHandler handlers[] = {
383   {&handle_init, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_INIT, 
384    sizeof(struct GNUNET_MessageHeader) }, 
385   {&handle_put, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_PUT, 0 }, 
386   {&handle_get, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_GET, 0 }, 
387   {&handle_get_random, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_GET_RANDOM, 
388    sizeof(struct GNUNET_MessageHeader) }, 
389   {&handle_remove, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_REMOVE, 0 }, 
390   {&handle_drop, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_DROP, 
391    sizeof(struct GNUNET_MessageHeader) }, 
392   {NULL, NULL, 0, 0}
393 };
394
395
396
397 /**
398  * Load the datastore plugin.
399  */
400 static struct DatastorePlugin *
401 load_plugin (struct GNUNET_CONFIGURATION_Handle *cfg,
402              struct GNUNET_SCHEDULER_Handle *sched)
403 {
404   struct DatastorePlugin *ret;
405   char *libname;
406   char *name;
407
408   if (GNUNET_OK !=
409       GNUNET_CONFIGURATION_get_value_string (cfg,
410                                              "DATASTORE", "DATABASE", &name))
411     {
412       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
413                   _("No `%s' specified for `%s' in configuration!\n"),
414                   "DATABASE",
415                   "DATASTORE");
416       return NULL;
417     }
418   ret = GNUNET_malloc (sizeof(struct DatastorePlugin));
419   ret->env.cfg = cfg;
420   ret->env.sched = sched;  
421   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
422               _("Loading `%s' datastore plugin\n"), name);
423   GNUNET_asprintf (&libname, "libgnunet_plugin_datastore_%s", name);
424   ret->short_name = GNUNET_strdup (name);
425   ret->lib_name = libname;
426   ret->api = GNUNET_PLUGIN_load (libname, &ret->env);
427   if (ret->api == NULL)
428     {
429       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
430                   _("Failed to load datastore plugin for `%s'\n"), name);
431       GNUNET_free (ret->short_name);
432       GNUNET_free (libname);
433       GNUNET_free (ret);
434       return NULL;
435     }
436   return ret;
437 }
438
439
440 /**
441  * Function called when the service shuts
442  * down.  Unloads our datastore plugin.
443  *
444  * @param cls closure
445  * @param cfg configuration to use
446  */
447 static void
448 unload_plugin (struct DatastorePlugin *plug)
449 {
450 #if DEBUG_DATASTORE
451   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
452               "Datastore service is unloading plugin...\n");
453 #endif
454   GNUNET_break (NULL == GNUNET_PLUGIN_unload (plug->lib_name, plug->api));
455   GNUNET_free (plug->lib_name);
456   GNUNET_free (plug->short_name);
457   GNUNET_free (plug);
458 }
459
460
461 /**
462  * Last task run during shutdown.  Disconnects us from
463  * the transport and core.
464  */
465 static void
466 cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
467 {
468   unload_plugin (plugin);
469   plugin = NULL;
470 }
471
472
473 /**
474  * Process datastore requests.
475  *
476  * @param cls closure
477  * @param sched scheduler to use
478  * @param server the initialized server
479  * @param cfg configuration to use
480  */
481 static void
482 run (void *cls,
483      struct GNUNET_SCHEDULER_Handle *sched,
484      struct GNUNET_SERVER_Handle *server,
485      struct GNUNET_CONFIGURATION_Handle *cfg)
486 {
487   plugin = load_plugin (cfg, sched);
488   if (NULL == plugin)
489     return;
490   GNUNET_SERVER_add_handlers (server, handlers);
491   GNUNET_SCHEDULER_add_delayed (sched,
492                                 GNUNET_YES,
493                                 GNUNET_SCHEDULER_PRIORITY_IDLE,
494                                 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
495                                 GNUNET_TIME_UNIT_FOREVER_REL,
496                                 &cleaning_task, NULL);
497 }
498
499
500 /**
501  * The main function for the datastore service.
502  *
503  * @param argc number of arguments from the command line
504  * @param argv command line arguments
505  * @return 0 ok, 1 on error
506  */
507 int
508 main (int argc, char *const *argv)
509 {
510   int ret;
511
512   ret = (GNUNET_OK ==
513          GNUNET_SERVICE_run (argc,
514                              argv,
515                              "datastore", &run, NULL, NULL, NULL)) ? 0 : 1;
516   return ret;
517 }
518
519
520 /* end of gnunet-service-datastore.c */