summaryrefslogtreecommitdiff
path: root/lib/yp_db.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/yp_db.c')
-rw-r--r--lib/yp_db.c532
1 files changed, 532 insertions, 0 deletions
diff --git a/lib/yp_db.c b/lib/yp_db.c
new file mode 100644
index 0000000..c98ec4a
--- /dev/null
+++ b/lib/yp_db.c
@@ -0,0 +1,532 @@
+/* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2009, 2011 Thorsten Kukuk
+ Author: Thorsten Kukuk <kukuk@suse.de>
+
+ The YP Server is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ version 2 as published by the Free Software Foundation.
+
+ The YP Server is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with the YP Server; see the file COPYING. If
+ not, write to the Free Software Foundation, Inc., 51 Franklin Street,
+ Suite 500, Boston, MA 02110-1335, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/param.h>
+
+#include "ypserv_conf.h"
+#include "log_msg.h"
+#include "yp_db.h"
+#include "yp.h"
+
+#if defined(HAVE_LIBGDBM)
+#include <gdbm.h>
+#elif defined(HAVE_LIBQDBM)
+#include <hovel.h>
+#elif defined(HAVE_NDBM)
+#include <ndbm.h>
+#elif defined(HAVE_LIBTC)
+#include <tcbdb.h>
+#endif
+
+#if defined(HAVE_COMPAT_LIBGDBM)
+
+/* Open a GDBM database */
+static GDBM_FILE
+_db_open (const char *domain, const char *map)
+{
+ GDBM_FILE dbp;
+ char buf[MAXPATHLEN + 2];
+
+ if (strlen (domain) + strlen (map) < MAXPATHLEN)
+ {
+ sprintf (buf, "%s/%s", domain, map);
+
+ dbp = gdbm_open (buf, 0, GDBM_READER, 0, NULL);
+
+ if (debug_flag && dbp == NULL)
+ log_msg ("gdbm_open: GDBM Error Code #%d", gdbm_errno);
+ else if (debug_flag)
+ log_msg ("\t\t->Returning OK!");
+ }
+ else
+ {
+ dbp = NULL;
+ log_msg ("Path to long: %s/%s", domain, map);
+ }
+
+ return dbp;
+}
+
+static inline int
+_db_close (GDBM_FILE file)
+{
+ gdbm_close (file);
+ return 0;
+}
+
+#elif defined(HAVE_NDBM)
+
+/*****************************************************
+ The following stuff is for NDBM suport !
+******************************************************/
+
+/* Open a NDBM database */
+static DB_FILE
+_db_open (const char *domain, const char *map)
+{
+ DB_FILE dbp;
+ char buf[MAXPATHLEN + 2];
+
+ if (strlen (domain) + strlen (map) < MAXPATHLEN)
+ {
+ sprintf (buf, "%s/%s", domain, map);
+
+ dbp = dbm_open (buf, O_RDONLY, 0600);
+
+ if (debug_flag && dbp == NULL)
+ log_msg ("dbm_open: NDBM Error Code #%d", errno);
+ else if (debug_flag)
+ log_msg ("\t\t->Returning OK!");
+ }
+ else
+ {
+ dbp = NULL;
+ log_msg ("Path to long: %s/%s", domain, map);
+ }
+
+ return dbp;
+}
+
+static inline int
+_db_close (DB_FILE file)
+{
+ dbm_close (file);
+ return 0;
+}
+
+int
+ypdb_exists (DB_FILE dbp, datum key)
+{
+ datum tmp = dbm_fetch (dbp, key);
+
+ if (tmp.dptr != NULL)
+ return 1;
+ else
+ return 0;
+}
+
+datum
+ypdb_nextkey (DB_FILE file, datum key)
+{
+ datum tkey;
+
+ tkey = dbm_firstkey (file);
+ while ((key.dsize != tkey.dsize) ||
+ (strncmp (key.dptr, tkey.dptr, tkey.dsize) != 0))
+ {
+ tkey = dbm_nextkey (file);
+ if (tkey.dptr == NULL)
+ return tkey;
+ }
+ tkey = dbm_nextkey (file);
+
+ return tkey;
+}
+
+#elif defined(HAVE_LIBTC)
+
+/*****************************************************
+ The following stuff is for Tokyo Cabinet suport !
+******************************************************/
+
+/* Open a Tokyo Cabinet B+ Tree database */
+static DB_FILE
+_db_open (const char *domain, const char *map)
+{
+ DB_FILE dbp;
+ char buf[MAXPATHLEN + 2];
+ int isok;
+
+ if (strlen (domain) + strlen (map) < MAXPATHLEN)
+ {
+ sprintf (buf, "%s/%s", domain, map);
+
+ dbp = tcbdbnew ();
+ isok = tcbdbopen (dbp, buf, BDBOREADER | BDBONOLCK);
+
+ if (debug_flag && !isok)
+ {
+ log_msg ("tcbdbopen: Tokyo Cabinet Error: %s",
+ tcbdberrmsg (tcbdbecode (dbp)));
+ log_msg ("tcbdbopen: consider rebuilding maps using ypinit");
+ }
+ else if (debug_flag)
+ log_msg ("\t\t->Returning OK!");
+ if ( !isok )
+ {
+ /* DB not successful opened. Close database object and set return value to NULL. */
+ tcbdbdel (dbp);
+ dbp = NULL;
+ }
+ }
+ else
+ {
+ dbp = NULL;
+ log_msg ("Path too long: %s/%s", domain, map);
+ }
+
+ return dbp;
+}
+
+static inline int
+_db_close (DB_FILE dbp)
+{
+ tcbdbclose (dbp);
+ tcbdbdel (dbp);
+ dbp = NULL;
+ return 0;
+}
+
+datum
+ypdb_firstkey (DB_FILE dbp)
+{
+ datum tkey;
+ BDBCUR *cur;
+
+ /* In case of error, we return original key */
+ if (!(cur = tcbdbcurnew (dbp)) || !tcbdbcurfirst (cur)
+ || (tkey.dptr = tcbdbcurkey (cur, &tkey.dsize)) == NULL)
+ {
+ tkey.dptr = NULL;
+ tkey.dsize = 0;
+ }
+
+ if (cur)
+ tcbdbcurdel (cur);
+
+ return tkey;
+}
+
+int
+ypdb_exists (DB_FILE dbp, datum key)
+{
+ return tcbdbvnum (dbp, key.dptr, key.dsize) > 0;
+}
+
+datum
+ypdb_nextkey (DB_FILE dbp, datum key)
+{
+ datum tkey;
+ BDBCUR *cur;
+
+ tkey.dptr = NULL;
+ tkey.dsize = 0;
+
+ /* In case of error, we return empty key */
+ if (!(cur = tcbdbcurnew (dbp)))
+ return tkey;
+
+ /* We try to jump to key and get next, or we move to the first */
+ if (tcbdbcurjump (cur, key.dptr, key.dsize) && tcbdbcurnext (cur))
+ {
+ if ((tkey.dptr = tcbdbcurkey (cur, &tkey.dsize)) == NULL)
+ tkey.dsize = 0;
+ }
+
+ tcbdbcurdel (cur);
+ return tkey;
+}
+
+datum
+ypdb_fetch (DB_FILE bdb, datum key)
+{
+ datum res;
+
+ if ((res.dptr = tcbdbget (bdb, key.dptr, key.dsize, &res.dsize)) == NULL)
+ res.dsize = 0;
+
+ return res;
+}
+
+#else
+
+#error "No database found or selected!"
+
+#endif
+
+typedef struct _fopen
+{
+ char *domain;
+ char *map;
+ DB_FILE dbp;
+ int flag;
+}
+Fopen, *FopenP;
+
+#define F_OPEN_FLAG 1
+#define F_MUST_CLOSE 2
+
+static int fast_open_init = -1;
+static Fopen fast_open_files[255];
+
+int
+ypdb_close_all (void)
+{
+ int i;
+
+ if (debug_flag)
+ log_msg ("ypdb_close_all() called");
+
+ if (fast_open_init == -1)
+ return 0;
+
+ for (i = 0; i < cached_filehandles; i++)
+ {
+ if (fast_open_files[i].dbp != NULL)
+ {
+ if (fast_open_files[i].flag & F_OPEN_FLAG)
+ {
+ if (debug_flag)
+ log_msg ("ypdb_close_all (%s/%s|%d) MARKED_TO_BE_CLOSE",
+ fast_open_files[i].domain,
+ fast_open_files[i].map, i);
+ fast_open_files[i].flag |= F_MUST_CLOSE;
+ }
+ else
+ {
+ if (debug_flag)
+ log_msg ("ypdb_close_all (%s/%s|%d)",
+ fast_open_files[i].domain,
+ fast_open_files[i].map, i);
+ free (fast_open_files[i].domain);
+ fast_open_files[i].domain = NULL;
+ free (fast_open_files[i].map);
+ fast_open_files[i].map = NULL;
+ _db_close (fast_open_files[i].dbp);
+ fast_open_files[i].dbp = NULL;
+ fast_open_files[i].flag = 0;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int
+ypdb_close (DB_FILE file)
+{
+ if (debug_flag)
+ log_msg ("ypdb_close() called");
+
+ if (cached_filehandles > 0)
+ {
+ if (fast_open_init != -1)
+ {
+ int i;
+
+ for (i = 0; i < cached_filehandles; ++i)
+ {
+ if (fast_open_files[i].dbp == file)
+ {
+ if (fast_open_files[i].flag & F_MUST_CLOSE)
+ {
+ if (debug_flag)
+ log_msg ("ypdb_MUST_close (%s/%s|%d)",
+ fast_open_files[i].domain,
+ fast_open_files[i].map, i);
+ free (fast_open_files[i].domain);
+ fast_open_files[i].domain = NULL;
+ free (fast_open_files[i].map);
+ fast_open_files[i].map = NULL;
+ _db_close (fast_open_files[i].dbp);
+ fast_open_files[i].dbp = NULL;
+ fast_open_files[i].flag = 0;
+ }
+ else
+ {
+ fast_open_files[i].flag &= ~F_OPEN_FLAG;
+ }
+ return 0;
+ }
+ }
+ }
+ log_msg ("ERROR: Could not close file!");
+ return 1;
+ }
+ else
+ {
+ _db_close (file);
+ return 0;
+ }
+}
+
+DB_FILE
+ypdb_open (const char *domain, const char *map)
+{
+ int i;
+
+ if (debug_flag)
+ log_msg ("\typdb_open(\"%s\", \"%s\")", domain, map);
+
+ if (map[0] == '.' || strchr (map, '/'))
+ {
+ if (debug_flag)
+ log_msg ("\t\t->Returning 0");
+ return NULL;
+ }
+
+ if (cached_filehandles > 0)
+ {
+ /* First call, initialize the fast_open_init struct */
+ if (fast_open_init == -1)
+ {
+ fast_open_init = 0;
+ for (i = 0; i < cached_filehandles; i++)
+ {
+ fast_open_files[i].domain =
+ fast_open_files[i].map = NULL;
+ fast_open_files[i].dbp = (DB_FILE) NULL;
+ fast_open_files[i].flag = 0;
+ }
+ }
+
+ /* Search if we have already open the domain/map file */
+ for (i = 0; i < cached_filehandles; i++)
+ {
+ if (fast_open_files[i].dbp != NULL)
+ {
+ if ((strcmp (domain, fast_open_files[i].domain) == 0) &&
+ (strcmp (map, fast_open_files[i].map) == 0))
+ {
+ /* The file is open and we know the file handle */
+ if (debug_flag)
+ log_msg ("Found: %s/%s (%d)", fast_open_files[i].domain,
+ fast_open_files[i].map, i);
+
+ if (fast_open_files[i].flag & F_OPEN_FLAG)
+ {
+ /* The file is already in use, don't open it twice.
+ I think this could never happen. */
+ log_msg ("\t%s/%s already open.", domain, map);
+ return NULL;
+ }
+ else
+ {
+ /* Mark the file as open */
+ fast_open_files[i].flag |= F_OPEN_FLAG;
+ return fast_open_files[i].dbp;
+ }
+ }
+ }
+ }
+
+ /* Search for free entry. If we do not found one, close the LRU */
+ for (i = 0; i < cached_filehandles; i++)
+ {
+#if 0
+ /* Bad Idea. If one of them is NULL, we will get a seg.fault
+ I think it will only work with Linux libc 5.x */
+ log_msg ("Opening: %s/%s (%d) %x",
+ fast_open_files[i].domain,
+ fast_open_files[i].map,
+ i, fast_open_files[i].dbp);
+#endif
+ if (fast_open_files[i].dbp == NULL)
+ {
+ /* Good, we have a free entry and don't need to close a map */
+ int j;
+ Fopen tmp;
+
+ if ((fast_open_files[i].dbp = _db_open (domain, map)) == NULL)
+ return NULL;
+ /* since .dbp is NULL, .domain must be NULL, too */
+ assert (fast_open_files[i].domain == NULL);
+ fast_open_files[i].domain = strdup (domain);
+ /* since .dbp is NULL, .map must be NULL, too */
+ assert (fast_open_files[i].map == NULL);
+ fast_open_files[i].map = strdup (map);
+ fast_open_files[i].flag |= F_OPEN_FLAG;
+
+ if (debug_flag)
+ log_msg ("Opening: %s/%s (%d) %x", domain, map, i,
+ fast_open_files[i].dbp);
+
+ /* LRU: put this entry at the first position, move all the other
+ one back */
+ tmp = fast_open_files[i];
+ for (j = i; j >= 1; --j)
+ fast_open_files[j] = fast_open_files[j-1];
+
+ fast_open_files[0] = tmp;
+ return fast_open_files[0].dbp;
+ }
+ }
+
+ /* The badest thing, which could happen: no free cache entrys.
+ Search the last entry, which isn't in use. */
+ for (i = (cached_filehandles - 1); i > 0; --i)
+ if ((fast_open_files[i].flag & F_OPEN_FLAG) != F_OPEN_FLAG)
+ {
+ int j;
+ Fopen tmp;
+ DB_FILE dbp;
+
+ /* Check, if we can open the file. Else there is no reason
+ to close a cached handle. */
+ if ((dbp = _db_open (domain, map)) == NULL)
+ return NULL;
+
+ if (debug_flag)
+ {
+ log_msg ("Closing %s/%s (%d)",
+ fast_open_files[i].domain,
+ fast_open_files[i].map, i);
+ log_msg ("Opening: %s/%s (%d)", domain, map, i);
+ }
+ free (fast_open_files[i].domain);
+ free (fast_open_files[i].map);
+ _db_close (fast_open_files[i].dbp);
+
+ fast_open_files[i].domain = strdup (domain);
+ fast_open_files[i].map = strdup (map);
+ fast_open_files[i].flag = F_OPEN_FLAG;
+ fast_open_files[i].dbp = dbp;
+
+ /* LRU: Move the new entry to the first positon */
+ tmp = fast_open_files[i];
+ for (j = i; j >= 1; --j)
+ fast_open_files[j] = fast_open_files[j-1];
+
+ fast_open_files[j] = tmp;
+ return fast_open_files[j].dbp;
+ }
+
+ log_msg ("ERROR: Couldn't find a free cache entry!");
+
+ for (i = 0; i < cached_filehandles; i++)
+ {
+ log_msg ("Open files: %s/%s (%d) %x (%x)",
+ fast_open_files[i].domain,
+ fast_open_files[i].map,
+ i, fast_open_files[i].dbp,
+ fast_open_files[i].flag);
+ }
+ return NULL;
+ }
+ else
+ return _db_open (domain, map);
+}