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