// 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 #include #include #include #include #include "base/base_export.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 BASE_EXPORT 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 BASE_EXPORT 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 BASE_EXPORT 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 BASE_EXPORT 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_