Froze rails gems
[depot.git] / vendor / rails / railties / lib / commands / process / spawner.rb
1 require 'active_support'
2 require 'optparse'
3 require 'socket'
4 require 'fileutils'
5
6 def daemonize #:nodoc:
7 exit if fork # Parent exits, child continues.
8 Process.setsid # Become session leader.
9 exit if fork # Zap session leader. See [1].
10 Dir.chdir "/" # Release old working directory.
11 File.umask 0000 # Ensure sensible umask. Adjust as needed.
12 STDIN.reopen "/dev/null" # Free file descriptors and
13 STDOUT.reopen "/dev/null", "a" # point them somewhere sensible.
14 STDERR.reopen STDOUT # STDOUT/ERR should better go to a logfile.
15 end
16
17 class Spawner
18 def self.record_pid(name = "#{OPTIONS[:process]}.spawner", id = Process.pid)
19 FileUtils.mkdir_p(OPTIONS[:pids])
20 File.open(File.expand_path(OPTIONS[:pids] + "/#{name}.pid"), "w+") { |f| f.write(id) }
21 end
22
23 def self.spawn_all
24 OPTIONS[:instances].times do |i|
25 port = OPTIONS[:port] + i
26 print "Checking if something is already running on #{OPTIONS[:address]}:#{port}..."
27
28 begin
29 srv = TCPServer.new(OPTIONS[:address], port)
30 srv.close
31 srv = nil
32
33 puts "NO"
34 puts "Starting dispatcher on port: #{OPTIONS[:address]}:#{port}"
35
36 FileUtils.mkdir_p(OPTIONS[:pids])
37 spawn(port)
38 rescue
39 puts "YES"
40 end
41 end
42 end
43 end
44
45 class FcgiSpawner < Spawner
46 def self.spawn(port)
47 cmd = "#{OPTIONS[:spawner]} -f #{OPTIONS[:dispatcher]} -p #{port} -P #{OPTIONS[:pids]}/#{OPTIONS[:process]}.#{port}.pid"
48 cmd << " -a #{OPTIONS[:address]}" if can_bind_to_custom_address?
49 system(cmd)
50 end
51
52 def self.can_bind_to_custom_address?
53 @@can_bind_to_custom_address ||= /^\s-a\s/.match `#{OPTIONS[:spawner]} -h`
54 end
55 end
56
57 class MongrelSpawner < Spawner
58 def self.spawn(port)
59 cmd =
60 "mongrel_rails start -d " +
61 "-a #{OPTIONS[:address]} " +
62 "-p #{port} " +
63 "-P #{OPTIONS[:pids]}/#{OPTIONS[:process]}.#{port}.pid " +
64 "-e #{OPTIONS[:environment]} " +
65 "-c #{OPTIONS[:rails_root]} " +
66 "-l #{OPTIONS[:rails_root]}/log/mongrel.log"
67
68 # Add prefix functionality to spawner's call to mongrel_rails
69 # Digging through mongrel's project subversion server, the earliest
70 # Tag that has prefix implemented in the bin/mongrel_rails file
71 # is 0.3.15 which also happens to be the earliest tag listed.
72 # References: http://mongrel.rubyforge.org/svn/tags
73 if Mongrel::Const::MONGREL_VERSION.to_f >=0.3 && !OPTIONS[:prefix].nil?
74 cmd = cmd + " --prefix #{OPTIONS[:prefix]}"
75 end
76 system(cmd)
77 end
78
79 def self.can_bind_to_custom_address?
80 true
81 end
82 end
83
84
85 begin
86 require_library_or_gem 'fcgi'
87 rescue Exception
88 # FCGI not available
89 end
90
91 begin
92 require_library_or_gem 'mongrel'
93 rescue Exception
94 # Mongrel not available
95 end
96
97 server = case ARGV.first
98 when "fcgi", "mongrel"
99 ARGV.shift
100 else
101 if defined?(Mongrel)
102 "mongrel"
103 elsif RUBY_PLATFORM !~ /(:?mswin|mingw)/ && !silence_stderr { `spawn-fcgi -version` }.blank? && defined?(FCGI)
104 "fcgi"
105 end
106 end
107
108 case server
109 when "fcgi"
110 puts "=> Starting FCGI dispatchers"
111 spawner_class = FcgiSpawner
112 when "mongrel"
113 puts "=> Starting mongrel dispatchers"
114 spawner_class = MongrelSpawner
115 else
116 puts "Neither FCGI (spawn-fcgi) nor Mongrel was installed and available!"
117 exit(0)
118 end
119
120
121
122 OPTIONS = {
123 :environment => "production",
124 :spawner => '/usr/bin/env spawn-fcgi',
125 :dispatcher => File.expand_path(RELATIVE_RAILS_ROOT + '/public/dispatch.fcgi'),
126 :pids => File.expand_path(RELATIVE_RAILS_ROOT + "/tmp/pids"),
127 :rails_root => File.expand_path(RELATIVE_RAILS_ROOT),
128 :process => "dispatch",
129 :port => 8000,
130 :address => '0.0.0.0',
131 :instances => 3,
132 :repeat => nil,
133 :prefix => nil
134 }
135
136 ARGV.options do |opts|
137 opts.banner = "Usage: spawner [platform] [options]"
138
139 opts.separator ""
140
141 opts.on <<-EOF
142 Description:
143 The spawner is a wrapper for spawn-fcgi and mongrel that makes it
144 easier to start multiple processes running the Rails dispatcher. The
145 spawn-fcgi command is included with the lighttpd web server, but can
146 be used with both Apache and lighttpd (and any other web server
147 supporting externally managed FCGI processes). Mongrel automatically
148 ships with with mongrel_rails for starting dispatchers.
149
150 The first choice you need to make is whether to spawn the Rails
151 dispatchers as FCGI or Mongrel. By default, this spawner will prefer
152 Mongrel, so if that's installed, and no platform choice is made,
153 Mongrel is used.
154
155 Then decide a starting port (default is 8000) and the number of FCGI
156 process instances you'd like to run. So if you pick 9100 and 3
157 instances, you'll start processes on 9100, 9101, and 9102.
158
159 By setting the repeat option, you get a protection loop, which will
160 attempt to restart any FCGI processes that might have been exited or
161 outright crashed.
162
163 You can select bind address for started processes. By default these
164 listen on every interface. For single machine installations you would
165 probably want to use 127.0.0.1, hiding them form the outside world.
166
167 Examples:
168 spawner # starts instances on 8000, 8001, and 8002
169 # using Mongrel if available.
170 spawner fcgi # starts instances on 8000, 8001, and 8002
171 # using FCGI.
172 spawner mongrel -i 5 # starts instances on 8000, 8001, 8002,
173 # 8003, and 8004 using Mongrel.
174 spawner -p 9100 -i 10 # starts 10 instances counting from 9100 to
175 # 9109 using Mongrel if available.
176 spawner -p 9100 -r 5 # starts 3 instances counting from 9100 to
177 # 9102 and attempts start them every 5
178 # seconds.
179 spawner -a 127.0.0.1 # starts 3 instances binding to localhost
180 EOF
181
182 opts.on(" Options:")
183
184 opts.on("-p", "--port=number", Integer, "Starting port number (default: #{OPTIONS[:port]})") { |v| OPTIONS[:port] = v }
185
186 if spawner_class.can_bind_to_custom_address?
187 opts.on("-a", "--address=ip", String, "Bind to IP address (default: #{OPTIONS[:address]})") { |v| OPTIONS[:address] = v }
188 end
189
190 opts.on("-p", "--port=number", Integer, "Starting port number (default: #{OPTIONS[:port]})") { |v| OPTIONS[:port] = v }
191 opts.on("-i", "--instances=number", Integer, "Number of instances (default: #{OPTIONS[:instances]})") { |v| OPTIONS[:instances] = v }
192 opts.on("-r", "--repeat=seconds", Integer, "Repeat spawn attempts every n seconds (default: off)") { |v| OPTIONS[:repeat] = v }
193 opts.on("-e", "--environment=name", String, "test|development|production (default: #{OPTIONS[:environment]})") { |v| OPTIONS[:environment] = v }
194 opts.on("-P", "--prefix=path", String, "URL prefix for Rails app. [Used only with Mongrel > v0.3.15]: (default: #{OPTIONS[:prefix]})") { |v| OPTIONS[:prefix] = v }
195 opts.on("-n", "--process=name", String, "default: #{OPTIONS[:process]}") { |v| OPTIONS[:process] = v }
196 opts.on("-s", "--spawner=path", String, "default: #{OPTIONS[:spawner]}") { |v| OPTIONS[:spawner] = v }
197 opts.on("-d", "--dispatcher=path", String, "default: #{OPTIONS[:dispatcher]}") { |dispatcher| OPTIONS[:dispatcher] = File.expand_path(dispatcher) }
198
199 opts.separator ""
200
201 opts.on("-h", "--help", "Show this help message.") { puts opts; exit }
202
203 opts.parse!
204 end
205
206 ENV["RAILS_ENV"] = OPTIONS[:environment]
207
208 if OPTIONS[:repeat]
209 daemonize
210 trap("TERM") { exit }
211 spawner_class.record_pid
212
213 loop do
214 spawner_class.spawn_all
215 sleep(OPTIONS[:repeat])
216 end
217 else
218 spawner_class.spawn_all
219 end