require 'rubygems' require 'win32/service' require 'mongrel' require 'mongrel/rails' require 'optparse' require 'yaml' # Avoid curious users from running this script from command line if ENV["HOMEDRIVE"]!=nil puts "mongrel_service is not designed to run form commandline," puts "please use mongrel_rails service:: commands to create a win32 service." exit end # We need to use OpenProcess and SetProcessAffinityMask on WinNT/2K/XP for # binding the process to each cpu. # Kernel32 Module Just for Win32 :D require 'dl/win32' module Kernel32 [ %w/OpenProcess LLL L/, %w/SetProcessAffinityMask LL L/, ].each do |fn| const_set fn[0].intern, Win32API.new('kernel32.dll', *fn) end PROCESS_ALL_ACCESS = 0x1f0fff module_function def set_affinity(pid, cpu) handle = OpenProcess.call(PROCESS_ALL_ACCESS, 0, pid) # CPU mask is a bit weird, hehehe :) # default mask for CPU 1 mask = 1 mask = %w{1 2 4 8 16 32 64 128}[cpu.to_i - 1] if cpu.to_i.between?(1, 8) SetProcessAffinityMask.call(handle, mask.to_i) end end # End Kernel32 Module # RailsService code here class RailsService < Win32::Daemon def initialize(settings) @settings = settings end def service_init @config = Mongrel::Rails::RailsConfigurator.new(@settings) do log "Starting Mongrel in #{defaults[:environment]} mode at #{defaults[:host]}:#{defaults[:port]}" listener do mime = {} if defaults[:mime_map] log "Loading additional MIME types from #{defaults[:mime_map]}" mime = load_mime_map(defaults[:mime_map], mime) end if defaults[:debug] log "Installing debugging prefixed filters. Look in log/mongrel_debug for the files." debug "/" end log "Starting Rails in environment #{defaults[:environment]} ..." uri "/", :handler => rails(:mime => mime) log "Rails loaded." #log "Loading any Rails specific GemPlugins" #load_plugins if defaults[:config_script] log "Loading #{defaults[:config_script]} external config script" run_config(defaults[:config_script]) end end end end def service_main @config.run @config.log "Mongrel available at #{@settings[:host]}:#{@settings[:port]}" while state == RUNNING sleep 1 end @config.stop end end # default options OPTIONS = { :environment => ENV['RAILS_ENV'] || "development", :port => 3000, :address => "0.0.0.0", :log_file => "log/mongrel.log", :pid_file => "log/mongrel.pid", :num_procs => 1024, :timeout => 0, :mime_map => nil, :cwd => Dir.pwd, :docroot => "public", :debug => false, :config_file => nil, :config_script => nil, :cpu => nil } ARGV.options do |opts| opts.on('-e', '--environment ENV', "Rails environment to run as") { |OPTIONS[:environment]| } opts.on('-p', '--port PORT', "Which port to bind to") { |OPTIONS[:port]| } opts.on('-a', '--address ADDR', "Address to bind to") { |OPTIONS[:address]| } opts.on('-l', '--log FILE', "Where to write log messages") { |OPTIONS[:log_file]| } opts.on('-P', '--pid FILE', "Where to write the PID") { |OPTIONS[:pid_file]| } opts.on('-n', '--num-procs INT', "Number of processors active before clients denied") { |OPTIONS[:num_procs]| } opts.on('-t', '--timeout TIME', "Timeout all requests after 100th seconds time") { |OPTIONS[:timeout]| } opts.on('-m', '--mime PATH', "A YAML file that lists additional MIME types") { |OPTIONS[:mime_map]| } opts.on('-c', '--chdir PATH', "Change to dir before starting (will be expanded)") { |OPTIONS[:cwd]| } opts.on('-r', '--root PATH', "Set the document root (default 'public')") { |OPTIONS[:docroot]| } opts.on('-B', '--debug', "Enable debugging mode") { |OPTIONS[:debug]| } opts.on('-C', '--config FILE', "Use a config file") { |OPTIONS[:config_file]| } opts.on('-S', '--script FILE', "Load the given file as an extra config script.") { |OPTIONS[:config_script]| } opts.on('-u', '--cpu CPU', "Bind the process to specific cpu, starting from 1.") { |OPTIONS[:cpu]| } opts.parse! end # We must bind to a specific cpu? if OPTIONS[:cpu] Kernel32.set_affinity(Process.pid, OPTIONS[:cpu]) end # expand path OPTIONS[:cwd] = File.expand_path(OPTIONS[:cwd]) # chdir to that path Dir.chdir(OPTIONS[:cwd]) # redirecting STDERR to a file so we could trace it. STDERR.reopen("log/service.log", "w") STDERR.sync = true #FIXME: I commented this because fails during load_plugins from configurator, we need to fix #GemPlugin::Manager.instance.load "mongrel" => GemPlugin::INCLUDE, "rails" => GemPlugin::EXCLUDE # initialize the daemon and pass control to win32/service svc = RailsService.new(OPTIONS) svc.mainloop