2015-04-14 10:20:05 +03:00
/*
CTDB protocol marshalling
Copyright ( C ) Amitay Isaacs 2015
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/>.
*/
# include "replace.h"
# include "system/network.h"
# include <talloc.h>
# include "protocol.h"
2017-09-04 09:00:48 +03:00
# include "protocol_util.h"
2015-04-14 10:20:05 +03:00
static struct {
enum ctdb_runstate runstate ;
const char * label ;
} runstate_map [ ] = {
{ CTDB_RUNSTATE_UNKNOWN , " UNKNOWN " } ,
{ CTDB_RUNSTATE_INIT , " INIT " } ,
{ CTDB_RUNSTATE_SETUP , " SETUP " } ,
{ CTDB_RUNSTATE_FIRST_RECOVERY , " FIRST_RECOVERY " } ,
{ CTDB_RUNSTATE_STARTUP , " STARTUP " } ,
{ CTDB_RUNSTATE_RUNNING , " RUNNING " } ,
{ CTDB_RUNSTATE_SHUTDOWN , " SHUTDOWN " } ,
{ - 1 , NULL } ,
} ;
const char * ctdb_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 ctdb_runstate_from_string ( const char * runstate_str )
{
int i ;
for ( i = 0 ; runstate_map [ i ] . label ! = NULL ; i + + ) {
if ( strcasecmp ( runstate_map [ i ] . label ,
runstate_str ) = = 0 ) {
return runstate_map [ i ] . runstate ;
}
}
return CTDB_RUNSTATE_UNKNOWN ;
}
static struct {
enum ctdb_event event ;
const char * label ;
} event_map [ ] = {
{ CTDB_EVENT_INIT , " init " } ,
{ CTDB_EVENT_SETUP , " setup " } ,
{ CTDB_EVENT_STARTUP , " startup " } ,
{ CTDB_EVENT_START_RECOVERY , " startrecovery " } ,
{ CTDB_EVENT_RECOVERED , " recovered " } ,
{ CTDB_EVENT_TAKE_IP , " takeip " } ,
{ CTDB_EVENT_RELEASE_IP , " releaseip " } ,
{ CTDB_EVENT_MONITOR , " monitor " } ,
{ CTDB_EVENT_SHUTDOWN , " shutdown " } ,
{ CTDB_EVENT_UPDATE_IP , " updateip " } ,
{ CTDB_EVENT_IPREALLOCATED , " ipreallocated " } ,
{ CTDB_EVENT_MAX , " all " } ,
{ - 1 , NULL } ,
} ;
const char * ctdb_event_to_string ( enum ctdb_event event )
{
int i ;
for ( i = 0 ; event_map [ i ] . label ! = NULL ; i + + ) {
if ( event_map [ i ] . event = = event ) {
return event_map [ i ] . label ;
}
}
return " unknown " ;
}
enum ctdb_event ctdb_event_from_string ( const char * event_str )
{
int i ;
for ( i = 0 ; event_map [ i ] . label ! = NULL ; i + + ) {
if ( strcmp ( event_map [ i ] . label , event_str ) = = 0 ) {
return event_map [ i ] . event ;
}
}
return CTDB_EVENT_MAX ;
}
2015-11-02 08:24:15 +03:00
2017-09-11 08:00:10 +03:00
int ctdb_sock_addr_to_buf ( char * buf , socklen_t buflen ,
ctdb_sock_addr * addr , bool with_port )
2015-11-02 08:24:15 +03:00
{
2017-06-30 08:11:04 +03:00
const char * t ;
2015-11-02 08:24:15 +03:00
switch ( addr - > sa . sa_family ) {
case AF_INET :
2017-06-30 08:11:04 +03:00
t = inet_ntop ( addr - > ip . sin_family , & addr - > ip . sin_addr ,
buf , buflen ) ;
if ( t = = NULL ) {
return errno ;
}
2015-11-02 08:24:15 +03:00
break ;
case AF_INET6 :
2017-06-30 08:11:04 +03:00
t = inet_ntop ( addr - > ip6 . sin6_family , & addr - > ip6 . sin6_addr ,
buf , buflen ) ;
if ( t = = NULL ) {
return errno ;
}
2015-11-02 08:24:15 +03:00
break ;
default :
2017-06-30 08:11:04 +03:00
return EAFNOSUPPORT ;
2015-11-02 08:24:15 +03:00
break ;
}
2017-09-11 08:00:10 +03:00
if ( with_port ) {
size_t len = strlen ( buf ) ;
int ret ;
ret = snprintf ( buf + len , buflen - len ,
" :%u " , ctdb_sock_addr_port ( addr ) ) ;
if ( ret > = buflen - len ) {
return ENOSPC ;
}
}
2017-06-30 08:11:04 +03:00
return 0 ;
}
2017-09-11 08:00:10 +03:00
const char * ctdb_sock_addr_to_string ( TALLOC_CTX * mem_ctx ,
ctdb_sock_addr * addr , bool with_port )
2017-06-30 08:11:04 +03:00
{
size_t len = 64 ;
char * cip ;
int ret ;
cip = talloc_size ( mem_ctx , len ) ;
if ( cip = = NULL ) {
return NULL ;
}
2017-09-11 08:00:10 +03:00
ret = ctdb_sock_addr_to_buf ( cip , len , addr , with_port ) ;
2017-06-30 08:11:04 +03:00
if ( ret ! = 0 ) {
talloc_free ( cip ) ;
return NULL ;
}
2015-11-02 08:24:15 +03:00
return cip ;
}
2016-04-27 11:30:12 +03:00
2017-09-11 09:39:38 +03:00
static int ipv4_from_string ( const char * str , struct sockaddr_in * ip )
{
int ret ;
* ip = ( struct sockaddr_in ) {
. sin_family = AF_INET ,
} ;
ret = inet_pton ( AF_INET , str , & ip - > sin_addr ) ;
if ( ret ! = 1 ) {
return EINVAL ;
}
# ifdef HAVE_SOCK_SIN_LEN
ip - > sin_len = sizeof ( * ip ) ;
# endif
return 0 ;
}
static int ipv6_from_string ( const char * str , struct sockaddr_in6 * ip6 )
{
int ret ;
* ip6 = ( struct sockaddr_in6 ) {
. sin6_family = AF_INET6 ,
} ;
ret = inet_pton ( AF_INET6 , str , & ip6 - > sin6_addr ) ;
if ( ret ! = 1 ) {
return EINVAL ;
}
# ifdef HAVE_SOCK_SIN_LEN
ip6 - > sin6_len = sizeof ( * ip6 ) ;
# endif
return 0 ;
}
static int ip_from_string ( const char * str , ctdb_sock_addr * addr )
{
char * p ;
int ret ;
if ( addr = = NULL ) {
return EINVAL ;
}
ZERO_STRUCTP ( addr ) ; /* valgrind :-) */
/* IPv4 or IPv6 address?
*
* Use rindex ( ) because we need the right - most ' : ' below for
* IPv4 - mapped IPv6 addresses anyway . . .
*/
p = rindex ( str , ' : ' ) ;
if ( p = = NULL ) {
ret = ipv4_from_string ( str , & addr - > ip ) ;
} else {
uint8_t ipv4_mapped_prefix [ 12 ] = {
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0xff , 0xff
} ;
ret = ipv6_from_string ( str , & addr - > ip6 ) ;
if ( ret ! = 0 ) {
return ret ;
}
/*
* Check for IPv4 - mapped IPv6 address
* ( e . g . : : ffff : 192.0 .2 .128 ) - reparse as IPv4 if
* necessary
*/
if ( memcmp ( & addr - > ip6 . sin6_addr . s6_addr [ 0 ] ,
ipv4_mapped_prefix ,
sizeof ( ipv4_mapped_prefix ) ) = = 0 ) {
/* Reparse as IPv4 */
ret = ipv4_from_string ( p + 1 , & addr - > ip ) ;
}
}
return ret ;
}
int ctdb_sock_addr_from_string ( const char * str ,
ctdb_sock_addr * addr , bool with_port )
{
char * p ;
char s [ 64 ] ; /* Much longer than INET6_ADDRSTRLEN */
unsigned port ;
char * endp = NULL ;
size_t len ;
2017-10-09 06:52:30 +03:00
int ret ;
2017-09-11 09:39:38 +03:00
if ( ! with_port ) {
ret = ip_from_string ( str , addr ) ;
return ret ;
}
/* Parse out port number and then IP address */
len = strlen ( str ) ;
if ( len > = sizeof ( s ) ) {
return EINVAL ;
}
strncpy ( s , str , len + 1 ) ;
p = rindex ( s , ' : ' ) ;
if ( p = = NULL ) {
return EINVAL ;
}
port = strtoul ( p + 1 , & endp , 10 ) ;
if ( endp = = p + 1 | | * endp ! = ' \0 ' ) {
/* Empty string or trailing garbage */
return EINVAL ;
}
* p = ' \0 ' ;
ret = ip_from_string ( s , addr ) ;
ctdb_sock_addr_set_port ( addr , port ) ;
return ret ;
}
2017-06-02 09:17:22 +03:00
unsigned int ctdb_sock_addr_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 :
return 0 ;
}
}
void ctdb_sock_addr_set_port ( ctdb_sock_addr * addr , unsigned int port )
{
switch ( addr - > sa . sa_family ) {
case AF_INET :
addr - > ip . sin_port = htons ( port ) ;
break ;
case AF_INET6 :
addr - > ip6 . sin6_port = htons ( port ) ;
break ;
default :
break ;
}
}
2017-09-13 09:18:29 +03:00
static int ctdb_sock_addr_cmp_family ( const ctdb_sock_addr * addr1 ,
const ctdb_sock_addr * addr2 )
2016-04-27 11:30:12 +03:00
{
2016-05-23 03:35:10 +03:00
/* This is somewhat arbitrary. However, when used for sorting
* it just needs to be consistent .
*/
if ( addr1 - > sa . sa_family < addr2 - > sa . sa_family ) {
return - 1 ;
}
if ( addr1 - > sa . sa_family > addr2 - > sa . sa_family ) {
return 1 ;
2016-04-27 11:30:12 +03:00
}
2017-09-13 09:18:29 +03:00
return 0 ;
}
int ctdb_sock_addr_cmp_ip ( const ctdb_sock_addr * addr1 ,
const ctdb_sock_addr * addr2 )
{
int ret ;
ret = ctdb_sock_addr_cmp_family ( addr1 , addr2 ) ;
if ( ret ! = 0 ) {
return ret ;
}
2016-04-27 11:30:12 +03:00
switch ( addr1 - > sa . sa_family ) {
case AF_INET :
2016-05-23 03:35:10 +03:00
ret = memcmp ( & addr1 - > ip . sin_addr . s_addr ,
& addr2 - > ip . sin_addr . s_addr , 4 ) ;
2016-04-27 11:30:12 +03:00
break ;
case AF_INET6 :
2016-05-23 03:35:10 +03:00
ret = memcmp ( addr1 - > ip6 . sin6_addr . s6_addr ,
addr2 - > ip6 . sin6_addr . s6_addr , 16 ) ;
2016-07-22 06:35:00 +03:00
break ;
default :
2016-05-23 03:35:10 +03:00
ret = - 1 ;
2016-07-22 06:35:00 +03:00
}
2016-05-23 03:35:10 +03:00
return ret ;
2016-07-22 06:35:00 +03:00
}
2016-05-23 03:35:10 +03:00
int ctdb_sock_addr_cmp ( const ctdb_sock_addr * addr1 ,
const ctdb_sock_addr * addr2 )
2016-07-22 06:35:00 +03:00
{
2016-05-23 03:35:10 +03:00
int ret = 0 ;
ret = ctdb_sock_addr_cmp_ip ( addr1 , addr2 ) ;
if ( ret ! = 0 ) {
return ret ;
2016-07-22 06:35:00 +03:00
}
switch ( addr1 - > sa . sa_family ) {
case AF_INET :
2016-05-23 03:35:10 +03:00
if ( addr1 - > ip . sin_port < addr2 - > ip . sin_port ) {
ret = - 1 ;
} else if ( addr1 - > ip . sin_port > addr2 - > ip . sin_port ) {
ret = 1 ;
2016-07-22 06:35:00 +03:00
}
break ;
case AF_INET6 :
2016-05-23 03:35:10 +03:00
if ( addr1 - > ip6 . sin6_port < addr2 - > ip6 . sin6_port ) {
ret = - 1 ;
} else if ( addr1 - > ip6 . sin6_port > addr2 - > ip6 . sin6_port ) {
ret = 1 ;
2016-04-27 11:30:12 +03:00
}
break ;
default :
2016-05-23 03:35:10 +03:00
ret = - 1 ;
2016-04-27 11:30:12 +03:00
}
2016-05-23 03:35:10 +03:00
return ret ;
}
bool ctdb_sock_addr_same_ip ( const ctdb_sock_addr * addr1 ,
const ctdb_sock_addr * addr2 )
{
return ( ctdb_sock_addr_cmp_ip ( addr1 , addr2 ) = = 0 ) ;
}
bool ctdb_sock_addr_same ( const ctdb_sock_addr * addr1 ,
const ctdb_sock_addr * addr2 )
{
return ( ctdb_sock_addr_cmp ( addr1 , addr2 ) = = 0 ) ;
2016-04-27 11:30:12 +03:00
}
2017-09-04 09:41:30 +03:00
int ctdb_connection_to_buf ( char * buf , size_t buflen ,
struct ctdb_connection * conn , bool client_first )
{
char server [ 64 ] , client [ 64 ] ;
int ret ;
ret = ctdb_sock_addr_to_buf ( server , sizeof ( server ) ,
& conn - > server , true ) ;
if ( ret ! = 0 ) {
return ret ;
}
ret = ctdb_sock_addr_to_buf ( client , sizeof ( client ) ,
& conn - > client , true ) ;
if ( ret ! = 0 ) {
return ret ;
}
if ( ! client_first ) {
ret = snprintf ( buf , buflen , " %s %s " , server , client ) ;
} else {
ret = snprintf ( buf , buflen , " %s %s " , client , server ) ;
}
if ( ret > = buflen ) {
return ENOSPC ;
}
return 0 ;
}
const char * ctdb_connection_to_string ( TALLOC_CTX * mem_ctx ,
struct ctdb_connection * conn ,
bool client_first )
{
const size_t len = 128 ;
char * out ;
int ret ;
out = talloc_size ( mem_ctx , len ) ;
if ( out = = NULL ) {
return NULL ;
}
ret = ctdb_connection_to_buf ( out , len , conn , client_first ) ;
if ( ret ! = 0 ) {
talloc_free ( out ) ;
return NULL ;
}
return out ;
}
int ctdb_connection_from_string ( const char * str , bool client_first ,
struct ctdb_connection * conn )
{
char s [ 128 ] ;
char * t1 = NULL , * t2 = NULL ;
size_t len ;
ctdb_sock_addr * first = ( client_first ? & conn - > client : & conn - > server ) ;
ctdb_sock_addr * second = ( client_first ? & conn - > server : & conn - > client ) ;
int ret ;
len = strlcpy ( s , str , sizeof ( s ) ) ;
if ( len > = sizeof ( s ) ) {
return EINVAL ;
}
t1 = strtok ( s , " \t \n " ) ;
if ( t1 = = NULL ) {
return EINVAL ;
}
t2 = strtok ( NULL , " \t \n \0 " ) ;
if ( t2 = = NULL ) {
return EINVAL ;
}
ret = ctdb_sock_addr_from_string ( t1 , first , true ) ;
if ( ret ! = 0 ) {
return ret ;
}
ret = ctdb_sock_addr_from_string ( t2 , second , true ) ;
if ( ret ! = 0 ) {
return ret ;
}
ret = ctdb_sock_addr_cmp_family ( first , second ) ;
if ( ret ! = 0 ) {
return EINVAL ;
}
return 0 ;
}
2017-09-04 10:01:05 +03:00
int ctdb_connection_list_add ( struct ctdb_connection_list * conn_list ,
struct ctdb_connection * conn )
{
uint32_t len ;
if ( conn_list = = NULL ) {
return EINVAL ;
}
/* Ensure array is big enough */
len = talloc_array_length ( conn_list - > conn ) ;
if ( conn_list - > num = = len ) {
conn_list - > conn = talloc_realloc ( conn_list , conn_list - > conn ,
struct ctdb_connection ,
len + 128 ) ;
if ( conn_list - > conn = = NULL ) {
return ENOMEM ;
}
}
conn_list - > conn [ conn_list - > num ] = * conn ;
conn_list - > num + + ;
return 0 ;
}
static int connection_cmp ( const void * a , const void * b )
{
const struct ctdb_connection * conn_a = a ;
const struct ctdb_connection * conn_b = b ;
int ret ;
ret = ctdb_sock_addr_cmp ( & conn_a - > server , & conn_b - > server ) ;
if ( ret = = 0 ) {
ret = ctdb_sock_addr_cmp ( & conn_a - > client , & conn_b - > client ) ;
}
return ret ;
}
int ctdb_connection_list_sort ( struct ctdb_connection_list * conn_list )
{
if ( conn_list = = NULL ) {
return EINVAL ;
}
if ( conn_list - > num > 0 ) {
qsort ( conn_list - > conn , conn_list - > num ,
sizeof ( struct ctdb_connection ) , connection_cmp ) ;
}
return 0 ;
}
const char * ctdb_connection_list_to_string (
TALLOC_CTX * mem_ctx ,
struct ctdb_connection_list * conn_list , bool client_first )
{
uint32_t i ;
char * out ;
out = talloc_strdup ( mem_ctx , " " ) ;
if ( out = = NULL ) {
return NULL ;
}
if ( conn_list = = NULL | | conn_list - > num = = 0 ) {
return out ;
}
for ( i = 0 ; i < conn_list - > num ; i + + ) {
char buf [ 128 ] ;
int ret ;
ret = ctdb_connection_to_buf ( buf , sizeof ( buf ) ,
& conn_list - > conn [ i ] , client_first ) ;
if ( ret ! = 0 ) {
talloc_free ( out ) ;
return NULL ;
}
out = talloc_asprintf_append ( out , " %s \n " , buf ) ;
if ( out = = NULL ) {
return NULL ;
}
}
return out ;
}
int ctdb_connection_list_read ( TALLOC_CTX * mem_ctx , bool client_first ,
struct ctdb_connection_list * * conn_list )
{
struct ctdb_connection_list * list ;
char line [ 128 ] ; /* long enough for IPv6 */
int ret ;
if ( conn_list = = NULL ) {
return EINVAL ;
}
list = talloc_zero ( mem_ctx , struct ctdb_connection_list ) ;
if ( list = = NULL ) {
return ENOMEM ;
}
while ( fgets ( line , sizeof ( line ) , stdin ) ! = NULL ) {
char * t ;
struct ctdb_connection conn ;
/* Skip empty lines */
if ( line [ 0 ] = = ' \n ' ) {
continue ;
}
/* Comment */
if ( line [ 0 ] = = ' # ' ) {
continue ;
}
t = strtok ( line , " \n " ) ;
if ( t = = NULL ) {
goto fail ;
}
ret = ctdb_connection_from_string ( t , client_first , & conn ) ;
if ( ret ! = 0 ) {
goto fail ;
}
ret = ctdb_connection_list_add ( list , & conn ) ;
if ( ret ! = 0 ) {
goto fail ;
}
}
* conn_list = list ;
return 0 ;
fail :
talloc_free ( list ) ;
return EINVAL ;
}