2011-04-14 14:51:59 +04:00
/*
* ctdb local tdb tool
*
* Copyright ( C ) Gregor Beck 2011
* Copyright ( C ) Michael Adam 2011
*
* 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 3 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 , see < http : //www.gnu.org/licenses/>.
*/
2015-10-28 11:52:02 +03:00
# include "replace.h"
# include "system/filesys.h"
# include "system/network.h"
# include "system/locale.h"
2011-04-14 14:51:59 +04:00
# include <tdb.h>
2015-10-28 11:52:02 +03:00
# include "protocol/protocol.h"
2011-04-14 14:51:59 +04:00
enum {
MAX_HEADER_SIZE = 24 ,
OUT_MODE = S_IRUSR | S_IWUSR ,
OUT_FLAGS = O_EXCL | O_CREAT | O_RDWR ,
} ;
union ltdb_header {
struct ctdb_ltdb_header hdr ;
uint32_t uints [ MAX_HEADER_SIZE / 4 ] ;
} ;
static const union ltdb_header DEFAULT_HDR = {
2019-01-14 14:07:40 +03:00
. hdr = {
. dmaster = - 1 ,
}
2011-04-14 14:51:59 +04:00
} ;
static int help ( const char * cmd )
{
fprintf ( stdout , " "
" Usage: %s [options] <command> \n "
" \n "
" Options: \n "
" -s {0|32|64} specify how to determine the ctdb record header size \n "
" for the input database: \n "
" 0: no ctdb header \n "
" 32: ctdb header size of a 32 bit system (20 bytes) \n "
" 64: ctdb header size of a 64 bit system (24 bytes) \n "
" default: 32 or 64 depending on the system architecture \n "
" \n "
" -S <num> the number of bytes to interpret as ctdb record header \n "
" for the input database (beware!) \n "
" \n "
" -o {0|32|64} specify how to determine the ctdb record header size \n "
" for the output database \n "
" 0: no ctdb header \n "
" 32: ctdb header size of a 32 bit system (20 bytes) \n "
" 64: ctdb header size of a 64 bit system (24 bytes) \n "
" default: 32 or 64 depending on the system architecture \n "
" \n "
" -O <num> the number of bytes to interpret as ctdb record header \n "
" for the output database (beware!) \n "
" \n "
2013-03-27 05:32:43 +04:00
" -e Include empty records, defaults to off \n "
" \n "
" -p print header (for the dump command), defaults to off \n "
2011-04-14 14:51:59 +04:00
" \n "
" -h print this help \n "
" \n "
" Commands: \n "
" help print this help \n "
" dump <db> dump the db to stdout \n "
" convert <in_db> <out_db> convert the db \n \n " , cmd ) ;
return 0 ;
}
static int usage ( const char * cmd )
{
fprintf ( stderr ,
2013-03-27 05:32:43 +04:00
" Usage: %s dump [-e] [-p] [-s{0|32|64}] <idb> \n "
" %s convert [-e] [-s{0|32|64}] [-o{0|32|64}] <idb> <odb> \n "
2011-04-14 14:51:59 +04:00
" %s {help|-h} \n "
, cmd , cmd , cmd ) ;
return - 1 ;
}
static int
ltdb_traverse ( TDB_CONTEXT * tdb , int ( * fn ) ( TDB_CONTEXT * , TDB_DATA , TDB_DATA ,
struct ctdb_ltdb_header * , void * ) ,
2019-05-28 03:57:49 +03:00
void * state , size_t hsize , bool skip_empty ) ;
2011-04-14 14:51:59 +04:00
struct write_record_ctx {
TDB_CONTEXT * tdb ;
size_t hsize ;
int tdb_store_flags ;
} ;
static int
write_record ( TDB_CONTEXT * tdb , TDB_DATA key , TDB_DATA val ,
struct ctdb_ltdb_header * hdr ,
void * write_record_ctx ) ;
struct dump_record_ctx {
FILE * file ;
void ( * print_data ) ( FILE * , TDB_DATA ) ;
void ( * dump_header ) ( struct dump_record_ctx * , struct ctdb_ltdb_header * ) ;
} ;
static int dump_record ( TDB_CONTEXT * tdb , TDB_DATA key , TDB_DATA val ,
struct ctdb_ltdb_header * hdr ,
void * dump_record_ctx ) ;
static void print_data_tdbdump ( FILE * file , TDB_DATA data ) ;
static void dump_header_full ( struct dump_record_ctx * , struct ctdb_ltdb_header * ) ;
static void dump_header_nop ( struct dump_record_ctx * c ,
struct ctdb_ltdb_header * h )
{ }
2019-05-28 03:57:49 +03:00
static int dump_db ( const char * iname ,
FILE * ofile ,
size_t hsize ,
bool dump_header ,
2011-09-27 13:41:29 +04:00
bool empty )
2011-04-14 14:51:59 +04:00
{
int ret = - 1 ;
TDB_CONTEXT * idb = tdb_open ( iname , 0 , TDB_DEFAULT , O_RDONLY , 0 ) ;
if ( ! idb ) {
perror ( " tdbopen in " ) ;
} else {
struct dump_record_ctx dump_ctx = {
. file = ofile ,
. print_data = & print_data_tdbdump ,
. dump_header = dump_header ? & dump_header_full
: & dump_header_nop ,
} ;
2011-09-27 13:41:29 +04:00
ret = ltdb_traverse ( idb , & dump_record , & dump_ctx , hsize , ! empty ) ;
2011-04-14 14:51:59 +04:00
tdb_close ( idb ) ;
}
return ret ;
}
2011-09-27 13:41:29 +04:00
static int conv_db ( const char * iname , const char * oname , size_t isize ,
size_t osize , bool keep_empty )
2011-04-14 14:51:59 +04:00
{
int ret = - 1 ;
TDB_CONTEXT * idb = tdb_open ( iname , 0 , TDB_DEFAULT , O_RDONLY , 0 ) ;
if ( ! idb ) {
perror ( " tdbopen in " ) ;
} else {
TDB_CONTEXT * odb = tdb_open ( oname , 0 , TDB_DEFAULT , OUT_FLAGS , OUT_MODE ) ;
if ( ! odb ) {
perror ( " tdbopen out " ) ;
} else {
struct write_record_ctx ctx = {
. tdb = odb ,
. hsize = osize ,
. tdb_store_flags = TDB_REPLACE ,
} ;
2011-09-27 13:41:29 +04:00
ret = ltdb_traverse ( idb , & write_record , & ctx , isize , ! keep_empty ) ;
2011-04-14 14:51:59 +04:00
tdb_close ( odb ) ;
}
tdb_close ( idb ) ;
}
return ret ;
}
static bool parse_size ( size_t * size , const char * arg , bool raw ) {
long val ;
errno = 0 ;
val = strtol ( arg , ( char * * ) NULL , 10 ) ;
if ( errno ! = 0 ) {
return false ;
}
if ( ! raw ) {
switch ( val ) {
case 0 :
break ;
case 32 :
val = 20 ;
break ;
case 64 :
val = 24 ;
break ;
default :
return false ;
}
}
* size = MIN ( val , MAX_HEADER_SIZE ) ;
return true ;
}
int main ( int argc , char * argv [ ] )
{
size_t isize = sizeof ( struct ctdb_ltdb_header ) ;
size_t osize = sizeof ( struct ctdb_ltdb_header ) ;
bool print_header = false ;
2011-09-27 13:41:29 +04:00
bool keep_empty = false ;
2011-04-14 14:51:59 +04:00
int opt ;
const char * cmd , * idb , * odb ;
2013-09-23 10:23:36 +04:00
while ( ( opt = getopt ( argc , argv , " s:o:S:O:phe " ) ) ! = - 1 ) {
2011-04-14 14:51:59 +04:00
switch ( opt ) {
case ' s ' :
case ' S ' :
if ( ! parse_size ( & isize , optarg , isupper ( opt ) ) ) {
return usage ( argv [ 0 ] ) ;
}
break ;
case ' o ' :
case ' O ' :
if ( ! parse_size ( & osize , optarg , isupper ( opt ) ) ) {
return usage ( argv [ 0 ] ) ;
}
break ;
case ' p ' :
print_header = true ;
break ;
2011-09-27 13:41:29 +04:00
case ' e ' :
keep_empty = true ;
2013-03-27 05:32:43 +04:00
break ;
2011-04-14 14:51:59 +04:00
case ' h ' :
return help ( argv [ 0 ] ) ;
default :
return usage ( argv [ 0 ] ) ;
}
}
if ( argc - optind < 1 ) {
return usage ( argv [ 0 ] ) ;
}
cmd = argv [ optind ] ;
if ( strcmp ( cmd , " help " ) = = 0 ) {
return help ( argv [ 0 ] ) ;
}
else if ( strcmp ( cmd , " dump " ) = = 0 ) {
int ret ;
if ( argc - optind ! = 2 ) {
return usage ( argv [ 0 ] ) ;
}
idb = argv [ optind + 1 ] ;
2011-09-27 13:41:29 +04:00
ret = dump_db ( idb , stdout , isize , print_header , keep_empty ) ;
2011-04-14 14:51:59 +04:00
return ( ret > = 0 ) ? 0 : ret ;
}
else if ( strcmp ( cmd , " convert " ) = = 0 ) {
int ret ;
if ( argc - optind ! = 3 ) {
return usage ( argv [ 0 ] ) ;
}
idb = argv [ optind + 1 ] ;
odb = argv [ optind + 2 ] ;
2011-09-27 13:41:29 +04:00
ret = conv_db ( idb , odb , isize , osize , keep_empty ) ;
2011-04-14 14:51:59 +04:00
return ( ret > = 0 ) ? 0 : ret ;
}
return usage ( argv [ 0 ] ) ;
}
struct ltdb_traverse_ctx {
int ( * fn ) ( TDB_CONTEXT * , TDB_DATA , TDB_DATA , struct ctdb_ltdb_header * , void * ) ;
void * state ;
size_t hsize ;
2011-09-27 13:41:29 +04:00
bool skip_empty ;
2019-05-28 03:55:19 +03:00
int nempty ;
2011-04-14 14:51:59 +04:00
} ;
static int
ltdb_traverse_fn ( TDB_CONTEXT * tdb , TDB_DATA key , TDB_DATA val ,
void * ltdb_traverse_ctx )
{
struct ltdb_traverse_ctx * ctx =
( struct ltdb_traverse_ctx * ) ltdb_traverse_ctx ;
union ltdb_header hdr = DEFAULT_HDR ;
const size_t hsize = MIN ( sizeof ( hdr ) , ctx - > hsize ) ;
if ( val . dsize < hsize ) {
fprintf ( stderr , " Value too short to contain a ctdb header: " ) ;
print_data_tdbdump ( stderr , key ) ;
fprintf ( stderr , " = " ) ;
print_data_tdbdump ( stderr , val ) ;
fputc ( ' \n ' , stderr ) ;
return - 1 ;
}
2011-09-27 13:41:29 +04:00
if ( val . dsize = = hsize & & ctx - > skip_empty ) {
ctx - > nempty + + ;
return 0 ;
}
2011-04-14 14:51:59 +04:00
memcpy ( & hdr , val . dptr , hsize ) ;
if ( hdr . uints [ 5 ] ! = 0 ) {
fprintf ( stderr , " Warning: header padding isn't zero! Wrong header size? \n " ) ;
}
val . dptr + = ctx - > hsize ;
val . dsize - = ctx - > hsize ;
return ctx - > fn ( tdb , key , val , & hdr . hdr , ctx - > state ) ;
}
2016-04-26 22:15:20 +03:00
static int ltdb_traverse ( TDB_CONTEXT * tdb ,
int ( * fn ) ( TDB_CONTEXT * , TDB_DATA , TDB_DATA ,
struct ctdb_ltdb_header * , void * ) ,
2019-05-28 03:57:49 +03:00
void * state , size_t hsize , bool skip_empty )
2011-04-14 14:51:59 +04:00
{
struct ltdb_traverse_ctx ctx = {
. fn = fn ,
. state = state ,
2019-05-28 03:57:49 +03:00
. hsize = hsize ,
2011-09-27 13:41:29 +04:00
. skip_empty = skip_empty ,
. nempty = 0 ,
2011-04-14 14:51:59 +04:00
} ;
2011-09-27 13:41:29 +04:00
int ret = tdb_traverse ( tdb , & ltdb_traverse_fn , & ctx ) ;
return ( ret < 0 ) ? ret : ( ret - ctx . nempty ) ;
2011-04-14 14:51:59 +04:00
}
2016-04-26 22:15:20 +03:00
static int write_record ( TDB_CONTEXT * tdb , TDB_DATA key , TDB_DATA val ,
struct ctdb_ltdb_header * hdr ,
void * write_record_ctx )
2011-04-14 14:51:59 +04:00
{
struct write_record_ctx * ctx
= ( struct write_record_ctx * ) write_record_ctx ;
2016-10-12 07:46:17 +03:00
int ret ;
2011-04-14 14:51:59 +04:00
if ( ctx - > hsize = = 0 ) {
2016-10-12 07:46:17 +03:00
ret = tdb_store ( ctx - > tdb , key , val , ctx - > tdb_store_flags ) ;
2011-04-14 14:51:59 +04:00
} else {
2016-10-12 07:41:25 +03:00
TDB_DATA rec [ 2 ] ;
rec [ 0 ] . dsize = ctx - > hsize ;
rec [ 0 ] . dptr = ( uint8_t * ) hdr ;
rec [ 1 ] . dsize = val . dsize ;
rec [ 1 ] . dptr = val . dptr ;
2016-10-12 07:46:17 +03:00
ret = tdb_storev ( ctx - > tdb , key , rec , 2 , ctx - > tdb_store_flags ) ;
}
if ( ret = = - 1 ) {
fprintf ( stderr , " tdb_store: %s \n " , tdb_errorstr ( ctx - > tdb ) ) ;
return - 1 ;
2011-04-14 14:51:59 +04:00
}
2016-10-12 07:46:17 +03:00
2011-04-14 14:51:59 +04:00
return 0 ;
}
2016-04-26 22:15:20 +03:00
static int dump_record ( TDB_CONTEXT * tdb , TDB_DATA key , TDB_DATA val ,
struct ctdb_ltdb_header * hdr ,
void * dump_record_ctx )
2011-04-14 14:51:59 +04:00
{
struct dump_record_ctx * ctx = ( struct dump_record_ctx * ) dump_record_ctx ;
fprintf ( ctx - > file , " { \n key(%d) = " , ( int ) key . dsize ) ;
ctx - > print_data ( ctx - > file , key ) ;
fputc ( ' \n ' , ctx - > file ) ;
ctx - > dump_header ( ctx , hdr ) ;
fprintf ( ctx - > file , " data(%d) = " , ( int ) val . dsize ) ;
ctx - > print_data ( ctx - > file , val ) ;
fprintf ( ctx - > file , " \n } \n " ) ;
return 0 ;
}
2016-04-26 22:15:20 +03:00
static void dump_header_full ( struct dump_record_ctx * c ,
struct ctdb_ltdb_header * h )
2011-04-14 14:51:59 +04:00
{
fprintf ( c - > file , " dmaster: %d \n rsn: %llu \n flags: 0x%X \n " ,
( int ) h - > dmaster ,
( unsigned long long ) h - > rsn , h - > flags ) ;
}
2016-04-26 22:15:20 +03:00
static void print_data_tdbdump ( FILE * file , TDB_DATA data )
{
2011-04-14 14:51:59 +04:00
unsigned char * ptr = data . dptr ;
fputc ( ' " ' , file ) ;
while ( data . dsize - - ) {
if ( isprint ( * ptr ) & & ! strchr ( " \" \\ " , * ptr ) ) {
fputc ( * ptr , file ) ;
} else {
fprintf ( file , " \\ %02X " , * ptr ) ;
}
ptr + + ;
}
fputc ( ' " ' , file ) ;
}