wmi-1.3.16 from opsview.com
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
low level tdb backup and restore utility
|
||||
Copyright (C) Andrew Tridgell 2002
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This program is meant for backup/restore of tdb databases. Typical usage would be:
|
||||
tdbbackup *.tdb
|
||||
when Samba shuts down cleanly, which will make a backup of all the local databases
|
||||
to *.bak files. Then on Samba startup you would use:
|
||||
tdbbackup -v *.tdb
|
||||
and this will check the databases for corruption and if corruption is detected then
|
||||
the backup will be restored.
|
||||
|
||||
You may also like to do a backup on a regular basis while Samba is
|
||||
running, perhaps using cron.
|
||||
|
||||
The reason this program is needed is to cope with power failures
|
||||
while Samba is running. A power failure could lead to database
|
||||
corruption and Samba will then not start correctly.
|
||||
|
||||
Note that many of the databases in Samba are transient and thus
|
||||
don't need to be backed up, so you can optimise the above a little
|
||||
by only running the backup on the critical databases.
|
||||
|
||||
*/
|
||||
|
||||
#include "replace.h"
|
||||
#include "tdb.h"
|
||||
#include "system/filesys.h"
|
||||
|
||||
/*
|
||||
see if one file is newer than another
|
||||
*/
|
||||
static int file_newer(const char *fname1, const char *fname2)
|
||||
{
|
||||
struct stat st1, st2;
|
||||
if (stat(fname1, &st1) != 0) {
|
||||
return 0;
|
||||
}
|
||||
if (stat(fname2, &st2) != 0) {
|
||||
return 1;
|
||||
}
|
||||
return (st1.st_mtime > st2.st_mtime);
|
||||
}
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
printf("Usage: tdbbackup [options] <fname...>\n\n");
|
||||
printf(" -h this help message\n");
|
||||
printf(" -s suffix set the backup suffix\n");
|
||||
printf(" -v verify mode (restore if corrupt)\n");
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
int ret = 0;
|
||||
int c;
|
||||
int verify = 0;
|
||||
const char *suffix = ".bak";
|
||||
extern int optind;
|
||||
extern char *optarg;
|
||||
|
||||
while ((c = getopt(argc, argv, "vhs:")) != -1) {
|
||||
switch (c) {
|
||||
case 'h':
|
||||
usage();
|
||||
exit(0);
|
||||
case 'v':
|
||||
verify = 1;
|
||||
break;
|
||||
case 's':
|
||||
suffix = optarg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (argc < 1) {
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (i=0; i<argc; i++) {
|
||||
const char *fname = argv[i];
|
||||
char *bak_name;
|
||||
|
||||
bak_name = add_suffix(fname, suffix);
|
||||
|
||||
if (verify) {
|
||||
if (verify_tdb(fname, bak_name) != 0) {
|
||||
ret = 1;
|
||||
}
|
||||
} else {
|
||||
if (file_newer(fname, bak_name) &&
|
||||
backup_tdb(fname, bak_name) != 0) {
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
|
||||
free(bak_name);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
simple tdb dump util
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "replace.h"
|
||||
#include "tdb.h"
|
||||
#include "system/locale.h"
|
||||
#include "system/filesys.h"
|
||||
|
||||
static void print_data(TDB_DATA d)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)d.dptr;
|
||||
int len = d.dsize;
|
||||
while (len--) {
|
||||
if (isprint(*p) && !strchr("\"\\", *p)) {
|
||||
fputc(*p, stdout);
|
||||
} else {
|
||||
printf("\\%02X", *p);
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
static int traverse_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
|
||||
{
|
||||
printf("{\n");
|
||||
printf("key = \"");
|
||||
print_data(key);
|
||||
printf("\"\n");
|
||||
printf("data = \"");
|
||||
print_data(dbuf);
|
||||
printf("\"\n");
|
||||
printf("}\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dump_tdb(const char *fname)
|
||||
{
|
||||
struct tdb_context *tdb;
|
||||
|
||||
tdb = tdb_open(fname, 0, 0, O_RDONLY, 0);
|
||||
if (!tdb) {
|
||||
printf("Failed to open %s\n", fname);
|
||||
return 1;
|
||||
}
|
||||
|
||||
tdb_traverse(tdb, traverse_fn, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *fname;
|
||||
|
||||
if (argc < 2) {
|
||||
printf("Usage: tdbdump <fname>\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fname = argv[1];
|
||||
|
||||
return dump_tdb(fname);
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
/* a test program for tdb - the trivial database */
|
||||
|
||||
#include "replace.h"
|
||||
#include "tdb.h"
|
||||
#include "system/filesys.h"
|
||||
#include "system/time.h"
|
||||
|
||||
#include <gdbm.h>
|
||||
|
||||
|
||||
#define DELETE_PROB 7
|
||||
#define STORE_PROB 5
|
||||
|
||||
static struct tdb_context *db;
|
||||
static GDBM_FILE gdbm;
|
||||
|
||||
struct timeval tp1,tp2;
|
||||
|
||||
static void _start_timer(void)
|
||||
{
|
||||
gettimeofday(&tp1,NULL);
|
||||
}
|
||||
|
||||
static double _end_timer(void)
|
||||
{
|
||||
gettimeofday(&tp2,NULL);
|
||||
return((tp2.tv_sec - tp1.tv_sec) +
|
||||
(tp2.tv_usec - tp1.tv_usec)*1.0e-6);
|
||||
}
|
||||
|
||||
static void fatal(const char *why)
|
||||
{
|
||||
perror(why);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#ifdef PRINTF_ATTRIBUTE
|
||||
static void tdb_log(struct tdb_context *tdb, int level, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
|
||||
#endif
|
||||
static void tdb_log(struct tdb_context *tdb, int level, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
vfprintf(stdout, format, ap);
|
||||
va_end(ap);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
static void compare_db(void)
|
||||
{
|
||||
TDB_DATA d, key, nextkey;
|
||||
datum gd, gkey, gnextkey;
|
||||
|
||||
key = tdb_firstkey(db);
|
||||
while (key.dptr) {
|
||||
d = tdb_fetch(db, key);
|
||||
gkey.dptr = key.dptr;
|
||||
gkey.dsize = key.dsize;
|
||||
|
||||
gd = gdbm_fetch(gdbm, gkey);
|
||||
|
||||
if (!gd.dptr) fatal("key not in gdbm");
|
||||
if (gd.dsize != d.dsize) fatal("data sizes differ");
|
||||
if (memcmp(gd.dptr, d.dptr, d.dsize)) {
|
||||
fatal("data differs");
|
||||
}
|
||||
|
||||
nextkey = tdb_nextkey(db, key);
|
||||
free(key.dptr);
|
||||
free(d.dptr);
|
||||
free(gd.dptr);
|
||||
key = nextkey;
|
||||
}
|
||||
|
||||
gkey = gdbm_firstkey(gdbm);
|
||||
while (gkey.dptr) {
|
||||
gd = gdbm_fetch(gdbm, gkey);
|
||||
key.dptr = gkey.dptr;
|
||||
key.dsize = gkey.dsize;
|
||||
|
||||
d = tdb_fetch(db, key);
|
||||
|
||||
if (!d.dptr) fatal("key not in db");
|
||||
if (d.dsize != gd.dsize) fatal("data sizes differ");
|
||||
if (memcmp(d.dptr, gd.dptr, gd.dsize)) {
|
||||
fatal("data differs");
|
||||
}
|
||||
|
||||
gnextkey = gdbm_nextkey(gdbm, gkey);
|
||||
free(gkey.dptr);
|
||||
free(gd.dptr);
|
||||
free(d.dptr);
|
||||
gkey = gnextkey;
|
||||
}
|
||||
}
|
||||
|
||||
static char *randbuf(int len)
|
||||
{
|
||||
char *buf;
|
||||
int i;
|
||||
buf = (char *)malloc(len+1);
|
||||
|
||||
for (i=0;i<len;i++) {
|
||||
buf[i] = 'a' + (rand() % 26);
|
||||
}
|
||||
buf[i] = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void addrec_db(void)
|
||||
{
|
||||
int klen, dlen;
|
||||
char *k, *d;
|
||||
TDB_DATA key, data;
|
||||
|
||||
klen = 1 + (rand() % 4);
|
||||
dlen = 1 + (rand() % 100);
|
||||
|
||||
k = randbuf(klen);
|
||||
d = randbuf(dlen);
|
||||
|
||||
key.dptr = k;
|
||||
key.dsize = klen+1;
|
||||
|
||||
data.dptr = d;
|
||||
data.dsize = dlen+1;
|
||||
|
||||
if (rand() % DELETE_PROB == 0) {
|
||||
tdb_delete(db, key);
|
||||
} else if (rand() % STORE_PROB == 0) {
|
||||
if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
|
||||
fatal("tdb_store failed");
|
||||
}
|
||||
} else {
|
||||
data = tdb_fetch(db, key);
|
||||
if (data.dptr) free(data.dptr);
|
||||
}
|
||||
|
||||
free(k);
|
||||
free(d);
|
||||
}
|
||||
|
||||
static void addrec_gdbm(void)
|
||||
{
|
||||
int klen, dlen;
|
||||
char *k, *d;
|
||||
datum key, data;
|
||||
|
||||
klen = 1 + (rand() % 4);
|
||||
dlen = 1 + (rand() % 100);
|
||||
|
||||
k = randbuf(klen);
|
||||
d = randbuf(dlen);
|
||||
|
||||
key.dptr = k;
|
||||
key.dsize = klen+1;
|
||||
|
||||
data.dptr = d;
|
||||
data.dsize = dlen+1;
|
||||
|
||||
if (rand() % DELETE_PROB == 0) {
|
||||
gdbm_delete(gdbm, key);
|
||||
} else if (rand() % STORE_PROB == 0) {
|
||||
if (gdbm_store(gdbm, key, data, GDBM_REPLACE) != 0) {
|
||||
fatal("gdbm_store failed");
|
||||
}
|
||||
} else {
|
||||
data = gdbm_fetch(gdbm, key);
|
||||
if (data.dptr) free(data.dptr);
|
||||
}
|
||||
|
||||
free(k);
|
||||
free(d);
|
||||
}
|
||||
|
||||
static int traverse_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
|
||||
{
|
||||
#if 0
|
||||
printf("[%s] [%s]\n", key.dptr, dbuf.dptr);
|
||||
#endif
|
||||
tdb_delete(tdb, key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void merge_test(void)
|
||||
{
|
||||
int i;
|
||||
char keys[5][2];
|
||||
char tdata[] = "test";
|
||||
TDB_DATA key, data;
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
snprintf(keys[i],2, "%d", i);
|
||||
key.dptr = keys[i];
|
||||
key.dsize = 2;
|
||||
|
||||
data.dptr = tdata;
|
||||
data.dsize = 4;
|
||||
|
||||
if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
|
||||
fatal("tdb_store failed");
|
||||
}
|
||||
}
|
||||
|
||||
key.dptr = keys[0];
|
||||
tdb_delete(db, key);
|
||||
key.dptr = keys[4];
|
||||
tdb_delete(db, key);
|
||||
key.dptr = keys[2];
|
||||
tdb_delete(db, key);
|
||||
key.dptr = keys[1];
|
||||
tdb_delete(db, key);
|
||||
key.dptr = keys[3];
|
||||
tdb_delete(db, key);
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int i, seed=0;
|
||||
int loops = 10000;
|
||||
int num_entries;
|
||||
char test_gdbm[] = "test.gdbm";
|
||||
|
||||
unlink("test.gdbm");
|
||||
|
||||
db = tdb_open("test.tdb", 0, TDB_CLEAR_IF_FIRST,
|
||||
O_RDWR | O_CREAT | O_TRUNC, 0600);
|
||||
gdbm = gdbm_open(test_gdbm, 512, GDBM_WRITER|GDBM_NEWDB|GDBM_FAST,
|
||||
0600, NULL);
|
||||
|
||||
if (!db || !gdbm) {
|
||||
fatal("db open failed");
|
||||
}
|
||||
|
||||
#if 1
|
||||
srand(seed);
|
||||
_start_timer();
|
||||
for (i=0;i<loops;i++) addrec_gdbm();
|
||||
printf("gdbm got %.2f ops/sec\n", i/_end_timer());
|
||||
#endif
|
||||
|
||||
merge_test();
|
||||
|
||||
srand(seed);
|
||||
_start_timer();
|
||||
for (i=0;i<loops;i++) addrec_db();
|
||||
printf("tdb got %.2f ops/sec\n", i/_end_timer());
|
||||
|
||||
if (tdb_validate_freelist(db, &num_entries) == -1) {
|
||||
printf("tdb freelist is corrupt\n");
|
||||
} else {
|
||||
printf("tdb freelist is good (%d entries)\n", num_entries);
|
||||
}
|
||||
|
||||
compare_db();
|
||||
|
||||
printf("traversed %d records\n", tdb_traverse(db, traverse_fn, NULL));
|
||||
printf("traversed %d records\n", tdb_traverse(db, traverse_fn, NULL));
|
||||
|
||||
tdb_close(db);
|
||||
gdbm_close(gdbm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,542 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Samba database functions
|
||||
Copyright (C) Andrew Tridgell 1999-2000
|
||||
Copyright (C) Paul `Rusty' Russell 2000
|
||||
Copyright (C) Jeremy Allison 2000
|
||||
Copyright (C) Andrew Esh 2001
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
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 "tdb.h"
|
||||
|
||||
/* a tdb tool for manipulating a tdb database */
|
||||
|
||||
#define FSTRING_LEN 256
|
||||
typedef char fstring[FSTRING_LEN];
|
||||
|
||||
typedef struct connections_key {
|
||||
pid_t pid;
|
||||
int cnum;
|
||||
fstring name;
|
||||
} connections_key;
|
||||
|
||||
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)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* We're probably printing ASCII strings so don't try to display
|
||||
the trailing NULL character. */
|
||||
|
||||
if (buf[len - 1] == 0)
|
||||
len--;
|
||||
|
||||
for (i=0;i<len;i++)
|
||||
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)
|
||||
{
|
||||
int i=0;
|
||||
if (len<=0) return;
|
||||
printf("[%03X] ",i);
|
||||
for (i=0;i<len;) {
|
||||
printf("%02X ",(int)buf[i]);
|
||||
i++;
|
||||
if (i%8 == 0) printf(" ");
|
||||
if (i%16 == 0) {
|
||||
print_asc(&buf[i-16],8); printf(" ");
|
||||
print_asc(&buf[i-8],8); printf("\n");
|
||||
if (i<len) printf("[%03X] ",i);
|
||||
}
|
||||
}
|
||||
if (i%16) {
|
||||
int n;
|
||||
|
||||
n = 16 - (i%16);
|
||||
printf(" ");
|
||||
if (n>8) printf(" ");
|
||||
while (n--) printf(" ");
|
||||
|
||||
n = i%16;
|
||||
if (n > 8) n = 8;
|
||||
print_asc(&buf[i-(i%16)],n); printf(" ");
|
||||
n = (i%16) - n;
|
||||
if (n>0) print_asc(&buf[i-n],n);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void help(void)
|
||||
{
|
||||
printf("\n"
|
||||
"tdbtool: \n"
|
||||
" create dbname : create a database\n"
|
||||
" open dbname : open an existing database\n"
|
||||
" erase : erase the database\n"
|
||||
" dump : dump the database as strings\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"
|
||||
" show key : show a record by key\n"
|
||||
" delete key : delete a record by key\n"
|
||||
" list : print the database hash table and freelist\n"
|
||||
" free : print the database freelist\n"
|
||||
" 1 | first : print the first record\n"
|
||||
" n | next : print the next record\n"
|
||||
" q | quit : terminate\n"
|
||||
" \\n : repeat 'next' command\n"
|
||||
"\n");
|
||||
}
|
||||
|
||||
static void terror(const char *why)
|
||||
{
|
||||
printf("%s\n", why);
|
||||
}
|
||||
|
||||
static char *get_token(int startover)
|
||||
{
|
||||
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);
|
||||
if (!tdb) {
|
||||
printf("Could not create %s: %s\n", tok, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void open_tdb(void)
|
||||
{
|
||||
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);
|
||||
if (!tdb) {
|
||||
printf("Could not open %s: %s\n", tok, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void insert_tdb(void)
|
||||
{
|
||||
char *k = get_token(1);
|
||||
char *d = get_token(0);
|
||||
TDB_DATA key, dbuf;
|
||||
|
||||
if (!k || !d) {
|
||||
help();
|
||||
return;
|
||||
}
|
||||
|
||||
key.dptr = (unsigned char *)k;
|
||||
key.dsize = strlen(k)+1;
|
||||
dbuf.dptr = (unsigned char *)d;
|
||||
dbuf.dsize = strlen(d)+1;
|
||||
|
||||
if (tdb_store(tdb, key, dbuf, TDB_INSERT) == -1) {
|
||||
terror("insert failed");
|
||||
}
|
||||
}
|
||||
|
||||
static void store_tdb(void)
|
||||
{
|
||||
char *k = get_token(1);
|
||||
char *d = get_token(0);
|
||||
TDB_DATA key, dbuf;
|
||||
|
||||
if (!k || !d) {
|
||||
help();
|
||||
return;
|
||||
}
|
||||
|
||||
key.dptr = (unsigned char *)k;
|
||||
key.dsize = strlen(k)+1;
|
||||
dbuf.dptr = (unsigned char *)d;
|
||||
dbuf.dsize = strlen(d)+1;
|
||||
|
||||
printf("Storing key:\n");
|
||||
print_rec(tdb, key, dbuf, NULL);
|
||||
|
||||
if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) {
|
||||
terror("store failed");
|
||||
}
|
||||
}
|
||||
|
||||
static void show_tdb(void)
|
||||
{
|
||||
char *k = get_token(1);
|
||||
TDB_DATA key, dbuf;
|
||||
|
||||
if (!k) {
|
||||
help();
|
||||
return;
|
||||
}
|
||||
|
||||
key.dptr = (unsigned char *)k;
|
||||
key.dsize = strlen(k)+1;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */
|
||||
print_rec(tdb, key, dbuf, NULL);
|
||||
|
||||
free( dbuf.dptr );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void delete_tdb(void)
|
||||
{
|
||||
char *k = get_token(1);
|
||||
TDB_DATA key;
|
||||
|
||||
if (!k) {
|
||||
help();
|
||||
return;
|
||||
}
|
||||
|
||||
key.dptr = (unsigned char *)k;
|
||||
key.dsize = strlen(k)+1;
|
||||
|
||||
if (tdb_delete(tdb, key) != 0) {
|
||||
terror("delete failed");
|
||||
}
|
||||
}
|
||||
|
||||
static void move_rec(void)
|
||||
{
|
||||
char *k = get_token(1);
|
||||
char *file = get_token(0);
|
||||
TDB_DATA key, dbuf;
|
||||
struct tdb_context *dst_tdb;
|
||||
|
||||
struct tdb_logging_context log_ctx;
|
||||
log_ctx.log_fn = tdb_log;
|
||||
|
||||
if (!k) {
|
||||
help();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !file ) {
|
||||
terror("need destination tdb name");
|
||||
return;
|
||||
}
|
||||
|
||||
key.dptr = (unsigned char *)k;
|
||||
key.dsize = strlen(k)+1;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
print_rec(tdb, key, dbuf, NULL);
|
||||
|
||||
dst_tdb = tdb_open_ex(file, 0, 0, O_RDWR, 0600, &log_ctx, NULL);
|
||||
if ( !dst_tdb ) {
|
||||
terror("unable to open destination tdb");
|
||||
return;
|
||||
}
|
||||
|
||||
if ( tdb_store( dst_tdb, key, dbuf, TDB_REPLACE ) == -1 ) {
|
||||
terror("failed to move record");
|
||||
}
|
||||
else
|
||||
printf("record moved\n");
|
||||
|
||||
tdb_close( dst_tdb );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int print_rec(struct 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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int print_key(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
|
||||
{
|
||||
print_asc(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)
|
||||
{
|
||||
total_bytes += dbuf.dsize;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void info_tdb(void)
|
||||
{
|
||||
int count;
|
||||
total_bytes = 0;
|
||||
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);
|
||||
}
|
||||
|
||||
static char *tdb_getline(const char *prompt)
|
||||
{
|
||||
static char line[1024];
|
||||
char *p;
|
||||
fputs(prompt, stdout);
|
||||
line[0] = 0;
|
||||
p = fgets(line, sizeof(line)-1, stdin);
|
||||
if (p) p = strchr(p, '\n');
|
||||
if (p) *p = 0;
|
||||
return p?line:NULL;
|
||||
}
|
||||
|
||||
static int do_delete_fn(struct 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)
|
||||
{
|
||||
TDB_DATA dbuf;
|
||||
*pkey = tdb_firstkey(the_tdb);
|
||||
|
||||
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)
|
||||
{
|
||||
TDB_DATA dbuf;
|
||||
*pkey = tdb_nextkey(the_tdb, *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);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int bIterate = 0;
|
||||
char *line;
|
||||
char *tok;
|
||||
TDB_DATA iterate_kbuf;
|
||||
|
||||
if (argv[1]) {
|
||||
static char tmp[1024];
|
||||
sprintf(tmp, "open %s", argv[1]);
|
||||
tok=strtok(tmp," ");
|
||||
open_tdb();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
if (tdb) tdb_close(tdb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
/* this tests tdb by doing lots of ops from several simultaneous
|
||||
writers - that stresses the locking code.
|
||||
*/
|
||||
|
||||
#include "replace.h"
|
||||
#include "tdb.h"
|
||||
#include "system/time.h"
|
||||
#include "system/wait.h"
|
||||
#include "system/filesys.h"
|
||||
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
|
||||
#define REOPEN_PROB 30
|
||||
#define DELETE_PROB 8
|
||||
#define STORE_PROB 4
|
||||
#define APPEND_PROB 6
|
||||
#define TRANSACTION_PROB 10
|
||||
#define LOCKSTORE_PROB 5
|
||||
#define TRAVERSE_PROB 20
|
||||
#define TRAVERSE_READ_PROB 20
|
||||
#define CULL_PROB 100
|
||||
#define KEYLEN 3
|
||||
#define DATALEN 100
|
||||
|
||||
static struct tdb_context *db;
|
||||
static int in_transaction;
|
||||
static int error_count;
|
||||
|
||||
#ifdef PRINTF_ATTRIBUTE
|
||||
static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
|
||||
#endif
|
||||
static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
error_count++;
|
||||
|
||||
va_start(ap, format);
|
||||
vfprintf(stdout, format, ap);
|
||||
va_end(ap);
|
||||
fflush(stdout);
|
||||
#if 0
|
||||
{
|
||||
char *ptr;
|
||||
asprintf(&ptr,"xterm -e gdb /proc/%d/exe %d", getpid(), getpid());
|
||||
system(ptr);
|
||||
free(ptr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void fatal(const char *why)
|
||||
{
|
||||
perror(why);
|
||||
error_count++;
|
||||
}
|
||||
|
||||
static char *randbuf(int len)
|
||||
{
|
||||
char *buf;
|
||||
int i;
|
||||
buf = (char *)malloc(len+1);
|
||||
|
||||
for (i=0;i<len;i++) {
|
||||
buf[i] = 'a' + (rand() % 26);
|
||||
}
|
||||
buf[i] = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int cull_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
|
||||
void *state)
|
||||
{
|
||||
#if CULL_PROB
|
||||
if (random() % CULL_PROB == 0) {
|
||||
tdb_delete(tdb, key);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void addrec_db(void)
|
||||
{
|
||||
int klen, dlen;
|
||||
char *k, *d;
|
||||
TDB_DATA key, data;
|
||||
|
||||
klen = 1 + (rand() % KEYLEN);
|
||||
dlen = 1 + (rand() % DATALEN);
|
||||
|
||||
k = randbuf(klen);
|
||||
d = randbuf(dlen);
|
||||
|
||||
key.dptr = (unsigned char *)k;
|
||||
key.dsize = klen+1;
|
||||
|
||||
data.dptr = (unsigned char *)d;
|
||||
data.dsize = dlen+1;
|
||||
|
||||
#if TRANSACTION_PROB
|
||||
if (in_transaction == 0 && random() % TRANSACTION_PROB == 0) {
|
||||
if (tdb_transaction_start(db) != 0) {
|
||||
fatal("tdb_transaction_start failed");
|
||||
}
|
||||
in_transaction++;
|
||||
goto next;
|
||||
}
|
||||
if (in_transaction && random() % TRANSACTION_PROB == 0) {
|
||||
if (tdb_transaction_commit(db) != 0) {
|
||||
fatal("tdb_transaction_commit failed");
|
||||
}
|
||||
in_transaction--;
|
||||
goto next;
|
||||
}
|
||||
if (in_transaction && random() % TRANSACTION_PROB == 0) {
|
||||
if (tdb_transaction_cancel(db) != 0) {
|
||||
fatal("tdb_transaction_cancel failed");
|
||||
}
|
||||
in_transaction--;
|
||||
goto next;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if REOPEN_PROB
|
||||
if (in_transaction == 0 && random() % REOPEN_PROB == 0) {
|
||||
tdb_reopen_all(0);
|
||||
goto next;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DELETE_PROB
|
||||
if (random() % DELETE_PROB == 0) {
|
||||
tdb_delete(db, key);
|
||||
goto next;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STORE_PROB
|
||||
if (random() % STORE_PROB == 0) {
|
||||
if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
|
||||
fatal("tdb_store failed");
|
||||
}
|
||||
goto next;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if APPEND_PROB
|
||||
if (random() % APPEND_PROB == 0) {
|
||||
if (tdb_append(db, key, data) != 0) {
|
||||
fatal("tdb_append failed");
|
||||
}
|
||||
goto next;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LOCKSTORE_PROB
|
||||
if (random() % LOCKSTORE_PROB == 0) {
|
||||
tdb_chainlock(db, key);
|
||||
data = tdb_fetch(db, key);
|
||||
if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
|
||||
fatal("tdb_store failed");
|
||||
}
|
||||
if (data.dptr) free(data.dptr);
|
||||
tdb_chainunlock(db, key);
|
||||
goto next;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TRAVERSE_PROB
|
||||
if (random() % TRAVERSE_PROB == 0) {
|
||||
tdb_traverse(db, cull_traverse, NULL);
|
||||
goto next;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TRAVERSE_READ_PROB
|
||||
if (random() % TRAVERSE_READ_PROB == 0) {
|
||||
tdb_traverse_read(db, NULL, NULL);
|
||||
goto next;
|
||||
}
|
||||
#endif
|
||||
|
||||
data = tdb_fetch(db, key);
|
||||
if (data.dptr) free(data.dptr);
|
||||
|
||||
next:
|
||||
free(k);
|
||||
free(d);
|
||||
}
|
||||
|
||||
static int traverse_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
|
||||
void *state)
|
||||
{
|
||||
tdb_delete(tdb, key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
printf("Usage: tdbtorture [-n NUM_PROCS] [-l NUM_LOOPS] [-s SEED] [-H HASH_SIZE]\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int argc, char * const *argv)
|
||||
{
|
||||
int i, seed = -1;
|
||||
int num_procs = 3;
|
||||
int num_loops = 5000;
|
||||
int hash_size = 2;
|
||||
int c;
|
||||
extern char *optarg;
|
||||
pid_t *pids;
|
||||
|
||||
struct tdb_logging_context log_ctx;
|
||||
log_ctx.log_fn = tdb_log;
|
||||
|
||||
while ((c = getopt(argc, argv, "n:l:s:H:h")) != -1) {
|
||||
switch (c) {
|
||||
case 'n':
|
||||
num_procs = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'l':
|
||||
num_loops = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'H':
|
||||
hash_size = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 's':
|
||||
seed = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
unlink("torture.tdb");
|
||||
|
||||
pids = calloc(sizeof(pid_t), num_procs);
|
||||
pids[0] = getpid();
|
||||
|
||||
for (i=0;i<num_procs-1;i++) {
|
||||
if ((pids[i+1]=fork()) == 0) break;
|
||||
}
|
||||
|
||||
db = tdb_open_ex("torture.tdb", hash_size, TDB_CLEAR_IF_FIRST,
|
||||
O_RDWR | O_CREAT, 0600, &log_ctx, NULL);
|
||||
if (!db) {
|
||||
fatal("db open failed");
|
||||
}
|
||||
|
||||
if (seed == -1) {
|
||||
seed = (getpid() + time(NULL)) & 0x7FFFFFFF;
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
printf("testing with %d processes, %d loops, %d hash_size, seed=%d\n",
|
||||
num_procs, num_loops, hash_size, seed);
|
||||
}
|
||||
|
||||
srand(seed + i);
|
||||
srandom(seed + i);
|
||||
|
||||
for (i=0;i<num_loops && error_count == 0;i++) {
|
||||
addrec_db();
|
||||
}
|
||||
|
||||
if (error_count == 0) {
|
||||
tdb_traverse_read(db, NULL, NULL);
|
||||
tdb_traverse(db, traverse_fn, NULL);
|
||||
tdb_traverse(db, traverse_fn, NULL);
|
||||
}
|
||||
|
||||
tdb_close(db);
|
||||
|
||||
if (getpid() != pids[0]) {
|
||||
return error_count;
|
||||
}
|
||||
|
||||
for (i=1;i<num_procs;i++) {
|
||||
int status, j;
|
||||
pid_t pid;
|
||||
if (error_count != 0) {
|
||||
/* try and stop the test on any failure */
|
||||
for (j=1;j<num_procs;j++) {
|
||||
if (pids[j] != 0) {
|
||||
kill(pids[j], SIGTERM);
|
||||
}
|
||||
}
|
||||
}
|
||||
pid = waitpid(-1, &status, 0);
|
||||
if (pid == -1) {
|
||||
perror("failed to wait for child\n");
|
||||
exit(1);
|
||||
}
|
||||
for (j=1;j<num_procs;j++) {
|
||||
if (pids[j] == pid) break;
|
||||
}
|
||||
if (j == num_procs) {
|
||||
printf("unknown child %d exited!?\n", (int)pid);
|
||||
exit(1);
|
||||
}
|
||||
if (WEXITSTATUS(status) != 0) {
|
||||
printf("child %d exited with status %d\n",
|
||||
(int)pid, WEXITSTATUS(status));
|
||||
error_count++;
|
||||
}
|
||||
pids[j] = 0;
|
||||
}
|
||||
|
||||
if (error_count == 0) {
|
||||
printf("OK\n");
|
||||
}
|
||||
|
||||
return error_count;
|
||||
}
|
||||
Reference in New Issue
Block a user