A thread-safe channel that multiplexes many threads onto a single 9P2000 connection.

The send/recv implementation is based on the XCB cookie approach: www.x.org/releases/X11R7.5/doc/libxcb/tutorial/#requestsreplies

Methods
A
C
E
N
O
R
S
T
W
Classes and Modules
Constants
MODES = { 'r' => Topen::OREAD, 'w' => Topen::OWRITE, 't' => Topen::ORCLOSE, '+' => Topen::ORDWR, }
Attributes
[R] msize
Class Public methods
new(stream)
stream
I/O stream on which a 9P2000 server is listening.
# File lib/rumai/ixp/transport.rb, line 26
      def initialize stream
        @stream = stream

        @recv_buf = {} # tag => message
        @recv_lock = Mutex.new

        @tag_pool = RangedPool.new(0...BYTE2_MASK)
        @fid_pool = RangedPool.new(0...BYTE4_MASK)

        # establish connection with 9P2000 server
        req = Tversion.new(
          :tag     => Fcall::NOTAG,
          :msize   => Tversion::MSIZE,
          :version => Tversion::VERSION
        )
        rsp = talk(req)

        unless req.version == rsp.version
          raise Error, "protocol mismatch: self=#{req.version.inspect} server=#{rsp.version.inspect}"
        end

        @msize = rsp.msize

        # authenticate the connection (not necessary for wmii)
        @auth_fid = Fcall::NOFID

        # attach to filesystem root
        @root_fid = @fid_pool.obtain
        attach @root_fid, @auth_fid
      end
Instance Public methods
attach(root_fid, auth_fid = Fcall::NOFID, auth_name = ENV['USER'])

Associates the given FID with the FS root.

# File lib/rumai/ixp/transport.rb, line 459
      def attach root_fid, auth_fid = Fcall::NOFID, auth_name = ENV['USER']
        talk Tattach.new(
          :fid    => root_fid,
          :afid   => auth_fid,
          :uname  => ENV['USER'],
          :aname  => auth_name
        )
      end
clunk(fid)

Retires the given FID from use.

# File lib/rumai/ixp/transport.rb, line 471
      def clunk fid
        talk Tclunk.new(:fid => fid)
        @fid_pool.release fid
      end
create(path, mode = 'rw', perm = 0644)

Creates a new file at the given path that is accessible using the given modes for a user having the given permission bits.

# File lib/rumai/ixp/transport.rb, line 382
      def create path, mode = 'rw', perm = 0644
        prefix = File.dirname(path)
        target = File.basename(path)

        mode = MODES.parse(mode)

        with_fid do |prefix_fid|
          walk_fid prefix_fid, prefix

          # create the file
          talk Tcreate.new(
            :fid => prefix_fid,
            :name => target,
            :perm => perm,
            :mode => mode
          )
        end
      end
entries(path)

Returns the basenames of all files inside the directory at the given path.

See Dir::entries in the Ruby documentation.

# File lib/rumai/ixp/transport.rb, line 360
      def entries path
        unless stat(path).directory?
          raise ArgumentError, "#{path.inspect} is not a directory"
        end

        read(path).map! {|t| t.name}
      end
open(path, mode = 'r')

Opens the given path for I/O access through a FidStream object. If a block is given, it is invoked with a FidStream object and the stream is closed afterwards.

See File::open in the Ruby documentation.

# File lib/rumai/ixp/transport.rb, line 199
      def open path, mode = 'r' # :yields: FidStream
        mode = MODES.parse(mode)

        # open the file
        path_fid = walk(path)

        talk Topen.new(
          :fid  => path_fid,
          :mode => mode
        )

        stream = FidStream.new(self, path_fid, @msize)

        # return the file stream
        if block_given?
          begin
            yield stream
          ensure
            stream.close
          end
        else
          stream
        end
      end
read(path, *args)

Returns the content of the file/directory at the given path.

# File lib/rumai/ixp/transport.rb, line 348
      def read path, *args
        open path do |f|
          f.read(*args)
        end
      end
recv(tag)

Returns the reply for the given ticket, which was previously given to you when you sent the corresponding request (Rumai::IXP::Fcall).

# File lib/rumai/ixp/transport.rb, line 124
      def recv tag
        loop do
          reply = @recv_lock.synchronize do
            if @recv_buf.key? tag
              @recv_buf.delete tag
            else
              # reply was not in the receive buffer, so wait
              # for the next reply... hoping that it is ours
              msg = Fcall.from_9p(@stream)

              if msg.tag == tag
                msg
              else
                # we got someone else's reply, so buffer
                # it (for them to receive) and try again
                @recv_buf[msg.tag] = msg
                nil
              end
            end
          end

          if reply
            @tag_pool.release tag

            if reply.is_a? Rerror
              raise Error, reply.ename
            end

            return reply
          else
            # give other threads a chance to receive
            Thread.pass
          end
        end
      end
remove(path)

Deletes the file at the given path.

# File lib/rumai/ixp/transport.rb, line 404
      def remove path
        path_fid = walk(path)
        remove_fid path_fid # remove also does clunk
      end
remove_fid(path_fid)

Deletes the file corresponding to the given FID and clunks the given FID.

# File lib/rumai/ixp/transport.rb, line 413
      def remove_fid path_fid
        talk Tremove.new(:fid => path_fid)
      end
send(request)

Sends the given request (Rumai::IXP::Fcall) and returns a ticket that you can use later to receive the reply.

# File lib/rumai/ixp/transport.rb, line 111
      def send request
        tag = @tag_pool.obtain

        request.tag = tag
        @stream << request.to_9p

        tag
      end
stat(path)

Returns information about the file at the given path.

# File lib/rumai/ixp/transport.rb, line 420
      def stat path
        with_fid do |path_fid|
          walk_fid path_fid, path
          stat_fid path_fid
        end
      end
stat_fid(path_fid)

Returns information about the file referenced by the given FID.

# File lib/rumai/ixp/transport.rb, line 430
      def stat_fid path_fid
        req = Tstat.new(:fid => path_fid)
        rsp = talk(req)
        rsp.stat
      end
talk(request)

Sends the given request (Rumai::IXP::Fcall) and returns its reply.

# File lib/rumai/ixp/transport.rb, line 163
      def talk request
        tag = send(request)

        begin
          recv tag
        rescue Error => e
          e.message << " -- in reply to #{request.inspect}"
          raise
        end
      end
walk(path)

Returns an FID corresponding to the given path.

# File lib/rumai/ixp/transport.rb, line 439
      def walk path
        fid = @fid_pool.obtain
        walk_fid fid, path
        fid
      end
walk_fid(path_fid, path)

Associates the given FID to the given path.

# File lib/rumai/ixp/transport.rb, line 448
      def walk_fid path_fid, path
        talk Twalk.new(
          :fid    => @root_fid,
          :newfid => path_fid,
          :wname  => path.to_s.split(%r{/+}).reject {|s| s.empty? }
        )
      end
write(path, content)

Writes the given content to the file at the given path.

# File lib/rumai/ixp/transport.rb, line 372
      def write path, content
        open path, 'w' do |f|
          f << content
        end
      end