instiki/vendor/rails/railties/lib/commands/process/reaper.rb

150 lines
4.6 KiB
Ruby
Raw Normal View History

2007-01-22 14:43:50 +01:00
require 'optparse'
require 'net/http'
require 'uri'
if RUBY_PLATFORM =~ /(:?mswin|mingw)/ then abort("Reaper is only for Unix") end
2007-01-22 14:43:50 +01:00
class Killer
2007-01-22 14:43:50 +01:00
class << self
# Searches for all processes matching the given keywords, and then invokes
# a specific action on each of them. This is useful for (e.g.) reloading a
# set of processes:
#
# Killer.process(:reload, "/tmp/pids", "dispatcher.*.pid")
def process(action, pid_path, pattern, keyword)
new(pid_path, pattern, keyword).process(action)
2007-01-22 14:43:50 +01:00
end
# Forces the (rails) application to reload by sending a +HUP+ signal to the
# process.
def reload(pid)
`kill -s HUP #{pid}`
2007-01-22 14:43:50 +01:00
end
# Force the (rails) application to restart by sending a +USR2+ signal to the
# process.
def restart(pid)
`kill -s USR2 #{pid}`
end
2007-01-22 14:43:50 +01:00
# Forces the (rails) application to gracefully terminate by sending a
# +TERM+ signal to the process.
def graceful(pid)
`kill -s TERM #{pid}`
end
2007-01-22 14:43:50 +01:00
# Forces the (rails) application to terminate immediately by sending a -9
# signal to the process.
def kill(pid)
`kill -9 #{pid}`
end
2007-01-22 14:43:50 +01:00
# Send a +USR1+ signal to the process.
def usr1(pid)
`kill -s USR1 #{pid}`
end
2007-01-22 14:43:50 +01:00
end
def initialize(pid_path, pattern, keyword=nil)
@pid_path, @pattern, @keyword = pid_path, pattern, keyword
2007-01-22 14:43:50 +01:00
end
def process(action)
pids = find_processes
2007-01-22 14:43:50 +01:00
if pids.empty?
warn "Couldn't find any pid file in '#{@pid_path}' matching '#{@pattern}'"
warn "(also looked for processes matching #{@keyword.inspect})" if @keyword
else
pids.each do |pid|
puts "#{action.capitalize}ing #{pid}"
self.class.send(action, pid)
end
delete_pid_files if terminating?(action)
end
2007-01-22 14:43:50 +01:00
end
private
def terminating?(action)
[ "kill", "graceful" ].include?(action)
end
def find_processes
files = pid_files
if files.empty?
find_processes_via_grep
else
files.collect { |pid_file| File.read(pid_file).to_i }
end
end
2007-01-22 14:43:50 +01:00
def find_processes_via_grep
lines = `ps axww -o 'pid command' | grep #{@keyword}`.split(/\n/).
reject { |line| line =~ /inq|ps axww|grep|spawn-fcgi|spawner|reaper/ }
lines.map { |line| line[/^\s*(\d+)/, 1].to_i }
end
def delete_pid_files
pid_files.each { |pid_file| File.delete(pid_file) }
end
def pid_files
Dir.glob(@pid_path + "/" + @pattern)
end
2007-01-22 14:43:50 +01:00
end
2007-01-22 14:43:50 +01:00
OPTIONS = {
:action => "restart",
:pid_path => File.expand_path(RAILS_ROOT + '/tmp/pids'),
:pattern => "dispatch.[0-9]*.pid",
:dispatcher => File.expand_path("#{RAILS_ROOT}/public/dispatch.fcgi")
2007-01-22 14:43:50 +01:00
}
ARGV.options do |opts|
opts.banner = "Usage: reaper [options]"
opts.separator ""
opts.on <<-EOF
Description:
The reaper is used to restart, reload, gracefully exit, and forcefully exit processes
running a Rails Dispatcher (or any other process responding to the same signals). This
is commonly done when a new version of the application is available, so the existing
processes can be updated to use the latest code.
It uses pid files to work on the processes and by default assume them to be located
in RAILS_ROOT/tmp/pids.
2007-01-22 14:43:50 +01:00
The reaper actions are:
* restart : Restarts the application by reloading both application and framework code
* reload : Only reloads the application, but not the framework (like the development environment)
* graceful: Marks all of the processes for exit after the next request
* kill : Forcefully exists all processes regardless of whether they're currently serving a request
Restart is the most common and default action.
Examples:
reaper # restarts the default dispatchers
reaper -a reload # reload the default dispatchers
reaper -a kill -r *.pid # kill all processes that keep pids in tmp/pids
2007-01-22 14:43:50 +01:00
EOF
opts.on(" Options:")
opts.on("-a", "--action=name", "reload|graceful|kill (default: #{OPTIONS[:action]})", String) { |v| OPTIONS[:action] = v }
opts.on("-p", "--pidpath=path", "default: #{OPTIONS[:pid_path]}", String) { |v| OPTIONS[:pid_path] = v }
opts.on("-r", "--pattern=pattern", "default: #{OPTIONS[:pattern]}", String) { |v| OPTIONS[:pattern] = v }
opts.on("-d", "--dispatcher=path", "DEPRECATED. default: #{OPTIONS[:dispatcher]}", String) { |v| OPTIONS[:dispatcher] = v }
2007-01-22 14:43:50 +01:00
opts.separator ""
opts.on("-h", "--help", "Show this help message.") { puts opts; exit }
opts.parse!
end
Killer.process(OPTIONS[:action], OPTIONS[:pid_path], OPTIONS[:pattern], OPTIONS[:dispatcher])