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