#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>
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 = 0, *bufReply = 0, *bufTemp;
register mach_msg_return_t mr;
register kern_return_t kr;
if ((kr = vm_allocate(mach_task_self(),
(vm_address_t *)&bufRequest,
max_size + MAX_TRAILER_SIZE,
VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE)) != KERN_SUCCESS)
return kr;
if ((kr = vm_allocate(mach_task_self(),
(vm_address_t *)&bufReply,
max_size + MAX_TRAILER_SIZE,
VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE)) != KERN_SUCCESS)
return kr;
mr = mach_msg_overwrite_trap(&bufRequest->Head, MACH_RCV_MSG|options,
0, max_size, rcv_name,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL,
(mach_msg_header_t *) 0, 0);
if (mr == MACH_MSG_SUCCESS) {
(void) (*demux)(&bufRequest->Head, &bufReply->Head);
if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) &&
bufReply->RetCode != KERN_SUCCESS) {
if (bufReply->RetCode == MIG_NO_REPLY)
return KERN_SUCCESS;
bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
mach_msg_destroy(&bufRequest->Head);
}
if (bufReply->Head.msgh_remote_port == MACH_PORT_NULL) {
if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
mach_msg_destroy(&bufReply->Head);
return KERN_SUCCESS;
}
bufTemp = bufRequest;
bufRequest = bufReply;
bufReply = bufTemp;
mr = mach_msg_overwrite_trap(&bufRequest->Head,
(MACH_MSGH_BITS_REMOTE(bufRequest->Head.msgh_bits) ==
MACH_MSG_TYPE_MOVE_SEND_ONCE) ?
MACH_SEND_MSG|options :
MACH_SEND_MSG|MACH_SEND_TIMEOUT|options,
bufRequest->Head.msgh_size, 0, MACH_PORT_NULL,
0, MACH_PORT_NULL, (mach_msg_header_t *) 0, 0);
}
switch (mr) {
case MACH_SEND_INVALID_DEST:
case MACH_SEND_TIMED_OUT:
mach_msg_destroy(&bufRequest->Head);
return KERN_SUCCESS;
case MACH_RCV_TOO_LARGE:
return KERN_SUCCESS;
default:
(void)vm_deallocate(mach_task_self(),
(vm_address_t) bufRequest,
max_size + MAX_TRAILER_SIZE);
(void)vm_deallocate(mach_task_self(),
(vm_address_t) bufReply,
max_size + MAX_TRAILER_SIZE);
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 = 0, *bufReply = 0, *bufTemp;
register mach_msg_return_t mr;
register kern_return_t kr;
if ((kr = vm_allocate(mach_task_self(),
(vm_address_t *)&bufRequest,
max_size + MAX_TRAILER_SIZE,
VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE)) != KERN_SUCCESS)
return kr;
if ((kr = vm_allocate(mach_task_self(),
(vm_address_t *)&bufReply,
max_size + MAX_TRAILER_SIZE,
VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE)) != KERN_SUCCESS)
return kr;
for (;;) {
get_request:
mr = mach_msg_overwrite_trap(&bufRequest->Head, MACH_RCV_MSG|options,
0, max_size, rcv_name,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL,
(mach_msg_header_t *) 0, 0);
while (mr == MACH_MSG_SUCCESS) {
(void) (*demux)(&bufRequest->Head, &bufReply->Head);
if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) &&
bufReply->RetCode != KERN_SUCCESS) {
if (bufReply->RetCode == MIG_NO_REPLY)
goto get_request;
bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
mach_msg_destroy(&bufRequest->Head);
}
if (bufReply->Head.msgh_remote_port == MACH_PORT_NULL) {
if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
mach_msg_destroy(&bufReply->Head);
goto get_request;
}
bufTemp = bufRequest;
bufRequest = bufReply;
bufReply = bufTemp;
mr = mach_msg_overwrite_trap(&bufRequest->Head,
(MACH_MSGH_BITS_REMOTE(bufRequest->Head.msgh_bits) ==
MACH_MSG_TYPE_MOVE_SEND_ONCE) ?
MACH_SEND_MSG|MACH_RCV_MSG|options :
MACH_SEND_MSG|MACH_SEND_TIMEOUT|MACH_RCV_MSG|options,
bufRequest->Head.msgh_size, max_size, rcv_name,
0, MACH_PORT_NULL, (mach_msg_header_t *) 0, 0);
}
switch (mr) {
case MACH_SEND_INVALID_DEST:
case MACH_SEND_TIMED_OUT:
mach_msg_destroy(&bufRequest->Head);
break;
case MACH_RCV_TOO_LARGE:
break;
default:
(void)vm_deallocate(mach_task_self(),
(vm_address_t) bufRequest,
max_size + MAX_TRAILER_SIZE);
(void)vm_deallocate(mach_task_self(),
(vm_address_t) bufReply,
max_size + MAX_TRAILER_SIZE);
return mr;
}
}
}