mirror of
https://github.com/clementine-player/Clementine
synced 2024-12-27 01:54:20 +01:00
308 lines
10 KiB
C
308 lines
10 KiB
C
|
// 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;
|
||
|
}
|
||
|
|
||
|
// We're just a simple wrapper for mach_msg_port_descriptor_t
|
||
|
// and have the same memory layout
|
||
|
operator mach_msg_port_descriptor_t&() {
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
// 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
|
||
|
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 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
|
||
|
u_int8_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;
|
||
|
u_int8_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__
|