michael@0: // Copyright (c) 2007, Google Inc. michael@0: // All rights reserved. michael@0: // michael@0: // Redistribution and use in source and binary forms, with or without michael@0: // modification, are permitted provided that the following conditions are michael@0: // met: michael@0: // michael@0: // * Redistributions of source code must retain the above copyright michael@0: // notice, this list of conditions and the following disclaimer. michael@0: // * Redistributions in binary form must reproduce the above michael@0: // copyright notice, this list of conditions and the following disclaimer michael@0: // in the documentation and/or other materials provided with the michael@0: // distribution. michael@0: // * Neither the name of Google Inc. nor the names of its michael@0: // contributors may be used to endorse or promote products derived from michael@0: // this software without specific prior written permission. michael@0: // michael@0: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: // michael@0: // MachIPC.mm michael@0: // Wrapper for mach IPC calls michael@0: michael@0: #import michael@0: #import "MachIPC.h" michael@0: #include "common/mac/bootstrap_compat.h" michael@0: michael@0: namespace google_breakpad { michael@0: //============================================================================== michael@0: MachSendMessage::MachSendMessage(int32_t message_id) : MachMessage() { michael@0: head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); michael@0: michael@0: // head.msgh_remote_port = ...; // filled out in MachPortSender::SendMessage() michael@0: head.msgh_local_port = MACH_PORT_NULL; michael@0: head.msgh_reserved = 0; michael@0: head.msgh_id = 0; michael@0: michael@0: SetDescriptorCount(0); // start out with no descriptors michael@0: michael@0: SetMessageID(message_id); michael@0: SetData(NULL, 0); // client may add data later michael@0: } michael@0: michael@0: //============================================================================== michael@0: // returns true if successful michael@0: bool MachMessage::SetData(void *data, michael@0: int32_t data_length) { michael@0: // first check to make sure we have enough space michael@0: size_t size = CalculateSize(); michael@0: size_t new_size = size + data_length; michael@0: michael@0: if (new_size > sizeof(MachMessage)) { michael@0: return false; // not enough space michael@0: } michael@0: michael@0: GetDataPacket()->data_length = EndianU32_NtoL(data_length); michael@0: if (data) memcpy(GetDataPacket()->data, data, data_length); michael@0: michael@0: CalculateSize(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: //============================================================================== michael@0: // calculates and returns the total size of the message michael@0: // Currently, the entire message MUST fit inside of the MachMessage michael@0: // messsage size <= sizeof(MachMessage) michael@0: mach_msg_size_t MachMessage::CalculateSize() { michael@0: size_t size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t); michael@0: michael@0: // add space for MessageDataPacket michael@0: int32_t alignedDataLength = (GetDataLength() + 3) & ~0x3; michael@0: size += 2*sizeof(int32_t) + alignedDataLength; michael@0: michael@0: // add space for descriptors michael@0: size += GetDescriptorCount() * sizeof(MachMsgPortDescriptor); michael@0: michael@0: head.msgh_size = static_cast(size); michael@0: michael@0: return head.msgh_size; michael@0: } michael@0: michael@0: //============================================================================== michael@0: MachMessage::MessageDataPacket *MachMessage::GetDataPacket() { michael@0: size_t desc_size = sizeof(MachMsgPortDescriptor)*GetDescriptorCount(); michael@0: MessageDataPacket *packet = michael@0: reinterpret_cast(padding + desc_size); michael@0: michael@0: return packet; michael@0: } michael@0: michael@0: //============================================================================== michael@0: void MachMessage::SetDescriptor(int n, michael@0: const MachMsgPortDescriptor &desc) { michael@0: MachMsgPortDescriptor *desc_array = michael@0: reinterpret_cast(padding); michael@0: desc_array[n] = desc; michael@0: } michael@0: michael@0: //============================================================================== michael@0: // returns true if successful otherwise there was not enough space michael@0: bool MachMessage::AddDescriptor(const MachMsgPortDescriptor &desc) { michael@0: // first check to make sure we have enough space michael@0: int size = CalculateSize(); michael@0: size_t new_size = size + sizeof(MachMsgPortDescriptor); michael@0: michael@0: if (new_size > sizeof(MachMessage)) { michael@0: return false; // not enough space michael@0: } michael@0: michael@0: // unfortunately, we need to move the data to allow space for the michael@0: // new descriptor michael@0: u_int8_t *p = reinterpret_cast(GetDataPacket()); michael@0: bcopy(p, p+sizeof(MachMsgPortDescriptor), GetDataLength()+2*sizeof(int32_t)); michael@0: michael@0: SetDescriptor(GetDescriptorCount(), desc); michael@0: SetDescriptorCount(GetDescriptorCount() + 1); michael@0: michael@0: CalculateSize(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: //============================================================================== michael@0: void MachMessage::SetDescriptorCount(int n) { michael@0: body.msgh_descriptor_count = n; michael@0: michael@0: if (n > 0) { michael@0: head.msgh_bits |= MACH_MSGH_BITS_COMPLEX; michael@0: } else { michael@0: head.msgh_bits &= ~MACH_MSGH_BITS_COMPLEX; michael@0: } michael@0: } michael@0: michael@0: //============================================================================== michael@0: MachMsgPortDescriptor *MachMessage::GetDescriptor(int n) { michael@0: if (n < GetDescriptorCount()) { michael@0: MachMsgPortDescriptor *desc = michael@0: reinterpret_cast(padding); michael@0: return desc + n; michael@0: } michael@0: michael@0: return nil; michael@0: } michael@0: michael@0: //============================================================================== michael@0: mach_port_t MachMessage::GetTranslatedPort(int n) { michael@0: if (n < GetDescriptorCount()) { michael@0: return GetDescriptor(n)->GetMachPort(); michael@0: } michael@0: return MACH_PORT_NULL; michael@0: } michael@0: michael@0: #pragma mark - michael@0: michael@0: //============================================================================== michael@0: // create a new mach port for receiving messages and register a name for it michael@0: ReceivePort::ReceivePort(const char *receive_port_name) { michael@0: mach_port_t current_task = mach_task_self(); michael@0: michael@0: init_result_ = mach_port_allocate(current_task, michael@0: MACH_PORT_RIGHT_RECEIVE, michael@0: &port_); michael@0: michael@0: if (init_result_ != KERN_SUCCESS) michael@0: return; michael@0: michael@0: init_result_ = mach_port_insert_right(current_task, michael@0: port_, michael@0: port_, michael@0: MACH_MSG_TYPE_MAKE_SEND); michael@0: michael@0: if (init_result_ != KERN_SUCCESS) michael@0: return; michael@0: michael@0: mach_port_t task_bootstrap_port = 0; michael@0: init_result_ = task_get_bootstrap_port(current_task, &task_bootstrap_port); michael@0: michael@0: if (init_result_ != KERN_SUCCESS) michael@0: return; michael@0: michael@0: init_result_ = breakpad::BootstrapRegister( michael@0: bootstrap_port, michael@0: const_cast(receive_port_name), michael@0: port_); michael@0: } michael@0: michael@0: //============================================================================== michael@0: // create a new mach port for receiving messages michael@0: ReceivePort::ReceivePort() { michael@0: mach_port_t current_task = mach_task_self(); michael@0: michael@0: init_result_ = mach_port_allocate(current_task, michael@0: MACH_PORT_RIGHT_RECEIVE, michael@0: &port_); michael@0: michael@0: if (init_result_ != KERN_SUCCESS) michael@0: return; michael@0: michael@0: init_result_ = mach_port_insert_right(current_task, michael@0: port_, michael@0: port_, michael@0: MACH_MSG_TYPE_MAKE_SEND); michael@0: } michael@0: michael@0: //============================================================================== michael@0: // Given an already existing mach port, use it. We take ownership of the michael@0: // port and deallocate it in our destructor. michael@0: ReceivePort::ReceivePort(mach_port_t receive_port) michael@0: : port_(receive_port), michael@0: init_result_(KERN_SUCCESS) { michael@0: } michael@0: michael@0: //============================================================================== michael@0: ReceivePort::~ReceivePort() { michael@0: if (init_result_ == KERN_SUCCESS) michael@0: mach_port_deallocate(mach_task_self(), port_); michael@0: } michael@0: michael@0: //============================================================================== michael@0: kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message, michael@0: mach_msg_timeout_t timeout) { michael@0: if (!out_message) { michael@0: return KERN_INVALID_ARGUMENT; michael@0: } michael@0: michael@0: // return any error condition encountered in constructor michael@0: if (init_result_ != KERN_SUCCESS) michael@0: return init_result_; michael@0: michael@0: out_message->head.msgh_bits = 0; michael@0: out_message->head.msgh_local_port = port_; michael@0: out_message->head.msgh_remote_port = MACH_PORT_NULL; michael@0: out_message->head.msgh_reserved = 0; michael@0: out_message->head.msgh_id = 0; michael@0: michael@0: mach_msg_option_t options = MACH_RCV_MSG; michael@0: if (timeout != MACH_MSG_TIMEOUT_NONE) michael@0: options |= MACH_RCV_TIMEOUT; michael@0: kern_return_t result = mach_msg(&out_message->head, michael@0: options, michael@0: 0, michael@0: sizeof(MachMessage), michael@0: port_, michael@0: timeout, // timeout in ms michael@0: MACH_PORT_NULL); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: #pragma mark - michael@0: michael@0: //============================================================================== michael@0: // get a port with send rights corresponding to a named registered service michael@0: MachPortSender::MachPortSender(const char *receive_port_name) { michael@0: mach_port_t task_bootstrap_port = 0; michael@0: init_result_ = task_get_bootstrap_port(mach_task_self(), michael@0: &task_bootstrap_port); michael@0: michael@0: if (init_result_ != KERN_SUCCESS) michael@0: return; michael@0: michael@0: init_result_ = bootstrap_look_up(task_bootstrap_port, michael@0: const_cast(receive_port_name), michael@0: &send_port_); michael@0: } michael@0: michael@0: //============================================================================== michael@0: MachPortSender::MachPortSender(mach_port_t send_port) michael@0: : send_port_(send_port), michael@0: init_result_(KERN_SUCCESS) { michael@0: } michael@0: michael@0: //============================================================================== michael@0: kern_return_t MachPortSender::SendMessage(MachSendMessage &message, michael@0: mach_msg_timeout_t timeout) { michael@0: if (message.head.msgh_size == 0) { michael@0: return KERN_INVALID_VALUE; // just for safety -- never should occur michael@0: }; michael@0: michael@0: if (init_result_ != KERN_SUCCESS) michael@0: return init_result_; michael@0: michael@0: message.head.msgh_remote_port = send_port_; michael@0: michael@0: kern_return_t result = mach_msg(&message.head, michael@0: MACH_SEND_MSG | MACH_SEND_TIMEOUT, michael@0: message.head.msgh_size, michael@0: 0, michael@0: MACH_PORT_NULL, michael@0: timeout, // timeout in ms michael@0: MACH_PORT_NULL); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: } // namespace google_breakpad