1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-25 06:04:04 +03:00

merge from tridge

(This used to be ctdb commit 1000ea33025b625e23e7065f95efad447c380c8c)
This commit is contained in:
Ronnie sahlberg 2007-04-17 09:37:13 +10:00
commit c6fe6d592b
21 changed files with 983 additions and 367 deletions

View File

@ -21,7 +21,7 @@ LIB_FLAGS=@LDFLAGS@ -Llib @LIBS@ -lpopt @INFINIBAND_LIBS@
EVENTS_OBJ = lib/events/events.o lib/events/events_standard.o
CTDB_COMMON_OBJ = common/ctdb.o common/ctdb_daemon.o common/ctdb_client.o common/ctdb_io.o common/util.o common/ctdb_util.o \
common/ctdb_call.o common/ctdb_ltdb.o common/ctdb_message.o \
common/ctdb_call.o common/ctdb_ltdb.o common/ctdb_lockwait.o common/ctdb_message.o \
lib/util/idtree.o lib/util/db_wrap.o
CTDB_TCP_OBJ = tcp/tcp_connect.o tcp/tcp_io.o tcp/tcp_init.o

View File

@ -190,7 +190,7 @@ uint32_t ctdb_get_num_nodes(struct ctdb_context *ctdb)
/*
called by the transport layer when a packet comes in
*/
static void ctdb_recv_pkt(struct ctdb_context *ctdb, uint8_t *data, uint32_t length)
void ctdb_recv_pkt(struct ctdb_context *ctdb, uint8_t *data, uint32_t length)
{
struct ctdb_req_header *hdr;

118
ctdb/common/ctdb_lockwait.c Normal file
View File

@ -0,0 +1,118 @@
/*
wait for a tdb chain lock
Copyright (C) Andrew Tridgell 2006
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "includes.h"
#include "lib/events/events.h"
#include "system/filesys.h"
#include "system/wait.h"
#include "popt.h"
#include "../include/ctdb_private.h"
#include "db_wrap.h"
#include "lib/tdb/include/tdb.h"
struct lockwait_handle {
struct fd_event *fde;
int fd[2];
pid_t child;
void *private_data;
void (*callback)(void *);
};
static void lockwait_handler(struct event_context *ev, struct fd_event *fde,
uint16_t flags, void *private_data)
{
struct lockwait_handle *h = talloc_get_type(private_data,
struct lockwait_handle);
void (*callback)(void *) = h->callback;
void *p = h->private_data;
pid_t child = h->child;
talloc_set_destructor(h, NULL);
close(h->fd[0]);
talloc_free(h);
callback(p);
waitpid(child, NULL, 0);
}
static int lockwait_destructor(struct lockwait_handle *h)
{
close(h->fd[0]);
kill(h->child, SIGKILL);
waitpid(h->child, NULL, 0);
return 0;
}
/*
setup a non-blocking chainlock on a tdb record. If this function
returns NULL then it could not get the chainlock. Otherwise it
returns a opaque handle, and will call callback() once it has
managed to get the chainlock. You can cancel it by using talloc_free
on the returned handle.
It is the callers responsibility to unlock the chainlock once
acquired
*/
struct lockwait_handle *ctdb_lockwait(struct ctdb_db_context *ctdb_db,
TDB_DATA key,
void (*callback)(void *), void *private_data)
{
struct lockwait_handle *h;
int ret;
h = talloc_zero(ctdb_db, struct lockwait_handle);
if (h == NULL) {
return NULL;
}
ret = pipe(h->fd);
if (ret != 0) {
talloc_free(h);
return NULL;
}
h->child = fork();
if (h->child == (pid_t)-1) {
close(h->fd[0]);
close(h->fd[1]);
talloc_free(h);
return NULL;
}
h->callback = callback;
h->private_data = private_data;
if (h->child == 0) {
struct tdb_context *tdb = ctdb_db->ltdb->tdb;
/* in child */
tdb_chainlock(tdb, key);
_exit(0);
}
close(h->fd[1]);
talloc_set_destructor(h, lockwait_destructor);
h->fde = event_add_fd(ctdb_db->ctdb->ev, h, h->fd[0], EVENT_FD_READ, lockwait_handler, h);
if (h->fde == NULL) {
talloc_free(h);
return NULL;
}
return h;
}

View File

@ -215,3 +215,64 @@ int ctdb_ltdb_unlock(struct ctdb_db_context *ctdb_db, TDB_DATA key)
return tdb_chainunlock(ctdb_db->ltdb->tdb, key);
}
/*
called when we should retry the operation
*/
static void lock_fetch_callback(void *p)
{
struct ctdb_req_header *hdr = p;
struct ctdb_context *ctdb = talloc_find_parent_bytype(p, struct ctdb_context);
ctdb_recv_pkt(ctdb, (uint8_t *)hdr, hdr->length);
printf("PACKET REQUEUED\n");
}
/*
do a non-blocking ltdb_fetch with a locked record, deferring this
ctdb request until we have the chainlock
It does the following:
1) tries to get the chainlock. If it succeeds, then it fetches the record, and
returns 0
2) if it fails to get a chainlock immediately then it sets up a
non-blocking chainlock via ctdb_lockwait, and when it gets the
chainlock it re-submits this ctdb request to the main packet
receive function
This effectively queues all ctdb requests that cannot be
immediately satisfied until it can get the lock. This means that
the main ctdb daemon will not block waiting for a chainlock held by
a client
*/
int ctdb_ltdb_lock_fetch_requeue(struct ctdb_db_context *ctdb_db,
TDB_DATA key, struct ctdb_ltdb_header *header,
struct ctdb_req_header *hdr, TDB_DATA *data)
{
int ret;
struct tdb_context *tdb = ctdb_db->ltdb->tdb;
struct lockwait_handle *h;
ret = tdb_chainlock_nonblock(tdb, key);
/* first the non-contended path */
if (ret == 0) {
ret = ctdb_ltdb_fetch(ctdb_db, key, header, hdr, data);
if (ret != 0) {
tdb_chainunlock(tdb, key);
}
return ret;
}
/* now the contended path */
h = ctdb_lockwait(ctdb_db, key, lock_fetch_callback, hdr);
if (h == NULL) {
tdb_chainunlock(tdb, key);
return -1;
}
/* we get an extra reference to the packet here, to
stop it being freed in the top level packet handler */
(void)talloc_reference(ctdb_db, hdr);
return 0;
}

View File

@ -353,6 +353,10 @@ int ctdb_ltdb_fetch(struct ctdb_db_context *ctdb_db,
int ctdb_ltdb_store(struct ctdb_db_context *ctdb_db, TDB_DATA key,
struct ctdb_ltdb_header *header, TDB_DATA data);
void ctdb_queue_packet(struct ctdb_context *ctdb, struct ctdb_req_header *hdr);
int ctdb_ltdb_lock_fetch_requeue(struct ctdb_db_context *ctdb_db,
TDB_DATA key, struct ctdb_ltdb_header *header,
struct ctdb_req_header *hdr, TDB_DATA *data);
void ctdb_recv_pkt(struct ctdb_context *ctdb, uint8_t *data, uint32_t length);
struct ctdb_call_state *ctdb_call_local_send(struct ctdb_db_context *ctdb_db,
struct ctdb_call *call,
@ -449,4 +453,8 @@ struct ctdb_record_handle *ctdb_client_fetch_lock(struct ctdb_db_context *ctdb_d
*/
int ctdb_client_store_unlock(struct ctdb_record_handle *rec, TDB_DATA data);
struct lockwait_handle *ctdb_lockwait(struct ctdb_db_context *ctdb_db,
TDB_DATA key,
void (*callback)(void *), void *private_data);
#endif

View File

@ -3,7 +3,7 @@
rm -rf autom4te.cache
rm -f configure config.h.in
IPATHS="-I libreplace -I lib/replace -I ../libreplace -I ../replace"
IPATHS="-I libreplace -I lib/replace -I ../libreplace -I ../replace -I ../lib/replace"
autoconf $IPATHS || exit 1
autoheader $IPATHS || exit 1

View File

@ -28,7 +28,8 @@
#include "tdb_private.h"
static tdb_off_t tdb_dump_record(struct tdb_context *tdb, tdb_off_t offset)
static tdb_off_t tdb_dump_record(struct tdb_context *tdb, int hash,
tdb_off_t offset)
{
struct list_struct rec;
tdb_off_t tailer_ofs, tailer;
@ -39,8 +40,10 @@ static tdb_off_t tdb_dump_record(struct tdb_context *tdb, tdb_off_t offset)
return 0;
}
printf(" rec: offset=0x%08x next=0x%08x rec_len=%d key_len=%d data_len=%d full_hash=0x%x magic=0x%x\n",
offset, rec.next, rec.rec_len, rec.key_len, rec.data_len, rec.full_hash, rec.magic);
printf(" rec: hash=%d offset=0x%08x next=0x%08x rec_len=%d "
"key_len=%d data_len=%d full_hash=0x%x magic=0x%x\n",
hash, offset, rec.next, rec.rec_len, rec.key_len, rec.data_len,
rec.full_hash, rec.magic);
tailer_ofs = offset + sizeof(rec) + rec.rec_len - sizeof(tdb_off_t);
@ -72,7 +75,7 @@ static int tdb_dump_chain(struct tdb_context *tdb, int i)
printf("hash=%d\n", i);
while (rec_ptr) {
rec_ptr = tdb_dump_record(tdb, rec_ptr);
rec_ptr = tdb_dump_record(tdb, i, rec_ptr);
}
return tdb_unlock(tdb, i, F_WRLCK);

View File

@ -39,7 +39,7 @@ static int seen_insert(struct tdb_context *mem_tdb, tdb_off_t rec_ptr)
TDB_DATA key, data;
memset(&data, '\0', sizeof(data));
key.dptr = (char *)&rec_ptr;
key.dptr = (unsigned char *)&rec_ptr;
key.dsize = sizeof(rec_ptr);
return tdb_store(mem_tdb, key, data, TDB_INSERT);
}

View File

@ -124,8 +124,10 @@ static int tdb_read(struct tdb_context *tdb, tdb_off_t off, void *buf,
if (ret != (ssize_t)len) {
/* Ensure ecode is set for log fn. */
tdb->ecode = TDB_ERR_IO;
TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_read failed at %d len=%d ret=%d (%s) map_size=%d\n",
off, len, ret, strerror(errno), (int)tdb->map_size));
TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_read failed at %d "
"len=%d ret=%d (%s) map_size=%d\n",
(int)off, (int)len, (int)ret, strerror(errno),
(int)tdb->map_size));
return TDB_ERRCODE(TDB_ERR_IO, -1);
}
}
@ -339,7 +341,7 @@ unsigned char *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len
len = 1;
}
if (!(buf = malloc(len))) {
if (!(buf = (unsigned char *)malloc(len))) {
/* Ensure ecode is set for log fn. */
tdb->ecode = TDB_ERR_OOM;
TDB_LOG((tdb, TDB_DEBUG_ERROR,"tdb_alloc_read malloc failed len=%d (%s)\n",
@ -353,6 +355,40 @@ unsigned char *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len
return buf;
}
/* Give a piece of tdb data to a parser */
int tdb_parse_data(struct tdb_context *tdb, TDB_DATA key,
tdb_off_t offset, tdb_len_t len,
int (*parser)(TDB_DATA key, TDB_DATA data,
void *private_data),
void *private_data)
{
TDB_DATA data;
int result;
data.dsize = len;
if ((tdb->transaction == NULL) && (tdb->map_ptr != NULL)) {
/*
* Optimize by avoiding the malloc/memcpy/free, point the
* parser directly at the mmap area.
*/
if (tdb->methods->tdb_oob(tdb, offset+len, 0) != 0) {
return -1;
}
data.dptr = offset + (unsigned char *)tdb->map_ptr;
return parser(key, data, private_data);
}
if (!(data.dptr = tdb_alloc_read(tdb, offset, len))) {
return -1;
}
result = parser(key, data, private_data);
free(data.dptr);
return result;
}
/* read/write a record */
int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec)
{

View File

@ -105,8 +105,11 @@ int tdb_brlock_upgrade(struct tdb_context *tdb, tdb_off_t offset, size_t len)
/* lock a list in the database. list -1 is the alloc list */
int tdb_lock(struct tdb_context *tdb, int list, int ltype)
static int _tdb_lock(struct tdb_context *tdb, int list, int ltype, int op)
{
struct tdb_lock_type *new_lck;
int i;
/* a global lock allows us to avoid per chain locks */
if (tdb->global_lock.count &&
(ltype == tdb->global_lock.ltype || ltype == F_RDLCK)) {
@ -125,27 +128,78 @@ int tdb_lock(struct tdb_context *tdb, int list, int ltype)
if (tdb->flags & TDB_NOLOCK)
return 0;
for (i=0; i<tdb->num_lockrecs; i++) {
if (tdb->lockrecs[i].list == list) {
if (tdb->lockrecs[i].count == 0) {
/*
* Can't happen, see tdb_unlock(). It should
* be an assert.
*/
TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock: "
"lck->count == 0 for list %d", list));
}
/*
* Just increment the in-memory struct, posix locks
* don't stack.
*/
tdb->lockrecs[i].count++;
return 0;
}
}
new_lck = (struct tdb_lock_type *)realloc(
tdb->lockrecs,
sizeof(*tdb->lockrecs) * (tdb->num_lockrecs+1));
if (new_lck == NULL) {
errno = ENOMEM;
return -1;
}
tdb->lockrecs = new_lck;
/* Since fcntl locks don't nest, we do a lock for the first one,
and simply bump the count for future ones */
if (tdb->locked[list+1].count == 0) {
if (tdb->methods->tdb_brlock(tdb,FREELIST_TOP+4*list,ltype,F_SETLKW, 0, 1)) {
TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock failed on list %d ltype=%d (%s)\n",
list, ltype, strerror(errno)));
return -1;
}
tdb->locked[list+1].ltype = ltype;
tdb->num_locks++;
if (tdb->methods->tdb_brlock(tdb,FREELIST_TOP+4*list,ltype, op,
0, 1)) {
return -1;
}
tdb->locked[list+1].count++;
tdb->num_locks++;
tdb->lockrecs[tdb->num_lockrecs].list = list;
tdb->lockrecs[tdb->num_lockrecs].count = 1;
tdb->lockrecs[tdb->num_lockrecs].ltype = ltype;
tdb->num_lockrecs += 1;
return 0;
}
/* lock a list in the database. list -1 is the alloc list */
int tdb_lock(struct tdb_context *tdb, int list, int ltype)
{
int ret;
ret = _tdb_lock(tdb, list, ltype, F_SETLKW);
if (ret) {
TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock failed on list %d "
"ltype=%d (%s)\n", list, ltype, strerror(errno)));
}
return ret;
}
/* lock a list in the database. list -1 is the alloc list. non-blocking lock */
int tdb_lock_nonblock(struct tdb_context *tdb, int list, int ltype)
{
return _tdb_lock(tdb, list, ltype, F_SETLK);
}
/* unlock the database: returns void because it's too late for errors. */
/* changed to return int it may be interesting to know there
has been an error --simo */
int tdb_unlock(struct tdb_context *tdb, int list, int ltype)
{
int ret = -1;
int i;
struct tdb_lock_type *lck = NULL;
/* a global lock allows us to avoid per chain locks */
if (tdb->global_lock.count &&
@ -166,19 +220,52 @@ int tdb_unlock(struct tdb_context *tdb, int list, int ltype)
return ret;
}
if (tdb->locked[list+1].count==0) {
TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: count is 0\n"));
return ret;
for (i=0; i<tdb->num_lockrecs; i++) {
if (tdb->lockrecs[i].list == list) {
lck = &tdb->lockrecs[i];
break;
}
}
if (tdb->locked[list+1].count == 1) {
/* Down to last nested lock: unlock underneath */
ret = tdb->methods->tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK, F_SETLKW, 0, 1);
tdb->num_locks--;
} else {
ret = 0;
if ((lck == NULL) || (lck->count == 0)) {
TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: count is 0\n"));
return -1;
}
if (lck->count > 1) {
lck->count--;
return 0;
}
/*
* This lock has count==1 left, so we need to unlock it in the
* kernel. We don't bother with decrementing the in-memory array
* element, we're about to overwrite it with the last array element
* anyway.
*/
ret = tdb->methods->tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK,
F_SETLKW, 0, 1);
tdb->num_locks--;
/*
* Shrink the array by overwriting the element just unlocked with the
* last array element.
*/
if (tdb->num_lockrecs > 1) {
*lck = tdb->lockrecs[tdb->num_lockrecs-1];
}
tdb->num_lockrecs -= 1;
/*
* We don't bother with realloc when the array shrinks, but if we have
* a completely idle tdb we should get rid of the locked array.
*/
if (tdb->num_lockrecs == 0) {
SAFE_FREE(tdb->lockrecs);
}
tdb->locked[list+1].count--;
if (ret)
TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: An error occurred unlocking!\n"));
@ -281,6 +368,14 @@ int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key)
return tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);
}
/* lock/unlock one hash chain, non-blocking. This is meant to be used
to reduce contention - it cannot guarantee how many records will be
locked */
int tdb_chainlock_nonblock(struct tdb_context *tdb, TDB_DATA key)
{
return tdb_lock_nonblock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);
}
int tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key)
{
return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);

View File

@ -263,15 +263,7 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
tdb->map_size = st.st_size;
tdb->device = st.st_dev;
tdb->inode = st.st_ino;
tdb->locked = (struct tdb_lock_type *)calloc(tdb->header.hash_size+1,
sizeof(tdb->locked[0]));
if (!tdb->locked) {
TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: "
"failed to allocate lock structure for %s\n",
name));
errno = ENOMEM;
goto fail;
}
tdb->max_dead_records = 0;
tdb_mmap(tdb);
if (locked) {
if (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_UNLCK, F_SETLK, 0, 1) == -1) {
@ -324,13 +316,21 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
if (tdb->fd != -1)
if (close(tdb->fd) != 0)
TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to close tdb->fd on error!\n"));
SAFE_FREE(tdb->locked);
SAFE_FREE(tdb);
errno = save_errno;
return NULL;
}
}
/*
* Set the maximum number of dead records per hash chain
*/
void tdb_set_max_dead(struct tdb_context *tdb, int max_dead)
{
tdb->max_dead_records = max_dead;
}
/**
* Close a database.
*
@ -354,7 +354,7 @@ int tdb_close(struct tdb_context *tdb)
SAFE_FREE(tdb->name);
if (tdb->fd != -1)
ret = close(tdb->fd);
SAFE_FREE(tdb->locked);
SAFE_FREE(tdb->lockrecs);
/* Remove from contexts list */
for (i = &tdbs; *i; i = &(*i)->next) {
@ -372,9 +372,9 @@ int tdb_close(struct tdb_context *tdb)
/* register a loging function */
void tdb_set_logging_function(struct tdb_context *tdb,
const struct tdb_logging_context *log)
const struct tdb_logging_context *log_ctx)
{
tdb->log = *log;
tdb->log = *log_ctx;
}
void *tdb_get_logging_private(struct tdb_context *tdb)

View File

@ -56,6 +56,10 @@ static void tdb_increment_seqnum(struct tdb_context *tdb)
tdb_brlock(tdb, TDB_SEQNUM_OFS, F_UNLCK, F_SETLKW, 1, 1);
}
static int tdb_key_compare(TDB_DATA key, TDB_DATA data, void *private_data)
{
return memcmp(data.dptr, key.dptr, data.dsize);
}
/* Returns 0 on fail. On success, return offset of record, and fills
in rec */
@ -73,19 +77,12 @@ static tdb_off_t tdb_find(struct tdb_context *tdb, TDB_DATA key, u32 hash,
if (tdb_rec_read(tdb, rec_ptr, r) == -1)
return 0;
if (!TDB_DEAD(r) && hash==r->full_hash && key.dsize==r->key_len) {
unsigned char *k;
/* a very likely hit - read the key */
k = tdb_alloc_read(tdb, rec_ptr + sizeof(*r),
r->key_len);
if (!k)
return 0;
if (memcmp(key.dptr, k, key.dsize) == 0) {
SAFE_FREE(k);
return rec_ptr;
}
SAFE_FREE(k);
if (!TDB_DEAD(r) && hash==r->full_hash
&& key.dsize==r->key_len
&& tdb_parse_data(tdb, key, rec_ptr + sizeof(*r),
r->key_len, tdb_key_compare,
NULL) == 0) {
return rec_ptr;
}
rec_ptr = r->next;
}
@ -163,6 +160,47 @@ TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key)
return ret;
}
/*
* Find an entry in the database and hand the record's data to a parsing
* function. The parsing function is executed under the chain read lock, so it
* should be fast and should not block on other syscalls.
*
* DONT CALL OTHER TDB CALLS FROM THE PARSER, THIS MIGHT LEAD TO SEGFAULTS.
*
* For mmapped tdb's that do not have a transaction open it points the parsing
* function directly at the mmap area, it avoids the malloc/memcpy in this
* case. If a transaction is open or no mmap is available, it has to do
* malloc/read/parse/free.
*
* This is interesting for all readers of potentially large data structures in
* the tdb records, ldb indexes being one example.
*/
int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key,
int (*parser)(TDB_DATA key, TDB_DATA data,
void *private_data),
void *private_data)
{
tdb_off_t rec_ptr;
struct list_struct rec;
int ret;
u32 hash;
/* find which hash bucket it is in */
hash = tdb->hash_fn(&key);
if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec))) {
return TDB_ERRCODE(TDB_ERR_NOEXIST, 0);
}
ret = tdb_parse_data(tdb, key, rec_ptr + sizeof(rec) + rec.key_len,
rec.data_len, parser, private_data);
tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
return ret;
}
/* check if an entry in the database exists
note that 1 is returned if the key is found and 0 is returned if not found
@ -220,6 +258,66 @@ int tdb_do_delete(struct tdb_context *tdb, tdb_off_t rec_ptr, struct list_struct
return 0;
}
static int tdb_count_dead(struct tdb_context *tdb, u32 hash)
{
int res = 0;
tdb_off_t rec_ptr;
struct list_struct rec;
/* read in the hash top */
if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1)
return 0;
while (rec_ptr) {
if (tdb_rec_read(tdb, rec_ptr, &rec) == -1)
return 0;
if (rec.magic == TDB_DEAD_MAGIC) {
res += 1;
}
rec_ptr = rec.next;
}
return res;
}
/*
* Purge all DEAD records from a hash chain
*/
static int tdb_purge_dead(struct tdb_context *tdb, u32 hash)
{
int res = -1;
struct list_struct rec;
tdb_off_t rec_ptr;
if (tdb_lock(tdb, -1, F_WRLCK) == -1) {
return -1;
}
/* read in the hash top */
if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1)
goto fail;
while (rec_ptr) {
tdb_off_t next;
if (tdb_rec_read(tdb, rec_ptr, &rec) == -1) {
goto fail;
}
next = rec.next;
if (rec.magic == TDB_DEAD_MAGIC
&& tdb_do_delete(tdb, rec_ptr, &rec) == -1) {
goto fail;
}
rec_ptr = next;
}
res = 0;
fail:
tdb_unlock(tdb, -1, F_WRLCK);
return res;
}
/* delete an entry in the database given a key */
static int tdb_delete_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash)
{
@ -227,9 +325,42 @@ static int tdb_delete_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash)
struct list_struct rec;
int ret;
if (!(rec_ptr = tdb_find_lock_hash(tdb, key, hash, F_WRLCK, &rec)))
return -1;
ret = tdb_do_delete(tdb, rec_ptr, &rec);
if (tdb->max_dead_records != 0) {
/*
* Allow for some dead records per hash chain, mainly for
* tdb's with a very high create/delete rate like locking.tdb.
*/
if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
return -1;
if (tdb_count_dead(tdb, hash) >= tdb->max_dead_records) {
/*
* Don't let the per-chain freelist grow too large,
* delete all existing dead records
*/
tdb_purge_dead(tdb, hash);
}
if (!(rec_ptr = tdb_find(tdb, key, hash, &rec))) {
tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
return -1;
}
/*
* Just mark the record as dead.
*/
rec.magic = TDB_DEAD_MAGIC;
ret = tdb_rec_write(tdb, rec_ptr, &rec);
}
else {
if (!(rec_ptr = tdb_find_lock_hash(tdb, key, hash, F_WRLCK,
&rec)))
return -1;
ret = tdb_do_delete(tdb, rec_ptr, &rec);
}
if (ret == 0) {
tdb_increment_seqnum(tdb);
@ -246,6 +377,35 @@ int tdb_delete(struct tdb_context *tdb, TDB_DATA key)
return tdb_delete_hash(tdb, key, hash);
}
/*
* See if we have a dead record around with enough space
*/
static tdb_off_t tdb_find_dead(struct tdb_context *tdb, u32 hash,
struct list_struct *r, tdb_len_t length)
{
tdb_off_t rec_ptr;
/* read in the hash top */
if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1)
return 0;
/* keep looking until we find the right record */
while (rec_ptr) {
if (tdb_rec_read(tdb, rec_ptr, r) == -1)
return 0;
if (TDB_DEAD(r) && r->rec_len >= length) {
/*
* First fit for simple coding, TODO: change to best
* fit
*/
return rec_ptr;
}
rec_ptr = r->next;
}
return 0;
}
/* store an element in the database, replacing any existing element
with the same key
@ -257,7 +417,7 @@ int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
u32 hash;
tdb_off_t rec_ptr;
char *p = NULL;
int ret = 0;
int ret = -1;
if (tdb->read_only || tdb->traverse_read) {
tdb->ecode = TDB_ERR_RDONLY;
@ -277,8 +437,9 @@ int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
}
} else {
/* first try in-place update, on modify or replace. */
if (tdb_update_hash(tdb, key, hash, dbuf) == 0)
goto out;
if (tdb_update_hash(tdb, key, hash, dbuf) == 0) {
goto done;
}
if (tdb->ecode == TDB_ERR_NOEXIST &&
flag == TDB_MODIFY) {
/* if the record doesn't exist and we are in TDB_MODIFY mode then
@ -307,9 +468,56 @@ int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
if (dbuf.dsize)
memcpy(p+key.dsize, dbuf.dptr, dbuf.dsize);
/* we have to allocate some space */
if (!(rec_ptr = tdb_allocate(tdb, key.dsize + dbuf.dsize, &rec)))
if (tdb->max_dead_records != 0) {
/*
* Allow for some dead records per hash chain, look if we can
* find one that can hold the new record. We need enough space
* for key, data and tailer. If we find one, we don't have to
* consult the central freelist.
*/
rec_ptr = tdb_find_dead(
tdb, hash, &rec,
key.dsize + dbuf.dsize + sizeof(tdb_off_t));
if (rec_ptr != 0) {
rec.key_len = key.dsize;
rec.data_len = dbuf.dsize;
rec.full_hash = hash;
rec.magic = TDB_MAGIC;
if (tdb_rec_write(tdb, rec_ptr, &rec) == -1
|| tdb->methods->tdb_write(
tdb, rec_ptr + sizeof(rec),
p, key.dsize + dbuf.dsize) == -1) {
goto fail;
}
goto done;
}
}
/*
* We have to allocate some space from the freelist, so this means we
* have to lock it. Use the chance to purge all the DEAD records from
* the hash chain under the freelist lock.
*/
if (tdb_lock(tdb, -1, F_WRLCK) == -1) {
goto fail;
}
if ((tdb->max_dead_records != 0)
&& (tdb_purge_dead(tdb, hash) == -1)) {
tdb_unlock(tdb, -1, F_WRLCK);
goto fail;
}
/* we have to allocate some space */
rec_ptr = tdb_allocate(tdb, key.dsize + dbuf.dsize, &rec);
tdb_unlock(tdb, -1, F_WRLCK);
if (rec_ptr == 0) {
goto fail;
}
/* Read hash top into next ptr */
if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1)
@ -328,15 +536,16 @@ int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
goto fail;
}
tdb_increment_seqnum(tdb);
done:
ret = 0;
fail:
if (ret == 0) {
tdb_increment_seqnum(tdb);
}
out:
SAFE_FREE(p);
tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
return ret;
fail:
ret = -1;
goto out;
}
@ -355,9 +564,10 @@ int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf)
dbuf = tdb_fetch(tdb, key);
if (dbuf.dptr == NULL) {
dbuf.dptr = malloc(new_dbuf.dsize);
dbuf.dptr = (unsigned char *)malloc(new_dbuf.dsize);
} else {
dbuf.dptr = realloc(dbuf.dptr, dbuf.dsize + new_dbuf.dsize);
dbuf.dptr = (unsigned char *)realloc(dbuf.dptr,
dbuf.dsize + new_dbuf.dsize);
}
if (dbuf.dptr == NULL) {

View File

@ -123,6 +123,7 @@ struct tdb_header {
};
struct tdb_lock_type {
int list;
u32 count;
u32 ltype;
};
@ -152,7 +153,8 @@ struct tdb_context {
int read_only; /* opened read-only */
int traverse_read; /* read-only traversal */
struct tdb_lock_type global_lock;
struct tdb_lock_type *locked; /* array of chain locks */
int num_lockrecs;
struct tdb_lock_type *lockrecs; /* only real locks, all with count>0 */
enum TDB_ERROR ecode; /* error code for last tdb error */
struct tdb_header header; /* a cached copy of the header */
u32 flags; /* the flags passed to tdb_open */
@ -167,6 +169,7 @@ struct tdb_context {
const struct tdb_methods *methods;
struct tdb_transaction *transaction;
int page_size;
int max_dead_records;
};
@ -194,9 +197,16 @@ int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *
int tdb_rec_write(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec);
int tdb_do_delete(struct tdb_context *tdb, tdb_off_t rec_ptr, struct list_struct *rec);
unsigned char *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len);
int tdb_parse_data(struct tdb_context *tdb, TDB_DATA key,
tdb_off_t offset, tdb_len_t len,
int (*parser)(TDB_DATA key, TDB_DATA data,
void *private_data),
void *private_data);
tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash, int locktype,
struct list_struct *rec);
void tdb_io_init(struct tdb_context *tdb);
int tdb_expand(struct tdb_context *tdb, tdb_off_t size);
int rec_free_read(struct tdb_context *tdb, tdb_off_t off,
struct list_struct *rec);

View File

@ -39,7 +39,7 @@
by the header. This removes the need for extra journal files as
used by some other databases
- dymacially allocated the transaction recover record, re-using it
- dynamically allocated the transaction recover record, re-using it
for subsequent transactions. If a larger record is needed then
tdb_free() the old record to place it on the normal tdb freelist
before allocating the new record
@ -88,6 +88,12 @@
*/
struct tdb_transaction_el {
struct tdb_transaction_el *next, *prev;
tdb_off_t offset;
tdb_len_t length;
unsigned char *data;
};
/*
hold the context of any current transaction
@ -105,12 +111,7 @@ struct tdb_transaction {
ordered, with first element at the front of the list. It
needs to be doubly linked as the read/write traversals need
to be backwards, while the commit needs to be forwards */
struct tdb_transaction_el {
struct tdb_transaction_el *next, *prev;
tdb_off_t offset;
tdb_len_t length;
unsigned char *data;
} *elements, *elements_last;
struct tdb_transaction_el *elements, *elements_last;
/* non-zero when an internal transaction error has
occurred. All write operations will then fail until the
@ -357,8 +358,8 @@ static int transaction_expand_file(struct tdb_context *tdb, tdb_off_t size,
/*
brlock during a transaction - ignore them
*/
int transaction_brlock(struct tdb_context *tdb, tdb_off_t offset,
int rw_type, int lck_type, int probe, size_t len)
static int transaction_brlock(struct tdb_context *tdb, tdb_off_t offset,
int rw_type, int lck_type, int probe, size_t len)
{
return 0;
}
@ -516,14 +517,14 @@ int tdb_transaction_cancel(struct tdb_context *tdb)
/* remove any locks created during the transaction */
if (tdb->num_locks != 0) {
int h;
for (h=0;h<tdb->header.hash_size+1;h++) {
if (tdb->locked[h].count != 0) {
tdb_brlock(tdb,FREELIST_TOP+4*h,F_UNLCK,F_SETLKW, 0, 1);
tdb->locked[h].count = 0;
}
int i;
for (i=0;i<tdb->num_lockrecs;i++) {
tdb_brlock(tdb,FREELIST_TOP+4*tdb->lockrecs[i].list,
F_UNLCK,F_SETLKW, 0, 1);
}
tdb->num_locks = 0;
tdb->num_lockrecs = 0;
SAFE_FREE(tdb->lockrecs);
}
/* restore the normal io methods */

View File

@ -18,7 +18,6 @@ PUBLIC_HEADERS = include/tdb.h
# Start BINARY tdbtool
[BINARY::tdbtool]
INSTALLDIR = BINDIR
ENABLE = NO
OBJ_FILES= \
tools/tdbtool.o
PRIVATE_DEPENDENCIES = \

View File

@ -77,9 +77,6 @@
/* Define to 1 if you have the `endnetgrent' function. */
#undef HAVE_ENDNETGRENT
/* Define to 1 if you have the `epoll_create' function. */
#undef HAVE_EPOLL_CREATE
/* Whether errno() is available */
#undef HAVE_ERRNO_DECL
@ -236,6 +233,9 @@
/* Define to 1 if you have the `seteuid' function. */
#undef HAVE_SETEUID
/* Define to 1 if you have the <setjmp.h> header file. */
#undef HAVE_SETJMP_H
/* Define to 1 if you have the `setlinebuf' function. */
#undef HAVE_SETLINEBUF
@ -358,9 +358,6 @@
*/
#undef HAVE_SYS_DIR_H
/* Define to 1 if you have the <sys/epoll.h> header file. */
#undef HAVE_SYS_EPOLL_H
/* Define to 1 if you have the <sys/fcntl.h> header file. */
#undef HAVE_SYS_FCNTL_H
@ -452,6 +449,9 @@
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to 1 if you have the `unsetenv' function. */
#undef HAVE_UNSETENV
/* Define to 1 if you have the `usleep' function. */
#undef HAVE_USLEEP

View File

@ -94,13 +94,18 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
int open_flags, mode_t mode,
const struct tdb_logging_context *log_ctx,
tdb_hash_func hash_fn);
void tdb_set_max_dead(struct tdb_context *tdb, int max_dead);
int tdb_reopen(struct tdb_context *tdb);
int tdb_reopen_all(int parent_longlived);
void tdb_set_logging_function(struct tdb_context *tdb, const struct tdb_logging_context *log);
void tdb_set_logging_function(struct tdb_context *tdb, const struct tdb_logging_context *log_ctx);
enum TDB_ERROR tdb_error(struct tdb_context *tdb);
const char *tdb_errorstr(struct tdb_context *tdb);
TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key);
int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key,
int (*parser)(TDB_DATA key, TDB_DATA data,
void *private_data),
void *private_data);
int tdb_delete(struct tdb_context *tdb, TDB_DATA key);
int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag);
int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf);
@ -129,6 +134,7 @@ int tdb_get_flags(struct tdb_context *tdb);
/* Low level locking functions: use with care */
int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key);
int tdb_chainlock_nonblock(struct tdb_context *tdb, TDB_DATA key);
int tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key);
int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key);
int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key);

View File

@ -5,6 +5,7 @@ includedir=${prefix}/include
Name: tdb
Description: Trivial Database Library
Requires.private:
Version: 0.0.1
Libs: -L${libdir} -ltdb
Libs.private: -lreplace

View File

@ -214,7 +214,7 @@ static void merge_test(void)
key.dptr = keys[3];
tdb_delete(db, key);
}
int main(int argc, const char *argv[])
{
int i, seed=0;

View File

@ -21,50 +21,82 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <ctype.h>
#include <signal.h>
#include <stdarg.h>
#include "replace.h"
#include "system/locale.h"
#include "system/time.h"
#include "system/filesys.h"
#include "tdb.h"
static int do_command(void);
const char *cmdname;
char *arg1, *arg2;
size_t arg1len, arg2len;
int bIterate = 0;
char *line;
TDB_DATA iterate_kbuf;
char cmdline[1024];
enum commands {
CMD_CREATE_TDB,
CMD_OPEN_TDB,
CMD_ERASE,
CMD_DUMP,
CMD_INSERT,
CMD_MOVE,
CMD_STORE,
CMD_SHOW,
CMD_KEYS,
CMD_HEXKEYS,
CMD_DELETE,
CMD_LIST_HASH_FREE,
CMD_LIST_FREE,
CMD_INFO,
CMD_FIRST,
CMD_NEXT,
CMD_SYSTEM,
CMD_QUIT,
CMD_HELP
};
typedef struct {
const char *name;
enum commands cmd;
} COMMAND_TABLE;
COMMAND_TABLE cmd_table[] = {
{"create", CMD_CREATE_TDB},
{"open", CMD_OPEN_TDB},
{"erase", CMD_ERASE},
{"dump", CMD_DUMP},
{"insert", CMD_INSERT},
{"move", CMD_MOVE},
{"store", CMD_STORE},
{"show", CMD_SHOW},
{"keys", CMD_KEYS},
{"hexkeys", CMD_HEXKEYS},
{"delete", CMD_DELETE},
{"list", CMD_LIST_HASH_FREE},
{"free", CMD_LIST_FREE},
{"info", CMD_INFO},
{"first", CMD_FIRST},
{"1", CMD_FIRST},
{"next", CMD_NEXT},
{"n", CMD_NEXT},
{"quit", CMD_QUIT},
{"q", CMD_QUIT},
{"!", CMD_SYSTEM},
{NULL, CMD_HELP}
};
/* a tdb tool for manipulating a tdb database */
#define FSTRING_LEN 256
typedef char fstring[FSTRING_LEN];
static TDB_CONTEXT *tdb;
typedef struct connections_key {
pid_t pid;
int cnum;
fstring name;
} connections_key;
static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
static int print_key(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
static int print_hexkey(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
typedef struct connections_data {
int magic;
pid_t pid;
int cnum;
uid_t uid;
gid_t gid;
char name[24];
char addr[24];
char machine[128];
time_t start;
} connections_data;
static struct tdb_context *tdb;
static int print_rec(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
static void print_asc(unsigned char *buf,int len)
static void print_asc(const char *buf,int len)
{
int i;
@ -78,20 +110,7 @@ static void print_asc(unsigned char *buf,int len)
printf("%c",isprint(buf[i])?buf[i]:'.');
}
#ifdef PRINTF_ATTRIBUTE
static void tdb_log(struct tdb_context *t, enum tdb_debug_level level, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
#endif
static void tdb_log(struct tdb_context *t, enum tdb_debug_level level, const char *format, ...)
{
va_list ap;
va_start(ap, format);
vfprintf(stdout, format, ap);
va_end(ap);
fflush(stdout);
}
static void print_data(unsigned char *buf,int len)
static void print_data(const char *buf,int len)
{
int i=0;
if (len<=0) return;
@ -131,6 +150,9 @@ static void help(void)
" open dbname : open an existing database\n"
" erase : erase the database\n"
" dump : dump the database as strings\n"
" keys : dump the database keys as strings\n"
" hexkeys : dump the database keys as hex values\n"
" info : print summary info about the database\n"
" insert key data : insert a record\n"
" move key file : move a record to a destination tdb\n"
" store key data : store a record (replace)\n"
@ -138,6 +160,7 @@ static void help(void)
" delete key : delete a record by key\n"
" list : print the database hash table and freelist\n"
" free : print the database freelist\n"
" ! command : execute system command\n"
" 1 | first : print the first record\n"
" n | next : print the next record\n"
" q | quit : terminate\n"
@ -150,110 +173,62 @@ static void terror(const char *why)
printf("%s\n", why);
}
static char *get_token(int startover)
static void create_tdb(const char *tdbname)
{
static char tmp[1024];
static char *cont = NULL;
char *insert, *start;
char *k = strtok(NULL, " ");
if (!k)
return NULL;
if (startover)
start = tmp;
else
start = cont;
strcpy(start, k);
insert = start + strlen(start) - 1;
while (*insert == '\\') {
*insert++ = ' ';
k = strtok(NULL, " ");
if (!k)
break;
strcpy(insert, k);
insert = start + strlen(start) - 1;
}
/* Get ready for next call */
cont = start + strlen(start) + 1;
return start;
}
static void create_tdb(void)
{
char *tok = get_token(1);
struct tdb_logging_context log_ctx;
log_ctx.log_fn = tdb_log;
if (!tok) {
help();
return;
}
if (tdb) tdb_close(tdb);
tdb = tdb_open_ex(tok, 0, TDB_CLEAR_IF_FIRST,
O_RDWR | O_CREAT | O_TRUNC, 0600, &log_ctx, NULL);
tdb = tdb_open(tdbname, 0, TDB_CLEAR_IF_FIRST,
O_RDWR | O_CREAT | O_TRUNC, 0600);
if (!tdb) {
printf("Could not create %s: %s\n", tok, strerror(errno));
printf("Could not create %s: %s\n", tdbname, strerror(errno));
}
}
static void open_tdb(void)
static void open_tdb(const char *tdbname)
{
struct tdb_logging_context log_ctx;
char *tok = get_token(1);
log_ctx.log_fn = tdb_log;
if (!tok) {
help();
return;
}
if (tdb) tdb_close(tdb);
tdb = tdb_open_ex(tok, 0, 0, O_RDWR, 0600, &log_ctx, NULL);
tdb = tdb_open(tdbname, 0, 0, O_RDWR, 0600);
if (!tdb) {
printf("Could not open %s: %s\n", tok, strerror(errno));
printf("Could not open %s: %s\n", tdbname, strerror(errno));
}
}
static void insert_tdb(void)
static void insert_tdb(char *keyname, size_t keylen, char* data, size_t datalen)
{
char *k = get_token(1);
char *d = get_token(0);
TDB_DATA key, dbuf;
if (!k || !d) {
help();
if ((keyname == NULL) || (keylen == 0)) {
terror("need key");
return;
}
key.dptr = (unsigned char *)k;
key.dsize = strlen(k)+1;
dbuf.dptr = (unsigned char *)d;
dbuf.dsize = strlen(d)+1;
key.dptr = (unsigned char *)keyname;
key.dsize = keylen;
dbuf.dptr = (unsigned char *)data;
dbuf.dsize = datalen;
if (tdb_store(tdb, key, dbuf, TDB_INSERT) == -1) {
terror("insert failed");
}
}
static void store_tdb(void)
static void store_tdb(char *keyname, size_t keylen, char* data, size_t datalen)
{
char *k = get_token(1);
char *d = get_token(0);
TDB_DATA key, dbuf;
if (!k || !d) {
help();
if ((keyname == NULL) || (keylen == 0)) {
terror("need key");
return;
}
key.dptr = (unsigned char *)k;
key.dsize = strlen(k)+1;
dbuf.dptr = (unsigned char *)d;
dbuf.dsize = strlen(d)+1;
if ((data == NULL) || (datalen == 0)) {
terror("need data");
return;
}
key.dptr = (unsigned char *)keyname;
key.dsize = keylen;
dbuf.dptr = (unsigned char *)data;
dbuf.dsize = datalen;
printf("Storing key:\n");
print_rec(tdb, key, dbuf, NULL);
@ -263,32 +238,24 @@ static void store_tdb(void)
}
}
static void show_tdb(void)
static void show_tdb(char *keyname, size_t keylen)
{
char *k = get_token(1);
TDB_DATA key, dbuf;
if (!k) {
help();
if ((keyname == NULL) || (keylen == 0)) {
terror("need key");
return;
}
key.dptr = (unsigned char *)k;
key.dsize = strlen(k)+1;
key.dptr = (unsigned char *)keyname;
key.dsize = keylen;
dbuf = tdb_fetch(tdb, key);
if (!dbuf.dptr) {
/* maybe it is non-NULL terminated key? */
key.dsize = strlen(k);
dbuf = tdb_fetch(tdb, key);
if ( !dbuf.dptr ) {
terror("fetch failed");
return;
}
terror("fetch failed");
return;
}
/* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */
print_rec(tdb, key, dbuf, NULL);
free( dbuf.dptr );
@ -296,62 +263,50 @@ static void show_tdb(void)
return;
}
static void delete_tdb(void)
static void delete_tdb(char *keyname, size_t keylen)
{
char *k = get_token(1);
TDB_DATA key;
if (!k) {
help();
if ((keyname == NULL) || (keylen == 0)) {
terror("need key");
return;
}
key.dptr = (unsigned char *)k;
key.dsize = strlen(k)+1;
key.dptr = (unsigned char *)keyname;
key.dsize = keylen;
if (tdb_delete(tdb, key) != 0) {
terror("delete failed");
}
}
static void move_rec(void)
static void move_rec(char *keyname, size_t keylen, char* tdbname)
{
char *k = get_token(1);
char *file = get_token(0);
TDB_DATA key, dbuf;
struct tdb_context *dst_tdb;
TDB_CONTEXT *dst_tdb;
struct tdb_logging_context log_ctx;
log_ctx.log_fn = tdb_log;
if (!k) {
help();
if ((keyname == NULL) || (keylen == 0)) {
terror("need key");
return;
}
if ( !file ) {
if ( !tdbname ) {
terror("need destination tdb name");
return;
}
key.dptr = (unsigned char *)k;
key.dsize = strlen(k)+1;
key.dptr = (unsigned char *)keyname;
key.dsize = keylen;
dbuf = tdb_fetch(tdb, key);
if (!dbuf.dptr) {
/* maybe it is non-NULL terminated key? */
key.dsize = strlen(k);
dbuf = tdb_fetch(tdb, key);
if ( !dbuf.dptr ) {
terror("fetch failed");
return;
}
terror("fetch failed");
return;
}
print_rec(tdb, key, dbuf, NULL);
dst_tdb = tdb_open_ex(file, 0, 0, O_RDWR, 0600, &log_ctx, NULL);
dst_tdb = tdb_open(tdbname, 0, 0, O_RDWR, 0600);
if ( !dst_tdb ) {
terror("unable to open destination tdb");
return;
@ -368,25 +323,34 @@ static void move_rec(void)
return;
}
static int print_rec(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
{
printf("\nkey %d bytes\n", key.dsize);
print_asc(key.dptr, key.dsize);
printf("\ndata %d bytes\n", dbuf.dsize);
print_data(dbuf.dptr, dbuf.dsize);
printf("\nkey %d bytes\n", (int)key.dsize);
print_asc((const char *)key.dptr, key.dsize);
printf("\ndata %d bytes\n", (int)dbuf.dsize);
print_data((const char *)dbuf.dptr, dbuf.dsize);
return 0;
}
static int print_key(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
static int print_key(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
{
print_asc(key.dptr, key.dsize);
printf("key %d bytes: ", (int)key.dsize);
print_asc((const char *)key.dptr, key.dsize);
printf("\n");
return 0;
}
static int print_hexkey(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
{
printf("key %d bytes\n", (int)key.dsize);
print_data((const char *)key.dptr, key.dsize);
printf("\n");
return 0;
}
static int total_bytes;
static int traverse_fn(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
{
total_bytes += dbuf.dsize;
return 0;
@ -396,7 +360,7 @@ static void info_tdb(void)
{
int count;
total_bytes = 0;
if ((count = tdb_traverse(tdb, traverse_fn, NULL) == -1))
if ((count = tdb_traverse(tdb, traverse_fn, NULL)) == -1)
printf("Error = %s\n", tdb_errorstr(tdb));
else
printf("%d records totalling %d bytes\n", count, total_bytes);
@ -404,23 +368,23 @@ static void info_tdb(void)
static char *tdb_getline(const char *prompt)
{
static char line[1024];
static char thisline[1024];
char *p;
fputs(prompt, stdout);
line[0] = 0;
p = fgets(line, sizeof(line)-1, stdin);
thisline[0] = 0;
p = fgets(thisline, sizeof(thisline)-1, stdin);
if (p) p = strchr(p, '\n');
if (p) *p = 0;
return p?line:NULL;
return p?thisline:NULL;
}
static int do_delete_fn(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf,
static int do_delete_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf,
void *state)
{
return tdb_delete(the_tdb, key);
}
static void first_record(struct tdb_context *the_tdb, TDB_DATA *pkey)
static void first_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
{
TDB_DATA dbuf;
*pkey = tdb_firstkey(the_tdb);
@ -428,12 +392,11 @@ static void first_record(struct tdb_context *the_tdb, TDB_DATA *pkey)
dbuf = tdb_fetch(the_tdb, *pkey);
if (!dbuf.dptr) terror("fetch failed");
else {
/* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */
print_rec(the_tdb, *pkey, dbuf, NULL);
}
}
static void next_record(struct tdb_context *the_tdb, TDB_DATA *pkey)
static void next_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
{
TDB_DATA dbuf;
*pkey = tdb_nextkey(the_tdb, *pkey);
@ -442,98 +405,202 @@ static void next_record(struct tdb_context *the_tdb, TDB_DATA *pkey)
if (!dbuf.dptr)
terror("fetch failed");
else
/* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */
print_rec(the_tdb, *pkey, dbuf, NULL);
}
static int do_command(void)
{
COMMAND_TABLE *ctp = cmd_table;
enum commands mycmd = CMD_HELP;
int cmd_len;
if (cmdname && strlen(cmdname) == 0) {
mycmd = CMD_NEXT;
} else {
while (ctp->name) {
cmd_len = strlen(ctp->name);
if (strncmp(ctp->name,cmdname,cmd_len) == 0) {
mycmd = ctp->cmd;
break;
}
ctp++;
}
}
switch (mycmd) {
case CMD_CREATE_TDB:
bIterate = 0;
create_tdb(arg1);
return 0;
case CMD_OPEN_TDB:
bIterate = 0;
open_tdb(arg1);
return 0;
case CMD_SYSTEM:
/* Shell command */
system(arg1);
return 0;
case CMD_QUIT:
return 1;
default:
/* all the rest require a open database */
if (!tdb) {
bIterate = 0;
terror("database not open");
help();
return 0;
}
switch (mycmd) {
case CMD_ERASE:
bIterate = 0;
tdb_traverse(tdb, do_delete_fn, NULL);
return 0;
case CMD_DUMP:
bIterate = 0;
tdb_traverse(tdb, print_rec, NULL);
return 0;
case CMD_INSERT:
bIterate = 0;
insert_tdb(arg1, arg1len,arg2,arg2len);
return 0;
case CMD_MOVE:
bIterate = 0;
move_rec(arg1,arg1len,arg2);
return 0;
case CMD_STORE:
bIterate = 0;
store_tdb(arg1,arg1len,arg2,arg2len);
return 0;
case CMD_SHOW:
bIterate = 0;
show_tdb(arg1, arg1len);
return 0;
case CMD_KEYS:
tdb_traverse(tdb, print_key, NULL);
return 0;
case CMD_HEXKEYS:
tdb_traverse(tdb, print_hexkey, NULL);
return 0;
case CMD_DELETE:
bIterate = 0;
delete_tdb(arg1,arg1len);
return 0;
case CMD_LIST_HASH_FREE:
tdb_dump_all(tdb);
return 0;
case CMD_LIST_FREE:
tdb_printfreelist(tdb);
return 0;
case CMD_INFO:
info_tdb();
return 0;
case CMD_FIRST:
bIterate = 1;
first_record(tdb, &iterate_kbuf);
return 0;
case CMD_NEXT:
if (bIterate)
next_record(tdb, &iterate_kbuf);
return 0;
case CMD_HELP:
help();
return 0;
case CMD_CREATE_TDB:
case CMD_OPEN_TDB:
case CMD_SYSTEM:
case CMD_QUIT:
/*
* unhandled commands. cases included here to avoid compiler
* warnings.
*/
return 0;
}
}
return 0;
}
static char *convert_string(char *instring, size_t *sizep)
{
size_t length = 0;
char *outp, *inp;
char temp[3];
outp = inp = instring;
while (*inp) {
if (*inp == '\\') {
inp++;
if (*inp && strchr("0123456789abcdefABCDEF",(int)*inp)) {
temp[0] = *inp++;
temp[1] = '\0';
if (*inp && strchr("0123456789abcdefABCDEF",(int)*inp)) {
temp[1] = *inp++;
temp[2] = '\0';
}
*outp++ = (char)strtol((const char *)temp,NULL,16);
} else {
*outp++ = *inp++;
}
} else {
*outp++ = *inp++;
}
length++;
}
*sizep = length;
return instring;
}
int main(int argc, char *argv[])
{
int bIterate = 0;
char *line;
char *tok;
TDB_DATA iterate_kbuf;
cmdname = "";
arg1 = NULL;
arg1len = 0;
arg2 = NULL;
arg2len = 0;
if (argv[1]) {
static char tmp[1024];
sprintf(tmp, "open %s", argv[1]);
tok=strtok(tmp," ");
open_tdb();
cmdname = "open";
arg1 = argv[1];
do_command();
cmdname = "";
arg1 = NULL;
}
while ((line = tdb_getline("tdb> "))) {
/* Shell command */
if (line[0] == '!') {
system(line + 1);
continue;
}
if ((tok = strtok(line," "))==NULL) {
if (bIterate)
next_record(tdb, &iterate_kbuf);
continue;
}
if (strcmp(tok,"create") == 0) {
bIterate = 0;
create_tdb();
continue;
} else if (strcmp(tok,"open") == 0) {
open_tdb();
continue;
} else if ((strcmp(tok, "q") == 0) ||
(strcmp(tok, "quit") == 0)) {
break;
}
/* all the rest require a open database */
if (!tdb) {
bIterate = 0;
terror("database not open");
help();
continue;
}
if (strcmp(tok,"insert") == 0) {
bIterate = 0;
insert_tdb();
} else if (strcmp(tok,"store") == 0) {
bIterate = 0;
store_tdb();
} else if (strcmp(tok,"show") == 0) {
bIterate = 0;
show_tdb();
} else if (strcmp(tok,"erase") == 0) {
bIterate = 0;
tdb_traverse(tdb, do_delete_fn, NULL);
} else if (strcmp(tok,"delete") == 0) {
bIterate = 0;
delete_tdb();
} else if (strcmp(tok,"dump") == 0) {
bIterate = 0;
tdb_traverse(tdb, print_rec, NULL);
} else if (strcmp(tok,"move") == 0) {
bIterate = 0;
move_rec();
} else if (strcmp(tok,"list") == 0) {
tdb_dump_all(tdb);
} else if (strcmp(tok, "free") == 0) {
tdb_printfreelist(tdb);
} else if (strcmp(tok,"info") == 0) {
info_tdb();
} else if ( (strcmp(tok, "1") == 0) ||
(strcmp(tok, "first") == 0)) {
bIterate = 1;
first_record(tdb, &iterate_kbuf);
} else if ((strcmp(tok, "n") == 0) ||
(strcmp(tok, "next") == 0)) {
next_record(tdb, &iterate_kbuf);
} else if ((strcmp(tok, "keys") == 0)) {
bIterate = 0;
tdb_traverse(tdb, print_key, NULL);
} else {
help();
}
switch (argc) {
case 1:
case 2:
/* Interactive mode */
while ((cmdname = tdb_getline("tdb> "))) {
arg2 = arg1 = NULL;
if ((arg1 = strchr((const char *)cmdname,' ')) != NULL) {
arg1++;
arg2 = arg1;
while (*arg2) {
if (*arg2 == ' ') {
*arg2++ = '\0';
break;
}
if ((*arg2++ == '\\') && (*arg2 == ' ')) {
arg2++;
}
}
}
if (arg1) arg1 = convert_string(arg1,&arg1len);
if (arg2) arg2 = convert_string(arg2,&arg2len);
if (do_command()) break;
}
break;
case 5:
arg2 = convert_string(argv[4],&arg2len);
case 4:
arg1 = convert_string(argv[3],&arg1len);
case 3:
cmdname = argv[2];
default:
do_command();
break;
}
if (tdb) tdb_close(tdb);

View File

@ -42,6 +42,7 @@ static void lockwait_handler(struct event_context *ev, struct fd_event *fde,
void (*callback)(void *) = h->callback;
void *p = h->private_data;
talloc_set_destructor(h, NULL);
close(h->fd[0]);
talloc_free(h);
callback(p);
waitpid(h->child, NULL, 0);
@ -102,7 +103,7 @@ static struct lockwait_handle *lockwait(struct event_context *ev,
close(h->fd[1]);
talloc_set_destructor(h, lockwait_destructor);
h->fde = event_add_fd(ev, h, fd, EVENT_FD_READ, lockwait_handler, h);
h->fde = event_add_fd(ev, h, h->fd[0], EVENT_FD_READ, lockwait_handler, h);
if (h->fde == NULL) {
talloc_free(h);
return NULL;