// Copyright (c) 2011 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 <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 base