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