CFLAGS = $(CPPFLAGS) @CFLAGS@
LDFLAGS = @LDFLAGS@
EXEEXT = @EXEEXT@
+SHLD = @SHLD@
+SHLD_FLAGS = @SHLD_FLAGS@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PICFLAG = @PICFLAG@
+SHLIBEXT = @SHLIBEXT@
+SWIG = swig
.PHONY: test
DIRS = bin common tools
-all: showflags dirs $(PROGS)
+SONAME = libtdb.$(SHLIBEXT).1
+SOLIB = libtdb.$(SHLIBEXT).$(PACKAGE_VERSION)
+
+all: showflags dirs $(PROGS) $(SOLIB) libtdb.a
showflags:
@echo 'tdb will be compiled with flags:'
@echo ' LDFLAGS = $(LDFLAGS)'
@echo ' LIBS = $(LIBS)'
+.SUFFIXES: .c .o
+
.c.o:
@echo Compiling $*.c
@mkdir -p `dirname $@`
- @$(CC) $(CFLAGS) -c $< -o $@
+ @$(CC) $(PICFLAG) $(CFLAGS) -c $< -o $@
dirs:
@mkdir -p $(DIRS)
cp $(PROGS) $(DESTDIR)$(bindir)
cp $(srcdir)/include/tdb.h $(DESTDIR)$(includedir)
cp tdb.pc $(DESTDIR)$(libdir)/pkgconfig
+ cp libtdb.a $(SOLIB) $(DESTDIR)$(libdir)
libtdb.a: $(TDB_OBJ)
ar -rv libtdb.a $(TDB_OBJ)
-bin/tdbtest$(EXEEXT): tools/tdbtest.o libtdb.a
+libtdb.$(SHLIBEXT): $(SOLIB)
+ ln -fs $< $@
+
+$(SONAME): $(SOLIB)
+ ln -fs $< $@
+
+$(SOLIB): $(TDB_OBJ)
+ $(SHLD) $(SHLD_FLAGS) -o $@ $(TDB_OBJ) @SONAMEFLAG@$(SONAME)
+
+TDB_LIB = libtdb.a
+
+bin/tdbtest$(EXEEXT): tools/tdbtest.o $(TDB_LIB)
$(CC) $(CFLAGS) $(LDFLAGS) -o bin/tdbtest tools/tdbtest.o -L. -ltdb -lgdbm
-bin/tdbtool$(EXEEXT): tools/tdbtool.o libtdb.a
+bin/tdbtool$(EXEEXT): tools/tdbtool.o $(TDB_LIB)
$(CC) $(CFLAGS) $(LDFLAGS) -o bin/tdbtool tools/tdbtool.o -L. -ltdb
-bin/tdbtorture$(EXEEXT): tools/tdbtorture.o libtdb.a
+bin/tdbtorture$(EXEEXT): tools/tdbtorture.o $(TDB_LIB)
$(CC) $(CFLAGS) $(LDFLAGS) -o bin/tdbtorture tools/tdbtorture.o -L. -ltdb
-bin/tdbdump$(EXEEXT): tools/tdbdump.o libtdb.a
+bin/tdbdump$(EXEEXT): tools/tdbdump.o $(TDB_LIB)
$(CC) $(CFLAGS) $(LDFLAGS) -o bin/tdbdump tools/tdbdump.o -L. -ltdb
-bin/tdbbackup$(EXEEXT): tools/tdbbackup.o libtdb.a
+bin/tdbbackup$(EXEEXT): tools/tdbbackup.o $(TDB_LIB)
$(CC) $(CFLAGS) $(LDFLAGS) -o bin/tdbbackup tools/tdbbackup.o -L. -ltdb
test: bin/tdbtorture$(EXEEXT)
clean:
rm -f $(ALL_PROGS) *.o *.a common/*.o tools/*.o tdb.pc
rm -f test.db test.tdb torture.tdb test.gdbm
+ rm -f $(SONAME) $(SOLIB) libtdb.a libtdb.$(SHLIBEXT)
distclean: clean
rm -f *~ */*~
realdistclean: distclean
rm -f configure include/config.h.in
+
+tdb_wrap.c tdb.py: tdb.i
+ $(SWIG) -O -Wall -python -keyword tdb.i
+
+build-python: libtdb.$(SHLIBEXT) tdb_wrap.c tdb.py
+ ./setup.py build
+
+install-python:
+ ./setup.py install --prefix=$(prefix)
+
+check-python: build-python
+ # FIXME: Should be more portable:
+ LD_LIBRARY_PATH=. PYTHONPATH=.:build/lib.linux-i686-2.4 trial python/tests/simple.py
+
+install-swig:
+ mkdir -p $(DESTDIR)`$(SWIG) -swiglib`
+ cp tdb.i $(DESTDIR)`$(SWIG) -swiglib`
+
+clean-python:
+ ./setup.py clean
rm -rf autom4te.cache
+swig -O -Wall -python -keyword tdb.i # Ignore errors for now
+
echo "Now run ./configure and then make."
exit 0
if (tdb->map_ptr) {
memcpy(off + (char *)tdb->map_ptr, buf, len);
- } else if (pwrite(tdb->fd, buf, len, off) != (ssize_t)len) {
+ } else {
+ ssize_t written = pwrite(tdb->fd, buf, len, off);
+ if ((written != (ssize_t)len) && (written != -1)) {
+ /* try once more */
+ TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_write: wrote only "
+ "%d of %d bytes at %d, trying once more\n",
+ (int)written, len, off));
+ errno = ENOSPC;
+ written = pwrite(tdb->fd, (const void *)((const char *)buf+written),
+ len-written,
+ off+written);
+ }
+ if (written == -1) {
/* Ensure ecode is set for log fn. */
tdb->ecode = TDB_ERR_IO;
- TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_write failed at %d len=%d (%s)\n",
- off, len, strerror(errno)));
+ TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_write failed at %d "
+ "len=%d (%s)\n", off, len, strerror(errno)));
+ return TDB_ERRCODE(TDB_ERR_IO, -1);
+ } else if (written != (ssize_t)len) {
+ TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_write: failed to "
+ "write %d bytes at %d in two attempts\n",
+ len, off));
+ errno = ENOSPC;
return TDB_ERRCODE(TDB_ERR_IO, -1);
}
+ }
return 0;
}
if (ftruncate(tdb->fd, size+addition) == -1) {
char b = 0;
- if (pwrite(tdb->fd, &b, 1, (size+addition) - 1) != 1) {
+ ssize_t written = pwrite(tdb->fd, &b, 1, (size+addition) - 1);
+ if (written == 0) {
+ /* try once more, potentially revealing errno */
+ written = pwrite(tdb->fd, &b, 1, (size+addition) - 1);
+ }
+ if (written == 0) {
+ /* again - give up, guessing errno */
+ errno = ENOSPC;
+ }
+ if (written != 1) {
TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file to %d failed (%s)\n",
size+addition, strerror(errno)));
return -1;
disk. This must be done with write, not via mmap */
memset(buf, TDB_PAD_BYTE, sizeof(buf));
while (addition) {
- int n = addition>sizeof(buf)?sizeof(buf):addition;
- int ret = pwrite(tdb->fd, buf, n, size);
- if (ret != n) {
- TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write of %d failed (%s)\n",
- n, strerror(errno)));
+ size_t n = addition>sizeof(buf)?sizeof(buf):addition;
+ ssize_t written = pwrite(tdb->fd, buf, n, size);
+ if (written == 0) {
+ /* prevent infinite loops: try _once_ more */
+ written = pwrite(tdb->fd, buf, n, size);
+ }
+ if (written == 0) {
+ /* give up, trying to provide a useful errno */
+ TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write "
+ "returned 0 twice: giving up!\n"));
+ errno = ENOSPC;
+ return -1;
+ } else if (written == -1) {
+ TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write of "
+ "%d bytes failed (%s)\n", (int)n,
+ strerror(errno)));
return -1;
+ } else if (written != n) {
+ TDB_LOG((tdb, TDB_DEBUG_WARNING, "expand_file: wrote "
+ "only %d of %d bytes - retrying\n", (int)written,
+ (int)n));
}
- addition -= n;
- size += n;
+ addition -= written;
+ size += written;
}
return 0;
}
#define TDB_MARK_LOCK 0x80000000
+void tdb_setalarm_sigptr(struct tdb_context *tdb, volatile sig_atomic_t *ptr)
+{
+ tdb->interrupt_sig_ptr = ptr;
+}
+
/* a byte range locking function - return 0 on success
this functions locks/unlocks 1 byte at the specified offset.
do {
ret = fcntl(tdb->fd,lck_type,&fl);
+
+ /* Check for a sigalarm break. */
+ if (ret == -1 && errno == EINTR &&
+ tdb->interrupt_sig_ptr &&
+ *tdb->interrupt_sig_ptr) {
+ break;
+ }
} while (ret == -1 && errno == EINTR);
if (ret == -1) {
static int tdb_new_database(struct tdb_context *tdb, int hash_size)
{
struct tdb_header *newdb;
- int size, ret = -1;
+ size_t size;
+ int ret = -1;
+ ssize_t written;
/* We make it up in memory, then write it out if not internal */
size = sizeof(struct tdb_header) + (hash_size+1)*sizeof(tdb_off_t);
memcpy(&tdb->header, newdb, sizeof(tdb->header));
/* Don't endian-convert the magic food! */
memcpy(newdb->magic_food, TDB_MAGIC_FOOD, strlen(TDB_MAGIC_FOOD)+1);
- if (write(tdb->fd, newdb, size) != size) {
- ret = -1;
- } else {
+ /* we still have "ret == -1" here */
+ written = write(tdb->fd, newdb, size);
+ if (written == size) {
+ ret = 0;
+ } else if (written != -1) {
+ /* call write once again, this usually should return -1 and
+ * set errno appropriately */
+ size -= written;
+ written = write(tdb->fd, newdb+written, size);
+ if (written == size) {
ret = 0;
+ } else if (written >= 0) {
+ /* a second incomplete write - we give up.
+ * guessing the errno... */
+ errno = ENOSPC;
+ }
}
fail:
tdb->page_size = 0x2000;
}
+ if (open_flags & TDB_VOLATILE) {
+ tdb->max_dead_records = 5;
+ }
+
if ((open_flags & O_ACCMODE) == O_WRONLY) {
TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: can't open tdb %s write-only\n",
name));
}
}
+ errno = 0;
if (read(tdb->fd, &tdb->header, sizeof(tdb->header)) != sizeof(tdb->header)
|| strcmp(tdb->header.magic_food, TDB_MAGIC_FOOD) != 0
|| (tdb->header.version != TDB_VERSION
&& !(rev = (tdb->header.version==TDB_BYTEREV(TDB_VERSION))))) {
/* its not a valid database - possibly initialise it */
if (!(open_flags & O_CREAT) || tdb_new_database(tdb, hash_size) == -1) {
+ if (errno == 0) {
errno = EIO; /* ie bad format or something */
+ }
goto fail;
}
rev = (tdb->flags & TDB_CONVERT);
if (dbuf.dptr == NULL) {
dbuf.dptr = (unsigned char *)malloc(new_dbuf.dsize);
} else {
- dbuf.dptr = (unsigned char *)realloc(dbuf.dptr,
+ unsigned char *new_dptr = (unsigned char *)realloc(dbuf.dptr,
dbuf.dsize + new_dbuf.dsize);
+ if (new_dptr == NULL) {
+ free(dbuf.dptr);
+ }
+ dbuf.dptr = new_dptr;
}
if (dbuf.dptr == NULL) {
#include "system/time.h"
#include "system/shmem.h"
#include "system/select.h"
+#include "system/wait.h"
#include "tdb.h"
#ifndef HAVE_GETPAGESIZE
#define TDB_RECOVERY_HEAD offsetof(struct tdb_header, recovery_start)
#define TDB_SEQNUM_OFS offsetof(struct tdb_header, sequence_number)
#define TDB_PAD_BYTE 0x42
-#define TDB_PAD_UINT32_T 0x42424242
+#define TDB_PAD_U32 0x42424242
/* NB assumes there is a local variable called "tdb" that is the
* current context, also takes doubly-parenthesized print-style
int page_size;
int max_dead_records;
bool have_transaction_lock;
+ volatile sig_atomic_t *interrupt_sig_ptr;
};
tdb->travlocks.off = tdb->travlocks.hash = 0;
tdb->travlocks.lock_rw = F_RDLCK;
+ /* Grab first record: locks chain and returned record. */
if (tdb_next_lock(tdb, &tdb->travlocks, &rec) <= 0)
return tdb_null;
/* now read the key */
key.dsize = rec.key_len;
key.dptr =tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),key.dsize);
- if (tdb_unlock(tdb, BUCKET(tdb->travlocks.hash), F_WRLCK) != 0)
+
+ /* Unlock the hash chain of the record we just read. */
+ if (tdb_unlock(tdb, tdb->travlocks.hash, tdb->travlocks.lock_rw) != 0)
TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_firstkey: error occurred while tdb_unlocking!\n"));
return key;
}
/* Is locked key the old key? If so, traverse will be reliable. */
if (tdb->travlocks.off) {
- if (tdb_lock(tdb,tdb->travlocks.hash,F_WRLCK))
+ if (tdb_lock(tdb,tdb->travlocks.hash,tdb->travlocks.lock_rw))
return tdb_null;
if (tdb_rec_read(tdb, tdb->travlocks.off, &rec) == -1
|| !(k = tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),
SAFE_FREE(k);
return tdb_null;
}
- if (tdb_unlock(tdb, tdb->travlocks.hash, F_WRLCK) != 0) {
+ if (tdb_unlock(tdb, tdb->travlocks.hash, tdb->travlocks.lock_rw) != 0) {
SAFE_FREE(k);
return tdb_null;
}
if (!tdb->travlocks.off) {
/* No previous element: do normal find, and lock record */
- tdb->travlocks.off = tdb_find_lock_hash(tdb, oldkey, tdb->hash_fn(&oldkey), F_WRLCK, &rec);
+ tdb->travlocks.off = tdb_find_lock_hash(tdb, oldkey, tdb->hash_fn(&oldkey), tdb->travlocks.lock_rw, &rec);
if (!tdb->travlocks.off)
return tdb_null;
tdb->travlocks.hash = BUCKET(rec.full_hash);
key.dptr = tdb_alloc_read(tdb, tdb->travlocks.off+sizeof(rec),
key.dsize);
/* Unlock the chain of this new record */
- if (tdb_unlock(tdb, tdb->travlocks.hash, F_WRLCK) != 0)
+ if (tdb_unlock(tdb, tdb->travlocks.hash, tdb->travlocks.lock_rw) != 0)
TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_nextkey: WARNING tdb_unlock failed!\n"));
}
/* Unlock the chain of old record */
- if (tdb_unlock(tdb, BUCKET(oldhash), F_WRLCK) != 0)
+ if (tdb_unlock(tdb, BUCKET(oldhash), tdb->travlocks.lock_rw) != 0)
TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_nextkey: WARNING tdb_unlock failed!\n"));
return key;
}
LIBTDB
# End BINARY tdbbackup
################################################
-
-#######################
-# Start LIBRARY swig_tdb
-[LIBRARY::swig_tdb]
-LIBRARY_REALNAME = swig/_tdb.$(SHLIBEXT)
-OBJ_FILES = swig/tdb_wrap.o
-PUBLIC_DEPENDENCIES = LIBTDB DYNCONFIG
-# End LIBRARY swig_tdb
-#######################
AC_DEFUN([SMB_MODULE_DEFAULT], [echo -n ""])
AC_DEFUN([SMB_LIBRARY_ENABLE], [echo -n ""])
AC_DEFUN([SMB_ENABLE], [echo -n ""])
-AC_INIT(tdb, 1.1.0)
+AC_INIT(tdb, 1.1.1)
AC_CONFIG_SRCDIR([common/tdb.c])
AC_CONFIG_HEADER(include/config.h)
AC_LIBREPLACE_ALL_CHECKS
+AC_LD_SONAMEFLAG
+AC_LD_PICFLAG
+AC_LD_SHLIBEXT
+AC_LIBREPLACE_SHLD
+AC_LIBREPLACE_SHLD_FLAGS
m4_include(libtdb.m4)
AC_OUTPUT(Makefile tdb.pc)
#define TDB_BIGENDIAN 32 /* header is big-endian (internal use) */
#define TDB_NOSYNC 64 /* don't use synchronous transactions */
#define TDB_SEQNUM 128 /* maintain a sequence number */
+#define TDB_VOLATILE 256 /* Activate the per-hashchain freelist, default 5 */
#define TDB_ERRCODE(code, ret) ((tdb->ecode = (code)), ret)
int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key);
int tdb_chainlock_unmark(struct tdb_context *tdb, TDB_DATA key);
+void tdb_setalarm_sigptr(struct tdb_context *tdb, volatile sig_atomic_t *sigptr);
+
/* Debug functions. Not used in production. */
void tdb_dump_all(struct tdb_context *tdb);
int tdb_printfreelist(struct tdb_context *tdb);
AC_HAVE_DECL(pread, [#include <unistd.h>])
AC_HAVE_DECL(pwrite, [#include <unistd.h>])
-
-AC_MSG_CHECKING([for Python])
-
-PYTHON=
-
-AC_ARG_WITH(python,
-[ --with-python=PYTHONNAME build Python libraries],
-[ case "${withval-python}" in
- yes)
- PYTHON=python
- ;;
- no)
- PYTHON=
- ;;
- *)
- PYTHON=${withval-python}
- ;;
- esac ])
-
-if test x"$PYTHON" != "x"; then
- incdir=`python -c 'import sys; print "%s/include/python%d.%d" % (sys.prefix, sys.version_info[[0]], sys.version_info[[1]])'`
- CPPFLAGS="$CPPFLAGS -I $incdir"
-fi
-
-if test x"$PYTHON" != "x"; then
- AC_MSG_RESULT([${withval-python}])
-else
- SMB_ENABLE(swig_tdb, NO)
- AC_MSG_RESULT(no)
-fi
-
-AC_SUBST(PYTHON)
#include "system/locale.h"
#include "system/time.h"
#include "system/filesys.h"
+#include "system/wait.h"
#include "tdb.h"
#ifdef HAVE_GETOPT_H
TDB_CONTEXT *tdb_new = (TDB_CONTEXT *)state;
if (tdb_store(tdb_new, key, dbuf, TDB_INSERT) != 0) {
- fprintf(stderr,"Failed to insert into %s\n", tdb_name(tdb));
+ fprintf(stderr,"Failed to insert into %s\n", tdb_name(tdb_new));
failed = 1;
return 1;
}
}
/* traverse the new tdb to confirm */
- count2 = tdb_traverse(tdb_new, test_fn, 0);
+ count2 = tdb_traverse(tdb_new, test_fn, NULL);
if (count2 != count1) {
fprintf(stderr,"failed to copy %s\n", old_name);
tdb_close(tdb_new);
/* close the new tdb and rename it to .bak */
tdb_close(tdb_new);
- unlink(new_name);
if (rename(tmp_name, new_name) != 0) {
perror(new_name);
free(tmp_name);
#include "system/locale.h"
#include "system/time.h"
#include "system/filesys.h"
+#include "system/wait.h"
#include "tdb.h"
static void print_data(TDB_DATA d)
#include "system/locale.h"
#include "system/time.h"
#include "system/filesys.h"
+#include "system/wait.h"
#include "tdb.h"
static int do_command(void);
char *line;
TDB_DATA iterate_kbuf;
char cmdline[1024];
+static int disable_mmap;
enum commands {
CMD_CREATE_TDB,
CMD_LIST_HASH_FREE,
CMD_LIST_FREE,
CMD_INFO,
+ CMD_MMAP,
+ CMD_SPEED,
CMD_FIRST,
CMD_NEXT,
CMD_SYSTEM,
{"list", CMD_LIST_HASH_FREE},
{"free", CMD_LIST_FREE},
{"info", CMD_INFO},
+ {"speed", CMD_SPEED},
+ {"mmap", CMD_MMAP},
{"first", CMD_FIRST},
{"1", CMD_FIRST},
{"next", CMD_NEXT},
{NULL, CMD_HELP}
};
+struct timeval tp1,tp2;
+
+static void _start_timer(void)
+{
+ gettimeofday(&tp1,NULL);
+}
+
+static double _end_timer(void)
+{
+ gettimeofday(&tp2,NULL);
+ return((tp2.tv_sec - tp1.tv_sec) +
+ (tp2.tv_usec - tp1.tv_usec)*1.0e-6);
+}
+
/* a tdb tool for manipulating a tdb database */
static TDB_CONTEXT *tdb;
static void create_tdb(const char *tdbname)
{
if (tdb) tdb_close(tdb);
- tdb = tdb_open(tdbname, 0, TDB_CLEAR_IF_FIRST,
+ tdb = tdb_open(tdbname, 0, TDB_CLEAR_IF_FIRST | (disable_mmap?TDB_NOMMAP:0),
O_RDWR | O_CREAT | O_TRUNC, 0600);
if (!tdb) {
printf("Could not create %s: %s\n", tdbname, strerror(errno));
static void open_tdb(const char *tdbname)
{
if (tdb) tdb_close(tdb);
- tdb = tdb_open(tdbname, 0, 0, O_RDWR, 0600);
+ tdb = tdb_open(tdbname, 0, disable_mmap?TDB_NOMMAP:0, O_RDWR, 0600);
if (!tdb) {
printf("Could not open %s: %s\n", tdbname, strerror(errno));
}
printf("%d records totalling %d bytes\n", count, total_bytes);
}
+static void speed_tdb(const char *tlimit)
+{
+ unsigned timelimit = tlimit?atoi(tlimit):0;
+ double t;
+ int ops=0;
+ if (timelimit == 0) timelimit = 10;
+ printf("Testing traverse speed for %u seconds\n", timelimit);
+ _start_timer();
+ while ((t=_end_timer()) < timelimit) {
+ tdb_traverse(tdb, traverse_fn, NULL);
+ printf("%10.3f ops/sec\r", (++ops)/t);
+ }
+ printf("\n");
+}
+
+static void toggle_mmap(void)
+{
+ disable_mmap = !disable_mmap;
+ if (disable_mmap) {
+ printf("mmap is disabled\n");
+ } else {
+ printf("mmap is enabled\n");
+ }
+}
+
static char *tdb_getline(const char *prompt)
{
static char thisline[1024];
case CMD_INFO:
info_tdb();
return 0;
+ case CMD_SPEED:
+ speed_tdb(arg1);
+ return 0;
+ case CMD_MMAP:
+ toggle_mmap();
+ return 0;
case CMD_FIRST:
bIterate = 1;
first_record(tdb, &iterate_kbuf);
*/
#include "replace.h"
-#include "tdb.h"
#include "system/time.h"
#include "system/wait.h"
#include "system/filesys.h"
+#include "tdb.h"
#ifdef HAVE_GETOPT_H
#include <getopt.h>
unlink("torture.tdb");
- pids = calloc(sizeof(pid_t), num_procs);
+ pids = (pid_t *)calloc(sizeof(pid_t), num_procs);
pids[0] = getpid();
for (i=0;i<num_procs-1;i++) {