#!/usr/bin/env ruby require 'nitro/adapter/scgi' require 'drb' require 'set' include SCGI class SCGIFixed alias_method :env, :env_table end class NitroProcessor < SCGIProcessor def process_request(request, body, socket) return if socket.closed? cgi = SCGIFixed.new(request, body, socket) begin #-- # TODO: remove sync, Nitro *is* thread safe! #++ synchronize do #-- # FIXME: this is uggly, something better? #++ cgi.stdinput.rewind cgi.env["QUERY_STRING"] = (cgi.env["REQUEST_URI"] =~ /^[^?]+\?(.+)$/ and $1).to_s Nitro::Cgi.process($server, cgi, cgi.stdinput, cgi.stdoutput) Og.manager.put_store if defined?(Og) and Og.respond_to?(:manager) end rescue IOError @log.error("received IOError #$! when handling client. Your web server doesn't like me.") rescue Object => nitro_error @log.error("calling Dispatcher.dispatch", nitro_error) end end end class NitroController attr_reader :restart_requested def initialize(config) @config = config @restart_requested = false if not File.exist? @config STDERR.puts("No configuration file given and #{config} not found.") exit 1 end settings = YAML.load_file(@config) ENV["NITRO_ENV"] = settings[:env] require(settings[:run] || 'run') $server = Nitro::Server.new.start @nitro = NitroProcessor.new(settings) @log = SCGI::LogFactory.instance.create(settings[:logfile] || "log/scgi.log") @log.info("Running in #{@nitro.settings[:env]} mode on #{@nitro.settings[:host]}:#{@nitro.settings[:port]}") end def run secure_drb_methods(["status","reconfigure","shutdown","restart"]) DRb.start_service(@nitro.settings[:control_url], self) trap("INT") { @nitro.shutdown } @nitro.listen end def status(password) verify_password(password) @nitro.status end def reconfigure(password) verify_password(password) @log.info("Reconfiguring via #@config...") @nitro.configure(YAML.load_file(@config)) @log.info("Done.") return true end def shutdown(password, force = false) verify_password(password) @nitro.shutdown(force) end def restart(password, force = true) verify_password(password) @restart_requested = true @log.info("Restart requested, running #{__FILE__} #{ARGV[0]} as command.") @nitro.shutdown(force) end private def verify_password(password) image = @nitro.settings[:password] pc = password.crypt(image[0..2]) if pc != image raise "Authentication failed" end end # DRb only allows blacklist exclusion, which is super bad. What we # really need is "ALLOW_ONLY_METHODS". This method does that by listing # all the methods this object has, then removing the methods listed in # allowed_methods. What remains is a block list for everything else which # we configure in DRb::DRbServer::INSECURE_METHODS. def secure_drb_methods(allowed_methods) all_methods = self.methods bad_methods = all_methods - allowed_methods DRb::DRbServer::INSECURE_METHOD << bad_methods.to_a DRb::DRbServer::INSECURE_METHOD.flatten! end end config = ARGV[0] || ENV["SCGI_CONFIG"] || "conf/scgi.yaml" control = NitroController.new(config) control.run # check to see if a restart was requested rather than a shutdown, then start up the new one if control.restart_requested exec "ruby", __FILE__, config end # * Zed A. Shaw # * George Moschovitis # * Guillaume Pierronnet