// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_MACH_IPC_MAC_H_
#define BASE_MACH_IPC_MAC_H_
#pragma once
#include <mach/mach.h>
#include <mach/message.h>
#include <servers/bootstrap.h>
#include <sys/types.h>
#include <CoreServices/CoreServices.h>
#include "base/basictypes.h"
//==============================================================================
// DISCUSSION:
//
// The three main classes of interest are
//
// MachMessage: a wrapper for a Mach message of the following form
// mach_msg_header_t
// mach_msg_body_t
// optional descriptors
// optional extra message data
//
// MachReceiveMessage and MachSendMessage subclass MachMessage
// and are used instead of MachMessage which is an abstract base class
//
// ReceivePort:
// Represents a Mach port for which we have receive rights
//
// MachPortSender:
// Represents a Mach port for which we have send rights
//
// Here's an example to receive a message on a server port:
//
// // This creates our named server port
// ReceivePort receivePort("com.Google.MyService");
//
// MachReceiveMessage message;
// kern_return_t result = receivePort.WaitForMessage(&message, 0);
//
// if (result == KERN_SUCCESS && message.GetMessageID() == 57) {
// mach_port_t task = message.GetTranslatedPort(0);
// mach_port_t thread = message.GetTranslatedPort(1);
//
// char *messageString = message.GetData();
//
// printf("message string = %s\n", messageString);
// }
//
// Here is an example of using these classes to send a message to this port:
//
// // send to already named port
// MachPortSender sender("com.Google.MyService");
// MachSendMessage message(57); // our message ID is 57
//
// // add some ports to be translated for us
// message.AddDescriptor(mach_task_self()); // our task
// message.AddDescriptor(mach_thread_self()); // this thread
//
// char messageString[] = "Hello server!\n";
// message.SetData(messageString, strlen(messageString)+1);
// // timeout 1000ms
// kern_return_t result = sender.SendMessage(message, 1000);
//
#define PRINT_MACH_RESULT(result_, message_) \
printf(message_" %s (%d)\n", mach_error_string(result_), result_ );
namespace base {
//==============================================================================
// A wrapper class for mach_msg_port_descriptor_t (with same memory layout)
// with convenient constructors and accessors
class MachMsgPortDescriptor : public mach_msg_port_descriptor_t {
public:
// General-purpose constructor
MachMsgPortDescriptor(mach_port_t in_name,
mach_msg_type_name_t in_disposition) {
name = in_name;
pad1 = 0;
pad2 = 0;
disposition = in_disposition;
type = MACH_MSG_PORT_DESCRIPTOR;
}
// For passing send rights to a port
MachMsgPortDescriptor(mach_port_t in_name) {
name = in_name;
pad1 = 0;
pad2 = 0;
disposition = MACH_MSG_TYPE_PORT_SEND;
type = MACH_MSG_PORT_DESCRIPTOR;
}
// Copy constructor
MachMsgPortDescriptor(const MachMsgPortDescriptor& desc) {
name = desc.name;
pad1 = desc.pad1;
pad2 = desc.pad2;
disposition = desc.disposition;
type = desc.type;
}
mach_port_t GetMachPort() const {
return name;
}
mach_msg_type_name_t GetDisposition() const {
return disposition;
}
// For convenience
operator mach_port_t() const {
return GetMachPort();
}
};
//==============================================================================
// MachMessage: a wrapper for a Mach message
// (mach_msg_header_t, mach_msg_body_t, extra data)
//
// This considerably simplifies the construction of a message for sending
// and the getting at relevant data and descriptors for the receiver.
//
// This class can be initialized using external storage of an arbitrary size
// or it can manage storage internally.
// 1. If storage is allocated internally, the combined size of the descriptors
// plus data must be less than 1024. But as a benefit no memory allocation is
// necessary.
// 2. For external storage, a buffer of at least EmptyMessageSize() must be
// provided.
//
// A MachMessage object is used by ReceivePort::WaitForMessage
// and MachPortSender::SendMessage
//
class MachMessage {
public:
static const size_t kEmptyMessageSize;
virtual ~MachMessage();
// The receiver of the message can retrieve the raw data this way
u_int8_t *GetData() {
return GetDataLength() > 0 ? GetDataPacket()->data : NULL;
}
u_int32_t GetDataLength() {
return EndianU32_LtoN(GetDataPacket()->data_length);
}
// The message ID may be used as a code identifying the type of message
void SetMessageID(int32_t message_id) {
GetDataPacket()->id = EndianU32_NtoL(message_id);
}
int32_t GetMessageID() { return EndianU32_LtoN(GetDataPacket()->id); }
// Adds a descriptor (typically a Mach port) to be translated
// returns true if successful, otherwise not enough space
bool AddDescriptor(const MachMsgPortDescriptor &desc);
int GetDescriptorCount() const {
return storage_->body.msgh_descriptor_count;
}
MachMsgPortDescriptor *GetDescriptor(int n);
// Convenience method which gets the Mach port described by the descriptor
mach_port_t GetTranslatedPort(int n);
// A simple message is one with no descriptors
bool IsSimpleMessage() const { return GetDescriptorCount() == 0; }
// Sets raw data for the message (returns false if not enough space)
bool SetData(const void* data, int32_t data_length);
protected:
// Consider this an abstract base class - must create an actual instance
// of MachReceiveMessage or MachSendMessage
MachMessage();
// Constructor for use with preallocate storage.
// storage_length must be >= EmptyMessageSize()
MachMessage(void *storage, size_t storage_length);
friend class ReceivePort;
friend class MachPortSender;
// Represents raw data in our message
struct MessageDataPacket {
int32_t id; // little-endian
int32_t data_length; // little-endian
u_int8_t data[1]; // actual size limited by storage_length_bytes_
};
MessageDataPacket* GetDataPacket();
void SetDescriptorCount(int n);
void SetDescriptor(int n, const MachMsgPortDescriptor &desc);
// Returns total message size setting msgh_size in the header to this value
int CalculateSize();
// Returns total storage size that this object can grow to, this is inclusive
// of the Mach header.
size_t MaxSize() const { return storage_length_bytes_; }
mach_msg_header_t *Head() { return &(storage_->head); }
private:
struct MachMessageData {
mach_msg_header_t head;
mach_msg_body_t body;
// descriptors and data may be embedded here.
u_int8_t padding[1024];
};
MachMessageData *storage_;
size_t storage_length_bytes_;
bool own_storage_; // Is storage owned by this object?
};
//==============================================================================
// MachReceiveMessage and MachSendMessage are useful to separate the idea
// of a Mach message being sent and being received, and adds increased type
// safety:
// ReceivePort::WaitForMessage() only accepts a MachReceiveMessage
// MachPortSender::SendMessage() only accepts a MachSendMessage
//==============================================================================
class MachReceiveMessage : public MachMessage {
public:
MachReceiveMessage() : MachMessage() {}
MachReceiveMessage(void *storage, size_t storage_length)
: MachMessage(storage, storage_length) {}
private:
DISALLOW_COPY_AND_ASSIGN(MachReceiveMessage);
};
//==============================================================================
class MachSendMessage : public MachMessage {
public:
explicit MachSendMessage(int32_t message_id);
MachSendMessage(void *storage, size_t storage_length, int32_t message_id);
private:
void Initialize(int32_t message_id);
DISALLOW_COPY_AND_ASSIGN(MachSendMessage);
};
//==============================================================================
// Represents a Mach port for which we have receive rights
class ReceivePort {
public:
// Creates a new Mach port for receiving messages and registers a name for it
explicit ReceivePort(const char *receive_port_name);
// Given an already existing Mach port, use it. We take ownership of the
// port and deallocate it in our destructor.
explicit ReceivePort(mach_port_t receive_port);
// Create a new Mach port for receiving messages
ReceivePort();
~ReceivePort();
// Waits on the Mach port until message received or timeout. If |timeout| is
// MACH_MSG_TIMEOUT_NONE, this method waits forever.
kern_return_t WaitForMessage(MachReceiveMessage *out_message,
mach_msg_timeout_t timeout);
// The underlying Mach port that we wrap
mach_port_t GetPort() const { return port_; }
private:
mach_port_t port_;
kern_return_t init_result_;
DISALLOW_COPY_AND_ASSIGN(ReceivePort);
};
//==============================================================================
// Represents a Mach port for which we have send rights
class MachPortSender {
public:
// get a port with send rights corresponding to a named registered service
explicit MachPortSender(const char *receive_port_name);
// Given an already existing Mach port, use it. Does not take ownership of
// |send_port|.
explicit MachPortSender(mach_port_t send_port);
kern_return_t SendMessage(MachSendMessage &message,
mach_msg_timeout_t timeout);
private:
mach_port_t send_port_;
kern_return_t init_result_;
DISALLOW_COPY_AND_ASSIGN(MachPortSender);
};
} // namespace base
#endif // BASE_MACH_IPC_MAC_H_