PageRenderTime 17ms CodeModel.GetById 12ms app.highlight 1ms RepoModel.GetById 1ms app.codeStats 0ms

/thirdparty/breakpad/common/mac/MachIPC.mm

http://github.com/tomahawk-player/tomahawk
Objective C++ | 306 lines | 180 code | 60 blank | 66 comment | 28 complexity | bf520cb0081aa277ac096b18e1130b22 MD5 | raw file
  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