module ChainReactor lib_dir = File.dirname(__FILE__) $:.unshift lib_dir require 'version' require 'rubygems' require 'main' require 'create_log' require 'conf' # Set up command line options and usage with the main gem. Main do examples 'chain-reactor start chainfile.rb ', 'chain-reactor stop chainfile.rb', 'chain-reactor template' description 'Chain reactor is a server that responds to network events and runs ruby code. Run with the `start\' mode and \'--help\' to see options, or use the `template\' mode to create an example chainfile.' # Show the command line options if run without a mode. def run help! end # Start the server, as a daemon or on top. mode :start do description 'Start the chain reactor server, as a daemon or (optionally) on top. Daemonizing starts a background process, creates a pid file and a log file.' examples 'chain-reactor start chainfile.rb ', 'chain-reactor start chainfile.rb --ontop', 'chain-reactor start chainfile.rb --pidfile /path/to/pidfile.pid' input :chainfile do description 'A valid chainfile - run with the template mode to create an example' error do |e| STDERR.puts "chain-reactor: A valid chainfile must be supplied - run with the 'template' mode to generate an example" end end option :debug do log_levels = %w(debug info warn error fatal) argument :required validate { |str| log_levels.include? str } synopsis '--debug=('+log_levels.join('|')+') (0 ~> debug=info)' defaults 'info' description 'Type of messages to send to output' end option :pidfile do argument :required description 'Pid file for the daemonized process' defaults Dir.pwd+'/chain-reactor.pid' end option :multithreaded do cast :bool defaults false description 'Start each new connection in a separate thread' end option :ontop do cast :bool defaults false description 'Keep the process on top instead of daemonizing' end option :logfile do argument :required description 'Log file to write messages to' defaults Dir.pwd+'/chain-reactor.log' end option :address do argument :required defaults '127.0.0.1' description 'IP address to bind to' end option :port do argument :required defaults 1987 description 'Port number to bind to' end # Run when using the 'start' mode from the command line. def run require 'controller' conf = Conf.new(params) log = ChainReactor.create_logger(params[:debug].value) begin Controller.new(conf,log).start rescue Interrupt, SystemExit exit_status exit_success rescue ChainfileParserError => e log.fatal { "Failed to parse chainfile {#{e.original.class.name}}: #{e.message}" } exit_status exit_failure rescue Exception => e log.fatal { "Unexpected exception {#{e.class.name}}: #{e.message}" } log.debug { $!.backtrace.join("\n\t") } exit_status exit_failure end end end mode :stop do description 'Stop a running chain reactor server daemon. Specify the path to the pid file to stop a specific chain reactor instance.' input :chainfile do description 'A valid chainfile - run with the template argument to create a template' error do |e| STDERR.puts "chain-reactor: A valid chainfile must be supplied - run with the 'template' mode to generate an example" end end option :debug do log_levels = %w(debug info warn error fatal) argument :required validate { |str| log_levels.include? str } synopsis '--debug=('+log_levels.join('|')+') (0 ~> debug=info)' defaults 'info' description 'Type of messages to send to output' end option :pidfile do argument :required description 'Pid file for the daemonized process' defaults Dir.pwd+'/chain-reactor.pid' end # Run when using the 'stop' mode from the command line. def run require 'controller' log = ChainReactor.create_empty_logger(params[:debug].value) conf = Conf.new(params) puts "Attempting to stop chain reactor server with pid file: #{conf.pid_file}" c = Controller.new(conf,log) failed = c.stop exit_status(exit_failure) if failed end end mode 'template' do description 'Create a template chainfile, to see examples of configuration options and reaction syntax.' examples 'chain-reactor template' # Run when using the 'template' mode from the command line. def run puts <<-eos ##################### # Chainfile # ##################### # Do something when 127.0.0.1 sends a JSON react_to('127.0.0.1') do |data| # The JSON string sent by the client is now a ruby hash puts data.inspect end # Use the same reaction for multiple clients, and require keys to exist react_to( ['127.0.0.1','192.168.0.2'], :requires => [:mykey] ) do |data| # You can be sure this exists puts data[:mykey] end # Change the parser, if the client sends something other than a JSON react_to('192.168.0.2', :parser => :xml_simple) do |data| # The XML string sent by the client is now a ruby hash puts data.inspect end ## Everything from here on is optional, and can be overridden ## by equivalent command line parameters ## Address to bind to address '127.0.0.1' ## Port to listen on port 1987 ## Location of pid file, for daemon # pidfile '/var/run/chain-reactor.pid' ## Location of log file, for daemon # logfile '/var/log/chain-reactor.log' ## Whether to accept each client in a separate thread. # multithreaded true eos end end end end