2010-05-21 06:37:41 +04:00
/*
core of libctdb
Copyright ( C ) Rusty Russell 2010
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 <ctdb.h>
# include <poll.h>
# include <errno.h>
# include <unistd.h>
# include <fcntl.h>
# include <stdlib.h>
# include <sys/socket.h>
# include <sys/un.h>
2010-12-10 05:39:18 +03:00
# include <sys/ioctl.h>
2010-05-21 06:37:41 +04:00
# include "libctdb_private.h"
# include "io_elem.h"
# include "local_tdb.h"
# include "messages.h"
# include <dlinklist.h>
# include <ctdb_protocol.h>
2010-05-24 07:47:36 +04:00
/* Remove type-safety macros. */
# undef ctdb_attachdb_send
2010-06-04 08:03:08 +04:00
# undef ctdb_readrecordlock_async
2010-06-04 14:57:03 +04:00
# undef ctdb_connect
2010-05-24 07:47:36 +04:00
2010-06-04 14:11:42 +04:00
struct ctdb_lock {
struct ctdb_lock * next , * prev ;
struct ctdb_db * ctdb_db ;
TDB_DATA key ;
2010-06-08 11:22:23 +04:00
/* This will always be set by the time user sees this. */
unsigned long held_magic ;
2010-06-04 14:11:42 +04:00
struct ctdb_ltdb_header * hdr ;
/* For convenience, we stash original callback here. */
ctdb_rrl_callback_t callback ;
} ;
2010-06-18 10:05:52 +04:00
struct ctdb_db {
struct ctdb_connection * ctdb ;
bool persistent ;
uint32_t tdb_flags ;
uint32_t id ;
struct tdb_context * tdb ;
ctdb_callback_t callback ;
void * private_data ;
} ;
2010-06-04 14:11:42 +04:00
static void remove_lock ( struct ctdb_connection * ctdb , struct ctdb_lock * lock )
{
DLIST_REMOVE ( ctdb - > locks , lock ) ;
}
/* FIXME: for thread safety, need tid info too. */
static bool holding_lock ( struct ctdb_connection * ctdb )
{
/* For the moment, you can't ever hold more than 1 lock. */
return ( ctdb - > locks ! = NULL ) ;
}
static void add_lock ( struct ctdb_connection * ctdb , struct ctdb_lock * lock )
{
DLIST_ADD ( ctdb - > locks , lock ) ;
}
2010-06-18 10:05:52 +04:00
static void cleanup_locks ( struct ctdb_connection * ctdb , struct ctdb_db * db )
{
struct ctdb_lock * i , * next ;
for ( i = ctdb - > locks ; i ; i = next ) {
/* Grab next pointer, as release_lock will free i */
next = i - > next ;
if ( i - > ctdb_db = = db ) {
ctdb_release_lock ( db , i ) ;
}
}
}
2010-05-21 06:37:41 +04:00
/* FIXME: Could be in shared util code with rest of ctdb */
static void close_noerr ( int fd )
{
int olderr = errno ;
close ( fd ) ;
errno = olderr ;
}
/* FIXME: Could be in shared util code with rest of ctdb */
static void free_noerr ( void * p )
{
int olderr = errno ;
free ( p ) ;
errno = olderr ;
}
/* FIXME: Could be in shared util code with rest of ctdb */
static void set_nonblocking ( int fd )
{
unsigned v ;
v = fcntl ( fd , F_GETFL , 0 ) ;
fcntl ( fd , F_SETFL , v | O_NONBLOCK ) ;
}
/* FIXME: Could be in shared util code with rest of ctdb */
static void set_close_on_exec ( int fd )
{
unsigned v ;
v = fcntl ( fd , F_GETFD , 0 ) ;
fcntl ( fd , F_SETFD , v | FD_CLOEXEC ) ;
}
2010-05-24 07:47:36 +04:00
static void set_pnn ( struct ctdb_connection * ctdb ,
struct ctdb_request * req ,
void * unused )
2010-05-21 06:37:41 +04:00
{
2010-06-04 14:49:25 +04:00
if ( ! ctdb_getpnn_recv ( ctdb , req , & ctdb - > pnn ) ) {
2010-06-04 14:57:06 +04:00
DEBUG ( ctdb , LOG_CRIT ,
" ctdb_connect(async): failed to get pnn " ) ;
2010-05-24 07:47:36 +04:00
ctdb - > broken = true ;
2010-05-21 06:37:41 +04:00
}
2010-06-04 11:24:08 +04:00
ctdb_request_free ( ctdb , req ) ;
2010-05-21 06:37:41 +04:00
}
2010-06-04 14:57:03 +04:00
struct ctdb_connection * ctdb_connect ( const char * addr ,
ctdb_log_fn_t log_fn , void * log_priv )
2010-05-21 06:37:41 +04:00
{
struct ctdb_connection * ctdb ;
struct sockaddr_un sun ;
ctdb = malloc ( sizeof ( * ctdb ) ) ;
2010-06-04 14:57:06 +04:00
if ( ! ctdb ) {
/* With no format string, we hope it doesn't use ap! */
va_list ap ;
memset ( & ap , 0 , sizeof ( ap ) ) ;
errno = ENOMEM ;
log_fn ( log_priv , LOG_ERR , " ctdb_connect: no memory " , ap ) ;
2010-05-21 06:37:41 +04:00
goto fail ;
2010-06-04 14:57:06 +04:00
}
2010-05-21 06:37:41 +04:00
ctdb - > outq = NULL ;
ctdb - > doneq = NULL ;
ctdb - > in = NULL ;
2011-01-10 23:37:17 +03:00
ctdb - > inqueue = NULL ;
2010-05-21 06:37:41 +04:00
ctdb - > message_handlers = NULL ;
2010-05-24 08:22:17 +04:00
ctdb - > next_id = 0 ;
ctdb - > broken = false ;
2010-06-04 14:57:03 +04:00
ctdb - > log = log_fn ;
ctdb - > log_priv = log_priv ;
2010-06-05 08:27:46 +04:00
ctdb - > locks = NULL ;
2010-05-21 06:37:41 +04:00
memset ( & sun , 0 , sizeof ( sun ) ) ;
sun . sun_family = AF_UNIX ;
if ( ! addr )
addr = CTDB_PATH ;
2011-08-18 16:47:09 +04:00
strncpy ( sun . sun_path , addr , sizeof ( sun . sun_path ) - 1 ) ;
2010-05-21 06:37:41 +04:00
ctdb - > fd = socket ( AF_UNIX , SOCK_STREAM , 0 ) ;
if ( ctdb - > fd < 0 )
goto free_fail ;
set_nonblocking ( ctdb - > fd ) ;
set_close_on_exec ( ctdb - > fd ) ;
if ( connect ( ctdb - > fd , ( struct sockaddr * ) & sun , sizeof ( sun ) ) = = - 1 )
goto close_fail ;
/* Immediately queue a request to get our pnn. */
2010-05-24 07:47:36 +04:00
if ( ! ctdb_getpnn_send ( ctdb , CTDB_CURRENT_NODE , set_pnn , NULL ) )
2010-05-21 06:37:41 +04:00
goto close_fail ;
return ctdb ;
close_fail :
close_noerr ( ctdb - > fd ) ;
free_fail :
free_noerr ( ctdb ) ;
fail :
return NULL ;
}
2010-06-18 10:05:52 +04:00
void ctdb_disconnect ( struct ctdb_connection * ctdb )
{
struct ctdb_request * i ;
DEBUG ( ctdb , LOG_DEBUG , " ctdb_disconnect " ) ;
while ( ( i = ctdb - > outq ) ! = NULL ) {
DLIST_REMOVE ( ctdb - > outq , i ) ;
ctdb_request_free ( ctdb , i ) ;
}
while ( ( i = ctdb - > doneq ) ! = NULL ) {
DLIST_REMOVE ( ctdb - > doneq , i ) ;
ctdb_request_free ( ctdb , i ) ;
}
if ( ctdb - > in )
free_io_elem ( ctdb - > in ) ;
remove_message_handlers ( ctdb ) ;
close ( ctdb - > fd ) ;
/* Just in case they try to reuse */
ctdb - > fd = - 1 ;
free ( ctdb ) ;
}
2010-05-21 06:37:41 +04:00
int ctdb_get_fd ( struct ctdb_connection * ctdb )
{
return ctdb - > fd ;
}
int ctdb_which_events ( struct ctdb_connection * ctdb )
{
int events = POLLIN ;
if ( ctdb - > outq )
events | = POLLOUT ;
return events ;
}
2010-05-24 07:47:36 +04:00
struct ctdb_request * new_ctdb_request ( size_t len ,
ctdb_callback_t cb , void * cbdata )
2010-05-21 06:37:41 +04:00
{
struct ctdb_request * req = malloc ( sizeof ( * req ) ) ;
if ( ! req )
return NULL ;
req - > io = new_io_elem ( len ) ;
if ( ! req - > io ) {
free ( req ) ;
return NULL ;
}
req - > hdr . hdr = io_elem_data ( req - > io , NULL ) ;
2010-05-24 07:47:36 +04:00
req - > reply = NULL ;
req - > callback = cb ;
req - > priv_data = cbdata ;
req - > extra = NULL ;
req - > extra_destructor = NULL ;
2010-05-21 06:37:41 +04:00
return req ;
}
2010-06-04 11:24:08 +04:00
void ctdb_request_free ( struct ctdb_connection * ctdb , struct ctdb_request * req )
2010-05-21 06:37:41 +04:00
{
2010-06-18 09:45:11 +04:00
if ( req - > next | | req - > prev ) {
DEBUG ( ctdb , LOG_ALERT ,
" ctdb_request_free: request not complete! ctdb_cancel? %p (id %u) " ,
req , req - > hdr . hdr ? req - > hdr . hdr - > reqid : 0 ) ;
ctdb_cancel ( ctdb , req ) ;
return ;
}
2010-05-24 07:47:36 +04:00
if ( req - > extra_destructor ) {
2010-06-04 11:24:08 +04:00
req - > extra_destructor ( ctdb , req ) ;
2010-05-24 07:47:36 +04:00
}
if ( req - > reply ) {
free_io_elem ( req - > reply ) ;
2010-05-21 06:37:41 +04:00
}
2010-05-24 07:47:36 +04:00
free_io_elem ( req - > io ) ;
2010-05-21 06:37:41 +04:00
free ( req ) ;
}
2010-06-04 14:57:06 +04:00
/* Sanity-checking wrapper for reply. */
2010-06-04 11:24:08 +04:00
static struct ctdb_reply_call * unpack_reply_call ( struct ctdb_connection * ctdb ,
struct ctdb_request * req ,
2010-05-24 07:47:36 +04:00
uint32_t callid )
2010-05-21 06:37:41 +04:00
{
2010-05-24 07:47:36 +04:00
size_t len ;
struct ctdb_reply_call * inhdr = io_elem_data ( req - > reply , & len ) ;
2010-05-21 06:37:41 +04:00
2010-06-04 14:57:06 +04:00
/* Library user error if this isn't a reply to a call. */
if ( req - > hdr . hdr - > operation ! = CTDB_REQ_CALL ) {
errno = EINVAL ;
2010-06-08 11:23:17 +04:00
DEBUG ( ctdb , LOG_ALERT ,
2010-06-04 14:57:06 +04:00
" This was not a ctdbd call request: operation %u " ,
req - > hdr . hdr - > operation ) ;
2010-05-24 07:47:36 +04:00
return NULL ;
2010-05-21 06:37:41 +04:00
}
2010-05-24 07:47:36 +04:00
2010-06-04 14:57:06 +04:00
if ( req - > hdr . call - > callid ! = callid ) {
2010-05-24 07:47:36 +04:00
errno = EINVAL ;
2010-06-08 11:23:17 +04:00
DEBUG ( ctdb , LOG_ALERT ,
2010-06-04 14:57:06 +04:00
" This was not a ctdbd %u call request: %u " ,
callid , req - > hdr . call - > callid ) ;
return NULL ;
}
/* ctdbd or our error if this isn't a reply call. */
if ( len < sizeof ( * inhdr ) | | inhdr - > hdr . operation ! = CTDB_REPLY_CALL ) {
errno = EIO ;
DEBUG ( ctdb , LOG_CRIT ,
" Invalid ctdbd call reply: len %zu, operation %u " ,
len , inhdr - > hdr . operation ) ;
2010-05-24 07:47:36 +04:00
return NULL ;
}
return inhdr ;
2010-05-21 06:37:41 +04:00
}
2010-06-04 14:57:06 +04:00
/* Sanity-checking wrapper for reply. */
2010-06-04 11:24:08 +04:00
struct ctdb_reply_control * unpack_reply_control ( struct ctdb_connection * ctdb ,
struct ctdb_request * req ,
2010-05-24 07:47:36 +04:00
enum ctdb_controls control )
2010-05-21 06:37:41 +04:00
{
2010-05-24 07:47:36 +04:00
size_t len ;
struct ctdb_reply_control * inhdr = io_elem_data ( req - > reply , & len ) ;
/* Library user error if this isn't a reply to a call. */
2010-06-04 14:57:06 +04:00
if ( len < sizeof ( * inhdr ) ) {
errno = EINVAL ;
2010-06-08 11:23:17 +04:00
DEBUG ( ctdb , LOG_ALERT ,
2010-06-04 14:57:06 +04:00
" Short ctdbd control reply: %zu bytes " , len ) ;
return NULL ;
}
if ( req - > hdr . hdr - > operation ! = CTDB_REQ_CONTROL ) {
2010-05-24 07:47:36 +04:00
errno = EINVAL ;
2010-06-08 11:23:17 +04:00
DEBUG ( ctdb , LOG_ALERT ,
2010-06-04 14:57:06 +04:00
" This was not a ctdbd control request: operation %u " ,
req - > hdr . hdr - > operation ) ;
2010-05-24 07:47:36 +04:00
return NULL ;
}
/* ... or if it was a different control from what we expected. */
if ( req - > hdr . control - > opcode ! = control ) {
errno = EINVAL ;
2010-06-08 11:23:17 +04:00
DEBUG ( ctdb , LOG_ALERT ,
2010-06-04 14:57:06 +04:00
" This was not an opcode %u ctdbd control request: %u " ,
control , req - > hdr . control - > opcode ) ;
2010-05-24 07:47:36 +04:00
return NULL ;
}
2010-05-21 06:37:41 +04:00
2010-05-24 07:47:36 +04:00
/* ctdbd or our error if this isn't a reply call. */
if ( inhdr - > hdr . operation ! = CTDB_REPLY_CONTROL ) {
errno = EIO ;
2010-06-04 14:57:06 +04:00
DEBUG ( ctdb , LOG_CRIT ,
" Invalid ctdbd control reply: operation %u " ,
inhdr - > hdr . operation ) ;
2010-05-24 07:47:36 +04:00
return NULL ;
2010-05-21 06:37:41 +04:00
}
2010-05-24 07:47:36 +04:00
return inhdr ;
2010-05-21 06:37:41 +04:00
}
2010-05-24 07:47:36 +04:00
static void handle_incoming ( struct ctdb_connection * ctdb , struct io_elem * in )
2010-05-21 06:37:41 +04:00
{
2010-05-24 07:47:36 +04:00
struct ctdb_req_header * hdr ;
size_t len ;
2010-05-21 06:37:41 +04:00
struct ctdb_request * i ;
2010-05-24 07:47:36 +04:00
hdr = io_elem_data ( in , & len ) ;
/* FIXME: use len to check packet! */
2010-05-21 06:37:41 +04:00
if ( hdr - > operation = = CTDB_REQ_MESSAGE ) {
deliver_message ( ctdb , hdr ) ;
return ;
}
for ( i = ctdb - > doneq ; i ; i = i - > next ) {
if ( i - > hdr . hdr - > reqid = = hdr - > reqid ) {
DLIST_REMOVE ( ctdb - > doneq , i ) ;
2010-05-24 07:47:36 +04:00
i - > reply = in ;
i - > callback ( ctdb , i , i - > priv_data ) ;
2010-05-21 06:37:41 +04:00
return ;
}
}
2010-06-04 14:57:06 +04:00
DEBUG ( ctdb , LOG_WARNING ,
" Unexpected ctdbd request reply: operation %u reqid %u " ,
hdr - > operation , hdr - > reqid ) ;
2010-05-24 07:47:36 +04:00
free_io_elem ( in ) ;
2010-05-21 06:37:41 +04:00
}
/* Remove "harmless" errors. */
static ssize_t real_error ( ssize_t ret )
{
if ( ret < 0 & & ( errno = = EINTR | | errno = = EWOULDBLOCK ) )
return 0 ;
return ret ;
}
2010-06-04 14:49:25 +04:00
bool ctdb_service ( struct ctdb_connection * ctdb , int revents )
2010-05-21 06:37:41 +04:00
{
if ( ctdb - > broken ) {
2010-06-04 14:49:25 +04:00
return false ;
2010-05-21 06:37:41 +04:00
}
2010-06-04 14:11:42 +04:00
if ( holding_lock ( ctdb ) ) {
2010-06-08 11:23:17 +04:00
DEBUG ( ctdb , LOG_ALERT , " Do not block while holding lock! " ) ;
2010-06-04 14:11:42 +04:00
}
2010-05-21 06:37:41 +04:00
if ( revents & POLLOUT ) {
while ( ctdb - > outq ) {
if ( real_error ( write_io_elem ( ctdb - > fd ,
ctdb - > outq - > io ) ) < 0 ) {
2010-06-04 14:57:06 +04:00
DEBUG ( ctdb , LOG_ERR ,
" ctdb_service: error writing to ctdbd " ) ;
2010-05-21 06:37:41 +04:00
ctdb - > broken = true ;
2010-06-04 14:49:25 +04:00
return false ;
2010-05-21 06:37:41 +04:00
}
if ( io_elem_finished ( ctdb - > outq - > io ) ) {
struct ctdb_request * done = ctdb - > outq ;
DLIST_REMOVE ( ctdb - > outq , done ) ;
2010-05-24 08:22:17 +04:00
/* We add at the head: any dead ones
* sit and end . */
DLIST_ADD ( ctdb - > doneq , done ) ;
2010-05-21 06:37:41 +04:00
}
}
}
while ( revents & POLLIN ) {
int ret ;
2010-12-10 05:39:18 +03:00
int num_ready = 0 ;
if ( ioctl ( ctdb - > fd , FIONREAD , & num_ready ) ! = 0 ) {
DEBUG ( ctdb , LOG_ERR ,
" ctdb_service: ioctl(FIONREAD) %d " , errno ) ;
ctdb - > broken = true ;
return false ;
}
if ( num_ready = = 0 ) {
/* the descriptor has been closed or we have all our data */
break ;
}
2010-05-21 06:37:41 +04:00
if ( ! ctdb - > in ) {
ctdb - > in = new_io_elem ( sizeof ( struct ctdb_req_header ) ) ;
if ( ! ctdb - > in ) {
2010-06-04 14:57:06 +04:00
DEBUG ( ctdb , LOG_ERR ,
" ctdb_service: allocating readbuf " ) ;
2010-05-21 06:37:41 +04:00
ctdb - > broken = true ;
2010-06-04 14:49:25 +04:00
return false ;
2010-05-21 06:37:41 +04:00
}
}
ret = read_io_elem ( ctdb - > fd , ctdb - > in ) ;
if ( real_error ( ret ) < 0 | | ret = = 0 ) {
/* They closed fd? */
if ( ret = = 0 )
errno = EBADF ;
2010-06-04 14:57:06 +04:00
DEBUG ( ctdb , LOG_ERR ,
" ctdb_service: error reading from ctdbd " ) ;
2010-05-21 06:37:41 +04:00
ctdb - > broken = true ;
2010-06-04 14:49:25 +04:00
return false ;
2010-05-21 06:37:41 +04:00
} else if ( ret < 0 ) {
/* No progress, stop loop. */
2010-12-10 05:39:18 +03:00
break ;
2010-05-21 06:37:41 +04:00
} else if ( io_elem_finished ( ctdb - > in ) ) {
2010-12-10 05:39:18 +03:00
io_elem_queue ( ctdb , ctdb - > in ) ;
2010-05-21 06:37:41 +04:00
ctdb - > in = NULL ;
}
}
2010-12-10 05:39:18 +03:00
while ( ctdb - > inqueue ! = NULL ) {
struct io_elem * io = ctdb - > inqueue ;
io_elem_dequeue ( ctdb , io ) ;
handle_incoming ( ctdb , io ) ;
}
2010-06-04 14:49:25 +04:00
return true ;
2010-05-21 06:37:41 +04:00
}
/* This is inefficient. We could pull in idtree.c. */
static bool reqid_used ( const struct ctdb_connection * ctdb , uint32_t reqid )
{
struct ctdb_request * i ;
for ( i = ctdb - > outq ; i ; i = i - > next ) {
if ( i - > hdr . hdr - > reqid = = reqid ) {
return true ;
}
}
for ( i = ctdb - > doneq ; i ; i = i - > next ) {
if ( i - > hdr . hdr - > reqid = = reqid ) {
return true ;
}
}
return false ;
}
uint32_t new_reqid ( struct ctdb_connection * ctdb )
{
while ( reqid_used ( ctdb , ctdb - > next_id ) ) {
ctdb - > next_id + + ;
}
return ctdb - > next_id + + ;
}
struct ctdb_request * new_ctdb_control_request ( struct ctdb_connection * ctdb ,
uint32_t opcode ,
uint32_t destnode ,
const void * extra_data ,
2010-05-24 07:47:36 +04:00
size_t extra ,
ctdb_callback_t callback ,
void * cbdata )
2010-05-21 06:37:41 +04:00
{
struct ctdb_request * req ;
struct ctdb_req_control * pkt ;
2010-06-02 09:13:32 +04:00
req = new_ctdb_request ( offsetof ( struct ctdb_req_control , data ) + extra , callback , cbdata ) ;
2010-05-21 06:37:41 +04:00
if ( ! req )
return NULL ;
io_elem_init_req_header ( req - > io ,
CTDB_REQ_CONTROL , destnode , new_reqid ( ctdb ) ) ;
pkt = req - > hdr . control ;
2010-06-02 10:49:05 +04:00
pkt - > pad = 0 ;
2010-05-21 06:37:41 +04:00
pkt - > opcode = opcode ;
pkt - > srvid = 0 ;
pkt - > client_id = 0 ;
pkt - > flags = 0 ;
pkt - > datalen = extra ;
memcpy ( pkt - > data , extra_data , extra ) ;
2010-05-24 08:22:17 +04:00
DLIST_ADD ( ctdb - > outq , req ) ;
2010-05-21 06:37:41 +04:00
return req ;
}
2010-05-24 07:47:36 +04:00
void ctdb_cancel_callback ( struct ctdb_connection * ctdb ,
struct ctdb_request * req ,
void * unused )
{
2010-06-04 11:24:08 +04:00
ctdb_request_free ( ctdb , req ) ;
2010-05-24 07:47:36 +04:00
}
2010-06-04 14:49:25 +04:00
void ctdb_cancel ( struct ctdb_connection * ctdb , struct ctdb_request * req )
2010-05-21 06:37:41 +04:00
{
2010-06-18 09:45:11 +04:00
if ( ! req - > next & & ! req - > prev ) {
DEBUG ( ctdb , LOG_ALERT ,
" ctdb_cancel: request completed! ctdb_request_free? %p (id %u) " ,
req , req - > hdr . hdr ? req - > hdr . hdr - > reqid : 0 ) ;
ctdb_request_free ( ctdb , req ) ;
return ;
}
2010-06-04 14:57:06 +04:00
DEBUG ( ctdb , LOG_DEBUG , " ctdb_cancel: %p (id %u) " ,
req , req - > hdr . hdr ? req - > hdr . hdr - > reqid : 0 ) ;
2010-05-21 06:37:41 +04:00
/* FIXME: If it's not sent, we could just free it right now. */
2010-05-24 07:47:36 +04:00
req - > callback = ctdb_cancel_callback ;
2010-05-21 06:37:41 +04:00
}
2010-06-18 10:05:52 +04:00
void ctdb_detachdb ( struct ctdb_connection * ctdb , struct ctdb_db * db )
{
cleanup_locks ( ctdb , db ) ;
tdb_close ( db - > tdb ) ;
free ( db ) ;
}
2010-05-21 06:37:41 +04:00
2011-08-18 15:37:23 +04:00
static void destroy_req_db ( struct ctdb_connection * ctdb ,
struct ctdb_request * req ) ;
static void attachdb_done ( struct ctdb_connection * ctdb ,
struct ctdb_request * req ,
void * _db ) ;
2010-05-24 07:47:36 +04:00
static void attachdb_getdbpath_done ( struct ctdb_connection * ctdb ,
struct ctdb_request * req ,
2011-08-18 15:37:23 +04:00
void * _db ) ;
2010-05-24 07:47:36 +04:00
2011-08-18 15:37:23 +04:00
struct ctdb_request *
ctdb_attachdb_send ( struct ctdb_connection * ctdb ,
const char * name , bool persistent , uint32_t tdb_flags ,
ctdb_callback_t callback , void * private_data )
2010-05-24 07:47:36 +04:00
{
2011-08-18 15:37:23 +04:00
struct ctdb_request * req ;
struct ctdb_db * db ;
uint32_t opcode ;
2010-05-21 06:37:41 +04:00
2011-08-18 15:37:23 +04:00
/* FIXME: Search if db already open. */
db = malloc ( sizeof ( * db ) ) ;
if ( ! db ) {
2010-05-24 07:47:36 +04:00
return NULL ;
}
2011-08-18 15:37:23 +04:00
if ( persistent ) {
opcode = CTDB_CONTROL_DB_ATTACH_PERSISTENT ;
} else {
opcode = CTDB_CONTROL_DB_ATTACH ;
2010-06-04 14:57:06 +04:00
}
2011-08-18 15:37:23 +04:00
req = new_ctdb_control_request ( ctdb , opcode , CTDB_CURRENT_NODE , name ,
strlen ( name ) + 1 , attachdb_done , db ) ;
if ( ! req ) {
DEBUG ( ctdb , LOG_ERR ,
" ctdb_attachdb_send: failed allocating DB_ATTACH " ) ;
free ( db ) ;
2010-05-24 07:47:36 +04:00
return NULL ;
2010-05-21 06:37:41 +04:00
}
2011-08-18 15:37:23 +04:00
db - > ctdb = ctdb ;
db - > tdb_flags = tdb_flags ;
db - > persistent = persistent ;
db - > callback = callback ;
db - > private_data = private_data ;
2010-05-21 06:37:41 +04:00
2011-08-18 15:37:23 +04:00
req - > extra_destructor = destroy_req_db ;
/* This is set non-NULL when we succeed, see ctdb_attachdb_recv */
req - > extra = NULL ;
2010-05-21 06:37:41 +04:00
2011-08-18 15:37:23 +04:00
/* Flags get overloaded into srvid. */
req - > hdr . control - > srvid = tdb_flags ;
2010-06-04 14:57:06 +04:00
DEBUG ( db - > ctdb , LOG_DEBUG ,
2011-08-18 15:37:23 +04:00
" ctdb_attachdb_send: DB_ATTACH request %p " , req ) ;
return req ;
}
static void destroy_req_db ( struct ctdb_connection * ctdb ,
struct ctdb_request * req )
{
/* Incomplete db is in priv_data. */
free ( req - > priv_data ) ;
/* second request is chained off this one. */
if ( req - > extra ) {
ctdb_request_free ( ctdb , req - > extra ) ;
}
2010-05-21 06:37:41 +04:00
}
2010-05-24 07:47:36 +04:00
static void attachdb_done ( struct ctdb_connection * ctdb ,
struct ctdb_request * req ,
void * _db )
2010-05-21 06:37:41 +04:00
{
2010-05-24 07:47:36 +04:00
struct ctdb_db * db = _db ;
struct ctdb_request * req2 ;
struct ctdb_reply_control * reply ;
enum ctdb_controls control = CTDB_CONTROL_DB_ATTACH ;
2010-05-21 06:37:41 +04:00
2010-05-24 07:47:36 +04:00
if ( db - > persistent ) {
control = CTDB_CONTROL_DB_ATTACH_PERSISTENT ;
}
2010-06-04 11:24:08 +04:00
reply = unpack_reply_control ( ctdb , req , control ) ;
2010-05-24 07:47:36 +04:00
if ( ! reply | | reply - > status ! = 0 ) {
2010-06-04 14:57:06 +04:00
if ( reply ) {
DEBUG ( ctdb , LOG_ERR ,
" ctdb_attachdb_send(async): DB_ATTACH status %i " ,
reply - > status ) ;
}
2010-05-24 07:47:36 +04:00
/* We failed. Hand request to user and have them discover it
* via ctdb_attachdb_recv . */
2010-06-04 08:04:06 +04:00
db - > callback ( ctdb , req , db - > private_data ) ;
2010-05-21 06:37:41 +04:00
return ;
}
2010-05-24 07:47:36 +04:00
db - > id = * ( uint32_t * ) reply - > data ;
2010-05-21 06:37:41 +04:00
/* Now we do another call, to get the dbpath. */
2010-05-24 07:47:36 +04:00
req2 = new_ctdb_control_request ( db - > ctdb , CTDB_CONTROL_GETDBPATH ,
CTDB_CURRENT_NODE ,
& db - > id , sizeof ( db - > id ) ,
attachdb_getdbpath_done , db ) ;
if ( ! req2 ) {
2010-06-04 14:57:06 +04:00
DEBUG ( db - > ctdb , LOG_ERR ,
" ctdb_attachdb_send(async): failed to allocate " ) ;
2010-06-04 08:04:06 +04:00
db - > callback ( ctdb , req , db - > private_data ) ;
2010-05-21 06:37:41 +04:00
return ;
}
2010-05-24 07:47:36 +04:00
req - > extra = req2 ;
req2 - > extra = req ;
2010-06-04 14:57:06 +04:00
DEBUG ( db - > ctdb , LOG_DEBUG ,
" ctdb_attachdb_send(async): created getdbpath request " ) ;
2010-05-24 07:47:36 +04:00
}
2011-08-18 15:37:23 +04:00
static void attachdb_getdbpath_done ( struct ctdb_connection * ctdb ,
struct ctdb_request * req ,
void * _db )
2010-05-24 07:47:36 +04:00
{
2011-08-18 15:37:23 +04:00
struct ctdb_db * db = _db ;
/* Do callback on original request. */
db - > callback ( ctdb , req - > extra , db - > private_data ) ;
2010-05-21 06:37:41 +04:00
}
2011-08-18 15:37:23 +04:00
struct ctdb_db * ctdb_attachdb_recv ( struct ctdb_connection * ctdb ,
struct ctdb_request * req )
2010-05-21 06:37:41 +04:00
{
2011-08-18 15:37:23 +04:00
struct ctdb_request * dbpath_req = req - > extra ;
struct ctdb_reply_control * reply ;
struct ctdb_db * db = req - > priv_data ;
uint32_t tdb_flags = db - > tdb_flags ;
struct tdb_logging_context log ;
2010-05-21 06:37:41 +04:00
2011-08-18 15:37:23 +04:00
/* Never sent the dbpath request? We've failed. */
if ( ! dbpath_req ) {
/* FIXME: Save errno? */
errno = EINVAL ;
2010-05-21 06:37:41 +04:00
return NULL ;
}
2011-08-18 15:37:23 +04:00
reply = unpack_reply_control ( ctdb , dbpath_req , CTDB_CONTROL_GETDBPATH ) ;
if ( ! reply ) {
return NULL ;
2010-05-21 06:37:41 +04:00
}
2011-08-18 15:37:23 +04:00
if ( reply - > status ! = 0 ) {
DEBUG ( db - > ctdb , LOG_ERR ,
" ctdb_attachdb_recv: reply status %i " , reply - > status ) ;
2010-05-21 06:37:41 +04:00
return NULL ;
}
2011-08-18 15:37:23 +04:00
tdb_flags = db - > persistent ? TDB_DEFAULT : TDB_NOSYNC ;
tdb_flags | = TDB_DISALLOW_NESTING ;
2010-05-21 06:37:41 +04:00
2011-08-18 15:37:23 +04:00
log . log_fn = ctdb_tdb_log_bridge ;
log . log_private = ctdb ;
db - > tdb = tdb_open_ex ( ( char * ) reply - > data , 0 , tdb_flags , O_RDWR , 0 ,
& log , NULL ) ;
if ( db - > tdb = = NULL ) {
DEBUG ( db - > ctdb , LOG_ERR ,
" ctdb_attachdb_recv: failed to tdb_open %s " ,
( char * ) reply - > data ) ;
return NULL ;
}
2010-05-21 06:37:41 +04:00
2011-08-18 15:37:23 +04:00
/* Finally, separate the db from the request (see destroy_req_db). */
req - > priv_data = NULL ;
2010-06-04 14:57:06 +04:00
DEBUG ( db - > ctdb , LOG_DEBUG ,
2011-08-18 15:37:23 +04:00
" ctdb_attachdb_recv: db %p, tdb %s " , db , ( char * ) reply - > data ) ;
return db ;
2010-05-21 06:37:41 +04:00
}
2010-06-08 11:22:23 +04:00
static unsigned long lock_magic ( struct ctdb_lock * lock )
2010-05-21 06:37:41 +04:00
{
2010-06-08 11:22:23 +04:00
/* A non-zero magic specific to this structure. */
return ( ( unsigned long ) lock - > key . dptr
^ ( ( ( unsigned long ) lock - > key . dptr ) < < 16 )
^ 0xBADC0FFEEBADC0DEULL )
| 1 ;
2010-06-05 09:38:11 +04:00
}
2010-06-08 11:22:23 +04:00
/* This is only called on locks before they're held. */
static void free_lock ( struct ctdb_lock * lock )
2010-06-05 09:38:11 +04:00
{
2010-06-08 11:22:23 +04:00
if ( lock - > held_magic ) {
2010-06-08 11:23:17 +04:00
DEBUG ( lock - > ctdb_db - > ctdb , LOG_ALERT ,
2010-06-08 11:22:23 +04:00
" free_lock invalid lock %p " , lock ) ;
2010-06-05 09:38:11 +04:00
}
2010-06-04 08:47:06 +04:00
free ( lock - > hdr ) ;
2010-05-21 06:37:41 +04:00
free ( lock ) ;
}
2010-06-08 11:22:23 +04:00
2010-06-08 11:41:40 +04:00
void ctdb_release_lock ( struct ctdb_db * ctdb_db , struct ctdb_lock * lock )
2010-06-05 09:38:11 +04:00
{
2010-06-08 11:22:23 +04:00
if ( lock - > held_magic ! = lock_magic ( lock ) ) {
2010-06-08 11:23:17 +04:00
DEBUG ( lock - > ctdb_db - > ctdb , LOG_ALERT ,
2010-06-08 11:22:23 +04:00
" ctdb_release_lock invalid lock %p " , lock ) ;
2010-06-08 11:41:40 +04:00
} else if ( lock - > ctdb_db ! = ctdb_db ) {
errno = EBADF ;
DEBUG ( ctdb_db - > ctdb , LOG_ALERT ,
" ctdb_release_lock: wrong ctdb_db. " ) ;
2010-06-08 11:22:23 +04:00
} else {
tdb_chainunlock ( lock - > ctdb_db - > tdb , lock - > key ) ;
DEBUG ( lock - > ctdb_db - > ctdb , LOG_DEBUG ,
" ctdb_release_lock %p " , lock ) ;
remove_lock ( lock - > ctdb_db - > ctdb , lock ) ;
}
lock - > held_magic = 0 ;
free_lock ( lock ) ;
2010-06-05 09:38:11 +04:00
}
2010-06-08 11:22:23 +04:00
2010-05-21 06:37:41 +04:00
/* We keep the lock if local node is the dmaster. */
2010-06-04 08:03:08 +04:00
static bool try_readrecordlock ( struct ctdb_lock * lock , TDB_DATA * data )
2010-05-21 06:37:41 +04:00
{
2010-05-24 07:47:36 +04:00
struct ctdb_ltdb_header * hdr ;
2010-05-21 06:37:41 +04:00
if ( tdb_chainlock ( lock - > ctdb_db - > tdb , lock - > key ) ! = 0 ) {
2010-06-04 14:57:06 +04:00
DEBUG ( lock - > ctdb_db - > ctdb , LOG_WARNING ,
" ctdb_readrecordlock_async: failed to chainlock " ) ;
2010-05-24 07:47:36 +04:00
return NULL ;
2010-05-21 06:37:41 +04:00
}
2010-06-04 08:03:08 +04:00
hdr = ctdb_local_fetch ( lock - > ctdb_db - > tdb , lock - > key , data ) ;
2010-05-24 07:47:36 +04:00
if ( hdr & & hdr - > dmaster = = lock - > ctdb_db - > ctdb - > pnn ) {
2010-06-04 14:57:06 +04:00
DEBUG ( lock - > ctdb_db - > ctdb , LOG_DEBUG ,
" ctdb_readrecordlock_async: got local lock " ) ;
2010-06-08 11:22:23 +04:00
lock - > held_magic = lock_magic ( lock ) ;
2010-05-24 07:47:36 +04:00
lock - > hdr = hdr ;
2010-06-04 14:11:42 +04:00
add_lock ( lock - > ctdb_db - > ctdb , lock ) ;
2010-05-21 06:37:41 +04:00
return true ;
}
tdb_chainunlock ( lock - > ctdb_db - > tdb , lock - > key ) ;
2010-05-24 07:47:36 +04:00
free ( hdr ) ;
return NULL ;
2010-05-21 06:37:41 +04:00
}
2010-06-04 08:03:08 +04:00
/* If they shutdown before we hand them the lock, we free it here. */
2010-06-04 11:24:08 +04:00
static void destroy_lock ( struct ctdb_connection * ctdb ,
struct ctdb_request * req )
2010-05-24 07:47:36 +04:00
{
2010-06-08 11:22:23 +04:00
free_lock ( req - > extra ) ;
2010-05-24 07:47:36 +04:00
}
2010-05-21 06:37:41 +04:00
2010-05-24 07:47:36 +04:00
static void readrecordlock_retry ( struct ctdb_connection * ctdb ,
struct ctdb_request * req , void * private )
2010-05-21 06:37:41 +04:00
{
2010-05-24 07:47:36 +04:00
struct ctdb_lock * lock = req - > extra ;
struct ctdb_reply_call * reply ;
2010-06-04 08:03:08 +04:00
TDB_DATA data ;
2010-05-21 06:37:41 +04:00
2010-05-24 07:47:36 +04:00
/* OK, we've received reply to noop migration */
2010-06-04 11:24:08 +04:00
reply = unpack_reply_call ( ctdb , req , CTDB_NULL_FUNC ) ;
2010-05-24 07:47:36 +04:00
if ( ! reply | | reply - > status ! = 0 ) {
2010-06-04 14:57:06 +04:00
if ( reply ) {
DEBUG ( ctdb , LOG_ERR ,
" ctdb_readrecordlock_async(async): "
" NULL_FUNC returned %i " , reply - > status ) ;
}
2010-06-04 08:03:08 +04:00
lock - > callback ( lock - > ctdb_db , NULL , tdb_null , private ) ;
2010-06-04 11:24:08 +04:00
ctdb_request_free ( ctdb , req ) ; /* Also frees lock. */
2010-05-21 06:37:41 +04:00
return ;
}
2010-05-24 07:47:36 +04:00
/* Can we get lock now? */
2010-06-04 08:03:08 +04:00
if ( try_readrecordlock ( lock , & data ) ) {
/* Now it's their responsibility to free lock & request! */
req - > extra_destructor = NULL ;
lock - > callback ( lock - > ctdb_db , lock , data , private ) ;
2010-06-21 09:17:34 +04:00
ctdb_request_free ( ctdb , req ) ;
2010-05-21 06:37:41 +04:00
return ;
}
2010-05-24 07:47:36 +04:00
/* Retransmit the same request again (we lost race). */
io_elem_reset ( req - > io ) ;
2010-05-24 08:22:17 +04:00
DLIST_ADD ( ctdb - > outq , req ) ;
2010-05-21 06:37:41 +04:00
}
2010-05-24 08:22:17 +04:00
bool
2010-06-04 08:03:08 +04:00
ctdb_readrecordlock_async ( struct ctdb_db * ctdb_db , TDB_DATA key ,
ctdb_rrl_callback_t callback , void * cbdata )
2010-05-21 06:37:41 +04:00
{
struct ctdb_request * req ;
struct ctdb_lock * lock ;
2010-06-04 08:03:08 +04:00
TDB_DATA data ;
2010-05-21 06:37:41 +04:00
2010-06-04 14:11:42 +04:00
if ( holding_lock ( ctdb_db - > ctdb ) ) {
2010-06-08 11:23:17 +04:00
DEBUG ( ctdb_db - > ctdb , LOG_ALERT ,
2010-06-04 14:11:42 +04:00
" ctdb_readrecordlock_async: already holding lock " ) ;
return false ;
}
2010-06-04 08:03:08 +04:00
/* Setup lock */
2010-05-24 07:47:36 +04:00
lock = malloc ( sizeof ( * lock ) + key . dsize ) ;
if ( ! lock ) {
2010-06-04 14:57:06 +04:00
DEBUG ( ctdb_db - > ctdb , LOG_ERR ,
" ctdb_readrecordlock_async: lock allocation failed " ) ;
2010-06-04 08:03:08 +04:00
return false ;
2010-05-21 06:37:41 +04:00
}
2010-05-24 07:47:36 +04:00
lock - > key . dptr = ( void * ) ( lock + 1 ) ;
2010-05-21 06:37:41 +04:00
memcpy ( lock - > key . dptr , key . dptr , key . dsize ) ;
lock - > key . dsize = key . dsize ;
lock - > ctdb_db = ctdb_db ;
lock - > hdr = NULL ;
2010-06-08 11:22:23 +04:00
lock - > held_magic = 0 ;
2010-05-21 06:37:41 +04:00
2010-06-04 08:03:08 +04:00
/* Fast path. */
if ( try_readrecordlock ( lock , & data ) ) {
callback ( ctdb_db , lock , data , cbdata ) ;
return true ;
}
/* Slow path: create request. */
2010-06-02 09:13:32 +04:00
req = new_ctdb_request ( offsetof ( struct ctdb_req_call , data )
2010-06-04 08:03:08 +04:00
+ key . dsize , readrecordlock_retry , cbdata ) ;
2010-05-24 07:47:36 +04:00
if ( ! req ) {
2010-06-04 14:57:06 +04:00
DEBUG ( ctdb_db - > ctdb , LOG_ERR ,
" ctdb_readrecordlock_async: allocation failed " ) ;
2010-06-08 11:22:23 +04:00
free_lock ( lock ) ;
2010-05-24 07:47:36 +04:00
return NULL ;
}
req - > extra = lock ;
req - > extra_destructor = destroy_lock ;
/* We store the original callback in the lock, and use our own. */
lock - > callback = callback ;
io_elem_init_req_header ( req - > io , CTDB_REQ_CALL , CTDB_CURRENT_NODE ,
new_reqid ( ctdb_db - > ctdb ) ) ;
req - > hdr . call - > flags = CTDB_IMMEDIATE_MIGRATION ;
req - > hdr . call - > db_id = ctdb_db - > id ;
req - > hdr . call - > callid = CTDB_NULL_FUNC ;
req - > hdr . call - > hopcount = 0 ;
req - > hdr . call - > keylen = key . dsize ;
req - > hdr . call - > calldatalen = 0 ;
memcpy ( req - > hdr . call - > data , key . dptr , key . dsize ) ;
2010-05-24 08:22:17 +04:00
DLIST_ADD ( ctdb_db - > ctdb - > outq , req ) ;
return true ;
2010-05-21 06:37:41 +04:00
}
2010-06-08 11:41:40 +04:00
bool ctdb_writerecord ( struct ctdb_db * ctdb_db ,
struct ctdb_lock * lock , TDB_DATA data )
2010-05-21 06:37:41 +04:00
{
2010-06-08 11:41:40 +04:00
if ( lock - > ctdb_db ! = ctdb_db ) {
errno = EBADF ;
DEBUG ( ctdb_db - > ctdb , LOG_ALERT ,
" ctdb_writerecord: Can not write, wrong ctdb_db. " ) ;
return false ;
}
2010-06-08 11:22:23 +04:00
if ( lock - > held_magic ! = lock_magic ( lock ) ) {
2010-06-05 09:43:01 +04:00
errno = EBADF ;
2010-06-08 11:41:40 +04:00
DEBUG ( ctdb_db - > ctdb , LOG_ALERT ,
2010-06-05 09:43:01 +04:00
" ctdb_writerecord: Can not write. Lock has been released. " ) ;
2010-06-08 11:41:40 +04:00
return false ;
2010-06-05 09:43:01 +04:00
}
2010-06-08 11:41:40 +04:00
if ( ctdb_db - > persistent ) {
2010-06-04 14:57:06 +04:00
errno = EINVAL ;
2010-06-08 11:41:40 +04:00
DEBUG ( ctdb_db - > ctdb , LOG_ALERT ,
2010-06-04 14:57:06 +04:00
" ctdb_writerecord: cannot write to persistent db " ) ;
2010-06-08 11:41:40 +04:00
return false ;
2010-05-21 06:37:41 +04:00
}
2010-06-08 12:40:36 +04:00
switch ( ctdb_local_store ( ctdb_db - > tdb , lock - > key , lock - > hdr , data ) ) {
case 0 :
DEBUG ( ctdb_db - > ctdb , LOG_DEBUG ,
" ctdb_writerecord: optimized away noop write. " ) ;
/* fall thru */
case 1 :
return true ;
default :
switch ( errno ) {
case ENOMEM :
DEBUG ( ctdb_db - > ctdb , LOG_CRIT ,
" ctdb_writerecord: out of memory. " ) ;
break ;
case EINVAL :
DEBUG ( ctdb_db - > ctdb , LOG_ALERT ,
" ctdb_writerecord: record changed under lock? " ) ;
break ;
2010-06-08 12:39:42 +04:00
default : /* TDB already logged. */
2010-06-08 12:40:36 +04:00
break ;
}
return false ;
}
2010-05-21 06:37:41 +04:00
}
2011-01-14 09:35:31 +03:00
struct ctdb_traverse_state {
struct ctdb_request * handle ;
struct ctdb_db * ctdb_db ;
uint64_t srvid ;
ctdb_traverse_callback_t callback ;
void * cbdata ;
} ;
static void traverse_remhnd_cb ( struct ctdb_connection * ctdb ,
struct ctdb_request * req , void * private_data )
{
struct ctdb_traverse_state * state = private_data ;
if ( ! ctdb_remove_message_handler_recv ( ctdb , state - > handle ) ) {
DEBUG ( ctdb , LOG_ERR ,
" Failed to remove message handler for "
" traverse. " ) ;
state - > callback ( state - > ctdb_db - > ctdb , state - > ctdb_db ,
TRAVERSE_STATUS_ERROR ,
tdb_null , tdb_null ,
state - > cbdata ) ;
}
ctdb_request_free ( ctdb , state - > handle ) ;
state - > handle = NULL ;
free ( state ) ;
}
static void msg_h ( struct ctdb_connection * ctdb , uint64_t srvid ,
TDB_DATA data , void * private_data )
{
struct ctdb_traverse_state * state = private_data ;
struct ctdb_db * ctdb_db = state - > ctdb_db ;
struct ctdb_rec_data * d = ( struct ctdb_rec_data * ) data . dptr ;
TDB_DATA key ;
if ( data . dsize < sizeof ( uint32_t ) | |
d - > length ! = data . dsize ) {
DEBUG ( ctdb , LOG_ERR ,
" Bad data size %u in traverse_handler " ,
( unsigned ) data . dsize ) ;
state - > callback ( state - > ctdb_db - > ctdb , state - > ctdb_db ,
TRAVERSE_STATUS_ERROR ,
tdb_null , tdb_null ,
state - > cbdata ) ;
state - > handle = ctdb_remove_message_handler_send (
state - > ctdb_db - > ctdb , state - > srvid ,
msg_h , state ,
traverse_remhnd_cb , state ) ;
return ;
}
key . dsize = d - > keylen ;
key . dptr = & d - > data [ 0 ] ;
data . dsize = d - > datalen ;
data . dptr = & d - > data [ d - > keylen ] ;
if ( key . dsize = = 0 & & data . dsize = = 0 ) {
state - > callback ( state - > ctdb_db - > ctdb , state - > ctdb_db ,
TRAVERSE_STATUS_FINISHED ,
tdb_null , tdb_null ,
state - > cbdata ) ;
state - > handle = ctdb_remove_message_handler_send (
state - > ctdb_db - > ctdb , state - > srvid ,
msg_h , state ,
traverse_remhnd_cb , state ) ;
return ;
}
if ( data . dsize < = sizeof ( struct ctdb_ltdb_header ) ) {
/* empty records are deleted records in ctdb */
return ;
}
data . dsize - = sizeof ( struct ctdb_ltdb_header ) ;
data . dptr + = sizeof ( struct ctdb_ltdb_header ) ;
if ( state - > callback ( ctdb , ctdb_db ,
TRAVERSE_STATUS_RECORD ,
key , data , state - > cbdata ) ! = 0 ) {
state - > handle = ctdb_remove_message_handler_send (
state - > ctdb_db - > ctdb , state - > srvid ,
msg_h , state ,
traverse_remhnd_cb , state ) ;
return ;
}
}
static void traverse_start_cb ( struct ctdb_connection * ctdb ,
struct ctdb_request * req , void * private_data )
{
struct ctdb_traverse_state * state = private_data ;
ctdb_request_free ( ctdb , state - > handle ) ;
state - > handle = NULL ;
}
static void traverse_msghnd_cb ( struct ctdb_connection * ctdb ,
struct ctdb_request * req , void * private_data )
{
struct ctdb_traverse_state * state = private_data ;
struct ctdb_db * ctdb_db = state - > ctdb_db ;
struct ctdb_traverse_start t ;
if ( ! ctdb_set_message_handler_recv ( ctdb , state - > handle ) ) {
DEBUG ( ctdb , LOG_ERR ,
" Failed to register message handler for "
" traverse. " ) ;
state - > callback ( state - > ctdb_db - > ctdb , state - > ctdb_db ,
TRAVERSE_STATUS_ERROR ,
tdb_null , tdb_null ,
state - > cbdata ) ;
ctdb_request_free ( ctdb , state - > handle ) ;
state - > handle = NULL ;
free ( state ) ;
return ;
}
ctdb_request_free ( ctdb , state - > handle ) ;
state - > handle = NULL ;
t . db_id = ctdb_db - > id ;
t . srvid = state - > srvid ;
t . reqid = 0 ;
state - > handle = new_ctdb_control_request ( ctdb ,
CTDB_CONTROL_TRAVERSE_START ,
CTDB_CURRENT_NODE ,
& t , sizeof ( t ) ,
traverse_start_cb , state ) ;
if ( state - > handle = = NULL ) {
DEBUG ( ctdb , LOG_ERR ,
" ctdb_traverse_async: "
" failed to send traverse_start control " ) ;
state - > callback ( state - > ctdb_db - > ctdb , state - > ctdb_db ,
TRAVERSE_STATUS_ERROR ,
tdb_null , tdb_null ,
state - > cbdata ) ;
state - > handle = ctdb_remove_message_handler_send (
state - > ctdb_db - > ctdb , state - > srvid ,
msg_h , state ,
traverse_remhnd_cb , state ) ;
return ;
}
}
bool ctdb_traverse_async ( struct ctdb_db * ctdb_db ,
ctdb_traverse_callback_t callback , void * cbdata )
{
struct ctdb_connection * ctdb = ctdb_db - > ctdb ;
struct ctdb_traverse_state * state ;
static uint32_t tid = 0 ;
state = malloc ( sizeof ( struct ctdb_traverse_state ) ) ;
if ( state = = NULL ) {
DEBUG ( ctdb , LOG_ERR ,
" ctdb_traverse_async: no memory. "
" allocate state failed " ) ;
return false ;
}
tid + + ;
state - > srvid = CTDB_SRVID_TRAVERSE_RANGE | tid ;
state - > callback = callback ;
state - > cbdata = cbdata ;
state - > ctdb_db = ctdb_db ;
state - > handle = ctdb_set_message_handler_send ( ctdb_db - > ctdb ,
state - > srvid ,
msg_h , state ,
traverse_msghnd_cb , state ) ;
if ( state - > handle = = NULL ) {
DEBUG ( ctdb , LOG_ERR ,
" ctdb_traverse_async: "
" failed ctdb_set_message_handler_send " ) ;
free ( state ) ;
return false ;
}
return true ;
}