2006-11-28 09:56:10 +03:00
/*
ctdb utility code
Copyright ( C ) Andrew Tridgell 2006
2007-05-31 07:50:53 +04:00
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
2007-07-10 09:29:31 +04:00
the Free Software Foundation ; either version 3 of the License , or
2007-05-31 07:50:53 +04:00
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
2006-11-28 09:56:10 +03:00
but WITHOUT ANY WARRANTY ; without even the implied warranty of
2007-05-31 07:50:53 +04:00
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
2007-07-10 09:29:31 +04:00
along with this program ; if not , see < http : //www.gnu.org/licenses/>.
2006-11-28 09:56:10 +03:00
*/
# include "includes.h"
2013-06-06 23:58:02 +04:00
# include "tdb.h"
2006-11-28 09:56:10 +03:00
# include "system/network.h"
# include "system/filesys.h"
2008-01-10 01:43:14 +03:00
# include "system/wait.h"
2007-04-16 04:21:44 +04:00
# include "../include/ctdb_private.h"
2006-11-28 09:56:10 +03:00
/*
return error string for last error
*/
const char * ctdb_errstr ( struct ctdb_context * ctdb )
{
return ctdb - > err_msg ;
}
/*
remember an error message
*/
void ctdb_set_error ( struct ctdb_context * ctdb , const char * fmt , . . . )
{
va_list ap ;
talloc_free ( ctdb - > err_msg ) ;
va_start ( ap , fmt ) ;
ctdb - > err_msg = talloc_vasprintf ( ctdb , fmt , ap ) ;
2008-02-04 12:07:15 +03:00
DEBUG ( DEBUG_ERR , ( " ctdb error: %s \n " , ctdb - > err_msg ) ) ;
2006-11-28 09:56:10 +03:00
va_end ( ap ) ;
}
2006-12-18 08:01:11 +03:00
/*
a fatal internal error occurred - no hope for recovery
*/
void ctdb_fatal ( struct ctdb_context * ctdb , const char * msg )
{
2008-02-04 09:44:24 +03:00
DEBUG ( DEBUG_ALERT , ( " ctdb fatal error: %s \n " , msg ) ) ;
2006-12-18 08:01:11 +03:00
abort ( ) ;
}
2013-06-30 11:42:11 +04:00
/*
like ctdb_fatal ( ) but a core / backtrace would not be useful
*/
void ctdb_die ( struct ctdb_context * ctdb , const char * msg )
{
DEBUG ( DEBUG_ALERT , ( " ctdb exiting with error: %s \n " , msg ) ) ;
exit ( 1 ) ;
}
2012-09-06 14:22:38 +04:00
/* Invoke an external program to do some sort of tracing on the CTDB
* process . This might block for a little while . The external
* program is specified by the environment variable
* CTDB_EXTERNAL_TRACE . This program should take one argument : the
* pid of the process to trace . Commonly , the program would be a
* wrapper script around gcore .
*/
void ctdb_external_trace ( void )
{
2014-07-30 15:10:01 +04:00
int ret ;
2012-09-06 14:22:38 +04:00
const char * t = getenv ( " CTDB_EXTERNAL_TRACE " ) ;
char * cmd ;
if ( t = = NULL ) {
return ;
}
cmd = talloc_asprintf ( NULL , " %s %lu " , t , ( unsigned long ) getpid ( ) ) ;
DEBUG ( DEBUG_WARNING , ( " begin external trace: %s \n " , cmd ) ) ;
2014-07-30 15:10:01 +04:00
ret = system ( cmd ) ;
if ( ret = = - 1 ) {
DEBUG ( DEBUG_ERR ,
( " external trace command \" %s \" failed \n " , cmd ) ) ;
}
2012-09-06 14:22:38 +04:00
DEBUG ( DEBUG_WARNING , ( " end external trace: %s \n " , cmd ) ) ;
talloc_free ( cmd ) ;
}
2006-11-28 09:56:10 +03:00
/*
parse a IP : port pair
*/
int ctdb_parse_address ( struct ctdb_context * ctdb ,
TALLOC_CTX * mem_ctx , const char * str ,
struct ctdb_address * address )
{
2007-05-29 09:15:00 +04:00
struct servent * se ;
2014-11-25 04:38:23 +03:00
ctdb_sock_addr addr ;
2007-05-29 09:15:00 +04:00
setservent ( 0 ) ;
se = getservbyname ( " ctdb " , " tcp " ) ;
endservent ( ) ;
2014-11-25 04:38:23 +03:00
/* Parse IP address and re-convert to string. This ensure correct
* string form for IPv6 addresses .
*/
if ( ! parse_ip ( str , NULL , 0 , & addr ) ) {
return - 1 ;
}
address - > address = talloc_strdup ( mem_ctx , ctdb_addr_to_str ( & addr ) ) ;
2009-05-21 05:49:16 +04:00
CTDB_NO_MEMORY ( ctdb , address - > address ) ;
2007-05-29 09:15:00 +04:00
if ( se = = NULL ) {
address - > port = CTDB_PORT ;
} else {
address - > port = ntohs ( se - > s_port ) ;
}
2006-11-28 09:56:10 +03:00
return 0 ;
}
/*
check if two addresses are the same
*/
bool ctdb_same_address ( struct ctdb_address * a1 , struct ctdb_address * a2 )
{
return strcmp ( a1 - > address , a2 - > address ) = = 0 & & a1 - > port = = a2 - > port ;
}
/*
hash function for mapping data to a VNN - taken from tdb
*/
2006-12-18 08:01:11 +03:00
uint32_t ctdb_hash ( const TDB_DATA * key )
2006-11-28 09:56:10 +03:00
{
2010-10-08 06:14:14 +04:00
return tdb_jenkins_hash ( discard_const ( key ) ) ;
2006-11-28 09:56:10 +03:00
}
2007-04-18 12:39:02 +04:00
2007-04-19 05:28:01 +04:00
/*
a type checking varient of idr_find
*/
2007-04-23 12:19:50 +04:00
static void * _idr_find_type ( struct idr_context * idp , int id , const char * type , const char * location )
2007-04-19 05:28:01 +04:00
{
void * p = idr_find ( idp , id ) ;
if ( p & & talloc_check_name ( p , type ) = = NULL ) {
2008-02-04 12:07:15 +03:00
DEBUG ( DEBUG_ERR , ( " %s idr_find_type expected type %s but got %s \n " ,
2007-04-19 05:28:01 +04:00
location , type , talloc_get_name ( p ) ) ) ;
return NULL ;
}
return p ;
}
2007-04-23 12:19:50 +04:00
uint32_t ctdb_reqid_new ( struct ctdb_context * ctdb , void * state )
{
2010-06-10 03:28:55 +04:00
int id = idr_get_new_above ( ctdb - > idr , state , ctdb - > lastid + 1 , INT_MAX ) ;
if ( id < 0 ) {
2010-10-28 06:32:29 +04:00
DEBUG ( DEBUG_DEBUG , ( " Reqid wrap! \n " ) ) ;
2010-06-10 03:28:55 +04:00
id = idr_get_new ( ctdb - > idr , state , INT_MAX ) ;
}
ctdb - > lastid = id ;
return id ;
2007-04-23 12:19:50 +04:00
}
void * _ctdb_reqid_find ( struct ctdb_context * ctdb , uint32_t reqid , const char * type , const char * location )
{
void * p ;
2010-05-08 16:54:11 +04:00
p = _idr_find_type ( ctdb - > idr , reqid , type , location ) ;
2007-04-23 12:19:50 +04:00
if ( p = = NULL ) {
2008-09-09 07:59:48 +04:00
DEBUG ( DEBUG_WARNING , ( " Could not find idr:%u \n " , reqid ) ) ;
2007-04-23 12:19:50 +04:00
}
return p ;
}
void ctdb_reqid_remove ( struct ctdb_context * ctdb , uint32_t reqid )
{
int ret ;
2010-05-08 16:54:11 +04:00
ret = idr_remove ( ctdb - > idr , reqid ) ;
2007-04-23 12:19:50 +04:00
if ( ret ! = 0 ) {
2008-02-04 12:07:15 +03:00
DEBUG ( DEBUG_ERR , ( " Removing idr that does not exist \n " ) ) ;
2007-04-23 12:19:50 +04:00
}
}
2007-05-10 11:43:45 +04:00
2014-07-22 15:23:03 +04:00
static uint32_t ctdb_marshall_record_size ( TDB_DATA key ,
struct ctdb_ltdb_header * header ,
TDB_DATA data )
{
return offsetof ( struct ctdb_rec_data , data ) + key . dsize +
data . dsize + ( header ? sizeof ( * header ) : 0 ) ;
}
static void ctdb_marshall_record_copy ( struct ctdb_rec_data * rec ,
uint32_t reqid ,
TDB_DATA key ,
struct ctdb_ltdb_header * header ,
TDB_DATA data ,
uint32_t length )
{
uint32_t offset ;
rec - > length = length ;
rec - > reqid = reqid ;
rec - > keylen = key . dsize ;
memcpy ( & rec - > data [ 0 ] , key . dptr , key . dsize ) ;
offset = key . dsize ;
if ( header ) {
rec - > datalen = data . dsize + sizeof ( * header ) ;
memcpy ( & rec - > data [ offset ] , header , sizeof ( * header ) ) ;
offset + = sizeof ( * header ) ;
} else {
rec - > datalen = data . dsize ;
}
memcpy ( & rec - > data [ offset ] , data . dptr , data . dsize ) ;
}
2007-05-10 11:43:45 +04:00
/*
form a ctdb_rec_data record from a key / data pair
2007-09-21 06:24:02 +04:00
note that header may be NULL . If not NULL then it is included in the data portion
of the record
2007-05-10 11:43:45 +04:00
*/
2014-07-22 15:22:25 +04:00
struct ctdb_rec_data * ctdb_marshall_record ( TALLOC_CTX * mem_ctx , uint32_t reqid ,
TDB_DATA key ,
2007-09-21 06:24:02 +04:00
struct ctdb_ltdb_header * header ,
TDB_DATA data )
2007-05-10 11:43:45 +04:00
{
size_t length ;
struct ctdb_rec_data * d ;
2014-07-22 15:23:03 +04:00
length = ctdb_marshall_record_size ( key , header , data ) ;
2007-05-10 11:43:45 +04:00
d = ( struct ctdb_rec_data * ) talloc_size ( mem_ctx , length ) ;
if ( d = = NULL ) {
return NULL ;
}
2014-07-22 15:23:03 +04:00
ctdb_marshall_record_copy ( d , reqid , key , header , data , length ) ;
2007-05-10 11:43:45 +04:00
return d ;
}
2008-07-30 13:58:17 +04:00
/* helper function for marshalling multiple records */
2014-07-22 15:22:25 +04:00
struct ctdb_marshall_buffer * ctdb_marshall_add ( TALLOC_CTX * mem_ctx ,
2008-07-30 13:58:17 +04:00
struct ctdb_marshall_buffer * m ,
uint64_t db_id ,
uint32_t reqid ,
TDB_DATA key ,
struct ctdb_ltdb_header * header ,
TDB_DATA data )
{
struct ctdb_rec_data * r ;
struct ctdb_marshall_buffer * m2 ;
2014-05-06 12:26:41 +04:00
uint32_t length , offset ;
2008-07-30 13:58:17 +04:00
2014-05-06 12:26:41 +04:00
length = ctdb_marshall_record_size ( key , header , data ) ;
2008-07-30 13:58:17 +04:00
if ( m = = NULL ) {
2014-05-06 12:26:41 +04:00
offset = offsetof ( struct ctdb_marshall_buffer , data ) ;
m2 = talloc_zero_size ( mem_ctx , offset + length ) ;
} else {
offset = talloc_get_size ( m ) ;
m2 = talloc_realloc_size ( mem_ctx , m , offset + length ) ;
2008-07-30 13:58:17 +04:00
}
if ( m2 = = NULL ) {
2014-05-06 12:26:41 +04:00
TALLOC_FREE ( m ) ;
2008-07-30 13:58:17 +04:00
return NULL ;
}
2014-05-06 12:26:41 +04:00
if ( m = = NULL ) {
m2 - > db_id = db_id ;
}
2008-07-30 13:58:17 +04:00
2014-05-06 12:26:41 +04:00
r = ( struct ctdb_rec_data * ) ( ( uint8_t * ) m2 + offset ) ;
ctdb_marshall_record_copy ( r , reqid , key , header , data , length ) ;
2008-07-30 13:58:17 +04:00
m2 - > count + + ;
return m2 ;
}
/* we've finished marshalling, return a data blob with the marshalled records */
TDB_DATA ctdb_marshall_finish ( struct ctdb_marshall_buffer * m )
{
TDB_DATA data ;
data . dptr = ( uint8_t * ) m ;
data . dsize = talloc_get_size ( m ) ;
return data ;
}
/*
loop over a marshalling buffer
- pass r = = NULL to start
- loop the number of times indicated by m - > count
*/
struct ctdb_rec_data * ctdb_marshall_loop_next ( struct ctdb_marshall_buffer * m , struct ctdb_rec_data * r ,
uint32_t * reqid ,
struct ctdb_ltdb_header * header ,
TDB_DATA * key , TDB_DATA * data )
{
if ( r = = NULL ) {
r = ( struct ctdb_rec_data * ) & m - > data [ 0 ] ;
} else {
r = ( struct ctdb_rec_data * ) ( r - > length + ( uint8_t * ) r ) ;
}
if ( reqid ! = NULL ) {
* reqid = r - > reqid ;
}
if ( key ! = NULL ) {
key - > dptr = & r - > data [ 0 ] ;
key - > dsize = r - > keylen ;
}
if ( data ! = NULL ) {
data - > dptr = & r - > data [ r - > keylen ] ;
data - > dsize = r - > datalen ;
if ( header ! = NULL ) {
data - > dptr + = sizeof ( * header ) ;
data - > dsize - = sizeof ( * header ) ;
}
}
if ( header ! = NULL ) {
if ( r - > datalen < sizeof ( * header ) ) {
return NULL ;
}
2014-08-04 08:50:17 +04:00
memcpy ( header , & r - > data [ r - > keylen ] , sizeof ( * header ) ) ;
2008-07-30 13:58:17 +04:00
}
return r ;
}
2008-08-20 05:52:36 +04:00
/*
This is used to canonicalize a ctdb_sock_addr structure .
*/
2008-08-20 05:58:27 +04:00
void ctdb_canonicalize_ip ( const ctdb_sock_addr * ip , ctdb_sock_addr * cip )
2008-06-04 11:12:57 +04:00
{
2008-08-20 05:52:36 +04:00
char prefix [ 12 ] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0xff , 0xff } ;
memcpy ( cip , ip , sizeof ( * cip ) ) ;
if ( ( ip - > sa . sa_family = = AF_INET6 )
& & ! memcmp ( & ip - > ip6 . sin6_addr , prefix , 12 ) ) {
memset ( cip , 0 , sizeof ( * cip ) ) ;
# ifdef HAVE_SOCK_SIN_LEN
cip - > ip . sin_len = sizeof ( * cip ) ;
# endif
cip - > ip . sin_family = AF_INET ;
cip - > ip . sin_port = ip - > ip6 . sin6_port ;
2012-02-06 14:01:47 +04:00
memcpy ( & cip - > ip . sin_addr , & ip - > ip6 . sin6_addr . s6_addr [ 12 ] , 4 ) ;
2008-08-20 05:52:36 +04:00
}
}
bool ctdb_same_ip ( const ctdb_sock_addr * tip1 , const ctdb_sock_addr * tip2 )
{
ctdb_sock_addr ip1 , ip2 ;
2008-08-20 05:58:27 +04:00
ctdb_canonicalize_ip ( tip1 , & ip1 ) ;
ctdb_canonicalize_ip ( tip2 , & ip2 ) ;
2008-08-20 05:52:36 +04:00
if ( ip1 . sa . sa_family ! = ip2 . sa . sa_family ) {
2008-06-04 11:12:57 +04:00
return false ;
}
2008-08-20 05:52:36 +04:00
switch ( ip1 . sa . sa_family ) {
2008-06-04 11:12:57 +04:00
case AF_INET :
2008-08-20 05:52:36 +04:00
return ip1 . ip . sin_addr . s_addr = = ip2 . ip . sin_addr . s_addr ;
2008-06-04 11:12:57 +04:00
case AF_INET6 :
2008-08-20 05:52:36 +04:00
return ! memcmp ( & ip1 . ip6 . sin6_addr . s6_addr [ 0 ] ,
& ip2 . ip6 . sin6_addr . s6_addr [ 0 ] ,
2008-06-04 11:12:57 +04:00
16 ) ;
default :
2008-08-20 05:52:36 +04:00
DEBUG ( DEBUG_ERR , ( __location__ " CRITICAL Can not compare sockaddr structures of type %u \n " , ip1 . sa . sa_family ) ) ;
2008-06-04 11:12:57 +04:00
return false ;
}
return true ;
}
2007-09-10 08:27:29 +04:00
/*
2008-08-19 08:58:29 +04:00
compare two ctdb_sock_addr structures
2007-09-10 08:27:29 +04:00
*/
2008-08-19 08:58:29 +04:00
bool ctdb_same_sockaddr ( const ctdb_sock_addr * ip1 , const ctdb_sock_addr * ip2 )
2007-09-10 08:27:29 +04:00
{
2008-08-19 08:58:29 +04:00
return ctdb_same_ip ( ip1 , ip2 ) & & ip1 - > ip . sin_port = = ip2 - > ip . sin_port ;
2007-09-10 08:27:29 +04:00
}
2008-01-10 01:43:14 +03:00
2008-08-19 08:58:29 +04:00
char * ctdb_addr_to_str ( ctdb_sock_addr * addr )
{
static char cip [ 128 ] = " " ;
switch ( addr - > sa . sa_family ) {
case AF_INET :
inet_ntop ( addr - > ip . sin_family , & addr - > ip . sin_addr , cip , sizeof ( cip ) ) ;
break ;
case AF_INET6 :
inet_ntop ( addr - > ip6 . sin6_family , & addr - > ip6 . sin6_addr , cip , sizeof ( cip ) ) ;
break ;
default :
DEBUG ( DEBUG_ERR , ( __location__ " ERROR, unknown family %u \n " , addr - > sa . sa_family ) ) ;
2012-09-06 14:22:38 +04:00
ctdb_external_trace ( ) ;
2008-08-19 08:58:29 +04:00
}
return cip ;
}
2008-01-10 01:43:14 +03:00
2009-03-24 05:45:11 +03:00
unsigned ctdb_addr_to_port ( ctdb_sock_addr * addr )
{
switch ( addr - > sa . sa_family ) {
case AF_INET :
return ntohs ( addr - > ip . sin_port ) ;
break ;
case AF_INET6 :
return ntohs ( addr - > ip6 . sin6_port ) ;
break ;
default :
DEBUG ( DEBUG_ERR , ( __location__ " ERROR, unknown family %u \n " , addr - > sa . sa_family ) ) ;
}
return 0 ;
}
2008-01-10 01:43:14 +03:00
2009-12-16 13:27:20 +03:00
2009-12-07 18:17:13 +03:00
const char * ctdb_eventscript_call_names [ ] = {
2010-01-19 12:07:14 +03:00
" init " ,
2010-02-12 13:24:08 +03:00
" setup " ,
2009-12-07 18:17:13 +03:00
" startup " ,
" startrecovery " ,
" recovered " ,
" takeip " ,
" releaseip " ,
" stopped " ,
" monitor " ,
" status " ,
" shutdown " ,
2009-12-21 10:33:55 +03:00
" reload " ,
2010-08-30 13:42:30 +04:00
" updateip " ,
" ipreallocated "
2009-12-07 18:17:13 +03:00
} ;
2013-01-10 09:06:25 +04:00
/* Runstate handling */
static struct {
enum ctdb_runstate runstate ;
const char * label ;
} runstate_map [ ] = {
{ CTDB_RUNSTATE_UNKNOWN , " UNKNOWN " } ,
{ CTDB_RUNSTATE_INIT , " INIT " } ,
{ CTDB_RUNSTATE_SETUP , " SETUP " } ,
2013-04-18 14:30:14 +04:00
{ CTDB_RUNSTATE_FIRST_RECOVERY , " FIRST_RECOVERY " } ,
2013-01-10 09:06:25 +04:00
{ CTDB_RUNSTATE_STARTUP , " STARTUP " } ,
{ CTDB_RUNSTATE_RUNNING , " RUNNING " } ,
{ CTDB_RUNSTATE_SHUTDOWN , " SHUTDOWN " } ,
{ - 1 , NULL } ,
} ;
const char * runstate_to_string ( enum ctdb_runstate runstate )
{
int i ;
for ( i = 0 ; runstate_map [ i ] . label ! = NULL ; i + + ) {
if ( runstate_map [ i ] . runstate = = runstate ) {
return runstate_map [ i ] . label ;
}
}
return runstate_map [ 0 ] . label ;
}
enum ctdb_runstate runstate_from_string ( const char * label )
{
int i ;
for ( i = 0 ; runstate_map [ i ] . label ! = NULL ; i + + ) {
if ( strcasecmp ( runstate_map [ i ] . label , label ) = = 0 ) {
return runstate_map [ i ] . runstate ;
}
}
return CTDB_RUNSTATE_UNKNOWN ;
}
void ctdb_set_runstate ( struct ctdb_context * ctdb , enum ctdb_runstate runstate )
{
if ( runstate < = ctdb - > runstate ) {
ctdb_fatal ( ctdb , " runstate must always increase " ) ;
}
DEBUG ( DEBUG_NOTICE , ( " Set runstate to %s (%d) \n " ,
runstate_to_string ( runstate ) , runstate ) ) ;
ctdb - > runstate = runstate ;
}
2014-08-15 07:22:29 +04:00
/* Convert arbitrary data to 4-byte boundary padded uint32 array */
uint32_t * ctdb_key_to_idkey ( TALLOC_CTX * mem_ctx , TDB_DATA key )
{
uint32_t idkey_size , * k ;
idkey_size = 1 + ( key . dsize + sizeof ( uint32_t ) - 1 ) / sizeof ( uint32_t ) ;
k = talloc_zero_array ( mem_ctx , uint32_t , idkey_size ) ;
if ( k = = NULL ) {
return NULL ;
}
k [ 0 ] = idkey_size ;
memcpy ( & k [ 1 ] , key . dptr , key . dsize ) ;
return k ;
}