CLI runner. Parse options and send command to the correct Controller.
Commands that wont load options from the config file
Arguments to be passed to the command.
Name of the command to be runned.
Parsed options
Return all available commands
# File lib/thin/runner.rb, line 24 def self.commands commands = COMMANDS commands += LINUX_ONLY_COMMANDS if Thin.linux? commands end
# File lib/thin/runner.rb, line 30 def initialize(argv) @argv = argv # Default options values @options = { :chdir => Dir.pwd, :environment => 'development', :address => '0.0.0.0', :port => Server::DEFAULT_PORT, :timeout => Server::DEFAULT_TIMEOUT, :log => 'log/thin.log', :pid => 'tmp/pids/thin.pid', :max_conns => Server::DEFAULT_MAXIMUM_CONNECTIONS, :max_persistent_conns => Server::DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS, :require => [], :wait => Controllers::Cluster::DEFAULT_WAIT_TIME } parse! end
true
if we're controlling a cluster.
# File lib/thin/runner.rb, line 195 def cluster? @options[:only] || @options[:servers] || @options[:config] end
Parse the options.
# File lib/thin/runner.rb, line 141 def parse! parser.parse! @argv @command = @argv.shift @arguments = @argv end
# File lib/thin/runner.rb, line 51 def parser # NOTE: If you add an option here make sure the key in the +options+ hash is the # same as the name of the command line option. # +option+ keys are used to build the command line to launch other processes, # see <tt>lib/thin/command.rb</tt>. @parser ||= OptionParser.new do |opts| opts.banner = "Usage: thin [options] #{self.class.commands.join('|')}" opts.separator "" opts.separator "Server options:" opts.on("-a", "--address HOST", "bind to HOST address " + "(default: #{@options[:address]})") { |host| @options[:address] = host } opts.on("-p", "--port PORT", "use PORT (default: #{@options[:port]})") { |port| @options[:port] = port.to_i } opts.on("-S", "--socket FILE", "bind to unix domain socket") { |file| @options[:socket] = file } opts.on("-y", "--swiftiply [KEY]", "Run using swiftiply") { |key| @options[:swiftiply] = key } opts.on("-A", "--adapter NAME", "Rack adapter to use (default: autodetect)", "(#{Rack::ADAPTERS.map{|(a,b)|a}.join(', ')})") { |name| @options[:adapter] = name } opts.on("-R", "--rackup FILE", "Load a Rack config file instead of " + "Rack adapter") { |file| @options[:rackup] = file } opts.on("-c", "--chdir DIR", "Change to dir before starting") { |dir| @options[:chdir] = File.expand_path(dir) } opts.on( "--stats PATH", "Mount the Stats adapter under PATH") { |path| @options[:stats] = path } opts.separator "" opts.separator "SSL options:" opts.on( "--ssl", "Enables SSL") { @options[:ssl] = true } opts.on( "--ssl-key-file PATH", "Path to private key") { |path| @options[:ssl_key_file] = path } opts.on( "--ssl-cert-file PATH", "Path to certificate") { |path| @options[:ssl_cert_file] = path } opts.on( "--ssl-verify", "Enables SSL certificate verification") { @options[:ssl_verify] = true } opts.separator "" opts.separator "Adapter options:" opts.on("-e", "--environment ENV", "Framework environment " + "(default: #{@options[:environment]})") { |env| @options[:environment] = env } opts.on( "--prefix PATH", "Mount the app under PATH (start with /)") { |path| @options[:prefix] = path } unless Thin.win? # Daemonizing not supported on Windows opts.separator "" opts.separator "Daemon options:" opts.on("-d", "--daemonize", "Run daemonized in the background") { @options[:daemonize] = true } opts.on("-l", "--log FILE", "File to redirect output " + "(default: #{@options[:log]})") { |file| @options[:log] = file } opts.on("-P", "--pid FILE", "File to store PID " + "(default: #{@options[:pid]})") { |file| @options[:pid] = file } opts.on("-u", "--user NAME", "User to run daemon as (use with -g)") { |user| @options[:user] = user } opts.on("-g", "--group NAME", "Group to run daemon as (use with -u)") { |group| @options[:group] = group } opts.on( "--tag NAME", "Additional text to display in process listing") { |tag| @options[:tag] = tag } opts.separator "" opts.separator "Cluster options:" opts.on("-s", "--servers NUM", "Number of servers to start") { |num| @options[:servers] = num.to_i } opts.on("-o", "--only NUM", "Send command to only one server of the cluster") { |only| @options[:only] = only.to_i } opts.on("-C", "--config FILE", "Load options from config file") { |file| @options[:config] = file } opts.on( "--all [DIR]", "Send command to each config files in DIR") { |dir| @options[:all] = dir } if Thin.linux? opts.on("-O", "--onebyone", "Restart the cluster one by one (only works with restart command)") { @options[:onebyone] = true } opts.on("-w", "--wait NUM", "Maximum wait time for server to be started in seconds (use with -O)") { |time| @options[:wait] = time.to_i } end opts.separator "" opts.separator "Tuning options:" opts.on("-b", "--backend CLASS", "Backend to use, full classname") { |name| @options[:backend] = name } opts.on("-t", "--timeout SEC", "Request or command timeout in sec " + "(default: #{@options[:timeout]})") { |sec| @options[:timeout] = sec.to_i } opts.on("-f", "--force", "Force the execution of the command") { @options[:force] = true } opts.on( "--max-conns NUM", "Maximum number of open file descriptors " + "(default: #{@options[:max_conns]})", "Might require sudo to set higher than 1024") { |num| @options[:max_conns] = num.to_i } unless Thin.win? opts.on( "--max-persistent-conns NUM", "Maximum number of persistent connections", "(default: #{@options[:max_persistent_conns]})") { |num| @options[:max_persistent_conns] = num.to_i } opts.on( "--threaded", "Call the Rack application in threads " + "[experimental]") { @options[:threaded] = true } opts.on( "--no-epoll", "Disable the use of epoll") { @options[:no_epoll] = true } if Thin.linux? opts.separator "" opts.separator "Common options:" opts.on_tail("-r", "--require FILE", "require the library") { |file| @options[:require] << file } opts.on_tail("-D", "--debug", "Set debugging on") { @options[:debug] = true } opts.on_tail("-V", "--trace", "Set tracing on (log raw request/response)") { @options[:trace] = true } opts.on_tail("-h", "--help", "Show this message") { puts opts; exit } opts.on_tail('-v', '--version', "Show version") { puts Thin::SERVER; exit } end end
Parse the current shell arguments and run the command. Exits on error.
# File lib/thin/runner.rb, line 149 def run! if self.class.commands.include?(@command) run_command elsif @command.nil? puts "Command required" puts @parser exit 1 else abort "Unknown command: #{@command}. Use one of #{self.class.commands.join(', ')}" end end
Send the command to the controller: single instance or cluster.
# File lib/thin/runner.rb, line 162 def run_command load_options_from_config_file! unless CONFIGLESS_COMMANDS.include?(@command) # PROGRAM_NAME is relative to the current directory, so make sure # we store and expand it before changing directory. Command.script = File.expand_path($PROGRAM_NAME) # Change the current directory ASAP so that all relative paths are # relative to this one. Dir.chdir(@options[:chdir]) unless CONFIGLESS_COMMANDS.include?(@command) @options[:require].each { |r| ruby_require r } Logging.debug = @options[:debug] Logging.trace = @options[:trace] controller = case when cluster? then Controllers::Cluster.new(@options) when service? then Controllers::Service.new(@options) else Controllers::Controller.new(@options) end if controller.respond_to?(@command) begin controller.send(@command, *@arguments) rescue RunnerError => e abort e.message end else abort "Invalid options for command: #{@command}" end end
true
if we're acting a as system service.
# File lib/thin/runner.rb, line 200 def service? @options.has_key?(:all) || @command == 'install' end