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