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:
commit
c6fe6d592b
@ -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
|
||||
|
@ -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
118
ctdb/common/ctdb_lockwait.c
Normal 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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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 = \
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user