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