-first draft of postgres namestore plugin (#2157)
[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 IF NOT EXISTS ir_zone_name_rv ON ns091records (zone_hash,record_name_hash,rvalue)")) ||
84        (GNUNET_OK !=
85         GNUNET_POSTGRES_exec (dbh, "CREATE INDEX IF NOT EXISTS ir_zone_delegation ON ns091records (zone_hash,zone_delegation)")) ||
86        (GNUNET_OK !=
87         GNUNET_POSTGRES_exec (dbh, "CREATE INDEX IF NOT EXISTS ir_zone_rv ON ns091records (zone_hash,rvalue)")) ||
88        (GNUNET_OK !=
89         GNUNET_POSTGRES_exec (dbh, "CREATE INDEX IF NOT EXISTS ir_zone ON ns091records (zone_hash)")) ||
90        (GNUNET_OK !=
91         GNUNET_POSTGRES_exec (dbh, "CREATE INDEX IF NOT EXISTS ir_name_rv ON ns091records (record_name_hash,rvalue)")) ||
92        (GNUNET_OK !=
93         GNUNET_POSTGRES_exec (dbh, "CREATE INDEX IF NOT EXISTS 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 BLOB NOT NULL DEFAULT ''," 
120               " zone_delegation BLOB NOT NULL DEFAULT ''," 
121               " zone_hash BLOB NOT NULL DEFAULT ''," 
122               " record_count INT NOT NULL DEFAULT 0,"
123               " record_data BLOB NOT NULL DEFAULT '',"
124               " block_expiration_time INT8 NOT NULL DEFAULT 0," 
125               " signature BLOB NOT NULL DEFAULT '',"
126               " record_name TEXT NOT NULL DEFAULT ''," 
127               " record_name_hash BLOB NOT NULL DEFAULT ''," 
128               " rvalue INT8 NOT NULL DEFAULT ''"
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", "put"))
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 + 1,
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", 10, paramValues, paramLengths,
331                       paramFormats, 1);
332     if (GNUNET_OK !=
333         GNUNET_POSTGRES_check_result (plugin->dbh, ret, PGRES_COMMAND_OK, "PQexecPrepared", "put"))
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
367   if (GNUNET_OK !=
368       GNUNET_POSTGRES_check_result (plugin->dbh, res, PGRES_TUPLES_OK, "PQexecPrepared",
369                                     stmt_name))
370   {
371     LOG (GNUNET_ERROR_TYPE_DEBUG,
372          "Ending iteration (postgres error)\n");
373     return GNUNET_SYSERR;
374   }
375
376   if (0 == (cnt = PQntuples (res)))
377   {
378     /* no result */
379     LOG (GNUNET_ERROR_TYPE_DEBUG, 
380          "Ending iteration (no more results)\n");
381     PQclear (res);
382     return GNUNET_NO;
383   }
384   GNUNET_assert (1 == cnt);
385   if ((6 != PQnfields (res)) || 
386       (sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) != PQfsize (res, 0)) || 
387       (sizeof (uint32_t) != PQfsize (res, 2)) || 
388       (sizeof (uint64_t) != PQfsize (res, 4)) || 
389       (sizeof (struct GNUNET_CRYPTO_RsaSignature) != PQfsize (res, 5)))
390   {
391     GNUNET_break (0);
392     PQclear (res);
393     return GNUNET_SYSERR;
394   }
395   zone_key = (const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *) PQgetvalue (res, 0, 0);
396   name = PQgetvalue (res, 0, 1);
397   if ('\0' != name[PQgetlength (res, 0, 1)-1])
398   {
399     GNUNET_break (0);
400     PQclear (res);
401     return GNUNET_SYSERR;
402   }
403   record_count = ntohl (*(uint32_t *) PQgetvalue (res, 0, 2));
404   data_size = PQgetlength (res, 0, 3);
405   data = PQgetvalue (res, 0, 3);
406   expiration.abs_value =
407     GNUNET_ntohll (*(uint64_t *) PQgetvalue (res, 0, 4));
408   sig = (const struct GNUNET_CRYPTO_RsaSignature*) PQgetvalue (res, 0, 5);
409   if (record_count > 64 * 1024)
410   {
411     /* sanity check, don't stack allocate far too much just
412        because database might contain a large value here */
413     GNUNET_break (0);
414     PQclear (res);
415     return GNUNET_SYSERR;
416   }
417   {
418     struct GNUNET_NAMESTORE_RecordData rd[record_count];
419     
420     if (GNUNET_OK !=
421         GNUNET_NAMESTORE_records_deserialize (data_size, data,
422                                               record_count, rd))
423     {
424       GNUNET_break (0);
425       PQclear (res);
426       return GNUNET_SYSERR;
427     }
428     iter (iter_cls, zone_key, expiration, name, 
429           record_count, rd, sig);
430   }
431   PQclear (res);
432   return GNUNET_OK;
433 }
434   
435   
436 /**
437  * Iterate over the results for a particular key and zone in the
438  * datastore.  Will return at most one result to the iterator.
439  *
440  * @param cls closure (internal context for the plugin)
441  * @param zone hash of public key of the zone, NULL to iterate over all zones
442  * @param name name as string, NULL to iterate over all records of the zone
443  * @param offset offset in the list of all matching records
444  * @param iter function to call with the result
445  * @param iter_cls closure for iter
446  * @return GNUNET_OK on success, GNUNET_NO if there were no results, GNUNET_SYSERR on error
447  */
448 static int 
449 namestore_postgres_iterate_records (void *cls, 
450                                   const struct GNUNET_CRYPTO_ShortHashCode *zone,
451                                   const char *name,
452                                   uint64_t offset,
453                                   GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls)
454 {
455   struct Plugin *plugin = cls;
456   const char *stmt_name;
457   struct GNUNET_CRYPTO_ShortHashCode name_hase;
458   uint64_t offset_be = GNUNET_htonll (offset);
459   const char *paramValues[] = {
460     (const char *) zone,
461     (const char *) &name_hase,
462     (const char *) &offset_be,
463     (const char *) zone,
464     (const char *) &offset_be,
465   };
466   int paramLengths[] = {
467     sizeof (struct GNUNET_CRYPTO_ShortHashCode),
468     sizeof (struct GNUNET_CRYPTO_ShortHashCode),
469     sizeof (uint64_t),
470     sizeof (struct GNUNET_CRYPTO_ShortHashCode),
471     sizeof (uint64_t)
472   };
473   const int paramFormats[] = { 1, 1, 1, 1, 1 };
474   unsigned int num_params;
475   unsigned int first_param;
476   PGresult *res;
477
478   if (NULL == zone)
479     if (NULL == name)
480     {
481       stmt_name = "iterate_all";
482       num_params = 1;
483       first_param = 2;
484     }
485     else
486     {
487       GNUNET_CRYPTO_short_hash (name, strlen(name), &name_hase);
488       stmt_name = "iterate_by_name";
489       num_params = 2;
490       first_param = 1;
491     }
492   else
493     if (NULL == name)
494     {
495       stmt_name = "iterate_by_zone";
496       num_params = 2;
497       first_param = 3;
498     }
499     else
500     {
501       GNUNET_CRYPTO_short_hash (name, strlen(name), &name_hase);
502       stmt_name = "iterate_records";
503       num_params = 3;
504       first_param = 0;
505     }
506   res =
507       PQexecPrepared (plugin->dbh, stmt_name, num_params, 
508                       &paramValues[first_param],
509                       &paramLengths[first_param],
510                       &paramFormats[first_param], 1);
511   return get_record_and_call_iterator (plugin, stmt_name, res, iter, iter_cls);
512 }
513
514
515 /**
516  * Look for an existing PKEY delegation record for a given public key.
517  * Returns at most one result to the iterator.
518  *
519  * @param cls closure (internal context for the plugin)
520  * @param zone hash of public key of the zone to look up in, never NULL
521  * @param value_zone hash of the public key of the target zone (value), never NULL
522  * @param iter function to call with the result
523  * @param iter_cls closure for iter
524  * @return GNUNET_OK on success, GNUNET_NO if there were no results, GNUNET_SYSERR on error
525  */
526 static int
527 namestore_postgres_zone_to_name (void *cls, 
528                                const struct GNUNET_CRYPTO_ShortHashCode *zone,
529                                const struct GNUNET_CRYPTO_ShortHashCode *value_zone,
530                                GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls)
531 {
532   struct Plugin *plugin = cls;
533   const char *paramValues[] = {
534     (const char *) zone,
535     (const char *) value_zone
536   };
537   int paramLengths[] = {
538     sizeof (struct GNUNET_CRYPTO_ShortHashCode),
539     sizeof (struct GNUNET_CRYPTO_ShortHashCode)
540   };
541   const int paramFormats[] = { 1, 1 };
542   PGresult *res;
543
544   res =
545     PQexecPrepared (plugin->dbh, "zone_to_name", 2,
546                     paramValues, paramLengths, paramFormats, 1);
547   return get_record_and_call_iterator (plugin, "zone_to_name", res, iter, iter_cls);
548 }
549
550
551 /**
552  * Delete an entire zone (all records).  Not used in normal operation.
553  *
554  * @param cls closure (internal context for the plugin)
555  * @param zone zone to delete
556  */
557 static void 
558 namestore_postgres_delete_zone (void *cls,
559                               const struct GNUNET_CRYPTO_ShortHashCode *zone)
560 {
561   struct Plugin *plugin = cls;
562   PGresult *ret;
563   const char *paramValues[] = {
564     (const char *) zone,
565   };
566   int paramLengths[] = {
567     sizeof (struct GNUNET_CRYPTO_ShortHashCode)
568   };
569   const int paramFormats[] = { 1 };
570
571   ret =
572       PQexecPrepared (plugin->dbh, "delete_zone", 1, paramValues, paramLengths,
573                       paramFormats, 1);
574   if (GNUNET_OK !=
575       GNUNET_POSTGRES_check_result (plugin->dbh, ret, PGRES_COMMAND_OK, "PQexecPrepared", "delete_zone"))
576   {
577     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
578                 "Deleting zone failed!\n");             
579     return;
580   }
581   PQclear (ret);
582 }
583
584
585 /**
586  * Shutdown database connection and associate data
587  * structures.
588  *
589  * @param plugin the plugin context (state for this module)
590  */
591 static void
592 database_shutdown (struct Plugin *plugin)
593 {
594   PQfinish (plugin->dbh);
595   plugin->dbh = NULL;
596 }
597
598
599 /**
600  * Entry point for the plugin.
601  *
602  * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
603  * @return NULL on error, othrewise the plugin context
604  */
605 void *
606 libgnunet_plugin_namestore_postgres_init (void *cls)
607 {
608   static struct Plugin plugin;
609   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
610   struct GNUNET_NAMESTORE_PluginFunctions *api;
611
612   if (NULL != plugin.cfg)
613     return NULL;                /* can only initialize once! */
614   memset (&plugin, 0, sizeof (struct Plugin));
615   plugin.cfg = cfg;  
616   if (GNUNET_OK != database_setup (&plugin))
617   {
618     database_shutdown (&plugin);
619     return NULL;
620   }
621   api = GNUNET_malloc (sizeof (struct GNUNET_NAMESTORE_PluginFunctions));
622   api->cls = &plugin;
623   api->put_records = &namestore_postgres_put_records;
624   api->remove_records = &namestore_postgres_remove_records;
625   api->iterate_records = &namestore_postgres_iterate_records;
626   api->zone_to_name = &namestore_postgres_zone_to_name;
627   api->delete_zone = &namestore_postgres_delete_zone;
628   LOG (GNUNET_ERROR_TYPE_INFO, 
629        _("Postgres database running\n"));
630   return api;
631 }
632
633
634 /**
635  * Exit point from the plugin.
636  *
637  * @param cls the plugin context (as returned by "init")
638  * @return always NULL
639  */
640 void *
641 libgnunet_plugin_namestore_postgres_done (void *cls)
642 {
643   struct GNUNET_NAMESTORE_PluginFunctions *api = cls;
644   struct Plugin *plugin = api->cls;
645
646   database_shutdown (plugin);
647   plugin->cfg = NULL;
648   GNUNET_free (api);
649   LOG (GNUNET_ERROR_TYPE_DEBUG, 
650        "postgres plugin is finished\n");
651   return NULL;
652 }
653
654 /* end of plugin_namestore_postgres.c */