#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 "system.h" #include "master.h" #include "worker.h" #include "coordinator.h" // for integrated webserver #ifndef _WIN32 #include "webserver.h" #endif #include "cli.h" #include "port.h" #include "biruda_conf.c" 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_validate_worker_execution( cfg_t *cfg, cfg_opt_t *opt, const char *value, void *result ) { if( strcmp( value, "disabled" ) == 0 ) { *(worker_execution_mode_t *)result = WORKER_EXECUTION_DISABLED; } else 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 conf_validate_master( cfg_t *cfg, cfg_opt_t *opt ) { cfg_t *master = cfg_opt_getnsec( opt, cfg_opt_size( opt ) - 1 ); if( cfg_size( master, "control" ) == 0 ) { cfg_error( cfg, "missing required option 'control' for master" ); return -1; } if( cfg_size( master, "data" ) == 0 ) { cfg_error( cfg, "missing required option 'data' for master" ); return -1; } return 0; } static int conf_validate_coordinator( cfg_t *cfg, cfg_opt_t *opt ) { cfg_t *coordinator = cfg_opt_getnsec( opt, cfg_opt_size( opt ) - 1 ); if( cfg_size( coordinator, "control" ) == 0 ) { cfg_error( cfg, "missing required option 'control' for coordinator" ); return -1; } return 0; } static int conf_validate_worker( cfg_t *cfg, cfg_opt_t *opt ) { cfg_t *worker = cfg_opt_getnsec( opt, cfg_opt_size( opt ) - 1 ); if( cfg_size( worker, "control" ) == 0 ) { cfg_error( cfg, "missing required option 'control' for worker" ); return -1; } if( cfg_size( worker, "data" ) == 0 ) { cfg_error( cfg, "missing required option 'data' for worker" ); return -1; } worker_execution_mode_t mode = (worker_execution_mode_t)cfg_getint( worker, "execution" ); switch( mode ) { case WORKER_EXECUTION_DISABLED: // skip, ok break; case WORKER_EXECUTION_DIRECT: if( cfg_size( worker, "command" ) == 0 ) { cfg_error( cfg, "missing required option 'command' for worker '%s'", cfg_title( worker ) ); 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_STR( "data", 0, CFGF_NODEFAULT ), CFG_STR( "spool_dir", 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_STR( "data", 0, CFGF_NODEFAULT ), CFG_INT_CB( "execution", WORKER_EXECUTION_DISABLED, CFGF_NONE, &conf_validate_worker_execution ), CFG_STR( "command", NULL, CFGF_NODEFAULT ), 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 ); cfg_set_validate_func( *cfg, "master", &conf_validate_master ); cfg_set_validate_func( *cfg, "coordinator", &conf_validate_coordinator ); cfg_set_validate_func( *cfg, "worker", &conf_validate_worker ); 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 ) { unsigned int nofCpus = system_available_cpus( ); puts( "Environment:" ); printf( " Number of CPUs: %d\n", nofCpus ); int physMem = system_phys_memory( ); printf( " Physically installed memory: %d kB\n", physMem ); char machine_arch[100]; system_arch( machine_arch, sizeof( machine_arch ) ); printf( " System architecture: %s\n", machine_arch ); char os_name[100]; system_os( os_name, sizeof( os_name ) ); printf( " Operating system: %s\n", os_name ); puts( "" ); 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" ) ); printf( " Data channel: %s\n", cfg_getstr( master_cfg, "data" ) ); printf( " Spool directory: %s\n", cfg_getstr( master_cfg, "spool_dir" ) ); 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( " Data channel: %s\n", cfg_getstr( worker_cfg, "data" ) ); 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( "" ); } cfg_t *webserver_cfg = cfg_getnsec( cfg, "webserver", 0 ); unsigned int has_webserver = cfg_size( cfg, "webserver" ); if( has_webserver > 0 ) { #ifndef _WIN32 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" ); char *data = cfg_getstr( master_cfg, "data" ); char *spool_dir = cfg_getstr( master_cfg, "spool_dir" ); return master_init( control, data, spool_dir ); } 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" ), cfg_getstr( worker_cfg, "control" ), cfg_getstr( worker_cfg, "data" ) ); } } 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 = NULL; if( parse_options_and_arguments( argc, argv, &args_info ) ) { exit( EXIT_FAILURE ); } if( args_info.generate_config_given ) { printf( "%*s", biruda_conf_len, biruda_conf ); exit( EXIT_SUCCESS ); } if( args_info.guess_env_given ) { print_guessed_env( args_info.human_readable_given ); exit( EXIT_SUCCESS ); } if( !( args_info.cli_given || args_info.filename_given ) || args_info.config_file_given ) { if( read_config( args_info.config_file_given ? args_info.config_file_arg : DEFAULT_CONFIG_FILE, &cfg ) ) { if( cfg ) cfg_free( cfg ); cmdline_parser_free( &args_info ); return 1; } } if( args_info.test_given ) { if( cfg ) 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 ); if( cfg ) cfg_free( cfg ); cmdline_parser_free( &args_info ); return( ( test_config( &args_info ) == 0 ) ? EXIT_SUCCESS : EXIT_FAILURE ); } if( args_info.cli_given || args_info.filename_given ) { FILE *in = stdin; if( args_info.filename_given ) { in = fopen( args_info.filename_arg, "r" ); if( in == NULL ) { fprintf( stderr, "ERROR: Unable to open command file '%s'\n", args_info.filename_arg ); if( cfg ) cfg_free( cfg ); cmdline_parser_free( &args_info ); exit( EXIT_FAILURE ); } } int ret; unsigned int port = DEFAULT_WEBSERVER_PORT; if( args_info.config_file_given ) { cfg_t *webserver_cfg = cfg_getnsec( cfg, "webserver", 0 ); unsigned int has_webserver = cfg_size( cfg, "webserver" ); if( has_webserver > 0 ) { port = cfg_getint( webserver_cfg, "port" ); } } const char *host = DEFAULT_WEBSERVER_HOST; if( args_info.inputs_num >= 1 ) { host = args_info.inputs[0]; } if( args_info.inputs_num >= 2 ) { port = atoi( args_info.inputs[1] ); } #ifndef _WIN32 ret = start_interactive( ( in == stdin ) ? !args_info.no_colors_given : false, host, port, in ); #else if( args_info.filename_given ) { ret = start_interactive( false, host, port, in ); } else { fprintf( stderr, "FATAL: No CLI mode implemented for Windows currently!\n" ); ret = -1; } #endif if( cfg ) cfg_free( cfg ); cmdline_parser_free( &args_info ); if( in != stdin ) fclose( in ); exit( ( 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" ); if( cfg ) 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" ); if( cfg ) cfg_free( cfg ); cmdline_parser_free( &args_info ); exit( EXIT_FAILURE ); } } unsigned int has_webserver = cfg_size( cfg, "webserver" ); if( has_webserver ) { #ifndef _WIN32 if( create_webserver( cfg ) != 0 ) { fprintf( stderr, "FATAL: Unable to create webserver thread!\n" ); if( cfg ) 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 if( cfg ) cfg_free( cfg ); cmdline_parser_free( &args_info ); exit( EXIT_SUCCESS ); }