-consistently use struct GNUNET_HashCode
[oweals/gnunet.git] / src / namestore / plugin_namestore_postgres.c
1  /*
2   * This file is part of GNUnet
3   * (C) 2009, 2011, 2012 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 3, 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 namestore/plugin_namestore_postgres.c
23  * @brief postgres-based namestore backend
24  * @author Christian Grothoff
25  */
26
27 #include "platform.h"
28 #include "gnunet_namestore_plugin.h"
29 #include "gnunet_namestore_service.h"
30 #include "gnunet_postgres_lib.h"
31 #include "namestore.h"
32
33
34 /**
35  * After how many ms "busy" should a DB operation fail for good?
36  * A low value makes sure that we are more responsive to requests
37  * (especially PUTs).  A high value guarantees a higher success
38  * rate (SELECTs in iterate can take several seconds despite LIMIT=1).
39  *
40  * The default value of 1s should ensure that users do not experience
41  * huge latencies while at the same time allowing operations to succeed
42  * with reasonable probability.
43  */
44 #define BUSY_TIMEOUT_MS 1000
45
46
47 /**
48  * Log an error message at log-level 'level' that indicates
49  * a failure of the command 'cmd' on file 'filename'
50  * with the message given by strerror(errno).
51  */
52 #define LOG_POSTGRES(db, level, cmd) do { GNUNET_log_from (level, "namestore-postgres", _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, sqlite3_errmsg(db->dbh)); } while(0)
53
54 #define LOG(kind,...) GNUNET_log_from (kind, "namestore-postgres", __VA_ARGS__)
55
56
57 /**
58  * Context for all functions in this plugin.
59  */
60 struct Plugin
61 {
62
63   const struct GNUNET_CONFIGURATION_Handle *cfg;
64
65   /**
66    * Native Postgres database handle.
67    */
68   PGconn *dbh;
69
70 };
71
72
73 /**
74  * Create our database indices.
75  *
76  * @param dbh handle to the database
77  */
78 static void
79 create_indices (PGconn * dbh)
80 {
81   /* create indices */
82   if ( (GNUNET_OK !=
83         GNUNET_POSTGRES_exec (dbh, "CREATE INDEX ir_zone_name_rv ON ns091records (zone_hash,record_name_hash,rvalue)")) ||
84        (GNUNET_OK !=
85         GNUNET_POSTGRES_exec (dbh, "CREATE INDEX ir_zone_delegation ON ns091records (zone_hash,zone_delegation)")) ||
86        (GNUNET_OK !=
87         GNUNET_POSTGRES_exec (dbh, "CREATE INDEX ir_zone_rv ON ns091records (zone_hash,rvalue)")) ||
88        (GNUNET_OK !=
89         GNUNET_POSTGRES_exec (dbh, "CREATE INDEX ir_zone ON ns091records (zone_hash)")) ||
90        (GNUNET_OK !=
91         GNUNET_POSTGRES_exec (dbh, "CREATE INDEX ir_name_rv ON ns091records (record_name_hash,rvalue)")) ||
92        (GNUNET_OK !=
93         GNUNET_POSTGRES_exec (dbh, "CREATE INDEX ir_rv ON ns091records (rvalue)")) )
94     LOG (GNUNET_ERROR_TYPE_ERROR, 
95          _("Failed to create indices\n"));
96 }
97
98
99 /**
100  * Initialize the database connections and associated
101  * data structures (create tables and indices
102  * as needed as well).
103  *
104  * @param plugin the plugin context (state for this module)
105  * @return GNUNET_OK on success
106  */
107 static int
108 database_setup (struct Plugin *plugin)
109 {
110   PGresult *res;
111
112   plugin->dbh = GNUNET_POSTGRES_connect (plugin->cfg,
113                                          "namestore-postgres");
114   if (NULL == plugin->dbh)
115     return GNUNET_SYSERR;
116   res =
117       PQexec (plugin->dbh,
118               "CREATE TABLE ns091records ("
119               " zone_key BYTEA NOT NULL DEFAULT ''," 
120               " zone_delegation BYTEA NOT NULL DEFAULT ''," 
121               " zone_hash BYTEA NOT NULL DEFAULT ''," 
122               " record_count INTEGER NOT NULL DEFAULT 0,"
123               " record_data BYTEA NOT NULL DEFAULT '',"
124               " block_expiration_time BIGINT NOT NULL DEFAULT 0," 
125               " signature BYTEA NOT NULL DEFAULT '',"
126               " record_name TEXT NOT NULL DEFAULT ''," 
127               " record_name_hash BYTEA NOT NULL DEFAULT ''," 
128               " rvalue BIGINT NOT NULL DEFAULT 0"
129               ")" "WITH OIDS");
130   if ((NULL == res) || ((PQresultStatus (res) != PGRES_COMMAND_OK) && (0 != strcmp ("42P07",    /* duplicate table */
131                                                                                     PQresultErrorField
132                                                                                     (res,
133                                                                                      PG_DIAG_SQLSTATE)))))
134   {
135     (void) GNUNET_POSTGRES_check_result (plugin->dbh, res, PGRES_COMMAND_OK, "CREATE TABLE",
136                                          "ns091records");
137     PQfinish (plugin->dbh);
138     plugin->dbh = NULL;
139     return GNUNET_SYSERR;
140   }
141   if (PQresultStatus (res) == PGRES_COMMAND_OK)
142     create_indices (plugin->dbh);
143   PQclear (res);
144
145 #define ALL "zone_key, record_name, record_count, record_data, block_expiration_time, signature"
146   if ((GNUNET_OK !=
147        GNUNET_POSTGRES_prepare (plugin->dbh,
148                                 "put_records",
149                                 "INSERT INTO ns091records (" ALL 
150                                 ", zone_delegation, zone_hash, record_name_hash, rvalue) VALUES "
151                                 "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", 10)) ||
152       (GNUNET_OK !=
153        GNUNET_POSTGRES_prepare (plugin->dbh,
154                                 "remove_records",
155                                 "DELETE FROM ns091records WHERE zone_hash=$1 AND record_name_hash=$2", 2)) ||
156       (GNUNET_OK !=
157        GNUNET_POSTGRES_prepare (plugin->dbh,
158                                 "iterate_records",
159                                 "SELECT " ALL
160                                 " FROM ns091records WHERE zone_hash=$1 AND record_name_hash=$2 ORDER BY rvalue LIMIT 1 OFFSET $3", 3)) ||
161       (GNUNET_OK !=
162        GNUNET_POSTGRES_prepare (plugin->dbh,
163                                 "iterate_by_zone",
164                                 "SELECT " ALL
165                                 " FROM ns091records WHERE zone_hash=$1 ORDER BY rvalue  LIMIT 1 OFFSET $2", 2)) ||
166       (GNUNET_OK !=
167        GNUNET_POSTGRES_prepare (plugin->dbh,
168                                 "iterate_by_name",
169                                 "SELECT " ALL 
170                                 " FROM ns091records WHERE record_name_hash=$1 ORDER BY rvalue LIMIT 1 OFFSET $2", 2)) ||
171       (GNUNET_OK !=
172        GNUNET_POSTGRES_prepare (plugin->dbh,
173                                 "iterate_all",
174                                 "SELECT " ALL
175                                 " FROM ns091records ORDER BY rvalue LIMIT 1 OFFSET $1", 1)) ||
176       (GNUNET_OK !=
177        GNUNET_POSTGRES_prepare (plugin->dbh,
178                                 "zone_to_name",
179                                 "SELECT " ALL
180                                 " FROM ns091records WHERE zone_hash=$1 AND zone_delegation=$2", 2)) ||
181       (GNUNET_OK !=
182        GNUNET_POSTGRES_prepare (plugin->dbh,
183                                 "delete_zone",
184                                 "DELETE FROM ns091records WHERE zone_hash=$1", 1)))
185   {
186     PQfinish (plugin->dbh);
187     plugin->dbh = NULL;
188     return GNUNET_SYSERR;
189   }
190 #undef ALL
191   return GNUNET_OK;
192 }
193
194
195 /**
196  * Removes any existing record in the given zone with the same name.
197  *
198  * @param cls closure (internal context for the plugin)
199  * @param zone hash of the public key of the zone
200  * @param name name to remove (at most 255 characters long)
201  * @return GNUNET_OK on success
202  */
203 static int 
204 namestore_postgres_remove_records (void *cls, 
205                                  const struct GNUNET_CRYPTO_ShortHashCode *zone,
206                                  const char *name)
207 {
208   struct Plugin *plugin = cls;
209   PGresult *ret;
210   struct GNUNET_CRYPTO_ShortHashCode nh;
211   const char *paramValues[] = {
212     (const char *) zone,
213     (const char *) &nh
214   };
215   int paramLengths[] = {
216     sizeof (struct GNUNET_CRYPTO_ShortHashCode),
217     sizeof (struct GNUNET_CRYPTO_ShortHashCode)
218   };
219   const int paramFormats[] = { 1, 1 };
220   size_t name_len;
221
222   name_len = strlen (name);
223   GNUNET_CRYPTO_short_hash (name, name_len, &nh);
224   ret =
225       PQexecPrepared (plugin->dbh, "remove_records", 2, paramValues, paramLengths,
226                       paramFormats, 1);
227   if (GNUNET_OK !=
228       GNUNET_POSTGRES_check_result (plugin->dbh, ret, PGRES_COMMAND_OK, "PQexecPrepared", "remove_records"))
229     return GNUNET_SYSERR;
230   PQclear (ret);
231   return GNUNET_OK;
232 }
233
234
235 /**
236  * Store a record in the datastore.  Removes any existing record in the
237  * same zone with the same name.
238  *
239  * @param cls closure (internal context for the plugin)
240  * @param zone_key public key of the zone
241  * @param expire when does the corresponding block in the DHT expire (until
242  *               when should we never do a DHT lookup for the same name again)?
243  * @param name name that is being mapped (at most 255 characters long)
244  * @param rd_count number of entries in 'rd' array
245  * @param rd array of records with data to store
246  * @param signature signature of the record block, NULL if signature is unavailable (i.e. 
247  *        because the user queried for a particular record type only)
248  * @return GNUNET_OK on success, else GNUNET_SYSERR
249  */
250 static int 
251 namestore_postgres_put_records (void *cls, 
252                                 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *zone_key,
253                                 struct GNUNET_TIME_Absolute expire,
254                                 const char *name,
255                                 unsigned int rd_count,
256                                 const struct GNUNET_NAMESTORE_RecordData *rd,
257                                 const struct GNUNET_CRYPTO_RsaSignature *signature)
258 {
259   struct Plugin *plugin = cls;
260   PGresult *ret;
261   struct GNUNET_CRYPTO_ShortHashCode zone;
262   struct GNUNET_CRYPTO_ShortHashCode zone_delegation;
263   struct GNUNET_CRYPTO_ShortHashCode nh;
264   size_t name_len;
265   uint64_t rvalue;
266   size_t data_size;
267   unsigned int i;
268
269   GNUNET_CRYPTO_short_hash (zone_key, 
270                             sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
271                             &zone);
272   (void) namestore_postgres_remove_records (plugin, &zone, name);
273   name_len = strlen (name);
274   GNUNET_CRYPTO_short_hash (name, name_len, &nh);
275   memset (&zone_delegation, 0, sizeof (zone_delegation));
276   for (i=0;i<rd_count;i++)
277     if (rd[i].record_type == GNUNET_NAMESTORE_TYPE_PKEY)
278     {
279       GNUNET_assert (sizeof (struct GNUNET_CRYPTO_ShortHashCode) == rd[i].data_size);
280       memcpy (&zone_delegation,
281               rd[i].data,
282               sizeof (struct GNUNET_CRYPTO_ShortHashCode));
283       break;
284     }
285   rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
286   data_size = GNUNET_NAMESTORE_records_get_size (rd_count, rd);
287   if (data_size > 64 * 65536)
288   {
289     GNUNET_break (0);
290     return GNUNET_SYSERR;
291   }
292   {
293     char data[data_size];
294     uint64_t expire_be = GNUNET_htonll (expire.abs_value);
295     uint64_t rvalue_be = GNUNET_htonll (rvalue);
296     uint32_t rd_count_be = htonl ((uint32_t) rd_count);
297     const char *paramValues[] = {
298       (const char *) zone_key,
299       (const char *) name,
300       (const char *) &rd_count_be,
301       (const char *) data,
302       (const char *) &expire_be,
303       (const char *) signature,
304       (const char *) &zone_delegation,
305       (const char *) &zone,
306       (const char *) &nh,
307       (const char *) &rvalue_be
308     };
309     int paramLengths[] = {
310       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
311       name_len,
312       sizeof (uint32_t),
313       data_size,
314       sizeof (uint64_t),
315       sizeof (struct GNUNET_CRYPTO_RsaSignature), 
316       sizeof (struct GNUNET_CRYPTO_ShortHashCode),
317       sizeof (struct GNUNET_CRYPTO_ShortHashCode),
318       sizeof (struct GNUNET_CRYPTO_ShortHashCode),
319       sizeof (uint64_t)
320     };
321     const int paramFormats[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
322
323     if (data_size != GNUNET_NAMESTORE_records_serialize (rd_count, rd,
324                                                          data_size, data))
325     {
326       GNUNET_break (0);
327       return GNUNET_SYSERR;
328     }
329     ret =
330       PQexecPrepared (plugin->dbh, "put_records", 10, paramValues, paramLengths,
331                       paramFormats, 1);
332     if (GNUNET_OK !=
333         GNUNET_POSTGRES_check_result (plugin->dbh, ret, PGRES_COMMAND_OK, "PQexecPrepared", "put_records"))
334       return GNUNET_SYSERR;
335     PQclear (ret);
336   }
337   return GNUNET_OK;  
338 }
339
340
341 /**
342  * The given 'postgres' result was obtained from the database.
343  * Parse the record and give it to the iterator.
344  *
345  * @param plugin plugin context
346  * @param stmt_name name of the prepared statement that was executed
347  * @param res result from postgres to interpret (and then clean up)
348  * @param iter iterator to call with the result
349  * @param iter_cls closure for 'iter'
350  * @return GNUNET_OK on success, GNUNET_NO if there were no results, GNUNET_SYSERR on error
351  */
352 static int
353 get_record_and_call_iterator (struct Plugin *plugin,
354                               const char *stmt_name,
355                               PGresult *res,
356                               GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls)
357 {
358   unsigned int record_count;
359   size_t data_size;
360   const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *zone_key;
361   const struct GNUNET_CRYPTO_RsaSignature *sig;
362   struct GNUNET_TIME_Absolute expiration;
363   const char *data;
364   const char *name;
365   unsigned int cnt;
366   size_t name_len;
367
368   if (GNUNET_OK !=
369       GNUNET_POSTGRES_check_result (plugin->dbh, res, PGRES_TUPLES_OK, "PQexecPrepared",
370                                     stmt_name))
371   {
372     LOG (GNUNET_ERROR_TYPE_DEBUG,
373          "Ending iteration (postgres error)\n");
374     return GNUNET_SYSERR;
375   }
376
377   if (0 == (cnt = PQntuples (res)))
378   {
379     /* no result */
380     LOG (GNUNET_ERROR_TYPE_DEBUG, 
381          "Ending iteration (no more results)\n");
382     PQclear (res);
383     return GNUNET_NO;
384   }
385   GNUNET_assert (1 == cnt);
386   if ((6 != PQnfields (res)) || 
387       (sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) != PQgetlength (res, 0, 0)) || 
388       (sizeof (uint32_t) != PQfsize (res, 2)) || 
389       (sizeof (uint64_t) != PQfsize (res, 4)) || 
390       (sizeof (struct GNUNET_CRYPTO_RsaSignature) != PQgetlength (res, 0, 5)))
391   {
392     GNUNET_break (0);
393     PQclear (res);
394     return GNUNET_SYSERR;
395   }
396   zone_key = (const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *) PQgetvalue (res, 0, 0);
397   name = PQgetvalue (res, 0, 1);
398   name_len = PQgetlength (res, 0, 1);
399   record_count = ntohl (*(uint32_t *) PQgetvalue (res, 0, 2));
400   data_size = PQgetlength (res, 0, 3);
401   data = PQgetvalue (res, 0, 3);
402   expiration.abs_value =
403     GNUNET_ntohll (*(uint64_t *) PQgetvalue (res, 0, 4));
404   sig = (const struct GNUNET_CRYPTO_RsaSignature*) PQgetvalue (res, 0, 5);
405   if (record_count > 64 * 1024)
406   {
407     /* sanity check, don't stack allocate far too much just
408        because database might contain a large value here */
409     GNUNET_break (0);
410     PQclear (res);
411     return GNUNET_SYSERR;
412   }
413   {
414     struct GNUNET_NAMESTORE_RecordData rd[record_count];
415     char buf[name_len + 1];
416     
417     memcpy (buf, name, name_len);
418     buf[name_len] = '\0';
419     if (GNUNET_OK !=
420         GNUNET_NAMESTORE_records_deserialize (data_size, data,
421                                               record_count, rd))
422     {
423       GNUNET_break (0);
424       PQclear (res);
425       return GNUNET_SYSERR;
426     }
427     iter (iter_cls, zone_key, expiration, buf, 
428           record_count, rd, sig);
429   }
430   PQclear (res);
431   return GNUNET_OK;
432 }
433   
434   
435 /**
436  * Iterate over the results for a particular key and zone in the
437  * datastore.  Will return at most one result to the iterator.
438  *
439  * @param cls closure (internal context for the plugin)
440  * @param zone hash of public key of the zone, NULL to iterate over all zones
441  * @param name name as string, NULL to iterate over all records of the zone
442  * @param offset offset in the list of all matching records
443  * @param iter function to call with the result
444  * @param iter_cls closure for iter
445  * @return GNUNET_OK on success, GNUNET_NO if there were no results, GNUNET_SYSERR on error
446  */
447 static int 
448 namestore_postgres_iterate_records (void *cls, 
449                                   const struct GNUNET_CRYPTO_ShortHashCode *zone,
450                                   const char *name,
451                                   uint64_t offset,
452                                   GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls)
453 {
454   struct Plugin *plugin = cls;
455   const char *stmt_name;
456   struct GNUNET_CRYPTO_ShortHashCode name_hase;
457   uint64_t offset_be = GNUNET_htonll (offset);
458   const char *paramValues[] = {
459     (const char *) zone,
460     (const char *) &name_hase,
461     (const char *) &offset_be,
462     (const char *) zone,
463     (const char *) &offset_be,
464   };
465   int paramLengths[] = {
466     sizeof (struct GNUNET_CRYPTO_ShortHashCode),
467     sizeof (struct GNUNET_CRYPTO_ShortHashCode),
468     sizeof (uint64_t),
469     sizeof (struct GNUNET_CRYPTO_ShortHashCode),
470     sizeof (uint64_t)
471   };
472   const int paramFormats[] = { 1, 1, 1, 1, 1 };
473   unsigned int num_params;
474   unsigned int first_param;
475   PGresult *res;
476
477   if (NULL == zone)
478     if (NULL == name)
479     {
480       stmt_name = "iterate_all";
481       num_params = 1;
482       first_param = 2;
483     }
484     else
485     {
486       GNUNET_CRYPTO_short_hash (name, strlen(name), &name_hase);
487       stmt_name = "iterate_by_name";
488       num_params = 2;
489       first_param = 1;
490     }
491   else
492     if (NULL == name)
493     {
494       stmt_name = "iterate_by_zone";
495       num_params = 2;
496       first_param = 3;
497     }
498     else
499     {
500       GNUNET_CRYPTO_short_hash (name, strlen(name), &name_hase);
501       stmt_name = "iterate_records";
502       num_params = 3;
503       first_param = 0;
504     }
505   res =
506       PQexecPrepared (plugin->dbh, stmt_name, num_params, 
507                       &paramValues[first_param],
508                       &paramLengths[first_param],
509                       &paramFormats[first_param], 1);
510   return get_record_and_call_iterator (plugin, stmt_name, res, iter, iter_cls);
511 }
512
513
514 /**
515  * Look for an existing PKEY delegation record for a given public key.
516  * Returns at most one result to the iterator.
517  *
518  * @param cls closure (internal context for the plugin)
519  * @param zone hash of public key of the zone to look up in, never NULL
520  * @param value_zone hash of the public key of the target zone (value), never NULL
521  * @param iter function to call with the result
522  * @param iter_cls closure for iter
523  * @return GNUNET_OK on success, GNUNET_NO if there were no results, GNUNET_SYSERR on error
524  */
525 static int
526 namestore_postgres_zone_to_name (void *cls, 
527                                const struct GNUNET_CRYPTO_ShortHashCode *zone,
528                                const struct GNUNET_CRYPTO_ShortHashCode *value_zone,
529                                GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls)
530 {
531   struct Plugin *plugin = cls;
532   const char *paramValues[] = {
533     (const char *) zone,
534     (const char *) value_zone
535   };
536   int paramLengths[] = {
537     sizeof (struct GNUNET_CRYPTO_ShortHashCode),
538     sizeof (struct GNUNET_CRYPTO_ShortHashCode)
539   };
540   const int paramFormats[] = { 1, 1 };
541   PGresult *res;
542
543   res =
544     PQexecPrepared (plugin->dbh, "zone_to_name", 2,
545                     paramValues, paramLengths, paramFormats, 1);
546   return get_record_and_call_iterator (plugin, "zone_to_name", res, iter, iter_cls);
547 }
548
549
550 /**
551  * Delete an entire zone (all records).  Not used in normal operation.
552  *
553  * @param cls closure (internal context for the plugin)
554  * @param zone zone to delete
555  */
556 static void 
557 namestore_postgres_delete_zone (void *cls,
558                               const struct GNUNET_CRYPTO_ShortHashCode *zone)
559 {
560   struct Plugin *plugin = cls;
561   PGresult *ret;
562   const char *paramValues[] = {
563     (const char *) zone,
564   };
565   int paramLengths[] = {
566     sizeof (struct GNUNET_CRYPTO_ShortHashCode)
567   };
568   const int paramFormats[] = { 1 };
569
570   ret =
571       PQexecPrepared (plugin->dbh, "delete_zone", 1, paramValues, paramLengths,
572                       paramFormats, 1);
573   if (GNUNET_OK !=
574       GNUNET_POSTGRES_check_result (plugin->dbh, ret, PGRES_COMMAND_OK, "PQexecPrepared", "delete_zone"))
575   {
576     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
577                 "Deleting zone failed!\n");             
578     return;
579   }
580   PQclear (ret);
581 }
582
583
584 /**
585  * Shutdown database connection and associate data
586  * structures.
587  *
588  * @param plugin the plugin context (state for this module)
589  */
590 static void
591 database_shutdown (struct Plugin *plugin)
592 {
593   PQfinish (plugin->dbh);
594   plugin->dbh = NULL;
595 }
596
597
598 /**
599  * Entry point for the plugin.
600  *
601  * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
602  * @return NULL on error, othrewise the plugin context
603  */
604 void *
605 libgnunet_plugin_namestore_postgres_init (void *cls)
606 {
607   static struct Plugin plugin;
608   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
609   struct GNUNET_NAMESTORE_PluginFunctions *api;
610
611   if (NULL != plugin.cfg)
612     return NULL;                /* can only initialize once! */
613   memset (&plugin, 0, sizeof (struct Plugin));
614   plugin.cfg = cfg;  
615   if (GNUNET_OK != database_setup (&plugin))
616   {
617     database_shutdown (&plugin);
618     return NULL;
619   }
620   api = GNUNET_malloc (sizeof (struct GNUNET_NAMESTORE_PluginFunctions));
621   api->cls = &plugin;
622   api->put_records = &namestore_postgres_put_records;
623   api->remove_records = &namestore_postgres_remove_records;
624   api->iterate_records = &namestore_postgres_iterate_records;
625   api->zone_to_name = &namestore_postgres_zone_to_name;
626   api->delete_zone = &namestore_postgres_delete_zone;
627   LOG (GNUNET_ERROR_TYPE_INFO, 
628        _("Postgres database running\n"));
629   return api;
630 }
631
632
633 /**
634  * Exit point from the plugin.
635  *
636  * @param cls the plugin context (as returned by "init")
637  * @return always NULL
638  */
639 void *
640 libgnunet_plugin_namestore_postgres_done (void *cls)
641 {
642   struct GNUNET_NAMESTORE_PluginFunctions *api = cls;
643   struct Plugin *plugin = api->cls;
644
645   database_shutdown (plugin);
646   plugin->cfg = NULL;
647   GNUNET_free (api);
648   LOG (GNUNET_ERROR_TYPE_DEBUG, 
649        "postgres plugin is finished\n");
650   return NULL;
651 }
652
653 /* end of plugin_namestore_postgres.c */