summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MANIFEST10
-rw-r--r--lib/drb.rb2
-rw-r--r--lib/drb/drb.rb854
-rw-r--r--lib/drb/eq.rb16
-rw-r--r--lib/drb/extserv.rb67
-rw-r--r--lib/drb/extservm.rb94
-rw-r--r--lib/drb/gw.rb60
-rw-r--r--lib/drb/invokemethod.rb39
-rw-r--r--lib/drb/observer.rb22
-rw-r--r--lib/drb/timeridconv.rb91
-rw-r--r--lib/drb/unix.rb98
11 files changed, 1353 insertions, 0 deletions
diff --git a/MANIFEST b/MANIFEST
index 8d41fb2519..36b99b2175 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -120,6 +120,16 @@ lib/date/format.rb
lib/date2.rb
lib/debug.rb
lib/delegate.rb
+lib/drb.rb
+lib/drb/drb.rb
+lib/drb/eq.rb
+lib/drb/extserv.rb
+lib/drb/extservm.rb
+lib/drb/gw.rb
+lib/drb/invokemethod.rb
+lib/drb/observer.rb
+lib/drb/timeridconv.rb
+lib/drb/unix.rb
lib/e2mmap.rb
lib/erb.rb
lib/eregex.rb
diff --git a/lib/drb.rb b/lib/drb.rb
new file mode 100644
index 0000000000..93cc811e14
--- /dev/null
+++ b/lib/drb.rb
@@ -0,0 +1,2 @@
+require 'drb/drb'
+
diff --git a/lib/drb/drb.rb b/lib/drb/drb.rb
new file mode 100644
index 0000000000..85cd3bc898
--- /dev/null
+++ b/lib/drb/drb.rb
@@ -0,0 +1,854 @@
+=begin
+= distributed Ruby --- dRuby 2.0.4
+ Copyright (c) 1999-2003 Masatoshi SEKI
+ You can redistribute it and/or modify it under the same terms as Ruby.
+=end
+
+require 'socket'
+require 'thread'
+require 'fcntl'
+
+module DRb
+ class DRbError < RuntimeError; end
+ class DRbConnError < DRbError; end
+
+ class DRbIdConv
+ def to_obj(ref)
+ ObjectSpace._id2ref(ref)
+ end
+
+ def to_id(obj)
+ obj.nil? ? nil : obj.__id__
+ end
+ end
+
+ module DRbUndumped
+ def _dump(dummy)
+ raise TypeError, 'can\'t dump'
+ end
+ end
+
+ class DRbServerNotFound < DRbError; end
+ class DRbBadURI < DRbError; end
+ class DRbBadScheme < DRbError; end
+
+ class DRbUnknownError < DRbError
+ def initialize(unknown)
+ @unknown = unknown
+ super(unknown.name)
+ end
+ attr_reader :unknown
+
+ def self._load(s)
+ Marshal::load(s)
+ end
+
+ def _dump(lv)
+ Marshal::dump(@unknown)
+ end
+ end
+
+ class DRbUnknown
+ def initialize(err, buf)
+ case err
+ when /uninitialized constant (\S+)/
+ @name = $1
+ when /undefined class\/module (\S+)/
+ @name = $1
+ else
+ @name = nil
+ end
+ @buf = buf
+ end
+ attr_reader :name, :buf
+
+ def self._load(s)
+ begin
+ Marshal::load(s)
+ rescue NameError, ArgumentError
+ DRbUnknown.new($!, s)
+ end
+ end
+
+ def _dump(lv)
+ @buf
+ end
+
+ def reload
+ self.class._load(@buf)
+ end
+
+ def exception
+ DRbUnknownError.new(self)
+ end
+ end
+
+ class DRbMessage
+ def initialize(config)
+ @load_limit = config[:load_limit]
+ @argc_limit = config[:argc_limit]
+ end
+
+ def dump(obj)
+ obj = DRbObject.new(obj) if obj.kind_of? DRbUndumped
+ begin
+ str = Marshal::dump(obj)
+ rescue
+ str = Marshal::dump(DRbObject.new(obj))
+ end
+ [str.size].pack('N') + str
+ end
+
+ def load(soc)
+ sz = soc.read(4) # sizeof (N)
+ raise(DRbConnError, 'connection closed') if sz.nil?
+ raise(DRbConnError, 'premature header') if sz.size < 4
+ sz = sz.unpack('N')[0]
+ raise(DRbConnError, "too large packet #{sz}") if @load_limit < sz
+ str = soc.read(sz)
+ raise(DRbConnError, 'connection closed') if sz.nil?
+ raise(DRbConnError, 'premature marshal format(can\'t read)') if str.size < sz
+ begin
+ Marshal::load(str)
+ rescue NameError, ArgumentError
+ DRbUnknown.new($!, str)
+ end
+ end
+
+ def send_request(stream, ref, msg_id, arg, b)
+ ary = []
+ ary.push(dump(ref.__drbref))
+ ary.push(dump(msg_id.id2name))
+ ary.push(dump(arg.length))
+ arg.each do |e|
+ ary.push(dump(e))
+ end
+ ary.push(dump(b))
+ stream.write(ary.join(''))
+ end
+
+ def recv_request(stream)
+ ref = load(stream)
+ ro = DRb.to_obj(ref)
+ msg = load(stream)
+ argc = load(stream)
+ raise ArgumentError, 'too many arguments' if @argc_limit < argc
+ argv = Array.new(argc, nil)
+ argc.times do |n|
+ argv[n] = load(stream)
+ end
+ block = load(stream)
+ return ro, msg, argv, block
+ end
+
+ def send_reply(stream, succ, result)
+ stream.write(dump(succ) + dump(result))
+ end
+
+ def recv_reply(stream)
+ succ = load(stream)
+ result = load(stream)
+ [succ, result]
+ end
+ end
+
+ module DRbProtocol
+ module_function
+ def add_protocol(prot)
+ @protocol.push(prot)
+ end
+
+ module_function
+ def open(uri, config, first=true)
+ @protocol.each do |prot|
+ begin
+ return prot.open(uri, config)
+ rescue DRbBadScheme
+ rescue DRbConnError
+ raise($!)
+ rescue
+ raise(DRbConnError, "#{uri} - #{$!.inspect}")
+ end
+ end
+ if first && (config[:auto_load] != false)
+ auto_load(uri, config)
+ return open(uri, config, false)
+ end
+ raise DRbBadURI, 'can\'t parse uri:' + uri
+ end
+
+ module_function
+ def open_server(uri, config, first=true)
+ @protocol.each do |prot|
+ begin
+ return prot.open_server(uri, config)
+ rescue DRbBadScheme
+ end
+ end
+ if first && (config[:auto_load] != false)
+ auto_load(uri, config)
+ return open_server(uri, config, false)
+ end
+ raise DRbBadURI, 'can\'t parse uri:' + uri
+ end
+
+ module_function
+ def uri_option(uri, config, first=true)
+ @protocol.each do |prot|
+ begin
+ uri, opt = prot.uri_option(uri, config)
+ # opt = nil if opt == ''
+ return uri, opt
+ rescue DRbBadScheme
+ end
+ end
+ if first && (config[:auto_load] != false)
+ auto_load(uri, config)
+ return uri_option(uri, config, false)
+ end
+ raise DRbBadURI, 'can\'t parse uri:' + uri
+ end
+
+ module_function
+ def auto_load(uri, config)
+ if uri =~ /^drb([a-z0-9]+):/
+ require("drb/#{$1}") rescue nil
+ end
+ end
+ end
+
+ class DRbTCPSocket
+ private
+ def self.parse_uri(uri)
+ if uri =~ /^druby:\/\/(.*?):(\d+)(\?(.*))?$/
+ host = $1
+ port = $2.to_i
+ option = $4
+ [host, port, option]
+ else
+ raise(DRbBadScheme, uri) unless uri =~ /^druby:/
+ raise(DRbBadURI, 'can\'t parse uri:' + uri)
+ end
+ end
+
+ public
+ def self.open(uri, config)
+ host, port, option = parse_uri(uri)
+ host.untaint
+ port.untaint
+ soc = TCPSocket.open(host, port)
+ self.new(uri, soc, config)
+ end
+
+ def self.open_server(uri, config)
+ uri = 'druby://:0' unless uri
+ host, port, opt = parse_uri(uri)
+ if host.size == 0
+ soc = TCPServer.open(port)
+ host = Socket.gethostname
+ else
+ soc = TCPServer.open(host, port)
+ end
+ port = soc.addr[1] if port == 0
+ uri = "druby://#{host}:#{port}"
+ self.new(uri, soc, config)
+ end
+
+ def self.uri_option(uri, config)
+ host, port, option = parse_uri(uri)
+ return "druby://#{host}:#{port}", option
+ end
+
+ def initialize(uri, soc, config={})
+ @uri = uri
+ @socket = soc
+ @config = config
+ @acl = config[:tcp_acl]
+ @msg = DRbMessage.new(config)
+ set_sockopt(@socket)
+ end
+ attr_reader :uri
+
+ def peeraddr
+ @socket.peeraddr
+ end
+
+ def stream; @socket; end
+
+ def send_request(ref, msg_id, arg, b)
+ @msg.send_request(stream, ref, msg_id, arg, b)
+ end
+
+ def recv_request
+ @msg.recv_request(stream)
+ end
+
+ def send_reply(succ, result)
+ @msg.send_reply(stream, succ, result)
+ end
+
+ def recv_reply
+ @msg.recv_reply(stream)
+ end
+
+ public
+ def close
+ if @socket
+ @socket.close
+ @socket = nil
+ end
+ end
+
+ def accept
+ while true
+ s = @socket.accept
+ break if (@acl ? @acl.allow_socket?(s) : true)
+ s.close
+ end
+ self.class.new(nil, s, @config)
+ end
+
+ def alive?
+ return false unless @socket
+ if IO.select([@socket], nil, nil, 0)
+ close
+ return false
+ end
+ true
+ end
+
+ def set_sockopt(soc)
+ soc.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
+ soc.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK) if defined? Fcntl::O_NONBLOCK
+ soc.fcntl(Fcntl::F_SETFL, Fcntl::FD_CLOEXEC) if defined? Fcntl::FD_CLOEXEC
+ end
+ end
+
+ module DRbProtocol
+ @protocol = [DRbTCPSocket] # default
+ end
+
+ class DRbURIOption
+ def initialize(option)
+ @option = option.to_s
+ end
+ attr :option
+ def to_s; @option; end
+
+ def ==(other)
+ return false unless DRbURIOption === other
+ @option == other.option
+ end
+
+ def hash
+ @option.hash
+ end
+
+ alias eql? ==
+ end
+
+ class DRbObject
+ def self._load(s)
+ uri, ref = Marshal.load(s)
+ if DRb.here?(uri)
+ return DRb.to_obj(ref)
+ end
+
+ it = self.new(nil)
+ it.reinit(uri, ref)
+ it
+ end
+
+ def self.new_with_uri(uri)
+ self.new(nil, uri)
+ end
+
+ def _dump(lv)
+ Marshal.dump([@uri, @ref])
+ end
+
+ def initialize(obj, uri=nil)
+ @uri = nil
+ @ref = nil
+ if obj.nil?
+ return if uri.nil?
+ @uri, option = DRbProtocol.uri_option(uri, DRb.config)
+ @ref = DRbURIOption.new(option) unless option.nil?
+ else
+ @uri = uri ? uri : (DRb.uri rescue nil)
+ @ref = obj ? DRb.to_id(obj) : nil
+ end
+ end
+
+ def reinit(uri, ref)
+ @uri = uri
+ @ref = ref
+ end
+
+ def __drburi
+ @uri
+ end
+
+ def __drbref
+ @ref
+ end
+
+ undef :to_s
+ undef :to_a
+ undef :respond_to?
+
+ def method_missing(msg_id, *a, &b)
+ if DRb.here?(@uri)
+ obj = DRb.to_obj(@ref)
+ DRb.current_server.check_insecure_method(obj, msg_id)
+ return obj.__send__(msg_id, *a, &b)
+ end
+
+ succ, result = DRbConn.open(@uri) do |conn|
+ conn.send_message(self, msg_id, a, b)
+ end
+ return result if succ
+ unless DRbUnknown === result
+ prefix = "(#{@uri}) "
+ bt = []
+ result.backtrace.each do |x|
+ break if /`__send__'$/ =~ x
+ if /^\(druby:\/\// =~ x
+ bt.push(x)
+ else
+ bt.push(prefix + x)
+ end
+ end
+ raise result, result.message, bt + caller
+ else
+ raise result
+ end
+ end
+ end
+
+ class DRbConn
+ POOL_SIZE = 16
+ @mutex = Mutex.new
+ @pool = []
+
+ def self.open(remote_uri)
+ begin
+ conn = nil
+
+ @mutex.synchronize do
+ #FIXME
+ new_pool = []
+ @pool.each do |c|
+ if conn.nil? and c.uri == remote_uri
+ conn = c if c.alive?
+ else
+ new_pool.push c
+ end
+ end
+ @pool = new_pool
+ end
+
+ conn = self.new(remote_uri) unless conn
+ succ, result = yield(conn)
+ return succ, result
+
+ ensure
+ @mutex.synchronize do
+ if @pool.size > POOL_SIZE or ! succ
+ conn.close if conn
+ else
+ @pool.unshift(conn)
+ end
+ end
+ end
+ end
+
+ def initialize(remote_uri)
+ @uri = remote_uri
+ @protocol = DRbProtocol.open(remote_uri, DRb.config)
+ end
+ attr_reader :uri
+
+ def send_message(ref, msg_id, arg, block)
+ @protocol.send_request(ref, msg_id, arg, block)
+ @protocol.recv_reply
+ end
+
+ def close
+ @protocol.close
+ @protocol = nil
+ end
+
+ def alive?
+ @protocol.alive?
+ end
+ end
+
+ class DRbServer
+ @@acl = nil
+ @@idconv = DRbIdConv.new
+ @@secondary_server = nil
+ @@argc_limit = 256
+ @@load_limit = 256 * 102400
+ @@verbose = false
+
+ def self.default_argc_limit(argc)
+ @@argc_limit = argc
+ end
+
+ def self.default_load_limit(sz)
+ @@load_limit = sz
+ end
+
+ def self.default_acl(acl)
+ @@acl = acl
+ end
+
+ def self.default_id_conv(idconv)
+ @@idconv = idconv
+ end
+
+ def self.verbose=(on)
+ @@verbose = on
+ end
+
+ def self.verbose
+ @@verbose
+ end
+
+ def self.make_config(hash={})
+ default_config = {
+ :idconv => @@idconv,
+ :verbose => @@verbose,
+ :tcp_acl => @@acl,
+ :load_limit => @@load_limit,
+ :argc_limit => @@argc_limit
+ }
+ default_config.update(hash)
+ end
+
+ def initialize(uri=nil, front=nil, config_or_acl=nil)
+ if Hash === config_or_acl
+ config = config_or_acl.dup
+ else
+ acl = config_or_acl || @@acl
+ config = {
+ :tcp_acl => acl
+ }
+ end
+
+ @config = self.class.make_config(config)
+
+ @protocol = DRbProtocol.open_server(uri, @config)
+ @uri = @protocol.uri
+
+ @front = front
+ @idconv = @config[:idconv]
+
+ @grp = ThreadGroup.new
+ @thread = run
+
+ Thread.exclusive do
+ DRb.primary_server = self unless DRb.primary_server
+ end
+ end
+ attr_reader :uri, :thread, :front
+ attr_reader :config
+
+ def verbose=(v); @config[:verbose]=v; end
+ def verbose; @config[:verbose]; end
+
+ def alive?
+ @thread.alive?
+ end
+
+ def stop_service
+ @thread.kill
+ end
+
+ def to_obj(ref)
+ return front if ref.nil?
+ return front[ref.to_s] if DRbURIOption === ref
+ @idconv.to_obj(ref)
+ end
+
+ def to_id(obj)
+ return nil if obj.__id__ == front.__id__
+ @idconv.to_id(obj)
+ end
+
+ private
+ def kill_sub_thread
+ Thread.new do
+ grp = ThreadGroup.new
+ grp.add(Thread.current)
+ list = @grp.list
+ while list.size > 0
+ list.each do |th|
+ th.kill if th.alive?
+ end
+ list = @grp.list
+ end
+ end
+ end
+
+ def run
+ Thread.start do
+ begin
+ while true
+ main_loop
+ end
+ ensure
+ @protocol.close if @protocol
+ kill_sub_thread
+ end
+ end
+ end
+
+ INSECURE_METHOD = [
+ :__send__
+ ]
+ def insecure_method?(msg_id)
+ INSECURE_METHOD.include?(msg_id)
+ end
+
+ def any_to_s(obj)
+ obj.to_s rescue sprintf("#<%s:0x%lx>", obj.class, obj.__id__)
+ end
+
+ def check_insecure_method(obj, msg_id)
+ return true if Proc === obj && msg_id == :__drb_yield
+ raise(ArgumentError, "#{any_to_s(msg_id)} is not a symbol") unless Symbol == msg_id.class
+ raise(SecurityError, "insecure method `#{msg_id}'") if insecure_method?(msg_id)
+ unless obj.respond_to?(msg_id)
+ desc = any_to_s(obj)
+ if desc.nil? || desc[0] == '#'
+ desc << ":#{obj.class}"
+ end
+
+ if obj.private_methods.include?(msg_id.to_s)
+ raise NameError, "private method `#{msg_id}' called for #{desc}"
+ else
+ raise NameError, "undefined method `#{msg_id}' called for #{desc}"
+ end
+ end
+ true
+ end
+ public :check_insecure_method
+
+ class InvokeMethod
+ def initialize(drb_server, client)
+ @drb_server = drb_server
+ @client = client
+ end
+
+ def perform
+ @result = nil
+ @succ = false
+ setup_message
+ if @block
+ @result = perform_with_block
+ else
+ @result = perform_without_block
+ end
+ @succ = true
+ return @succ, @result
+ rescue StandardError, ScriptError, Interrupt
+ @result = $!
+ return @succ, @result
+ end
+
+ private
+ def init_with_client
+ obj, msg, argv, block = @client.recv_request
+ @obj = obj
+ @msg_id = msg.intern
+ @argv = argv
+ @block = block
+ end
+
+ def check_insecure_method
+ @drb_server.check_insecure_method(@obj, @msg_id)
+ end
+
+ def setup_message
+ init_with_client
+ check_insecure_method
+ end
+
+ def perform_without_block
+ if Proc === @obj && @msg_id == :__drb_yield
+ if @argv.size == 1
+ ary = @argv
+ else
+ ary = [@argv]
+ end
+ ary.collect(&@obj)[0]
+ else
+ @obj.__send__(@msg_id, *@argv)
+ end
+ end
+
+ def rescue_local_jump(err)
+ case err.message
+ when /^retry/ # retry from proc-closure
+ return :retry
+ when /^break/ # break from proc-closure
+ return rescue_break(err)
+ else
+ return :unknown
+ end
+ end
+
+ end
+
+ if RUBY_VERSION >= '1.8'
+ require 'drb/invokemethod'
+ class InvokeMethod
+ include InvokeMethod18Mixin
+ end
+ else
+ module InvokeMethod16Mixin
+ def block_yield(x)
+ if x.class == Array
+ block_value = @block.__drb_yield(*x)
+ else
+ block_value = @block.__drb_yield(x)
+ end
+ end
+
+ def rescue_break(err)
+ return :break
+ end
+
+ def perform_with_block
+ @obj.__send__(@msg_id, *@argv) do |x|
+ jump_error = nil
+ begin
+ block_value = block_yield(x)
+ rescue LocalJumpError
+ jump_error = $!
+ end
+ if jump_error
+ reason ,= rescue_local_jump(jump_error)
+ case reason
+ when :retry
+ retry
+ when :break
+ break
+ else
+ raise jump_error
+ end
+ end
+ block_value
+ end
+ end
+ end
+
+ class InvokeMethod
+ include InvokeMethod16Mixin
+ end
+ end
+
+ def main_loop
+ Thread.start(@protocol.accept) do |client|
+ @grp.add Thread.current
+ Thread.current['DRb'] = { 'client' => client ,
+ 'server' => self }
+ loop do
+ begin
+ succ = false
+ invoke_method = InvokeMethod.new(self, client)
+ succ, result = invoke_method.perform
+ if !succ && verbose
+ p result
+ result.backtrace.each do |x|
+ puts x
+ end
+ end
+ client.send_reply(succ, result) rescue nil
+ ensure
+ unless succ
+ client.close
+ return
+ end
+ end
+ end
+ end
+ end
+ end
+
+ @primary_server = nil
+
+ def start_service(uri=nil, front=nil, config=nil)
+ @primary_server = DRbServer.new(uri, front, config)
+ end
+ module_function :start_service
+
+ attr_accessor :primary_server
+ module_function :primary_server=, :primary_server
+
+ def current_server
+ drb = Thread.current['DRb']
+ server = (drb && drb['server']) ? drb['server'] : @primary_server
+ raise DRbServerNotFound unless server
+ return server
+ end
+ module_function :current_server
+
+ def stop_service
+ @primary_server.stop_service if @primary_server
+ @primary_server = nil
+ end
+ module_function :stop_service
+
+ def uri
+ current_server.uri
+ end
+ module_function :uri
+
+ def here?(uri)
+ (current_server.uri rescue nil) == uri
+ end
+ module_function :here?
+
+ def config
+ current_server.config
+ rescue
+ DRbServer.make_config
+ end
+ module_function :config
+
+ def front
+ current_server.front
+ end
+ module_function :front
+
+ def to_obj(ref)
+ current_server.to_obj(ref)
+ end
+ def to_id(obj)
+ current_server.to_id(obj)
+ end
+ module_function :to_id
+ module_function :to_obj
+
+ def thread
+ @primary_server ? @primary_server.thread : nil
+ end
+ module_function :thread
+
+ def install_id_conv(idconv)
+ DRbServer.default_id_conv(idconv)
+ end
+ module_function :install_id_conv
+
+ def install_acl(acl)
+ DRbServer.default_acl(acl)
+ end
+ module_function :install_acl
+end
+
+DRbObject = DRb::DRbObject
+DRbUndumped = DRb::DRbUndumped
+DRbIdConv = DRb::DRbIdConv
diff --git a/lib/drb/eq.rb b/lib/drb/eq.rb
new file mode 100644
index 0000000000..e24512d6a7
--- /dev/null
+++ b/lib/drb/eq.rb
@@ -0,0 +1,16 @@
+require 'drb/drb'
+
+module DRb
+ class DRbObject
+ def ==(other)
+ return false unless DRbObject === other
+ (@ref == other.__drbref) && (@uri == other.__drburi)
+ end
+
+ def hash
+ [@uri, @ref].hash
+ end
+
+ alias eql? ==
+ end
+end
diff --git a/lib/drb/extserv.rb b/lib/drb/extserv.rb
new file mode 100644
index 0000000000..f05bae5264
--- /dev/null
+++ b/lib/drb/extserv.rb
@@ -0,0 +1,67 @@
+=begin
+ external service
+ Copyright (c) 2000,2002 Masatoshi SEKI
+=end
+
+require 'drb/drb'
+
+module DRb
+ class ExtServ
+ include DRbUndumped
+
+ def initialize(there, name, server=nil)
+ @server = server || DRb::primary_server
+ @name = name
+ ro = DRbObject.new(nil, there)
+ @invoker = ro.regist(name, DRbObject.new(self, @server.uri))
+ end
+ attr_reader :server
+
+ def front
+ DRbObject.new(nil, @server.uri)
+ end
+
+ def stop_service
+ @invoker.unregist(@name)
+ server = @server
+ @server = nil
+ Thread.new do
+ sleep 1
+ server.stop_service
+ end
+ true
+ end
+
+ def alive?
+ @server ? @server.alive? : false
+ end
+ end
+end
+
+if __FILE__ == $0
+ class Foo
+ include DRbUndumped
+
+ def initialize(str)
+ @str = str
+ end
+
+ def hello(it)
+ "#{it}: #{self}"
+ end
+
+ def to_s
+ @str
+ end
+ end
+
+ cmd = ARGV.shift
+ case cmd
+ when 'itest1', 'itest2'
+ front = Foo.new(cmd)
+ manager = DRb::DRbServer.new(nil, front)
+ es = DRb::ExtService.new(ARGV.shift, ARGV.shift, manager)
+ es.server.thread.join
+ end
+end
+
diff --git a/lib/drb/extservm.rb b/lib/drb/extservm.rb
new file mode 100644
index 0000000000..50009dbae2
--- /dev/null
+++ b/lib/drb/extservm.rb
@@ -0,0 +1,94 @@
+=begin
+ external service manager
+ Copyright (c) 2000 Masatoshi SEKI
+=end
+
+require 'drb/drb'
+require 'thread'
+
+module DRb
+ class ExtServManager
+ include DRbUndumped
+
+ @@command = {}
+
+ def self.command
+ @@command
+ end
+
+ def self.command=(cmd)
+ @@command = cmd
+ end
+
+ def initialize
+ @servers = {}
+ @waiting = []
+ @queue = Queue.new
+ @thread = invoke_thread
+ end
+ attr_reader :server
+
+ def service(name)
+ while true
+ server = nil
+ Thread.exclusive do
+ server = @servers[name] if @servers[name]
+ end
+ return server if server && server.alive?
+ invoke_service(name)
+ end
+ end
+
+ def regist(name, ro)
+ ary = nil
+ Thread.exclusive do
+ @servers[name] = ro
+ ary = @waiting
+ @waiting = []
+ end
+ ary.each do |th|
+ begin
+ th.run
+ rescue ThreadError
+ end
+ end
+ self
+ end
+
+ def unregist(name)
+ Thread.exclusive do
+ @servers.delete(name)
+ end
+ end
+
+ private
+ def invoke_thread
+ Thread.new do
+ while true
+ name = @queue.pop
+ invoke_service_command(name, @@command[name])
+ end
+ end
+ end
+
+ def invoke_service(name)
+ Thread.critical = true
+ @waiting.push Thread.current
+ @queue.push name
+ Thread.stop
+ end
+
+ def invoke_service_command(name, command)
+ raise "invalid command. name: #{name}" unless command
+ Thread.exclusive do
+ return if @servers.include?(name)
+ @servers[name] = false
+ end
+ if RUBY_PLATFORM =~ /mswin32/
+ system("cmd /c start /b #{command} #{DRb.uri} #{name}")
+ else
+ system("#{command} #{DRb.uri} #{name} &")
+ end
+ end
+ end
+end
diff --git a/lib/drb/gw.rb b/lib/drb/gw.rb
new file mode 100644
index 0000000000..012a2b0a11
--- /dev/null
+++ b/lib/drb/gw.rb
@@ -0,0 +1,60 @@
+require 'drb/drb'
+require 'monitor'
+
+module DRb
+ class GWIdConv < DRbIdConv
+ def to_obj(ref)
+ if Array === ref && ref[0] == :DRbObject
+ it = DRbObject.new(nil)
+ it.reinit(ref[1], ref[2])
+ return it
+ end
+ super(ref)
+ end
+ end
+
+ class GW
+ include MonitorMixin
+ def initialize
+ super()
+ @hash = {}
+ end
+
+ def [](key)
+ synchronize do
+ @hash[key]
+ end
+ end
+
+ def []=(key, v)
+ synchronize do
+ @hash[key] = v
+ end
+ end
+ end
+
+ class DRbObject
+ def self._load(s)
+ uri, ref = Marshal.load(s)
+ if DRb.uri == uri
+ return ref ? DRb.to_obj(ref) : DRb.front
+ end
+
+ it = self.new(nil)
+ it.reinit(DRb.uri, [:DRbObject, uri, ref])
+ it
+ end
+
+ def _dump(lv)
+ if DRb.uri == @uri
+ if Array === @ref && @ref[0] == :DRbObject
+ Marshal.dump([@ref[1], @ref[2]])
+ else
+ Marshal.dump([@uri, @ref]) # ??
+ end
+ else
+ Marshal.dump([DRb.uri, [:DRbObject, @uri, @ref]])
+ end
+ end
+ end
+end
diff --git a/lib/drb/invokemethod.rb b/lib/drb/invokemethod.rb
new file mode 100644
index 0000000000..f8ce0ad09b
--- /dev/null
+++ b/lib/drb/invokemethod.rb
@@ -0,0 +1,39 @@
+
+# for ruby-1.8.0
+
+module DRb
+ class DRbServer
+ module InvokeMethod18Mixin
+ def block_yield(x)
+ block_value = @block.call(*x)
+ end
+
+ def rescue_break(err)
+ return :break, err.exit_value
+ end
+
+ def perform_with_block
+ @obj.__send__(@msg_id, *@argv) do |*x|
+ jump_error = nil
+ begin
+ block_value = block_yield(x)
+ rescue LocalJumpError
+ jump_error = $!
+ end
+ if jump_error
+ reason, jump_value = rescue_local_jump(jump_error)
+ case reason
+ when :retry
+ retry
+ when :break
+ break(jump_value)
+ else
+ raise jump_error
+ end
+ end
+ block_value
+ end
+ end
+ end
+ end
+end
diff --git a/lib/drb/observer.rb b/lib/drb/observer.rb
new file mode 100644
index 0000000000..e7f1668c52
--- /dev/null
+++ b/lib/drb/observer.rb
@@ -0,0 +1,22 @@
+require 'observer'
+
+module DRb
+ module DRbObservable
+ include Observable
+
+ def notify_observers(*arg)
+ if defined? @observer_state and @observer_state
+ if defined? @observer_peers
+ for i in @observer_peers.dup
+ begin
+ i.update(*arg)
+ rescue
+ delete_observer(i)
+ end
+ end
+ end
+ @observer_state = false
+ end
+ end
+ end
+end
diff --git a/lib/drb/timeridconv.rb b/lib/drb/timeridconv.rb
new file mode 100644
index 0000000000..bb2c48d528
--- /dev/null
+++ b/lib/drb/timeridconv.rb
@@ -0,0 +1,91 @@
+require 'drb/drb'
+require 'monitor'
+
+module DRb
+ class TimerIdConv < DRbIdConv
+ class TimerHolder2
+ include MonitorMixin
+
+ class InvalidIndexError < RuntimeError; end
+
+ def initialize(timeout=600)
+ super()
+ @sentinel = Object.new
+ @gc = {}
+ @curr = {}
+ @renew = {}
+ @timeout = timeout
+ @keeper = keeper
+ end
+
+ def add(obj)
+ synchronize do
+ key = obj.__id__
+ @curr[key] = obj
+ return key
+ end
+ end
+
+ def fetch(key, dv=@sentinel)
+ synchronize do
+ obj = peek(key)
+ if obj == @sentinel
+ return dv unless dv == @sentinel
+ raise InvalidIndexError
+ end
+ @renew[key] = obj # KeepIt
+ return obj
+ end
+ end
+
+ def include?(key)
+ synchronize do
+ obj = peek(key)
+ return false if obj == @sentinel
+ true
+ end
+ end
+
+ def peek(key)
+ synchronize do
+ return @curr.fetch(key, @renew.fetch(key, @gc.fetch(key, @sentinel)))
+ end
+ end
+
+ private
+ def alternate
+ synchronize do
+ @gc = @curr # GCed
+ @curr = @renew
+ @renew = {}
+ end
+ end
+
+ def keeper
+ Thread.new do
+ loop do
+ size = alternate
+ sleep(@timeout)
+ end
+ end
+ end
+ end
+
+ def initialize(timeout=600)
+ @holder = TimerHolder2.new(timeout)
+ end
+
+ def to_obj(ref)
+ return super if ref.nil?
+ @holder.fetch(ref)
+ rescue TimerHolder2::InvalidIndexError
+ raise "invalid reference"
+ end
+
+ def to_id(obj)
+ return @holder.add(obj)
+ end
+ end
+end
+
+# DRb.install_id_conv(TimerIdConv.new)
diff --git a/lib/drb/unix.rb b/lib/drb/unix.rb
new file mode 100644
index 0000000000..f9a2f75acd
--- /dev/null
+++ b/lib/drb/unix.rb
@@ -0,0 +1,98 @@
+require 'socket'
+require 'drb/drb'
+
+module DRb
+
+ class DRbUNIXSocket < DRbTCPSocket
+ def self.parse_uri(uri)
+ if /^drbunix:(.*?)(\?(.*))?$/ =~ uri
+ filename = $1
+ option = $3
+ [filename, option]
+ else
+ raise(DRbBadScheme, uri) unless uri =~ /^drbunix:/
+ raise(DRbBadURI, 'can\'t parse uri:' + uri)
+ end
+ end
+
+ def self.open(uri, config)
+ filename, option = parse_uri(uri)
+ filename.untaint
+ soc = UNIXSocket.open(filename)
+ self.new(uri, soc, config)
+ end
+
+ def self.open_server(uri, config)
+ filename, option = parse_uri(uri)
+ if filename.size == 0
+ soc = temp_server
+ uri = 'drbunix:' + soc.path
+ else
+ soc = UNIXServer.open(filename)
+ end
+ mode = config[:UNIXFileMode]
+ File.chmod(mode, filename) if mode
+
+ self.new(uri, soc, config, true)
+ end
+
+ def self.uri_option(uri, config)
+ filename, option = parse_uri(uri)
+ return "drbunix:#{filename}", option
+ end
+
+ def initialize(uri, soc, config={}, server_mode = false)
+ super(uri, soc, config)
+ set_sockopt(@socket)
+ @server_mode = server_mode
+ @acl = nil
+ end
+
+ # import from tempfile.rb
+ Max_try = 10
+ private
+ def self.temp_server
+ tmpdir = ENV['TMPDIR'] || ENV['TMP'] || ENV['TEMP'] || '/tmp'
+ tmpdir = '/tmp' if $SAFE > 0 and tmpdir.tainted?
+ n = 0
+ while true
+ begin
+ tmpname = sprintf('%s/druby%d.%d', tmpdir, $$, n)
+ lock = tmpname + '.lock'
+ unless File.exist?(tmpname) or File.exist?(lock)
+ Dir.mkdir(lock)
+ break
+ end
+ rescue
+ raise "cannot generate tempfile `%s'" % tmpname if n >= Max_try
+ #sleep(1)
+ end
+ n += 1
+ end
+ soc = UNIXServer.new(tmpname)
+ Dir.rmdir(lock)
+ soc
+ end
+
+ public
+ def close
+ return unless @socket
+ path = @socket.path
+ @socket.close
+ File.unlink(path) if @server_mode
+ @socket = nil
+ end
+
+ def accept
+ s = @socket.accept
+ self.class.new(nil, s, @config)
+ end
+
+ def set_sockopt(soc)
+ soc.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK) if defined? Fcntl::O_NONBLOCK
+ soc.fcntl(Fcntl::F_SETFL, Fcntl::FD_CLOEXEC) if defined? Fcntl::FD_CLOEXEC
+ end
+ end
+
+ DRbProtocol.add_protocol(DRbUNIXSocket)
+end