Froze rails gems
[depot.git] / vendor / rails / railties / lib / commands / process / reaper.rb
1 require 'optparse'
2 require 'net/http'
3 require 'uri'
4
5 if RUBY_PLATFORM =~ /(:?mswin|mingw)/ then abort("Reaper is only for Unix") end
6
7 class Killer
8 class << self
9 # Searches for all processes matching the given keywords, and then invokes
10 # a specific action on each of them. This is useful for (e.g.) reloading a
11 # set of processes:
12 #
13 # Killer.process(:reload, "/tmp/pids", "dispatcher.*.pid")
14 def process(action, pid_path, pattern, keyword)
15 new(pid_path, pattern, keyword).process(action)
16 end
17
18 # Forces the (rails) application to reload by sending a +HUP+ signal to the
19 # process.
20 def reload(pid)
21 `kill -s HUP #{pid}`
22 end
23
24 # Force the (rails) application to restart by sending a +USR2+ signal to the
25 # process.
26 def restart(pid)
27 `kill -s USR2 #{pid}`
28 end
29
30 # Forces the (rails) application to gracefully terminate by sending a
31 # +TERM+ signal to the process.
32 def graceful(pid)
33 `kill -s TERM #{pid}`
34 end
35
36 # Forces the (rails) application to terminate immediately by sending a -9
37 # signal to the process.
38 def kill(pid)
39 `kill -9 #{pid}`
40 end
41
42 # Send a +USR1+ signal to the process.
43 def usr1(pid)
44 `kill -s USR1 #{pid}`
45 end
46 end
47
48 def initialize(pid_path, pattern, keyword=nil)
49 @pid_path, @pattern, @keyword = pid_path, pattern, keyword
50 end
51
52 def process(action)
53 pids = find_processes
54
55 if pids.empty?
56 warn "Couldn't find any pid file in '#{@pid_path}' matching '#{@pattern}'"
57 warn "(also looked for processes matching #{@keyword.inspect})" if @keyword
58 else
59 pids.each do |pid|
60 puts "#{action.capitalize}ing #{pid}"
61 self.class.send(action, pid)
62 end
63
64 delete_pid_files if terminating?(action)
65 end
66 end
67
68 private
69 def terminating?(action)
70 [ "kill", "graceful" ].include?(action)
71 end
72
73 def find_processes
74 files = pid_files
75 if files.empty?
76 find_processes_via_grep
77 else
78 files.collect { |pid_file| File.read(pid_file).to_i }
79 end
80 end
81
82 def find_processes_via_grep
83 lines = `ps axww -o 'pid command' | grep #{@keyword}`.split(/\n/).
84 reject { |line| line =~ /inq|ps axww|grep|spawn-fcgi|spawner|reaper/ }
85 lines.map { |line| line[/^\s*(\d+)/, 1].to_i }
86 end
87
88 def delete_pid_files
89 pid_files.each { |pid_file| File.delete(pid_file) }
90 end
91
92 def pid_files
93 Dir.glob(@pid_path + "/" + @pattern)
94 end
95 end
96
97
98 OPTIONS = {
99 :action => "restart",
100 :pid_path => File.expand_path(RAILS_ROOT + '/tmp/pids'),
101 :pattern => "dispatch.[0-9]*.pid",
102 :dispatcher => File.expand_path("#{RAILS_ROOT}/public/dispatch.fcgi")
103 }
104
105 ARGV.options do |opts|
106 opts.banner = "Usage: reaper [options]"
107
108 opts.separator ""
109
110 opts.on <<-EOF
111 Description:
112 The reaper is used to restart, reload, gracefully exit, and forcefully exit processes
113 running a Rails Dispatcher (or any other process responding to the same signals). This
114 is commonly done when a new version of the application is available, so the existing
115 processes can be updated to use the latest code.
116
117 It uses pid files to work on the processes and by default assume them to be located
118 in RAILS_ROOT/tmp/pids.
119
120 The reaper actions are:
121
122 * restart : Restarts the application by reloading both application and framework code
123 * reload : Only reloads the application, but not the framework (like the development environment)
124 * graceful: Marks all of the processes for exit after the next request
125 * kill : Forcefully exists all processes regardless of whether they're currently serving a request
126
127 Restart is the most common and default action.
128
129 Examples:
130 reaper # restarts the default dispatchers
131 reaper -a reload # reload the default dispatchers
132 reaper -a kill -r *.pid # kill all processes that keep pids in tmp/pids
133 EOF
134
135 opts.on(" Options:")
136
137 opts.on("-a", "--action=name", "reload|graceful|kill (default: #{OPTIONS[:action]})", String) { |v| OPTIONS[:action] = v }
138 opts.on("-p", "--pidpath=path", "default: #{OPTIONS[:pid_path]}", String) { |v| OPTIONS[:pid_path] = v }
139 opts.on("-r", "--pattern=pattern", "default: #{OPTIONS[:pattern]}", String) { |v| OPTIONS[:pattern] = v }
140 opts.on("-d", "--dispatcher=path", "DEPRECATED. default: #{OPTIONS[:dispatcher]}", String) { |v| OPTIONS[:dispatcher] = v }
141
142 opts.separator ""
143
144 opts.on("-h", "--help", "Show this help message.") { puts opts; exit }
145
146 opts.parse!
147 end
148
149 Killer.process(OPTIONS[:action], OPTIONS[:pid_path], OPTIONS[:pattern], OPTIONS[:dispatcher])