#include <stdlib.h>
#include <mach/mach.h>
#include <mach/boolean.h>
#include <mach/kern_return.h>
#include <mach/message.h>
#include <mach/mig_errors.h>
#include <mach/vm_statistics.h>
#ifdef HOST_MACH_MSG_TRAP
__private_extern__ kern_return_t _host_mach_msg_trap_return_ = KERN_FAILURE;
#define MACH_MSG_TRAP(msg, opt, ssize, rsize, rname, to, not) \
((_host_mach_msg_trap_return_ == KERN_SUCCESS) ? \
mach_msg_trap((msg), (opt), (ssize), (rsize), (rname), (to), (not)) : \
mach_msg_overwrite_trap((msg), (opt), (ssize), (rsize), (rname), \
(to), (not), MACH_MSG_NULL, 0))
#else
#define MACH_MSG_TRAP(msg, opt, ssize, rsize, rname, to, not) \
mach_msg_overwrite_trap((msg), (opt), (ssize), (rsize), (rname), \
(to), (not), MACH_MSG_NULL, 0))
#endif
#define LIBMACH_OPTIONS (MACH_SEND_INTERRUPT|MACH_RCV_INTERRUPT)
mach_msg_return_t
mach_msg(msg, option, send_size, rcv_size, rcv_name, timeout, notify)
mach_msg_header_t *msg;
mach_msg_option_t option;
mach_msg_size_t send_size;
mach_msg_size_t rcv_size;
mach_port_t rcv_name;
mach_msg_timeout_t timeout;
mach_port_t notify;
{
mach_msg_return_t mr;
mr = MACH_MSG_TRAP(msg, option &~ LIBMACH_OPTIONS,
send_size, rcv_size, rcv_name,
timeout, notify);
if (mr == MACH_MSG_SUCCESS)
return MACH_MSG_SUCCESS;
if ((option & MACH_SEND_INTERRUPT) == 0)
while (mr == MACH_SEND_INTERRUPTED)
mr = MACH_MSG_TRAP(msg,
option &~ LIBMACH_OPTIONS,
send_size, rcv_size, rcv_name,
timeout, notify);
if ((option & MACH_RCV_INTERRUPT) == 0)
while (mr == MACH_RCV_INTERRUPTED)
mr = MACH_MSG_TRAP(msg,
option &~ (LIBMACH_OPTIONS|MACH_SEND_MSG),
0, rcv_size, rcv_name,
timeout, notify);
return mr;
}
mach_msg_return_t
mach_msg_overwrite(msg, option, send_size, rcv_limit, rcv_name, timeout,
notify, rcv_msg, rcv_scatter_size)
mach_msg_header_t *msg;
mach_msg_option_t option;
mach_msg_size_t send_size;
mach_msg_size_t rcv_limit;
mach_port_t rcv_name;
mach_msg_timeout_t timeout;
mach_port_t notify;
mach_msg_header_t *rcv_msg;
mach_msg_size_t rcv_scatter_size;
{
mach_msg_return_t mr;
mr = mach_msg_overwrite_trap(msg, option &~ LIBMACH_OPTIONS,
send_size, rcv_limit, rcv_name,
timeout, notify, rcv_msg, rcv_scatter_size);
if (mr == MACH_MSG_SUCCESS)
return MACH_MSG_SUCCESS;
if ((option & MACH_SEND_INTERRUPT) == 0)
while (mr == MACH_SEND_INTERRUPTED)
mr = mach_msg_overwrite_trap(msg,
option &~ LIBMACH_OPTIONS,
send_size, rcv_limit, rcv_name,
timeout, notify, rcv_msg, rcv_scatter_size);
if ((option & MACH_RCV_INTERRUPT) == 0)
while (mr == MACH_RCV_INTERRUPTED)
mr = mach_msg_overwrite_trap(msg,
option &~ (LIBMACH_OPTIONS|MACH_SEND_MSG),
0, rcv_limit, rcv_name,
timeout, notify, rcv_msg, rcv_scatter_size);
return mr;
}
mach_msg_return_t
mach_msg_send(mach_msg_header_t *msg)
{
return mach_msg(msg, MACH_SEND_MSG,
msg->msgh_size, 0, MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
}
mach_msg_return_t
mach_msg_receive(mach_msg_header_t *msg)
{
return mach_msg(msg, MACH_RCV_MSG,
0, msg->msgh_size, msg->msgh_local_port,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
}
static void
mach_msg_destroy_port(mach_port_t port, mach_msg_type_name_t type)
{
if (MACH_PORT_VALID(port)) switch (type) {
case MACH_MSG_TYPE_MOVE_SEND:
case MACH_MSG_TYPE_MOVE_SEND_ONCE:
(void) mach_port_deallocate(mach_task_self(), port);
break;
case MACH_MSG_TYPE_MOVE_RECEIVE:
(void) mach_port_mod_refs(mach_task_self(), port,
MACH_PORT_RIGHT_RECEIVE, -1);
break;
case MACH_MSG_TYPE_MAKE_SEND:
(void) mach_port_insert_right(mach_task_self(), port,
port, MACH_MSG_TYPE_MAKE_SEND);
(void) mach_port_deallocate(mach_task_self(), port);
break;
case MACH_MSG_TYPE_MAKE_SEND_ONCE:
(void) mach_port_extract_right(mach_task_self(), port,
MACH_MSG_TYPE_MAKE_SEND_ONCE,
&port, &type);
(void) mach_port_deallocate(mach_task_self(), port);
break;
}
}
static void
mach_msg_destroy_memory(vm_offset_t addr, vm_size_t size)
{
if (size != 0)
(void) vm_deallocate(mach_task_self(), addr, size);
}
void
mach_msg_destroy(mach_msg_header_t *msg)
{
mach_msg_bits_t mbits = msg->msgh_bits;
mach_msg_destroy_port(msg->msgh_remote_port, MACH_MSGH_BITS_REMOTE(mbits));
if (mbits & MACH_MSGH_BITS_COMPLEX) {
mach_msg_body_t *body;
mach_msg_descriptor_t *saddr, *eaddr;
body = (mach_msg_body_t *) (msg + 1);
saddr = (mach_msg_descriptor_t *)
((mach_msg_base_t *) msg + 1);
eaddr = saddr + body->msgh_descriptor_count;
for ( ; saddr < eaddr; saddr++) {
switch (saddr->type.type) {
case MACH_MSG_PORT_DESCRIPTOR: {
mach_msg_port_descriptor_t *dsc;
dsc = &saddr->port;
mach_msg_destroy_port(dsc->name, dsc->disposition);
break;
}
case MACH_MSG_OOL_DESCRIPTOR : {
mach_msg_ool_descriptor_t *dsc;
dsc = &saddr->out_of_line;
if (dsc->deallocate) {
mach_msg_destroy_memory((vm_offset_t)dsc->address,
dsc->size);
}
break;
}
case MACH_MSG_OOL_PORTS_DESCRIPTOR : {
mach_port_t *ports;
mach_msg_ool_ports_descriptor_t *dsc;
mach_msg_type_number_t j;
dsc = &saddr->ool_ports;
ports = (mach_port_t *) dsc->address;
for (j = 0; j < dsc->count; j++, ports++) {
mach_msg_destroy_port(*ports, dsc->disposition);
}
if (dsc->deallocate) {
mach_msg_destroy_memory((vm_offset_t)dsc->address,
dsc->count * sizeof(mach_port_t));
}
break;
}
}
}
}
}
mach_msg_return_t
mach_msg_server_once(
boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *),
mach_msg_size_t max_size,
mach_port_t rcv_name,
mach_msg_options_t options)
{
mig_reply_error_t *bufRequest, *bufReply;
mach_msg_size_t request_size;
mach_msg_size_t request_alloc;
mach_msg_size_t trailer_alloc;
mach_msg_size_t reply_alloc;
mach_msg_return_t mr;
kern_return_t kr;
mach_port_t self = mach_task_self();
options &= ~(MACH_SEND_MSG|MACH_RCV_MSG);
trailer_alloc = REQUESTED_TRAILER_SIZE(options);
request_alloc = round_page(max_size + trailer_alloc);
request_size = (options & MACH_RCV_LARGE) ?
request_alloc : max_size + trailer_alloc;
reply_alloc = round_page((options & MACH_SEND_TRAILER) ?
(max_size + MAX_TRAILER_SIZE) :
max_size);
kr = vm_allocate(self,
(vm_address_t *)&bufReply,
reply_alloc,
VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE);
if (kr != KERN_SUCCESS)
return kr;
for (;;) {
mach_msg_size_t new_request_alloc;
kr = vm_allocate(self,
(vm_address_t *)&bufRequest,
request_alloc,
VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE);
if (kr != KERN_SUCCESS) {
vm_deallocate(self,
(vm_address_t)bufReply,
reply_alloc);
return kr;
}
mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG|options,
0, request_size, rcv_name,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if (!((mr == MACH_RCV_TOO_LARGE) && (options & MACH_RCV_LARGE)))
break;
new_request_alloc = round_page(bufRequest->Head.msgh_size +
trailer_alloc);
vm_deallocate(self,
(vm_address_t) bufRequest,
request_alloc);
request_size = request_alloc = new_request_alloc;
}
if (mr == MACH_MSG_SUCCESS) {
(void) (*demux)(&bufRequest->Head, &bufReply->Head);
if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
if (bufReply->RetCode == MIG_NO_REPLY)
bufReply->Head.msgh_remote_port = MACH_PORT_NULL;
else if ((bufReply->RetCode != KERN_SUCCESS) &&
(bufRequest->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
mach_msg_destroy(&bufRequest->Head);
}
}
if (bufReply->Head.msgh_remote_port != MACH_PORT_NULL) {
mr = mach_msg(&bufReply->Head,
(MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) ==
MACH_MSG_TYPE_MOVE_SEND_ONCE) ?
MACH_SEND_MSG|options :
MACH_SEND_MSG|MACH_SEND_TIMEOUT|options,
bufReply->Head.msgh_size, 0, MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if ((mr != MACH_SEND_INVALID_DEST) &&
(mr != MACH_SEND_TIMED_OUT))
goto done_once;
mr = MACH_MSG_SUCCESS;
}
if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
mach_msg_destroy(&bufReply->Head);
}
done_once:
(void)vm_deallocate(self,
(vm_address_t) bufRequest,
request_alloc);
(void)vm_deallocate(self,
(vm_address_t) bufReply,
reply_alloc);
return mr;
}
mach_msg_return_t
mach_msg_server(
boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *),
mach_msg_size_t max_size,
mach_port_t rcv_name,
mach_msg_options_t options)
{
mig_reply_error_t *bufRequest, *bufReply;
mach_msg_size_t request_size;
mach_msg_size_t new_request_alloc;
mach_msg_size_t request_alloc;
mach_msg_size_t trailer_alloc;
mach_msg_size_t reply_alloc;
mach_msg_return_t mr;
kern_return_t kr;
mach_port_t self = mach_task_self();
options &= ~(MACH_SEND_MSG|MACH_RCV_MSG);
reply_alloc = round_page((options & MACH_SEND_TRAILER) ?
(max_size + MAX_TRAILER_SIZE) : max_size);
kr = vm_allocate(self,
(vm_address_t *)&bufReply,
reply_alloc,
VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE);
if (kr != KERN_SUCCESS)
return kr;
request_alloc = 0;
trailer_alloc = REQUESTED_TRAILER_SIZE(options);
new_request_alloc = round_page(max_size + trailer_alloc);
request_size = (options & MACH_RCV_LARGE) ?
new_request_alloc : max_size + trailer_alloc;
for (;;) {
if (request_alloc < new_request_alloc) {
request_alloc = new_request_alloc;
kr = vm_allocate(self,
(vm_address_t *)&bufRequest,
request_alloc,
VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE);
if (kr != KERN_SUCCESS) {
vm_deallocate(self,
(vm_address_t)bufReply,
reply_alloc);
return kr;
}
}
mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG|options,
0, request_size, rcv_name,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
while (mr == MACH_MSG_SUCCESS) {
(void) (*demux)(&bufRequest->Head, &bufReply->Head);
if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
if (bufReply->RetCode == MIG_NO_REPLY)
bufReply->Head.msgh_remote_port = MACH_PORT_NULL;
else if ((bufReply->RetCode != KERN_SUCCESS) &&
(bufRequest->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
mach_msg_destroy(&bufRequest->Head);
}
}
if (bufReply->Head.msgh_remote_port != MACH_PORT_NULL) {
if (request_alloc == reply_alloc) {
mig_reply_error_t *bufTemp;
mr = mach_msg(
&bufReply->Head,
(MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) ==
MACH_MSG_TYPE_MOVE_SEND_ONCE) ?
MACH_SEND_MSG|MACH_RCV_MSG|options :
MACH_SEND_MSG|MACH_RCV_MSG|MACH_SEND_TIMEOUT|options,
bufReply->Head.msgh_size, request_size, rcv_name,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
bufTemp = bufRequest;
bufRequest = bufReply;
bufReply = bufTemp;
} else {
mr = mach_msg_overwrite(
&bufReply->Head,
(MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) ==
MACH_MSG_TYPE_MOVE_SEND_ONCE) ?
MACH_SEND_MSG|MACH_RCV_MSG|options :
MACH_SEND_MSG|MACH_RCV_MSG|MACH_SEND_TIMEOUT|options,
bufReply->Head.msgh_size, request_size, rcv_name,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL,
&bufRequest->Head, request_size);
}
if ((mr != MACH_SEND_INVALID_DEST) &&
(mr != MACH_SEND_TIMED_OUT))
continue;
}
if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
mach_msg_destroy(&bufReply->Head);
mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG|options,
0, request_size, rcv_name,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
}
if ((mr == MACH_RCV_TOO_LARGE) && (options & MACH_RCV_LARGE)) {
new_request_alloc = round_page(bufRequest->Head.msgh_size +
trailer_alloc);
request_size = new_request_alloc;
vm_deallocate(self,
(vm_address_t) bufRequest,
request_alloc);
continue;
}
break;
}
(void)vm_deallocate(self,
(vm_address_t) bufRequest,
request_alloc);
(void)vm_deallocate(self,
(vm_address_t) bufReply,
reply_alloc);
return mr;
}