1// Copyright (c) 2007, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10//     * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14//     * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29//
30//  MachIPC.h
31//
32//  Some helpful wrappers for using Mach IPC calls
33
34#ifndef MACH_IPC_H__
35#define MACH_IPC_H__
36
37#import <mach/mach.h>
38#import <mach/message.h>
39#import <servers/bootstrap.h>
40#import <sys/types.h>
41
42#import <CoreServices/CoreServices.h>
43
44//==============================================================================
45// DISCUSSION:
46//
47// The three main classes of interest are
48//
49//  MachMessage:    a wrapper for a mach message of the following form
50//   mach_msg_header_t
51//   mach_msg_body_t
52//   optional descriptors
53//   optional extra message data
54//
55//  MachReceiveMessage and MachSendMessage subclass MachMessage
56//    and are used instead of MachMessage which is an abstract base class
57//
58//  ReceivePort:
59//    Represents a mach port for which we have receive rights
60//
61//  MachPortSender:
62//    Represents a mach port for which we have send rights
63//
64// Here's an example to receive a message on a server port:
65//
66//        // This creates our named server port
67//        ReceivePort receivePort("com.Google.MyService");
68//
69//        MachReceiveMessage message;
70//        kern_return_t result = receivePort.WaitForMessage(&message, 0);
71//
72//        if (result == KERN_SUCCESS && message.GetMessageID() == 57) {
73//          mach_port_t task = message.GetTranslatedPort(0);
74//          mach_port_t thread = message.GetTranslatedPort(1);
75//
76//          char *messageString = message.GetData();
77//
78//          printf("message string = %s\n", messageString);
79//        }
80//
81// Here is an example of using these classes to send a message to this port:
82//
83//    // send to already named port
84//    MachPortSender sender("com.Google.MyService");
85//    MachSendMessage message(57);      // our message ID is 57
86//
87//    // add some ports to be translated for us
88//    message.AddDescriptor(mach_task_self());     // our task
89//    message.AddDescriptor(mach_thread_self());   // this thread
90//
91//    char messageString[] = "Hello server!\n";
92//    message.SetData(messageString, strlen(messageString)+1);
93//
94//    kern_return_t result = sender.SendMessage(message, 1000); // timeout 1000ms
95//
96
97namespace google_breakpad {
98#define PRINT_MACH_RESULT(result_, message_) \
99  printf(message_" %s (%d)\n", mach_error_string(result_), result_ );
100
101//==============================================================================
102// A wrapper class for mach_msg_port_descriptor_t (with same memory layout)
103// with convenient constructors and accessors
104class MachMsgPortDescriptor : public mach_msg_port_descriptor_t {
105 public:
106  // General-purpose constructor
107  MachMsgPortDescriptor(mach_port_t in_name,
108                        mach_msg_type_name_t in_disposition) {
109    name = in_name;
110    pad1 = 0;
111    pad2 = 0;
112    disposition = in_disposition;
113    type = MACH_MSG_PORT_DESCRIPTOR;
114  }
115
116  // For passing send rights to a port
117  MachMsgPortDescriptor(mach_port_t in_name) {
118    name = in_name;
119    pad1 = 0;
120    pad2 = 0;
121    disposition = MACH_MSG_TYPE_COPY_SEND;
122    type = MACH_MSG_PORT_DESCRIPTOR;
123  }
124
125  // Copy constructor
126  MachMsgPortDescriptor(const MachMsgPortDescriptor& desc) {
127    name = desc.name;
128    pad1 = desc.pad1;
129    pad2 = desc.pad2;
130    disposition = desc.disposition;
131    type = desc.type;
132  }
133
134  mach_port_t GetMachPort() const {
135    return name;
136  }
137
138  mach_msg_type_name_t GetDisposition() const {
139    return disposition;
140  }
141
142  // For convenience
143  operator mach_port_t() const {
144    return GetMachPort();
145  }
146};
147
148//==============================================================================
149// MachMessage: a wrapper for a mach message
150//  (mach_msg_header_t, mach_msg_body_t, extra data)
151//
152//  This considerably simplifies the construction of a message for sending
153//  and the getting at relevant data and descriptors for the receiver.
154//
155//  Currently the combined size of the descriptors plus data must be
156//  less than 1024.  But as a benefit no memory allocation is necessary.
157//
158// TODO: could consider adding malloc() support for very large messages
159//
160//  A MachMessage object is used by ReceivePort::WaitForMessage
161//  and MachPortSender::SendMessage
162//
163class MachMessage {
164 public:
165
166  // The receiver of the message can retrieve the raw data this way
167  uint8_t *GetData() {
168    return GetDataLength() > 0 ? GetDataPacket()->data : NULL;
169  }
170
171  uint32_t GetDataLength() {
172    return EndianU32_LtoN(GetDataPacket()->data_length);
173  }
174
175  // The message ID may be used as a code identifying the type of message
176  void SetMessageID(int32_t message_id) {
177    GetDataPacket()->id = EndianU32_NtoL(message_id);
178  }
179
180  int32_t GetMessageID() { return EndianU32_LtoN(GetDataPacket()->id); }
181
182  // Adds a descriptor (typically a mach port) to be translated
183  // returns true if successful, otherwise not enough space
184  bool AddDescriptor(const MachMsgPortDescriptor &desc);
185
186  int GetDescriptorCount() const { return body.msgh_descriptor_count; }
187  MachMsgPortDescriptor *GetDescriptor(int n);
188
189  // Convenience method which gets the mach port described by the descriptor
190  mach_port_t GetTranslatedPort(int n);
191
192  // A simple message is one with no descriptors
193  bool IsSimpleMessage() const { return GetDescriptorCount() == 0; }
194
195  // Sets raw data for the message (returns false if not enough space)
196  bool SetData(void *data, int32_t data_length);
197
198 protected:
199  // Consider this an abstract base class - must create an actual instance
200  // of MachReceiveMessage or MachSendMessage
201
202  MachMessage() {
203    memset(this, 0, sizeof(MachMessage));
204  }
205
206  friend class ReceivePort;
207  friend class MachPortSender;
208
209  // Represents raw data in our message
210  struct MessageDataPacket {
211    int32_t      id;          // little-endian
212    int32_t      data_length; // little-endian
213    uint8_t      data[1];     // actual size limited by sizeof(MachMessage)
214  };
215
216  MessageDataPacket* GetDataPacket();
217
218  void SetDescriptorCount(int n);
219  void SetDescriptor(int n, const MachMsgPortDescriptor &desc);
220
221  // Returns total message size setting msgh_size in the header to this value
222  mach_msg_size_t CalculateSize();
223
224  mach_msg_header_t  head;
225  mach_msg_body_t    body;
226  uint8_t            padding[1024]; // descriptors and data may be embedded here
227};
228
229//==============================================================================
230// MachReceiveMessage and MachSendMessage are useful to separate the idea
231// of a mach message being sent and being received, and adds increased type
232// safety:
233//  ReceivePort::WaitForMessage() only accepts a MachReceiveMessage
234//  MachPortSender::SendMessage() only accepts a MachSendMessage
235
236//==============================================================================
237class MachReceiveMessage : public MachMessage {
238 public:
239  MachReceiveMessage() : MachMessage() {};
240};
241
242//==============================================================================
243class MachSendMessage : public MachMessage {
244 public:
245  MachSendMessage(int32_t message_id);
246};
247
248//==============================================================================
249// Represents a mach port for which we have receive rights
250class ReceivePort {
251 public:
252  // Creates a new mach port for receiving messages and registers a name for it
253  explicit ReceivePort(const char *receive_port_name);
254
255  // Given an already existing mach port, use it.  We take ownership of the
256  // port and deallocate it in our destructor.
257  explicit ReceivePort(mach_port_t receive_port);
258
259  // Create a new mach port for receiving messages
260  ReceivePort();
261
262  ~ReceivePort();
263
264  // Waits on the mach port until message received or timeout
265  kern_return_t WaitForMessage(MachReceiveMessage *out_message,
266                               mach_msg_timeout_t timeout);
267
268  // The underlying mach port that we wrap
269  mach_port_t  GetPort() const { return port_; }
270
271 private:
272  ReceivePort(const ReceivePort&);  // disable copy c-tor
273
274  mach_port_t   port_;
275  kern_return_t init_result_;
276};
277
278//==============================================================================
279// Represents a mach port for which we have send rights
280class MachPortSender {
281 public:
282  // get a port with send rights corresponding to a named registered service
283  explicit MachPortSender(const char *receive_port_name);
284
285
286  // Given an already existing mach port, use it.
287  explicit MachPortSender(mach_port_t send_port);
288
289  kern_return_t SendMessage(MachSendMessage &message,
290                            mach_msg_timeout_t timeout);
291
292 private:
293  MachPortSender(const MachPortSender&);  // disable copy c-tor
294
295  mach_port_t   send_port_;
296  kern_return_t init_result_;
297};
298
299}  // namespace google_breakpad
300
301#endif // MACH_IPC_H__
302