144 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			144 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Ruby
		
	
	
	
require 'ffi'
 | 
						|
require 'mach/functions'
 | 
						|
 | 
						|
module Mach
 | 
						|
  # Michael Weber's "Some Fun with Mach Ports" was an indispensable
 | 
						|
  # resource in learning the Mach ports API.
 | 
						|
  #
 | 
						|
  # @see http://www.foldr.org/~michaelw/log/computers/macosx/task-info-fun-with-mach
 | 
						|
  class Port
 | 
						|
    include Functions
 | 
						|
 | 
						|
    class SendRightMsg < FFI::Struct
 | 
						|
      include Functions
 | 
						|
 | 
						|
      layout(:header, MsgHeader,
 | 
						|
             :body, MsgBody,
 | 
						|
             :port, MsgPortDescriptor)
 | 
						|
    end
 | 
						|
 | 
						|
    class ReceiveRightMsg < FFI::Struct
 | 
						|
      include Functions
 | 
						|
 | 
						|
      layout(:header, MsgHeader,
 | 
						|
             :body, MsgBody,
 | 
						|
             :port, MsgPortDescriptor,
 | 
						|
             :trailer, MsgTrailer)
 | 
						|
    end
 | 
						|
 | 
						|
    attr_reader :ipc_space, :port
 | 
						|
 | 
						|
    # @param [Hash] opts
 | 
						|
    #
 | 
						|
    # @option opts [Integer] :ipc_space defaults to +mach_task_self+
 | 
						|
    #
 | 
						|
    # @option opts [MachPortRight] :right defaults to +:receive+
 | 
						|
    #
 | 
						|
    # @option opts [Port, Integer] :port if given, the existing port
 | 
						|
    # is wrapped in a new Port object; otherwise a new port is
 | 
						|
    # allocated according to the other options
 | 
						|
    def initialize(opts = {})
 | 
						|
      @ipc_space = opts[:ipc_space] || mach_task_self
 | 
						|
      right = opts[:right] || :receive
 | 
						|
 | 
						|
      @port = if opts[:port]
 | 
						|
                opts[:port].to_i
 | 
						|
              else
 | 
						|
                mem = new_memory_pointer(:mach_port_right_t)
 | 
						|
                mach_port_allocate(@ipc_space.to_i, right, mem)
 | 
						|
                mem.get_uint(0)
 | 
						|
              end
 | 
						|
    end
 | 
						|
 | 
						|
    # With this alias, we can call #to_i on either bare Integer ports
 | 
						|
    # or wrapped Port objects when passing the arg to a foreign
 | 
						|
    # function.
 | 
						|
    alias_method :to_i, :port
 | 
						|
 | 
						|
    def to_s
 | 
						|
      "#<#{self.class} #{to_i}>"
 | 
						|
    end
 | 
						|
 | 
						|
    def ==(other)
 | 
						|
      (port == other.port) && (ipc_space == other.ipc_space)
 | 
						|
    end
 | 
						|
 | 
						|
    def destroy(opts = {})
 | 
						|
      ipc_space = opts[:ipc_space] || @ipc_space
 | 
						|
      mach_port_destroy(ipc_space, @port)
 | 
						|
    end
 | 
						|
 | 
						|
    def deallocate(opts = {})
 | 
						|
      ipc_space = opts[:ipc_space] || @ipc_space
 | 
						|
      mach_port_deallocate(ipc_space.to_i, @port)
 | 
						|
    end
 | 
						|
 | 
						|
    # Insert +right+ into another ipc space.  The current task must
 | 
						|
    # have sufficient rights to insert the requested right.
 | 
						|
    #
 | 
						|
    # @param [MsgType] right
 | 
						|
    #
 | 
						|
    # @param [Hash] opts
 | 
						|
    #
 | 
						|
    # @option opts [Port,Integer] :ipc_space the space (task port) into which
 | 
						|
    # +right+ will be inserted; defaults to this port's ipc_space
 | 
						|
    #
 | 
						|
    # @option opts [Port,Integer] :port the name the port right should
 | 
						|
    # have in :ipc_space; defaults to the same name as this port
 | 
						|
    def insert_right(right, opts = {})
 | 
						|
      ipc_space = opts[:ipc_space] || @ipc_space
 | 
						|
      port_name = opts[:port_name] || @port
 | 
						|
      
 | 
						|
      mach_port_insert_right(ipc_space.to_i, port_name.to_i, @port, right)
 | 
						|
    end
 | 
						|
 | 
						|
    # Send +right+ on this Port to +remote_port+.  The current task
 | 
						|
    # must already have the requisite rights allowing it to send
 | 
						|
    # +right+.
 | 
						|
    def send_right(right, remote_port)
 | 
						|
      msg = SendRightMsg.new
 | 
						|
 | 
						|
      msg[:header].tap do |h|
 | 
						|
        h[:remote_port] = remote_port.to_i
 | 
						|
        h[:local_port] = PORT_NULL
 | 
						|
        h[:bits] =
 | 
						|
          (MsgType[right] | (0 << 8)) | 0x80000000 # MACH_MSGH_BITS_COMPLEX
 | 
						|
        h[:size] = 40 # msg.size
 | 
						|
      end
 | 
						|
 | 
						|
      msg[:body][:descriptor_count] = 1
 | 
						|
 | 
						|
      msg[:port].tap do |p|
 | 
						|
        p[:name] = port
 | 
						|
        p[:disposition] = MsgType[right]
 | 
						|
        p[:type] = 0 # MSG_PORT_DESCRIPTOR;
 | 
						|
      end
 | 
						|
      
 | 
						|
      mach_msg_send msg
 | 
						|
    end
 | 
						|
 | 
						|
    # Copy the send right on this port and send it in a message to
 | 
						|
    # +remote_port+.  The current task must have an existing send
 | 
						|
    # right on this Port.
 | 
						|
    def copy_send(remote_port)
 | 
						|
      send_right(:copy_send, remote_port)
 | 
						|
    end
 | 
						|
 | 
						|
    # Create a new Port by receiving a port right message on this
 | 
						|
    # port.
 | 
						|
    def receive_right
 | 
						|
      msg = ReceiveRightMsg.new
 | 
						|
  
 | 
						|
      mach_msg(msg,
 | 
						|
               2, # RCV_MSG,
 | 
						|
               0,
 | 
						|
               msg.size,
 | 
						|
               port,
 | 
						|
               MSG_TIMEOUT_NONE,
 | 
						|
               PORT_NULL)
 | 
						|
 | 
						|
      self.class.new :port => msg[:port][:name]
 | 
						|
    end
 | 
						|
  end
 | 
						|
end
 |