From 0ac03cfefc68b00d1fc0fefdb2f974576eae302b Mon Sep 17 00:00:00 2001 From: Patrick Mahoney Date: Sat, 21 Jan 2012 15:06:46 -0600 Subject: [PATCH] Add methods on port to send and receive rights from other tasks. --- lib/mach/functions.rb | 75 +++++++++++++++++++++++++++++++++++++--- lib/mach/port.rb | 79 ++++++++++++++++++++++++++++++++++++++++--- lib/mach/semaphore.rb | 6 ++-- lib/mach/task.rb | 7 ++-- 4 files changed, 152 insertions(+), 15 deletions(-) diff --git a/lib/mach/functions.rb b/lib/mach/functions.rb index 5fb47b6..f4bd59b 100644 --- a/lib/mach/functions.rb +++ b/lib/mach/functions.rb @@ -11,6 +11,13 @@ module Mach typedef :__darwin_mach_port_t, :mach_port_t typedef :__darwin_natural_t, :natural_t + typedef :int, :integer_t + typedef :int, :kern_return_t # true for 64 bit?? + typedef :int, :mach_error_t + typedef :int, :sync_policy_t # SyncPolicy + + typedef :string, :name_t + typedef :mach_port_t, :task_t typedef :mach_port_t, :ipc_space_t typedef :mach_port_t, :semaphore_t @@ -22,12 +29,47 @@ module Mach typedef :pointer, :mach_port_name_pointer_t typedef :uint, :mach_msg_type_name_t + typedef :uint, :mach_msg_bits_t + typedef :uint, :mach_msg_trailer_type_t + typedef :uint, :mach_msg_trailer_size_t + typedef :uint, :mach_msg_descriptor_type_t + typedef :natural_t, :mach_msg_timeout_t - typedef :int, :kern_return_t # true for 64 bit?? - typedef :int, :mach_error_t - typedef :int, :sync_policy_t # SyncPolicy + typedef :natural_t, :mach_msg_size_t + typedef :integer_t, :mach_msg_id_t + typedef :integer_t, :mach_msg_options_t + typedef :integer_t, :mach_msg_option_t - typedef :string, :name_t + class MsgHeader < FFI::Struct + layout(:bits, :mach_msg_bits_t, + :size, :mach_msg_size_t, + :remote_port, :mach_port_t, + :local_port, :mach_port_t, + :reserved, :mach_msg_size_t, + :id, :mach_msg_id_t) + end + + class MsgBody < FFI::Struct + layout(:descriptor_count, :mach_msg_size_t) + end + + class MsgBase < FFI::Struct + layout(:header, MsgHeader, + :body, MsgBody) + end + + class MsgTrailer < FFI::Struct + layout(:type, :mach_msg_trailer_type_t, + :size, :mach_msg_trailer_size_t) + end + + class MsgPortDescriptor < FFI::Struct + layout(:name, :mach_port_t, + :pad1, :mach_msg_size_t, # FIXME: leave oout on __LP64__ + :pad2, :uint16, # :uint + :disposition, :uint8, # :mach_msg_type_name_t + :type, :uint8) # :mach_msg_descriptor_type_t + end SyncPolicy = enum( :fifo, 0x0, :fixed_priority, 0x1, @@ -52,6 +94,9 @@ module Mach end acc end + + MACH_PORT_NULL = 0 + MACH_MSG_TIMEOUT_NONE = 0 MachPortType = enum(:none, 0, @@ -224,6 +269,28 @@ module Mach :mach_port_t], :kern_return_t) + ##################### + # Message functions # + ##################### + + attach_mach_function(:mach_msg_send, + [:pointer], # msg_header_t + :kern_return_t) + + attach_mach_function(:mach_msg, + [:pointer, # msg_header_t + :mach_msg_option_t, + :mach_msg_size_t, + :mach_msg_size_t, + :mach_port_name_t, + :mach_msg_timeout_t, + :mach_port_name_t], + :kern_return_t) + + attach_mach_function(:mach_msg_receive, + [:pointer], # msg_header_t + :kern_return_t) + ####################### # Semaphore functions # ####################### diff --git a/lib/mach/port.rb b/lib/mach/port.rb index 87759ae..0232dd7 100644 --- a/lib/mach/port.rb +++ b/lib/mach/port.rb @@ -1,6 +1,11 @@ +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 @@ -21,15 +26,24 @@ module Mach right = opts[:right] || :receive @port = if opts[:port] - opts[:port].kind_of?(Port) ? opts[:port].port : opts[:port] + opts[:port].to_i else mem = new_memory_pointer(:mach_port_right_t) - mach_port_allocate(@ipc_space, right, mem) + mach_port_allocate(@ipc_space.to_i, right, mem) mem.get_uint(0) end 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 @@ -41,14 +55,71 @@ module Mach def deallocate(opts = {}) ipc_space = opts[:ipc_space] || @ipc_space - mach_port_deallocate(ipc_space, @port) + mach_port_deallocate(ipc_space.to_i, @port) end def insert_right(msg_type, opts = {}) ipc_space = opts[:ipc_space] || @ipc_space port_name = opts[:port_name] || @port - mach_port_insert_right(ipc_space, port_name, @port, msg_type) + mach_port_insert_right(ipc_space.to_i, port_name.to_i, @port, msg_type) + 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) + puts "send_right: (in #{mach_task_self}) sending #{right} on #{port} -> #{remote_port.to_i}" + + msg = FFI::Struct.new(nil, + :header, MsgHeader, + :body, MsgBody, + :port, MsgPortDescriptor) + + msg[:header].tap do |h| + h[:remote_port] = remote_port.to_i + h[:local_port] = MACH_PORT_NULL + h[:bits] = + (MachMsgType[right] | (0 << 8)) | 0x80000000 # MACH_MSGH_BITS_COMPLEX + h[:size] = msg.size + end + + msg[:body][:descriptor_count] = 1 + + msg[:port].tap do |p| + p[:name] = port + p[:disposition] = MachMsgType[right] + p[:type] = 0 # MACH_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 be receiving a port right message on this + # port. + def receive_right + msg = FFI::Struct.new(nil, + :header, MsgHeader, + :body, MsgBody, + :port, MsgPortDescriptor, + :trailer, MsgTrailer) + + mach_msg(msg, + 2, # MACH_RCV_MSG, + 0, + msg.size, + port, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL) + + self.class.new :port => msg[:port][:name] end end end diff --git a/lib/mach/semaphore.rb b/lib/mach/semaphore.rb index 29947d9..007eaaf 100644 --- a/lib/mach/semaphore.rb +++ b/lib/mach/semaphore.rb @@ -24,11 +24,11 @@ module Mach # @return [Integer] a semaphore port name def initialize(opts = {}) value = opts[:value] || 1 - task = opts[:task] || ipc_space || mach_task_self + task = (opts[:task] && opts[:task].to_i) || ipc_space || mach_task_self sync_policy = opts[:sync_policy] || :fifo port = if opts[:port] - opts[:port] + opts[:port].to_i else mem = new_memory_pointer(:semaphore_t) semaphore_create(task, mem, sync_policy, value) @@ -46,7 +46,7 @@ module Mach # semaphore (defaults to the owning task) def destroy(opts = {}) task = opts[:task] || ipc_space || mach_task_self - semaphore_destroy(task, port) + semaphore_destroy(task.to_i, port) end def signal diff --git a/lib/mach/task.rb b/lib/mach/task.rb index 6a1329f..a585ad7 100644 --- a/lib/mach/task.rb +++ b/lib/mach/task.rb @@ -18,17 +18,16 @@ module Mach # @param [MachSpecialPort] which_port def get_special_port(which_port) - mem = FFI::MemoryPointer.new(:int) + mem = new_memory_pointer(:mach_port_t) task_get_special_port(task, which_port, mem) - Port.new(:port => mem.get_int(0)) + Port.new(:port => mem.get_uint(0)) end # @param [MachSpecialPort] which_port # # @param [Port,Integer] newport def set_special_port(which_port, newport) - p = newport.respond_to?(:port) ? newport.port : newport - task_set_special_port(task, which_port, p) + task_set_special_port(task, which_port, newport.to_i) end def get_bootstrap_port