debugging
[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 size;
98
99   /**
100    * Number of items (still) reserved.
101    */
102   uint64_t items;
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 (tcc->tc != NULL)
201         tcc->tc (tcc->tc_cls, GNUNET_SYSERR);
202       if (GNUNET_YES == tcc->end)
203         GNUNET_SERVER_receive_done (tcc->client, GNUNET_SYSERR);
204       GNUNET_free (tcc->msg);
205       GNUNET_free (tcc);
206       return 0;
207     }
208   GNUNET_assert (size >= msize);
209   memcpy (buf, tcc->msg, msize);
210   if (tcc->tc != NULL)
211     tcc->tc (tcc->tc_cls, GNUNET_OK);
212   if (GNUNET_YES == tcc->end)
213     GNUNET_SERVER_receive_done (tcc->client, GNUNET_OK);     
214   GNUNET_free (tcc->msg);
215   GNUNET_free (tcc);
216   return msize;
217 }
218
219
220 /**
221  * Transmit the given message to the client.
222  *
223  * @param client target of the message
224  * @param msg message to transmit, will be freed!
225  * @param end is this the last response (and we should
226  *        signal the server completion accodingly after
227  *        transmitting this message)?
228  */
229 static void
230 transmit (struct GNUNET_SERVER_Client *client,
231           struct GNUNET_MessageHeader *msg,
232           TransmitContinuation tc,
233           void *tc_cls,
234           int end)
235 {
236   struct TransmitCallbackContext *tcc;
237
238   tcc = GNUNET_malloc (sizeof(struct TransmitCallbackContext));
239   tcc->msg = msg;
240   tcc->client = client;
241   tcc->tc = tc;
242   tcc->tc_cls = tc_cls;
243   tcc->end = end;
244
245   if (NULL ==
246       GNUNET_SERVER_notify_transmit_ready (client,
247                                            ntohs(msg->size),
248                                            GNUNET_TIME_UNIT_FOREVER_REL,
249                                            &transmit_callback,
250                                            tcc))
251     {
252       GNUNET_break (0);
253       if (GNUNET_YES == end)
254         GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
255       if (NULL != tc)
256         tc (tc_cls, GNUNET_SYSERR);
257       GNUNET_free (msg);
258       GNUNET_free (tcc);
259     }
260 }
261
262
263 /**
264  * Transmit a status code to the client.
265  *
266  * @param client receiver of the response
267  * @param code status code
268  * @param msg optional error message (can be NULL)
269  */
270 static void
271 transmit_status (struct GNUNET_SERVER_Client *client,
272                  int code,
273                  const char *msg)
274 {
275   struct StatusMessage *sm;
276   size_t slen;
277
278   slen = (msg == NULL) ? 0 : strlen(msg) + 1;  
279   sm = GNUNET_malloc (sizeof(struct StatusMessage) + slen);
280   sm->header.size = htons(sizeof(struct StatusMessage) + slen);
281   sm->header.type = htons(GNUNET_MESSAGE_TYPE_DATASTORE_STATUS);
282   sm->status = htonl(code);
283   memcpy (&sm[1], msg, slen);  
284   transmit (client, &sm->header, NULL, NULL, GNUNET_YES);
285 }
286
287
288 /**
289  * Function called once the transmit operation has
290  * either failed or succeeded.
291  *
292  * @param cls closure
293  * @param status GNUNET_OK on success, GNUNET_SYSERR on error
294  */
295 static void 
296 get_next(void *next_cls,
297          int status)
298 {
299   if (status != GNUNET_OK)
300     {
301       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
302                   _("Failed to transmit an item to the client; aborting iteration.\n"));    
303       plugin->api->next_request (next_cls, GNUNET_YES);
304       return;
305     }
306   plugin->api->next_request (next_cls, GNUNET_NO);
307 }
308
309
310 /**
311  * Function that will transmit the given datastore entry
312  * to the client.
313  *
314  * @param cls closure, pointer to the client (of type GNUNET_SERVER_Client).
315  * @param next_cls closure to use to ask for the next item
316  * @param key key for the content
317  * @param size number of bytes in data
318  * @param data content stored
319  * @param type type of the content
320  * @param priority priority of the content
321  * @param anonymity anonymity-level for the content
322  * @param expiration expiration time for the content
323  * @param uid unique identifier for the datum;
324  *        maybe 0 if no unique identifier is available
325  *
326  * @return GNUNET_SYSERR to abort the iteration, GNUNET_OK to continue,
327  *         GNUNET_NO to delete the item and continue (if supported)
328  */
329 static int
330 transmit_item (void *cls,
331                void *next_cls,
332                const GNUNET_HashCode * key,
333                uint32_t size,
334                const void *data,
335                uint32_t type,
336                uint32_t priority,
337                uint32_t anonymity,
338                struct GNUNET_TIME_Absolute
339                expiration, uint64_t uid)
340 {
341   struct GNUNET_SERVER_Client *client = cls;
342   struct GNUNET_MessageHeader *end;
343   struct DataMessage *dm;
344
345   if (key == NULL)
346     {
347       /* transmit 'DATA_END' */
348       end = GNUNET_malloc (sizeof(struct GNUNET_MessageHeader));
349       end->size = htons(sizeof(struct GNUNET_MessageHeader));
350       end->type = htons(GNUNET_MESSAGE_TYPE_DATASTORE_DATA_END);
351       transmit (client, end, NULL, NULL, GNUNET_YES);
352       GNUNET_SERVER_client_drop (client);
353       return GNUNET_OK;
354     }
355   dm = GNUNET_malloc (sizeof(struct DataMessage) + size);
356   dm->header.size = htons(sizeof(struct DataMessage) + size);
357   dm->header.type = htons(GNUNET_MESSAGE_TYPE_DATASTORE_DATA);
358   dm->rid = htonl(0);
359   dm->size = htonl(size);
360   dm->type = htonl(type);
361   dm->priority = htonl(priority);
362   dm->anonymity = htonl(anonymity);
363   dm->expiration = GNUNET_TIME_absolute_hton(expiration);
364   dm->uid = GNUNET_htonll(uid);
365   dm->key = *key;
366   memcpy (&dm[1], data, size);
367   transmit (client, &dm->header, &get_next, next_cls, GNUNET_NO);
368   return GNUNET_OK;
369 }
370
371
372 /**
373  * Handle RESERVE-message.
374  *
375  * @param cls closure
376  * @param client identification of the client
377  * @param message the actual message
378  */
379 static void
380 handle_reserve (void *cls,
381              struct GNUNET_SERVER_Client *client,
382              const struct GNUNET_MessageHeader *message)
383 {
384   const struct ReserveMessage *msg = (const struct ReserveMessage*) message;
385   struct ReservationList *e;
386
387   /* FIXME: check if we have that much space... */
388   e = GNUNET_malloc (sizeof(struct ReservationList));
389   e->next = reservations;
390   reservations = e;
391   e->client = client;
392   e->size = GNUNET_ntohll(msg->size);
393   e->items = GNUNET_ntohll(msg->items);
394   e->rid = ++reservation_gen;
395   if (reservation_gen < 0)
396     reservation_gen = 0; /* wrap around */
397   transmit_status (client, e->rid, NULL);
398 }
399
400
401 /**
402  * Handle RELEASE_RESERVE-message.
403  *
404  * @param cls closure
405  * @param client identification of the client
406  * @param message the actual message
407  */
408 static void
409 handle_release_reserve (void *cls,
410                         struct GNUNET_SERVER_Client *client,
411                         const struct GNUNET_MessageHeader *message)
412 {
413   const struct ReleaseReserveMessage *msg = (const struct ReleaseReserveMessage*) message;
414   struct ReservationList *pos;
415   struct ReservationList *prev;
416   struct ReservationList *next;
417   
418   int rid = ntohl(msg->rid);
419   next = reservations;
420   prev = NULL;
421   while (NULL != (pos = next))
422     {
423       next = pos->next;
424       if (rid == pos->rid)
425         {
426           if (prev == NULL)
427             reservations = next;
428           else
429             prev->next = next;
430           /* FIXME: released remaining reserved space! */
431           GNUNET_free (pos);
432           transmit_status (client, GNUNET_OK, NULL);
433           return;
434         }       
435       prev = pos;
436       pos = next;
437     }
438   transmit_status (client, GNUNET_SYSERR, "Could not find matching reservation");
439 }
440
441
442 /**
443  * Check that the given message is a valid data message.
444  *
445  * @return NULL if the message is not well-formed, otherwise the message
446  */
447 static const struct DataMessage *
448 check_data (const struct GNUNET_MessageHeader *message)
449 {
450   uint16_t size;
451   uint32_t dsize;
452   const struct DataMessage *dm;
453
454   size = ntohs(message->size);
455   if (size < sizeof(struct DataMessage))
456     { 
457       GNUNET_break (0);
458       return NULL;
459     }
460   dm = (const struct DataMessage *) message;
461   dsize = ntohl(dm->size);
462   if (size != dsize + sizeof(struct DataMessage))
463     {
464       GNUNET_break (0);
465       return NULL;
466     }
467   if (ntohl(dm->type) == 0) 
468     {
469       GNUNET_break (0);
470       return NULL;
471     }
472   return dm;
473 }
474
475
476 /**
477  * Handle PUT-message.
478  *
479  * @param cls closure
480  * @param client identification of the client
481  * @param message the actual message
482  */
483 static void
484 handle_put (void *cls,
485             struct GNUNET_SERVER_Client *client,
486             const struct GNUNET_MessageHeader *message)
487 {
488   const struct DataMessage *dm = check_data (message);
489   char *msg;
490   int ret;
491   int rid;
492
493   if (dm == NULL)
494     {
495       GNUNET_break (0);
496       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
497       return;
498     }
499   rid = ntohl(dm->rid);
500   if (rid > 0)
501     {
502       /* FIXME: find reservation, update remaining! */
503     }
504   msg = NULL;
505   ret = plugin->api->put (plugin->api->cls,
506                           &dm->key,
507                           ntohl(dm->size),
508                           &dm[1],
509                           ntohl(dm->type),
510                           ntohl(dm->priority),
511                           ntohl(dm->anonymity),
512                           GNUNET_TIME_absolute_ntoh(dm->expiration),
513                           &msg);
514   if (GNUNET_OK == ret)
515     GNUNET_CONTAINER_bloomfilter_add (filter,
516                                       &dm->key);
517   transmit_status (client, 
518                    GNUNET_SYSERR == ret ? GNUNET_SYSERR : GNUNET_OK, 
519                    msg);
520   GNUNET_free_non_null (msg);
521 }
522
523
524 /**
525  * Handle GET-message.
526  *
527  * @param cls closure
528  * @param client identification of the client
529  * @param message the actual message
530  */
531 static void
532 handle_get (void *cls,
533              struct GNUNET_SERVER_Client *client,
534              const struct GNUNET_MessageHeader *message)
535 {
536   static struct GNUNET_TIME_Absolute zero;
537   const struct GetMessage *msg;
538   uint16_t size;
539
540   size = ntohs(message->size);
541   if ( (size != sizeof(struct GetMessage)) &&
542        (size != sizeof(struct GetMessage) - sizeof(GNUNET_HashCode)) )
543     {
544       GNUNET_break (0);
545       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
546       return;
547     }
548   msg = (const struct GetMessage*) message;
549   if ( (size == sizeof(struct GetMessage)) &&
550        (GNUNET_YES != GNUNET_CONTAINER_bloomfilter_test (filter,
551                                                          &msg->key)) )
552     {
553       /* don't bother database... */
554       transmit_item (client,
555                      NULL, NULL, 0, NULL, 0, 0, 0, zero, 0);
556       return;
557     }
558   GNUNET_SERVER_client_drop (client);
559   plugin->api->get (plugin->api->cls,
560                     ((size == sizeof(struct GetMessage)) ? &msg->key : NULL),
561                     NULL,
562                     ntohl(msg->type),
563                     &transmit_item,
564                     client);    
565 }
566
567
568 /**
569  * Handle UPDATE-message.
570  *
571  * @param cls closure
572  * @param client identification of the client
573  * @param message the actual message
574  */
575 static void
576 handle_update (void *cls,
577                struct GNUNET_SERVER_Client *client,
578                const struct GNUNET_MessageHeader *message)
579 {
580   const struct UpdateMessage *msg;
581   int ret;
582   char *emsg;
583
584   msg = (const struct UpdateMessage*) message;
585   emsg = NULL;
586   ret = plugin->api->update (plugin->api->cls,
587                              GNUNET_ntohll(msg->uid),
588                              (int32_t) ntohl(msg->priority),
589                              GNUNET_TIME_absolute_ntoh(msg->expiration),
590                              &emsg);
591   transmit_status (client, ret, emsg);
592   GNUNET_free_non_null (emsg);
593 }
594
595
596 /**
597  * Handle GET_RANDOM-message.
598  *
599  * @param cls closure
600  * @param client identification of the client
601  * @param message the actual message
602  */
603 static void
604 handle_get_random (void *cls,
605                    struct GNUNET_SERVER_Client *client,
606                    const struct GNUNET_MessageHeader *message)
607 {
608   GNUNET_SERVER_client_drop (client);
609   plugin->api->iter_migration_order (plugin->api->cls,
610                                      0,
611                                      &transmit_item,
612                                      client);  
613 }
614
615
616 /**
617  * Context for the 'remove_callback'.
618  */
619 struct RemoveContext 
620 {
621   /**
622    * Client for whom we're doing the remvoing.
623    */
624   struct GNUNET_SERVER_Client *client;
625
626   /**
627    * GNUNET_YES if we managed to remove something.
628    */
629   int found;
630 };
631
632
633 /**
634  * Callback function that will cause the item that is passed
635  * in to be deleted (by returning GNUNET_NO).
636  */
637 static int
638 remove_callback (void *cls,
639                  void *next_cls,
640                  const GNUNET_HashCode * key,
641                  uint32_t size,
642                  const void *data,
643                  uint32_t type,
644                  uint32_t priority,
645                  uint32_t anonymity,
646                  struct GNUNET_TIME_Absolute
647                  expiration, uint64_t uid)
648 {
649   struct RemoveContext *rc = cls;
650   if (key == NULL)
651     {
652       if (GNUNET_YES == rc->found)
653         transmit_status (rc->client, GNUNET_OK, NULL);       
654       else
655         transmit_status (rc->client, GNUNET_SYSERR, _("Content not found"));            
656       GNUNET_SERVER_client_drop (rc->client);
657       GNUNET_free (rc);
658       return GNUNET_OK; /* last item */
659     }
660   rc->found = GNUNET_YES;
661   plugin->api->next_request (next_cls, GNUNET_YES);
662   GNUNET_CONTAINER_bloomfilter_remove (filter,
663                                        key);
664   return GNUNET_NO;
665 }
666
667
668 /**
669  * Handle REMOVE-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_remove (void *cls,
677              struct GNUNET_SERVER_Client *client,
678              const struct GNUNET_MessageHeader *message)
679 {
680   const struct DataMessage *dm = check_data (message);
681   GNUNET_HashCode vhash;
682   struct RemoveContext *rc;
683
684   if (dm == NULL)
685     {
686       GNUNET_break (0);
687       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
688       return;
689     }
690   rc = GNUNET_malloc (sizeof(struct RemoveContext));
691   GNUNET_SERVER_client_keep (client);
692   rc->client = client;
693   GNUNET_CRYPTO_hash (&dm[1],
694                       ntohl(dm->size),
695                       &vhash);
696   plugin->api->get (plugin->api->cls,
697                     &dm->key,
698                     &vhash,
699                     ntohl(dm->type),
700                     &remove_callback,
701                     rc);
702 }
703
704
705 /**
706  * Handle DROP-message.
707  *
708  * @param cls closure
709  * @param client identification of the client
710  * @param message the actual message
711  */
712 static void
713 handle_drop (void *cls,
714              struct GNUNET_SERVER_Client *client,
715              const struct GNUNET_MessageHeader *message)
716 {
717   plugin->api->drop (plugin->api->cls);
718   GNUNET_SERVER_receive_done (client, GNUNET_OK);
719 }
720
721
722 /**
723  * List of handlers for the messages understood by this
724  * service.
725  */
726 static struct GNUNET_SERVER_MessageHandler handlers[] = {
727   {&handle_reserve, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_RESERVE, 
728    sizeof(struct ReserveMessage) }, 
729   {&handle_release_reserve, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_RELEASE_RESERVE, 
730    sizeof(struct ReleaseReserveMessage) }, 
731   {&handle_put, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_PUT, 0 }, 
732   {&handle_update, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_UPDATE, 
733    sizeof (struct UpdateMessage) }, 
734   {&handle_get, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_GET, 0 }, 
735   {&handle_get_random, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_GET_RANDOM, 
736    sizeof(struct GNUNET_MessageHeader) }, 
737   {&handle_remove, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_REMOVE, 0 }, 
738   {&handle_drop, NULL, GNUNET_MESSAGE_TYPE_DATASTORE_DROP, 
739    sizeof(struct GNUNET_MessageHeader) }, 
740   {NULL, NULL, 0, 0}
741 };
742
743
744
745 /**
746  * Load the datastore plugin.
747  */
748 static struct DatastorePlugin *
749 load_plugin (struct GNUNET_CONFIGURATION_Handle *cfg,
750              struct GNUNET_SCHEDULER_Handle *sched)
751 {
752   struct DatastorePlugin *ret;
753   char *libname;
754   char *name;
755
756   if (GNUNET_OK !=
757       GNUNET_CONFIGURATION_get_value_string (cfg,
758                                              "DATASTORE", "DATABASE", &name))
759     {
760       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
761                   _("No `%s' specified for `%s' in configuration!\n"),
762                   "DATABASE",
763                   "DATASTORE");
764       return NULL;
765     }
766   ret = GNUNET_malloc (sizeof(struct DatastorePlugin));
767   ret->env.cfg = cfg;
768   ret->env.sched = sched;  
769   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
770               _("Loading `%s' datastore plugin\n"), name);
771   GNUNET_asprintf (&libname, "libgnunet_plugin_datastore_%s", name);
772   ret->short_name = GNUNET_strdup (name);
773   ret->lib_name = libname;
774   ret->api = GNUNET_PLUGIN_load (libname, &ret->env);
775   if (ret->api == NULL)
776     {
777       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
778                   _("Failed to load datastore plugin for `%s'\n"), name);
779       GNUNET_free (ret->short_name);
780       GNUNET_free (libname);
781       GNUNET_free (ret);
782       return NULL;
783     }
784   return ret;
785 }
786
787
788 /**
789  * Function called when the service shuts
790  * down.  Unloads our datastore plugin.
791  *
792  * @param plug plugin to unload
793  */
794 static void
795 unload_plugin (struct DatastorePlugin *plug)
796 {
797 #if DEBUG_DATASTORE
798   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
799               "Datastore service is unloading plugin...\n");
800 #endif
801   GNUNET_break (NULL == GNUNET_PLUGIN_unload (plug->lib_name, plug->api));
802   GNUNET_free (plug->lib_name);
803   GNUNET_free (plug->short_name);
804   GNUNET_free (plug);
805 }
806
807
808 /**
809  * Last task run during shutdown.  Disconnects us from
810  * the transport and core.
811  */
812 static void
813 cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
814 {
815   unload_plugin (plugin);
816   plugin = NULL;
817 }
818
819
820 /**
821  * Function that removes all active reservations made
822  * by the given client and releases the space for other
823  * requests.
824  *
825  * @param cls closure
826  * @param client identification of the client
827  */
828 static void
829 cleanup_reservations (void *cls,
830                       struct GNUNET_SERVER_Client
831                       * client)
832 {
833   /* FIXME */
834 }
835
836
837 /**
838  * Process datastore requests.
839  *
840  * @param cls closure
841  * @param sched scheduler to use
842  * @param server the initialized server
843  * @param cfg configuration to use
844  */
845 static void
846 run (void *cls,
847      struct GNUNET_SCHEDULER_Handle *sched,
848      struct GNUNET_SERVER_Handle *server,
849      struct GNUNET_CONFIGURATION_Handle *cfg)
850 {
851   char *fn;
852   unsigned int bf_size;
853
854   if (GNUNET_OK !=
855       GNUNET_CONFIGURATION_get_value_number (cfg,
856                                              "DATASTORE", "QUOTA", &quota))
857     {
858       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
859                   _("No `%s' specified for `%s' in configuration!\n"),
860                   "QUOTA",
861                   "DATASTORE");
862       return;
863     }
864   bf_size = quota / 32; /* 8 bit per entry, 1 bit per 32 kb in DB */
865   fn = NULL;
866   if ( (GNUNET_OK !=
867         GNUNET_CONFIGURATION_get_value_filename (cfg,
868                                                  "DATASTORE",
869                                                  "BLOOMFILTER",
870                                                  &fn)) ||
871        (GNUNET_OK !=
872         GNUNET_DISK_directory_create_for_file (fn)) )
873     {
874       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
875                   _("Could not use specified filename `%s' for bloomfilter.\n"),
876                   fn != NULL ? fn : "");
877       GNUNET_free_non_null (fn);
878       fn = NULL;
879     }
880   filter = GNUNET_CONTAINER_bloomfilter_load (fn, bf_size, 5);  /* approx. 3% false positives at max use */  
881   GNUNET_free_non_null (fn);
882   if (filter == NULL)
883     {
884       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
885                   _("Failed to initialize bloomfilter.\n"));
886       return;
887     }
888   plugin = load_plugin (cfg, sched);
889   if (NULL == plugin)
890     {
891       GNUNET_CONTAINER_bloomfilter_free (filter);
892       return;
893     }
894   GNUNET_SERVER_disconnect_notify (server, &cleanup_reservations, NULL);
895   GNUNET_SERVER_add_handlers (server, handlers);
896   GNUNET_SCHEDULER_add_delayed (sched,
897                                 GNUNET_YES,
898                                 GNUNET_SCHEDULER_PRIORITY_IDLE,
899                                 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
900                                 GNUNET_TIME_UNIT_FOREVER_REL,
901                                 &cleaning_task, NULL);
902 }
903
904
905 /**
906  * The main function for the datastore service.
907  *
908  * @param argc number of arguments from the command line
909  * @param argv command line arguments
910  * @return 0 ok, 1 on error
911  */
912 int
913 main (int argc, char *const *argv)
914 {
915   int ret;
916
917   ret = (GNUNET_OK ==
918          GNUNET_SERVICE_run (argc,
919                              argv,
920                              "datastore", &run, NULL, NULL, NULL)) ? 0 : 1;
921   return ret;
922 }
923
924
925 /* end of gnunet-service-datastore.c */