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"
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 ) ;
}
static bool wait_for_parent_recv ( struct tevent_req * req )
{
if ( tevent_req_is_unix_error ( req , NULL ) ) {
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 ) ;
static struct tevent_req * wait_for_exit_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
pid_t ppid )
{
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 ) ;
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 ;
status = wait_for_parent_recv ( subreq ) ;
TALLOC_FREE ( subreq ) ;
if ( ! status ) {
/* Ignore error */
fprintf ( stderr ,
" ctdb_mutex_fcntl_helper: "
" wait_for_parent_recv() failed \n " ) ;
}
tevent_req_done ( req ) ;
}
static bool wait_for_exit_recv ( struct tevent_req * req )
{
if ( tevent_req_is_unix_error ( req , NULL ) ) {
return false ;
}
return true ;
}
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 ;
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 ] ;
if ( argc ! = 2 ) {
fprintf ( stderr , " Usage: %s <file> \n " , progname ) ;
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 ] ;
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 07:13:50 +03:00
req = wait_for_exit_send ( ev , ev , ppid ) ;
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 ) ;
2019-06-27 07:13:50 +03:00
status = wait_for_exit_recv ( req ) ;
2019-06-27 06:45:01 +03:00
if ( ! status ) {
fprintf ( stderr ,
2019-06-27 07:13:50 +03:00
" %s: wait_for_exit_recv() failed \n " ,
2019-06-27 06:45:01 +03:00
progname ) ;
}
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 ;
}