MachIPC.mm
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 36 namespace google_breakpad { 37 //============================================================================== 38 MachSendMessage::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 54 bool 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) 76 mach_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 //============================================================================== 92 MachMessage::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 //============================================================================== 101 void 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 110 bool 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 //============================================================================== 133 void 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 //============================================================================== 144 MachMsgPortDescriptor* 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 //============================================================================== 155 mach_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 166 ReceivePort::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 198 ReceivePort::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. 217 ReceivePort::ReceivePort(mach_port_t receive_port) 218 : port_(receive_port), 219 init_result_(KERN_SUCCESS) { 220 } 221 222 //============================================================================== 223 ReceivePort::~ReceivePort() { 224 if (init_result_ == KERN_SUCCESS) 225 mach_port_deallocate(mach_task_self(), port_); 226 } 227 228 //============================================================================== 229 kern_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 263 MachPortSender::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 //============================================================================== 277 MachPortSender::MachPortSender(mach_port_t send_port) 278 : send_port_(send_port), 279 init_result_(KERN_SUCCESS) { 280 } 281 282 //============================================================================== 283 kern_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