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