blob: 3d45dd7f87523d7a05e12f609d6f2237a18feee1 [file] [log] [blame]
[email protected]23c6c2b2012-01-28 00:51:421// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]369537e82008-11-19 18:27:562// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]c0028792010-01-12 00:39:155#include "base/mach_ipc_mac.h"
[email protected]5062bb02008-11-20 21:11:206
7#import <Foundation/Foundation.h>
[email protected]23c6c2b2012-01-28 00:51:428#include <mach/vm_map.h>
[email protected]5062bb02008-11-20 21:11:209
10#include <stdio.h>
11#include "base/logging.h"
[email protected]369537e82008-11-19 18:27:5612
[email protected]1fdb095d2010-01-12 21:33:4813namespace base {
14
[email protected]8e03fec2011-03-31 20:34:2515// static
[email protected]b4a45ea2011-03-31 20:52:0516const size_t MachMessage::kEmptyMessageSize = sizeof(mach_msg_header_t) +
[email protected]8e03fec2011-03-31 20:34:2517 sizeof(mach_msg_body_t) + sizeof(MessageDataPacket);
18
[email protected]369537e82008-11-19 18:27:5619//==============================================================================
20MachSendMessage::MachSendMessage(int32_t message_id) : MachMessage() {
[email protected]5062bb02008-11-20 21:11:2021 Initialize(message_id);
22}
23
24MachSendMessage::MachSendMessage(void *storage, size_t storage_length,
25 int32_t message_id)
26 : MachMessage(storage, storage_length) {
27 Initialize(message_id);
28}
29
30void MachSendMessage::Initialize(int32_t message_id) {
31 Head()->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
[email protected]369537e82008-11-19 18:27:5632
33 // head.msgh_remote_port = ...; // filled out in MachPortSender::SendMessage()
[email protected]5062bb02008-11-20 21:11:2034 Head()->msgh_local_port = MACH_PORT_NULL;
35 Head()->msgh_reserved = 0;
36 Head()->msgh_id = 0;
[email protected]369537e82008-11-19 18:27:5637
38 SetDescriptorCount(0); // start out with no descriptors
39
40 SetMessageID(message_id);
41 SetData(NULL, 0); // client may add data later
42}
43
44//==============================================================================
[email protected]5062bb02008-11-20 21:11:2045MachMessage::MachMessage()
46 : storage_(new MachMessageData), // Allocate storage_ ourselves
47 storage_length_bytes_(sizeof(MachMessageData)),
48 own_storage_(true) {
49 memset(storage_, 0, storage_length_bytes_);
50}
51
52//==============================================================================
53MachMessage::MachMessage(void *storage, size_t storage_length)
54 : storage_(static_cast<MachMessageData*>(storage)),
55 storage_length_bytes_(storage_length),
56 own_storage_(false) {
57 DCHECK(storage);
[email protected]8e03fec2011-03-31 20:34:2558 DCHECK_GE(storage_length, kEmptyMessageSize);
[email protected]5062bb02008-11-20 21:11:2059}
60
61//==============================================================================
62MachMessage::~MachMessage() {
63 if (own_storage_) {
64 delete storage_;
65 storage_ = NULL;
66 }
67}
68
69//==============================================================================
[email protected]369537e82008-11-19 18:27:5670// returns true if successful
[email protected]5062bb02008-11-20 21:11:2071bool MachMessage::SetData(const void* data,
[email protected]369537e82008-11-19 18:27:5672 int32_t data_length) {
[email protected]5062bb02008-11-20 21:11:2073 // Enforce the fact that it's only safe to call this method once on a
74 // message.
75 DCHECK(GetDataPacket()->data_length == 0);
76
[email protected]369537e82008-11-19 18:27:5677 // first check to make sure we have enough space
78 int size = CalculateSize();
79 int new_size = size + data_length;
[email protected]5062bb02008-11-20 21:11:2080
81 if ((unsigned)new_size > storage_length_bytes_) {
[email protected]369537e82008-11-19 18:27:5682 return false; // not enough space
83 }
84
85 GetDataPacket()->data_length = EndianU32_NtoL(data_length);
86 if (data) memcpy(GetDataPacket()->data, data, data_length);
87
[email protected]5062bb02008-11-20 21:11:2088 // Update the Mach header with the new aligned size of the message.
[email protected]369537e82008-11-19 18:27:5689 CalculateSize();
90
91 return true;
92}
93
94//==============================================================================
95// calculates and returns the total size of the message
96// Currently, the entire message MUST fit inside of the MachMessage
[email protected]5062bb02008-11-20 21:11:2097// messsage size <= EmptyMessageSize()
[email protected]369537e82008-11-19 18:27:5698int MachMessage::CalculateSize() {
99 int size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t);
[email protected]5062bb02008-11-20 21:11:20100
[email protected]369537e82008-11-19 18:27:56101 // add space for MessageDataPacket
102 int32_t alignedDataLength = (GetDataLength() + 3) & ~0x3;
103 size += 2*sizeof(int32_t) + alignedDataLength;
[email protected]5062bb02008-11-20 21:11:20104
[email protected]369537e82008-11-19 18:27:56105 // add space for descriptors
106 size += GetDescriptorCount() * sizeof(MachMsgPortDescriptor);
[email protected]5062bb02008-11-20 21:11:20107
108 Head()->msgh_size = size;
109
[email protected]369537e82008-11-19 18:27:56110 return size;
111}
112
113//==============================================================================
114MachMessage::MessageDataPacket *MachMessage::GetDataPacket() {
115 int desc_size = sizeof(MachMsgPortDescriptor)*GetDescriptorCount();
116 MessageDataPacket *packet =
[email protected]5062bb02008-11-20 21:11:20117 reinterpret_cast<MessageDataPacket*>(storage_->padding + desc_size);
[email protected]369537e82008-11-19 18:27:56118
119 return packet;
120}
121
122//==============================================================================
123void MachMessage::SetDescriptor(int n,
124 const MachMsgPortDescriptor &desc) {
125 MachMsgPortDescriptor *desc_array =
[email protected]5062bb02008-11-20 21:11:20126 reinterpret_cast<MachMsgPortDescriptor*>(storage_->padding);
[email protected]369537e82008-11-19 18:27:56127 desc_array[n] = desc;
128}
129
130//==============================================================================
131// returns true if successful otherwise there was not enough space
132bool MachMessage::AddDescriptor(const MachMsgPortDescriptor &desc) {
133 // first check to make sure we have enough space
134 int size = CalculateSize();
135 int new_size = size + sizeof(MachMsgPortDescriptor);
[email protected]5062bb02008-11-20 21:11:20136
137 if ((unsigned)new_size > storage_length_bytes_) {
[email protected]369537e82008-11-19 18:27:56138 return false; // not enough space
139 }
140
141 // unfortunately, we need to move the data to allow space for the
142 // new descriptor
143 u_int8_t *p = reinterpret_cast<u_int8_t*>(GetDataPacket());
144 bcopy(p, p+sizeof(MachMsgPortDescriptor), GetDataLength()+2*sizeof(int32_t));
[email protected]5062bb02008-11-20 21:11:20145
[email protected]369537e82008-11-19 18:27:56146 SetDescriptor(GetDescriptorCount(), desc);
147 SetDescriptorCount(GetDescriptorCount() + 1);
148
149 CalculateSize();
[email protected]5062bb02008-11-20 21:11:20150
[email protected]369537e82008-11-19 18:27:56151 return true;
152}
153
154//==============================================================================
155void MachMessage::SetDescriptorCount(int n) {
[email protected]5062bb02008-11-20 21:11:20156 storage_->body.msgh_descriptor_count = n;
[email protected]369537e82008-11-19 18:27:56157
158 if (n > 0) {
[email protected]5062bb02008-11-20 21:11:20159 Head()->msgh_bits |= MACH_MSGH_BITS_COMPLEX;
[email protected]369537e82008-11-19 18:27:56160 } else {
[email protected]5062bb02008-11-20 21:11:20161 Head()->msgh_bits &= ~MACH_MSGH_BITS_COMPLEX;
[email protected]369537e82008-11-19 18:27:56162 }
163}
164
165//==============================================================================
166MachMsgPortDescriptor *MachMessage::GetDescriptor(int n) {
167 if (n < GetDescriptorCount()) {
168 MachMsgPortDescriptor *desc =
[email protected]5062bb02008-11-20 21:11:20169 reinterpret_cast<MachMsgPortDescriptor*>(storage_->padding);
[email protected]369537e82008-11-19 18:27:56170 return desc + n;
171 }
[email protected]5062bb02008-11-20 21:11:20172
[email protected]369537e82008-11-19 18:27:56173 return nil;
174}
175
176//==============================================================================
177mach_port_t MachMessage::GetTranslatedPort(int n) {
178 if (n < GetDescriptorCount()) {
179 return GetDescriptor(n)->GetMachPort();
180 }
181 return MACH_PORT_NULL;
182}
183
184#pragma mark -
185
186//==============================================================================
187// create a new mach port for receiving messages and register a name for it
188ReceivePort::ReceivePort(const char *receive_port_name) {
189 mach_port_t current_task = mach_task_self();
190
191 init_result_ = mach_port_allocate(current_task,
192 MACH_PORT_RIGHT_RECEIVE,
193 &port_);
194
195 if (init_result_ != KERN_SUCCESS)
196 return;
[email protected]5062bb02008-11-20 21:11:20197
[email protected]369537e82008-11-19 18:27:56198 init_result_ = mach_port_insert_right(current_task,
199 port_,
200 port_,
201 MACH_MSG_TYPE_MAKE_SEND);
202
203 if (init_result_ != KERN_SUCCESS)
204 return;
205
[email protected]b88a7492010-09-17 12:28:32206 // Without |NSMachPortDeallocateNone|, the NSMachPort seems to deallocate
207 // receive rights on port when it is eventually released. It is not necessary
208 // to deallocate any rights here as |port_| is fully deallocated in the
209 // ReceivePort destructor.
210 NSPort *ns_port = [NSMachPort portWithMachPort:port_
211 options:NSMachPortDeallocateNone];
[email protected]5062bb02008-11-20 21:11:20212 NSString *port_name = [NSString stringWithUTF8String:receive_port_name];
213 [[NSMachBootstrapServer sharedInstance] registerPort:ns_port name:port_name];
[email protected]369537e82008-11-19 18:27:56214}
215
216//==============================================================================
217// create a new mach port for receiving messages
218ReceivePort::ReceivePort() {
219 mach_port_t current_task = mach_task_self();
220
221 init_result_ = mach_port_allocate(current_task,
222 MACH_PORT_RIGHT_RECEIVE,
223 &port_);
224
225 if (init_result_ != KERN_SUCCESS)
226 return;
227
[email protected]3740cb9b52009-12-19 04:50:04228 init_result_ = mach_port_insert_right(current_task,
229 port_,
230 port_,
231 MACH_MSG_TYPE_MAKE_SEND);
[email protected]369537e82008-11-19 18:27:56232}
233
234//==============================================================================
235// Given an already existing mach port, use it. We take ownership of the
236// port and deallocate it in our destructor.
237ReceivePort::ReceivePort(mach_port_t receive_port)
238 : port_(receive_port),
239 init_result_(KERN_SUCCESS) {
240}
241
242//==============================================================================
243ReceivePort::~ReceivePort() {
244 if (init_result_ == KERN_SUCCESS)
245 mach_port_deallocate(mach_task_self(), port_);
246}
247
248//==============================================================================
249kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message,
250 mach_msg_timeout_t timeout) {
251 if (!out_message) {
252 return KERN_INVALID_ARGUMENT;
253 }
254
255 // return any error condition encountered in constructor
256 if (init_result_ != KERN_SUCCESS)
257 return init_result_;
[email protected]369537e82008-11-19 18:27:56258
[email protected]5062bb02008-11-20 21:11:20259 out_message->Head()->msgh_bits = 0;
260 out_message->Head()->msgh_local_port = port_;
261 out_message->Head()->msgh_remote_port = MACH_PORT_NULL;
262 out_message->Head()->msgh_reserved = 0;
263 out_message->Head()->msgh_id = 0;
264
[email protected]b88a7492010-09-17 12:28:32265 mach_msg_option_t rcv_options = MACH_RCV_MSG;
266 if (timeout != MACH_MSG_TIMEOUT_NONE)
267 rcv_options |= MACH_RCV_TIMEOUT;
268
[email protected]5062bb02008-11-20 21:11:20269 kern_return_t result = mach_msg(out_message->Head(),
[email protected]b88a7492010-09-17 12:28:32270 rcv_options,
[email protected]369537e82008-11-19 18:27:56271 0,
[email protected]5062bb02008-11-20 21:11:20272 out_message->MaxSize(),
[email protected]369537e82008-11-19 18:27:56273 port_,
274 timeout, // timeout in ms
275 MACH_PORT_NULL);
276
277 return result;
278}
279
280#pragma mark -
281
282//==============================================================================
283// get a port with send rights corresponding to a named registered service
284MachPortSender::MachPortSender(const char *receive_port_name) {
285 mach_port_t bootstrap_port = 0;
286 init_result_ = task_get_bootstrap_port(mach_task_self(), &bootstrap_port);
[email protected]5062bb02008-11-20 21:11:20287
[email protected]369537e82008-11-19 18:27:56288 if (init_result_ != KERN_SUCCESS)
289 return;
290
291 init_result_ = bootstrap_look_up(bootstrap_port,
292 const_cast<char*>(receive_port_name),
293 &send_port_);
294}
295
296//==============================================================================
[email protected]5062bb02008-11-20 21:11:20297MachPortSender::MachPortSender(mach_port_t send_port)
[email protected]369537e82008-11-19 18:27:56298 : send_port_(send_port),
299 init_result_(KERN_SUCCESS) {
300}
301
302//==============================================================================
303kern_return_t MachPortSender::SendMessage(MachSendMessage &message,
304 mach_msg_timeout_t timeout) {
[email protected]5062bb02008-11-20 21:11:20305 if (message.Head()->msgh_size == 0) {
306 NOTREACHED();
[email protected]369537e82008-11-19 18:27:56307 return KERN_INVALID_VALUE; // just for safety -- never should occur
308 };
[email protected]5062bb02008-11-20 21:11:20309
[email protected]369537e82008-11-19 18:27:56310 if (init_result_ != KERN_SUCCESS)
311 return init_result_;
[email protected]369537e82008-11-19 18:27:56312
[email protected]5062bb02008-11-20 21:11:20313 message.Head()->msgh_remote_port = send_port_;
314
315 kern_return_t result = mach_msg(message.Head(),
[email protected]369537e82008-11-19 18:27:56316 MACH_SEND_MSG | MACH_SEND_TIMEOUT,
[email protected]5062bb02008-11-20 21:11:20317 message.Head()->msgh_size,
[email protected]369537e82008-11-19 18:27:56318 0,
319 MACH_PORT_NULL,
320 timeout, // timeout in ms
321 MACH_PORT_NULL);
322
323 return result;
324}
[email protected]1fdb095d2010-01-12 21:33:48325
[email protected]23c6c2b2012-01-28 00:51:42326//==============================================================================
327
328namespace mac {
329
330kern_return_t GetNumberOfMachPorts(mach_port_t task_port, int* num_ports) {
331 mach_port_name_array_t names;
332 mach_msg_type_number_t names_count;
333 mach_port_type_array_t types;
334 mach_msg_type_number_t types_count;
335
336 // A friendlier interface would allow NULL buffers to only get the counts.
337 kern_return_t kr = mach_port_names(task_port, &names, &names_count,
338 &types, &types_count);
339 if (kr != KERN_SUCCESS)
340 return kr;
341
342 // The documentation states this is an invariant.
343 DCHECK_EQ(names_count, types_count);
344 *num_ports = names_count;
345
346 kr = vm_deallocate(mach_task_self(),
347 reinterpret_cast<vm_address_t>(names),
348 names_count * sizeof(mach_port_name_array_t));
349 kr = vm_deallocate(mach_task_self(),
350 reinterpret_cast<vm_address_t>(types),
351 types_count * sizeof(mach_port_type_array_t));
352
353 return kr;
354}
355
356} // namespace mac
357
[email protected]1fdb095d2010-01-12 21:33:48358} // namespace base