diff options
author | Johan Sørensen <johan@johansorensen.com> | 2008-04-19 00:40:42 +0200 |
---|---|---|
committer | Johan Sørensen <johan@johansorensen.com> | 2008-04-19 00:40:42 +0200 |
commit | 37e1c41b4f693c787096e3fb7858f8b8c68c966b (patch) | |
tree | 955acfba731ad0823cc9777bc0afeeab9d3295b5 /script/git-daemon | |
parent | 68b75354f51f222d689ca22b623ff89bcefe7b6f (diff) | |
parent | 7d56991920c9e56a6991f41324b1d73835f3eb0f (diff) | |
download | gitorious-mainline-outdated-37e1c41b4f693c787096e3fb7858f8b8c68c966b.zip gitorious-mainline-outdated-37e1c41b4f693c787096e3fb7858f8b8c68c966b.tar.gz gitorious-mainline-outdated-37e1c41b4f693c787096e3fb7858f8b8c68c966b.tar.bz2 |
Merge branch 'ruby-git-daemon'
Diffstat (limited to 'script/git-daemon')
-rwxr-xr-x | script/git-daemon | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/script/git-daemon b/script/git-daemon new file mode 100755 index 0000000..0064342 --- /dev/null +++ b/script/git-daemon @@ -0,0 +1,209 @@ +#!/usr/bin/env ruby + +require 'rubygems' +require 'daemons' +require 'geoip' +require 'socket' +require "optparse" + +ENV["RAILS_ENV"] ||= "production" +require File.dirname(__FILE__)+'/../config/environment' + +Rails.configuration.log_level = :info # Disable debug +ActiveRecord::Base.allow_concurrency = true + +BASE_PATH = File.expand_path(GitoriousConfig['repository_base_path']) + +TIMEOUT = 30 +MAX_CHILDREN = 30 +$children_reaped = 0 +$children_active = 0 + +module Git + class Daemon + include Daemonize + + SERVICE_REGEXP = /(\w{4})(git\-upload\-pack)\s(.+)\x0host=([\w\.\-]+)/.freeze + + def initialize(options) + @options = options + @geoip = GeoIP.new(File.join(RAILS_ROOT, "data", "GeoIP.dat")) + end + + def start + if @options[:daemonize] + daemonize(@options[:logfile]) + File.open(@options[:pidfile], "w") do |f| + f.write(Process.pid) + end + end + @socket = TCPServer.new(@options[:host], @options[:port]) + log(Process.pid, "Listening on #{@options[:host]}:#{@options[:port]}...") + run + end + + def run + while session = @socket.accept + connections = $children_active - $children_reaped + if connections > MAX_CHILDREN + log(Process.pid, "too many active children #{connections}/#{MAX_CHILDREN}") + session.close + next + end + + run_service(session) + end + end + + def run_service(session) + $children_active += 1 + + line = session.recv(1000) + + if line =~ SERVICE_REGEXP + start_time = Time.now + code = $1 + service = $2 + base_path = $3 + host = $4 + + path = File.expand_path("#{BASE_PATH}/#{base_path}") + if !path.index(BASE_PATH) == 0 || !File.directory?(path) + log(Process.pid, "Invalid path: #{base_path}") + session.close + $children_active -= 1 + return + end + + if !File.exist?(File.join(path, "git-daemon-export-ok")) + session.close + $children_active -= 1 + return + end + + Dir.chdir(path) do + cmd = "git-upload-pack --strict --timeout=#{TIMEOUT} ." + + fork do + repository = nil + + begin + repository = ::Repository.find_by_path(path) + rescue => e + log(Process.pid, "AR error: #{e.class.name} #{e.message}:\n #{e.backtrace.join("\n ")}") + end + + pid = Process.pid + ip_family, port, name, ip = session.peeraddr + log(pid, "Connection from #{ip} for #{path.inspect}") + + $stdout.reopen(session) + $stdin.reopen(session) + session.close + + if repository + if ip_family == "AF_INET6" + repository.cloned_from(ip) + else + localization = @geoip.country(ip) + repository.cloned_from(ip, localization[3], localization[5]) + end + else + log(pid, "Cannot find repository: #{path}") + end + log(Process.pid, "Deferred in #{'%0.5f' % (Time.now - start_time)}s") + + exec(cmd) + # FIXME; we don't ever get here since we exec(), so reaped count may be incorrect + $children_reaped += 1 + exit! + end + end rescue Errno::EAGAIN + else + $stderr.puts "Invalid request: #{line}" + session.close + $children_active -= 1 + end + end + + def handle_stop(signal) + log(Process.pid, "Received #{signal}, exiting..") + exit 0 + end + + def handle_cld + loop do + pid = nil + begin + pid = Process.wait(-1, Process::WNOHANG) + rescue Errno::ECHILD + break + end + + if pid && $? + $children_reaped += 1 + log(pid, "Disconnected. (status=#{$?.exitstatus})") if pid > 0 + if $children_reaped == $children_active + $children_reaped = 0 + $children_active = 0 + end + + next + end + break + end + end + + def log(pid, msg) + $stderr.puts "#{Time.now.strftime("%Y-%m-%d %H:%M:%S")} [#{pid}] #{msg}" + end + + end +end + +options = { + :port => 9418, + :host => "0.0.0.0", + :logfile => File.join(RAILS_ROOT, "log", "git-daemon.log"), + :pidfile => File.join(RAILS_ROOT, "log", "git-daemon.pid"), + :daemonize => false + +} + +OptionParser.new do |opts| + opts.banner = "Usage: #{$0} start|stop [options]" + + opts.on("-p", "--port", Integer, "Port to listen on") do |o| + options[:port] = o + end + + opts.on("-h", "--host", "Host to listen on") do |o| + options[:host] = o + end + + opts.on("-l", "--logfile", "File to log to") do |o| + options[:logfile] = o + end + + opts.on("-l", "--logfile", "PID file to use (if daemonized)") do |o| + options[:pidfile] = o + end + + opts.on("-d", "--daemonize", "Daemonize or run in foreground (default)") do |o| + options[:daemonize] = o + end + + # opts.on("-e", "--environment", "RAILS_ENV to use") do |o| + # options[:port] = o + # end +end.parse! + +@git_daemon = Git::Daemon.new(options) + +trap("SIGKILL") { @git_daemon.handle_stop("SIGKILL") } +trap("TERM") { @git_daemon.handle_stop("TERM") } +trap("SIGINT") { @git_daemon.handle_stop("SIGINT") } +trap("CLD") { @git_daemon.handle_cld } + +@git_daemon.start + |