2012-01-16 14:14:58 +00:00
|
|
|
require 'ffi'
|
|
|
|
|
2012-02-09 03:30:18 +00:00
|
|
|
require 'mach/types'
|
2012-02-02 03:34:51 +00:00
|
|
|
require 'mach/time_spec'
|
|
|
|
|
2012-01-15 14:02:53 +00:00
|
|
|
module Mach
|
|
|
|
# FFI wrapper around a subset of the Mach API (likely Mac OS X
|
|
|
|
# specific).
|
|
|
|
module Functions
|
|
|
|
extend FFI::Library
|
2012-02-09 03:30:18 +00:00
|
|
|
extend Types
|
2012-01-15 14:02:53 +00:00
|
|
|
|
|
|
|
ffi_lib 'c'
|
|
|
|
|
|
|
|
# Replace methods in +syms+ with error checking wrappers that
|
|
|
|
# invoke the original method and raise a {SystemCallError}.
|
|
|
|
#
|
2014-01-01 00:25:25 +00:00
|
|
|
# The original method is invoked, and its return value is passed
|
2012-01-15 14:02:53 +00:00
|
|
|
# to the block (or a default check). The block should return true
|
|
|
|
# if the return value indicates an error state.
|
|
|
|
def self.error_check(*syms, &is_err)
|
|
|
|
unless block_given?
|
|
|
|
is_err = lambda { |v| (v != KERN_SUCCESS) }
|
|
|
|
end
|
|
|
|
|
|
|
|
syms.each do |sym|
|
|
|
|
method = self.method(sym)
|
|
|
|
|
|
|
|
new_method_body = proc do |*args|
|
|
|
|
ret = method.call(*args)
|
|
|
|
if is_err.call(ret)
|
|
|
|
raise Error.new("error in #{sym}", ret)
|
|
|
|
else
|
|
|
|
ret
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
define_singleton_method(sym, &new_method_body)
|
|
|
|
define_method(sym, &new_method_body)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Replace methods in +syms+ with error checking wrappers that
|
|
|
|
# invoke the original method and raise a {SystemCallError}.
|
|
|
|
#
|
|
|
|
# The original method is invoked, and it's return value is passed
|
|
|
|
# to the block (or a default check). The block should return true
|
|
|
|
# if the return value indicates an error state.
|
|
|
|
def self.error_check_bootstrap(*syms, &is_err)
|
|
|
|
unless block_given?
|
|
|
|
is_err = lambda { |v| (v != KERN_SUCCESS) }
|
|
|
|
end
|
|
|
|
|
|
|
|
syms.each do |sym|
|
|
|
|
method = self.method(sym)
|
|
|
|
|
|
|
|
new_method_body = proc do |*args|
|
|
|
|
ret = method.call(*args)
|
|
|
|
if is_err.call(ret)
|
|
|
|
ptr = bootstrap_strerror(ret)
|
|
|
|
msg = ptr.null? ? nil : ptr.read_string()
|
|
|
|
raise "error in #{sym}: #{msg}"
|
|
|
|
else
|
|
|
|
ret
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
define_singleton_method(sym, &new_method_body)
|
|
|
|
define_method(sym, &new_method_body)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Attach a function as with +attach_function+, but check the
|
|
|
|
# return value and raise an exception on errors.
|
2013-12-26 17:27:36 +00:00
|
|
|
def self.attach_mach_function(sym, argtypes, rettype, options = nil)
|
|
|
|
attach_function(sym, argtypes, rettype, options)
|
2012-01-15 14:02:53 +00:00
|
|
|
error_check(sym)
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.new_memory_pointer(type)
|
|
|
|
FFI::MemoryPointer.new(find_type(type))
|
|
|
|
end
|
|
|
|
|
|
|
|
def new_memory_pointer(type)
|
|
|
|
Mach::Functions.new_memory_pointer(type)
|
|
|
|
end
|
|
|
|
|
|
|
|
attach_function :mach_task_self, [], :task_t
|
|
|
|
attach_function :mach_error_string, [:mach_error_t], :pointer
|
|
|
|
|
|
|
|
#######################
|
|
|
|
# Bootstrap functions #
|
|
|
|
#######################
|
|
|
|
|
|
|
|
attach_variable :bootstrap_port, :mach_port_t
|
|
|
|
|
|
|
|
attach_function(:bootstrap_strerror,
|
|
|
|
[:kern_return_t],
|
|
|
|
:pointer)
|
|
|
|
|
|
|
|
attach_function(:bootstrap_register,
|
|
|
|
[:mach_port_t, :name_t, :mach_port_t],
|
|
|
|
:kern_return_t)
|
|
|
|
error_check_bootstrap :bootstrap_register
|
|
|
|
|
|
|
|
##################
|
|
|
|
# Port functions #
|
|
|
|
##################
|
|
|
|
|
|
|
|
attach_mach_function(:mach_port_allocate,
|
|
|
|
[:ipc_space_t,
|
2012-02-09 03:30:18 +00:00
|
|
|
PortRight,
|
2012-01-15 14:02:53 +00:00
|
|
|
:mach_port_name_pointer_t],
|
|
|
|
:kern_return_t)
|
|
|
|
|
|
|
|
attach_mach_function(:mach_port_destroy,
|
|
|
|
[:ipc_space_t,
|
|
|
|
:mach_port_name_t],
|
|
|
|
:kern_return_t)
|
|
|
|
|
|
|
|
attach_mach_function(:mach_port_deallocate,
|
|
|
|
[:ipc_space_t,
|
|
|
|
:mach_port_name_t],
|
|
|
|
:kern_return_t)
|
|
|
|
|
|
|
|
attach_mach_function(:mach_port_insert_right,
|
|
|
|
[:ipc_space_t,
|
|
|
|
:mach_port_name_t,
|
|
|
|
:mach_port_t,
|
2012-02-09 03:30:18 +00:00
|
|
|
MsgType],
|
2012-01-15 14:02:53 +00:00
|
|
|
:kern_return_t)
|
|
|
|
|
2012-02-02 03:34:51 +00:00
|
|
|
##################
|
|
|
|
# Host functions #
|
|
|
|
##################
|
|
|
|
|
|
|
|
attach_function :mach_host_self, [], :mach_port_t
|
|
|
|
|
|
|
|
attach_mach_function(:host_get_clock_service,
|
|
|
|
[:host_t,
|
|
|
|
:clock_id_t,
|
|
|
|
:pointer],
|
|
|
|
:kern_return_t)
|
|
|
|
|
2012-01-15 14:02:53 +00:00
|
|
|
##################
|
|
|
|
# Task functions #
|
|
|
|
##################
|
|
|
|
|
|
|
|
attach_mach_function(:task_get_special_port,
|
|
|
|
[:task_t,
|
2012-02-09 03:30:18 +00:00
|
|
|
SpecialPort,
|
2012-01-15 14:02:53 +00:00
|
|
|
:mach_port_pointer_t],
|
|
|
|
:kern_return_t)
|
|
|
|
|
|
|
|
attach_mach_function(:task_set_special_port,
|
|
|
|
[:task_t,
|
2012-02-09 03:30:18 +00:00
|
|
|
SpecialPort,
|
2012-01-15 14:02:53 +00:00
|
|
|
:mach_port_t],
|
|
|
|
:kern_return_t)
|
|
|
|
|
2012-02-02 03:34:51 +00:00
|
|
|
###################
|
|
|
|
# Clock functions #
|
|
|
|
###################
|
|
|
|
|
|
|
|
attach_mach_function(:clock_get_time,
|
|
|
|
[:clock_id_t,
|
|
|
|
TimeSpec],
|
|
|
|
:kern_return_t)
|
|
|
|
|
2012-01-21 21:06:46 +00:00
|
|
|
#####################
|
|
|
|
# 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)
|
|
|
|
|
2012-01-15 14:02:53 +00:00
|
|
|
#######################
|
|
|
|
# Semaphore functions #
|
|
|
|
#######################
|
|
|
|
|
|
|
|
attach_mach_function(:semaphore_create,
|
|
|
|
[:task_t, :pointer, SyncPolicy, :int],
|
|
|
|
:kern_return_t)
|
|
|
|
attach_mach_function(:semaphore_destroy,
|
|
|
|
[:task_t, :semaphore_t],
|
|
|
|
:kern_return_t)
|
|
|
|
|
|
|
|
attach_mach_function(:semaphore_signal,
|
|
|
|
[:semaphore_t],
|
|
|
|
:kern_return_t)
|
|
|
|
attach_mach_function(:semaphore_signal_all,
|
|
|
|
[:semaphore_t],
|
|
|
|
:kern_return_t)
|
|
|
|
attach_mach_function(:semaphore_wait,
|
|
|
|
[:semaphore_t],
|
2013-12-26 17:27:36 +00:00
|
|
|
:kern_return_t,
|
|
|
|
:blocking => true)
|
2012-01-15 14:02:53 +00:00
|
|
|
attach_mach_function(:semaphore_timedwait,
|
2012-02-02 03:34:51 +00:00
|
|
|
[:semaphore_t, TimeSpec.val],
|
2013-12-26 17:45:04 +00:00
|
|
|
:kern_return_t,
|
|
|
|
:blocking => true)
|
2012-01-15 14:02:53 +00:00
|
|
|
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
require 'mach/error'
|