1 // Copyright (c) 2011 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>
10 #include "base/logging.h"
15 const size_t MachMessage::kEmptyMessageSize = sizeof(mach_msg_header_t) +
16 sizeof(mach_msg_body_t) + sizeof(MessageDataPacket);
18 //==============================================================================
19 MachSendMessage::MachSendMessage(int32_t message_id) : MachMessage() {
20 Initialize(message_id);
23 MachSendMessage::MachSendMessage(void *storage, size_t storage_length,
25 : MachMessage(storage, storage_length) {
26 Initialize(message_id);
29 void MachSendMessage::Initialize(int32_t message_id) {
30 Head()->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
32 // head.msgh_remote_port = ...; // filled out in MachPortSender::SendMessage()
33 Head()->msgh_local_port = MACH_PORT_NULL;
34 Head()->msgh_reserved = 0;
37 SetDescriptorCount(0); // start out with no descriptors
39 SetMessageID(message_id);
40 SetData(NULL, 0); // client may add data later
43 //==============================================================================
44 MachMessage::MachMessage()
45 : storage_(new MachMessageData), // Allocate storage_ ourselves
46 storage_length_bytes_(sizeof(MachMessageData)),
48 memset(storage_, 0, storage_length_bytes_);
51 //==============================================================================
52 MachMessage::MachMessage(void *storage, size_t storage_length)
53 : storage_(static_cast<MachMessageData*>(storage)),
54 storage_length_bytes_(storage_length),
57 DCHECK_GE(storage_length, kEmptyMessageSize);
60 //==============================================================================
61 MachMessage::~MachMessage() {
68 //==============================================================================
69 // returns true if successful
70 bool MachMessage::SetData(const void* data,
71 int32_t data_length) {
72 // Enforce the fact that it's only safe to call this method once on a
74 DCHECK(GetDataPacket()->data_length == 0);
76 // first check to make sure we have enough space
77 int size = CalculateSize();
78 int new_size = size + data_length;
80 if ((unsigned)new_size > storage_length_bytes_) {
81 return false; // not enough space
84 GetDataPacket()->data_length = EndianU32_NtoL(data_length);
85 if (data) memcpy(GetDataPacket()->data, data, data_length);
87 // Update the Mach header with the new aligned size of the message.
93 //==============================================================================
94 // calculates and returns the total size of the message
95 // Currently, the entire message MUST fit inside of the MachMessage
96 // messsage size <= EmptyMessageSize()
97 int MachMessage::CalculateSize() {
98 int size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t);
100 // add space for MessageDataPacket
101 int32_t alignedDataLength = (GetDataLength() + 3) & ~0x3;
102 size += 2*sizeof(int32_t) + alignedDataLength;
104 // add space for descriptors
105 size += GetDescriptorCount() * sizeof(MachMsgPortDescriptor);
107 Head()->msgh_size = size;
112 //==============================================================================
113 MachMessage::MessageDataPacket *MachMessage::GetDataPacket() {
114 int desc_size = sizeof(MachMsgPortDescriptor)*GetDescriptorCount();
115 MessageDataPacket *packet =
116 reinterpret_cast<MessageDataPacket*>(storage_->padding + desc_size);
121 //==============================================================================
122 void MachMessage::SetDescriptor(int n,
123 const MachMsgPortDescriptor &desc) {
124 MachMsgPortDescriptor *desc_array =
125 reinterpret_cast<MachMsgPortDescriptor*>(storage_->padding);
126 desc_array[n] = desc;
129 //==============================================================================
130 // returns true if successful otherwise there was not enough space
131 bool MachMessage::AddDescriptor(const MachMsgPortDescriptor &desc) {
132 // first check to make sure we have enough space
133 int size = CalculateSize();
134 int new_size = size + sizeof(MachMsgPortDescriptor);
136 if ((unsigned)new_size > storage_length_bytes_) {
137 return false; // not enough space
140 // unfortunately, we need to move the data to allow space for the
142 u_int8_t *p = reinterpret_cast<u_int8_t*>(GetDataPacket());
143 bcopy(p, p+sizeof(MachMsgPortDescriptor), GetDataLength()+2*sizeof(int32_t));
145 SetDescriptor(GetDescriptorCount(), desc);
146 SetDescriptorCount(GetDescriptorCount() + 1);
153 //==============================================================================
154 void MachMessage::SetDescriptorCount(int n) {
155 storage_->body.msgh_descriptor_count = n;
158 Head()->msgh_bits |= MACH_MSGH_BITS_COMPLEX;
160 Head()->msgh_bits &= ~MACH_MSGH_BITS_COMPLEX;
164 //==============================================================================
165 MachMsgPortDescriptor *MachMessage::GetDescriptor(int n) {
166 if (n < GetDescriptorCount()) {
167 MachMsgPortDescriptor *desc =
168 reinterpret_cast<MachMsgPortDescriptor*>(storage_->padding);
175 //==============================================================================
176 mach_port_t MachMessage::GetTranslatedPort(int n) {
177 if (n < GetDescriptorCount()) {
178 return GetDescriptor(n)->GetMachPort();
180 return MACH_PORT_NULL;
185 //==============================================================================
186 // create a new mach port for receiving messages and register a name for it
187 ReceivePort::ReceivePort(const char *receive_port_name) {
188 mach_port_t current_task = mach_task_self();
190 init_result_ = mach_port_allocate(current_task,
191 MACH_PORT_RIGHT_RECEIVE,
194 if (init_result_ != KERN_SUCCESS)
197 init_result_ = mach_port_insert_right(current_task,
200 MACH_MSG_TYPE_MAKE_SEND);
202 if (init_result_ != KERN_SUCCESS)
205 // Without |NSMachPortDeallocateNone|, the NSMachPort seems to deallocate
206 // receive rights on port when it is eventually released. It is not necessary
207 // to deallocate any rights here as |port_| is fully deallocated in the
208 // ReceivePort destructor.
209 NSPort *ns_port = [NSMachPort portWithMachPort:port_
210 options:NSMachPortDeallocateNone];
211 NSString *port_name = [NSString stringWithUTF8String:receive_port_name];
212 [[NSMachBootstrapServer sharedInstance] registerPort:ns_port name:port_name];
215 //==============================================================================
216 // create a new mach port for receiving messages
217 ReceivePort::ReceivePort() {
218 mach_port_t current_task = mach_task_self();
220 init_result_ = mach_port_allocate(current_task,
221 MACH_PORT_RIGHT_RECEIVE,
224 if (init_result_ != KERN_SUCCESS)
227 init_result_ = mach_port_insert_right(current_task,
230 MACH_MSG_TYPE_MAKE_SEND);
233 //==============================================================================
234 // Given an already existing mach port, use it. We take ownership of the
235 // port and deallocate it in our destructor.
236 ReceivePort::ReceivePort(mach_port_t receive_port)
237 : port_(receive_port),
238 init_result_(KERN_SUCCESS) {
241 //==============================================================================
242 ReceivePort::~ReceivePort() {
243 if (init_result_ == KERN_SUCCESS)
244 mach_port_deallocate(mach_task_self(), port_);
247 //==============================================================================
248 kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message,
249 mach_msg_timeout_t timeout) {
251 return KERN_INVALID_ARGUMENT;
254 // return any error condition encountered in constructor
255 if (init_result_ != KERN_SUCCESS)
258 out_message->Head()->msgh_bits = 0;
259 out_message->Head()->msgh_local_port = port_;
260 out_message->Head()->msgh_remote_port = MACH_PORT_NULL;
261 out_message->Head()->msgh_reserved = 0;
262 out_message->Head()->msgh_id = 0;
264 mach_msg_option_t rcv_options = MACH_RCV_MSG;
265 if (timeout != MACH_MSG_TIMEOUT_NONE)
266 rcv_options |= MACH_RCV_TIMEOUT;
268 kern_return_t result = mach_msg(out_message->Head(),
271 out_message->MaxSize(),
273 timeout, // timeout in ms
281 //==============================================================================
282 // get a port with send rights corresponding to a named registered service
283 MachPortSender::MachPortSender(const char *receive_port_name) {
284 mach_port_t bootstrap_port = 0;
285 init_result_ = task_get_bootstrap_port(mach_task_self(), &bootstrap_port);
287 if (init_result_ != KERN_SUCCESS)
290 init_result_ = bootstrap_look_up(bootstrap_port,
291 const_cast<char*>(receive_port_name),
295 //==============================================================================
296 MachPortSender::MachPortSender(mach_port_t send_port)
297 : send_port_(send_port),
298 init_result_(KERN_SUCCESS) {
301 //==============================================================================
302 kern_return_t MachPortSender::SendMessage(MachSendMessage &message,
303 mach_msg_timeout_t timeout) {
304 if (message.Head()->msgh_size == 0) {
306 return KERN_INVALID_VALUE; // just for safety -- never should occur
309 if (init_result_ != KERN_SUCCESS)
312 message.Head()->msgh_remote_port = send_port_;
314 kern_return_t result = mach_msg(message.Head(),
315 MACH_SEND_MSG | MACH_SEND_TIMEOUT,
316 message.Head()->msgh_size,
319 timeout, // timeout in ms