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 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_ = bootstrap_register(bootstrap_port, 191 const_cast<char*>(receive_port_name), 192 port_); 193} 194 195//============================================================================== 196// create a new mach port for receiving messages 197ReceivePort::ReceivePort() { 198 mach_port_t current_task = mach_task_self(); 199 200 init_result_ = mach_port_allocate(current_task, 201 MACH_PORT_RIGHT_RECEIVE, 202 &port_); 203 204 if (init_result_ != KERN_SUCCESS) 205 return; 206 207 init_result_ = mach_port_insert_right(current_task, 208 port_, 209 port_, 210 MACH_MSG_TYPE_MAKE_SEND); 211} 212 213//============================================================================== 214// Given an already existing mach port, use it. We take ownership of the 215// port and deallocate it in our destructor. 216ReceivePort::ReceivePort(mach_port_t receive_port) 217 : port_(receive_port), 218 init_result_(KERN_SUCCESS) { 219} 220 221//============================================================================== 222ReceivePort::~ReceivePort() { 223 if (init_result_ == KERN_SUCCESS) 224 mach_port_deallocate(mach_task_self(), port_); 225} 226 227//============================================================================== 228kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message, 229 mach_msg_timeout_t timeout) { 230 if (!out_message) { 231 return KERN_INVALID_ARGUMENT; 232 } 233 234 // return any error condition encountered in constructor 235 if (init_result_ != KERN_SUCCESS) 236 return init_result_; 237 238 out_message->head.msgh_bits = 0; 239 out_message->head.msgh_local_port = port_; 240 out_message->head.msgh_remote_port = MACH_PORT_NULL; 241 out_message->head.msgh_reserved = 0; 242 out_message->head.msgh_id = 0; 243 244 mach_msg_option_t options = MACH_RCV_MSG; 245 if (timeout != MACH_MSG_TIMEOUT_NONE) 246 options |= MACH_RCV_TIMEOUT; 247 kern_return_t result = mach_msg(&out_message->head, 248 options, 249 0, 250 sizeof(MachMessage), 251 port_, 252 timeout, // timeout in ms 253 MACH_PORT_NULL); 254 255 return result; 256} 257 258#pragma mark - 259 260//============================================================================== 261// get a port with send rights corresponding to a named registered service 262MachPortSender::MachPortSender(const char *receive_port_name) { 263 mach_port_t task_bootstrap_port = 0; 264 init_result_ = task_get_bootstrap_port(mach_task_self(), 265 &task_bootstrap_port); 266 267 if (init_result_ != KERN_SUCCESS) 268 return; 269 270 init_result_ = bootstrap_look_up(task_bootstrap_port, 271 const_cast<char*>(receive_port_name), 272 &send_port_); 273} 274 275//============================================================================== 276MachPortSender::MachPortSender(mach_port_t send_port) 277 : send_port_(send_port), 278 init_result_(KERN_SUCCESS) { 279} 280 281//============================================================================== 282kern_return_t MachPortSender::SendMessage(MachSendMessage &message, 283 mach_msg_timeout_t timeout) { 284 if (message.head.msgh_size == 0) { 285 return KERN_INVALID_VALUE; // just for safety -- never should occur 286 }; 287 288 if (init_result_ != KERN_SUCCESS) 289 return init_result_; 290 291 message.head.msgh_remote_port = send_port_; 292 293 kern_return_t result = mach_msg(&message.head, 294 MACH_SEND_MSG | MACH_SEND_TIMEOUT, 295 message.head.msgh_size, 296 0, 297 MACH_PORT_NULL, 298 timeout, // timeout in ms 299 MACH_PORT_NULL); 300 301 return result; 302} 303 304} // namespace google_breakpad 305