#!/usr/bin/env ruby # Will require rflow after option parsing to speed up a couple of # startup cases (version and help) that don't need it require 'optparse' if Process::Sys.geteuid == 0 STDERR.puts "Error: RFlow will not run as root." exit 1 end options = { :daemonize => true, :startup_log_level => :INFO, :extensions_file_paths => [], :gems => [] } option_parser = OptionParser.new do |opts| opts.banner = < e STDERR.puts "Error processing arguments: #{e.class}: #{e.message}" exit 1 end # Now require rflow because the following parts of the startup require # pieces (usually RFlow::Configuration or RFlow.logger) begin require 'rflow' rescue Exception => e STDERR.puts "Error loading RFlow: #{e.class} - #{e.message}\n#{e.backtrace.join("\n")}" exit 1 end # Set up the startup logging, which is distinct from the runtime logging that # is defined in the config database. The startup logging will always go to # STDOUT, as well as to the file specified with the '-l' parameter # # This logging setup will be used while we call into RFlow to check on or setup # things, like the config database. We want those log messages to go to the # startup log when setting up. The running log will transition to what is # specified in the config database. RFlow.logger.reconfigure({'rflow.application_name' => 'startup', 'rflow.log_level' => options[:startup_log_level].to_s, 'rflow.log_file_path' => options[:startup_log_file_path]}, true) command = ARGV[0] unless ['start', 'stop', 'status', 'load'].include? command RFlow.logger.fatal "Command needs to be one of [start|stop|status|load]\n#{option_parser.help}" exit 1 end if options[:config_file_path] && command != 'load' RFlow.logger.fatal "Config file only valid for 'load' command" exit 1 end unless options[:config_database_path] RFlow.logger.warn "Config database not specified, using default 'config.sqlite'" options[:config_database_path] = File.expand_path(File.join(Dir.getwd, 'config.sqlite')) end case command when 'load' # Load the database with the config file, if it exists. Will # otherwise default values (not very useful) if options[:config_file_path] unless File.exist? options[:config_file_path] RFlow.logger.fatal "Config file '#{options[:config_file_path]}' not found\n#{option_parser.help}" exit 1 end unless File.readable? options[:config_file_path] RFlow.logger.fatal "Config file '#{options[:config_file_path]}' not readable\n#{option_parser.help}" exit 1 end end if File.exist? options[:config_database_path] RFlow.logger.fatal "Config database '#{options[:config_database_path]}' exists, exiting to prevent accidental overwrite from config file '#{options[:config_file_path]}'" exit 1 end RFlow.logger.info "Creating config database '#{options[:config_database_path]}'" begin config = RFlow::Configuration::initialize_database(options[:config_database_path], options[:config_file_path]) rescue Exception => e RFlow.logger.fatal "Error initializing configuration database: #{e.message}: #{e.backtrace.join "\n"}" exit 1 end RFlow.logger.warn "Successfully initialized database '#{options[:config_database_path]}' with '#{options[:config_file_path]}'" RFlow.logger.debug config.to_s exit 0 end # Load the database config and start setting up environment begin config = RFlow::Configuration.new(options[:config_database_path]) rescue Exception => e RFlow.logger.fatal "Error loading config database: #{e.class} - #{e.message}" exit 1 end Dir.chdir(File.dirname(options[:config_database_path])) Dir.chdir(config['rflow.application_directory_path']) pid_file = RFlow::PIDFile.new(config['rflow.pid_file_path']) case command when 'stop' if pid_file.running? RFlow.logger.info "#{config['rflow.application_name']} running, process #{pid_file.read} found in #{pid_file.to_s}, terminating" # TODO: check if it actually shut down pid_file.signal(:INT) else RFlow.logger.warn "#{config['rflow.application_name']} process not found in #{pid_file.to_s}" exit 1 end exit 0 when 'status' unless pid_file.running? RFlow.logger.error "#{config['rflow.application_name']} process not found in #{pid_file.to_s}" exit 1 end RFlow.logger.info "#{config['rflow.application_name']} running, process #{pid_file.read} found in #{pid_file.to_s}" exit 0 when 'start' if pid_file.running? RFlow.logger.error "#{config['rflow.application_name']} already running, process #{pid_file.read} found in #{pid_file.to_s}" exit 1 end end # We should have eliminated all commands but 'start' at this point # require all the gem extensions options[:gems].each do |extension_gem| RFlow.logger.info "Requiring #{extension_gem}" require extension_gem end # load all the file extensions options[:extensions_file_paths].each do |extensions_file_path| RFlow.logger.info "Loading #{extensions_file_path}" unless File.readable? extensions_file_path RFlow.logger.fatal "Extensions file ('#{Dir.getwd}') '#{extensions_file_path}' not readable\n#{option_parser.help}" exit 1 end begin load extensions_file_path rescue Exception => e RFlow.logger.fatal "Error running rflow: #{e.class}: #{e.message}, because: #{e.backtrace}" exit 1 end end # Start the flow begin RFlow.run! options[:config_database_path], options[:daemonize] rescue Exception => e RFlow.logger.fatal "Error running rflow: #{e.class}: #{e.message}, because: #{e.backtrace}" exit(1) end __END__