2015-12-08 08:23:50 +03:00
/*
CTDB mutex fcntl lock file helper
Copyright ( C ) Martin Schwenke 2015
2019-06-27 06:45:01 +03:00
wait_for_parent ( ) code from ctdb_lock_helper . c :
Copyright ( C ) Amitay Isaacs 2013
2015-12-08 08:23:50 +03: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
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/filesys.h"
# include "system/network.h"
2019-06-27 06:45:01 +03:00
# include <tevent.h>
2016-11-29 04:55:06 +03:00
# include "lib/util/sys_rw.h"
2019-06-27 06:45:01 +03:00
# include "lib/util/tevent_unix.h"
2019-06-27 09:14:26 +03:00
# include "lib/util/util.h"
2020-07-03 09:11:20 +03:00
# include "lib/util/smb_strtox.h"
2016-11-29 04:55:06 +03:00
2015-12-08 08:23:50 +03:00
/* protocol.h is just needed for ctdb_sock_addr, which is used in system.h */
# include "protocol/protocol.h"
# include "common/system.h"
static char * progname = NULL ;
2016-07-28 07:04:23 +03:00
static char fcntl_lock ( const char * file , int * outfd )
2015-12-08 08:23:50 +03:00
{
int fd ;
struct flock lock ;
fd = open ( file , O_RDWR | O_CREAT , 0600 ) ;
if ( fd = = - 1 ) {
fprintf ( stderr , " %s: Unable to open %s - (%s) \n " ,
progname , file , strerror ( errno ) ) ;
return ' 3 ' ;
}
lock . l_type = F_WRLCK ;
lock . l_whence = SEEK_SET ;
lock . l_start = 0 ;
lock . l_len = 1 ;
lock . l_pid = 0 ;
if ( fcntl ( fd , F_SETLK , & lock ) ! = 0 ) {
int saved_errno = errno ;
close ( fd ) ;
if ( saved_errno = = EACCES | |
saved_errno = = EAGAIN ) {
/* Lock contention, fail silently */
return ' 1 ' ;
}
/* Log an error for any other failure */
fprintf ( stderr ,
" %s: Failed to get lock on '%s' - (%s) \n " ,
progname , file , strerror ( saved_errno ) ) ;
return ' 3 ' ;
}
2016-07-28 07:04:23 +03:00
* outfd = fd ;
2015-12-08 08:23:50 +03:00
return ' 0 ' ;
}
2019-06-27 06:45:01 +03:00
/*
* Wait and see if the parent exits
*/
struct wait_for_parent_state {
struct tevent_context * ev ;
pid_t ppid ;
} ;
static void wait_for_parent_check ( struct tevent_req * subreq ) ;
static struct tevent_req * wait_for_parent_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
pid_t ppid )
{
struct tevent_req * req , * subreq ;
struct wait_for_parent_state * state ;
req = tevent_req_create ( mem_ctx , & state , struct wait_for_parent_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
state - > ppid = ppid ;
if ( ppid = = 1 ) {
fprintf ( stderr , " parent == 1 \n " ) ;
tevent_req_done ( req ) ;
return tevent_req_post ( req , ev ) ;
}
subreq = tevent_wakeup_send ( state , ev ,
tevent_timeval_current_ofs ( 5 , 0 ) ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , wait_for_parent_check , req ) ;
return req ;
}
static void wait_for_parent_check ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct wait_for_parent_state * state = tevent_req_data (
req , struct wait_for_parent_state ) ;
bool status ;
status = tevent_wakeup_recv ( subreq ) ;
TALLOC_FREE ( subreq ) ;
if ( ! status ) {
/* Ignore error */
fprintf ( stderr ,
" ctdb_mutex_fcntl_helper: "
" tevent_wakeup_recv() failed \n " ) ;
}
if ( kill ( state - > ppid , 0 ) = = - 1 & & errno = = ESRCH ) {
fprintf ( stderr , " parent gone \n " ) ;
tevent_req_done ( req ) ;
return ;
}
subreq = tevent_wakeup_send ( state , state - > ev ,
tevent_timeval_current_ofs ( 5 , 0 ) ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq , wait_for_parent_check , req ) ;
}
2022-01-21 04:09:45 +03:00
static bool wait_for_parent_recv ( struct tevent_req * req , int * perr )
2019-06-27 06:45:01 +03:00
{
2022-01-21 04:09:45 +03:00
if ( tevent_req_is_unix_error ( req , perr ) ) {
2019-06-27 06:45:01 +03:00
return false ;
}
return true ;
}
2019-06-27 09:14:26 +03:00
/*
* Wait and check for lost lock - file removed or replaced
*/
struct wait_for_lost_state {
struct tevent_context * ev ;
const char * lock_file ;
ino_t inode ;
unsigned long recheck_time ;
} ;
static void wait_for_lost_check ( struct tevent_req * subreq ) ;
static struct tevent_req * wait_for_lost_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
const char * lock_file ,
int fd ,
unsigned long recheck_time )
{
struct tevent_req * req , * subreq ;
struct wait_for_lost_state * state ;
struct stat sb ;
int ret ;
req = tevent_req_create ( mem_ctx , & state , struct wait_for_lost_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
state - > lock_file = lock_file ;
state - > recheck_time = recheck_time ;
ret = fstat ( fd , & sb ) ;
if ( ret ! = 0 ) {
fprintf ( stderr ,
" ctdb_mutex_fcntl_helper: "
" lock lost - lock file \" %s \" check failed (ret=%d) \n " ,
state - > lock_file ,
errno ) ;
tevent_req_done ( req ) ;
return tevent_req_post ( req , ev ) ;
}
state - > inode = sb . st_ino ;
subreq = tevent_wakeup_send (
state ,
ev ,
tevent_timeval_current_ofs ( state - > recheck_time , 0 ) ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , wait_for_lost_check , req ) ;
return req ;
}
static void wait_for_lost_check ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct wait_for_lost_state * state = tevent_req_data (
req , struct wait_for_lost_state ) ;
bool status ;
struct stat sb ;
int ret ;
status = tevent_wakeup_recv ( subreq ) ;
TALLOC_FREE ( subreq ) ;
if ( ! status ) {
/* Ignore error */
fprintf ( stderr ,
" ctdb_mutex_fcntl_helper: "
" tevent_wakeup_recv() failed \n " ) ;
}
ret = stat ( state - > lock_file , & sb ) ;
if ( ret ! = 0 ) {
fprintf ( stderr ,
" ctdb_mutex_fcntl_helper: "
" lock lost - lock file \" %s \" check failed (ret=%d) \n " ,
state - > lock_file ,
errno ) ;
tevent_req_done ( req ) ;
return ;
}
if ( sb . st_ino ! = state - > inode ) {
fprintf ( stderr ,
" ctdb_mutex_fcntl_helper: "
" lock lost - lock file \" %s \" inode changed \n " ,
state - > lock_file ) ;
tevent_req_done ( req ) ;
return ;
}
subreq = tevent_wakeup_send (
state ,
state - > ev ,
tevent_timeval_current_ofs ( state - > recheck_time , 0 ) ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq , wait_for_lost_check , req ) ;
}
2022-01-21 04:09:45 +03:00
static bool wait_for_lost_recv ( struct tevent_req * req , int * perr )
2019-06-27 09:14:26 +03:00
{
2022-01-21 04:09:45 +03:00
if ( tevent_req_is_unix_error ( req , perr ) ) {
2019-06-27 09:14:26 +03:00
return false ;
}
return true ;
}
2019-06-27 07:13:50 +03:00
/*
* Wait for a reason to exit , indicating that the lock is lost
*/
struct wait_for_exit_state {
} ;
static void wait_for_exit_parent_done ( struct tevent_req * subreq ) ;
2019-06-27 09:14:26 +03:00
static void wait_for_exit_lost_done ( struct tevent_req * subreq ) ;
2019-06-27 07:13:50 +03:00
static struct tevent_req * wait_for_exit_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
2019-06-27 09:14:26 +03:00
pid_t ppid ,
const char * lock_file ,
int fd ,
unsigned long recheck_time )
2019-06-27 07:13:50 +03:00
{
struct tevent_req * req , * subreq ;
struct wait_for_exit_state * state ;
req = tevent_req_create ( mem_ctx , & state , struct wait_for_exit_state ) ;
if ( req = = NULL ) {
return NULL ;
}
subreq = wait_for_parent_send ( state , ev , ppid ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , wait_for_exit_parent_done , req ) ;
2019-06-27 09:14:26 +03:00
if ( recheck_time > 0 ) {
subreq = wait_for_lost_send ( state ,
ev ,
lock_file ,
fd ,
recheck_time ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , wait_for_exit_lost_done , req ) ;
}
2019-06-27 07:13:50 +03:00
return req ;
}
static void wait_for_exit_parent_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
bool status ;
2022-01-21 04:09:45 +03:00
int err ;
2019-06-27 07:13:50 +03:00
2022-01-21 04:09:45 +03:00
status = wait_for_parent_recv ( subreq , & err ) ;
2019-06-27 07:13:50 +03:00
TALLOC_FREE ( subreq ) ;
if ( ! status ) {
/* Ignore error */
fprintf ( stderr ,
" ctdb_mutex_fcntl_helper: "
2022-01-21 04:09:45 +03:00
" wait_for_parent_recv() failed (%d) \n " ,
err ) ;
2019-06-27 07:13:50 +03:00
}
tevent_req_done ( req ) ;
}
2019-06-27 09:14:26 +03:00
static void wait_for_exit_lost_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
bool status ;
2022-01-21 04:09:45 +03:00
int err ;
2019-06-27 09:14:26 +03:00
2022-01-21 04:09:45 +03:00
status = wait_for_lost_recv ( subreq , & err ) ;
2019-06-27 09:14:26 +03:00
TALLOC_FREE ( subreq ) ;
if ( ! status ) {
/* Ignore error */
fprintf ( stderr ,
" ctdb_mutex_fcntl_helper: "
2022-01-21 04:09:45 +03:00
" wait_for_lost_recv() failed (%d) \n " ,
err ) ;
2019-06-27 09:14:26 +03:00
}
tevent_req_done ( req ) ;
}
2022-01-21 04:09:45 +03:00
static bool wait_for_exit_recv ( struct tevent_req * req , int * perr )
2019-06-27 07:13:50 +03:00
{
2022-01-21 04:09:45 +03:00
if ( tevent_req_is_unix_error ( req , perr ) ) {
2019-06-27 07:13:50 +03:00
return false ;
}
return true ;
}
2019-06-27 09:14:26 +03:00
static void usage ( void )
{
fprintf ( stderr , " Usage: %s <file> [recheck_time] \n " , progname ) ;
}
2015-12-08 08:23:50 +03:00
int main ( int argc , char * argv [ ] )
{
2019-06-27 06:45:01 +03:00
struct tevent_context * ev ;
2015-12-08 08:23:50 +03:00
char result ;
int ppid ;
const char * file = NULL ;
2019-06-27 09:14:26 +03:00
unsigned long recheck_time ;
int ret ;
2016-07-28 07:04:23 +03:00
int fd = - 1 ;
2019-06-27 06:45:01 +03:00
struct tevent_req * req ;
bool status ;
2015-12-08 08:23:50 +03:00
progname = argv [ 0 ] ;
2019-06-27 09:14:26 +03:00
if ( argc < 2 | | argc > 3 ) {
usage ( ) ;
2015-12-08 08:23:50 +03:00
exit ( 1 ) ;
}
2019-06-27 06:45:01 +03:00
ev = tevent_context_init ( NULL ) ;
if ( ev = = NULL ) {
fprintf ( stderr , " locking: tevent_context_init() failed \n " ) ;
2016-08-05 07:17:01 +03:00
exit ( 1 ) ;
}
2019-06-27 06:45:01 +03:00
ppid = getppid ( ) ;
2015-12-08 08:23:50 +03:00
file = argv [ 1 ] ;
2020-01-10 06:25:39 +03:00
recheck_time = 5 ;
2019-06-27 09:14:26 +03:00
if ( argc = = 3 ) {
recheck_time = smb_strtoul ( argv [ 2 ] ,
NULL ,
10 ,
& ret ,
SMB_STR_STANDARD ) ;
if ( ret ! = 0 ) {
usage ( ) ;
exit ( 1 ) ;
}
}
2016-07-28 07:04:23 +03:00
result = fcntl_lock ( file , & fd ) ;
2015-12-08 08:23:50 +03:00
sys_write ( STDOUT_FILENO , & result , 1 ) ;
2019-07-05 08:39:23 +03:00
if ( result ! = ' 0 ' ) {
return 0 ;
}
2019-06-27 09:14:26 +03:00
req = wait_for_exit_send ( ev , ev , ppid , file , fd , recheck_time ) ;
2019-06-27 06:45:01 +03:00
if ( req = = NULL ) {
fprintf ( stderr ,
2019-06-27 07:13:50 +03:00
" %s: wait_for_exit_send() failed \n " ,
2019-06-27 06:45:01 +03:00
progname ) ;
exit ( 1 ) ;
}
tevent_req_poll ( req , ev ) ;
2022-01-21 04:09:45 +03:00
status = wait_for_exit_recv ( req , & ret ) ;
2019-06-27 06:45:01 +03:00
if ( ! status ) {
fprintf ( stderr ,
2022-01-21 04:09:45 +03:00
" %s: wait_for_exit_recv() failed (%d) \n " ,
progname ,
ret ) ;
2019-06-27 06:45:01 +03:00
}
2015-12-08 08:23:50 +03:00
2016-07-28 07:04:23 +03:00
if ( fd ! = - 1 ) {
close ( fd ) ;
}
2015-12-08 08:23:50 +03:00
return 0 ;
}