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 37 namespace google_breakpad { 38 //============================================================================== 39 MachSendMessage::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 55 bool 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) 77 mach_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 //============================================================================== 93 MachMessage::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 //============================================================================== 102 void 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 111 bool 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 //============================================================================== 134 void 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 //============================================================================== 145 MachMsgPortDescriptor *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 //============================================================================== 156 mach_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 167 ReceivePort::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 199 ReceivePort::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. 218 ReceivePort::ReceivePort(mach_port_t receive_port) 219 : port_(receive_port), 220 init_result_(KERN_SUCCESS) { 221 } 222 223 //============================================================================== 224 ReceivePort::~ReceivePort() { 225 if (init_result_ == KERN_SUCCESS) 226 mach_port_deallocate(mach_task_self(), port_); 227 } 228 229 //============================================================================== 230 kern_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 264 MachPortSender::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 //============================================================================== 278 MachPortSender::MachPortSender(mach_port_t send_port) 279 : send_port_(send_port), 280 init_result_(KERN_SUCCESS) { 281 } 282 283 //============================================================================== 284 kern_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