#include #include #include #ifdef _WIN32 #define WIN32_MEAN_AND_LEAN #include #else #include #include #include #endif // command line parser #include "biruda_cmdline.h" // configuration parser #include "confuse.h" // daemonizing #ifndef _WIN32 #include "libdaemon/daemon.h" #endif #include "master.h" #include "worker.h" #include "coordinator.h" // for integrated webserver #ifndef _WIN32 #include "webserver.h" #endif #ifndef _WIN32 #include "cli.h" #endif #include "port.h" static const char *DEFAULT_CONFIG_FILE = "/etc/biruda/biruda.conf"; static const unsigned int DEFAULT_WEBSERVER_PORT = 8080; static const char *DEFAULT_WEBSERVER_HOST = "localhost"; static const unsigned int DEFAULT_WEBSERVER_THREADS = 4; static int parse_options_and_arguments( int argc, char *argv[], struct gengetopt_args_info *args_info ) { struct cmdline_parser_params params; cmdline_parser_params_init( ¶ms ); cmdline_parser_init( args_info ); if( cmdline_parser_ext( argc, argv, args_info, ¶ms ) != 0 ) { cmdline_parser_free( args_info ); return 1; } return 0; } static int test_config( const struct gengetopt_args_info *args_info ) { return 0; } static int conf_parse_worker_execution( cfg_t *cfg, cfg_opt_t *opt, const char *value, void *result ) { if( strcmp( value, "direct" ) == 0 ) { *(worker_execution_mode_t *)result = WORKER_EXECUTION_DIRECT; } else { cfg_error( cfg, "invalid value for worker execution mode option '%s': %s", cfg_opt_name( opt ), value ); return -1; } return 0; } static int read_config( const char *filename, cfg_t **cfg ) { cfg_opt_t opts_master[] = { CFG_STR( "control", 0, CFGF_NODEFAULT ), CFG_END( ) }; cfg_opt_t opts_coordinator[] = { CFG_STR( "control", 0, CFGF_NODEFAULT ), CFG_END( ) }; cfg_opt_t opts_worker[] = { CFG_STR( "control", 0, CFGF_NODEFAULT ), CFG_INT_CB( "execution", WORKER_EXECUTION_DIRECT, CFGF_NONE, conf_parse_worker_execution ), CFG_STR( "command", NULL, CFGF_NONE ), CFG_END( ) }; cfg_opt_t opts_webserver[] = { CFG_STR( "host", (char *)DEFAULT_WEBSERVER_HOST, CFGF_NONE ), CFG_INT( "port", DEFAULT_WEBSERVER_PORT, CFGF_NONE ), CFG_INT( "threads", DEFAULT_WEBSERVER_THREADS, CFGF_NONE ), CFG_END( ) }; cfg_opt_t opts[] = { CFG_SEC( "master", opts_master, CFGF_MULTI ), CFG_SEC( "coordinator", opts_coordinator, CFGF_MULTI ), CFG_SEC( "worker", opts_worker, CFGF_MULTI | CFGF_TITLE ), CFG_SEC( "webserver", opts_webserver, CFGF_MULTI ), CFG_END( ) }; *cfg = cfg_init( opts, CFGF_NONE ); switch( cfg_parse( *cfg, filename ) ) { case CFG_FILE_ERROR: fprintf( stderr, "ERROR: configuration file '%s' could not be read: %s\n", filename, strerror( errno ) ); return 1; case CFG_PARSE_ERROR: return 1; case CFG_SUCCESS: break; } return 0; } static void print_config( struct gengetopt_args_info *args_info, cfg_t *cfg ) { cfg_t *master_cfg = cfg_getnsec( cfg, "master", 0 ); unsigned int has_master = cfg_size( cfg, "master" ); if( has_master ) { puts( "Master:" ); printf( " Control channel: %s\n", cfg_getstr( master_cfg, "control" ) ); puts( "" ); } cfg_t *coordinator_cfg = cfg_getnsec( cfg, "coordinator", 0 ); unsigned int has_coordinator = cfg_size( cfg, "coordinator" ); if( has_coordinator ) { puts( "Coordinator:" ); printf( " Control channel: %s\n", cfg_getstr( coordinator_cfg, "control" ) ); puts( "" ); } unsigned int nof_workers = cfg_size( cfg, "worker" ); if( nof_workers > 0 ) { puts( "Workers:\n" ); for( unsigned int i = 0; i < nof_workers; i++ ) { cfg_t *worker_cfg = cfg_getnsec( cfg, "worker", i ); printf( " Worker %s:\n", cfg_title( worker_cfg ) ); printf( " Control channel: %s\n", cfg_getstr( worker_cfg, "control" ) ); printf( " Execution mode: %s\n", worker_exection_mode_str( (worker_execution_mode_t)cfg_getint( worker_cfg, "execution" ) ) ); if( cfg_getint( worker_cfg, "execution" ) == WORKER_EXECUTION_DIRECT ) { char *command = cfg_getstr( worker_cfg, "command" ); if( command != NULL ) { printf( " Command: %s\n", cfg_getstr( worker_cfg, "command" ) ); } else { printf( " No command configured\n" ); } } puts( "" ); } puts( "" ); } #ifndef _WIN32 cfg_t *webserver_cfg = cfg_getnsec( cfg, "webserver", 0 ); unsigned int has_webserver = cfg_size( cfg, "webserver" ); if( has_webserver > 0 ) { printf( "Webserver:\n" ); printf( " Host: %s\n", cfg_getstr( webserver_cfg, "host" ) ); printf( " Port: %ld\n", cfg_getint( webserver_cfg, "port" ) ); printf( " Number of threads: %ld\n", cfg_getint( webserver_cfg, "threads" ) ); puts( "" ); } #else fprintf( stderr, "WARNING: Currently no built-in webserver available for master on Windows!\n" ); #endif } static int create_master( cfg_t *cfg ) { cfg_t *master_cfg = cfg_getnsec( cfg, "master", 0 ); char *control = cfg_getstr( master_cfg, "control" ); return master_init( control ); } static int create_coordinator( cfg_t *cfg ) { cfg_t *master_cfg = cfg_getnsec( cfg, "coordinator", 0 ); char *control = cfg_getstr( master_cfg, "control" ); int ret = coordinator_init( control ); unsigned int nof_workers = cfg_size( cfg, "worker" ); if( nof_workers > 0 ) { for( unsigned int i = 0; i < nof_workers; i++ ) { cfg_t *worker_cfg = cfg_getnsec( cfg, "worker", i ); coordinator_add_worker( cfg_title( worker_cfg ), (worker_execution_mode_t)cfg_getint( worker_cfg, "execution" ), cfg_getstr( worker_cfg, "command" ) ); } } return ret; } #ifndef _WIN32 static int create_webserver( cfg_t *cfg ) { cfg_t *webserver_cfg = cfg_getnsec( cfg, "webserver", 0 ); unsigned int port = cfg_getint( webserver_cfg, "port" ); return webserver_init( port ); } #endif static volatile int got_terminate = 0; #ifdef _WIN32 BOOL WINAPI terminate_foreground_func( DWORD ctrlType ) { switch( ctrlType ) { case CTRL_C_EVENT: case CTRL_BREAK_EVENT: case CTRL_CLOSE_EVENT: case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: got_terminate = 1; return TRUE; default: return FALSE; } } #else static void terminate_foreground_func( int sig ) { got_terminate = 1; } #endif int main( int argc, char *argv[] ) { struct gengetopt_args_info args_info; cfg_t *cfg; if( parse_options_and_arguments( argc, argv, &args_info ) ) { exit( EXIT_FAILURE ); } if( read_config( args_info.config_file_given ? args_info.config_file_arg : DEFAULT_CONFIG_FILE, &cfg ) ) { cfg_free( cfg ); cmdline_parser_free( &args_info ); return 1; } if( args_info.test_given ) { cfg_free( cfg ); cmdline_parser_free( &args_info ); return( ( test_config( &args_info ) == 0 ) ? EXIT_SUCCESS : EXIT_FAILURE ); } if( args_info.print_given ) { print_config( &args_info, cfg ); cfg_free( cfg ); cmdline_parser_free( &args_info ); return( ( test_config( &args_info ) == 0 ) ? EXIT_SUCCESS : EXIT_FAILURE ); } if( args_info.cli_given ) { #ifndef _WIN32 int ret = start_interactive( !args_info.no_colors_given ); #else fprintf( stderr, "FATAL: No CLI mode implemented for Windows currently!\n" ); int ret = -1; #endif cfg_free( cfg ); cmdline_parser_free( &args_info ); return( ( ret == 0 ) ? EXIT_SUCCESS : EXIT_FAILURE ); } unsigned int has_master = cfg_size( cfg, "master" ); if( has_master ) { if( create_master( cfg ) != 0 ) { fprintf( stderr, "FATAL: Unable to create master thread!\n" ); cfg_free( cfg ); cmdline_parser_free( &args_info ); exit( EXIT_FAILURE ); } } unsigned int has_coordinator = cfg_size( cfg, "coordinator" ); if( has_coordinator ) { if( create_coordinator( cfg ) != 0 ) { fprintf( stderr, "FATAL: Unable to create coordinator thread!\n" ); cfg_free( cfg ); cmdline_parser_free( &args_info ); exit( EXIT_FAILURE ); } } #ifndef _WIN32 unsigned int has_webserver = cfg_size( cfg, "webserver" ); if( has_webserver ) { if( create_webserver( cfg ) != 0 ) { fprintf( stderr, "FATAL: Unable to create webserver thread!\n" ); cfg_free( cfg ); cmdline_parser_free( &args_info ); exit( EXIT_FAILURE ); } } #else fprintf( stderr, "WARNING: Currently no built-in webserver available for master on Windows!\n" ); #endif if( args_info.foreground_given ) { #ifdef _WIN32 SetConsoleCtrlHandler( terminate_foreground_func, TRUE ); #else struct sigaction sa; memset( &sa, 0, sizeof( struct sigaction ) ); sa.sa_handler = terminate_foreground_func; sa.sa_flags = SA_RESTART; (void)sigaction( SIGINT, &sa, NULL ); (void)sigaction( SIGTERM, &sa, NULL ); #endif while( !got_terminate ) { sleep( 1 ); } } #ifndef _WIN32 if( has_webserver ) { webserver_terminate( ); } #endif if( has_coordinator ) { coordinator_terminate( ); } if( has_master ) { master_terminate( !has_coordinator ); } if( has_coordinator ) { coordinator_free( ); } if( has_master ) { master_free( ); } #ifndef _WIN32 if( has_webserver ) { webserver_free( ); } #endif cfg_free( cfg ); cmdline_parser_free( &args_info ); exit( EXIT_SUCCESS ); }