2006-07-06 13:21:42 -04:00
require 'active_support'
2005-07-24 03:57:10 -04:00
require 'optparse'
2006-03-18 14:21:54 -05:00
require 'socket'
2006-04-27 20:00:50 -04:00
require 'fileutils'
2005-07-24 03:57:10 -04:00
2006-04-02 23:03:33 -04:00
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
2006-04-27 20:00:50 -04:00
class Spawner
2006-05-02 18:39:48 -04:00
def self . record_pid ( name = " #{ OPTIONS [ :process ] } .spawner " , id = Process . pid )
2006-04-27 20:00:50 -04:00
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
2006-10-26 12:59:35 -04:00
print " Checking if something is already running on #{ OPTIONS [ :address ] } : #{ port } ... "
2006-04-27 20:00:50 -04:00
begin
2006-10-26 12:59:35 -04:00
srv = TCPServer . new ( OPTIONS [ :address ] , port )
2006-04-27 20:00:50 -04:00
srv . close
srv = nil
2006-07-06 13:21:42 -04:00
puts " NO "
2006-10-26 12:59:35 -04:00
puts " Starting dispatcher on port: #{ OPTIONS [ :address ] } : #{ port } "
2006-04-27 20:00:50 -04:00
FileUtils . mkdir_p ( OPTIONS [ :pids ] )
spawn ( port )
rescue
2006-07-06 13:21:42 -04:00
puts " YES "
2006-04-27 20:00:50 -04:00
end
end
2006-03-18 14:21:54 -05:00
end
2005-07-24 03:57:10 -04:00
end
2006-04-27 20:00:50 -04:00
class FcgiSpawner < Spawner
def self . spawn ( port )
2006-10-26 12:59:35 -04:00
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 `
2006-04-27 20:00:50 -04:00
end
2006-02-26 15:25:24 -05:00
end
2006-07-06 13:21:42 -04:00
class MongrelSpawner < Spawner
def self . spawn ( port )
2006-12-03 22:50:57 -05:00
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 "
2007-09-30 23:15:51 -04:00
# Add prefix functionality to spawner's call to mongrel_rails
2008-07-16 08:00:36 -04:00
# Digging through mongrel's project subversion server, the earliest
2007-09-30 23:15:51 -04:00
# Tag that has prefix implemented in the bin/mongrel_rails file
2008-07-16 08:00:36 -04:00
# is 0.3.15 which also happens to be the earliest tag listed.
2007-09-30 23:15:51 -04:00
# 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
2006-10-26 12:59:35 -04:00
system ( cmd )
end
2006-12-03 22:50:57 -05:00
2006-10-26 12:59:35 -04:00
def self . can_bind_to_custom_address?
true
2006-07-06 13:21:42 -04:00
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 "
2007-03-06 04:22:07 -05:00
elsif RUBY_PLATFORM !~ / (:?mswin|mingw) / && ! silence_stderr { ` spawn-fcgi -version ` } . blank? && defined? ( FCGI )
2006-07-06 13:21:42 -04:00
" 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
2006-04-27 20:00:50 -04:00
2005-07-24 03:57:10 -04:00
OPTIONS = {
:environment = > " production " ,
:spawner = > '/usr/bin/env spawn-fcgi' ,
2007-04-16 17:55:59 -04:00
: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 ) ,
2006-05-02 18:18:20 -04:00
:process = > " dispatch " ,
2005-07-24 03:57:10 -04:00
:port = > 8000 ,
2006-10-26 12:59:35 -04:00
:address = > '0.0.0.0' ,
2006-02-26 15:25:24 -05:00
:instances = > 3 ,
2007-09-30 23:15:51 -04:00
:repeat = > nil ,
:prefix = > nil
2005-07-24 03:57:10 -04:00
}
ARGV . options do | opts |
2006-07-06 13:21:42 -04:00
opts . banner = " Usage: spawner [platform] [options] "
2005-07-24 03:57:10 -04:00
opts . separator " "
opts . on <<-EOF
Description :
2006-10-26 12:59:35 -04:00
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
2005-07-24 03:57:10 -04:00
EOF
opts . on ( " Options: " )
2008-09-09 05:58:34 -04:00
opts . on ( " -p " , " --port=number " , Integer , " Starting port number (default: #{ OPTIONS [ :port ] } ) " ) { | v | OPTIONS [ :port ] = v }
2006-12-03 22:50:57 -05:00
2006-10-26 12:59:35 -04:00
if spawner_class . can_bind_to_custom_address?
2008-09-09 05:58:34 -04:00
opts . on ( " -a " , " --address=ip " , String , " Bind to IP address (default: #{ OPTIONS [ :address ] } ) " ) { | v | OPTIONS [ :address ] = v }
2006-10-26 12:59:35 -04:00
end
2006-12-03 22:50:57 -05:00
2006-06-28 16:53:00 -04:00
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 }
2007-09-30 23:15:51 -04:00
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 }
2006-06-28 16:53:00 -04:00
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 }
2006-04-02 23:34:53 -04:00
opts . on ( " -d " , " --dispatcher=path " , String , " default: #{ OPTIONS [ :dispatcher ] } " ) { | dispatcher | OPTIONS [ :dispatcher ] = File . expand_path ( dispatcher ) }
2005-07-24 03:57:10 -04:00
opts . separator " "
opts . on ( " -h " , " --help " , " Show this help message. " ) { puts opts ; exit }
opts . parse!
end
ENV [ " RAILS_ENV " ] = OPTIONS [ :environment ]
2006-02-26 15:25:24 -05:00
if OPTIONS [ :repeat ]
2006-04-02 23:03:33 -04:00
daemonize
trap ( " TERM " ) { exit }
2006-07-06 13:21:42 -04:00
spawner_class . record_pid
2006-04-02 23:03:33 -04:00
2006-02-26 15:25:24 -05:00
loop do
2006-07-06 13:21:42 -04:00
spawner_class . spawn_all
2006-04-02 23:03:33 -04:00
sleep ( OPTIONS [ :repeat ] )
2006-02-26 15:25:24 -05:00
end
else
2006-07-06 13:21:42 -04:00
spawner_class . spawn_all
2007-03-06 04:22:07 -05:00
end