improving datastore API
[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 a status code to the client.
96  *
97  * @param client receiver of the response
98  * @param code status code
99  * @param msg optional error message (can be NULL)
100  */
101 static void
102 transmit_status (struct GNUNET_SERVER_Client *client,
103                  int code,
104                  const char *msg)
105 {
106   struct StatusMessage *sm;
107   size_t slen;
108
109   slen = (msg == NULL) ? 0 : strlen(msg) + 1;  
110   sm = GNUNET_malloc (sizeof(struct StatusMessage) + slen);
111   sm->header.size = htons(sizeof(struct StatusMessage) + slen);
112   sm->header.type = htons(GNUNET_MESSAGE_TYPE_DATASTORE_STATUS);
113   sm->status = htonl(code);
114   memcpy (&sm[1], msg, slen);  
115   transmit (client, &sm->header);
116   GNUNET_free (sm);
117 }
118
119
120 /**
121  * Function that will transmit the given datastore entry
122  * to the client.
123  *
124  * @param cls closure, pointer to the client (of type GNUNET_SERVER_Client).
125  * @param key key for the content
126  * @param size number of bytes in data
127  * @param data content stored
128  * @param type type of the content
129  * @param priority priority of the content
130  * @param anonymity anonymity-level for the content
131  * @param expiration expiration time for the content
132  * @param uid unique identifier for the datum;
133  *        maybe 0 if no unique identifier is available
134  *
135  * @return GNUNET_SYSERR to abort the iteration, GNUNET_OK to continue,
136  *         GNUNET_NO to delete the item and continue (if supported)
137  */
138 static int
139 transmit_item (void *cls,
140                const GNUNET_HashCode * key,
141                uint32_t size,
142                const void *data,
143                uint32_t type,
144                uint32_t priority,
145                uint32_t anonymity,
146                struct GNUNET_TIME_Absolute
147                expiration, unsigned long long uid)
148 {
149   struct GNUNET_SERVER_Client *client = cls;
150   struct GNUNET_MessageHeader end;
151   struct DataMessage *dm;
152
153   if (key == NULL)
154     {
155       /* transmit 'DATA_END' */
156       end.size = htons(sizeof(struct GNUNET_MessageHeader));
157       end.type = htons(GNUNET_MESSAGE_TYPE_DATASTORE_DATA_END);
158       transmit (client, &end);
159       return GNUNET_OK;
160     }
161   dm = GNUNET_malloc (sizeof(struct DataMessage) + size);
162   dm->header.size = htons(sizeof(struct DataMessage) + size);
163   dm->header.type = htons(GNUNET_MESSAGE_TYPE_DATASTORE_DATA);
164   dm->rid = htonl(0);
165   dm->size = htonl(size);
166   dm->type = htonl(type);
167   dm->priority = htonl(priority);
168   dm->anonymity = htonl(anonymity);
169   dm->expiration = GNUNET_TIME_absolute_hton(expiration);
170   dm->uid = GNUNET_htonll(uid);
171   dm->key = *key;
172   memcpy (&dm[1], data, size);
173   transmit (client, &dm->header);
174   GNUNET_free (dm);
175   return GNUNET_OK;
176 }
177
178
179 /**
180  * Handle RESERVE-message.
181  *
182  * @param cls closure
183  * @param client identification of the client
184  * @param message the actual message
185  */
186 static void
187 handle_reserve (void *cls,
188              struct GNUNET_SERVER_Client *client,
189              const struct GNUNET_MessageHeader *message)
190 {
191   transmit_status (client, GNUNET_SYSERR, "not implemented");
192   GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
193 }
194
195
196 /**
197  * Handle RELEASE_RESERVE-message.
198  *
199  * @param cls closure
200  * @param client identification of the client
201  * @param message the actual message
202  */
203 static void
204 handle_release_reserve (void *cls,
205                         struct GNUNET_SERVER_Client *client,
206                         const struct GNUNET_MessageHeader *message)
207 {
208   transmit_status (client, GNUNET_SYSERR, "not implemented");
209   GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
210 }
211
212
213 /**
214  * Check that the given message is a valid data message.
215  *
216  * @return NULL if the message is not well-formed, otherwise the message
217  */
218 static const struct DataMessage *
219 check_data (const struct GNUNET_MessageHeader *message)
220 {
221   uint16_t size;
222   uint32_t dsize;
223   const struct DataMessage *dm;
224
225   size = ntohs(message->size);
226   if (size < sizeof(struct DataMessage))
227     { 
228       GNUNET_break (0);
229       return NULL;
230     }
231   dm = (const struct DataMessage *) message;
232   dsize = ntohl(dm->size);
233   if (size != dsize + sizeof(struct DataMessage))
234     {
235       GNUNET_break (0);
236       return NULL;
237     }
238   if (ntohl(dm->type) == 0) 
239     {
240       GNUNET_break (0);
241       return NULL;
242     }
243   return dm;
244 }
245
246
247 /**
248  * Handle PUT-message.
249  *
250  * @param cls closure
251  * @param client identification of the client
252  * @param message the actual message
253  */
254 static void
255 handle_put (void *cls,
256             struct GNUNET_SERVER_Client *client,
257             const struct GNUNET_MessageHeader *message)
258 {
259   const struct DataMessage *dm = check_data (message);
260   char *msg;
261   int ret;
262   int rid;
263
264   if (dm == NULL)
265     {
266       GNUNET_break (0);
267       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
268       return;
269     }
270   rid = ntohl(dm->rid);
271   if (rid > 0)
272     {
273       /* FIXME: find reservation, update remaining! */
274     }
275   msg = NULL;
276   ret = plugin->api->put (plugin->api->cls,
277                           &dm->key,
278                           ntohl(dm->size),
279                           &dm[1],
280                           ntohl(dm->type),
281                           ntohl(dm->priority),
282                           ntohl(dm->anonymity),
283                           GNUNET_TIME_absolute_ntoh(dm->expiration),
284                           &msg);
285   transmit_status (client, ret, msg);
286   GNUNET_free_non_null (msg);
287   GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
288 }
289
290
291 /**
292  * Handle GET-message.
293  *
294  * @param cls closure
295  * @param client identification of the client
296  * @param message the actual message
297  */
298 static void
299 handle_get (void *cls,
300              struct GNUNET_SERVER_Client *client,
301              const struct GNUNET_MessageHeader *message)
302 {
303   const struct GetMessage *msg;
304   uint16_t size;
305
306   size = ntohs(message->size);
307   if ( (size != sizeof(struct GetMessage)) &&
308        (size != sizeof(struct GetMessage) - sizeof(GNUNET_HashCode)) )
309     {
310       GNUNET_break (0);
311       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
312       return;
313     }
314   msg = (const struct GetMessage*) message;
315   plugin->api->get (plugin->api->cls,
316                     ((size == sizeof(struct GetMessage)) ? &msg->key : NULL),
317                     NULL,
318                     ntohl(msg->type),
319                     &transmit_item,
320                     client);    
321   GNUNET_SERVER_receive_done (client, GNUNET_OK);
322 }
323
324
325 /**
326  * Handle UPDATE-message.
327  *
328  * @param cls closure
329  * @param client identification of the client
330  * @param message the actual message
331  */
332 static void
333 handle_update (void *cls,
334                struct GNUNET_SERVER_Client *client,
335                const struct GNUNET_MessageHeader *message)
336 {
337   const struct UpdateMessage *msg;
338   int ret;
339   char *emsg;
340
341   msg = (const struct UpdateMessage*) message;
342   emsg = NULL;
343   ret = plugin->api->update (plugin->api->cls,
344                              GNUNET_ntohll(msg->uid),
345                              (int32_t) ntohl(msg->priority),
346                              GNUNET_TIME_absolute_ntoh(msg->expiration),
347                              &emsg);
348   transmit_status (client, ret, emsg);
349   GNUNET_free_non_null (emsg);
350   GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
351 }
352
353
354 /**
355  * Handle GET_RANDOM-message.
356  *
357  * @param cls closure
358  * @param client identification of the client
359  * @param message the actual message
360  */
361 static void
362 handle_get_random (void *cls,
363                    struct GNUNET_SERVER_Client *client,
364                    const struct GNUNET_MessageHeader *message)
365 {
366   plugin->api->iter_migration_order (plugin->api->cls,
367                                      0,
368                                      &transmit_item,
369                                      client);  
370   GNUNET_SERVER_receive_done (client, GNUNET_OK);
371 }
372
373
374 /**
375  * Callback function that will cause the item that is passed
376  * in to be deleted (by returning GNUNET_NO).
377  */
378 static int
379 remove_callback (void *cls,
380                  const GNUNET_HashCode * key,
381                  uint32_t size,
382                  const void *data,
383                  uint32_t type,
384                  uint32_t priority,
385                  uint32_t anonymity,
386                  struct GNUNET_TIME_Absolute
387                  expiration, unsigned long long uid)
388 {
389   int *found = cls;
390   *found = GNUNET_YES;
391   return GNUNET_NO;
392 }
393
394
395 /**
396  * Handle REMOVE-message.
397  *
398  * @param cls closure
399  * @param client identification of the client
400  * @param message the actual message
401  */
402 static void
403 handle_remove (void *cls,
404              struct GNUNET_SERVER_Client *client,
405              const struct GNUNET_MessageHeader *message)
406 {
407   const struct DataMessage *dm = check_data (message);
408   GNUNET_HashCode vhash;
409   int found;
410
411   if (dm == NULL)
412     {
413       GNUNET_break (0);
414       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
415       return;
416     }
417   found = GNUNET_NO;
418   GNUNET_CRYPTO_hash (&dm[1],
419                       ntohl(dm->size),
420                       &vhash);
421   plugin->api->get (plugin->api->cls,
422                     &dm->key,
423                     &vhash,
424                     ntohl(dm->type),
425                     &remove_callback,
426                     &found);
427   if (GNUNET_YES == found)
428     transmit_status (client, GNUNET_OK, NULL);
429   else
430     transmit_status (client, GNUNET_SYSERR, _("Content not found"));
431   GNUNET_SERVER_receive_done (client, GNUNET_OK);
432 }
433
434
435 /**
436  * Handle DROP-message.
437  *
438  * @param cls closure
439  * @param client identification of the client
440  * @param message the actual message
441  */
442 static void
443 handle_drop (void *cls,
444              struct GNUNET_SERVER_Client *client,
445              const struct GNUNET_MessageHeader *message)
446 {
447   plugin->api->drop (plugin->api->cls);
448   GNUNET_SERVER_receive_done (client, GNUNET_OK);
449 }
450
451
452 /**
453  * List of handlers for the messages understood by this
454  * service.
455  */
456 static struct GNUNET_SERVER_MessageHandler handlers[] = {
457   {&handle_reserve, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_RESERVE, 
458    sizeof(struct ReserveMessage) }, 
459   {&handle_release_reserve, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_RELEASE_RESERVE, 
460    sizeof(struct ReleaseReserveMessage) }, 
461   {&handle_put, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_PUT, 0 }, 
462   {&handle_update, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_UPDATE, 
463    sizeof (struct UpdateMessage) }, 
464   {&handle_get, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_GET, 0 }, 
465   {&handle_get_random, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_GET_RANDOM, 
466    sizeof(struct GNUNET_MessageHeader) }, 
467   {&handle_remove, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_REMOVE, 0 }, 
468   {&handle_drop, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_DROP, 
469    sizeof(struct GNUNET_MessageHeader) }, 
470   {NULL, NULL, 0, 0}
471 };
472
473
474
475 /**
476  * Load the datastore plugin.
477  */
478 static struct DatastorePlugin *
479 load_plugin (struct GNUNET_CONFIGURATION_Handle *cfg,
480              struct GNUNET_SCHEDULER_Handle *sched)
481 {
482   struct DatastorePlugin *ret;
483   char *libname;
484   char *name;
485
486   if (GNUNET_OK !=
487       GNUNET_CONFIGURATION_get_value_string (cfg,
488                                              "DATASTORE", "DATABASE", &name))
489     {
490       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
491                   _("No `%s' specified for `%s' in configuration!\n"),
492                   "DATABASE",
493                   "DATASTORE");
494       return NULL;
495     }
496   ret = GNUNET_malloc (sizeof(struct DatastorePlugin));
497   ret->env.cfg = cfg;
498   ret->env.sched = sched;  
499   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
500               _("Loading `%s' datastore plugin\n"), name);
501   GNUNET_asprintf (&libname, "libgnunet_plugin_datastore_%s", name);
502   ret->short_name = GNUNET_strdup (name);
503   ret->lib_name = libname;
504   ret->api = GNUNET_PLUGIN_load (libname, &ret->env);
505   if (ret->api == NULL)
506     {
507       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
508                   _("Failed to load datastore plugin for `%s'\n"), name);
509       GNUNET_free (ret->short_name);
510       GNUNET_free (libname);
511       GNUNET_free (ret);
512       return NULL;
513     }
514   return ret;
515 }
516
517
518 /**
519  * Function called when the service shuts
520  * down.  Unloads our datastore plugin.
521  *
522  * @param cls closure
523  * @param cfg configuration to use
524  */
525 static void
526 unload_plugin (struct DatastorePlugin *plug)
527 {
528 #if DEBUG_DATASTORE
529   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
530               "Datastore service is unloading plugin...\n");
531 #endif
532   GNUNET_break (NULL == GNUNET_PLUGIN_unload (plug->lib_name, plug->api));
533   GNUNET_free (plug->lib_name);
534   GNUNET_free (plug->short_name);
535   GNUNET_free (plug);
536 }
537
538
539 /**
540  * Last task run during shutdown.  Disconnects us from
541  * the transport and core.
542  */
543 static void
544 cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
545 {
546   unload_plugin (plugin);
547   plugin = NULL;
548 }
549
550
551 /**
552  * Process datastore requests.
553  *
554  * @param cls closure
555  * @param sched scheduler to use
556  * @param server the initialized server
557  * @param cfg configuration to use
558  */
559 static void
560 run (void *cls,
561      struct GNUNET_SCHEDULER_Handle *sched,
562      struct GNUNET_SERVER_Handle *server,
563      struct GNUNET_CONFIGURATION_Handle *cfg)
564 {
565   plugin = load_plugin (cfg, sched);
566   if (NULL == plugin)
567     return;
568   GNUNET_SERVER_add_handlers (server, handlers);
569   GNUNET_SCHEDULER_add_delayed (sched,
570                                 GNUNET_YES,
571                                 GNUNET_SCHEDULER_PRIORITY_IDLE,
572                                 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
573                                 GNUNET_TIME_UNIT_FOREVER_REL,
574                                 &cleaning_task, NULL);
575 }
576
577
578 /**
579  * The main function for the datastore service.
580  *
581  * @param argc number of arguments from the command line
582  * @param argv command line arguments
583  * @return 0 ok, 1 on error
584  */
585 int
586 main (int argc, char *const *argv)
587 {
588   int ret;
589
590   ret = (GNUNET_OK ==
591          GNUNET_SERVICE_run (argc,
592                              argv,
593                              "datastore", &run, NULL, NULL, NULL)) ? 0 : 1;
594   return ret;
595 }
596
597
598 /* end of gnunet-service-datastore.c */