summaryrefslogtreecommitdiffstats
path: root/script/git-daemon
diff options
context:
space:
mode:
authorJohan Sørensen <johan@johansorensen.com>2008-04-19 00:40:42 +0200
committerJohan Sørensen <johan@johansorensen.com>2008-04-19 00:40:42 +0200
commit37e1c41b4f693c787096e3fb7858f8b8c68c966b (patch)
tree955acfba731ad0823cc9777bc0afeeab9d3295b5 /script/git-daemon
parent68b75354f51f222d689ca22b623ff89bcefe7b6f (diff)
parent7d56991920c9e56a6991f41324b1d73835f3eb0f (diff)
downloadgitorious-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-xscript/git-daemon209
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
+