| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/mach_ipc_mac.h" |
| |
| #import <Foundation/Foundation.h> |
| #include <mach/vm_map.h> |
| |
| #include <stdio.h> |
| #include "base/logging.h" |
| |
| namespace base { |
| |
| // static |
| const size_t MachMessage::kEmptyMessageSize = sizeof(mach_msg_header_t) + |
| sizeof(mach_msg_body_t) + sizeof(MessageDataPacket); |
| |
| //============================================================================== |
| MachSendMessage::MachSendMessage(int32_t message_id) : MachMessage() { |
| Initialize(message_id); |
| } |
| |
| MachSendMessage::MachSendMessage(void *storage, size_t storage_length, |
| int32_t message_id) |
| : MachMessage(storage, storage_length) { |
| Initialize(message_id); |
| } |
| |
| void MachSendMessage::Initialize(int32_t message_id) { |
| 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 |
| } |
| |
| //============================================================================== |
| MachMessage::MachMessage() |
| : storage_(new MachMessageData), // Allocate storage_ ourselves |
| storage_length_bytes_(sizeof(MachMessageData)), |
| own_storage_(true) { |
| memset(storage_, 0, storage_length_bytes_); |
| } |
| |
| //============================================================================== |
| MachMessage::MachMessage(void *storage, size_t storage_length) |
| : storage_(static_cast<MachMessageData*>(storage)), |
| storage_length_bytes_(storage_length), |
| own_storage_(false) { |
| DCHECK(storage); |
| DCHECK_GE(storage_length, kEmptyMessageSize); |
| } |
| |
| //============================================================================== |
| MachMessage::~MachMessage() { |
| if (own_storage_) { |
| delete storage_; |
| storage_ = NULL; |
| } |
| } |
| |
| //============================================================================== |
| // returns true if successful |
| bool MachMessage::SetData(const void* data, |
| int32_t data_length) { |
| // Enforce the fact that it's only safe to call this method once on a |
| // message. |
| DCHECK(GetDataPacket()->data_length == 0); |
| |
| // first check to make sure we have enough space |
| int size = CalculateSize(); |
| int new_size = size + data_length; |
| |
| if ((unsigned)new_size > storage_length_bytes_) { |
| return false; // not enough space |
| } |
| |
| GetDataPacket()->data_length = EndianU32_NtoL(data_length); |
| if (data) memcpy(GetDataPacket()->data, data, data_length); |
| |
| // Update the Mach header with the new aligned size of the message. |
| CalculateSize(); |
| |
| return true; |
| } |
| |
| //============================================================================== |
| // calculates and returns the total size of the message |
| // Currently, the entire message MUST fit inside of the MachMessage |
| // messsage size <= EmptyMessageSize() |
| int MachMessage::CalculateSize() { |
| int 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 = size; |
| |
| return size; |
| } |
| |
| //============================================================================== |
| MachMessage::MessageDataPacket *MachMessage::GetDataPacket() { |
| int desc_size = sizeof(MachMsgPortDescriptor)*GetDescriptorCount(); |
| MessageDataPacket *packet = |
| reinterpret_cast<MessageDataPacket*>(storage_->padding + desc_size); |
| |
| return packet; |
| } |
| |
| //============================================================================== |
| void MachMessage::SetDescriptor(int n, |
| const MachMsgPortDescriptor &desc) { |
| MachMsgPortDescriptor *desc_array = |
| reinterpret_cast<MachMsgPortDescriptor*>(storage_->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(); |
| int new_size = size + sizeof(MachMsgPortDescriptor); |
| |
| if ((unsigned)new_size > storage_length_bytes_) { |
| 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) { |
| storage_->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*>(storage_->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; |
| |
| // Without |NSMachPortDeallocateNone|, the NSMachPort seems to deallocate |
| // receive rights on port when it is eventually released. It is not necessary |
| // to deallocate any rights here as |port_| is fully deallocated in the |
| // ReceivePort destructor. |
| NSPort *ns_port = [NSMachPort portWithMachPort:port_ |
| options:NSMachPortDeallocateNone]; |
| NSString *port_name = [NSString stringWithUTF8String:receive_port_name]; |
| [[NSMachBootstrapServer sharedInstance] registerPort:ns_port name:port_name]; |
| } |
| |
| //============================================================================== |
| // 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 rcv_options = MACH_RCV_MSG; |
| if (timeout != MACH_MSG_TIMEOUT_NONE) |
| rcv_options |= MACH_RCV_TIMEOUT; |
| |
| kern_return_t result = mach_msg(out_message->Head(), |
| rcv_options, |
| 0, |
| out_message->MaxSize(), |
| 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 bootstrap_port = 0; |
| init_result_ = task_get_bootstrap_port(mach_task_self(), &bootstrap_port); |
| |
| if (init_result_ != KERN_SUCCESS) |
| return; |
| |
| init_result_ = bootstrap_look_up(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) { |
| NOTREACHED(); |
| 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 mac { |
| |
| kern_return_t GetNumberOfMachPorts(mach_port_t task_port, int* num_ports) { |
| mach_port_name_array_t names; |
| mach_msg_type_number_t names_count; |
| mach_port_type_array_t types; |
| mach_msg_type_number_t types_count; |
| |
| // A friendlier interface would allow NULL buffers to only get the counts. |
| kern_return_t kr = mach_port_names(task_port, &names, &names_count, |
| &types, &types_count); |
| if (kr != KERN_SUCCESS) |
| return kr; |
| |
| // The documentation states this is an invariant. |
| DCHECK_EQ(names_count, types_count); |
| *num_ports = names_count; |
| |
| kr = vm_deallocate(mach_task_self(), |
| reinterpret_cast<vm_address_t>(names), |
| names_count * sizeof(mach_port_name_array_t)); |
| kr = vm_deallocate(mach_task_self(), |
| reinterpret_cast<vm_address_t>(types), |
| types_count * sizeof(mach_port_type_array_t)); |
| |
| return kr; |
| } |
| |
| } // namespace mac |
| |
| } // namespace base |