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
 |