2018-03-03 18:11:16 +03:00
/*
CTDB event daemon
Copyright ( C ) Amitay Isaacs 2018
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 <popt.h>
# include <talloc.h>
# include <tevent.h>
# include "lib/util/tevent_unix.h"
# include "common/logging.h"
# include "common/path.h"
# include "common/sock_daemon.h"
# include "event/event_private.h"
struct event_daemon_state {
TALLOC_CTX * mem_ctx ;
char * socket ;
char * pidfile ;
struct tevent_context * ev ;
struct event_config * config ;
struct sock_daemon_context * sockd ;
struct event_context * eventd ;
} ;
static int event_daemon_startup ( void * private_data )
{
struct event_daemon_state * e_state = talloc_get_type_abort (
private_data , struct event_daemon_state ) ;
int ret ;
ret = event_context_init ( e_state ,
e_state - > ev ,
e_state - > config ,
& e_state - > eventd ) ;
if ( ret ! = 0 ) {
D_ERR ( " Failed to initialize event context \n " ) ;
return ret ;
}
return 0 ;
}
static int event_daemon_reconfigure ( void * private_data )
{
struct event_daemon_state * e_state = talloc_get_type_abort (
private_data , struct event_daemon_state ) ;
int ret ;
ret = event_config_reload ( e_state - > config ) ;
if ( ret ! = 0 ) {
D_WARNING ( " Configuration reload failed \n " ) ;
}
return 0 ;
}
static void event_daemon_shutdown ( void * private_data )
{
struct event_daemon_state * e_state = talloc_get_type_abort (
private_data , struct event_daemon_state ) ;
TALLOC_FREE ( e_state - > eventd ) ;
}
static bool event_client_connect ( struct sock_client_context * client ,
pid_t pid ,
void * private_data )
{
struct event_daemon_state * e_state = talloc_get_type_abort (
private_data , struct event_daemon_state ) ;
int ret ;
ret = eventd_client_add ( e_state - > eventd , client ) ;
if ( ret ! = 0 ) {
D_ERR ( " Failed to register client, ret=%d \n " , ret ) ;
return false ;
}
return true ;
}
static void event_client_disconnect ( struct sock_client_context * client ,
void * private_data )
{
struct event_daemon_state * e_state = talloc_get_type_abort (
private_data , struct event_daemon_state ) ;
eventd_client_del ( e_state - > eventd , client ) ;
}
struct event_client_state {
struct tevent_context * ev ;
struct event_context * eventd ;
struct sock_client_context * client ;
uint8_t * buf ;
size_t buflen ;
} ;
static void event_client_request_done ( struct tevent_req * subreq ) ;
static void event_client_reply_done ( struct tevent_req * subreq ) ;
static struct tevent_req * event_client_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct sock_client_context * client ,
uint8_t * buf ,
size_t buflen ,
void * private_data )
{
struct event_daemon_state * e_state = talloc_get_type_abort (
private_data , struct event_daemon_state ) ;
struct tevent_req * req , * subreq ;
struct event_client_state * state ;
req = tevent_req_create ( mem_ctx , & state , struct event_client_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
state - > eventd = e_state - > eventd ;
state - > client = client ;
subreq = event_pkt_send ( state , ev , e_state - > eventd , buf , buflen ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , event_client_request_done , req ) ;
return req ;
}
static void event_client_request_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct event_client_state * state = tevent_req_data (
req , struct event_client_state ) ;
int ret = 0 ;
bool ok ;
ok = event_pkt_recv ( subreq , & ret , state , & state - > buf , & state - > buflen ) ;
TALLOC_FREE ( subreq ) ;
if ( ! ok ) {
tevent_req_error ( req , ret ) ;
return ;
}
ok = eventd_client_exists ( state - > eventd , state - > client ) ;
if ( ! ok ) {
/* Client has already disconnected */
talloc_free ( state - > buf ) ;
tevent_req_done ( req ) ;
return ;
}
subreq = sock_socket_write_send ( state ,
state - > ev ,
state - > client ,
state - > buf ,
state - > buflen ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
talloc_free ( state - > buf ) ;
return ;
}
tevent_req_set_callback ( subreq , event_client_reply_done , req ) ;
}
static void event_client_reply_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct event_client_state * state = tevent_req_data (
req , struct event_client_state ) ;
int ret = 0 ;
bool ok ;
talloc_free ( state - > buf ) ;
ok = sock_socket_write_recv ( subreq , & ret ) ;
TALLOC_FREE ( subreq ) ;
if ( ! ok ) {
D_ERR ( " Sending reply failed \n " ) ;
tevent_req_error ( req , ret ) ;
return ;
}
tevent_req_done ( req ) ;
}
static bool event_client_recv ( struct tevent_req * req , int * perr )
{
if ( tevent_req_is_unix_error ( req , perr ) ) {
return false ;
}
return true ;
}
static struct {
int pid ;
2018-08-24 07:52:29 +03:00
int startup_fd ;
2018-03-03 18:11:16 +03:00
} options = {
. pid = - 1 ,
2018-08-24 07:52:29 +03:00
. startup_fd = - 1 ,
2018-03-03 18:11:16 +03:00
} ;
struct poptOption cmdline_options [ ] = {
POPT_AUTOHELP
{ " pid " , ' P ' , POPT_ARG_INT , & options . pid , 0 ,
" pid to wait for " , " PID " } ,
2018-08-24 07:52:29 +03:00
{ " startup-fd " , ' S ' , POPT_ARG_INT , & options . startup_fd , 0 ,
" file descriptor to notify of successful start " , " FD " } ,
2018-03-03 18:11:16 +03:00
POPT_TABLEEND
} ;
int main ( int argc , const char * * argv )
{
poptContext pc ;
struct event_daemon_state * e_state ;
struct sock_daemon_funcs daemon_funcs ;
struct sock_socket_funcs socket_funcs ;
const char * log_location = " file: " ;
const char * log_level = " NOTICE " ;
const char * t ;
int interactive = 0 ;
int opt , ret ;
pc = poptGetContext ( argv [ 0 ] ,
argc ,
argv ,
cmdline_options ,
0 ) ;
while ( ( opt = poptGetNextOpt ( pc ) ) ! = - 1 ) {
D_ERR ( " Invalid options %s: %s \n " ,
poptBadOption ( pc , 0 ) ,
poptStrerror ( opt ) ) ;
exit ( 1 ) ;
}
t = getenv ( " CTDB_INTERACTIVE " ) ;
if ( t ! = NULL ) {
interactive = 1 ;
}
e_state = talloc_zero ( NULL , struct event_daemon_state ) ;
if ( e_state = = NULL ) {
D_ERR ( " Memory allocation error \n " ) ;
ret = 1 ;
goto fail ;
}
e_state - > mem_ctx = talloc_new ( e_state ) ;
if ( e_state - > mem_ctx = = NULL ) {
D_ERR ( " Memory allocation error \n " ) ;
ret = 1 ;
goto fail ;
}
e_state - > socket = path_socket ( e_state , " eventd " ) ;
if ( e_state - > socket = = NULL ) {
D_ERR ( " Memory allocation error \n " ) ;
ret = 1 ;
goto fail ;
}
e_state - > pidfile = path_pidfile ( e_state , " eventd " ) ;
if ( e_state - > pidfile = = NULL ) {
D_ERR ( " Memory allocation error \n " ) ;
ret = 1 ;
goto fail ;
}
ret = event_config_init ( e_state , & e_state - > config ) ;
if ( ret ! = 0 ) {
D_ERR ( " Failed to initalize event config \n " ) ;
goto fail ;
}
e_state - > ev = tevent_context_init ( e_state - > mem_ctx ) ;
if ( e_state - > ev = = NULL ) {
D_ERR ( " Failed to initalize tevent \n " ) ;
ret = 1 ;
goto fail ;
}
daemon_funcs = ( struct sock_daemon_funcs ) {
. startup = event_daemon_startup ,
. reconfigure = event_daemon_reconfigure ,
. shutdown = event_daemon_shutdown ,
} ;
if ( interactive = = 0 ) {
log_location = event_config_log_location ( e_state - > config ) ;
log_level = event_config_log_level ( e_state - > config ) ;
}
ret = sock_daemon_setup ( e_state - > mem_ctx ,
" ctdb-eventd " ,
log_location ,
log_level ,
& daemon_funcs ,
e_state ,
& e_state - > sockd ) ;
if ( ret ! = 0 ) {
D_ERR ( " Failed to setup sock daemon \n " ) ;
goto fail ;
}
socket_funcs = ( struct sock_socket_funcs ) {
. connect = event_client_connect ,
. disconnect = event_client_disconnect ,
. read_send = event_client_send ,
. read_recv = event_client_recv ,
} ;
ret = sock_daemon_add_unix ( e_state - > sockd ,
e_state - > socket ,
& socket_funcs ,
e_state ) ;
if ( ret ! = 0 ) {
D_ERR ( " Failed to setup socket %s \n " , e_state - > socket ) ;
goto fail ;
}
2018-08-24 07:52:29 +03:00
if ( options . startup_fd ! = - 1 ) {
sock_daemon_set_startup_fd ( e_state - > sockd , options . startup_fd ) ;
}
2018-03-03 18:11:16 +03:00
ret = sock_daemon_run ( e_state - > ev ,
e_state - > sockd ,
e_state - > pidfile ,
( interactive = = 1 ) ,
false ,
options . pid ) ;
if ( ret = = EINTR ) {
ret = 0 ;
}
if ( getenv ( " CTDB_TEST_MODE " ) ! = NULL ) {
talloc_report_full ( e_state - > mem_ctx , stderr ) ;
}
fail :
talloc_free ( e_state ) ;
( void ) poptFreeContext ( pc ) ;
exit ( ret ) ;
}