// Copyright (c) 2007, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// MachIPC.h
//
// Some helpful wrappers for using Mach IPC calls
#ifndef MACH_IPC_H__
#define MACH_IPC_H__
#import <mach/mach.h>
#import <mach/message.h>
#import <servers/bootstrap.h>
#import <sys/types.h>
#import <CoreServices/CoreServices.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);
//
// kern_return_t result = sender.SendMessage(message, 1000); // timeout 1000ms
//
namespace google_breakpad {
#define PRINT_MACH_RESULT(result_, message_) \
printf(message_" %s (%d)\n", mach_error_string(result_), result_ );
//==============================================================================
// 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_COPY_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.
//
// Currently the combined size of the descriptors plus data must be
// less than 1024. But as a benefit no memory allocation is necessary.
//
// TODO: could consider adding malloc() support for very large messages
//
// A MachMessage object is used by ReceivePort::WaitForMessage
// and MachPortSender::SendMessage
//
class MachMessage {
public:
// The receiver of the message can retrieve the raw data this way
uint8_t *GetData() {
return GetDataLength() > 0 ? GetDataPacket()->data : NULL;
}
uint32_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 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(void *data, int32_t data_length);
protected:
// Consider this an abstract base class - must create an actual instance
// of MachReceiveMessage or MachSendMessage
MachMessage() {
memset(this, 0, sizeof(MachMessage));
}
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
uint8_t data[1]; // actual size limited by sizeof(MachMessage)
};
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
mach_msg_size_t CalculateSize();
mach_msg_header_t head;
mach_msg_body_t body;
uint8_t padding[1024]; // descriptors and data may be embedded here
};
//==============================================================================
// 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() {};
};
//==============================================================================
class MachSendMessage : public MachMessage {
public:
MachSendMessage(int32_t message_id);
};
//==============================================================================
// 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
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:
ReceivePort(const ReceivePort&); // disable copy c-tor
mach_port_t port_;
kern_return_t init_result_;
};
//==============================================================================
// 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.
explicit MachPortSender(mach_port_t send_port);
kern_return_t SendMessage(MachSendMessage &message,
mach_msg_timeout_t timeout);
private:
MachPortSender(const MachPortSender&); // disable copy c-tor
mach_port_t send_port_;
kern_return_t init_result_;
};
} // namespace google_breakpad
#endif // MACH_IPC_H__