1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/mach_ipc_mac.h"
7 #import <Foundation/Foundation.h>
8 #include <mach/vm_map.h>
11 #include "base/logging.h"
16 const size_t MachMessage::kEmptyMessageSize = sizeof(mach_msg_header_t) +
17 sizeof(mach_msg_body_t) + sizeof(MessageDataPacket);
19 //==============================================================================
20 MachSendMessage::MachSendMessage(int32_t message_id) : MachMessage() {
21 Initialize(message_id);
24 MachSendMessage::MachSendMessage(void *storage, size_t storage_length,
26 : MachMessage(storage, storage_length) {
27 Initialize(message_id);
30 void MachSendMessage::Initialize(int32_t message_id) {
31 Head()->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
33 // head.msgh_remote_port = ...; // filled out in MachPortSender::SendMessage()
34 Head()->msgh_local_port = MACH_PORT_NULL;
35 Head()->msgh_reserved = 0;
38 SetDescriptorCount(0); // start out with no descriptors
40 SetMessageID(message_id);
41 SetData(NULL, 0); // client may add data later
44 //==============================================================================
45 MachMessage::MachMessage()
46 : storage_(new MachMessageData), // Allocate storage_ ourselves
47 storage_length_bytes_(sizeof(MachMessageData)),
49 memset(storage_, 0, storage_length_bytes_);
52 //==============================================================================
53 MachMessage::MachMessage(void *storage, size_t storage_length)
54 : storage_(static_cast<MachMessageData*>(storage)),
55 storage_length_bytes_(storage_length),
58 DCHECK_GE(storage_length, kEmptyMessageSize);
61 //==============================================================================
62 MachMessage::~MachMessage() {
69 //==============================================================================
70 // returns true if successful
71 bool MachMessage::SetData(const void* data,
72 int32_t data_length) {
73 // Enforce the fact that it's only safe to call this method once on a
75 DCHECK(GetDataPacket()->data_length == 0);
77 // first check to make sure we have enough space
78 int size = CalculateSize();
79 int new_size = size + data_length;
81 if ((unsigned)new_size > storage_length_bytes_) {
82 return false; // not enough space
85 GetDataPacket()->data_length = EndianU32_NtoL(data_length);
86 if (data) memcpy(GetDataPacket()->data, data, data_length);
88 // Update the Mach header with the new aligned size of the message.
94 //==============================================================================
95 // calculates and returns the total size of the message
96 // Currently, the entire message MUST fit inside of the MachMessage
97 // messsage size <= EmptyMessageSize()
98 int MachMessage::CalculateSize() {
99 int size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t);
101 // add space for MessageDataPacket
102 int32_t alignedDataLength = (GetDataLength() + 3) & ~0x3;
103 size += 2*sizeof(int32_t) + alignedDataLength;
105 // add space for descriptors
106 size += GetDescriptorCount() * sizeof(MachMsgPortDescriptor);
108 Head()->msgh_size = size;
113 //==============================================================================
114 MachMessage::MessageDataPacket *MachMessage::GetDataPacket() const {
115 int desc_size = sizeof(MachMsgPortDescriptor)*GetDescriptorCount();
116 MessageDataPacket *packet =
117 reinterpret_cast<MessageDataPacket*>(storage_->padding + desc_size);
122 //==============================================================================
123 void MachMessage::SetDescriptor(int n,
124 const MachMsgPortDescriptor &desc) {
125 MachMsgPortDescriptor *desc_array =
126 reinterpret_cast<MachMsgPortDescriptor*>(storage_->padding);
127 desc_array[n] = desc;
130 //==============================================================================
131 // returns true if successful otherwise there was not enough space
132 bool 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);
137 if ((unsigned)new_size > storage_length_bytes_) {
138 return false; // not enough space
141 // unfortunately, we need to move the data to allow space for the
143 u_int8_t *p = reinterpret_cast<u_int8_t*>(GetDataPacket());
144 bcopy(p, p+sizeof(MachMsgPortDescriptor), GetDataLength()+2*sizeof(int32_t));
146 SetDescriptor(GetDescriptorCount(), desc);
147 SetDescriptorCount(GetDescriptorCount() + 1);
154 //==============================================================================
155 void MachMessage::SetDescriptorCount(int n) {
156 storage_->body.msgh_descriptor_count = n;
159 Head()->msgh_bits |= MACH_MSGH_BITS_COMPLEX;
161 Head()->msgh_bits &= ~MACH_MSGH_BITS_COMPLEX;
165 //==============================================================================
166 MachMsgPortDescriptor *MachMessage::GetDescriptor(int n) const {
167 if (n < GetDescriptorCount()) {
168 MachMsgPortDescriptor *desc =
169 reinterpret_cast<MachMsgPortDescriptor*>(storage_->padding);
176 //==============================================================================
177 mach_port_t MachMessage::GetTranslatedPort(int n) const {
178 if (n < GetDescriptorCount()) {
179 return GetDescriptor(n)->GetMachPort();
181 return MACH_PORT_NULL;
186 //==============================================================================
187 // create a new mach port for receiving messages and register a name for it
188 ReceivePort::ReceivePort(const char *receive_port_name) {
189 mach_port_t current_task = mach_task_self();
191 init_result_ = mach_port_allocate(current_task,
192 MACH_PORT_RIGHT_RECEIVE,
195 if (init_result_ != KERN_SUCCESS)
198 init_result_ = mach_port_insert_right(current_task,
201 MACH_MSG_TYPE_MAKE_SEND);
203 if (init_result_ != KERN_SUCCESS)
206 // 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];
212 NSString *port_name = [NSString stringWithUTF8String:receive_port_name];
213 [[NSMachBootstrapServer sharedInstance] registerPort:ns_port name:port_name];
216 //==============================================================================
217 // create a new mach port for receiving messages
218 ReceivePort::ReceivePort() {
219 mach_port_t current_task = mach_task_self();
221 init_result_ = mach_port_allocate(current_task,
222 MACH_PORT_RIGHT_RECEIVE,
225 if (init_result_ != KERN_SUCCESS)
228 init_result_ = mach_port_insert_right(current_task,
231 MACH_MSG_TYPE_MAKE_SEND);
234 //==============================================================================
235 // Given an already existing mach port, use it. We take ownership of the
236 // port and deallocate it in our destructor.
237 ReceivePort::ReceivePort(mach_port_t receive_port)
238 : port_(receive_port),
239 init_result_(KERN_SUCCESS) {
242 //==============================================================================
243 ReceivePort::~ReceivePort() {
244 if (init_result_ == KERN_SUCCESS)
245 mach_port_deallocate(mach_task_self(), port_);
248 //==============================================================================
249 kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message,
250 mach_msg_timeout_t timeout) {
252 return KERN_INVALID_ARGUMENT;
255 // return any error condition encountered in constructor
256 if (init_result_ != KERN_SUCCESS)
259 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;
265 mach_msg_option_t rcv_options = MACH_RCV_MSG;
266 if (timeout != MACH_MSG_TIMEOUT_NONE)
267 rcv_options |= MACH_RCV_TIMEOUT;
269 kern_return_t result = mach_msg(out_message->Head(),
272 out_message->MaxSize(),
274 timeout, // timeout in ms
282 //==============================================================================
283 // get a port with send rights corresponding to a named registered service
284 MachPortSender::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);
288 if (init_result_ != KERN_SUCCESS)
291 init_result_ = bootstrap_look_up(bootstrap_port,
292 const_cast<char*>(receive_port_name),
296 //==============================================================================
297 MachPortSender::MachPortSender(mach_port_t send_port)
298 : send_port_(send_port),
299 init_result_(KERN_SUCCESS) {
302 //==============================================================================
303 kern_return_t MachPortSender::SendMessage(const MachSendMessage& message,
304 mach_msg_timeout_t timeout) {
305 if (message.Head()->msgh_size == 0) {
307 return KERN_INVALID_VALUE; // just for safety -- never should occur
310 if (init_result_ != KERN_SUCCESS)
313 message.Head()->msgh_remote_port = send_port_;
315 kern_return_t result = mach_msg(message.Head(),
316 MACH_SEND_MSG | MACH_SEND_TIMEOUT,
317 message.Head()->msgh_size,
320 timeout, // timeout in ms
326 //==============================================================================
330 kern_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;
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)
342 // The documentation states this is an invariant.
343 DCHECK_EQ(names_count, types_count);
344 *num_ports = names_count;
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));