class Thin::Controllers::Controller

Controls one Thin server. Allow to start, stop, restart and configure a single thin server.

Attributes

options[RW]

Command line options passed to the thin script

Public Class Methods

new(options) click to toggle source
# File lib/thin/controllers/controller.rb, line 28
def initialize(options)
  @options = options
  
  if @options[:socket]
    @options.delete(:address)
    @options.delete(:port)
  end
end

Public Instance Methods

config() click to toggle source
# File lib/thin/controllers/controller.rb, line 110
def config
  config_file = @options.delete(:config) || raise(OptionRequired, :config)

  # Stringify keys
  @options.keys.each { |o| @options[o.to_s] = @options.delete(o) }

  File.open(config_file, 'w') { |f| f << @options.to_yaml }
  log_info "Wrote configuration to #{config_file}"
end
restart() click to toggle source
# File lib/thin/controllers/controller.rb, line 100
def restart
  raise OptionRequired, :pid unless @options[:pid]
  
  tail_log(@options[:log]) do
    if Server.restart(@options[:pid])
      wait_for_file :creation, @options[:pid]
    end
  end
end
start() click to toggle source
# File lib/thin/controllers/controller.rb, line 37
def start
  # Constantize backend class
  @options[:backend] = eval(@options[:backend], TOPLEVEL_BINDING) if @options[:backend]

  server = Server.new(@options[:socket] || @options[:address], # Server detects kind of socket
                      @options[:port],                         # Port ignored on UNIX socket
                      @options)
  
  # Set options
  server.pid_file                       = @options[:pid]
  server.log_file                       = @options[:log]
  server.timeout                        = @options[:timeout]
  server.maximum_connections            = @options[:max_conns]
  server.maximum_persistent_connections = @options[:max_persistent_conns]
  server.threaded                       = @options[:threaded]
  server.no_epoll                       = @options[:no_epoll] if server.backend.respond_to?(:no_epoll=)
  server.threadpool_size                = @options[:threadpool_size] if server.threaded?

  # ssl support
  if @options[:ssl]
    server.ssl = true
    server.ssl_options = { :private_key_file => @options[:ssl_key_file], :cert_chain_file => @options[:ssl_cert_file], :verify_peer => true }
  end

  # Detach the process, after this line the current process returns
  server.daemonize if @options[:daemonize]

  # +config+ must be called before changing privileges since it might require superuser power.
  server.config
  
  server.change_privilege @options[:user], @options[:group] if @options[:user] && @options[:group]

  # If a Rack config file is specified we eval it inside a Rack::Builder block to create
  # a Rack adapter from it. Or else we guess which adapter to use and load it.
  if @options[:rackup]
    server.app = load_rackup_config
  else
    server.app = load_adapter
  end

  # If a prefix is required, wrap in Rack URL mapper
  server.app = Rack::URLMap.new(@options[:prefix] => server.app) if @options[:prefix]

  # If a stats URL is specified, wrap in Stats adapter
  server.app = Stats::Adapter.new(server.app, @options[:stats]) if @options[:stats]

  # Register restart procedure which just start another process with same options,
  # so that's why this is done here.
  server.on_restart { Command.run(:start, @options) }

  server.start
end
stop() click to toggle source
# File lib/thin/controllers/controller.rb, line 90
def stop
  raise OptionRequired, :pid unless @options[:pid]

  tail_log(@options[:log]) do
    if Server.kill(@options[:pid], @options[:force] ? 0 : (@options[:timeout] || 60))
      wait_for_file :deletion, @options[:pid]
    end
  end
end

Protected Instance Methods

tail(file) click to toggle source

Acts like GNU tail command. Taken from Rails.

# File lib/thin/controllers/controller.rb, line 143
def tail(file)
  cursor = File.exist?(file) ? File.size(file) : 0
  last_checked = Time.now
  tail_thread = Thread.new do
    Thread.pass until File.exist?(file)
    File.open(file, 'r') do |f|
      loop do
        f.seek cursor
        if f.mtime > last_checked
          last_checked = f.mtime
          contents = f.read
          cursor += contents.length
          print contents
          STDOUT.flush
        end
        sleep 0.1
      end
    end
  end
  sleep 1 if File.exist?(file) # HACK Give the thread a little time to open the file
  tail_thread
end
tail_log(log_file) { || ... } click to toggle source

Tail the log file of server number during the execution of the block.

# File lib/thin/controllers/controller.rb, line 132
def tail_log(log_file)
  if log_file
    tail_thread = tail(log_file)
    yield
    tail_thread.kill
  else
    yield
  end
end
wait_for_file(state, file) click to toggle source

Wait for a pid file to either be created or deleted.

# File lib/thin/controllers/controller.rb, line 122
def wait_for_file(state, file)
  Timeout.timeout(@options[:timeout] || 30) do
    case state
    when :creation then sleep 0.1 until File.exist?(file)
    when :deletion then sleep 0.1 while File.exist?(file)
    end
  end
end

Private Instance Methods

load_adapter() click to toggle source
# File lib/thin/controllers/controller.rb, line 167
def load_adapter
  adapter = @options[:adapter] || Rack::Adapter.guess(@options[:chdir])
  log_info "Using #{adapter} adapter"
  Rack::Adapter.for(adapter, @options)
rescue Rack::AdapterNotFound => e
  raise InvalidOption, e.message
end
load_rackup_config() click to toggle source
# File lib/thin/controllers/controller.rb, line 175
def load_rackup_config
  ENV['RACK_ENV'] = @options[:environment]
  case @options[:rackup]
  when /\.rb$/
    Kernel.load(@options[:rackup])
    Object.const_get(File.basename(@options[:rackup], '.rb').capitalize.to_sym)
  when /\.ru$/
    Rack::Adapter.load(@options[:rackup])
  else
    raise "Invalid rackup file.  please specify either a .ru or .rb file"
  end
end