michael@0: // Copyright (c) 2007, Google Inc. michael@0: // All rights reserved. michael@0: // michael@0: // Redistribution and use in source and binary forms, with or without michael@0: // modification, are permitted provided that the following conditions are michael@0: // met: michael@0: // michael@0: // * Redistributions of source code must retain the above copyright michael@0: // notice, this list of conditions and the following disclaimer. michael@0: // * Redistributions in binary form must reproduce the above michael@0: // copyright notice, this list of conditions and the following disclaimer michael@0: // in the documentation and/or other materials provided with the michael@0: // distribution. michael@0: // * Neither the name of Google Inc. nor the names of its michael@0: // contributors may be used to endorse or promote products derived from michael@0: // this software without specific prior written permission. michael@0: // michael@0: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: // michael@0: // MachIPC.h michael@0: // michael@0: // Some helpful wrappers for using Mach IPC calls michael@0: michael@0: #ifndef MACH_IPC_H__ michael@0: #define MACH_IPC_H__ michael@0: michael@0: #import michael@0: #import michael@0: #import michael@0: #import michael@0: michael@0: #import michael@0: michael@0: //============================================================================== michael@0: // DISCUSSION: michael@0: // michael@0: // The three main classes of interest are michael@0: // michael@0: // MachMessage: a wrapper for a mach message of the following form michael@0: // mach_msg_header_t michael@0: // mach_msg_body_t michael@0: // optional descriptors michael@0: // optional extra message data michael@0: // michael@0: // MachReceiveMessage and MachSendMessage subclass MachMessage michael@0: // and are used instead of MachMessage which is an abstract base class michael@0: // michael@0: // ReceivePort: michael@0: // Represents a mach port for which we have receive rights michael@0: // michael@0: // MachPortSender: michael@0: // Represents a mach port for which we have send rights michael@0: // michael@0: // Here's an example to receive a message on a server port: michael@0: // michael@0: // // This creates our named server port michael@0: // ReceivePort receivePort("com.Google.MyService"); michael@0: // michael@0: // MachReceiveMessage message; michael@0: // kern_return_t result = receivePort.WaitForMessage(&message, 0); michael@0: // michael@0: // if (result == KERN_SUCCESS && message.GetMessageID() == 57) { michael@0: // mach_port_t task = message.GetTranslatedPort(0); michael@0: // mach_port_t thread = message.GetTranslatedPort(1); michael@0: // michael@0: // char *messageString = message.GetData(); michael@0: // michael@0: // printf("message string = %s\n", messageString); michael@0: // } michael@0: // michael@0: // Here is an example of using these classes to send a message to this port: michael@0: // michael@0: // // send to already named port michael@0: // MachPortSender sender("com.Google.MyService"); michael@0: // MachSendMessage message(57); // our message ID is 57 michael@0: // michael@0: // // add some ports to be translated for us michael@0: // message.AddDescriptor(mach_task_self()); // our task michael@0: // message.AddDescriptor(mach_thread_self()); // this thread michael@0: // michael@0: // char messageString[] = "Hello server!\n"; michael@0: // message.SetData(messageString, strlen(messageString)+1); michael@0: // michael@0: // kern_return_t result = sender.SendMessage(message, 1000); // timeout 1000ms michael@0: // michael@0: michael@0: namespace google_breakpad { michael@0: #define PRINT_MACH_RESULT(result_, message_) \ michael@0: printf(message_" %s (%d)\n", mach_error_string(result_), result_ ); michael@0: michael@0: //============================================================================== michael@0: // A wrapper class for mach_msg_port_descriptor_t (with same memory layout) michael@0: // with convenient constructors and accessors michael@0: class MachMsgPortDescriptor : public mach_msg_port_descriptor_t { michael@0: public: michael@0: // General-purpose constructor michael@0: MachMsgPortDescriptor(mach_port_t in_name, michael@0: mach_msg_type_name_t in_disposition) { michael@0: name = in_name; michael@0: pad1 = 0; michael@0: pad2 = 0; michael@0: disposition = in_disposition; michael@0: type = MACH_MSG_PORT_DESCRIPTOR; michael@0: } michael@0: michael@0: // For passing send rights to a port michael@0: MachMsgPortDescriptor(mach_port_t in_name) { michael@0: name = in_name; michael@0: pad1 = 0; michael@0: pad2 = 0; michael@0: disposition = MACH_MSG_TYPE_COPY_SEND; michael@0: type = MACH_MSG_PORT_DESCRIPTOR; michael@0: } michael@0: michael@0: // Copy constructor michael@0: MachMsgPortDescriptor(const MachMsgPortDescriptor& desc) { michael@0: name = desc.name; michael@0: pad1 = desc.pad1; michael@0: pad2 = desc.pad2; michael@0: disposition = desc.disposition; michael@0: type = desc.type; michael@0: } michael@0: michael@0: mach_port_t GetMachPort() const { michael@0: return name; michael@0: } michael@0: michael@0: mach_msg_type_name_t GetDisposition() const { michael@0: return disposition; michael@0: } michael@0: michael@0: // For convenience michael@0: operator mach_port_t() const { michael@0: return GetMachPort(); michael@0: } michael@0: }; michael@0: michael@0: //============================================================================== michael@0: // MachMessage: a wrapper for a mach message michael@0: // (mach_msg_header_t, mach_msg_body_t, extra data) michael@0: // michael@0: // This considerably simplifies the construction of a message for sending michael@0: // and the getting at relevant data and descriptors for the receiver. michael@0: // michael@0: // Currently the combined size of the descriptors plus data must be michael@0: // less than 1024. But as a benefit no memory allocation is necessary. michael@0: // michael@0: // TODO: could consider adding malloc() support for very large messages michael@0: // michael@0: // A MachMessage object is used by ReceivePort::WaitForMessage michael@0: // and MachPortSender::SendMessage michael@0: // michael@0: class MachMessage { michael@0: public: michael@0: michael@0: // The receiver of the message can retrieve the raw data this way michael@0: uint8_t *GetData() { michael@0: return GetDataLength() > 0 ? GetDataPacket()->data : NULL; michael@0: } michael@0: michael@0: uint32_t GetDataLength() { michael@0: return EndianU32_LtoN(GetDataPacket()->data_length); michael@0: } michael@0: michael@0: // The message ID may be used as a code identifying the type of message michael@0: void SetMessageID(int32_t message_id) { michael@0: GetDataPacket()->id = EndianU32_NtoL(message_id); michael@0: } michael@0: michael@0: int32_t GetMessageID() { return EndianU32_LtoN(GetDataPacket()->id); } michael@0: michael@0: // Adds a descriptor (typically a mach port) to be translated michael@0: // returns true if successful, otherwise not enough space michael@0: bool AddDescriptor(const MachMsgPortDescriptor &desc); michael@0: michael@0: int GetDescriptorCount() const { return body.msgh_descriptor_count; } michael@0: MachMsgPortDescriptor *GetDescriptor(int n); michael@0: michael@0: // Convenience method which gets the mach port described by the descriptor michael@0: mach_port_t GetTranslatedPort(int n); michael@0: michael@0: // A simple message is one with no descriptors michael@0: bool IsSimpleMessage() const { return GetDescriptorCount() == 0; } michael@0: michael@0: // Sets raw data for the message (returns false if not enough space) michael@0: bool SetData(void *data, int32_t data_length); michael@0: michael@0: protected: michael@0: // Consider this an abstract base class - must create an actual instance michael@0: // of MachReceiveMessage or MachSendMessage michael@0: michael@0: MachMessage() { michael@0: memset(this, 0, sizeof(MachMessage)); michael@0: } michael@0: michael@0: friend class ReceivePort; michael@0: friend class MachPortSender; michael@0: michael@0: // Represents raw data in our message michael@0: struct MessageDataPacket { michael@0: int32_t id; // little-endian michael@0: int32_t data_length; // little-endian michael@0: uint8_t data[1]; // actual size limited by sizeof(MachMessage) michael@0: }; michael@0: michael@0: MessageDataPacket* GetDataPacket(); michael@0: michael@0: void SetDescriptorCount(int n); michael@0: void SetDescriptor(int n, const MachMsgPortDescriptor &desc); michael@0: michael@0: // Returns total message size setting msgh_size in the header to this value michael@0: mach_msg_size_t CalculateSize(); michael@0: michael@0: mach_msg_header_t head; michael@0: mach_msg_body_t body; michael@0: uint8_t padding[1024]; // descriptors and data may be embedded here michael@0: }; michael@0: michael@0: //============================================================================== michael@0: // MachReceiveMessage and MachSendMessage are useful to separate the idea michael@0: // of a mach message being sent and being received, and adds increased type michael@0: // safety: michael@0: // ReceivePort::WaitForMessage() only accepts a MachReceiveMessage michael@0: // MachPortSender::SendMessage() only accepts a MachSendMessage michael@0: michael@0: //============================================================================== michael@0: class MachReceiveMessage : public MachMessage { michael@0: public: michael@0: MachReceiveMessage() : MachMessage() {}; michael@0: }; michael@0: michael@0: //============================================================================== michael@0: class MachSendMessage : public MachMessage { michael@0: public: michael@0: MachSendMessage(int32_t message_id); michael@0: }; michael@0: michael@0: //============================================================================== michael@0: // Represents a mach port for which we have receive rights michael@0: class ReceivePort { michael@0: public: michael@0: // Creates a new mach port for receiving messages and registers a name for it michael@0: explicit ReceivePort(const char *receive_port_name); michael@0: michael@0: // Given an already existing mach port, use it. We take ownership of the michael@0: // port and deallocate it in our destructor. michael@0: explicit ReceivePort(mach_port_t receive_port); michael@0: michael@0: // Create a new mach port for receiving messages michael@0: ReceivePort(); michael@0: michael@0: ~ReceivePort(); michael@0: michael@0: // Waits on the mach port until message received or timeout michael@0: kern_return_t WaitForMessage(MachReceiveMessage *out_message, michael@0: mach_msg_timeout_t timeout); michael@0: michael@0: // The underlying mach port that we wrap michael@0: mach_port_t GetPort() const { return port_; } michael@0: michael@0: private: michael@0: ReceivePort(const ReceivePort&); // disable copy c-tor michael@0: michael@0: mach_port_t port_; michael@0: kern_return_t init_result_; michael@0: }; michael@0: michael@0: //============================================================================== michael@0: // Represents a mach port for which we have send rights michael@0: class MachPortSender { michael@0: public: michael@0: // get a port with send rights corresponding to a named registered service michael@0: explicit MachPortSender(const char *receive_port_name); michael@0: michael@0: michael@0: // Given an already existing mach port, use it. michael@0: explicit MachPortSender(mach_port_t send_port); michael@0: michael@0: kern_return_t SendMessage(MachSendMessage &message, michael@0: mach_msg_timeout_t timeout); michael@0: michael@0: private: michael@0: MachPortSender(const MachPortSender&); // disable copy c-tor michael@0: michael@0: mach_port_t send_port_; michael@0: kern_return_t init_result_; michael@0: }; michael@0: michael@0: } // namespace google_breakpad michael@0: michael@0: #endif // MACH_IPC_H__