summaryrefslogtreecommitdiff
path: root/lib/drb/unix.rb
blob: 7dcf2daaf83c9a799899c2429ed5cd7507f812b0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# frozen_string_literal: false
require 'socket'
require 'drb/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 <code>drbunix:<path>?<option></code>.  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)
      filename.untaint
      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