Warning: file_get_contents(https://raw.githubusercontent.com/Den1xxx/Filemanager/master/languages/ru.json): failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 88

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 215

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 216

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 217

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 218

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 219

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 220
PK!{X## version.rbnu[module DRb VERSION = "2.0.5" end PK!D[timeridconv.rbnu[# frozen_string_literal: false require_relative 'drb' require 'monitor' module DRb # Timer id conversion keeps objects alive for a certain amount of time after # their last access. The default time period is 600 seconds and can be # changed upon initialization. # # To use TimerIdConv: # # DRb.install_id_conv TimerIdConv.new 60 # one minute class TimerIdConv < DRbIdConv class TimerHolder2 # :nodoc: include MonitorMixin class InvalidIndexError < RuntimeError; end def initialize(keeping=600) super() @sentinel = Object.new @gc = {} @renew = {} @keeping = keeping @expires = nil end def add(obj) synchronize do rotate key = obj.__id__ @renew[key] = obj invoke_keeper return key end end def fetch(key) synchronize do rotate obj = peek(key) raise InvalidIndexError if obj == @sentinel @renew[key] = obj # KeepIt return obj end end private def peek(key) return @renew.fetch(key) { @gc.fetch(key, @sentinel) } end def invoke_keeper return if @expires @expires = Time.now + @keeping on_gc end def on_gc return unless Thread.main.alive? return if @expires.nil? Thread.new { rotate } if @expires < Time.now ObjectSpace.define_finalizer(Object.new) {on_gc} end def rotate synchronize do if @expires &.< Time.now @gc = @renew # GCed @renew = {} @expires = @gc.empty? ? nil : Time.now + @keeping end end end end # Creates a new TimerIdConv which will hold objects for +keeping+ seconds. def initialize(keeping=600) @holder = TimerHolder2.new(keeping) end def to_obj(ref) # :nodoc: return super if ref.nil? @holder.fetch(ref) rescue TimerHolder2::InvalidIndexError raise "invalid reference" end def to_id(obj) # :nodoc: return @holder.add(obj) end end end # DRb.install_id_conv(TimerIdConv.new) PK!4eq.rbnu[# frozen_string_literal: false module DRb class DRbObject # :nodoc: def ==(other) return false unless DRbObject === other (@ref == other.__drbref) && (@uri == other.__drburi) end def hash [@uri, @ref].hash end alias eql? == end end PK!Ew| extservm.rbnu[# frozen_string_literal: false =begin external service manager Copyright (c) 2000 Masatoshi SEKI =end require_relative 'drb' require 'monitor' module DRb class ExtServManager include DRbUndumped include MonitorMixin @@command = {} def self.command @@command end def self.command=(cmd) @@command = cmd end def initialize super() @cond = new_cond @servers = {} @waiting = [] @queue = Thread::Queue.new @thread = invoke_thread @uri = nil end attr_accessor :uri def service(name) synchronize do while true server = @servers[name] return server if server && server.alive? # server may be `false' invoke_service(name) @cond.wait end end end def regist(name, ro) synchronize do @servers[name] = ro @cond.signal end self end def unregist(name) synchronize do @servers.delete(name) end end private def invoke_thread Thread.new do while name = @queue.pop invoke_service_command(name, @@command[name]) end end end def invoke_service(name) @queue.push(name) end def invoke_service_command(name, command) raise "invalid command. name: #{name}" unless command synchronize do return if @servers.include?(name) @servers[name] = false end uri = @uri || DRb.uri if command.respond_to? :to_ary command = command.to_ary + [uri, name] pid = spawn(*command) else pid = spawn("#{command} #{uri} #{name}") end th = Process.detach(pid) th[:drb_service] = name th end end end PK!+  gw.rbnu[# frozen_string_literal: false require_relative 'drb' require 'monitor' module DRb # Gateway id conversion forms a gateway between different DRb protocols or # networks. # # The gateway needs to install this id conversion and create servers for # each of the protocols or networks it will be a gateway between. It then # needs to create a server that attaches to each of these networks. For # example: # # require 'drb/drb' # require 'drb/unix' # require 'drb/gw' # # DRb.install_id_conv DRb::GWIdConv.new # gw = DRb::GW.new # s1 = DRb::DRbServer.new 'drbunix:/path/to/gateway', gw # s2 = DRb::DRbServer.new 'druby://example:10000', gw # # s1.thread.join # s2.thread.join # # Each client must register services with the gateway, for example: # # DRb.start_service 'drbunix:', nil # an anonymous server # gw = DRbObject.new nil, 'drbunix:/path/to/gateway' # gw[:unix] = some_service # DRb.thread.join class GWIdConv < DRbIdConv def to_obj(ref) # :nodoc: if Array === ref && ref[0] == :DRbObject return DRbObject.new_with(ref[1], ref[2]) end super(ref) end end # The GW provides a synchronized store for participants in the gateway to # communicate. class GW include MonitorMixin # Creates a new GW def initialize super() @hash = {} end # Retrieves +key+ from the GW def [](key) synchronize do @hash[key] end end # Stores value +v+ at +key+ in the GW def []=(key, v) synchronize do @hash[key] = v end end end class DRbObject # :nodoc: def self._load(s) uri, ref = Marshal.load(s) if DRb.uri == uri return ref ? DRb.to_obj(ref) : DRb.front end self.new_with(DRb.uri, [:DRbObject, uri, ref]) 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 =begin DRb.install_id_conv(DRb::GWIdConv.new) front = DRb::GW.new s1 = DRb::DRbServer.new('drbunix:/tmp/gw_b_a', front) s2 = DRb::DRbServer.new('drbunix:/tmp/gw_b_c', front) s1.thread.join s2.thread.join =end =begin # foo.rb require 'drb/drb' class Foo include DRbUndumped def initialize(name, peer=nil) @name = name @peer = peer end def ping(obj) puts "#{@name}: ping: #{obj.inspect}" @peer.ping(self) if @peer end end =end =begin # gw_a.rb require 'drb/unix' require 'foo' obj = Foo.new('a') DRb.start_service("drbunix:/tmp/gw_a", obj) robj = DRbObject.new_with_uri('drbunix:/tmp/gw_b_a') robj[:a] = obj DRb.thread.join =end =begin # gw_c.rb require 'drb/unix' require 'foo' foo = Foo.new('c', nil) DRb.start_service("drbunix:/tmp/gw_c", nil) robj = DRbObject.new_with_uri("drbunix:/tmp/gw_b_c") puts "c->b" a = robj[:a] sleep 2 a.ping(foo) DRb.thread.join =end PK!ы>qqacl.rbnu[# frozen_string_literal: false # Copyright (c) 2000,2002,2003 Masatoshi SEKI # # acl.rb is copyrighted free software by Masatoshi SEKI. # You can redistribute it and/or modify it under the same terms as Ruby. require 'ipaddr' ## # Simple Access Control Lists. # # Access control lists are composed of "allow" and "deny" halves to control # access. Use "all" or "*" to match any address. To match a specific address # use any address or address mask that IPAddr can understand. # # Example: # # list = %w[ # deny all # allow 192.168.1.1 # allow ::ffff:192.168.1.2 # allow 192.168.1.3 # ] # # # From Socket#peeraddr, see also ACL#allow_socket? # addr = ["AF_INET", 10, "lc630", "192.168.1.3"] # # acl = ACL.new # p acl.allow_addr?(addr) # => true # # acl = ACL.new(list, ACL::DENY_ALLOW) # p acl.allow_addr?(addr) # => true class ACL ## # The current version of ACL VERSION=["2.0.0"] ## # An entry in an ACL class ACLEntry ## # Creates a new entry using +str+. # # +str+ may be "*" or "all" to match any address, an IP address string # to match a specific address, an IP address mask per IPAddr, or one # containing "*" to match part of an IPv4 address. # # IPAddr::InvalidPrefixError may be raised when an IP network # address with an invalid netmask/prefix is given. def initialize(str) if str == '*' or str == 'all' @pat = [:all] elsif str.include?('*') @pat = [:name, dot_pat(str)] else begin @pat = [:ip, IPAddr.new(str)] rescue IPAddr::InvalidPrefixError # In this case, `str` shouldn't be a host name pattern # because it contains a slash. raise rescue ArgumentError @pat = [:name, dot_pat(str)] end end end private ## # Creates a regular expression to match IPv4 addresses def dot_pat_str(str) list = str.split('.').collect { |s| (s == '*') ? '.+' : s } list.join("\\.") end private ## # Creates a Regexp to match an address. def dot_pat(str) /\A#{dot_pat_str(str)}\z/ end public ## # Matches +addr+ against this entry. def match(addr) case @pat[0] when :all true when :ip begin ipaddr = IPAddr.new(addr[3]) ipaddr = ipaddr.ipv4_mapped if @pat[1].ipv6? && ipaddr.ipv4? rescue ArgumentError return false end (@pat[1].include?(ipaddr)) ? true : false when :name (@pat[1] =~ addr[2]) ? true : false else false end end end ## # A list of ACLEntry objects. Used to implement the allow and deny halves # of an ACL class ACLList ## # Creates an empty ACLList def initialize @list = [] end public ## # Matches +addr+ against each ACLEntry in this list. def match(addr) @list.each do |e| return true if e.match(addr) end false end public ## # Adds +str+ as an ACLEntry in this list def add(str) @list.push(ACLEntry.new(str)) end end ## # Default to deny DENY_ALLOW = 0 ## # Default to allow ALLOW_DENY = 1 ## # Creates a new ACL from +list+ with an evaluation +order+ of DENY_ALLOW or # ALLOW_DENY. # # An ACL +list+ is an Array of "allow" or "deny" and an address or address # mask or "all" or "*" to match any address: # # %w[ # deny all # allow 192.0.2.2 # allow 192.0.2.128/26 # ] def initialize(list=nil, order = DENY_ALLOW) @order = order @deny = ACLList.new @allow = ACLList.new install_list(list) if list end public ## # Allow connections from Socket +soc+? def allow_socket?(soc) allow_addr?(soc.peeraddr) end public ## # Allow connections from addrinfo +addr+? It must be formatted like # Socket#peeraddr: # # ["AF_INET", 10, "lc630", "192.0.2.1"] def allow_addr?(addr) case @order when DENY_ALLOW return true if @allow.match(addr) return false if @deny.match(addr) return true when ALLOW_DENY return false if @deny.match(addr) return true if @allow.match(addr) return false else false end end public ## # Adds +list+ of ACL entries to this ACL. def install_list(list) i = 0 while i < list.size permission, domain = list.slice(i,2) case permission.downcase when 'allow' @allow.add(domain) when 'deny' @deny.add(domain) else raise "Invalid ACL entry #{list}" end i += 2 end end end PK!,}} weakidconv.rbnu[# frozen_string_literal: false require_relative 'drb' require 'monitor' module DRb # To use WeakIdConv: # # DRb.start_service(nil, nil, {:idconv => DRb::WeakIdConv.new}) class WeakIdConv < DRbIdConv class WeakSet include MonitorMixin def initialize super() @immutable = {} @map = ObjectSpace::WeakMap.new end def add(obj) synchronize do begin @map[obj] = self rescue ArgumentError @immutable[obj.__id__] = obj end return obj.__id__ end end def fetch(ref) synchronize do @immutable.fetch(ref) { @map.each { |key, _| return key if key.__id__ == ref } raise RangeError.new("invalid reference") } end end end def initialize() super() @weak_set = WeakSet.new end def to_obj(ref) # :nodoc: return super if ref.nil? @weak_set.fetch(ref) end def to_id(obj) # :nodoc: return @weak_set.add(obj) end end end # DRb.install_id_conv(WeakIdConv.new) PK!@%.%.ssl.rbnu[# frozen_string_literal: false require 'socket' require 'openssl' require_relative 'drb' require 'singleton' module DRb # The protocol for DRb over an SSL socket # # The URI for a DRb socket over SSL is: # drbssl://:?. The option is optional class DRbSSLSocket < DRbTCPSocket # SSLConfig handles the needed SSL information for establishing a # DRbSSLSocket connection, including generating the X509 / RSA pair. # # An instance of this config can be passed to DRbSSLSocket.new, # DRbSSLSocket.open and DRbSSLSocket.open_server # # See DRb::DRbSSLSocket::SSLConfig.new for more details class SSLConfig # Default values for a SSLConfig instance. # # See DRb::DRbSSLSocket::SSLConfig.new for more details DEFAULT = { :SSLCertificate => nil, :SSLPrivateKey => nil, :SSLClientCA => nil, :SSLCACertificatePath => nil, :SSLCACertificateFile => nil, :SSLTmpDhCallback => nil, :SSLVerifyMode => ::OpenSSL::SSL::VERIFY_NONE, :SSLVerifyDepth => nil, :SSLVerifyCallback => nil, # custom verification :SSLCertificateStore => nil, # Must specify if you use auto generated certificate. :SSLCertName => nil, # e.g. [["CN","fqdn.example.com"]] :SSLCertComment => "Generated by Ruby/OpenSSL" } # Create a new DRb::DRbSSLSocket::SSLConfig instance # # The DRb::DRbSSLSocket will take either a +config+ Hash or an instance # of SSLConfig, and will setup the certificate for its session for the # configuration. If want it to generate a generic certificate, the bare # minimum is to provide the :SSLCertName # # === Config options # # From +config+ Hash: # # :SSLCertificate :: # An instance of OpenSSL::X509::Certificate. If this is not provided, # then a generic X509 is generated, with a correspond :SSLPrivateKey # # :SSLPrivateKey :: # A private key instance, like OpenSSL::PKey::RSA. This key must be # the key that signed the :SSLCertificate # # :SSLClientCA :: # An OpenSSL::X509::Certificate, or Array of certificates that will # used as ClientCAs in the SSL Context # # :SSLCACertificatePath :: # A path to the directory of CA certificates. The certificates must # be in PEM format. # # :SSLCACertificateFile :: # A path to a CA certificate file, in PEM format. # # :SSLTmpDhCallback :: # A DH callback. See OpenSSL::SSL::SSLContext.tmp_dh_callback # # :SSLVerifyMode :: # This is the SSL verification mode. See OpenSSL::SSL::VERIFY_* for # available modes. The default is OpenSSL::SSL::VERIFY_NONE # # :SSLVerifyDepth :: # Number of CA certificates to walk, when verifying a certificate # chain. # # :SSLVerifyCallback :: # A callback to be used for additional verification. See # OpenSSL::SSL::SSLContext.verify_callback # # :SSLCertificateStore :: # A OpenSSL::X509::Store used for verification of certificates # # :SSLCertName :: # Issuer name for the certificate. This is required when generating # the certificate (if :SSLCertificate and :SSLPrivateKey were not # given). The value of this is to be an Array of pairs: # # [["C", "Raleigh"], ["ST","North Carolina"], # ["CN","fqdn.example.com"]] # # See also OpenSSL::X509::Name # # :SSLCertComment :: # A comment to be used for generating the certificate. The default is # "Generated by Ruby/OpenSSL" # # # === Example # # These values can be added after the fact, like a Hash. # # require 'drb/ssl' # c = DRb::DRbSSLSocket::SSLConfig.new {} # c[:SSLCertificate] = # OpenSSL::X509::Certificate.new(File.read('mycert.crt')) # c[:SSLPrivateKey] = OpenSSL::PKey::RSA.new(File.read('mycert.key')) # c[:SSLVerifyMode] = OpenSSL::SSL::VERIFY_PEER # c[:SSLCACertificatePath] = "/etc/ssl/certs/" # c.setup_certificate # # or # # require 'drb/ssl' # c = DRb::DRbSSLSocket::SSLConfig.new({ # :SSLCertName => [["CN" => DRb::DRbSSLSocket.getservername]] # }) # c.setup_certificate # def initialize(config) @config = config @cert = config[:SSLCertificate] @pkey = config[:SSLPrivateKey] @ssl_ctx = nil end # A convenience method to access the values like a Hash def [](key); @config[key] || DEFAULT[key] end # Connect to IO +tcp+, with context of the current certificate # configuration def connect(tcp) ssl = ::OpenSSL::SSL::SSLSocket.new(tcp, @ssl_ctx) ssl.sync = true ssl.connect ssl end # Accept connection to IO +tcp+, with context of the current certificate # configuration def accept(tcp) ssl = OpenSSL::SSL::SSLSocket.new(tcp, @ssl_ctx) ssl.sync = true ssl.accept ssl end # Ensures that :SSLCertificate and :SSLPrivateKey have been provided # or that a new certificate is generated with the other parameters # provided. def setup_certificate if @cert && @pkey return end rsa = OpenSSL::PKey::RSA.new(2048){|p, n| next unless self[:verbose] case p when 0; $stderr.putc "." # BN_generate_prime when 1; $stderr.putc "+" # BN_generate_prime when 2; $stderr.putc "*" # searching good prime, # n = #of try, # but also data from BN_generate_prime when 3; $stderr.putc "\n" # found good prime, n==0 - p, n==1 - q, # but also data from BN_generate_prime else; $stderr.putc "*" # BN_generate_prime end } cert = OpenSSL::X509::Certificate.new cert.version = 3 cert.serial = 0 name = OpenSSL::X509::Name.new(self[:SSLCertName]) cert.subject = name cert.issuer = name cert.not_before = Time.now cert.not_after = Time.now + (365*24*60*60) cert.public_key = rsa.public_key ef = OpenSSL::X509::ExtensionFactory.new(nil,cert) cert.extensions = [ ef.create_extension("basicConstraints","CA:FALSE"), ef.create_extension("subjectKeyIdentifier", "hash") ] ef.issuer_certificate = cert cert.add_extension(ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always")) if comment = self[:SSLCertComment] cert.add_extension(ef.create_extension("nsComment", comment)) end cert.sign(rsa, OpenSSL::Digest::SHA256.new) @cert = cert @pkey = rsa end # Establish the OpenSSL::SSL::SSLContext with the configuration # parameters provided. def setup_ssl_context ctx = ::OpenSSL::SSL::SSLContext.new ctx.cert = @cert ctx.key = @pkey ctx.client_ca = self[:SSLClientCA] ctx.ca_path = self[:SSLCACertificatePath] ctx.ca_file = self[:SSLCACertificateFile] ctx.tmp_dh_callback = self[:SSLTmpDhCallback] ctx.verify_mode = self[:SSLVerifyMode] ctx.verify_depth = self[:SSLVerifyDepth] ctx.verify_callback = self[:SSLVerifyCallback] ctx.cert_store = self[:SSLCertificateStore] @ssl_ctx = ctx end end # Parse the dRuby +uri+ for an SSL connection. # # Expects drbssl://... # # Raises DRbBadScheme or DRbBadURI if +uri+ is not matching or malformed def self.parse_uri(uri) # :nodoc: if /\Adrbssl:\/\/(.*?):(\d+)(\?(.*))?\z/ =~ uri host = $1 port = $2.to_i option = $4 [host, port, option] else raise(DRbBadScheme, uri) unless uri.start_with?('drbssl:') raise(DRbBadURI, 'can\'t parse uri:' + uri) end end # Return an DRb::DRbSSLSocket instance as a client-side connection, # with the SSL connected. This is called from DRb::start_service or while # connecting to a remote object: # # DRb.start_service 'drbssl://localhost:0', front, config # # +uri+ is the URI we are connected to, # 'drbssl://localhost:0' above, +config+ is our # configuration. Either a Hash or DRb::DRbSSLSocket::SSLConfig def self.open(uri, config) host, port, = parse_uri(uri) soc = TCPSocket.open(host, port) ssl_conf = SSLConfig::new(config) ssl_conf.setup_ssl_context ssl = ssl_conf.connect(soc) self.new(uri, ssl, ssl_conf, true) end # Returns a DRb::DRbSSLSocket instance as a server-side connection, with # the SSL connected. This is called from DRb::start_service or while # connecting to a remote object: # # DRb.start_service 'drbssl://localhost:0', front, config # # +uri+ is the URI we are connected to, # 'drbssl://localhost:0' above, +config+ is our # configuration. Either a Hash or DRb::DRbSSLSocket::SSLConfig def self.open_server(uri, config) uri = 'drbssl://:0' unless uri host, port, = parse_uri(uri) if host.size == 0 host = getservername soc = open_server_inaddr_any(host, port) else soc = TCPServer.open(host, port) end port = soc.addr[1] if port == 0 @uri = "drbssl://#{host}:#{port}" ssl_conf = SSLConfig.new(config) ssl_conf.setup_certificate ssl_conf.setup_ssl_context self.new(@uri, soc, ssl_conf, false) end # This is a convenience method to parse +uri+ and separate out any # additional options appended in the +uri+. # # Returns an option-less uri and the option => [uri,option] # # The +config+ is completely unused, so passing nil is sufficient. def self.uri_option(uri, config) # :nodoc: host, port, option = parse_uri(uri) return "drbssl://#{host}:#{port}", option end # Create a DRb::DRbSSLSocket instance. # # +uri+ is the URI we are connected to. # +soc+ is the tcp socket we are bound to. # +config+ is our configuration. Either a Hash or SSLConfig # +is_established+ is a boolean of whether +soc+ is currently established # # This is called automatically based on the DRb protocol. def initialize(uri, soc, config, is_established) @ssl = is_established ? soc : nil super(uri, soc.to_io, config) end # Returns the SSL stream def stream; @ssl; end # :nodoc: # Closes the SSL stream before closing the dRuby connection. def close # :nodoc: if @ssl @ssl.close @ssl = nil end super end def accept # :nodoc: begin while true soc = accept_or_shutdown return nil unless soc break if (@acl ? @acl.allow_socket?(soc) : true) soc.close end begin ssl = @config.accept(soc) rescue Exception soc.close raise end self.class.new(uri, ssl, @config, true) rescue OpenSSL::SSL::SSLError warn("#{$!.message} (#{$!.class})", uplevel: 0) if @config[:verbose] retry end end end DRbProtocol.add_protocol(DRbSSLSocket) end PK!^DD extserv.rbnu[# frozen_string_literal: false =begin external service Copyright (c) 2000,2002 Masatoshi SEKI =end require_relative 'drb' require 'monitor' module DRb class ExtServ include MonitorMixin include DRbUndumped def initialize(there, name, server=nil) super() @server = server || DRb::primary_server @name = name ro = DRbObject.new(nil, there) synchronize do @invoker = ro.regist(name, DRbObject.new(self, @server.uri)) end end attr_reader :server def front DRbObject.new(nil, @server.uri) end def stop_service synchronize do @invoker.unregist(@name) server = @server @server = nil server.stop_service true end end def alive? @server ? @server.alive? : false end end end PK!Ydrb.rbnu[# frozen_string_literal: false # # = drb/drb.rb # # Distributed Ruby: _dRuby_ version 2.0.4 # # Copyright (c) 1999-2003 Masatoshi SEKI. You can redistribute it and/or # modify it under the same terms as Ruby. # # Author:: Masatoshi SEKI # # Documentation:: William Webber (william@williamwebber.com) # # == Overview # # dRuby is a distributed object system for Ruby. It allows an object in one # Ruby process to invoke methods on an object in another Ruby process on the # same or a different machine. # # The Ruby standard library contains the core classes of the dRuby package. # However, the full package also includes access control lists and the # Rinda tuple-space distributed task management system, as well as a # large number of samples. The full dRuby package can be downloaded from # the dRuby home page (see *References*). # # For an introduction and examples of usage see the documentation to the # DRb module. # # == References # # [http://www2a.biglobe.ne.jp/~seki/ruby/druby.html] # The dRuby home page, in Japanese. Contains the full dRuby package # and links to other Japanese-language sources. # # [http://www2a.biglobe.ne.jp/~seki/ruby/druby.en.html] # The English version of the dRuby home page. # # [http://pragprog.com/book/sidruby/the-druby-book] # The dRuby Book: Distributed and Parallel Computing with Ruby # by Masatoshi Seki and Makoto Inoue # # [http://www.ruby-doc.org/docs/ProgrammingRuby/html/ospace.html] # The chapter from *Programming* *Ruby* by Dave Thomas and Andy Hunt # which discusses dRuby. # # [http://www.clio.ne.jp/home/web-i31s/Flotuard/Ruby/PRC2K_seki/dRuby.en.html] # Translation of presentation on Ruby by Masatoshi Seki. require 'socket' require 'io/wait' require 'monitor' require_relative 'eq' # # == Overview # # dRuby is a distributed object system for Ruby. It is written in # pure Ruby and uses its own protocol. No add-in services are needed # beyond those provided by the Ruby runtime, such as TCP sockets. It # does not rely on or interoperate with other distributed object # systems such as CORBA, RMI, or .NET. # # dRuby allows methods to be called in one Ruby process upon a Ruby # object located in another Ruby process, even on another machine. # References to objects can be passed between processes. Method # arguments and return values are dumped and loaded in marshalled # format. All of this is done transparently to both the caller of the # remote method and the object that it is called upon. # # An object in a remote process is locally represented by a # DRb::DRbObject instance. This acts as a sort of proxy for the # remote object. Methods called upon this DRbObject instance are # forwarded to its remote object. This is arranged dynamically at run # time. There are no statically declared interfaces for remote # objects, such as CORBA's IDL. # # dRuby calls made into a process are handled by a DRb::DRbServer # instance within that process. This reconstitutes the method call, # invokes it upon the specified local object, and returns the value to # the remote caller. Any object can receive calls over dRuby. There # is no need to implement a special interface, or mixin special # functionality. Nor, in the general case, does an object need to # explicitly register itself with a DRbServer in order to receive # dRuby calls. # # One process wishing to make dRuby calls upon another process must # somehow obtain an initial reference to an object in the remote # process by some means other than as the return value of a remote # method call, as there is initially no remote object reference it can # invoke a method upon. This is done by attaching to the server by # URI. Each DRbServer binds itself to a URI such as # 'druby://example.com:8787'. A DRbServer can have an object attached # to it that acts as the server's *front* *object*. A DRbObject can # be explicitly created from the server's URI. This DRbObject's # remote object will be the server's front object. This front object # can then return references to other Ruby objects in the DRbServer's # process. # # Method calls made over dRuby behave largely the same as normal Ruby # method calls made within a process. Method calls with blocks are # supported, as are raising exceptions. In addition to a method's # standard errors, a dRuby call may also raise one of the # dRuby-specific errors, all of which are subclasses of DRb::DRbError. # # Any type of object can be passed as an argument to a dRuby call or # returned as its return value. By default, such objects are dumped # or marshalled at the local end, then loaded or unmarshalled at the # remote end. The remote end therefore receives a copy of the local # object, not a distributed reference to it; methods invoked upon this # copy are executed entirely in the remote process, not passed on to # the local original. This has semantics similar to pass-by-value. # # However, if an object cannot be marshalled, a dRuby reference to it # is passed or returned instead. This will turn up at the remote end # as a DRbObject instance. All methods invoked upon this remote proxy # are forwarded to the local object, as described in the discussion of # DRbObjects. This has semantics similar to the normal Ruby # pass-by-reference. # # The easiest way to signal that we want an otherwise marshallable # object to be passed or returned as a DRbObject reference, rather # than marshalled and sent as a copy, is to include the # DRb::DRbUndumped mixin module. # # dRuby supports calling remote methods with blocks. As blocks (or # rather the Proc objects that represent them) are not marshallable, # the block executes in the local, not the remote, context. Each # value yielded to the block is passed from the remote object to the # local block, then the value returned by each block invocation is # passed back to the remote execution context to be collected, before # the collected values are finally returned to the local context as # the return value of the method invocation. # # == Examples of usage # # For more dRuby samples, see the +samples+ directory in the full # dRuby distribution. # # === dRuby in client/server mode # # This illustrates setting up a simple client-server drb # system. Run the server and client code in different terminals, # starting the server code first. # # ==== Server code # # require 'drb/drb' # # # The URI for the server to connect to # URI="druby://localhost:8787" # # class TimeServer # # def get_current_time # return Time.now # end # # end # # # The object that handles requests on the server # FRONT_OBJECT=TimeServer.new # # DRb.start_service(URI, FRONT_OBJECT) # # Wait for the drb server thread to finish before exiting. # DRb.thread.join # # ==== Client code # # require 'drb/drb' # # # The URI to connect to # SERVER_URI="druby://localhost:8787" # # # Start a local DRbServer to handle callbacks. # # # # Not necessary for this small example, but will be required # # as soon as we pass a non-marshallable object as an argument # # to a dRuby call. # # # # Note: this must be called at least once per process to take any effect. # # This is particularly important if your application forks. # DRb.start_service # # timeserver = DRbObject.new_with_uri(SERVER_URI) # puts timeserver.get_current_time # # === Remote objects under dRuby # # This example illustrates returning a reference to an object # from a dRuby call. The Logger instances live in the server # process. References to them are returned to the client process, # where methods can be invoked upon them. These methods are # executed in the server process. # # ==== Server code # # require 'drb/drb' # # URI="druby://localhost:8787" # # class Logger # # # Make dRuby send Logger instances as dRuby references, # # not copies. # include DRb::DRbUndumped # # def initialize(n, fname) # @name = n # @filename = fname # end # # def log(message) # File.open(@filename, "a") do |f| # f.puts("#{Time.now}: #{@name}: #{message}") # end # end # # end # # # We have a central object for creating and retrieving loggers. # # This retains a local reference to all loggers created. This # # is so an existing logger can be looked up by name, but also # # to prevent loggers from being garbage collected. A dRuby # # reference to an object is not sufficient to prevent it being # # garbage collected! # class LoggerFactory # # def initialize(bdir) # @basedir = bdir # @loggers = {} # end # # def get_logger(name) # if !@loggers.has_key? name # # make the filename safe, then declare it to be so # fname = name.gsub(/[.\/\\\:]/, "_") # @loggers[name] = Logger.new(name, @basedir + "/" + fname) # end # return @loggers[name] # end # # end # # FRONT_OBJECT=LoggerFactory.new("/tmp/dlog") # # DRb.start_service(URI, FRONT_OBJECT) # DRb.thread.join # # ==== Client code # # require 'drb/drb' # # SERVER_URI="druby://localhost:8787" # # DRb.start_service # # log_service=DRbObject.new_with_uri(SERVER_URI) # # ["loga", "logb", "logc"].each do |logname| # # logger=log_service.get_logger(logname) # # logger.log("Hello, world!") # logger.log("Goodbye, world!") # logger.log("=== EOT ===") # # end # # == Security # # As with all network services, security needs to be considered when # using dRuby. By allowing external access to a Ruby object, you are # not only allowing outside clients to call the methods you have # defined for that object, but by default to execute arbitrary Ruby # code on your server. Consider the following: # # # !!! UNSAFE CODE !!! # ro = DRbObject::new_with_uri("druby://your.server.com:8989") # class << ro # undef :instance_eval # force call to be passed to remote object # end # ro.instance_eval("`rm -rf *`") # # The dangers posed by instance_eval and friends are such that a # DRbServer should only be used when clients are trusted. # # A DRbServer can be configured with an access control list to # selectively allow or deny access from specified IP addresses. The # main druby distribution provides the ACL class for this purpose. In # general, this mechanism should only be used alongside, rather than # as a replacement for, a good firewall. # # == dRuby internals # # dRuby is implemented using three main components: a remote method # call marshaller/unmarshaller; a transport protocol; and an # ID-to-object mapper. The latter two can be directly, and the first # indirectly, replaced, in order to provide different behaviour and # capabilities. # # Marshalling and unmarshalling of remote method calls is performed by # a DRb::DRbMessage instance. This uses the Marshal module to dump # the method call before sending it over the transport layer, then # reconstitute it at the other end. There is normally no need to # replace this component, and no direct way is provided to do so. # However, it is possible to implement an alternative marshalling # scheme as part of an implementation of the transport layer. # # The transport layer is responsible for opening client and server # network connections and forwarding dRuby request across them. # Normally, it uses DRb::DRbMessage internally to manage marshalling # and unmarshalling. The transport layer is managed by # DRb::DRbProtocol. Multiple protocols can be installed in # DRbProtocol at the one time; selection between them is determined by # the scheme of a dRuby URI. The default transport protocol is # selected by the scheme 'druby:', and implemented by # DRb::DRbTCPSocket. This uses plain TCP/IP sockets for # communication. An alternative protocol, using UNIX domain sockets, # is implemented by DRb::DRbUNIXSocket in the file drb/unix.rb, and # selected by the scheme 'drbunix:'. A sample implementation over # HTTP can be found in the samples accompanying the main dRuby # distribution. # # The ID-to-object mapping component maps dRuby object ids to the # objects they refer to, and vice versa. The implementation to use # can be specified as part of a DRb::DRbServer's configuration. The # default implementation is provided by DRb::DRbIdConv. It uses an # object's ObjectSpace id as its dRuby id. This means that the dRuby # reference to that object only remains meaningful for the lifetime of # the object's process and the lifetime of the object within that # process. A modified implementation is provided by DRb::TimerIdConv # in the file drb/timeridconv.rb. This implementation retains a local # reference to all objects exported over dRuby for a configurable # period of time (defaulting to ten minutes), to prevent them being # garbage-collected within this time. Another sample implementation # is provided in sample/name.rb in the main dRuby distribution. This # allows objects to specify their own id or "name". A dRuby reference # can be made persistent across processes by having each process # register an object using the same dRuby name. # module DRb # Superclass of all errors raised in the DRb module. class DRbError < RuntimeError; end # Error raised when an error occurs on the underlying communication # protocol. class DRbConnError < DRbError; end # Class responsible for converting between an object and its id. # # This, the default implementation, uses an object's local ObjectSpace # __id__ as its id. This means that an object's identification over # drb remains valid only while that object instance remains alive # within the server runtime. # # For alternative mechanisms, see DRb::TimerIdConv in drb/timeridconv.rb # and DRbNameIdConv in sample/name.rb in the full drb distribution. class DRbIdConv # Convert an object reference id to an object. # # This implementation looks up the reference id in the local object # space and returns the object it refers to. def to_obj(ref) ObjectSpace._id2ref(ref) end # Convert an object into a reference id. # # This implementation returns the object's __id__ in the local # object space. def to_id(obj) case obj when Object obj.nil? ? nil : obj.__id__ when BasicObject obj.__id__ end end end # Mixin module making an object undumpable or unmarshallable. # # If an object which includes this module is returned by method # called over drb, then the object remains in the server space # and a reference to the object is returned, rather than the # object being marshalled and moved into the client space. module DRbUndumped def _dump(dummy) # :nodoc: raise TypeError, 'can\'t dump' end end # Error raised by the DRb module when an attempt is made to refer to # the context's current drb server but the context does not have one. # See #current_server. class DRbServerNotFound < DRbError; end # Error raised by the DRbProtocol module when it cannot find any # protocol implementation support the scheme specified in a URI. class DRbBadURI < DRbError; end # Error raised by a dRuby protocol when it doesn't support the # scheme specified in a URI. See DRb::DRbProtocol. class DRbBadScheme < DRbError; end # An exception wrapping a DRb::DRbUnknown object class DRbUnknownError < DRbError # Create a new DRbUnknownError for the DRb::DRbUnknown object +unknown+ def initialize(unknown) @unknown = unknown super(unknown.name) end # Get the wrapped DRb::DRbUnknown object. attr_reader :unknown def self._load(s) # :nodoc: Marshal::load(s) end def _dump(lv) # :nodoc: Marshal::dump(@unknown) end end # An exception wrapping an error object class DRbRemoteError < DRbError # Creates a new remote error that wraps the Exception +error+ def initialize(error) @reason = error.class.to_s super("#{error.message} (#{error.class})") set_backtrace(error.backtrace) end # the class of the error, as a string. attr_reader :reason end # Class wrapping a marshalled object whose type is unknown locally. # # If an object is returned by a method invoked over drb, but the # class of the object is unknown in the client namespace, or # the object is a constant unknown in the client namespace, then # the still-marshalled object is returned wrapped in a DRbUnknown instance. # # If this object is passed as an argument to a method invoked over # drb, then the wrapped object is passed instead. # # The class or constant name of the object can be read from the # +name+ attribute. The marshalled object is held in the +buf+ # attribute. class DRbUnknown # Create a new DRbUnknown object. # # +buf+ is a string containing a marshalled object that could not # be unmarshalled. +err+ is the error message that was raised # when the unmarshalling failed. It is used to determine the # name of the unmarshalled object. def initialize(err, buf) case err.to_s when /uninitialized constant (\S+)/ @name = $1 when /undefined class\/module (\S+)/ @name = $1 else @name = nil end @buf = buf end # The name of the unknown thing. # # Class name for unknown objects; variable name for unknown # constants. attr_reader :name # Buffer contained the marshalled, unknown object. attr_reader :buf def self._load(s) # :nodoc: begin Marshal::load(s) rescue NameError, ArgumentError DRbUnknown.new($!, s) end end def _dump(lv) # :nodoc: @buf end # Attempt to load the wrapped marshalled object again. # # If the class of the object is now known locally, the object # will be unmarshalled and returned. Otherwise, a new # but identical DRbUnknown object will be returned. def reload self.class._load(@buf) end # Create a DRbUnknownError exception containing this object. def exception DRbUnknownError.new(self) end end # An Array wrapper that can be sent to another server via DRb. # # All entries in the array will be dumped or be references that point to # the local server. class DRbArray # Creates a new DRbArray that either dumps or wraps all the items in the # Array +ary+ so they can be loaded by a remote DRb server. def initialize(ary) @ary = ary.collect { |obj| if obj.kind_of? DRbUndumped DRbObject.new(obj) else begin Marshal.dump(obj) obj rescue DRbObject.new(obj) end end } end def self._load(s) # :nodoc: Marshal::load(s) end def _dump(lv) # :nodoc: Marshal.dump(@ary) end end # Handler for sending and receiving drb messages. # # This takes care of the low-level marshalling and unmarshalling # of drb requests and responses sent over the wire between server # and client. This relieves the implementor of a new drb # protocol layer with having to deal with these details. # # The user does not have to directly deal with this object in # normal use. class DRbMessage def initialize(config) # :nodoc: @load_limit = config[:load_limit] @argc_limit = config[:argc_limit] end def dump(obj, error=false) # :nodoc: case obj when DRbUndumped obj = make_proxy(obj, error) when Object # nothing else obj = make_proxy(obj, error) end begin str = Marshal::dump(obj) rescue str = Marshal::dump(make_proxy(obj, error)) end [str.size].pack('N') + str end def load(soc) # :nodoc: begin sz = soc.read(4) # sizeof (N) rescue raise(DRbConnError, $!.message, $!.backtrace) end 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 begin str = soc.read(sz) rescue raise(DRbConnError, $!.message, $!.backtrace) end raise(DRbConnError, 'connection closed') if str.nil? raise(DRbConnError, 'premature marshal format(can\'t read)') if str.size < sz DRb.mutex.synchronize do begin Marshal::load(str) rescue NameError, ArgumentError DRbUnknown.new($!, str) end end end def send_request(stream, ref, msg_id, arg, b) # :nodoc: 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('')) rescue raise(DRbConnError, $!.message, $!.backtrace) end def recv_request(stream) # :nodoc: ref = load(stream) ro = DRb.to_obj(ref) msg = load(stream) argc = load(stream) raise(DRbConnError, "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) # :nodoc: stream.write(dump(succ) + dump(result, !succ)) rescue raise(DRbConnError, $!.message, $!.backtrace) end def recv_reply(stream) # :nodoc: succ = load(stream) result = load(stream) [succ, result] end private def make_proxy(obj, error=false) # :nodoc: if error DRbRemoteError.new(obj) else DRbObject.new(obj) end end end # Module managing the underlying network protocol(s) used by drb. # # By default, drb uses the DRbTCPSocket protocol. Other protocols # can be defined. A protocol must define the following class methods: # # [open(uri, config)] Open a client connection to the server at +uri+, # using configuration +config+. Return a protocol # instance for this connection. # [open_server(uri, config)] Open a server listening at +uri+, # using configuration +config+. Return a # protocol instance for this listener. # [uri_option(uri, config)] Take a URI, possibly containing an option # component (e.g. a trailing '?param=val'), # and return a [uri, option] tuple. # # All of these methods should raise a DRbBadScheme error if the URI # does not identify the protocol they support (e.g. "druby:" for # the standard Ruby protocol). This is how the DRbProtocol module, # given a URI, determines which protocol implementation serves that # protocol. # # The protocol instance returned by #open_server must have the # following methods: # # [accept] Accept a new connection to the server. Returns a protocol # instance capable of communicating with the client. # [close] Close the server connection. # [uri] Get the URI for this server. # # The protocol instance returned by #open must have the following methods: # # [send_request (ref, msg_id, arg, b)] # Send a request to +ref+ with the given message id and arguments. # This is most easily implemented by calling DRbMessage.send_request, # providing a stream that sits on top of the current protocol. # [recv_reply] # Receive a reply from the server and return it as a [success-boolean, # reply-value] pair. This is most easily implemented by calling # DRb.recv_reply, providing a stream that sits on top of the # current protocol. # [alive?] # Is this connection still alive? # [close] # Close this connection. # # The protocol instance returned by #open_server().accept() must have # the following methods: # # [recv_request] # Receive a request from the client and return a [object, message, # args, block] tuple. This is most easily implemented by calling # DRbMessage.recv_request, providing a stream that sits on top of # the current protocol. # [send_reply(succ, result)] # Send a reply to the client. This is most easily implemented # by calling DRbMessage.send_reply, providing a stream that sits # on top of the current protocol. # [close] # Close this connection. # # A new protocol is registered with the DRbProtocol module using # the add_protocol method. # # For examples of other protocols, see DRbUNIXSocket in drb/unix.rb, # and HTTP0 in sample/http0.rb and sample/http0serv.rb in the full # drb distribution. module DRbProtocol # Add a new protocol to the DRbProtocol module. def add_protocol(prot) @protocol.push(prot) end module_function :add_protocol # Open a client connection to +uri+ with the configuration +config+. # # The DRbProtocol module asks each registered protocol in turn to # try to open the URI. Each protocol signals that it does not handle that # URI by raising a DRbBadScheme error. If no protocol recognises the # URI, then a DRbBadURI error is raised. If a protocol accepts the # URI, but an error occurs in opening it, a DRbConnError is raised. 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) return open(uri, config, false) end raise DRbBadURI, 'can\'t parse uri:' + uri end module_function :open # Open a server listening for connections at +uri+ with # configuration +config+. # # The DRbProtocol module asks each registered protocol in turn to # try to open a server at the URI. Each protocol signals that it does # not handle that URI by raising a DRbBadScheme error. If no protocol # recognises the URI, then a DRbBadURI error is raised. If a protocol # accepts the URI, but an error occurs in opening it, the underlying # error is passed on to the caller. 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) return open_server(uri, config, false) end raise DRbBadURI, 'can\'t parse uri:' + uri end module_function :open_server # Parse +uri+ into a [uri, option] pair. # # The DRbProtocol module asks each registered protocol in turn to # try to parse the URI. Each protocol signals that it does not handle that # URI by raising a DRbBadScheme error. If no protocol recognises the # URI, then a DRbBadURI error is raised. 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) return uri_option(uri, config, false) end raise DRbBadURI, 'can\'t parse uri:' + uri end module_function :uri_option def auto_load(uri) # :nodoc: if /\Adrb([a-z0-9]+):/ =~ uri require("drb/#{$1}") rescue nil end end module_function :auto_load end # The default drb protocol which communicates over a TCP socket. # # The DRb TCP protocol URI looks like: # druby://:?. The option is optional. class DRbTCPSocket # :stopdoc: private def self.parse_uri(uri) if /\Adruby:\/\/(.*?):(\d+)(\?(.*))?\z/ =~ uri host = $1 port = $2.to_i option = $4 [host, port, option] else raise(DRbBadScheme, uri) unless uri.start_with?('druby:') raise(DRbBadURI, 'can\'t parse uri:' + uri) end end public # Open a client connection to +uri+ (DRb URI string) using configuration # +config+. # # This can raise DRb::DRbBadScheme or DRb::DRbBadURI if +uri+ is not for a # recognized protocol. See DRb::DRbServer.new for information on built-in # URI protocols. def self.open(uri, config) host, port, = parse_uri(uri) soc = TCPSocket.open(host, port) self.new(uri, soc, config) end # Returns the hostname of this server def self.getservername host = Socket::gethostname begin Socket::getaddrinfo(host, nil, Socket::AF_UNSPEC, Socket::SOCK_STREAM, 0, Socket::AI_PASSIVE)[0][3] rescue 'localhost' end end # For the families available for +host+, returns a TCPServer on +port+. # If +port+ is 0 the first available port is used. IPv4 servers are # preferred over IPv6 servers. def self.open_server_inaddr_any(host, port) infos = Socket::getaddrinfo(host, nil, Socket::AF_UNSPEC, Socket::SOCK_STREAM, 0, Socket::AI_PASSIVE) families = Hash[*infos.collect { |af, *_| af }.uniq.zip([]).flatten] return TCPServer.open('0.0.0.0', port) if families.has_key?('AF_INET') return TCPServer.open('::', port) if families.has_key?('AF_INET6') return TCPServer.open(port) # :stopdoc: end # Open a server listening for connections at +uri+ using # configuration +config+. def self.open_server(uri, config) uri = 'druby://:0' unless uri host, port, _ = parse_uri(uri) config = {:tcp_original_host => host}.update(config) if host.size == 0 host = getservername soc = open_server_inaddr_any(host, port) else soc = TCPServer.open(host, port) end port = soc.addr[1] if port == 0 config[:tcp_port] = port uri = "druby://#{host}:#{port}" self.new(uri, soc, config) end # Parse +uri+ into a [uri, option] pair. def self.uri_option(uri, config) host, port, option = parse_uri(uri) return "druby://#{host}:#{port}", option end # Create a new DRbTCPSocket instance. # # +uri+ is the URI we are connected to. # +soc+ is the tcp socket we are bound to. +config+ is our # configuration. def initialize(uri, soc, config={}) @uri = uri @socket = soc @config = config @acl = config[:tcp_acl] @msg = DRbMessage.new(config) set_sockopt(@socket) @shutdown_pipe_r, @shutdown_pipe_w = IO.pipe end # Get the URI that we are connected to. attr_reader :uri # Get the address of our TCP peer (the other end of the socket # we are bound to. def peeraddr @socket.peeraddr end # Get the socket. def stream; @socket; end # On the client side, send a request to the server. def send_request(ref, msg_id, arg, b) @msg.send_request(stream, ref, msg_id, arg, b) end # On the server side, receive a request from the client. def recv_request @msg.recv_request(stream) end # On the server side, send a reply to the client. def send_reply(succ, result) @msg.send_reply(stream, succ, result) end # On the client side, receive a reply from the server. def recv_reply @msg.recv_reply(stream) end public # Close the connection. # # If this is an instance returned by #open_server, then this stops # listening for new connections altogether. If this is an instance # returned by #open or by #accept, then it closes this particular # client-server session. def close shutdown if @socket @socket.close @socket = nil end close_shutdown_pipe end def close_shutdown_pipe @shutdown_pipe_w.close @shutdown_pipe_r.close end private :close_shutdown_pipe # On the server side, for an instance returned by #open_server, # accept a client connection and return a new instance to handle # the server's side of this client-server session. def accept while true s = accept_or_shutdown return nil unless s break if (@acl ? @acl.allow_socket?(s) : true) s.close end if @config[:tcp_original_host].to_s.size == 0 uri = "druby://#{s.addr[3]}:#{@config[:tcp_port]}" else uri = @uri end self.class.new(uri, s, @config) end def accept_or_shutdown readables, = IO.select([@socket, @shutdown_pipe_r]) if readables.include? @shutdown_pipe_r return nil end @socket.accept end private :accept_or_shutdown # Graceful shutdown def shutdown @shutdown_pipe_w.close end # Check to see if this connection is alive. def alive? return false unless @socket if @socket.to_io.wait_readable(0) close return false end true end def set_sockopt(soc) # :nodoc: soc.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) rescue IOError, Errno::ECONNRESET, Errno::EINVAL # closed/shutdown socket, ignore error end end module DRbProtocol @protocol = [DRbTCPSocket] # default end class DRbURIOption # :nodoc: I don't understand the purpose of this class... def initialize(option) @option = option.to_s end attr_reader :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 # Object wrapping a reference to a remote drb object. # # Method calls on this object are relayed to the remote # object that this object is a stub for. class DRbObject # Unmarshall a marshalled DRbObject. # # If the referenced object is located within the local server, then # the object itself is returned. Otherwise, a new DRbObject is # created to act as a stub for the remote referenced object. def self._load(s) uri, ref = Marshal.load(s) if DRb.here?(uri) obj = DRb.to_obj(ref) return obj end self.new_with(uri, ref) end # Creates a DRb::DRbObject given the reference information to the remote # host +uri+ and object +ref+. def self.new_with(uri, ref) it = self.allocate it.instance_variable_set(:@uri, uri) it.instance_variable_set(:@ref, ref) it end # Create a new DRbObject from a URI alone. def self.new_with_uri(uri) self.new(nil, uri) end # Marshall this object. # # The URI and ref of the object are marshalled. def _dump(lv) Marshal.dump([@uri, @ref]) end # Create a new remote object stub. # # +obj+ is the (local) object we want to create a stub for. Normally # this is +nil+. +uri+ is the URI of the remote object that this # will be a stub for. def initialize(obj, uri=nil) @uri = nil @ref = nil case obj when Object is_nil = obj.nil? when BasicObject is_nil = false end if is_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 # Get the URI of the remote object. def __drburi @uri end # Get the reference of the object, if local. def __drbref @ref end undef :to_s undef :to_a if respond_to?(:to_a) # Routes respond_to? to the referenced remote object. def respond_to?(msg_id, priv=false) case msg_id when :_dump true when :marshal_dump false else method_missing(:respond_to?, msg_id, priv) end end # Routes method calls to the referenced remote object. ruby2_keywords 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 = self.class.with_friend(@uri) do DRbConn.open(@uri) do |conn| conn.send_message(self, msg_id, a, b) end end if succ return result elsif DRbUnknown === result raise result else bt = self.class.prepare_backtrace(@uri, result) result.set_backtrace(bt + caller) raise result end end # Given the +uri+ of another host executes the block provided. def self.with_friend(uri) # :nodoc: friend = DRb.fetch_server(uri) return yield() unless friend save = Thread.current['DRb'] Thread.current['DRb'] = { 'server' => friend } return yield ensure Thread.current['DRb'] = save if friend end # Returns a modified backtrace from +result+ with the +uri+ where each call # in the backtrace came from. def self.prepare_backtrace(uri, result) # :nodoc: prefix = "(#{uri}) " bt = [] result.backtrace.each do |x| break if /`__send__'$/ =~ x if /\A\(druby:\/\// =~ x bt.push(x) else bt.push(prefix + x) end end bt end def pretty_print(q) # :nodoc: q.pp_object(self) end def pretty_print_cycle(q) # :nodoc: q.object_address_group(self) { q.breakable q.text '...' } end end class ThreadObject include MonitorMixin def initialize(&blk) super() @wait_ev = new_cond @req_ev = new_cond @res_ev = new_cond @status = :wait @req = nil @res = nil @thread = Thread.new(self, &blk) end def alive? @thread.alive? end def kill @thread.kill @thread.join end def method_missing(msg, *arg, &blk) synchronize do @wait_ev.wait_until { @status == :wait } @req = [msg] + arg @status = :req @req_ev.broadcast @res_ev.wait_until { @status == :res } value = @res @req = @res = nil @status = :wait @wait_ev.broadcast return value end end def _execute() synchronize do @req_ev.wait_until { @status == :req } @res = yield(@req) @status = :res @res_ev.signal end end end # Class handling the connection between a DRbObject and the # server the real object lives on. # # This class maintains a pool of connections, to reduce the # overhead of starting and closing down connections for each # method call. # # This class is used internally by DRbObject. The user does # not normally need to deal with it directly. class DRbConn POOL_SIZE = 16 # :nodoc: def self.make_pool ThreadObject.new do |queue| pool = [] while true queue._execute do |message| case(message[0]) when :take then remote_uri = message[1] conn = nil 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 conn when :store then conn = message[1] pool.unshift(conn) pool.pop.close while pool.size > POOL_SIZE conn else nil end end end end end @pool_proxy = nil def self.stop_pool @pool_proxy&.kill @pool_proxy = nil end def self.open(remote_uri) # :nodoc: begin @pool_proxy = make_pool unless @pool_proxy&.alive? conn = @pool_proxy.take(remote_uri) conn = self.new(remote_uri) unless conn succ, result = yield(conn) return succ, result ensure if conn if succ @pool_proxy.store(conn) else conn.close end end end end def initialize(remote_uri) # :nodoc: @uri = remote_uri @protocol = DRbProtocol.open(remote_uri, DRb.config) end attr_reader :uri # :nodoc: def send_message(ref, msg_id, arg, block) # :nodoc: @protocol.send_request(ref, msg_id, arg, block) @protocol.recv_reply end def close # :nodoc: @protocol.close @protocol = nil end def alive? # :nodoc: return false unless @protocol @protocol.alive? end end # Class representing a drb server instance. # # A DRbServer must be running in the local process before any incoming # dRuby calls can be accepted, or any local objects can be passed as # dRuby references to remote processes, even if those local objects are # never actually called remotely. You do not need to start a DRbServer # in the local process if you are only making outgoing dRuby calls # passing marshalled parameters. # # Unless multiple servers are being used, the local DRbServer is normally # started by calling DRb.start_service. class DRbServer @@acl = nil @@idconv = DRbIdConv.new @@secondary_server = nil @@argc_limit = 256 @@load_limit = 0xffffffff @@verbose = false # Set the default value for the :argc_limit option. # # See #new(). The initial default value is 256. def self.default_argc_limit(argc) @@argc_limit = argc end # Set the default value for the :load_limit option. # # See #new(). The initial default value is 25 MB. def self.default_load_limit(sz) @@load_limit = sz end # Set the default access control list to +acl+. The default ACL is +nil+. # # See also DRb::ACL and #new() def self.default_acl(acl) @@acl = acl end # Set the default value for the :id_conv option. # # See #new(). The initial default value is a DRbIdConv instance. def self.default_id_conv(idconv) @@idconv = idconv end def self.default_safe_level(level) # :nodoc: # Remove in Ruby 3.0 end # Set the default value of the :verbose option. # # See #new(). The initial default value is false. def self.verbose=(on) @@verbose = on end # Get the default value of the :verbose option. def self.verbose @@verbose end def self.make_config(hash={}) # :nodoc: default_config = { :idconv => @@idconv, :verbose => @@verbose, :tcp_acl => @@acl, :load_limit => @@load_limit, :argc_limit => @@argc_limit, } default_config.update(hash) end # Create a new DRbServer instance. # # +uri+ is the URI to bind to. This is normally of the form # 'druby://:' where is a hostname of # the local machine. If nil, then the system's default hostname # will be bound to, on a port selected by the system; these value # can be retrieved from the +uri+ attribute. 'druby:' specifies # the default dRuby transport protocol: another protocol, such # as 'drbunix:', can be specified instead. # # +front+ is the front object for the server, that is, the object # to which remote method calls on the server will be passed. If # nil, then the server will not accept remote method calls. # # If +config_or_acl+ is a hash, it is the configuration to # use for this server. The following options are recognised: # # :idconv :: an id-to-object conversion object. This defaults # to an instance of the class DRb::DRbIdConv. # :verbose :: if true, all unsuccessful remote calls on objects # in the server will be logged to $stdout. false # by default. # :tcp_acl :: the access control list for this server. See # the ACL class from the main dRuby distribution. # :load_limit :: the maximum message size in bytes accepted by # the server. Defaults to 25 MB (26214400). # :argc_limit :: the maximum number of arguments to a remote # method accepted by the server. Defaults to # 256. # The default values of these options can be modified on # a class-wide basis by the class methods #default_argc_limit, # #default_load_limit, #default_acl, #default_id_conv, # and #verbose= # # If +config_or_acl+ is not a hash, but is not nil, it is # assumed to be the access control list for this server. # See the :tcp_acl option for more details. # # If no other server is currently set as the primary server, # this will become the primary server. # # The server will immediately start running in its own thread. 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 @exported_uri = [@uri] @front = front @idconv = @config[:idconv] @grp = ThreadGroup.new @thread = run DRb.regist_server(self) end # The URI of this DRbServer. attr_reader :uri # The main thread of this DRbServer. # # This is the thread that listens for and accepts connections # from clients, not that handles each client's request-response # session. attr_reader :thread # The front object of the DRbServer. # # This object receives remote method calls made on the server's # URI alone, with an object id. attr_reader :front # The configuration of this DRbServer attr_reader :config def safe_level # :nodoc: # Remove in Ruby 3.0 0 end # Set whether to operate in verbose mode. # # In verbose mode, failed calls are logged to stdout. def verbose=(v); @config[:verbose]=v; end # Get whether the server is in verbose mode. # # In verbose mode, failed calls are logged to stdout. def verbose; @config[:verbose]; end # Is this server alive? def alive? @thread.alive? end # Is +uri+ the URI for this server? def here?(uri) @exported_uri.include?(uri) end # Stop this server. def stop_service DRb.remove_server(self) if Thread.current['DRb'] && Thread.current['DRb']['server'] == self Thread.current['DRb']['stop_service'] = true else shutdown end end # Convert a dRuby reference to the local object it refers to. def to_obj(ref) return front if ref.nil? return front[ref.to_s] if DRbURIOption === ref @idconv.to_obj(ref) end # Convert a local object to a dRuby reference. def to_id(obj) return nil if obj.__id__ == front.__id__ @idconv.to_id(obj) end private def shutdown current = Thread.current if @protocol.respond_to? :shutdown @protocol.shutdown else [@thread, *@grp.list].each { |thread| thread.kill unless thread == current # xxx: Thread#kill } end @thread.join unless @thread == current end ## # Starts the DRb main loop in a new thread. def run Thread.start do begin while main_loop end ensure @protocol.close if @protocol end end end # List of insecure methods. # # These methods are not callable via dRuby. INSECURE_METHOD = [ :__send__ ] # Has a method been included in the list of insecure methods? def insecure_method?(msg_id) INSECURE_METHOD.include?(msg_id) end # Coerce an object to a string, providing our own representation if # to_s is not defined for the object. def any_to_s(obj) "#{obj}:#{obj.class}" rescue Kernel.instance_method(:to_s).bind_call(obj) end # Check that a method is callable via dRuby. # # +obj+ is the object we want to invoke the method on. +msg_id+ is the # method name, as a Symbol. # # If the method is an insecure method (see #insecure_method?) a # SecurityError is thrown. If the method is private or undefined, # a NameError is thrown. 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) case obj when Object if obj.private_methods.include?(msg_id) desc = any_to_s(obj) raise NoMethodError, "private method `#{msg_id}' called for #{desc}" elsif obj.protected_methods.include?(msg_id) desc = any_to_s(obj) raise NoMethodError, "protected method `#{msg_id}' called for #{desc}" else true end else if Kernel.instance_method(:private_methods).bind(obj).call.include?(msg_id) desc = any_to_s(obj) raise NoMethodError, "private method `#{msg_id}' called for #{desc}" elsif Kernel.instance_method(:protected_methods).bind(obj).call.include?(msg_id) desc = any_to_s(obj) raise NoMethodError, "protected method `#{msg_id}' called for #{desc}" else true end end end public :check_insecure_method class InvokeMethod # :nodoc: 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 case @result when Array if @msg_id == :to_ary @result = DRbArray.new(@result) end end return @succ, @result rescue NoMemoryError, SystemExit, SystemStackError, SecurityError raise rescue Exception @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 end require_relative 'invokemethod' class InvokeMethod include InvokeMethod18Mixin end def error_print(exception) exception.backtrace.inject(true) do |first, x| if first $stderr.puts "#{x}: #{exception} (#{exception.class})" else $stderr.puts "\tfrom #{x}" end false end end # The main loop performed by a DRbServer's internal thread. # # Accepts a connection from a client, and starts up its own # thread to handle it. This thread loops, receiving requests # from the client, invoking them on a local object, and # returning responses, until the client closes the connection # or a local method call fails. def main_loop client0 = @protocol.accept return nil if !client0 Thread.start(client0) do |client| @grp.add Thread.current Thread.current['DRb'] = { 'client' => client , 'server' => self } DRb.mutex.synchronize do client_uri = client.uri @exported_uri << client_uri unless @exported_uri.include?(client_uri) end loop do begin succ = false invoke_method = InvokeMethod.new(self, client) succ, result = invoke_method.perform error_print(result) if !succ && verbose unless DRbConnError === result && result.message == 'connection closed' client.send_reply(succ, result) end rescue Exception => e error_print(e) if verbose ensure client.close unless succ if Thread.current['DRb']['stop_service'] shutdown break end break unless succ end end end end end @primary_server = nil # Start a dRuby server locally. # # The new dRuby server will become the primary server, even # if another server is currently the primary server. # # +uri+ is the URI for the server to bind to. If nil, # the server will bind to random port on the default local host # name and use the default dRuby protocol. # # +front+ is the server's front object. This may be nil. # # +config+ is the configuration for the new server. This may # be nil. # # See DRbServer::new. def start_service(uri=nil, front=nil, config=nil) @primary_server = DRbServer.new(uri, front, config) end module_function :start_service # The primary local dRuby server. # # This is the server created by the #start_service call. attr_accessor :primary_server module_function :primary_server=, :primary_server # Get the 'current' server. # # In the context of execution taking place within the main # thread of a dRuby server (typically, as a result of a remote # call on the server or one of its objects), the current # server is that server. Otherwise, the current server is # the primary server. # # If the above rule fails to find a server, a DRbServerNotFound # error is raised. 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 # Stop the local dRuby server. # # This operates on the primary server. If there is no primary # server currently running, it is a noop. def stop_service @primary_server.stop_service if @primary_server @primary_server = nil end module_function :stop_service # Get the URI defining the local dRuby space. # # This is the URI of the current server. See #current_server. def uri drb = Thread.current['DRb'] client = (drb && drb['client']) if client uri = client.uri return uri if uri end current_server.uri end module_function :uri # Is +uri+ the URI for the current local server? def here?(uri) current_server.here?(uri) rescue false # (current_server.uri rescue nil) == uri end module_function :here? # Get the configuration of the current server. # # If there is no current server, this returns the default configuration. # See #current_server and DRbServer::make_config. def config current_server.config rescue DRbServer.make_config end module_function :config # Get the front object of the current server. # # This raises a DRbServerNotFound error if there is no current server. # See #current_server. def front current_server.front end module_function :front # Convert a reference into an object using the current server. # # This raises a DRbServerNotFound error if there is no current server. # See #current_server. def to_obj(ref) current_server.to_obj(ref) end # Get a reference id for an object using the current server. # # This raises a DRbServerNotFound error if there is no current server. # See #current_server. def to_id(obj) current_server.to_id(obj) end module_function :to_id module_function :to_obj # Get the thread of the primary server. # # This returns nil if there is no primary server. See #primary_server. def thread @primary_server ? @primary_server.thread : nil end module_function :thread # Set the default id conversion object. # # This is expected to be an instance such as DRb::DRbIdConv that responds to # #to_id and #to_obj that can convert objects to and from DRb references. # # See DRbServer#default_id_conv. def install_id_conv(idconv) DRbServer.default_id_conv(idconv) end module_function :install_id_conv # Set the default ACL to +acl+. # # See DRb::DRbServer.default_acl. def install_acl(acl) DRbServer.default_acl(acl) end module_function :install_acl @mutex = Thread::Mutex.new def mutex # :nodoc: @mutex end module_function :mutex @server = {} # Registers +server+ with DRb. # # This is called when a new DRb::DRbServer is created. # # If there is no primary server then +server+ becomes the primary server. # # Example: # # require 'drb' # # s = DRb::DRbServer.new # automatically calls regist_server # DRb.fetch_server s.uri #=> # def regist_server(server) @server[server.uri] = server mutex.synchronize do @primary_server = server unless @primary_server end end module_function :regist_server # Removes +server+ from the list of registered servers. def remove_server(server) @server.delete(server.uri) mutex.synchronize do if @primary_server == server @primary_server = nil end end end module_function :remove_server # Retrieves the server with the given +uri+. # # See also regist_server and remove_server. def fetch_server(uri) @server[uri] end module_function :fetch_server end # :stopdoc: DRbObject = DRb::DRbObject DRbUndumped = DRb::DRbUndumped DRbIdConv = DRb::DRbIdConv PK!U  unix.rbnu[# frozen_string_literal: false require 'socket' require_relative 'drb' require 'tmpdir' raise(LoadError, "UNIXServer is required") unless defined?(UNIXServer) module DRb # Implements DRb over a UNIX socket # # DRb UNIX socket URIs look like drbunix:?. The # option is optional. class DRbUNIXSocket < DRbTCPSocket # :stopdoc: def self.parse_uri(uri) if /\Adrbunix:(.*?)(\?(.*))?\z/ =~ uri filename = $1 option = $3 [filename, option] else raise(DRbBadScheme, uri) unless uri.start_with?('drbunix:') raise(DRbBadURI, 'can\'t parse uri:' + uri) end end def self.open(uri, config) filename, = parse_uri(uri) soc = UNIXSocket.open(filename) self.new(uri, soc, config) end def self.open_server(uri, config) filename, = parse_uri(uri) if filename.size == 0 soc = temp_server filename = soc.path uri = 'drbunix:' + soc.path else soc = UNIXServer.open(filename) end owner = config[:UNIXFileOwner] group = config[:UNIXFileGroup] if owner || group require 'etc' owner = Etc.getpwnam( owner ).uid if owner group = Etc.getgrnam( group ).gid if group File.chown owner, group, 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 = Dir::tmpdir 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 shutdown # DRbProtocol#shutdown path = @socket.path if @server_mode @socket.close File.unlink(path) if @server_mode @socket = nil close_shutdown_pipe end def accept s = accept_or_shutdown return nil unless s self.class.new(nil, s, @config) end def set_sockopt(soc) # no-op for now end end DRbProtocol.add_protocol(DRbUNIXSocket) # :startdoc: end PK!a observer.rbnu[# frozen_string_literal: false require 'observer' module DRb # The Observable module extended to DRb. See Observable for details. module DRbObservable include Observable # Notifies observers of a change in state. See also # Observable#notify_observers def notify_observers(*arg) if defined? @observer_state and @observer_state if defined? @observer_peers @observer_peers.each do |observer, method| begin observer.send(method, *arg) rescue delete_observer(observer) end end end @observer_state = false end end end end PK!*Y)  invokemethod.rbnu[# frozen_string_literal: false # for ruby-1.8.0 module DRb # :nodoc: all class DRbServer module InvokeMethod18Mixin def block_yield(x) if x.size == 1 && x[0].class == Array x[0] = DRbArray.new(x[0]) end @block.call(*x) 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 case jump_error.reason when :break break(jump_error.exit_value) else raise jump_error end end block_value end end end end end PK!{X## version.rbnu[PK!D[]timeridconv.rbnu[PK!4@ eq.rbnu[PK!Ew|  extservm.rbnu[PK!+  gw.rbnu[PK!ы>qqacl.rbnu[PK!,}} 0weakidconv.rbnu[PK!@%.%.Y5ssl.rbnu[PK!^DD cextserv.rbnu[PK!Y2gdrb.rbnu[PK!U  Munix.rbnu[PK!a JYobserver.rbnu[PK!*Y)   \invokemethod.rbnu[PK h_