/ src / common / mac / MachIPC.mm
MachIPC.mm
  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.mm
 30  //  Wrapper for mach IPC calls
 31  
 32  #import <stdio.h>
 33  #import "MachIPC.h"
 34  #include "common/mac/bootstrap_compat.h"
 35  
 36  namespace google_breakpad {
 37  //==============================================================================
 38  MachSendMessage::MachSendMessage(int32_t message_id) : MachMessage() {
 39    head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
 40  
 41    // head.msgh_remote_port = ...; // filled out in MachPortSender::SendMessage()
 42    head.msgh_local_port = MACH_PORT_NULL;
 43    head.msgh_reserved = 0;
 44    head.msgh_id = 0;
 45  
 46    SetDescriptorCount(0);  // start out with no descriptors
 47  
 48    SetMessageID(message_id);
 49    SetData(NULL, 0);       // client may add data later
 50  }
 51  
 52  //==============================================================================
 53  // returns true if successful
 54  bool MachMessage::SetData(void* data,
 55                            int32_t data_length) {
 56    // first check to make sure we have enough space
 57    size_t size = CalculateSize();
 58    size_t new_size = size + data_length;
 59    
 60    if (new_size > sizeof(MachMessage)) {
 61      return false;  // not enough space
 62    }
 63  
 64    GetDataPacket()->data_length = EndianU32_NtoL(data_length);
 65    if (data) memcpy(GetDataPacket()->data, data, data_length);
 66  
 67    CalculateSize();
 68  
 69    return true;
 70  }
 71  
 72  //==============================================================================
 73  // calculates and returns the total size of the message
 74  // Currently, the entire message MUST fit inside of the MachMessage
 75  //    messsage size <= sizeof(MachMessage)
 76  mach_msg_size_t MachMessage::CalculateSize() {
 77    size_t size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t);
 78    
 79    // add space for MessageDataPacket
 80    int32_t alignedDataLength = (GetDataLength() + 3) & ~0x3;
 81    size += 2*sizeof(int32_t) + alignedDataLength;
 82    
 83    // add space for descriptors
 84    size += GetDescriptorCount() * sizeof(MachMsgPortDescriptor);
 85    
 86    head.msgh_size = static_cast<mach_msg_size_t>(size);
 87    
 88    return head.msgh_size;
 89  }
 90  
 91  //==============================================================================
 92  MachMessage::MessageDataPacket* MachMessage::GetDataPacket() {
 93    size_t desc_size = sizeof(MachMsgPortDescriptor)*GetDescriptorCount();
 94    MessageDataPacket* packet =
 95      reinterpret_cast<MessageDataPacket*>(padding + desc_size);
 96  
 97    return packet;
 98  }
 99  
100  //==============================================================================
101  void MachMessage::SetDescriptor(int n,
102                                  const MachMsgPortDescriptor& desc) {
103    MachMsgPortDescriptor* desc_array =
104      reinterpret_cast<MachMsgPortDescriptor*>(padding);
105    desc_array[n] = desc;
106  }
107  
108  //==============================================================================
109  // returns true if successful otherwise there was not enough space
110  bool MachMessage::AddDescriptor(const MachMsgPortDescriptor& desc) {
111    // first check to make sure we have enough space
112    int size = CalculateSize();
113    size_t new_size = size + sizeof(MachMsgPortDescriptor);
114    
115    if (new_size > sizeof(MachMessage)) {
116      return false;  // not enough space
117    }
118  
119    // unfortunately, we need to move the data to allow space for the
120    // new descriptor
121    u_int8_t* p = reinterpret_cast<u_int8_t*>(GetDataPacket());
122    bcopy(p, p+sizeof(MachMsgPortDescriptor), GetDataLength()+2*sizeof(int32_t));
123    
124    SetDescriptor(GetDescriptorCount(), desc);
125    SetDescriptorCount(GetDescriptorCount() + 1);
126  
127    CalculateSize();
128    
129    return true;
130  }
131  
132  //==============================================================================
133  void MachMessage::SetDescriptorCount(int n) {
134    body.msgh_descriptor_count = n;
135  
136    if (n > 0) {
137      head.msgh_bits |= MACH_MSGH_BITS_COMPLEX;
138    } else {
139      head.msgh_bits &= ~MACH_MSGH_BITS_COMPLEX;
140    }
141  }
142  
143  //==============================================================================
144  MachMsgPortDescriptor* MachMessage::GetDescriptor(int n) {
145    if (n < GetDescriptorCount()) {
146      MachMsgPortDescriptor* desc =
147        reinterpret_cast<MachMsgPortDescriptor*>(padding);
148      return desc + n;
149    }
150    
151    return nil;
152  }
153  
154  //==============================================================================
155  mach_port_t MachMessage::GetTranslatedPort(int n) {
156    if (n < GetDescriptorCount()) {
157      return GetDescriptor(n)->GetMachPort();
158    }
159    return MACH_PORT_NULL;
160  }
161  
162  #pragma mark -
163  
164  //==============================================================================
165  // create a new mach port for receiving messages and register a name for it
166  ReceivePort::ReceivePort(const char* receive_port_name) {
167    mach_port_t current_task = mach_task_self();
168  
169    init_result_ = mach_port_allocate(current_task,
170                                      MACH_PORT_RIGHT_RECEIVE,
171                                      &port_);
172  
173    if (init_result_ != KERN_SUCCESS)
174      return;
175      
176    init_result_ = mach_port_insert_right(current_task,
177                                          port_,
178                                          port_,
179                                          MACH_MSG_TYPE_MAKE_SEND);
180  
181    if (init_result_ != KERN_SUCCESS)
182      return;
183  
184    mach_port_t task_bootstrap_port = 0;
185    init_result_ = task_get_bootstrap_port(current_task, &task_bootstrap_port);
186  
187    if (init_result_ != KERN_SUCCESS)
188      return;
189  
190    init_result_ = breakpad::BootstrapRegister(
191        bootstrap_port,
192        const_cast<char*>(receive_port_name),
193        port_);
194  }
195  
196  //==============================================================================
197  // create a new mach port for receiving messages
198  ReceivePort::ReceivePort() {
199    mach_port_t current_task = mach_task_self();
200  
201    init_result_ = mach_port_allocate(current_task,
202                                      MACH_PORT_RIGHT_RECEIVE,
203                                      &port_);
204  
205    if (init_result_ != KERN_SUCCESS)
206      return;
207  
208    init_result_ =   mach_port_insert_right(current_task,
209                                            port_,
210                                            port_,
211                                            MACH_MSG_TYPE_MAKE_SEND);
212  }
213  
214  //==============================================================================
215  // Given an already existing mach port, use it.  We take ownership of the
216  // port and deallocate it in our destructor.
217  ReceivePort::ReceivePort(mach_port_t receive_port)
218    : port_(receive_port),
219      init_result_(KERN_SUCCESS) {
220  }
221  
222  //==============================================================================
223  ReceivePort::~ReceivePort() {
224    if (init_result_ == KERN_SUCCESS)
225      mach_port_deallocate(mach_task_self(), port_);
226  }
227  
228  //==============================================================================
229  kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage* out_message,
230                                            mach_msg_timeout_t timeout) {
231    if (!out_message) {
232      return KERN_INVALID_ARGUMENT;
233    }
234  
235    // return any error condition encountered in constructor
236    if (init_result_ != KERN_SUCCESS)
237      return init_result_;
238    
239    out_message->head.msgh_bits = 0;
240    out_message->head.msgh_local_port = port_;
241    out_message->head.msgh_remote_port = MACH_PORT_NULL;
242    out_message->head.msgh_reserved = 0;
243    out_message->head.msgh_id = 0;
244  
245    mach_msg_option_t options = MACH_RCV_MSG;
246    if (timeout != MACH_MSG_TIMEOUT_NONE)
247      options |= MACH_RCV_TIMEOUT;
248    kern_return_t result = mach_msg(&out_message->head,
249                                    options,
250                                    0,
251                                    sizeof(MachMessage),
252                                    port_,
253                                    timeout,              // timeout in ms
254                                    MACH_PORT_NULL);
255  
256    return result;
257  }
258  
259  #pragma mark -
260  
261  //==============================================================================
262  // get a port with send rights corresponding to a named registered service
263  MachPortSender::MachPortSender(const char* receive_port_name) {
264    mach_port_t task_bootstrap_port = 0;
265    init_result_ = task_get_bootstrap_port(mach_task_self(), 
266                                           &task_bootstrap_port);
267    
268    if (init_result_ != KERN_SUCCESS)
269      return;
270  
271    init_result_ = bootstrap_look_up(task_bootstrap_port,
272                      const_cast<char*>(receive_port_name),
273                      &send_port_);
274  }
275  
276  //==============================================================================
277  MachPortSender::MachPortSender(mach_port_t send_port) 
278    : send_port_(send_port),
279      init_result_(KERN_SUCCESS) {
280  }
281  
282  //==============================================================================
283  kern_return_t MachPortSender::SendMessage(MachSendMessage& message,
284                                            mach_msg_timeout_t timeout) {
285    if (message.head.msgh_size == 0) {
286      return KERN_INVALID_VALUE;    // just for safety -- never should occur
287    };
288    
289    if (init_result_ != KERN_SUCCESS)
290      return init_result_;
291    
292    message.head.msgh_remote_port = send_port_;
293  
294    kern_return_t result = mach_msg(&message.head,
295                                    MACH_SEND_MSG | MACH_SEND_TIMEOUT,
296                                    message.head.msgh_size,
297                                    0,
298                                    MACH_PORT_NULL,
299                                    timeout,              // timeout in ms
300                                    MACH_PORT_NULL);
301  
302    return result;
303  }
304  
305  }  // namespace google_breakpad