Skip to content

Commit

Permalink
Merge branch 'restart'
Browse files Browse the repository at this point in the history
  • Loading branch information
evanphx committed Apr 10, 2012
2 parents 17219d9 + 8920846 commit 008ea9d
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 28 deletions.
8 changes: 1 addition & 7 deletions bin/puma
Expand Up @@ -7,10 +7,4 @@ require 'puma/cli'

cli = Puma::CLI.new ARGV

begin
cli.run
rescue => e
raise e if $DEBUG
STDERR.puts e.message
exit 1
end
cli.run
3 changes: 2 additions & 1 deletion lib/puma/app/status.rb
Expand Up @@ -30,7 +30,8 @@ def call(env)

when "/restart"
if @cli and @cli.restart_on_stop!
@server.stop
@server.begin_restart

return [200, {}, ['{ "status": "ok" }']]
else
return [200, {}, ['{ "status": "not configured" }']]
Expand Down
132 changes: 115 additions & 17 deletions lib/puma/cli.rb
Expand Up @@ -33,9 +33,26 @@ def initialize(argv, stdout=STDOUT, stderr=STDERR)
@restart = false
@temp_status_path = nil

@listeners = []

setup_options

generate_restart_data

@inherited_fds = {}
remove = []

ENV.each do |k,v|
if k =~ /PUMA_INHERIT_\d+/
fd, url = v.split(":", 2)
@inherited_fds[url] = fd.to_i
remove << k
end
end

remove.each do |k|
ENV.delete k
end
end

def restart_on_stop!
Expand All @@ -62,6 +79,8 @@ def generate_restart_data

@restart_dir ||= Dir.pwd

@original_argv = ARGV.dup

if defined? Rubinius::OS_ARGV
@restart_argv = Rubinius::OS_ARGV
else
Expand All @@ -72,20 +91,47 @@ def generate_restart_data
# picked up in PATH.
#
if File.exists?($0)
@restart_argv = [Gem.ruby, $0] + ARGV
arg0 = [Gem.ruby, $0]
else
@restart_argv = [Gem.ruby, "-S", $0] + ARGV
arg0 = [Gem.ruby, "-S", $0]
end

@restart_argv = arg0 + ARGV
end
end

def restart!
@options[:on_restart].each do |blk|
blk.call self
end

if IS_JRUBY
@listeners.each_with_index do |(str,io),i|
io.close

# We have to unlink a unix socket path that's not being used
uri = URI.parse str
if uri.scheme == "unix"
path = "#{uri.host}#{uri.path}"
File.unlink path
end
end

require 'puma/jruby_restart'
JRubyRestart.chdir_exec(@restart_dir, Gem.ruby, *@restart_argv)
else
@listeners.each_with_index do |(l,io),i|
ENV["PUMA_INHERIT_#{i}"] = "#{io.to_i}:#{l}"
end

if cmd = @options[:restart_cmd]
argv = cmd.split(' ') + @original_argv
else
argv = @restart_argv
end

Dir.chdir @restart_dir
Kernel.exec(*@restart_argv)
Kernel.exec(*argv)
end
end

Expand Down Expand Up @@ -163,6 +209,11 @@ def setup_options
end
end

o.on "--restart-cmd CMD",
"The puma command to run during a hot restart",
"Default: inferred" do |cmd|
@options[:restart_cmd] = cmd
end
end

@parser.banner = "puma <options> <rackup file>"
Expand Down Expand Up @@ -190,7 +241,10 @@ def write_state
if path = @options[:state]
state = { "pid" => Process.pid }

state["config"] = @config
cfg = @config.dup
cfg.options.delete :on_restart

state["config"] = cfg

File.open(path, "w") do |f|
f.write state.to_yaml
Expand Down Expand Up @@ -237,25 +291,38 @@ def run
uri = URI.parse str
case uri.scheme
when "tcp"
log "* Listening on #{str}"
server.add_tcp_listener uri.host, uri.port
if fd = @inherited_fds.delete(str)
log "* Inherited #{str}"
io = server.inherit_tcp_listener uri.host, uri.port, fd
else
log "* Listening on #{str}"
io = server.add_tcp_listener uri.host, uri.port
end

@listeners << [str, io]
when "unix"
log "* Listening on #{str}"
path = "#{uri.host}#{uri.path}"
if fd = @inherited_fds.delete(str)
log "* Inherited #{str}"
io = server.inherit_unix_listener uri.path, fd
else
log "* Listening on #{str}"
path = "#{uri.host}#{uri.path}"

umask = nil
umask = nil

if uri.query
params = Rack::Utils.parse_query uri.query
if u = params['umask']
# Use Integer() to respect the 0 prefix as octal
umask = Integer(u)
if uri.query
params = Rack::Utils.parse_query uri.query
if u = params['umask']
# Use Integer() to respect the 0 prefix as octal
umask = Integer(u)
end
end

io = server.add_unix_listener path, umask
end

server.add_unix_listener path, umask
@listeners << [str, io]
when "ssl"
log "* Listening on #{str}"
params = Rack::Utils.parse_query uri.query
require 'openssl'

Expand All @@ -274,12 +341,38 @@ def run

ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE

server.add_ssl_listener uri.host, uri.port, ctx
if fd = @inherited_fds.delete(str)
log "* Inherited #{str}"
io = server.inherited_ssl_listener fd, ctx
else
log "* Listening on #{str}"
io = server.add_ssl_listener uri.host, uri.port, ctx
end

@listeners << [str, io]
else
error "Invalid URI: #{str}"
end
end

# If we inherited fds but didn't use them (because of a
# configuration change), then be sure to close them.
@inherited_fds.each do |str, fd|
log "* Closing unused inherited connection: #{str}"

begin
IO.for_fd(fd).close
rescue SystemCallError
end

# We have to unlink a unix socket path that's not being used
uri = URI.parse str
if uri.scheme == "unix"
path = "#{uri.host}#{uri.path}"
File.unlink path
end
end

@server = server

if str = @options[:control_url]
Expand Down Expand Up @@ -314,6 +407,11 @@ def run
@status = status
end

Signal.trap "SIGUSR2" do
@restart = true
server.begin_restart
end

log "Use Ctrl-C to stop"

begin
Expand Down
14 changes: 14 additions & 0 deletions lib/puma/configuration.rb
Expand Up @@ -8,10 +8,15 @@ class Configuration
def initialize(options)
@options = options
@options[:binds] ||= []
@options[:on_restart] ||= []
end

attr_reader :options

def initialize_copy(other)
@options = @options.dup
end

def load
if path = @options[:config_file]
DSL.new(@options)._load_from path
Expand Down Expand Up @@ -150,6 +155,15 @@ def bind(url)
@options[:binds] << url
end

# Code to run before doing a restart. This code should
# close logfiles, database connections, etc.
#
# This can be called multiple times to add code each time.
#
def on_restart(&blk)
@options[:on_restart] << blk
end

# Store the pid of the server in the file at +path+.
def pidfile(path)
@options[:pidfile] = path
Expand Down
1 change: 1 addition & 0 deletions lib/puma/const.rb
Expand Up @@ -133,6 +133,7 @@ module Const

STOP_COMMAND = "?".freeze
HALT_COMMAND = "!".freeze
RESTART_COMMAND = "R".freeze

RACK_INPUT = "rack.input".freeze
RACK_URL_SCHEME = "rack.url_scheme".freeze
Expand Down
42 changes: 39 additions & 3 deletions lib/puma/server.rb
Expand Up @@ -109,6 +109,13 @@ def add_tcp_listener(host, port, optimize_for_latency=true, backlog=1024)
end
s.listen backlog
@ios << s
s
end

def inherit_tcp_listener(host, port, fd)
s = TCPServer.for_fd(fd)
@ios << s
s
end

def add_ssl_listener(host, port, ctx, optimize_for_latency=true, backlog=1024)
Expand All @@ -119,6 +126,13 @@ def add_ssl_listener(host, port, ctx, optimize_for_latency=true, backlog=1024)
s.listen backlog
@proto_env[HTTPS_KEY] = HTTPS
@ios << OpenSSL::SSL::SSLServer.new(s, ctx)
s
end

def inherited_ssl_listener(fd, ctx)
s = TCPServer.for_fd(fd)
@ios << OpenSSL::SSL::SSLServer.new(s, ctx)
s
end

# Tell the server to listen on +path+ as a UNIX domain socket.
Expand All @@ -131,10 +145,22 @@ def add_unix_listener(path, umask=nil)

begin
old_mask = File.umask(umask)
@ios << UNIXServer.new(path)
s = UNIXServer.new(path)
@ios << s
ensure
File.umask old_mask
end

s
end

def inherit_unix_listener(path, fd)
@unix_paths << path

s = UNIXServer.for_fd fd
@ios << s

s
end

def backlog
Expand Down Expand Up @@ -187,8 +213,10 @@ def run

graceful_shutdown if @status == :stop
ensure
@ios.each { |i| i.close }
@unix_paths.each { |i| File.unlink i }
unless @status == :restart
@ios.each { |i| i.close }
@unix_paths.each { |i| File.unlink i }
end
end
end

Expand All @@ -206,6 +234,9 @@ def handle_check
when HALT_COMMAND
@status = :halt
return true
when RESTART_COMMAND
@status = :restart
return true
end

return false
Expand Down Expand Up @@ -585,5 +616,10 @@ def halt(sync=false)

@thread.join if @thread && sync
end

def begin_restart
@persistent_wakeup.close
@notify << RESTART_COMMAND
end
end
end

0 comments on commit 008ea9d

Please sign in to comment.