X-Git-Url: https://git.njae.me.uk/?a=blobdiff_plain;f=vendor%2Frails%2Frailties%2Flib%2Fcommands%2Fprocess%2Freaper.rb;fp=vendor%2Frails%2Frailties%2Flib%2Fcommands%2Fprocess%2Freaper.rb;h=95175d41e0d71230501d9a823029c0272df8fa03;hb=d115f2e23823271635bad69229a42cd8ac68debe;hp=0000000000000000000000000000000000000000;hpb=37cb670bf3ddde90b214e591f100ed4446469484;p=depot.git diff --git a/vendor/rails/railties/lib/commands/process/reaper.rb b/vendor/rails/railties/lib/commands/process/reaper.rb new file mode 100644 index 0000000..95175d4 --- /dev/null +++ b/vendor/rails/railties/lib/commands/process/reaper.rb @@ -0,0 +1,149 @@ +require 'optparse' +require 'net/http' +require 'uri' + +if RUBY_PLATFORM =~ /(:?mswin|mingw)/ then abort("Reaper is only for Unix") end + +class Killer + 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) + end + + # Forces the (rails) application to reload by sending a +HUP+ signal to the + # process. + def reload(pid) + `kill -s HUP #{pid}` + end + + # Force the (rails) application to restart by sending a +USR2+ signal to the + # process. + def restart(pid) + `kill -s USR2 #{pid}` + end + + # Forces the (rails) application to gracefully terminate by sending a + # +TERM+ signal to the process. + def graceful(pid) + `kill -s TERM #{pid}` + end + + # Forces the (rails) application to terminate immediately by sending a -9 + # signal to the process. + def kill(pid) + `kill -9 #{pid}` + end + + # Send a +USR1+ signal to the process. + def usr1(pid) + `kill -s USR1 #{pid}` + end + end + + def initialize(pid_path, pattern, keyword=nil) + @pid_path, @pattern, @keyword = pid_path, pattern, keyword + end + + def process(action) + pids = find_processes + + 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 + 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 + + 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 +end + + +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") +} + +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. + + 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 + 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 } + + 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])