1// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "base/mach_ipc_mac.h" 6 7#import <Foundation/Foundation.h> 8 9#include <stdio.h> 10#include "base/logging.h" 11 12namespace base { 13 14//============================================================================== 15MachSendMessage::MachSendMessage(int32_t message_id) : MachMessage() { 16 Initialize(message_id); 17} 18 19MachSendMessage::MachSendMessage(void *storage, size_t storage_length, 20 int32_t message_id) 21 : MachMessage(storage, storage_length) { 22 Initialize(message_id); 23} 24 25void MachSendMessage::Initialize(int32_t message_id) { 26 Head()->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); 27 28 // head.msgh_remote_port = ...; // filled out in MachPortSender::SendMessage() 29 Head()->msgh_local_port = MACH_PORT_NULL; 30 Head()->msgh_reserved = 0; 31 Head()->msgh_id = 0; 32 33 SetDescriptorCount(0); // start out with no descriptors 34 35 SetMessageID(message_id); 36 SetData(NULL, 0); // client may add data later 37} 38 39//============================================================================== 40MachMessage::MachMessage() 41 : storage_(new MachMessageData), // Allocate storage_ ourselves 42 storage_length_bytes_(sizeof(MachMessageData)), 43 own_storage_(true) { 44 memset(storage_, 0, storage_length_bytes_); 45} 46 47//============================================================================== 48MachMessage::MachMessage(void *storage, size_t storage_length) 49 : storage_(static_cast<MachMessageData*>(storage)), 50 storage_length_bytes_(storage_length), 51 own_storage_(false) { 52 DCHECK(storage); 53 DCHECK(storage_length >= kEmptyMessageSize); 54} 55 56//============================================================================== 57MachMessage::~MachMessage() { 58 if (own_storage_) { 59 delete storage_; 60 storage_ = NULL; 61 } 62} 63 64//============================================================================== 65// returns true if successful 66bool MachMessage::SetData(const void* data, 67 int32_t data_length) { 68 // Enforce the fact that it's only safe to call this method once on a 69 // message. 70 DCHECK(GetDataPacket()->data_length == 0); 71 72 // first check to make sure we have enough space 73 int size = CalculateSize(); 74 int new_size = size + data_length; 75 76 if ((unsigned)new_size > storage_length_bytes_) { 77 return false; // not enough space 78 } 79 80 GetDataPacket()->data_length = EndianU32_NtoL(data_length); 81 if (data) memcpy(GetDataPacket()->data, data, data_length); 82 83 // Update the Mach header with the new aligned size of the message. 84 CalculateSize(); 85 86 return true; 87} 88 89//============================================================================== 90// calculates and returns the total size of the message 91// Currently, the entire message MUST fit inside of the MachMessage 92// messsage size <= EmptyMessageSize() 93int MachMessage::CalculateSize() { 94 int size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t); 95 96 // add space for MessageDataPacket 97 int32_t alignedDataLength = (GetDataLength() + 3) & ~0x3; 98 size += 2*sizeof(int32_t) + alignedDataLength; 99 100 // add space for descriptors 101 size += GetDescriptorCount() * sizeof(MachMsgPortDescriptor); 102 103 Head()->msgh_size = size; 104 105 return size; 106} 107 108//============================================================================== 109MachMessage::MessageDataPacket *MachMessage::GetDataPacket() { 110 int desc_size = sizeof(MachMsgPortDescriptor)*GetDescriptorCount(); 111 MessageDataPacket *packet = 112 reinterpret_cast<MessageDataPacket*>(storage_->padding + desc_size); 113 114 return packet; 115} 116 117//============================================================================== 118void MachMessage::SetDescriptor(int n, 119 const MachMsgPortDescriptor &desc) { 120 MachMsgPortDescriptor *desc_array = 121 reinterpret_cast<MachMsgPortDescriptor*>(storage_->padding); 122 desc_array[n] = desc; 123} 124 125//============================================================================== 126// returns true if successful otherwise there was not enough space 127bool MachMessage::AddDescriptor(const MachMsgPortDescriptor &desc) { 128 // first check to make sure we have enough space 129 int size = CalculateSize(); 130 int new_size = size + sizeof(MachMsgPortDescriptor); 131 132 if ((unsigned)new_size > storage_length_bytes_) { 133 return false; // not enough space 134 } 135 136 // unfortunately, we need to move the data to allow space for the 137 // new descriptor 138 u_int8_t *p = reinterpret_cast<u_int8_t*>(GetDataPacket()); 139 bcopy(p, p+sizeof(MachMsgPortDescriptor), GetDataLength()+2*sizeof(int32_t)); 140 141 SetDescriptor(GetDescriptorCount(), desc); 142 SetDescriptorCount(GetDescriptorCount() + 1); 143 144 CalculateSize(); 145 146 return true; 147} 148 149//============================================================================== 150void MachMessage::SetDescriptorCount(int n) { 151 storage_->body.msgh_descriptor_count = n; 152 153 if (n > 0) { 154 Head()->msgh_bits |= MACH_MSGH_BITS_COMPLEX; 155 } else { 156 Head()->msgh_bits &= ~MACH_MSGH_BITS_COMPLEX; 157 } 158} 159 160//============================================================================== 161MachMsgPortDescriptor *MachMessage::GetDescriptor(int n) { 162 if (n < GetDescriptorCount()) { 163 MachMsgPortDescriptor *desc = 164 reinterpret_cast<MachMsgPortDescriptor*>(storage_->padding); 165 return desc + n; 166 } 167 168 return nil; 169} 170 171//============================================================================== 172mach_port_t MachMessage::GetTranslatedPort(int n) { 173 if (n < GetDescriptorCount()) { 174 return GetDescriptor(n)->GetMachPort(); 175 } 176 return MACH_PORT_NULL; 177} 178 179#pragma mark - 180 181//============================================================================== 182// create a new mach port for receiving messages and register a name for it 183ReceivePort::ReceivePort(const char *receive_port_name) { 184 mach_port_t current_task = mach_task_self(); 185 186 init_result_ = mach_port_allocate(current_task, 187 MACH_PORT_RIGHT_RECEIVE, 188 &port_); 189 190 if (init_result_ != KERN_SUCCESS) 191 return; 192 193 init_result_ = mach_port_insert_right(current_task, 194 port_, 195 port_, 196 MACH_MSG_TYPE_MAKE_SEND); 197 198 if (init_result_ != KERN_SUCCESS) 199 return; 200 201 NSPort *ns_port = [NSMachPort portWithMachPort:port_]; 202 NSString *port_name = [NSString stringWithUTF8String:receive_port_name]; 203 [[NSMachBootstrapServer sharedInstance] registerPort:ns_port name:port_name]; 204} 205 206//============================================================================== 207// create a new mach port for receiving messages 208ReceivePort::ReceivePort() { 209 mach_port_t current_task = mach_task_self(); 210 211 init_result_ = mach_port_allocate(current_task, 212 MACH_PORT_RIGHT_RECEIVE, 213 &port_); 214 215 if (init_result_ != KERN_SUCCESS) 216 return; 217 218 init_result_ = mach_port_insert_right(current_task, 219 port_, 220 port_, 221 MACH_MSG_TYPE_MAKE_SEND); 222} 223 224//============================================================================== 225// Given an already existing mach port, use it. We take ownership of the 226// port and deallocate it in our destructor. 227ReceivePort::ReceivePort(mach_port_t receive_port) 228 : port_(receive_port), 229 init_result_(KERN_SUCCESS) { 230} 231 232//============================================================================== 233ReceivePort::~ReceivePort() { 234 if (init_result_ == KERN_SUCCESS) 235 mach_port_deallocate(mach_task_self(), port_); 236} 237 238//============================================================================== 239kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message, 240 mach_msg_timeout_t timeout) { 241 if (!out_message) { 242 return KERN_INVALID_ARGUMENT; 243 } 244 245 // return any error condition encountered in constructor 246 if (init_result_ != KERN_SUCCESS) 247 return init_result_; 248 249 out_message->Head()->msgh_bits = 0; 250 out_message->Head()->msgh_local_port = port_; 251 out_message->Head()->msgh_remote_port = MACH_PORT_NULL; 252 out_message->Head()->msgh_reserved = 0; 253 out_message->Head()->msgh_id = 0; 254 255 kern_return_t result = mach_msg(out_message->Head(), 256 MACH_RCV_MSG | MACH_RCV_TIMEOUT, 257 0, 258 out_message->MaxSize(), 259 port_, 260 timeout, // timeout in ms 261 MACH_PORT_NULL); 262 263 return result; 264} 265 266#pragma mark - 267 268//============================================================================== 269// get a port with send rights corresponding to a named registered service 270MachPortSender::MachPortSender(const char *receive_port_name) { 271 mach_port_t bootstrap_port = 0; 272 init_result_ = task_get_bootstrap_port(mach_task_self(), &bootstrap_port); 273 274 if (init_result_ != KERN_SUCCESS) 275 return; 276 277 init_result_ = bootstrap_look_up(bootstrap_port, 278 const_cast<char*>(receive_port_name), 279 &send_port_); 280} 281 282//============================================================================== 283MachPortSender::MachPortSender(mach_port_t send_port) 284 : send_port_(send_port), 285 init_result_(KERN_SUCCESS) { 286} 287 288//============================================================================== 289kern_return_t MachPortSender::SendMessage(MachSendMessage &message, 290 mach_msg_timeout_t timeout) { 291 if (message.Head()->msgh_size == 0) { 292 NOTREACHED(); 293 return KERN_INVALID_VALUE; // just for safety -- never should occur 294 }; 295 296 if (init_result_ != KERN_SUCCESS) 297 return init_result_; 298 299 message.Head()->msgh_remote_port = send_port_; 300 301 kern_return_t result = mach_msg(message.Head(), 302 MACH_SEND_MSG | MACH_SEND_TIMEOUT, 303 message.Head()->msgh_size, 304 0, 305 MACH_PORT_NULL, 306 timeout, // timeout in ms 307 MACH_PORT_NULL); 308 309 return result; 310} 311 312} // namespace base 313