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
307