0.1.0
This commit is contained in:
commit
54a825211e
58
Rakefile
Normal file
58
Rakefile
Normal file
|
@ -0,0 +1,58 @@
|
|||
require 'rubygems'
|
||||
require 'rake'
|
||||
|
||||
begin
|
||||
require 'jeweler'
|
||||
Jeweler::Tasks.new do |gem|
|
||||
gem.name = "svdrpd"
|
||||
gem.summary = %Q{Multiplexer for SVDRP}
|
||||
gem.description = %Q{Allowes to more than one connection to communicate with VDR}
|
||||
gem.email = "Denis.Knauf@gmail.com"
|
||||
gem.homepage = "http://github.com/DenisKnauf/svdrpd"
|
||||
gem.authors = ["Denis Knauf"]
|
||||
gem.files = ["README.md", "VERSION", "bin/**/*", "lib/**/*.rb", "test/**/*.rb"]
|
||||
gem.require_paths = ["bin"]
|
||||
gem.add_dependency 'select'
|
||||
end
|
||||
Jeweler::GemcutterTasks.new
|
||||
rescue LoadError
|
||||
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
||||
end
|
||||
|
||||
require 'rake/testtask'
|
||||
Rake::TestTask.new(:test) do |test|
|
||||
test.libs << 'lib' << 'test' << 'ext'
|
||||
test.pattern = 'test/**/*_test.rb'
|
||||
test.verbose = true
|
||||
end
|
||||
|
||||
begin
|
||||
require 'rcov/rcovtask'
|
||||
Rcov::RcovTask.new do |test|
|
||||
test.libs << 'test'
|
||||
test.pattern = 'test/**/*_test.rb'
|
||||
test.verbose = true
|
||||
end
|
||||
rescue LoadError
|
||||
task :rcov do
|
||||
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
||||
end
|
||||
end
|
||||
|
||||
task :test => :check_dependencies
|
||||
|
||||
task :default => :test
|
||||
|
||||
require 'rake/rdoctask'
|
||||
Rake::RDocTask.new do |rdoc|
|
||||
if File.exist?('VERSION')
|
||||
version = File.read('VERSION')
|
||||
else
|
||||
version = ""
|
||||
end
|
||||
|
||||
rdoc.rdoc_dir = 'rdoc'
|
||||
rdoc.title = "sbdb #{version}"
|
||||
rdoc.rdoc_files.include('README*')
|
||||
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||
end
|
263
bin/svdrpd
Executable file
263
bin/svdrpd
Executable file
|
@ -0,0 +1,263 @@
|
|||
#!/usr/bin/ruby
|
||||
|
||||
require 'socket'
|
||||
require 'syslog'
|
||||
require 'select'
|
||||
|
||||
module Kernel
|
||||
def debug line
|
||||
STDOUT.puts "#{caller[0]}: #{line}"
|
||||
end
|
||||
end
|
||||
|
||||
class SVDRPC <Select::Socket
|
||||
def initialize opts
|
||||
opts.update( :delimiter => /\r?\n/)
|
||||
@vdr = opts[ :vdr] || raise( ArgumentError, "need VDR")
|
||||
super opts
|
||||
@sock.puts "220 #{ENV["HOSTNAME"]} SVDRP svdrpd 0.0.1; #{Time.now}"
|
||||
end
|
||||
|
||||
def event_line line
|
||||
if /^\s*quit/i.match line
|
||||
self.quit "quit"
|
||||
else
|
||||
@vdr.push self, line
|
||||
end
|
||||
end
|
||||
|
||||
def event_write *args
|
||||
self.close if super( *args).empty? && @quit
|
||||
end
|
||||
|
||||
def quit reason = "unknown reason"
|
||||
@sock.close_read
|
||||
@select.del @sock, :read
|
||||
@quit = true
|
||||
puts "221 #{ENV["HOSTNAME"]} closing connection (#{reason})"
|
||||
rescue IOError
|
||||
end
|
||||
end
|
||||
|
||||
class SVDRPD <Select::Server
|
||||
def initialize opts
|
||||
@vdr = opts[ :vdr] || raise( ArgumentError, "need VDR")
|
||||
super opts
|
||||
end
|
||||
|
||||
def event_new_client sock
|
||||
{ :vdr => @vdr, :clientclass => SVDRPC }
|
||||
end
|
||||
|
||||
def quit reason = "unknown reason"
|
||||
self.close
|
||||
@clients.each do |i|
|
||||
i.quit reason
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class VDR
|
||||
Request = Struct.new :client, :str
|
||||
attr_reader :sock, :serv, :port, :select, :firstline
|
||||
|
||||
def initialize host = 'localhost', port = 2001, select = Select.new
|
||||
@host, @port, @select = host, port, select
|
||||
@quit, @queue = false, []
|
||||
end
|
||||
|
||||
def closed?
|
||||
@sock.nil? || @sock.closed?
|
||||
end
|
||||
|
||||
def disconnect
|
||||
@sock.close unless @sock.nil?
|
||||
end
|
||||
alias :close :disconnect
|
||||
|
||||
def connect
|
||||
@sock = VDR::Socket.new :sock => TCPSocket.new( serv, port), :select => @select, :parent => self
|
||||
@answer = FirstLine.new
|
||||
rescue Errno::ECONNREFUSED
|
||||
retry
|
||||
end
|
||||
|
||||
def event_answer line
|
||||
# Kernel.debug "@answer=#{@answer}"
|
||||
l = /^(\d\d\d)([ -])(.*?)[\n\r]*$/.match line
|
||||
if l.nil?
|
||||
# Kernel.debug "i don't understand this line: #{line}"
|
||||
return
|
||||
elsif l[ 1].to_i == 221
|
||||
else
|
||||
@answer.client.puts l[ 1..-1].to_s
|
||||
self.next true if l[ 2] == ' '
|
||||
end
|
||||
end
|
||||
|
||||
def event_client_closed client
|
||||
@queue.unshift @answer unless @answer.nil?
|
||||
@firstline = @sock = nil
|
||||
self.next unless @queue.empty?
|
||||
end
|
||||
|
||||
def next clear_answer = false
|
||||
# Kernel.debug "@queue = [#{@queue.collect{|i|i.to_s}.join ", "}]; @answer = #{@answer.inspect}"
|
||||
@answer = nil if clear_answer
|
||||
return self.close if @quit
|
||||
return @answer if @answer
|
||||
begin
|
||||
@answer = @queue.shift
|
||||
end while !@answer.nil? && @answer.client.closed?
|
||||
if @answer.nil?
|
||||
elsif self.closed?
|
||||
@queue.unshift @answer
|
||||
self.connect
|
||||
else
|
||||
@sock.puts @answer.str
|
||||
end
|
||||
@answer
|
||||
end
|
||||
|
||||
def push client, str
|
||||
r = Request.new client, str.strip
|
||||
raise "Not a valid String: #{r.str.inject}" if !r.str.kind_of?( String) || r.str.empty?
|
||||
@queue.unshift r
|
||||
self.next
|
||||
end
|
||||
|
||||
def quit
|
||||
unless self.closed?
|
||||
q = Class.new
|
||||
class <<q
|
||||
def client; self; end
|
||||
def closed?; false; end
|
||||
def str; "quit"; end
|
||||
end
|
||||
@queue.unshift q
|
||||
self.next
|
||||
end
|
||||
@quit = true
|
||||
self.closed?
|
||||
end
|
||||
end
|
||||
|
||||
class VDR::Socket <Select::Socket
|
||||
def initialize opts
|
||||
opts.update( :delimiter => /\r?\n/)
|
||||
super opts
|
||||
end
|
||||
|
||||
def event_line line
|
||||
@parent.event_answer line
|
||||
end
|
||||
|
||||
def quit
|
||||
@sock.puts "quit"
|
||||
end
|
||||
end
|
||||
|
||||
class VDR::FirstLine
|
||||
attr_reader :line, :client, :str
|
||||
|
||||
def initialize
|
||||
@client = self
|
||||
@str = nil
|
||||
end
|
||||
|
||||
def write line
|
||||
@line = line
|
||||
end
|
||||
|
||||
alias :print :write
|
||||
alias :puts :write
|
||||
alias :to_s :line
|
||||
end
|
||||
|
||||
###############################################################################
|
||||
# debug #######################################################################
|
||||
###############################################################################
|
||||
if %W{-D --debug}.include? ARGV[0]
|
||||
ARGV.shift
|
||||
$DEBUG = true
|
||||
end
|
||||
$DEBUG = true if ENV['DEBUG']
|
||||
|
||||
if $DEBUG
|
||||
def debug_func c, f
|
||||
ff = case f
|
||||
when /^(.*)\?$/ then "#{$1}_f"
|
||||
when /^(.*)\!$/ then "#{$1}_a"
|
||||
when "<<" then "_s"
|
||||
when "+" then "_p"
|
||||
when "-" then "_m"
|
||||
when "@+" then "_P"
|
||||
when "@-" then "_M"
|
||||
else "#{f}_n"
|
||||
end
|
||||
wf = "__wrapped_#{c.object_id.to_s.sub /^-/, "x"}_#{ff}__".intern
|
||||
return "#{c}##{wf} already exists" if c.instance_methods.include? wf
|
||||
pre = "\#{\"%x\"%self.hash.abs}:#{c}##{f}"
|
||||
c.class_eval <<-EOF
|
||||
alias :#{wf} :#{f}
|
||||
def #{f} *args, &e
|
||||
ret = if e
|
||||
STDERR.puts "==>#{pre} \#{args.collect {|i| i.inspect }.join ", "}, &\#{e.inspect}"
|
||||
#{wf} *args, &e
|
||||
else
|
||||
STDERR.puts "==>#{pre} \#{args.collect {|i| i.inspect }.join ", "}"
|
||||
#{wf} *args
|
||||
end
|
||||
#STDERR.puts "<==#{pre}"
|
||||
ret
|
||||
rescue
|
||||
STDERR.puts "<==#{pre} EXCEPTION: \#{$!.inspect}"
|
||||
Kernel.raise
|
||||
end
|
||||
EOF
|
||||
end
|
||||
|
||||
def debug_class c, fs
|
||||
c.instance_methods.grep fs do |f|
|
||||
debug_func c, f
|
||||
end
|
||||
end
|
||||
|
||||
debug_class Select, /_set|_del$/
|
||||
debug_class Select::Socket, /^event_.*|write|print|init|close$/
|
||||
debug_class Select::Server, /^event_.*|close|init$/
|
||||
debug_class VDR, /^event_.*|next|push|connect|close|disconnect$/
|
||||
debug_class VDR::Socket, /^event_.*|close|init$/
|
||||
debug_class SVDRPD, /^event_.*|init$/
|
||||
debug_class SVDRPC, /^event_.*|init$/
|
||||
debug_class VDR::FirstLine, /^write|print|puts$/
|
||||
end
|
||||
|
||||
###############################################################################
|
||||
# main ########################################################################
|
||||
###############################################################################
|
||||
opts = {
|
||||
:vdraddr => ARGV[0] || 'localhost',
|
||||
:vdrport => ARGV[1] || 2002,
|
||||
:servaddr => ARGV[2] || 'localhost',
|
||||
:servport => ARGV[3] || 2001
|
||||
}
|
||||
|
||||
Syslog.open 'svdrpd', Syslog::LOG_NDELAY | Syslog::LOG_PERROR,
|
||||
Syslog::LOG_DAEMON
|
||||
|
||||
begin
|
||||
$select = Select.new
|
||||
$select.timeout 5*60
|
||||
|
||||
$vdr = VDR.new opts[ :vdraddr], opts[ :vdrport], $select
|
||||
$serv = SVDRPD.new :vdr => $vdr, :sock => TCPServer.new( opts[ :servaddr], opts[ :servport]), :select => $select
|
||||
$select.exit_on_empty = true
|
||||
|
||||
$serv.run
|
||||
rescue Object
|
||||
Syslog.err "#{$!} (#{$!.class}) -- #{$!.backtrace.join ' -- '}"
|
||||
$serv.quit "server shuting down"
|
||||
$vdr.quit
|
||||
retry
|
||||
end
|
44
svdrpd.gemspec
Normal file
44
svdrpd.gemspec
Normal file
|
@ -0,0 +1,44 @@
|
|||
# Generated by jeweler
|
||||
# DO NOT EDIT THIS FILE DIRECTLY
|
||||
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.name = %q{svdrpd}
|
||||
s.version = "0.1.0"
|
||||
|
||||
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
||||
s.authors = ["Denis Knauf"]
|
||||
s.date = %q{2010-02-26}
|
||||
s.default_executable = %q{svdrpd}
|
||||
s.description = %q{Allowes to more than one connection to communicate with VDR}
|
||||
s.email = %q{Denis.Knauf@gmail.com}
|
||||
s.executables = ["svdrpd"]
|
||||
s.extra_rdoc_files = [
|
||||
"README.md"
|
||||
]
|
||||
s.files = [
|
||||
"README.md",
|
||||
"VERSION",
|
||||
"bin/svdrpd"
|
||||
]
|
||||
s.homepage = %q{http://github.com/DenisKnauf/svdrpd}
|
||||
s.rdoc_options = ["--charset=UTF-8"]
|
||||
s.require_paths = ["bin"]
|
||||
s.rubygems_version = %q{1.3.5}
|
||||
s.summary = %q{Multiplexer for SVDRP}
|
||||
|
||||
if s.respond_to? :specification_version then
|
||||
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
||||
s.specification_version = 3
|
||||
|
||||
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
||||
s.add_runtime_dependency(%q<select>, [">= 0"])
|
||||
else
|
||||
s.add_dependency(%q<select>, [">= 0"])
|
||||
end
|
||||
else
|
||||
s.add_dependency(%q<select>, [">= 0"])
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in a new issue