run indent twice, it alternates between two 'canonical' forms, also run whitespace...
[oweals/gnunet.git] / src / datacache / datacache.c
1 /*
2      This file is part of GNUnet
3      (C) 2004, 2005, 2006, 2007, 2009, 2010 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 datacache/datacache.c
23  * @brief datacache API implementation
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_datacache_lib.h"
29 #include "gnunet_statistics_service.h"
30 #include "gnunet_datacache_plugin.h"
31
32 /**
33  * Internal state of the datacache library.
34  */
35 struct GNUNET_DATACACHE_Handle
36 {
37
38   /**
39    * Bloomfilter to quickly tell if we don't have the content.
40    */
41   struct GNUNET_CONTAINER_BloomFilter *filter;
42
43   /**
44    * Our configuration.
45    */
46   const struct GNUNET_CONFIGURATION_Handle *cfg;
47
48   /**
49    * Opaque handle for the statistics service.
50    */
51   struct GNUNET_STATISTICS_Handle *stats;
52
53   /**
54    * Configuration section to use.
55    */
56   char *section;
57
58   /**
59    * API of the transport as returned by the plugin's
60    * initialization function.
61    */
62   struct GNUNET_DATACACHE_PluginFunctions *api;
63
64   /**
65    * Short name for the plugin (i.e. "sqlite").
66    */
67   char *short_name;
68
69   /**
70    * Name of the library (i.e. "gnunet_plugin_datacache_sqlite").
71    */
72   char *lib_name;
73
74   /**
75    * Name for the bloom filter file.
76    */
77   char *bloom_name;
78
79   /**
80    * Environment provided to our plugin.
81    */
82   struct GNUNET_DATACACHE_PluginEnvironment env;
83
84   /**
85    * How much space is in use right now?
86    */
87   unsigned long long utilization;
88
89 };
90
91
92 /**
93  * Function called by plugins to notify the datacache
94  * about content deletions.
95  *
96  * @param cls closure
97  * @param key key of the content that was deleted
98  * @param size number of bytes that were made available
99  */
100 static void
101 env_delete_notify (void *cls, const GNUNET_HashCode * key, size_t size)
102 {
103   struct GNUNET_DATACACHE_Handle *h = cls;
104
105   GNUNET_assert (h->utilization >= size);
106   h->utilization -= size;
107   GNUNET_CONTAINER_bloomfilter_remove (h->filter, key);
108   GNUNET_STATISTICS_update (h->stats, gettext_noop ("# bytes stored"), -size,
109                             GNUNET_NO);
110 }
111
112
113 /**
114  * Create a data cache.
115  *
116  * @param cfg configuration to use
117  * @param section section in the configuration that contains our options
118  * @return handle to use to access the service
119  */
120 struct GNUNET_DATACACHE_Handle *
121 GNUNET_DATACACHE_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
122                          const char *section)
123 {
124   unsigned int bf_size;
125   unsigned long long quota;
126   struct GNUNET_DATACACHE_Handle *ret;
127   char *libname;
128   char *name;
129
130   if (GNUNET_OK !=
131       GNUNET_CONFIGURATION_get_value_number (cfg, section, "QUOTA", &quota))
132   {
133     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
134                 _("No `%s' specified for `%s' in configuration!\n"), "QUOTA",
135                 section);
136     return NULL;
137   }
138   if (GNUNET_OK !=
139       GNUNET_CONFIGURATION_get_value_string (cfg, section, "DATABASE", &name))
140   {
141     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
142                 _("No `%s' specified for `%s' in configuration!\n"), "DATABASE",
143                 section);
144     return NULL;
145   }
146   bf_size = quota / 32;         /* 8 bit per entry, 1 bit per 32 kb in DB */
147
148   ret = GNUNET_malloc (sizeof (struct GNUNET_DATACACHE_Handle));
149   ret->bloom_name = GNUNET_DISK_mktemp ("gnunet-datacachebloom");
150   if (NULL != ret->bloom_name)
151   {
152     ret->filter = GNUNET_CONTAINER_bloomfilter_load (ret->bloom_name, quota / 1024,     /* 8 bit per entry in DB, expect 1k entries */
153                                                      5);
154   }
155   else
156   {
157     ret->filter = GNUNET_CONTAINER_bloomfilter_init (NULL, bf_size, 5); /* approx. 3% false positives at max use */
158   }
159   ret->stats = GNUNET_STATISTICS_create ("datacache", cfg);
160   ret->section = GNUNET_strdup (section);
161   ret->env.cfg = cfg;
162   ret->env.delete_notify = &env_delete_notify;
163   ret->env.section = ret->section;
164   ret->env.cls = ret;
165   ret->env.delete_notify = &env_delete_notify;
166   ret->env.quota = quota;
167   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Loading `%s' datacache plugin\n"),
168               name);
169   GNUNET_asprintf (&libname, "libgnunet_plugin_datacache_%s", name);
170   ret->short_name = name;
171   ret->lib_name = libname;
172   ret->api = GNUNET_PLUGIN_load (libname, &ret->env);
173   if (ret->api == NULL)
174   {
175     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
176                 _("Failed to load datacache plugin for `%s'\n"), name);
177     GNUNET_DATACACHE_destroy (ret);
178     return NULL;
179   }
180   return ret;
181 }
182
183
184 /**
185  * Destroy a data cache (and free associated resources).
186  *
187  * @param h handle to the datastore
188  */
189 void
190 GNUNET_DATACACHE_destroy (struct GNUNET_DATACACHE_Handle *h)
191 {
192   if (h->filter != NULL)
193     GNUNET_CONTAINER_bloomfilter_free (h->filter);
194   if (h->api != NULL)
195     GNUNET_break (NULL == GNUNET_PLUGIN_unload (h->lib_name, h->api));
196   GNUNET_free (h->lib_name);
197   GNUNET_free (h->short_name);
198   GNUNET_free (h->section);
199   if (h->bloom_name != NULL)
200   {
201     if (0 != UNLINK (h->bloom_name))
202       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink",
203                                 h->bloom_name);
204     GNUNET_free (h->bloom_name);
205   }
206   GNUNET_STATISTICS_destroy (h->stats, GNUNET_NO);
207   GNUNET_free (h);
208 }
209
210
211 /**
212  * Store an item in the datastore.
213  *
214  * @param h handle to the datacache
215  * @param key key to store data under
216  * @param size number of bytes in data
217  * @param data data to store
218  * @param type type of the value
219  * @param discard_time when to discard the value in any case
220  * @return GNUNET_OK on success, GNUNET_SYSERR on error (full, etc.)
221  */
222 int
223 GNUNET_DATACACHE_put (struct GNUNET_DATACACHE_Handle *h,
224                       const GNUNET_HashCode * key, size_t size,
225                       const char *data, enum GNUNET_BLOCK_Type type,
226                       struct GNUNET_TIME_Absolute discard_time)
227 {
228   uint32_t used;
229
230   used = h->api->put (h->api->cls, key, size, data, type, discard_time);
231   if (used == 0)
232   {
233     GNUNET_break (0);
234     return GNUNET_SYSERR;
235   }
236   GNUNET_STATISTICS_update (h->stats, gettext_noop ("# bytes stored"), size,
237                             GNUNET_NO);
238   GNUNET_CONTAINER_bloomfilter_add (h->filter, key);
239   while (h->utilization + used > h->env.quota)
240     GNUNET_assert (GNUNET_OK == h->api->del (h->api->cls));
241   h->utilization += used;
242   return GNUNET_OK;
243 }
244
245
246 /**
247  * Iterate over the results for a particular key
248  * in the datacache.
249  *
250  * @param h handle to the datacache
251  * @param key what to look up
252  * @param type entries of which type are relevant?
253  * @param iter maybe NULL (to just count)
254  * @param iter_cls closure for iter
255  * @return the number of results found
256  */
257 unsigned int
258 GNUNET_DATACACHE_get (struct GNUNET_DATACACHE_Handle *h,
259                       const GNUNET_HashCode * key, enum GNUNET_BLOCK_Type type,
260                       GNUNET_DATACACHE_Iterator iter, void *iter_cls)
261 {
262   GNUNET_STATISTICS_update (h->stats, gettext_noop ("# requests received"), 1,
263                             GNUNET_NO);
264   if (GNUNET_OK != GNUNET_CONTAINER_bloomfilter_test (h->filter, key))
265   {
266     GNUNET_STATISTICS_update (h->stats,
267                               gettext_noop
268                               ("# requests filtered by bloom filter"), 1,
269                               GNUNET_NO);
270     return 0;                   /* can not be present */
271   }
272   return h->api->get (h->api->cls, key, type, iter, iter_cls);
273 }
274
275
276
277 /* end of datacache_api.c */