Add methods on port to send and receive rights from other tasks.

This commit is contained in:
Patrick Mahoney 2012-01-21 15:06:46 -06:00
parent 9f17aac114
commit 0ac03cfefc
4 changed files with 152 additions and 15 deletions

View File

@ -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 #
#######################

View File

@ -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

View File

@ -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

View File

@ -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