X-Git-Url: https://git.njae.me.uk/?a=blobdiff_plain;f=vendor%2Frails%2Frailties%2Flib%2Fcommands%2Fprocess%2Fspawner.rb;fp=vendor%2Frails%2Frailties%2Flib%2Fcommands%2Fprocess%2Fspawner.rb;h=8bf47abb755d5ca0e33c7b917e75280b927501d5;hb=d115f2e23823271635bad69229a42cd8ac68debe;hp=0000000000000000000000000000000000000000;hpb=37cb670bf3ddde90b214e591f100ed4446469484;p=depot.git diff --git a/vendor/rails/railties/lib/commands/process/spawner.rb b/vendor/rails/railties/lib/commands/process/spawner.rb new file mode 100644 index 0000000..8bf47ab --- /dev/null +++ b/vendor/rails/railties/lib/commands/process/spawner.rb @@ -0,0 +1,219 @@ +require 'active_support' +require 'optparse' +require 'socket' +require 'fileutils' + +def daemonize #:nodoc: + exit if fork # Parent exits, child continues. + Process.setsid # Become session leader. + exit if fork # Zap session leader. See [1]. + Dir.chdir "/" # Release old working directory. + File.umask 0000 # Ensure sensible umask. Adjust as needed. + STDIN.reopen "/dev/null" # Free file descriptors and + STDOUT.reopen "/dev/null", "a" # point them somewhere sensible. + STDERR.reopen STDOUT # STDOUT/ERR should better go to a logfile. +end + +class Spawner + def self.record_pid(name = "#{OPTIONS[:process]}.spawner", id = Process.pid) + FileUtils.mkdir_p(OPTIONS[:pids]) + File.open(File.expand_path(OPTIONS[:pids] + "/#{name}.pid"), "w+") { |f| f.write(id) } + end + + def self.spawn_all + OPTIONS[:instances].times do |i| + port = OPTIONS[:port] + i + print "Checking if something is already running on #{OPTIONS[:address]}:#{port}..." + + begin + srv = TCPServer.new(OPTIONS[:address], port) + srv.close + srv = nil + + puts "NO" + puts "Starting dispatcher on port: #{OPTIONS[:address]}:#{port}" + + FileUtils.mkdir_p(OPTIONS[:pids]) + spawn(port) + rescue + puts "YES" + end + end + end +end + +class FcgiSpawner < Spawner + def self.spawn(port) + cmd = "#{OPTIONS[:spawner]} -f #{OPTIONS[:dispatcher]} -p #{port} -P #{OPTIONS[:pids]}/#{OPTIONS[:process]}.#{port}.pid" + cmd << " -a #{OPTIONS[:address]}" if can_bind_to_custom_address? + system(cmd) + end + + def self.can_bind_to_custom_address? + @@can_bind_to_custom_address ||= /^\s-a\s/.match `#{OPTIONS[:spawner]} -h` + end +end + +class MongrelSpawner < Spawner + def self.spawn(port) + cmd = + "mongrel_rails start -d " + + "-a #{OPTIONS[:address]} " + + "-p #{port} " + + "-P #{OPTIONS[:pids]}/#{OPTIONS[:process]}.#{port}.pid " + + "-e #{OPTIONS[:environment]} " + + "-c #{OPTIONS[:rails_root]} " + + "-l #{OPTIONS[:rails_root]}/log/mongrel.log" + + # Add prefix functionality to spawner's call to mongrel_rails + # Digging through mongrel's project subversion server, the earliest + # Tag that has prefix implemented in the bin/mongrel_rails file + # is 0.3.15 which also happens to be the earliest tag listed. + # References: http://mongrel.rubyforge.org/svn/tags + if Mongrel::Const::MONGREL_VERSION.to_f >=0.3 && !OPTIONS[:prefix].nil? + cmd = cmd + " --prefix #{OPTIONS[:prefix]}" + end + system(cmd) + end + + def self.can_bind_to_custom_address? + true + end +end + + +begin + require_library_or_gem 'fcgi' +rescue Exception + # FCGI not available +end + +begin + require_library_or_gem 'mongrel' +rescue Exception + # Mongrel not available +end + +server = case ARGV.first + when "fcgi", "mongrel" + ARGV.shift + else + if defined?(Mongrel) + "mongrel" + elsif RUBY_PLATFORM !~ /(:?mswin|mingw)/ && !silence_stderr { `spawn-fcgi -version` }.blank? && defined?(FCGI) + "fcgi" + end +end + +case server + when "fcgi" + puts "=> Starting FCGI dispatchers" + spawner_class = FcgiSpawner + when "mongrel" + puts "=> Starting mongrel dispatchers" + spawner_class = MongrelSpawner + else + puts "Neither FCGI (spawn-fcgi) nor Mongrel was installed and available!" + exit(0) +end + + + +OPTIONS = { + :environment => "production", + :spawner => '/usr/bin/env spawn-fcgi', + :dispatcher => File.expand_path(RELATIVE_RAILS_ROOT + '/public/dispatch.fcgi'), + :pids => File.expand_path(RELATIVE_RAILS_ROOT + "/tmp/pids"), + :rails_root => File.expand_path(RELATIVE_RAILS_ROOT), + :process => "dispatch", + :port => 8000, + :address => '0.0.0.0', + :instances => 3, + :repeat => nil, + :prefix => nil +} + +ARGV.options do |opts| + opts.banner = "Usage: spawner [platform] [options]" + + opts.separator "" + + opts.on <<-EOF + Description: + The spawner is a wrapper for spawn-fcgi and mongrel that makes it + easier to start multiple processes running the Rails dispatcher. The + spawn-fcgi command is included with the lighttpd web server, but can + be used with both Apache and lighttpd (and any other web server + supporting externally managed FCGI processes). Mongrel automatically + ships with with mongrel_rails for starting dispatchers. + + The first choice you need to make is whether to spawn the Rails + dispatchers as FCGI or Mongrel. By default, this spawner will prefer + Mongrel, so if that's installed, and no platform choice is made, + Mongrel is used. + + Then decide a starting port (default is 8000) and the number of FCGI + process instances you'd like to run. So if you pick 9100 and 3 + instances, you'll start processes on 9100, 9101, and 9102. + + By setting the repeat option, you get a protection loop, which will + attempt to restart any FCGI processes that might have been exited or + outright crashed. + + You can select bind address for started processes. By default these + listen on every interface. For single machine installations you would + probably want to use 127.0.0.1, hiding them form the outside world. + + Examples: + spawner # starts instances on 8000, 8001, and 8002 + # using Mongrel if available. + spawner fcgi # starts instances on 8000, 8001, and 8002 + # using FCGI. + spawner mongrel -i 5 # starts instances on 8000, 8001, 8002, + # 8003, and 8004 using Mongrel. + spawner -p 9100 -i 10 # starts 10 instances counting from 9100 to + # 9109 using Mongrel if available. + spawner -p 9100 -r 5 # starts 3 instances counting from 9100 to + # 9102 and attempts start them every 5 + # seconds. + spawner -a 127.0.0.1 # starts 3 instances binding to localhost + EOF + + opts.on(" Options:") + + opts.on("-p", "--port=number", Integer, "Starting port number (default: #{OPTIONS[:port]})") { |v| OPTIONS[:port] = v } + + if spawner_class.can_bind_to_custom_address? + opts.on("-a", "--address=ip", String, "Bind to IP address (default: #{OPTIONS[:address]})") { |v| OPTIONS[:address] = v } + end + + opts.on("-p", "--port=number", Integer, "Starting port number (default: #{OPTIONS[:port]})") { |v| OPTIONS[:port] = v } + opts.on("-i", "--instances=number", Integer, "Number of instances (default: #{OPTIONS[:instances]})") { |v| OPTIONS[:instances] = v } + opts.on("-r", "--repeat=seconds", Integer, "Repeat spawn attempts every n seconds (default: off)") { |v| OPTIONS[:repeat] = v } + opts.on("-e", "--environment=name", String, "test|development|production (default: #{OPTIONS[:environment]})") { |v| OPTIONS[:environment] = v } + 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 } + opts.on("-n", "--process=name", String, "default: #{OPTIONS[:process]}") { |v| OPTIONS[:process] = v } + opts.on("-s", "--spawner=path", String, "default: #{OPTIONS[:spawner]}") { |v| OPTIONS[:spawner] = v } + opts.on("-d", "--dispatcher=path", String, "default: #{OPTIONS[:dispatcher]}") { |dispatcher| OPTIONS[:dispatcher] = File.expand_path(dispatcher) } + + opts.separator "" + + opts.on("-h", "--help", "Show this help message.") { puts opts; exit } + + opts.parse! +end + +ENV["RAILS_ENV"] = OPTIONS[:environment] + +if OPTIONS[:repeat] + daemonize + trap("TERM") { exit } + spawner_class.record_pid + + loop do + spawner_class.spawn_all + sleep(OPTIONS[:repeat]) + end +else + spawner_class.spawn_all +end