1// Copyright (c) 2007, Google Inc. 2// All rights reserved. 3// 4// Redistribution and use in source and binary forms, with or without 5// modification, are permitted provided that the following conditions are 6// met: 7// 8// * Redistributions of source code must retain the above copyright 9// notice, this list of conditions and the following disclaimer. 10// * Redistributions in binary form must reproduce the above 11// copyright notice, this list of conditions and the following disclaimer 12// in the documentation and/or other materials provided with the 13// distribution. 14// * Neither the name of Google Inc. nor the names of its 15// contributors may be used to endorse or promote products derived from 16// this software without specific prior written permission. 17// 18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29// 30// MachIPC.mm 31// Wrapper for mach IPC calls 32 33#import <stdio.h> 34#import "MachIPC.h" 35#include "common/mac/bootstrap_compat.h" 36 37namespace google_breakpad { 38//============================================================================== 39MachSendMessage::MachSendMessage(int32_t message_id) : MachMessage() { 40 head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); 41 42 // head.msgh_remote_port = ...; // filled out in MachPortSender::SendMessage() 43 head.msgh_local_port = MACH_PORT_NULL; 44 head.msgh_reserved = 0; 45 head.msgh_id = 0; 46 47 SetDescriptorCount(0); // start out with no descriptors 48 49 SetMessageID(message_id); 50 SetData(NULL, 0); // client may add data later 51} 52 53//============================================================================== 54// returns true if successful 55bool MachMessage::SetData(void *data, 56 int32_t data_length) { 57 // first check to make sure we have enough space 58 size_t size = CalculateSize(); 59 size_t new_size = size + data_length; 60 61 if (new_size > sizeof(MachMessage)) { 62 return false; // not enough space 63 } 64 65 GetDataPacket()->data_length = EndianU32_NtoL(data_length); 66 if (data) memcpy(GetDataPacket()->data, data, data_length); 67 68 CalculateSize(); 69 70 return true; 71} 72 73//============================================================================== 74// calculates and returns the total size of the message 75// Currently, the entire message MUST fit inside of the MachMessage 76// messsage size <= sizeof(MachMessage) 77mach_msg_size_t MachMessage::CalculateSize() { 78 size_t size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t); 79 80 // add space for MessageDataPacket 81 int32_t alignedDataLength = (GetDataLength() + 3) & ~0x3; 82 size += 2*sizeof(int32_t) + alignedDataLength; 83 84 // add space for descriptors 85 size += GetDescriptorCount() * sizeof(MachMsgPortDescriptor); 86 87 head.msgh_size = static_cast<mach_msg_size_t>(size); 88 89 return head.msgh_size; 90} 91 92//============================================================================== 93MachMessage::MessageDataPacket *MachMessage::GetDataPacket() { 94 size_t desc_size = sizeof(MachMsgPortDescriptor)*GetDescriptorCount(); 95 MessageDataPacket *packet = 96 reinterpret_cast<MessageDataPacket*>(padding + desc_size); 97 98 return packet; 99} 100 101//============================================================================== 102void MachMessage::SetDescriptor(int n, 103 const MachMsgPortDescriptor &desc) { 104 MachMsgPortDescriptor *desc_array = 105 reinterpret_cast<MachMsgPortDescriptor*>(padding); 106 desc_array[n] = desc; 107} 108 109//============================================================================== 110// returns true if successful otherwise there was not enough space 111bool MachMessage::AddDescriptor(const MachMsgPortDescriptor &desc) { 112 // first check to make sure we have enough space 113 int size = CalculateSize(); 114 size_t new_size = size + sizeof(MachMsgPortDescriptor); 115 116 if (new_size > sizeof(MachMessage)) { 117 return false; // not enough space 118 } 119 120 // unfortunately, we need to move the data to allow space for the 121 // new descriptor 122 u_int8_t *p = reinterpret_cast<u_int8_t*>(GetDataPacket()); 123 bcopy(p, p+sizeof(MachMsgPortDescriptor), GetDataLength()+2*sizeof(int32_t)); 124 125 SetDescriptor(GetDescriptorCount(), desc); 126 SetDescriptorCount(GetDescriptorCount() + 1); 127 128 CalculateSize(); 129 130 return true; 131} 132 133//============================================================================== 134void MachMessage::SetDescriptorCount(int n) { 135 body.msgh_descriptor_count = n; 136 137 if (n > 0) { 138 head.msgh_bits |= MACH_MSGH_BITS_COMPLEX; 139 } else { 140 head.msgh_bits &= ~MACH_MSGH_BITS_COMPLEX; 141 } 142} 143 144//============================================================================== 145MachMsgPortDescriptor *MachMessage::GetDescriptor(int n) { 146 if (n < GetDescriptorCount()) { 147 MachMsgPortDescriptor *desc = 148 reinterpret_cast<MachMsgPortDescriptor*>(padding); 149 return desc + n; 150 } 151 152 return nil; 153} 154 155//============================================================================== 156mach_port_t MachMessage::GetTranslatedPort(int n) { 157 if (n < GetDescriptorCount()) { 158 return GetDescriptor(n)->GetMachPort(); 159 } 160 return MACH_PORT_NULL; 161} 162 163#pragma mark - 164 165//============================================================================== 166// create a new mach port for receiving messages and register a name for it 167ReceivePort::ReceivePort(const char *receive_port_name) { 168 mach_port_t current_task = mach_task_self(); 169 170 init_result_ = mach_port_allocate(current_task, 171 MACH_PORT_RIGHT_RECEIVE, 172 &port_); 173 174 if (init_result_ != KERN_SUCCESS) 175 return; 176 177 init_result_ = mach_port_insert_right(current_task, 178 port_, 179 port_, 180 MACH_MSG_TYPE_MAKE_SEND); 181 182 if (init_result_ != KERN_SUCCESS) 183 return; 184 185 mach_port_t task_bootstrap_port = 0; 186 init_result_ = task_get_bootstrap_port(current_task, &task_bootstrap_port); 187 188 if (init_result_ != KERN_SUCCESS) 189 return; 190 191 init_result_ = breakpad::BootstrapRegister( 192 bootstrap_port, 193 const_cast<char*>(receive_port_name), 194 port_); 195} 196 197//============================================================================== 198// create a new mach port for receiving messages 199ReceivePort::ReceivePort() { 200 mach_port_t current_task = mach_task_self(); 201 202 init_result_ = mach_port_allocate(current_task, 203 MACH_PORT_RIGHT_RECEIVE, 204 &port_); 205 206 if (init_result_ != KERN_SUCCESS) 207 return; 208 209 init_result_ = mach_port_insert_right(current_task, 210 port_, 211 port_, 212 MACH_MSG_TYPE_MAKE_SEND); 213} 214 215//============================================================================== 216// Given an already existing mach port, use it. We take ownership of the 217// port and deallocate it in our destructor. 218ReceivePort::ReceivePort(mach_port_t receive_port) 219 : port_(receive_port), 220 init_result_(KERN_SUCCESS) { 221} 222 223//============================================================================== 224ReceivePort::~ReceivePort() { 225 if (init_result_ == KERN_SUCCESS) 226 mach_port_deallocate(mach_task_self(), port_); 227} 228 229//============================================================================== 230kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message, 231 mach_msg_timeout_t timeout) { 232 if (!out_message) { 233 return KERN_INVALID_ARGUMENT; 234 } 235 236 // return any error condition encountered in constructor 237 if (init_result_ != KERN_SUCCESS) 238 return init_result_; 239 240 out_message->head.msgh_bits = 0; 241 out_message->head.msgh_local_port = port_; 242 out_message->head.msgh_remote_port = MACH_PORT_NULL; 243 out_message->head.msgh_reserved = 0; 244 out_message->head.msgh_id = 0; 245 246 mach_msg_option_t options = MACH_RCV_MSG; 247 if (timeout != MACH_MSG_TIMEOUT_NONE) 248 options |= MACH_RCV_TIMEOUT; 249 kern_return_t result = mach_msg(&out_message->head, 250 options, 251 0, 252 sizeof(MachMessage), 253 port_, 254 timeout, // timeout in ms 255 MACH_PORT_NULL); 256 257 return result; 258} 259 260#pragma mark - 261 262//============================================================================== 263// get a port with send rights corresponding to a named registered service 264MachPortSender::MachPortSender(const char *receive_port_name) { 265 mach_port_t task_bootstrap_port = 0; 266 init_result_ = task_get_bootstrap_port(mach_task_self(), 267 &task_bootstrap_port); 268 269 if (init_result_ != KERN_SUCCESS) 270 return; 271 272 init_result_ = bootstrap_look_up(task_bootstrap_port, 273 const_cast<char*>(receive_port_name), 274 &send_port_); 275} 276 277//============================================================================== 278MachPortSender::MachPortSender(mach_port_t send_port) 279 : send_port_(send_port), 280 init_result_(KERN_SUCCESS) { 281} 282 283//============================================================================== 284kern_return_t MachPortSender::SendMessage(MachSendMessage &message, 285 mach_msg_timeout_t timeout) { 286 if (message.head.msgh_size == 0) { 287 return KERN_INVALID_VALUE; // just for safety -- never should occur 288 }; 289 290 if (init_result_ != KERN_SUCCESS) 291 return init_result_; 292 293 message.head.msgh_remote_port = send_port_; 294 295 kern_return_t result = mach_msg(&message.head, 296 MACH_SEND_MSG | MACH_SEND_TIMEOUT, 297 message.head.msgh_size, 298 0, 299 MACH_PORT_NULL, 300 timeout, // timeout in ms 301 MACH_PORT_NULL); 302 303 return result; 304} 305 306} // namespace google_breakpad 307