bugfixes
[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  * quota management code:
28  * - track storage use
29  * - track reservations
30  * - refuse above-quota
31  * - content expiration job
32  * - near-quota low-priority content discard job
33  */
34
35 #include "platform.h"
36 #include "gnunet_util_lib.h"
37 #include "gnunet_protocols.h"
38 #include "plugin_datastore.h"
39 #include "datastore.h"
40
41 /**
42  * How many messages do we queue at most per client?
43  */
44 #define MAX_PENDING 1024
45
46
47 /**
48  * Our datastore plugin.
49  */
50 struct DatastorePlugin
51 {
52
53   /**
54    * API of the transport as returned by the plugin's
55    * initialization function.
56    */
57   struct GNUNET_DATASTORE_PluginFunctions *api;
58
59   /**
60    * Short name for the plugin (i.e. "sqlite").
61    */
62   char *short_name;
63
64   /**
65    * Name of the library (i.e. "gnunet_plugin_datastore_sqlite").
66    */
67   char *lib_name;
68
69   /**
70    * Environment this transport service is using
71    * for this plugin.
72    */
73   struct GNUNET_DATASTORE_PluginEnvironment env;
74
75 };
76
77
78 /**
79  * Linked list of active reservations.
80  */
81 struct ReservationList 
82 {
83
84   /**
85    * This is a linked list.
86    */
87   struct ReservationList *next;
88
89   /**
90    * Client that made the reservation.
91    */
92   struct GNUNET_SERVER_Client *client;
93
94   /**
95    * Number of bytes (still) reserved.
96    */
97   uint64_t amount;
98
99   /**
100    * Number of items (still) reserved.
101    */
102   uint64_t entries;
103
104   /**
105    * Reservation identifier.
106    */
107   int32_t rid;
108
109 };
110
111
112 /**
113  * Our datastore plugin (NULL if not available).
114  */
115 static struct DatastorePlugin *plugin;
116
117 /**
118  * Linked list of space reservations made by clients.
119  */
120 static struct ReservationList *reservations;
121
122 /**
123  * Bloomfilter to quickly tell if we don't have the content.
124  */
125 static struct GNUNET_CONTAINER_BloomFilter *filter;
126
127 /**
128  * Static counter to produce reservation identifiers.
129  */
130 static int reservation_gen;
131
132 /**
133  * How much space are we allowed to use?
134  */
135 static unsigned long long quota;
136
137
138 /**
139  * Function called once the transmit operation has
140  * either failed or succeeded.
141  *
142  * @param cls closure
143  * @param status GNUNET_OK on success, GNUNET_SYSERR on error
144  */
145 typedef void (*TransmitContinuation)(void *cls,
146                                      int status);
147
148 struct TransmitCallbackContext 
149 {
150   /**
151    * The message that we're asked to transmit.
152    */
153   struct GNUNET_MessageHeader *msg;
154
155   /**
156    * Client that we are transmitting to.
157    */
158   struct GNUNET_SERVER_Client *client;
159
160   /**
161    * Function to call once msg has been transmitted
162    * (or at least added to the buffer).
163    */
164   TransmitContinuation tc;
165
166   /**
167    * Closure for tc.
168    */
169   void *tc_cls;
170
171   /**
172    * GNUNET_YES if we are supposed to signal the server
173    * completion of the client's request.
174    */
175   int end;
176 };
177
178
179 /**
180  * Function called to notify a client about the socket
181  * begin ready to queue more data.  "buf" will be
182  * NULL and "size" zero if the socket was closed for
183  * writing in the meantime.
184  *
185  * @param cls closure
186  * @param size number of bytes available in buf
187  * @param buf where the callee should write the message
188  * @return number of bytes written to buf
189  */
190 static size_t
191 transmit_callback (void *cls,
192                    size_t size, void *buf)
193 {
194   struct TransmitCallbackContext *tcc = cls;
195   size_t msize;
196   
197   msize = ntohs(tcc->msg->size);
198   if (size == 0)
199     {
200 #if DEBUG_DATASTORE
201       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
202                   "Transmission failed.\n");
203 #endif
204       if (tcc->tc != NULL)
205         tcc->tc (tcc->tc_cls, GNUNET_SYSERR);
206       if (GNUNET_YES == tcc->end)
207         {
208           GNUNET_SERVER_receive_done (tcc->client, GNUNET_SYSERR);
209         }
210       GNUNET_free (tcc->msg);
211       GNUNET_free (tcc);
212       return 0;
213     }
214   GNUNET_assert (size >= msize);
215   memcpy (buf, tcc->msg, msize);
216   if (tcc->tc != NULL)
217     tcc->tc (tcc->tc_cls, GNUNET_OK);
218   if (GNUNET_YES == tcc->end)
219     {
220 #if DEBUG_DATASTORE
221       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
222                   "Request completed, ready for the next request!\n");
223 #endif
224       GNUNET_SERVER_receive_done (tcc->client, GNUNET_OK);
225     }
226   else
227     {
228 #if DEBUG_DATASTORE
229       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
230                   "Response transmitted, more pending!\n");
231 #endif
232     }
233   GNUNET_free (tcc->msg);
234   GNUNET_free (tcc);
235   return msize;
236 }
237
238
239 /**
240  * Transmit the given message to the client.
241  *
242  * @param client target of the message
243  * @param msg message to transmit, will be freed!
244  * @param end is this the last response (and we should
245  *        signal the server completion accodingly after
246  *        transmitting this message)?
247  */
248 static void
249 transmit (struct GNUNET_SERVER_Client *client,
250           struct GNUNET_MessageHeader *msg,
251           TransmitContinuation tc,
252           void *tc_cls,
253           int end)
254 {
255   struct TransmitCallbackContext *tcc;
256
257   tcc = GNUNET_malloc (sizeof(struct TransmitCallbackContext));
258   tcc->msg = msg;
259   tcc->client = client;
260   tcc->tc = tc;
261   tcc->tc_cls = tc_cls;
262   tcc->end = end;
263
264   if (NULL ==
265       GNUNET_SERVER_notify_transmit_ready (client,
266                                            ntohs(msg->size),
267                                            GNUNET_TIME_UNIT_FOREVER_REL,
268                                            &transmit_callback,
269                                            tcc))
270     {
271       GNUNET_break (0);
272       if (GNUNET_YES == end)
273         {
274 #if DEBUG_DATASTORE
275           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
276                       "Disconnecting client.\n");
277 #endif    
278           GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
279         }
280       if (NULL != tc)
281         tc (tc_cls, GNUNET_SYSERR);
282       GNUNET_free (msg);
283       GNUNET_free (tcc);
284     }
285 }
286
287
288 /**
289  * Transmit a status code to the client.
290  *
291  * @param client receiver of the response
292  * @param code status code
293  * @param msg optional error message (can be NULL)
294  */
295 static void
296 transmit_status (struct GNUNET_SERVER_Client *client,
297                  int code,
298                  const char *msg)
299 {
300   struct StatusMessage *sm;
301   size_t slen;
302
303 #if DEBUG_DATASTORE
304   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
305               "Transmitting `%s' message with value %d and message %s\n",
306               "STATUS",
307               code,
308               msg != NULL ? msg : "(none)");
309 #endif
310   slen = (msg == NULL) ? 0 : strlen(msg) + 1;  
311   sm = GNUNET_malloc (sizeof(struct StatusMessage) + slen);
312   sm->header.size = htons(sizeof(struct StatusMessage) + slen);
313   sm->header.type = htons(GNUNET_MESSAGE_TYPE_DATASTORE_STATUS);
314   sm->status = htonl(code);
315   memcpy (&sm[1], msg, slen);  
316   transmit (client, &sm->header, NULL, NULL, GNUNET_YES);
317 }
318
319
320 /**
321  * Function called once the transmit operation has
322  * either failed or succeeded.
323  *
324  * @param cls closure
325  * @param status GNUNET_OK on success, GNUNET_SYSERR on error
326  */
327 static void 
328 get_next(void *next_cls,
329          int status)
330 {
331   if (status != GNUNET_OK)
332     {
333       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
334                   _("Failed to transmit an item to the client; aborting iteration.\n"));    
335       plugin->api->next_request (next_cls, GNUNET_YES);
336       return;
337     }
338   plugin->api->next_request (next_cls, GNUNET_NO);
339 }
340
341
342 /**
343  * Function that will transmit the given datastore entry
344  * to the client.
345  *
346  * @param cls closure, pointer to the client (of type GNUNET_SERVER_Client).
347  * @param next_cls closure to use to ask for the next item
348  * @param key key for the content
349  * @param size number of bytes in data
350  * @param data content stored
351  * @param type type of the content
352  * @param priority priority of the content
353  * @param anonymity anonymity-level for the content
354  * @param expiration expiration time for the content
355  * @param uid unique identifier for the datum;
356  *        maybe 0 if no unique identifier is available
357  *
358  * @return GNUNET_SYSERR to abort the iteration, GNUNET_OK to continue,
359  *         GNUNET_NO to delete the item and continue (if supported)
360  */
361 static int
362 transmit_item (void *cls,
363                void *next_cls,
364                const GNUNET_HashCode * key,
365                uint32_t size,
366                const void *data,
367                uint32_t type,
368                uint32_t priority,
369                uint32_t anonymity,
370                struct GNUNET_TIME_Absolute
371                expiration, uint64_t uid)
372 {
373   struct GNUNET_SERVER_Client *client = cls;
374   struct GNUNET_MessageHeader *end;
375   struct DataMessage *dm;
376
377   if (key == NULL)
378     {
379       /* transmit 'DATA_END' */
380 #if DEBUG_DATASTORE
381       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
382                   "Transmitting `%s' message\n",
383                   "DATA_END");
384 #endif
385       end = GNUNET_malloc (sizeof(struct GNUNET_MessageHeader));
386       end->size = htons(sizeof(struct GNUNET_MessageHeader));
387       end->type = htons(GNUNET_MESSAGE_TYPE_DATASTORE_DATA_END);
388       transmit (client, end, NULL, NULL, GNUNET_YES);
389       GNUNET_SERVER_client_drop (client);
390       return GNUNET_OK;
391     }
392   dm = GNUNET_malloc (sizeof(struct DataMessage) + size);
393   dm->header.size = htons(sizeof(struct DataMessage) + size);
394   dm->header.type = htons(GNUNET_MESSAGE_TYPE_DATASTORE_DATA);
395   dm->rid = htonl(0);
396   dm->size = htonl(size);
397   dm->type = htonl(type);
398   dm->priority = htonl(priority);
399   dm->anonymity = htonl(anonymity);
400   dm->expiration = GNUNET_TIME_absolute_hton(expiration);
401   dm->uid = GNUNET_htonll(uid);
402   dm->key = *key;
403   memcpy (&dm[1], data, size);
404 #if DEBUG_DATASTORE
405   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
406               "Transmitting `%s' message\n",
407               "DATA");
408 #endif
409   transmit (client, &dm->header, &get_next, next_cls, GNUNET_NO);
410   return GNUNET_OK;
411 }
412
413
414 /**
415  * Handle RESERVE-message.
416  *
417  * @param cls closure
418  * @param client identification of the client
419  * @param message the actual message
420  */
421 static void
422 handle_reserve (void *cls,
423              struct GNUNET_SERVER_Client *client,
424              const struct GNUNET_MessageHeader *message)
425 {
426   const struct ReserveMessage *msg = (const struct ReserveMessage*) message;
427   struct ReservationList *e;
428
429 #if DEBUG_DATASTORE
430   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
431               "Processing `%s' request\n",
432               "RESERVE");
433 #endif
434  /* FIXME: check if we have that much space... */
435   e = GNUNET_malloc (sizeof(struct ReservationList));
436   e->next = reservations;
437   reservations = e;
438   e->client = client;
439   e->amount = GNUNET_ntohll(msg->amount);
440   e->entries = GNUNET_ntohll(msg->entries);
441   e->rid = ++reservation_gen;
442   if (reservation_gen < 0)
443     reservation_gen = 0; /* wrap around */
444   transmit_status (client, e->rid, NULL);
445 }
446
447
448 /**
449  * Handle RELEASE_RESERVE-message.
450  *
451  * @param cls closure
452  * @param client identification of the client
453  * @param message the actual message
454  */
455 static void
456 handle_release_reserve (void *cls,
457                         struct GNUNET_SERVER_Client *client,
458                         const struct GNUNET_MessageHeader *message)
459 {
460   const struct ReleaseReserveMessage *msg = (const struct ReleaseReserveMessage*) message;
461   struct ReservationList *pos;
462   struct ReservationList *prev;
463   struct ReservationList *next;
464   int rid = ntohl(msg->rid);
465
466 #if DEBUG_DATASTORE
467   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
468               "Processing `%s' request\n",
469               "RELEASE_RESERVE");
470 #endif
471   next = reservations;
472   prev = NULL;
473   while (NULL != (pos = next))
474     {
475       next = pos->next;
476       if (rid == pos->rid)
477         {
478           if (prev == NULL)
479             reservations = next;
480           else
481             prev->next = next;
482           /* FIXME: released remaining reserved space! */
483           GNUNET_free (pos);
484           transmit_status (client, GNUNET_OK, NULL);
485           return;
486         }       
487       prev = pos;
488       pos = next;
489     }
490   transmit_status (client, GNUNET_SYSERR, "Could not find matching reservation");
491 }
492
493
494 /**
495  * Check that the given message is a valid data message.
496  *
497  * @return NULL if the message is not well-formed, otherwise the message
498  */
499 static const struct DataMessage *
500 check_data (const struct GNUNET_MessageHeader *message)
501 {
502   uint16_t size;
503   uint32_t dsize;
504   const struct DataMessage *dm;
505
506   size = ntohs(message->size);
507   if (size < sizeof(struct DataMessage))
508     { 
509       GNUNET_break (0);
510       return NULL;
511     }
512   dm = (const struct DataMessage *) message;
513   dsize = ntohl(dm->size);
514   if (size != dsize + sizeof(struct DataMessage))
515     {
516       GNUNET_break (0);
517       return NULL;
518     }
519   return dm;
520 }
521
522
523 /**
524  * Handle PUT-message.
525  *
526  * @param cls closure
527  * @param client identification of the client
528  * @param message the actual message
529  */
530 static void
531 handle_put (void *cls,
532             struct GNUNET_SERVER_Client *client,
533             const struct GNUNET_MessageHeader *message)
534 {
535   const struct DataMessage *dm = check_data (message);
536   char *msg;
537   int ret;
538   int rid;
539
540 #if DEBUG_DATASTORE
541   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
542               "Processing `%s' request\n",
543               "PUT");
544 #endif
545   if (ntohl(dm->type) == 0) 
546     {
547       GNUNET_break (0);
548       dm = NULL;
549     }
550   if (dm == NULL)
551     {
552       GNUNET_break (0);
553       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
554       return;
555     }
556   rid = ntohl(dm->rid);
557   if (rid > 0)
558     {
559       /* FIXME: find reservation, update remaining! */
560     }
561   msg = NULL;
562   ret = plugin->api->put (plugin->api->cls,
563                           &dm->key,
564                           ntohl(dm->size),
565                           &dm[1],
566                           ntohl(dm->type),
567                           ntohl(dm->priority),
568                           ntohl(dm->anonymity),
569                           GNUNET_TIME_absolute_ntoh(dm->expiration),
570                           &msg);
571   if (GNUNET_OK == ret)
572     GNUNET_CONTAINER_bloomfilter_add (filter,
573                                       &dm->key);
574   transmit_status (client, 
575                    (GNUNET_SYSERR == ret) ? GNUNET_SYSERR : GNUNET_OK, 
576                    msg);
577   GNUNET_free_non_null (msg);
578 }
579
580
581 /**
582  * Handle GET-message.
583  *
584  * @param cls closure
585  * @param client identification of the client
586  * @param message the actual message
587  */
588 static void
589 handle_get (void *cls,
590              struct GNUNET_SERVER_Client *client,
591              const struct GNUNET_MessageHeader *message)
592 {
593   static struct GNUNET_TIME_Absolute zero;
594   const struct GetMessage *msg;
595   uint16_t size;
596
597 #if DEBUG_DATASTORE
598   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
599               "Processing `%s' request\n",
600               "GET");
601 #endif
602   size = ntohs(message->size);
603   if ( (size != sizeof(struct GetMessage)) &&
604        (size != sizeof(struct GetMessage) - sizeof(GNUNET_HashCode)) )
605     {
606       GNUNET_break (0);
607       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
608       return;
609     }
610   msg = (const struct GetMessage*) message;
611   if ( (size == sizeof(struct GetMessage)) &&
612        (GNUNET_YES != GNUNET_CONTAINER_bloomfilter_test (filter,
613                                                          &msg->key)) )
614     {
615       /* don't bother database... */
616 #if DEBUG_DATASTORE
617       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
618                   "Empty result set for `%s' request.\n",
619                   "GET");
620 #endif  
621       transmit_item (client,
622                      NULL, NULL, 0, NULL, 0, 0, 0, zero, 0);
623       return;
624     }
625   GNUNET_SERVER_client_keep (client);
626   plugin->api->get (plugin->api->cls,
627                     ((size == sizeof(struct GetMessage)) ? &msg->key : NULL),
628                     NULL,
629                     ntohl(msg->type),
630                     &transmit_item,
631                     client);    
632 }
633
634
635 /**
636  * Handle UPDATE-message.
637  *
638  * @param cls closure
639  * @param client identification of the client
640  * @param message the actual message
641  */
642 static void
643 handle_update (void *cls,
644                struct GNUNET_SERVER_Client *client,
645                const struct GNUNET_MessageHeader *message)
646 {
647   const struct UpdateMessage *msg;
648   int ret;
649   char *emsg;
650
651 #if DEBUG_DATASTORE
652   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
653               "Processing `%s' request\n",
654               "UPDATE");
655 #endif
656   msg = (const struct UpdateMessage*) message;
657   emsg = NULL;
658   ret = plugin->api->update (plugin->api->cls,
659                              GNUNET_ntohll(msg->uid),
660                              (int32_t) ntohl(msg->priority),
661                              GNUNET_TIME_absolute_ntoh(msg->expiration),
662                              &emsg);
663   transmit_status (client, ret, emsg);
664   GNUNET_free_non_null (emsg);
665 }
666
667
668 /**
669  * Handle GET_RANDOM-message.
670  *
671  * @param cls closure
672  * @param client identification of the client
673  * @param message the actual message
674  */
675 static void
676 handle_get_random (void *cls,
677                    struct GNUNET_SERVER_Client *client,
678                    const struct GNUNET_MessageHeader *message)
679 {
680 #if DEBUG_DATASTORE
681   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
682               "Processing `%s' request\n",
683               "GET_RANDOM");
684 #endif
685   GNUNET_SERVER_client_keep (client);
686   plugin->api->iter_migration_order (plugin->api->cls,
687                                      0,
688                                      &transmit_item,
689                                      client);  
690 }
691
692
693 /**
694  * Context for the 'remove_callback'.
695  */
696 struct RemoveContext 
697 {
698   /**
699    * Client for whom we're doing the remvoing.
700    */
701   struct GNUNET_SERVER_Client *client;
702
703   /**
704    * GNUNET_YES if we managed to remove something.
705    */
706   int found;
707 };
708
709
710 /**
711  * Callback function that will cause the item that is passed
712  * in to be deleted (by returning GNUNET_NO).
713  */
714 static int
715 remove_callback (void *cls,
716                  void *next_cls,
717                  const GNUNET_HashCode * key,
718                  uint32_t size,
719                  const void *data,
720                  uint32_t type,
721                  uint32_t priority,
722                  uint32_t anonymity,
723                  struct GNUNET_TIME_Absolute
724                  expiration, uint64_t uid)
725 {
726   struct RemoveContext *rc = cls;
727
728   if (key == NULL)
729     {
730 #if DEBUG_DATASTORE
731       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
732                   "No further matches for `%s' request.\n",
733                   "REMOVE");
734 #endif  
735       if (GNUNET_YES == rc->found)
736         transmit_status (rc->client, GNUNET_OK, NULL);       
737       else
738         transmit_status (rc->client, GNUNET_SYSERR, _("Content not found"));            
739       GNUNET_SERVER_client_drop (rc->client);
740       GNUNET_free (rc);
741       return GNUNET_OK; /* last item */
742     }
743   rc->found = GNUNET_YES;
744 #if DEBUG_DATASTORE
745   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
746               "Item %llu matches `%s' request.\n",
747               (unsigned long long) uid,
748               "REMOVE");
749 #endif  
750   GNUNET_CONTAINER_bloomfilter_remove (filter,
751                                        key);
752   plugin->api->next_request (next_cls, GNUNET_YES);
753   return GNUNET_NO;
754 }
755
756
757 /**
758  * Handle REMOVE-message.
759  *
760  * @param cls closure
761  * @param client identification of the client
762  * @param message the actual message
763  */
764 static void
765 handle_remove (void *cls,
766              struct GNUNET_SERVER_Client *client,
767              const struct GNUNET_MessageHeader *message)
768 {
769   const struct DataMessage *dm = check_data (message);
770   GNUNET_HashCode vhash;
771   struct RemoveContext *rc;
772
773 #if DEBUG_DATASTORE
774   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
775               "Processing `%s' request\n",
776               "REMOVE");
777 #endif
778   if (dm == NULL)
779     {
780       GNUNET_break (0);
781       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
782       return;
783     }
784   rc = GNUNET_malloc (sizeof(struct RemoveContext));
785   GNUNET_SERVER_client_keep (client);
786   rc->client = client;
787   GNUNET_CRYPTO_hash (&dm[1],
788                       ntohl(dm->size),
789                       &vhash);
790   GNUNET_SERVER_client_keep (client);
791   plugin->api->get (plugin->api->cls,
792                     &dm->key,
793                     &vhash,
794                     ntohl(dm->type),
795                     &remove_callback,
796                     rc);
797 }
798
799
800 /**
801  * Handle DROP-message.
802  *
803  * @param cls closure
804  * @param client identification of the client
805  * @param message the actual message
806  */
807 static void
808 handle_drop (void *cls,
809              struct GNUNET_SERVER_Client *client,
810              const struct GNUNET_MessageHeader *message)
811 {
812 #if DEBUG_DATASTORE
813   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
814               "Processing `%s' request\n",
815               "DROP");
816 #endif
817   plugin->api->drop (plugin->api->cls);
818   GNUNET_SERVER_receive_done (client, GNUNET_OK);
819 }
820
821
822 /**
823  * List of handlers for the messages understood by this
824  * service.
825  */
826 static struct GNUNET_SERVER_MessageHandler handlers[] = {
827   {&handle_reserve, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_RESERVE, 
828    sizeof(struct ReserveMessage) }, 
829   {&handle_release_reserve, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_RELEASE_RESERVE, 
830    sizeof(struct ReleaseReserveMessage) }, 
831   {&handle_put, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_PUT, 0 }, 
832   {&handle_update, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_UPDATE, 
833    sizeof (struct UpdateMessage) }, 
834   {&handle_get, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_GET, 0 }, 
835   {&handle_get_random, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_GET_RANDOM, 
836    sizeof(struct GNUNET_MessageHeader) }, 
837   {&handle_remove, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_REMOVE, 0 }, 
838   {&handle_drop, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_DROP, 
839    sizeof(struct GNUNET_MessageHeader) }, 
840   {NULL, NULL, 0, 0}
841 };
842
843
844
845 /**
846  * Load the datastore plugin.
847  */
848 static struct DatastorePlugin *
849 load_plugin (struct GNUNET_CONFIGURATION_Handle *cfg,
850              struct GNUNET_SCHEDULER_Handle *sched)
851 {
852   struct DatastorePlugin *ret;
853   char *libname;
854   char *name;
855
856   if (GNUNET_OK !=
857       GNUNET_CONFIGURATION_get_value_string (cfg,
858                                              "DATASTORE", "DATABASE", &name))
859     {
860       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
861                   _("No `%s' specified for `%s' in configuration!\n"),
862                   "DATABASE",
863                   "DATASTORE");
864       return NULL;
865     }
866   ret = GNUNET_malloc (sizeof(struct DatastorePlugin));
867   ret->env.cfg = cfg;
868   ret->env.sched = sched;  
869   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
870               _("Loading `%s' datastore plugin\n"), name);
871   GNUNET_asprintf (&libname, "libgnunet_plugin_datastore_%s", name);
872   ret->short_name = name;
873   ret->lib_name = libname;
874   ret->api = GNUNET_PLUGIN_load (libname, &ret->env);
875   if (ret->api == NULL)
876     {
877       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
878                   _("Failed to load datastore plugin for `%s'\n"), name);
879       GNUNET_free (ret->short_name);
880       GNUNET_free (libname);
881       GNUNET_free (ret);
882       return NULL;
883     }
884   return ret;
885 }
886
887
888 /**
889  * Function called when the service shuts
890  * down.  Unloads our datastore plugin.
891  *
892  * @param plug plugin to unload
893  */
894 static void
895 unload_plugin (struct DatastorePlugin *plug)
896 {
897 #if DEBUG_DATASTORE
898   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
899               "Datastore service is unloading plugin...\n");
900 #endif
901   GNUNET_break (NULL == GNUNET_PLUGIN_unload (plug->lib_name, plug->api));
902   GNUNET_free (plug->lib_name);
903   GNUNET_free (plug->short_name);
904   GNUNET_free (plug);
905 }
906
907
908 /**
909  * Last task run during shutdown.  Disconnects us from
910  * the transport and core.
911  */
912 static void
913 cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
914 {
915   unload_plugin (plugin);
916   plugin = NULL;
917 }
918
919
920 /**
921  * Function that removes all active reservations made
922  * by the given client and releases the space for other
923  * requests.
924  *
925  * @param cls closure
926  * @param client identification of the client
927  */
928 static void
929 cleanup_reservations (void *cls,
930                       struct GNUNET_SERVER_Client
931                       * client)
932 {
933   /* FIXME */
934 }
935
936
937 /**
938  * Process datastore requests.
939  *
940  * @param cls closure
941  * @param sched scheduler to use
942  * @param server the initialized server
943  * @param cfg configuration to use
944  */
945 static void
946 run (void *cls,
947      struct GNUNET_SCHEDULER_Handle *sched,
948      struct GNUNET_SERVER_Handle *server,
949      struct GNUNET_CONFIGURATION_Handle *cfg)
950 {
951   char *fn;
952   unsigned int bf_size;
953
954   if (GNUNET_OK !=
955       GNUNET_CONFIGURATION_get_value_number (cfg,
956                                              "DATASTORE", "QUOTA", &quota))
957     {
958       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
959                   _("No `%s' specified for `%s' in configuration!\n"),
960                   "QUOTA",
961                   "DATASTORE");
962       return;
963     }
964   bf_size = quota / 32; /* 8 bit per entry, 1 bit per 32 kb in DB */
965   fn = NULL;
966   if ( (GNUNET_OK !=
967         GNUNET_CONFIGURATION_get_value_filename (cfg,
968                                                  "DATASTORE",
969                                                  "BLOOMFILTER",
970                                                  &fn)) ||
971        (GNUNET_OK !=
972         GNUNET_DISK_directory_create_for_file (fn)) )
973     {
974       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
975                   _("Could not use specified filename `%s' for bloomfilter.\n"),
976                   fn != NULL ? fn : "");
977       GNUNET_free_non_null (fn);
978       fn = NULL;
979     }
980   filter = GNUNET_CONTAINER_bloomfilter_load (fn, bf_size, 5);  /* approx. 3% false positives at max use */  
981   GNUNET_free_non_null (fn);
982   if (filter == NULL)
983     {
984       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
985                   _("Failed to initialize bloomfilter.\n"));
986       return;
987     }
988   plugin = load_plugin (cfg, sched);
989   if (NULL == plugin)
990     {
991       GNUNET_CONTAINER_bloomfilter_free (filter);
992       return;
993     }
994   GNUNET_SERVER_disconnect_notify (server, &cleanup_reservations, NULL);
995   GNUNET_SERVER_add_handlers (server, handlers);
996   GNUNET_SCHEDULER_add_delayed (sched,
997                                 GNUNET_YES,
998                                 GNUNET_SCHEDULER_PRIORITY_IDLE,
999                                 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1000                                 GNUNET_TIME_UNIT_FOREVER_REL,
1001                                 &cleaning_task, NULL);
1002 }
1003
1004
1005 /**
1006  * The main function for the datastore service.
1007  *
1008  * @param argc number of arguments from the command line
1009  * @param argv command line arguments
1010  * @return 0 ok, 1 on error
1011  */
1012 int
1013 main (int argc, char *const *argv)
1014 {
1015   int ret;
1016
1017   ret = (GNUNET_OK ==
1018          GNUNET_SERVICE_run (argc,
1019                              argv,
1020                              "datastore", &run, NULL, NULL, NULL)) ? 0 : 1;
1021   return ret;
1022 }
1023
1024
1025 /* end of gnunet-service-datastore.c */