]> git.openstreetmap.org Git - rails.git/blob - vendor/plugins/irs_process_scripts/lib/commands/process/spawner.rb
169072c415c127f2ea0efa0d024e5e534905ebe8
[rails.git] / vendor / plugins / irs_process_scripts / 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