2015-03-13 17:01:25 +03:00
/*
2007-07-04 11:45:46 +04:00
event script handling
Copyright ( C ) Andrew Tridgell 2007
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-07-04 11:45:46 +04:00
( at your option ) any later version .
2015-03-13 17:01:25 +03:00
2007-07-04 11:45:46 +04:00
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 .
2015-03-13 17:01:25 +03:00
2007-07-04 11:45:46 +04:00
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/>.
2007-07-04 11:45:46 +04:00
*/
2015-10-26 08:50:46 +03:00
# include "replace.h"
2007-07-04 11:45:46 +04:00
# include "system/filesys.h"
2015-10-26 08:50:46 +03:00
# include "system/network.h"
2007-07-04 11:45:46 +04:00
# include "system/wait.h"
2007-08-20 05:10:30 +04:00
# include "system/dir.h"
# include "system/locale.h"
2015-10-26 08:50:46 +03:00
# include "system/time.h"
# include <talloc.h>
# include <tevent.h>
2012-02-22 10:38:12 +04:00
# include "lib/util/dlinklist.h"
2015-10-26 08:50:46 +03:00
# include "lib/util/debug.h"
# include "lib/util/samba_util.h"
# include "ctdb_private.h"
# include "common/rb_tree.h"
2015-10-23 06:11:53 +03:00
# include "common/system.h"
2015-10-23 06:17:34 +03:00
# include "common/common.h"
2015-11-11 07:41:10 +03:00
# include "common/logging.h"
2007-08-20 05:10:30 +04:00
2015-10-26 08:50:46 +03:00
2015-10-26 08:50:09 +03:00
static void ctdb_event_script_timeout ( struct tevent_context * ev ,
struct tevent_timer * te ,
struct timeval t , void * p ) ;
2009-10-28 08:11:54 +03:00
2011-07-25 12:26:06 +04:00
/* This is attached to the event script state. */
struct event_script_callback {
2012-02-22 10:38:12 +04:00
struct event_script_callback * next , * prev ;
struct ctdb_context * ctdb ;
2011-07-25 12:26:06 +04:00
/* Warning: this can free us! */
void ( * fn ) ( struct ctdb_context * , int , void * ) ;
void * private_data ;
} ;
2008-06-13 06:18:00 +04:00
struct ctdb_event_script_state {
struct ctdb_context * ctdb ;
2011-07-25 12:26:06 +04:00
struct event_script_callback * callback ;
2008-06-13 06:18:00 +04:00
pid_t child ;
int fd [ 2 ] ;
2015-10-28 08:54:10 +03:00
enum ctdb_event call ;
2008-06-13 06:18:00 +04:00
const char * options ;
2009-10-28 08:11:54 +03:00
struct timeval timeout ;
2015-03-13 17:01:25 +03:00
2009-12-08 05:17:05 +03:00
unsigned int current ;
2015-10-28 12:02:45 +03:00
struct ctdb_script_list_old * scripts ;
2008-06-13 06:18:00 +04:00
} ;
2015-10-28 08:49:47 +03:00
static struct ctdb_script * get_current_script ( struct ctdb_event_script_state * state )
2009-12-08 05:17:24 +03:00
{
return & state - > scripts - > scripts [ state - > current ] ;
}
2009-03-23 11:07:45 +03:00
/* called from ctdb_logging when we have received output on STDERR from
* one of the eventscripts
*/
2009-12-08 05:14:30 +03:00
static void log_event_script_output ( const char * str , uint16_t len , void * p )
2009-03-23 11:07:45 +03:00
{
2009-12-08 05:17:05 +03:00
struct ctdb_event_script_state * state
= talloc_get_type ( p , struct ctdb_event_script_state ) ;
2015-10-28 08:49:47 +03:00
struct ctdb_script * current ;
2009-12-08 05:18:17 +03:00
unsigned int slen , min ;
2009-03-23 11:07:45 +03:00
2009-12-18 06:43:09 +03:00
/* We may have been aborted to run something else. Discard */
if ( state - > scripts = = NULL ) {
return ;
}
current = get_current_script ( state ) ;
2009-12-08 05:18:17 +03:00
/* Append, but don't overfill buffer. It starts zero-filled. */
slen = strlen ( current - > output ) ;
min = MIN ( len , sizeof ( current - > output ) - slen - 1 ) ;
2009-12-08 05:15:17 +03:00
2009-12-08 05:18:17 +03:00
memcpy ( current - > output + slen , str , min ) ;
2009-03-23 11:07:45 +03:00
}
2009-12-07 18:20:55 +03:00
int32_t ctdb_control_get_event_script_status ( struct ctdb_context * ctdb ,
uint32_t call_type ,
TDB_DATA * outdata )
2009-12-07 15:39:40 +03:00
{
2009-12-07 18:20:55 +03:00
if ( call_type > = CTDB_EVENT_MAX ) {
2009-03-23 11:07:45 +03:00
return - 1 ;
}
2009-12-07 18:20:55 +03:00
if ( ctdb - > last_status [ call_type ] = = NULL ) {
/* If it's never been run, return nothing so they can tell. */
outdata - > dsize = 0 ;
} else {
outdata - > dsize = talloc_get_size ( ctdb - > last_status [ call_type ] ) ;
outdata - > dptr = ( uint8_t * ) ctdb - > last_status [ call_type ] ;
}
2009-03-23 11:07:45 +03:00
return 0 ;
}
2014-01-16 06:05:58 +04:00
/* To ignore directory entry return 0, else return non-zero */
static int script_filter ( const struct dirent * de )
{
int namelen = strlen ( de - > d_name ) ;
/* Ignore . and .. */
if ( namelen < 3 ) {
return 0 ;
}
/* Skip temporary files left behind by emacs */
if ( de - > d_name [ namelen - 1 ] = = ' ~ ' ) {
return 0 ;
}
/* Filename should start with [0-9][0-9]. */
if ( ! isdigit ( de - > d_name [ 0 ] ) | | ! isdigit ( de - > d_name [ 1 ] ) | |
de - > d_name [ 2 ] ! = ' . ' ) {
return 0 ;
}
if ( namelen > MAX_SCRIPT_NAME ) {
return 0 ;
}
return 1 ;
}
2009-08-13 07:04:08 +04:00
2009-12-07 15:39:39 +03:00
/* Return true if OK, otherwise set errno. */
static bool check_executable ( const char * dir , const char * name )
{
char * full ;
struct stat st ;
full = talloc_asprintf ( NULL , " %s/%s " , dir , name ) ;
if ( ! full )
return false ;
if ( stat ( full , & st ) ! = 0 ) {
DEBUG ( DEBUG_ERR , ( " Could not stat event script %s: %s \n " ,
full , strerror ( errno ) ) ) ;
talloc_free ( full ) ;
return false ;
}
if ( ! ( st . st_mode & S_IXUSR ) ) {
2010-02-11 04:00:43 +03:00
DEBUG ( DEBUG_DEBUG , ( " Event script %s is not executable. Ignoring this event script \n " , full ) ) ;
2009-12-07 15:39:39 +03:00
errno = ENOEXEC ;
talloc_free ( full ) ;
return false ;
}
talloc_free ( full ) ;
return true ;
}
2015-10-28 12:02:45 +03:00
static struct ctdb_script_list_old * ctdb_get_script_list (
struct ctdb_context * ctdb ,
TALLOC_CTX * mem_ctx )
2007-07-04 11:45:46 +04:00
{
2014-01-16 06:05:58 +04:00
struct dirent * * namelist ;
2015-10-28 12:02:45 +03:00
struct ctdb_script_list_old * scripts ;
2015-03-13 17:11:20 +03:00
int i , count ;
2007-08-20 05:10:30 +04:00
2015-03-13 17:01:25 +03:00
/* scan all directory entries and insert all valid scripts into the
2007-08-15 08:44:03 +04:00
tree
*/
2014-01-16 06:05:58 +04:00
count = scandir ( ctdb - > event_script_dir , & namelist , script_filter , alphasort ) ;
if ( count = = - 1 ) {
DEBUG ( DEBUG_CRIT , ( " Failed to read event script directory '%s' - %s \n " ,
ctdb - > event_script_dir , strerror ( errno ) ) ) ;
2009-08-13 07:04:08 +04:00
return NULL ;
2007-07-04 11:45:46 +04:00
}
2009-12-08 05:17:05 +03:00
/* Overallocates by one, but that's OK */
2014-01-16 06:05:58 +04:00
scripts = talloc_zero_size ( mem_ctx ,
2009-12-08 05:18:17 +03:00
sizeof ( * scripts )
+ sizeof ( scripts - > scripts [ 0 ] ) * count ) ;
2009-12-08 05:17:05 +03:00
if ( scripts = = NULL ) {
DEBUG ( DEBUG_ERR , ( __location__ " Failed to allocate scripts \n " ) ) ;
2015-03-13 17:12:41 +03:00
goto done ;
2009-12-08 05:17:05 +03:00
}
scripts - > num_scripts = count ;
2009-08-13 07:04:08 +04:00
2015-03-13 17:11:20 +03:00
for ( i = 0 ; i < count ; i + + ) {
2015-10-28 08:49:47 +03:00
struct ctdb_script * s = & scripts - > scripts [ i ] ;
2015-03-13 17:16:17 +03:00
2015-03-13 17:20:05 +03:00
if ( strlcpy ( s - > name , namelist [ i ] - > d_name , sizeof ( s - > name ) ) > =
sizeof ( s - > name ) ) {
s - > status = - ENAMETOOLONG ;
continue ;
}
2015-03-13 17:16:17 +03:00
s - > status = 0 ;
2015-03-13 17:11:20 +03:00
if ( ! check_executable ( ctdb - > event_script_dir ,
namelist [ i ] - > d_name ) ) {
2015-03-13 17:16:17 +03:00
s - > status = - errno ;
2014-01-16 06:05:58 +04:00
}
2009-12-08 05:17:05 +03:00
}
2009-08-13 07:04:08 +04:00
2015-03-13 17:12:41 +03:00
done :
for ( i = 0 ; i < count ; i + + ) {
free ( namelist [ i ] ) ;
}
2014-01-16 06:05:58 +04:00
free ( namelist ) ;
2009-12-08 05:17:05 +03:00
return scripts ;
2009-08-13 07:04:08 +04:00
}
2007-07-04 11:45:46 +04:00
2013-12-17 12:19:51 +04:00
/* There cannot be more than 10 arguments to command helper. */
# define MAX_HELPER_ARGS (10)
static bool child_helper_args ( TALLOC_CTX * mem_ctx , struct ctdb_context * ctdb ,
2015-10-28 08:54:10 +03:00
enum ctdb_event call ,
2013-12-17 12:19:51 +04:00
const char * options ,
2015-10-28 08:49:47 +03:00
struct ctdb_script * current , int fd ,
2013-12-17 12:19:51 +04:00
int * argc , const char * * * argv )
{
const char * * tmp ;
int n , i ;
char * t , * saveptr , * opt ;
tmp = talloc_array ( mem_ctx , const char * , 10 + 1 ) ;
if ( tmp = = NULL ) goto failed ;
tmp [ 0 ] = talloc_asprintf ( tmp , " %d " , fd ) ;
tmp [ 1 ] = talloc_asprintf ( tmp , " %s/%s " , ctdb - > event_script_dir , current - > name ) ;
tmp [ 2 ] = talloc_asprintf ( tmp , " %s " , ctdb_eventscript_call_names [ call ] ) ;
n = 3 ;
/* Split options into individual arguments */
opt = talloc_strdup ( mem_ctx , options ) ;
if ( opt = = NULL ) {
goto failed ;
}
t = strtok_r ( opt , " " , & saveptr ) ;
while ( t ! = NULL ) {
tmp [ n + + ] = talloc_strdup ( tmp , t ) ;
if ( n > MAX_HELPER_ARGS ) {
goto args_failed ;
}
t = strtok_r ( NULL , " " , & saveptr ) ;
}
for ( i = 0 ; i < n ; i + + ) {
if ( tmp [ i ] = = NULL ) {
goto failed ;
}
}
/* Last argument should be NULL */
tmp [ n + + ] = NULL ;
* argc = n ;
* argv = tmp ;
return true ;
args_failed :
DEBUG ( DEBUG_ERR , ( __location__ " too many arguments '%s' to eventscript '%s' \n " ,
options , ctdb_eventscript_call_names [ call ] ) ) ;
failed :
if ( tmp ) {
talloc_free ( tmp ) ;
}
return false ;
}
2015-10-26 08:50:09 +03:00
static void ctdb_event_script_handler ( struct tevent_context * ev ,
struct tevent_fd * fde ,
2009-12-07 16:52:55 +03:00
uint16_t flags , void * p ) ;
2015-03-06 06:05:23 +03:00
static char helper_prog [ PATH_MAX + 1 ] = " " ;
2013-12-17 12:19:51 +04:00
2009-12-07 16:52:55 +03:00
static int fork_child_for_script ( struct ctdb_context * ctdb ,
struct ctdb_event_script_state * state )
{
int r ;
2010-08-18 03:46:31 +04:00
struct tevent_fd * fde ;
2015-10-28 08:49:47 +03:00
struct ctdb_script * current = get_current_script ( state ) ;
2013-12-17 12:19:51 +04:00
int argc ;
const char * * argv ;
2015-03-06 06:05:23 +03:00
if ( ! ctdb_set_helper ( " event helper " , helper_prog , sizeof ( helper_prog ) ,
" CTDB_EVENT_HELPER " ,
CTDB_HELPER_BINDIR , " ctdb_event_helper " ) ) {
ctdb_die ( ctdb , __location__
" Unable to set event helper \n " ) ;
2013-12-17 12:19:51 +04:00
}
2009-12-07 16:52:55 +03:00
2009-12-08 05:17:05 +03:00
current - > start = timeval_current ( ) ;
2009-12-07 16:57:40 +03:00
2009-12-07 16:52:55 +03:00
r = pipe ( state - > fd ) ;
if ( r ! = 0 ) {
DEBUG ( DEBUG_ERR , ( __location__ " pipe failed for child eventscript process \n " ) ) ;
return - errno ;
}
2013-12-17 12:19:51 +04:00
/* Arguments for helper */
if ( ! child_helper_args ( state , ctdb , state - > call , state - > options , current ,
state - > fd [ 1 ] , & argc , & argv ) ) {
DEBUG ( DEBUG_ERR , ( __location__ " failed to create arguments for eventscript helper \n " ) ) ;
r = - ENOMEM ;
2009-12-07 16:52:55 +03:00
close ( state - > fd [ 0 ] ) ;
close ( state - > fd [ 1 ] ) ;
return r ;
}
2013-12-17 12:19:51 +04:00
if ( ! ctdb_vfork_with_logging ( state , ctdb , current - > name ,
helper_prog , argc , argv ,
log_event_script_output ,
state , & state - > child ) ) {
talloc_free ( argv ) ;
r = - errno ;
2009-12-07 16:52:55 +03:00
close ( state - > fd [ 0 ] ) ;
close ( state - > fd [ 1 ] ) ;
2013-12-17 12:19:51 +04:00
return r ;
2009-12-07 16:52:55 +03:00
}
2013-12-17 12:19:51 +04:00
talloc_free ( argv ) ;
2009-12-07 16:52:55 +03:00
close ( state - > fd [ 1 ] ) ;
set_close_on_exec ( state - > fd [ 0 ] ) ;
/* Set ourselves up to be called when that's done. */
2015-10-26 08:50:09 +03:00
fde = tevent_add_fd ( ctdb - > ev , state , state - > fd [ 0 ] , TEVENT_FD_READ ,
ctdb_event_script_handler , state ) ;
2010-08-18 03:46:31 +04:00
tevent_fd_set_auto_close ( fde ) ;
2009-12-07 16:52:55 +03:00
return 0 ;
}
2009-12-18 06:24:40 +03:00
/*
Summarize status of this run of scripts .
*/
2015-10-28 12:02:45 +03:00
static int script_status ( struct ctdb_script_list_old * scripts )
2009-12-18 06:24:40 +03:00
{
unsigned int i ;
for ( i = 0 ; i < scripts - > num_scripts ; i + + ) {
switch ( scripts - > scripts [ i ] . status ) {
2015-03-13 17:20:05 +03:00
case - ENAMETOOLONG :
2009-12-18 06:24:40 +03:00
case - ENOENT :
case - ENOEXEC :
/* Disabled or missing; that's OK. */
break ;
case 0 :
/* No problem. */
break ;
default :
return scripts - > scripts [ i ] . status ;
}
}
/* All OK! */
return 0 ;
}
2007-07-04 11:45:46 +04:00
/* called when child is finished */
2015-10-26 08:50:09 +03:00
static void ctdb_event_script_handler ( struct tevent_context * ev ,
struct tevent_fd * fde ,
2007-07-04 11:45:46 +04:00
uint16_t flags , void * p )
{
2015-03-13 17:01:25 +03:00
struct ctdb_event_script_state * state =
2007-07-04 11:45:46 +04:00
talloc_get_type ( p , struct ctdb_event_script_state ) ;
2015-10-28 08:49:47 +03:00
struct ctdb_script * current = get_current_script ( state ) ;
2007-07-04 11:45:46 +04:00
struct ctdb_context * ctdb = state - > ctdb ;
2009-12-18 06:24:40 +03:00
int r , status ;
2008-07-09 08:02:54 +04:00
2011-04-11 23:24:43 +04:00
if ( ctdb = = NULL ) {
DEBUG ( DEBUG_ERR , ( " Eventscript finished but ctdb is NULL \n " ) ) ;
return ;
}
2014-07-30 15:03:53 +04:00
r = sys_read ( state - > fd [ 0 ] , & current - > status , sizeof ( current - > status ) ) ;
2009-12-07 15:45:56 +03:00
if ( r < 0 ) {
2009-12-08 05:17:24 +03:00
current - > status = - errno ;
2014-11-13 03:02:26 +03:00
} else if ( r = = 0 ) {
current - > status = - EINTR ;
2009-12-08 05:17:24 +03:00
} else if ( r ! = sizeof ( current - > status ) ) {
current - > status = - EIO ;
2009-12-07 16:57:40 +03:00
}
2009-12-08 05:17:24 +03:00
current - > finished = timeval_current ( ) ;
2009-12-15 12:56:16 +03:00
/* valgrind gets overloaded if we run next script as it's still doing
* post - execution analysis , so kill finished child here . */
2009-12-16 13:29:15 +03:00
if ( ctdb - > valgrinding ) {
2012-05-03 05:42:41 +04:00
ctdb_kill ( ctdb , state - > child , SIGKILL ) ;
2009-12-16 13:29:15 +03:00
}
2009-11-24 03:36:53 +03:00
state - > child = 0 ;
2009-12-07 16:51:25 +03:00
2009-12-18 06:24:40 +03:00
status = script_status ( state - > scripts ) ;
2009-12-07 16:51:25 +03:00
/* Aborted or finished all scripts? We're done. */
2009-12-18 06:24:40 +03:00
if ( status ! = 0 | | state - > current + 1 = = state - > scripts - > num_scripts ) {
2014-11-13 03:02:26 +03:00
if ( status ! = 0 ) {
DEBUG ( DEBUG_INFO ,
( " Eventscript %s %s finished with state %d \n " ,
ctdb_eventscript_call_names [ state - > call ] ,
state - > options , status ) ) ;
}
2009-12-07 16:51:25 +03:00
talloc_free ( state ) ;
return ;
}
/* Forget about that old fd. */
talloc_free ( fde ) ;
/* Next script! */
2009-12-08 05:17:05 +03:00
state - > current + + ;
2009-12-18 06:24:40 +03:00
current + + ;
current - > status = fork_child_for_script ( ctdb , state ) ;
if ( current - > status ! = 0 ) {
2009-12-07 16:52:55 +03:00
/* This calls the callback. */
talloc_free ( state ) ;
2009-12-07 16:51:25 +03:00
}
2008-06-13 07:18:06 +04:00
}
2013-08-22 09:12:17 +04:00
struct debug_hung_script_state {
struct ctdb_context * ctdb ;
pid_t child ;
2015-10-28 08:54:10 +03:00
enum ctdb_event call ;
2013-08-22 09:12:17 +04:00
} ;
static int debug_hung_script_state_destructor ( struct debug_hung_script_state * state )
{
if ( state - > child ) {
ctdb_kill ( state - > ctdb , state - > child , SIGKILL ) ;
}
return 0 ;
}
2013-08-05 08:08:28 +04:00
static void debug_hung_script_timeout ( struct tevent_context * ev , struct tevent_timer * te ,
struct timeval t , void * p )
{
2013-08-22 09:12:17 +04:00
struct debug_hung_script_state * state =
talloc_get_type ( p , struct debug_hung_script_state ) ;
2013-08-05 08:08:28 +04:00
talloc_free ( state ) ;
}
static void debug_hung_script_done ( struct tevent_context * ev , struct tevent_fd * fde ,
uint16_t flags , void * p )
{
2013-08-22 09:12:17 +04:00
struct debug_hung_script_state * state =
talloc_get_type ( p , struct debug_hung_script_state ) ;
2013-08-05 08:08:28 +04:00
talloc_free ( state ) ;
}
2013-08-22 09:12:17 +04:00
static void ctdb_run_debug_hung_script ( struct ctdb_context * ctdb , struct debug_hung_script_state * state )
2010-04-08 09:43:29 +04:00
{
pid_t pid ;
2014-06-23 12:03:17 +04:00
const char * debug_hung_script = CTDB_ETCDIR " /debug-hung-script.sh " ;
2013-08-05 08:08:28 +04:00
int fd [ 2 ] ;
struct tevent_timer * ttimer ;
struct tevent_fd * tfd ;
2013-12-18 07:07:57 +04:00
const char * * argv ;
int i ;
2010-04-08 09:43:29 +04:00
2013-12-18 07:07:57 +04:00
if ( pipe ( fd ) < 0 ) {
DEBUG ( DEBUG_ERR , ( " Failed to create pipe fd for debug hung script \n " ) ) ;
2013-08-22 09:12:17 +04:00
return ;
2012-05-17 04:17:51 +04:00
}
2014-02-11 10:29:26 +04:00
if ( getenv ( " CTDB_DEBUG_HUNG_SCRIPT " ) ! = NULL ) {
debug_hung_script = getenv ( " CTDB_DEBUG_HUNG_SCRIPT " ) ;
}
2013-12-18 07:07:57 +04:00
argv = talloc_array ( state , const char * , 5 ) ;
2013-02-05 08:49:52 +04:00
2013-12-18 07:07:57 +04:00
argv [ 0 ] = talloc_asprintf ( argv , " %d " , fd [ 1 ] ) ;
argv [ 1 ] = talloc_strdup ( argv , debug_hung_script ) ;
argv [ 2 ] = talloc_asprintf ( argv , " %d " , state - > child ) ;
argv [ 3 ] = talloc_strdup ( argv , ctdb_eventscript_call_names [ state - > call ] ) ;
argv [ 4 ] = NULL ;
for ( i = 0 ; i < 4 ; i + + ) {
if ( argv [ i ] = = NULL ) {
close ( fd [ 0 ] ) ;
close ( fd [ 1 ] ) ;
talloc_free ( argv ) ;
return ;
}
}
2013-08-05 08:08:28 +04:00
2012-05-17 04:17:51 +04:00
2013-12-18 07:07:57 +04:00
if ( ! ctdb_vfork_with_logging ( state , ctdb , " Hung-script " ,
helper_prog , 5 , argv , NULL , NULL , & pid ) ) {
DEBUG ( DEBUG_ERR , ( " Failed to fork a child to track hung event script \n " ) ) ;
talloc_free ( argv ) ;
close ( fd [ 0 ] ) ;
close ( fd [ 1 ] ) ;
return ;
2010-04-08 09:43:29 +04:00
}
2012-05-17 04:17:51 +04:00
2013-12-18 07:07:57 +04:00
talloc_free ( argv ) ;
2013-08-05 08:08:28 +04:00
close ( fd [ 1 ] ) ;
ttimer = tevent_add_timer ( ctdb - > ev , state ,
timeval_current_ofs ( ctdb - > tunable . script_timeout , 0 ) ,
debug_hung_script_timeout , state ) ;
if ( ttimer = = NULL ) {
close ( fd [ 0 ] ) ;
2013-08-22 09:12:17 +04:00
return ;
2013-08-05 08:08:28 +04:00
}
2015-10-26 08:50:09 +03:00
tfd = tevent_add_fd ( ctdb - > ev , state , fd [ 0 ] , TEVENT_FD_READ ,
2013-08-05 08:08:28 +04:00
debug_hung_script_done , state ) ;
if ( tfd = = NULL ) {
talloc_free ( ttimer ) ;
close ( fd [ 0 ] ) ;
2013-08-22 09:12:17 +04:00
return ;
2013-08-05 08:08:28 +04:00
}
tevent_fd_set_auto_close ( tfd ) ;
2010-04-08 09:43:29 +04:00
}
2007-07-04 11:45:46 +04:00
/* called when child times out */
2015-10-26 08:50:09 +03:00
static void ctdb_event_script_timeout ( struct tevent_context * ev ,
struct tevent_timer * te ,
2007-07-04 11:45:46 +04:00
struct timeval t , void * p )
{
struct ctdb_event_script_state * state = talloc_get_type ( p , struct ctdb_event_script_state ) ;
struct ctdb_context * ctdb = state - > ctdb ;
2015-10-28 08:49:47 +03:00
struct ctdb_script * current = get_current_script ( state ) ;
2013-08-22 09:12:17 +04:00
struct debug_hung_script_state * debug_state ;
2009-10-28 08:11:54 +03:00
2015-10-28 08:39:18 +03:00
DEBUG ( DEBUG_ERR , ( " Event script '%s %s %s' timed out after %.1fs, pid: %d \n " ,
2013-08-22 09:12:17 +04:00
current - > name , ctdb_eventscript_call_names [ state - > call ] , state - > options ,
timeval_elapsed ( & current - > start ) ,
2015-10-28 08:39:18 +03:00
state - > child ) ) ;
2007-07-04 11:45:46 +04:00
2010-02-16 03:18:43 +03:00
/* ignore timeouts for these events */
switch ( state - > call ) {
case CTDB_EVENT_START_RECOVERY :
case CTDB_EVENT_RECOVERED :
case CTDB_EVENT_TAKE_IP :
case CTDB_EVENT_RELEASE_IP :
state - > scripts - > scripts [ state - > current ] . status = 0 ;
DEBUG ( DEBUG_ERR , ( " Ignoring hung script for %s call %d \n " , state - > options , state - > call ) ) ;
break ;
default :
state - > scripts - > scripts [ state - > current ] . status = - ETIME ;
}
2009-12-07 16:18:57 +03:00
2013-08-22 09:12:17 +04:00
debug_state = talloc_zero ( ctdb , struct debug_hung_script_state ) ;
if ( debug_state = = NULL ) {
2013-08-05 08:08:28 +04:00
talloc_free ( state ) ;
2013-08-22 09:12:17 +04:00
return ;
2013-08-05 08:08:28 +04:00
}
2013-08-22 09:12:17 +04:00
/* Save information useful for running debug hung script, so
* eventscript state can be freed .
*/
debug_state - > ctdb = ctdb ;
debug_state - > child = state - > child ;
debug_state - > call = state - > call ;
/* This destructor will actually kill the hung event script */
talloc_set_destructor ( debug_state , debug_hung_script_state_destructor ) ;
state - > child = 0 ;
talloc_free ( state ) ;
ctdb_run_debug_hung_script ( ctdb , debug_state ) ;
2007-07-04 11:45:46 +04:00
}
/*
2009-11-24 03:36:53 +03:00
destroy an event script : kill it if - > child ! = 0.
2007-07-04 11:45:46 +04:00
*/
static int event_script_destructor ( struct ctdb_event_script_state * state )
{
2009-12-18 06:24:40 +03:00
int status ;
2011-07-25 12:26:06 +04:00
struct event_script_callback * callback ;
2009-12-18 06:24:40 +03:00
2009-11-24 03:36:53 +03:00
if ( state - > child ) {
2010-04-08 09:41:05 +04:00
DEBUG ( DEBUG_ERR , ( __location__ " Sending SIGTERM to child pid:%d \n " , state - > child ) ) ;
2012-05-03 05:42:41 +04:00
if ( ctdb_kill ( state - > ctdb , state - > child , SIGTERM ) ! = 0 ) {
2010-04-08 09:41:05 +04:00
DEBUG ( DEBUG_ERR , ( " Failed to kill child process for eventscript, errno %s(%d) \n " , strerror ( errno ) , errno ) ) ;
2009-11-24 03:36:53 +03:00
}
2009-10-28 08:11:54 +03:00
}
2009-12-07 16:44:01 +03:00
/* If we were the current monitor, we no longer are. */
if ( state - > ctdb - > current_monitor = = state ) {
state - > ctdb - > current_monitor = NULL ;
}
2009-12-18 06:24:40 +03:00
/* Save our scripts as the last executed status, if we have them.
* See ctdb_event_script_callback_v where we abort monitor event . */
2009-12-10 12:55:33 +03:00
if ( state - > scripts ) {
talloc_free ( state - > ctdb - > last_status [ state - > call ] ) ;
state - > ctdb - > last_status [ state - > call ] = state - > scripts ;
if ( state - > current < state - > ctdb - > last_status [ state - > call ] - > num_scripts ) {
state - > ctdb - > last_status [ state - > call ] - > num_scripts = state - > current + 1 ;
}
2009-12-08 04:59:10 +03:00
}
2009-12-18 06:24:40 +03:00
/* Use last status as result, or "OK" if none. */
if ( state - > ctdb - > last_status [ state - > call ] ) {
status = script_status ( state - > ctdb - > last_status [ state - > call ] ) ;
} else {
status = 0 ;
}
2013-12-19 06:01:25 +04:00
state - > ctdb - > active_events - - ;
if ( state - > ctdb - > active_events < 0 ) {
ctdb_fatal ( state - > ctdb , " Active events < 0 " ) ;
}
2009-11-24 03:36:53 +03:00
/* This is allowed to free us; talloc will prevent double free anyway,
2012-02-22 10:38:12 +04:00
* but beware if you call this outside the destructor !
* the callback hangs off a different context so we walk the list
* of " active " callbacks until we find the one state points to .
* if we cant find it it means the callback has been removed .
*/
for ( callback = state - > ctdb - > script_callbacks ; callback ! = NULL ; callback = callback - > next ) {
if ( callback = = state - > callback ) {
break ;
}
}
2015-03-13 17:01:25 +03:00
2012-02-22 10:38:12 +04:00
state - > callback = NULL ;
2011-07-25 12:26:06 +04:00
if ( callback ) {
/* Make sure destructor doesn't free itself! */
talloc_steal ( NULL , callback ) ;
callback - > fn ( state - > ctdb , status , callback - > private_data ) ;
talloc_free ( callback ) ;
2009-10-14 07:14:28 +04:00
}
2007-07-04 11:45:46 +04:00
return 0 ;
}
2009-11-24 03:53:13 +03:00
static unsigned int count_words ( const char * options )
{
unsigned int words = 0 ;
options + = strspn ( options , " \t " ) ;
while ( * options ) {
words + + ;
options + = strcspn ( options , " \t " ) ;
options + = strspn ( options , " \t " ) ;
}
return words ;
}
2015-10-28 08:54:10 +03:00
static bool check_options ( enum ctdb_event call , const char * options )
2009-11-24 03:53:13 +03:00
{
switch ( call ) {
/* These all take no arguments. */
2010-01-19 12:07:14 +03:00
case CTDB_EVENT_INIT :
2010-02-12 13:24:08 +03:00
case CTDB_EVENT_SETUP :
2009-11-24 03:53:13 +03:00
case CTDB_EVENT_STARTUP :
case CTDB_EVENT_START_RECOVERY :
case CTDB_EVENT_RECOVERED :
case CTDB_EVENT_MONITOR :
case CTDB_EVENT_SHUTDOWN :
2010-08-30 12:08:38 +04:00
case CTDB_EVENT_IPREALLOCATED :
2009-11-24 03:53:13 +03:00
return count_words ( options ) = = 0 ;
case CTDB_EVENT_TAKE_IP : /* interface, IP address, netmask bits. */
case CTDB_EVENT_RELEASE_IP :
return count_words ( options ) = = 3 ;
2009-12-21 10:33:55 +03:00
case CTDB_EVENT_UPDATE_IP : /* old interface, new interface, IP address, netmask bits. */
return count_words ( options ) = = 4 ;
2009-11-24 03:53:13 +03:00
default :
2015-10-28 08:54:10 +03:00
DEBUG ( DEBUG_ERR , ( __location__ " Unknown ctdb_event %u \n " , call ) ) ;
2009-11-24 03:53:13 +03:00
return false ;
}
}
2011-07-25 12:26:06 +04:00
static int remove_callback ( struct event_script_callback * callback )
{
2012-02-22 10:38:12 +04:00
DLIST_REMOVE ( callback - > ctdb - > script_callbacks , callback ) ;
2011-07-25 12:26:06 +04:00
return 0 ;
}
2016-08-26 09:29:47 +03:00
struct schedule_callback_state {
struct ctdb_context * ctdb ;
void ( * callback ) ( struct ctdb_context * , int , void * ) ;
void * private_data ;
int status ;
struct tevent_immediate * im ;
} ;
static void schedule_callback_handler ( struct tevent_context * ctx ,
struct tevent_immediate * im ,
void * private_data )
{
struct schedule_callback_state * state =
talloc_get_type_abort ( private_data ,
struct schedule_callback_state ) ;
if ( state - > callback ! = NULL ) {
state - > callback ( state - > ctdb , state - > status ,
state - > private_data ) ;
}
talloc_free ( state ) ;
}
static int
schedule_callback_immediate ( struct ctdb_context * ctdb ,
void ( * callback ) ( struct ctdb_context * ,
int , void * ) ,
void * private_data ,
int status )
{
struct schedule_callback_state * state ;
struct tevent_immediate * im ;
state = talloc_zero ( ctdb , struct schedule_callback_state ) ;
if ( state = = NULL ) {
DEBUG ( DEBUG_ERR , ( __location__ " out of memory \n " ) ) ;
return - 1 ;
}
im = tevent_create_immediate ( state ) ;
if ( im = = NULL ) {
DEBUG ( DEBUG_ERR , ( __location__ " out of memory \n " ) ) ;
talloc_free ( state ) ;
return - 1 ;
}
state - > ctdb = ctdb ;
state - > callback = callback ;
state - > private_data = private_data ;
state - > status = status ;
state - > im = im ;
tevent_schedule_immediate ( im , ctdb - > ev ,
schedule_callback_handler , state ) ;
return 0 ;
}
2007-07-04 11:45:46 +04:00
/*
2015-03-13 17:01:25 +03:00
run the event script in the background , calling the callback when
2007-07-04 11:45:46 +04:00
finished
*/
2016-08-02 11:58:51 +03:00
static int ctdb_event_script_callback_v ( struct ctdb_context * ctdb ,
const void * mem_ctx ,
void ( * callback ) ( struct ctdb_context * , int , void * ) ,
void * private_data ,
enum ctdb_event call ,
const char * fmt , va_list ap )
PRINTF_ATTRIBUTE ( 6 , 0 ) ;
2011-07-25 12:26:06 +04:00
static int ctdb_event_script_callback_v ( struct ctdb_context * ctdb ,
const void * mem_ctx ,
2007-07-19 07:36:00 +04:00
void ( * callback ) ( struct ctdb_context * , int , void * ) ,
void * private_data ,
2015-10-28 08:54:10 +03:00
enum ctdb_event call ,
2007-07-19 07:36:00 +04:00
const char * fmt , va_list ap )
2007-07-04 11:45:46 +04:00
{
struct ctdb_event_script_state * state ;
2009-12-07 16:23:35 +03:00
if ( ctdb - > recovery_mode ! = CTDB_RECOVERY_NORMAL ) {
/* we guarantee that only some specifically allowed event scripts are run
while in recovery */
2015-10-28 08:54:10 +03:00
const enum ctdb_event allowed_calls [ ] = {
2010-01-19 12:07:14 +03:00
CTDB_EVENT_INIT ,
2010-02-12 13:24:08 +03:00
CTDB_EVENT_SETUP ,
2010-01-19 12:07:14 +03:00
CTDB_EVENT_START_RECOVERY ,
CTDB_EVENT_SHUTDOWN ,
CTDB_EVENT_RELEASE_IP ,
2013-04-19 07:05:02 +04:00
CTDB_EVENT_IPREALLOCATED ,
2010-01-19 12:07:14 +03:00
} ;
2009-12-07 16:23:35 +03:00
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( allowed_calls ) ; i + + ) {
if ( call = = allowed_calls [ i ] ) break ;
}
if ( i = = ARRAY_SIZE ( allowed_calls ) ) {
DEBUG ( DEBUG_ERR , ( " Refusing to run event scripts call '%s' while in recovery \n " ,
2009-12-07 18:17:13 +03:00
ctdb_eventscript_call_names [ call ] ) ) ;
2009-12-07 16:23:35 +03:00
return - 1 ;
}
}
2015-07-14 06:43:14 +03:00
/* Do not run new monitor events if some event is already
* running , unless the running event is a monitor event , in
* which case running a new one should cancel the old one . */
if ( call = = CTDB_EVENT_MONITOR & &
ctdb - > active_events > 0 & &
ctdb - > current_monitor = = NULL ) {
2013-12-19 06:01:25 +04:00
if ( callback ! = NULL ) {
callback ( ctdb , - ECANCELED , private_data ) ;
}
return 0 ;
}
2009-12-07 16:39:20 +03:00
/* Kill off any running monitor events to run this event. */
2009-12-10 12:55:33 +03:00
if ( ctdb - > current_monitor ) {
2011-11-17 06:34:29 +04:00
struct ctdb_event_script_state * ms = talloc_get_type ( ctdb - > current_monitor , struct ctdb_event_script_state ) ;
2012-10-29 07:56:10 +04:00
/* Cancel current monitor callback state only if monitoring
* context ctdb - > monitor - > monitor_context has not been freed */
if ( ms - > callback ! = NULL & & ! ctdb_stopped_monitoring ( ctdb ) ) {
2011-11-17 06:34:29 +04:00
ms - > callback - > fn ( ctdb , - ECANCELED , ms - > callback - > private_data ) ;
talloc_free ( ms - > callback ) ;
}
2009-12-10 12:55:33 +03:00
/* Discard script status so we don't save to last_status */
talloc_free ( ctdb - > current_monitor - > scripts ) ;
ctdb - > current_monitor - > scripts = NULL ;
talloc_free ( ctdb - > current_monitor ) ;
ctdb - > current_monitor = NULL ;
}
2009-12-07 16:39:20 +03:00
2013-04-30 11:22:23 +04:00
state = talloc ( ctdb - > event_script_ctx , struct ctdb_event_script_state ) ;
CTDB_NO_MEMORY ( ctdb , state ) ;
/* The callback isn't done if the context is freed. */
state - > callback = talloc ( mem_ctx , struct event_script_callback ) ;
CTDB_NO_MEMORY ( ctdb , state - > callback ) ;
DLIST_ADD ( ctdb - > script_callbacks , state - > callback ) ;
talloc_set_destructor ( state - > callback , remove_callback ) ;
state - > callback - > ctdb = ctdb ;
state - > callback - > fn = callback ;
state - > callback - > private_data = private_data ;
state - > ctdb = ctdb ;
state - > call = call ;
state - > options = talloc_vasprintf ( state , fmt , ap ) ;
state - > timeout = timeval_set ( ctdb - > tunable . script_timeout , 0 ) ;
state - > scripts = NULL ;
if ( state - > options = = NULL ) {
DEBUG ( DEBUG_ERR , ( __location__ " could not allocate state->options \n " ) ) ;
talloc_free ( state ) ;
return - 1 ;
}
if ( ! check_options ( state - > call , state - > options ) ) {
2014-06-05 14:48:03 +04:00
DEBUG ( DEBUG_ERR , ( " Bad eventscript options '%s' for '%s' \n " ,
state - > options ,
ctdb_eventscript_call_names [ state - > call ] ) ) ;
2013-04-30 11:22:23 +04:00
talloc_free ( state ) ;
return - 1 ;
}
2009-11-24 03:52:46 +03:00
DEBUG ( DEBUG_INFO , ( __location__ " Starting eventscript %s %s \n " ,
2009-12-07 18:17:13 +03:00
ctdb_eventscript_call_names [ state - > call ] ,
state - > options ) ) ;
2009-12-08 05:15:17 +03:00
2009-12-08 04:59:10 +03:00
/* This is not a child of state, since we save it in destructor. */
state - > scripts = ctdb_get_script_list ( ctdb , ctdb ) ;
2009-12-08 05:15:17 +03:00
if ( state - > scripts = = NULL ) {
2009-12-07 16:51:25 +03:00
talloc_free ( state ) ;
return - 1 ;
}
2009-12-08 05:17:05 +03:00
state - > current = 0 ;
2010-04-08 05:05:04 +04:00
state - > child = 0 ;
2009-12-08 05:17:05 +03:00
/* Nothing to do? */
if ( state - > scripts - > num_scripts = = 0 ) {
2016-08-26 09:29:47 +03:00
int ret = schedule_callback_immediate ( ctdb , callback ,
private_data , 0 ) ;
2009-12-08 05:17:05 +03:00
talloc_free ( state ) ;
2016-08-26 09:29:47 +03:00
if ( ret ! = 0 ) {
DEBUG ( DEBUG_ERR ,
( " Unable to schedule callback for 0 scripts \n " ) ) ;
return 1 ;
}
2009-12-08 05:17:05 +03:00
return 0 ;
}
2009-12-07 16:23:35 +03:00
2009-12-18 06:24:40 +03:00
state - > scripts - > scripts [ 0 ] . status = fork_child_for_script ( ctdb , state ) ;
if ( state - > scripts - > scripts [ 0 ] . status ! = 0 ) {
talloc_free ( state ) ;
2016-08-26 09:38:56 +03:00
return - 1 ;
2009-12-18 06:24:40 +03:00
}
2007-07-04 11:45:46 +04:00
2016-08-26 09:38:56 +03:00
if ( call = = CTDB_EVENT_MONITOR ) {
ctdb - > current_monitor = state ;
}
ctdb - > active_events + + ;
talloc_set_destructor ( state , event_script_destructor ) ;
2009-10-28 08:11:54 +03:00
if ( ! timeval_is_zero ( & state - > timeout ) ) {
2015-10-26 08:50:09 +03:00
tevent_add_timer ( ctdb - > ev , state ,
timeval_current_ofs ( state - > timeout . tv_sec ,
state - > timeout . tv_usec ) ,
ctdb_event_script_timeout , state ) ;
2008-06-13 06:18:00 +04:00
} else {
2009-11-24 03:52:46 +03:00
DEBUG ( DEBUG_ERR , ( __location__ " eventscript %s %s called with no timeout \n " ,
2009-12-07 18:17:13 +03:00
ctdb_eventscript_call_names [ state - > call ] ,
state - > options ) ) ;
2007-07-04 11:45:46 +04:00
}
return 0 ;
}
2007-07-19 07:36:00 +04:00
/*
2015-03-13 17:01:25 +03:00
run the event script in the background , calling the callback when
2011-07-25 12:26:06 +04:00
finished . If mem_ctx is freed , callback will never be called .
2007-07-19 07:36:00 +04:00
*/
2015-03-13 17:01:25 +03:00
int ctdb_event_script_callback ( struct ctdb_context * ctdb ,
2007-07-19 07:36:00 +04:00
TALLOC_CTX * mem_ctx ,
void ( * callback ) ( struct ctdb_context * , int , void * ) ,
void * private_data ,
2015-10-28 08:54:10 +03:00
enum ctdb_event call ,
2007-07-19 07:36:00 +04:00
const char * fmt , . . . )
{
va_list ap ;
int ret ;
va_start ( ap , fmt ) ;
2013-12-16 08:57:42 +04:00
ret = ctdb_event_script_callback_v ( ctdb , mem_ctx , callback , private_data , call , fmt , ap ) ;
2007-07-19 07:36:00 +04:00
va_end ( ap ) ;
return ret ;
}
struct callback_status {
bool done ;
int status ;
} ;
/*
called when ctdb_event_script ( ) finishes
*/
static void event_script_callback ( struct ctdb_context * ctdb , int status , void * private_data )
{
struct callback_status * s = ( struct callback_status * ) private_data ;
s - > done = true ;
s - > status = status ;
}
/*
2009-11-23 23:40:51 +03:00
run the event script , waiting for it to complete . Used when the caller
doesn ' t want to continue till the event script has finished .
2007-07-19 07:36:00 +04:00
*/
2015-10-28 08:54:10 +03:00
int ctdb_event_script_args ( struct ctdb_context * ctdb , enum ctdb_event call ,
2009-11-24 03:46:49 +03:00
const char * fmt , . . . )
2007-07-19 07:36:00 +04:00
{
va_list ap ;
int ret ;
2015-03-18 12:46:46 +03:00
struct callback_status status = {
. status = - 1 ,
. done = false ,
} ;
2007-07-19 07:36:00 +04:00
va_start ( ap , fmt ) ;
2011-07-25 12:26:06 +04:00
ret = ctdb_event_script_callback_v ( ctdb , ctdb ,
2013-12-16 08:57:42 +04:00
event_script_callback , & status , call , fmt , ap ) ;
2011-08-10 19:53:56 +04:00
va_end ( ap ) ;
2007-07-19 07:36:00 +04:00
if ( ret ! = 0 ) {
return ret ;
}
2015-10-26 08:50:09 +03:00
while ( status . done = = false & & tevent_loop_once ( ctdb - > ev ) = = 0 ) /* noop */ ;
2007-07-19 07:36:00 +04:00
2009-12-07 16:18:57 +03:00
if ( status . status = = - ETIME ) {
2016-06-01 01:44:21 +03:00
DEBUG ( DEBUG_ERR , ( __location__ " eventscript for '%s' timed out. "
2009-12-07 16:18:57 +03:00
" Immediately banning ourself for %d seconds \n " ,
2009-12-07 18:17:13 +03:00
ctdb_eventscript_call_names [ call ] ,
2009-12-07 16:18:57 +03:00
ctdb - > tunable . recovery_ban_period ) ) ;
2013-07-02 06:40:37 +04:00
/* Don't ban self if CTDB is starting up or shutting down */
if ( call ! = CTDB_EVENT_INIT & & call ! = CTDB_EVENT_SHUTDOWN ) {
ctdb_ban_self ( ctdb ) ;
}
2009-12-07 16:18:57 +03:00
}
2007-07-19 07:36:00 +04:00
return status . status ;
}
2008-04-02 04:13:30 +04:00
2015-10-28 08:54:10 +03:00
int ctdb_event_script ( struct ctdb_context * ctdb , enum ctdb_event call )
2009-11-24 03:46:49 +03:00
{
/* GCC complains about empty format string, so use %s and "". */
return ctdb_event_script_args ( ctdb , call , " %s " , " " ) ;
}
2008-04-02 04:13:30 +04:00
struct eventscript_callback_state {
2015-10-29 08:42:05 +03:00
struct ctdb_req_control_old * c ;
2008-04-02 04:13:30 +04:00
} ;
/*
2009-11-19 03:03:51 +03:00
called when a forced eventscript run has finished
2008-04-02 04:13:30 +04:00
*/
2015-03-13 17:01:25 +03:00
static void run_eventscripts_callback ( struct ctdb_context * ctdb , int status ,
2008-04-02 04:13:30 +04:00
void * private_data )
{
2015-07-06 05:02:00 +03:00
const char * errmsg = NULL ;
2015-03-13 17:01:25 +03:00
struct eventscript_callback_state * state =
2008-04-02 04:13:30 +04:00
talloc_get_type ( private_data , struct eventscript_callback_state ) ;
if ( status ! = 0 ) {
2015-07-06 05:02:00 +03:00
if ( status = = - ECANCELED ) {
DEBUG ( DEBUG_WARNING ,
( __location__ " Eventscript cancelled \n " ) ) ;
errmsg = " cancelled " ;
} else {
DEBUG ( DEBUG_ERR ,
( __location__ " Failed to run eventscripts \n " ) ) ;
}
2008-04-02 04:13:30 +04:00
}
2015-07-06 05:02:00 +03:00
ctdb_request_control_reply ( ctdb , state - > c , NULL , status , errmsg ) ;
2009-11-24 03:36:53 +03:00
/* This will free the struct ctdb_event_script_state we are in! */
2008-04-02 04:13:30 +04:00
talloc_free ( state ) ;
return ;
}
2009-11-23 23:40:51 +03:00
2009-11-25 03:32:29 +03:00
/* Returns rest of string, or NULL if no match. */
2015-10-28 08:54:10 +03:00
static const char * get_call ( const char * p , enum ctdb_event * call )
2009-11-24 03:54:22 +03:00
{
unsigned int len ;
/* Skip any initial whitespace. */
p + = strspn ( p , " \t " ) ;
/* See if we match any. */
2009-12-07 18:17:13 +03:00
for ( * call = 0 ; * call < CTDB_EVENT_MAX ; ( * call ) + + ) {
len = strlen ( ctdb_eventscript_call_names [ * call ] ) ;
if ( strncmp ( p , ctdb_eventscript_call_names [ * call ] , len ) = = 0 ) {
2009-11-25 03:32:29 +03:00
/* If end of string or whitespace, we're done. */
if ( strcspn ( p + len , " \t " ) = = 0 ) {
return p + len ;
}
2009-11-24 03:54:22 +03:00
}
}
return NULL ;
}
2008-04-02 04:13:30 +04:00
/*
A control to force running of the eventscripts from the ctdb client tool
*/
int32_t ctdb_run_eventscripts ( struct ctdb_context * ctdb ,
2015-10-29 08:42:05 +03:00
struct ctdb_req_control_old * c ,
2008-04-02 04:13:30 +04:00
TDB_DATA indata , bool * async_reply )
{
int ret ;
struct eventscript_callback_state * state ;
2009-11-24 03:54:22 +03:00
const char * options ;
2015-10-28 08:54:10 +03:00
enum ctdb_event call ;
2009-11-24 03:54:22 +03:00
/* Figure out what call they want. */
options = get_call ( ( const char * ) indata . dptr , & call ) ;
if ( ! options ) {
2012-10-11 09:10:19 +04:00
DEBUG ( DEBUG_ERR , ( __location__ " Invalid event name \" %s \" \n " , ( const char * ) indata . dptr ) ) ;
2009-11-24 03:54:22 +03:00
return - 1 ;
}
2008-04-02 04:13:30 +04:00
2009-11-23 23:40:51 +03:00
if ( ctdb - > recovery_mode ! = CTDB_RECOVERY_NORMAL ) {
DEBUG ( DEBUG_ERR , ( __location__ " Aborted running eventscript \" %s \" while in RECOVERY mode \n " , indata . dptr ) ) ;
return - 1 ;
2008-04-02 04:13:30 +04:00
}
2009-12-07 16:39:20 +03:00
state = talloc ( ctdb - > event_script_ctx , struct eventscript_callback_state ) ;
2008-04-02 04:13:30 +04:00
CTDB_NO_MEMORY ( ctdb , state ) ;
2016-08-31 01:29:13 +03:00
state - > c = NULL ;
2008-04-02 04:13:30 +04:00
2012-07-02 11:15:42 +04:00
DEBUG ( DEBUG_NOTICE , ( " Running eventscripts with arguments %s \n " , indata . dptr ) ) ;
2008-04-02 04:13:30 +04:00
2015-03-13 17:01:25 +03:00
ret = ctdb_event_script_callback ( ctdb ,
2015-07-10 07:02:29 +03:00
ctdb , run_eventscripts_callback , state ,
2013-12-16 08:57:42 +04:00
call , " %s " , options ) ;
2008-04-02 04:13:30 +04:00
if ( ret ! = 0 ) {
DEBUG ( DEBUG_ERR , ( __location__ " Failed to run eventscripts with arguments %s \n " , indata . dptr ) ) ;
talloc_free ( state ) ;
return - 1 ;
}
/* tell ctdb_control.c that we will be replying asynchronously */
* async_reply = true ;
2016-08-31 01:29:13 +03:00
state - > c = talloc_steal ( state , c ) ;
2008-04-02 04:13:30 +04:00
return 0 ;
}
2009-08-13 07:04:08 +04:00
int32_t ctdb_control_enable_script ( struct ctdb_context * ctdb , TDB_DATA indata )
{
const char * script ;
struct stat st ;
char * filename ;
TALLOC_CTX * tmp_ctx = talloc_new ( ctdb ) ;
script = ( char * ) indata . dptr ;
if ( indata . dsize = = 0 ) {
DEBUG ( DEBUG_ERR , ( __location__ " No script specified. \n " ) ) ;
talloc_free ( tmp_ctx ) ;
return - 1 ;
}
if ( indata . dptr [ indata . dsize - 1 ] ! = ' \0 ' ) {
DEBUG ( DEBUG_ERR , ( __location__ " String is not null terminated. \n " ) ) ;
talloc_free ( tmp_ctx ) ;
return - 1 ;
}
if ( index ( script , ' / ' ) ! = NULL ) {
DEBUG ( DEBUG_ERR , ( __location__ " Script name contains '/'. Failed to enable script %s \n " , script ) ) ;
talloc_free ( tmp_ctx ) ;
return - 1 ;
}
2015-03-13 17:01:25 +03:00
if ( stat ( ctdb - > event_script_dir , & st ) ! = 0 & &
2009-08-13 07:04:08 +04:00
errno = = ENOENT ) {
DEBUG ( DEBUG_CRIT , ( " No event script directory found at '%s' \n " , ctdb - > event_script_dir ) ) ;
talloc_free ( tmp_ctx ) ;
return - 1 ;
}
filename = talloc_asprintf ( tmp_ctx , " %s/%s " , ctdb - > event_script_dir , script ) ;
if ( filename = = NULL ) {
DEBUG ( DEBUG_ERR , ( __location__ " Failed to create script path \n " ) ) ;
talloc_free ( tmp_ctx ) ;
return - 1 ;
}
if ( stat ( filename , & st ) ! = 0 ) {
DEBUG ( DEBUG_ERR , ( " Could not stat event script %s. Failed to enable script. \n " , filename ) ) ;
talloc_free ( tmp_ctx ) ;
return - 1 ;
}
if ( chmod ( filename , st . st_mode | S_IXUSR ) = = - 1 ) {
DEBUG ( DEBUG_ERR , ( " Could not chmod %s. Failed to enable script. \n " , filename ) ) ;
talloc_free ( tmp_ctx ) ;
return - 1 ;
}
talloc_free ( tmp_ctx ) ;
return 0 ;
}
int32_t ctdb_control_disable_script ( struct ctdb_context * ctdb , TDB_DATA indata )
{
const char * script ;
struct stat st ;
char * filename ;
TALLOC_CTX * tmp_ctx = talloc_new ( ctdb ) ;
script = ( char * ) indata . dptr ;
if ( indata . dsize = = 0 ) {
DEBUG ( DEBUG_ERR , ( __location__ " No script specified. \n " ) ) ;
talloc_free ( tmp_ctx ) ;
return - 1 ;
}
if ( indata . dptr [ indata . dsize - 1 ] ! = ' \0 ' ) {
DEBUG ( DEBUG_ERR , ( __location__ " String is not null terminated. \n " ) ) ;
talloc_free ( tmp_ctx ) ;
return - 1 ;
}
if ( index ( script , ' / ' ) ! = NULL ) {
DEBUG ( DEBUG_ERR , ( __location__ " Script name contains '/'. Failed to disable script %s \n " , script ) ) ;
talloc_free ( tmp_ctx ) ;
return - 1 ;
}
2015-03-13 17:01:25 +03:00
if ( stat ( ctdb - > event_script_dir , & st ) ! = 0 & &
2009-08-13 07:04:08 +04:00
errno = = ENOENT ) {
DEBUG ( DEBUG_CRIT , ( " No event script directory found at '%s' \n " , ctdb - > event_script_dir ) ) ;
talloc_free ( tmp_ctx ) ;
return - 1 ;
}
filename = talloc_asprintf ( tmp_ctx , " %s/%s " , ctdb - > event_script_dir , script ) ;
if ( filename = = NULL ) {
DEBUG ( DEBUG_ERR , ( __location__ " Failed to create script path \n " ) ) ;
talloc_free ( tmp_ctx ) ;
return - 1 ;
}
if ( stat ( filename , & st ) ! = 0 ) {
DEBUG ( DEBUG_ERR , ( " Could not stat event script %s. Failed to disable script. \n " , filename ) ) ;
talloc_free ( tmp_ctx ) ;
return - 1 ;
}
if ( chmod ( filename , st . st_mode & ~ ( S_IXUSR | S_IXGRP | S_IXOTH ) ) = = - 1 ) {
DEBUG ( DEBUG_ERR , ( " Could not chmod %s. Failed to disable script. \n " , filename ) ) ;
talloc_free ( tmp_ctx ) ;
return - 1 ;
}
talloc_free ( tmp_ctx ) ;
return 0 ;
}