Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/mach_ipc_mac.h"
      6 
      7 #import <Foundation/Foundation.h>
      8 
      9 #include <stdio.h>
     10 #include "base/logging.h"
     11 
     12 namespace base {
     13 
     14 // static
     15 const size_t MachMessage::kEmptyMessageSize = sizeof(mach_msg_header_t) +
     16     sizeof(mach_msg_body_t) + sizeof(MessageDataPacket);
     17 
     18 //==============================================================================
     19 MachSendMessage::MachSendMessage(int32_t message_id) : MachMessage() {
     20   Initialize(message_id);
     21 }
     22 
     23 MachSendMessage::MachSendMessage(void *storage, size_t storage_length,
     24                                  int32_t message_id)
     25     : MachMessage(storage, storage_length) {
     26   Initialize(message_id);
     27 }
     28 
     29 void MachSendMessage::Initialize(int32_t message_id) {
     30   Head()->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
     31 
     32   // head.msgh_remote_port = ...; // filled out in MachPortSender::SendMessage()
     33   Head()->msgh_local_port = MACH_PORT_NULL;
     34   Head()->msgh_reserved = 0;
     35   Head()->msgh_id = 0;
     36 
     37   SetDescriptorCount(0);  // start out with no descriptors
     38 
     39   SetMessageID(message_id);
     40   SetData(NULL, 0);       // client may add data later
     41 }
     42 
     43 //==============================================================================
     44 MachMessage::MachMessage()
     45     : storage_(new MachMessageData),  // Allocate storage_ ourselves
     46       storage_length_bytes_(sizeof(MachMessageData)),
     47       own_storage_(true) {
     48   memset(storage_, 0, storage_length_bytes_);
     49 }
     50 
     51 //==============================================================================
     52 MachMessage::MachMessage(void *storage, size_t storage_length)
     53     : storage_(static_cast<MachMessageData*>(storage)),
     54       storage_length_bytes_(storage_length),
     55       own_storage_(false) {
     56   DCHECK(storage);
     57   DCHECK_GE(storage_length, kEmptyMessageSize);
     58 }
     59 
     60 //==============================================================================
     61 MachMessage::~MachMessage() {
     62   if (own_storage_) {
     63     delete storage_;
     64     storage_ = NULL;
     65   }
     66 }
     67 
     68 //==============================================================================
     69 // returns true if successful
     70 bool MachMessage::SetData(const void* data,
     71                           int32_t data_length) {
     72   // Enforce the fact that it's only safe to call this method once on a
     73   // message.
     74   DCHECK(GetDataPacket()->data_length == 0);
     75 
     76   // first check to make sure we have enough space
     77   int size = CalculateSize();
     78   int new_size = size + data_length;
     79 
     80   if ((unsigned)new_size > storage_length_bytes_) {
     81     return false;  // not enough space
     82   }
     83 
     84   GetDataPacket()->data_length = EndianU32_NtoL(data_length);
     85   if (data) memcpy(GetDataPacket()->data, data, data_length);
     86 
     87   // Update the Mach header with the new aligned size of the message.
     88   CalculateSize();
     89 
     90   return true;
     91 }
     92 
     93 //==============================================================================
     94 // calculates and returns the total size of the message
     95 // Currently, the entire message MUST fit inside of the MachMessage
     96 //    messsage size <= EmptyMessageSize()
     97 int MachMessage::CalculateSize() {
     98   int size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t);
     99 
    100   // add space for MessageDataPacket
    101   int32_t alignedDataLength = (GetDataLength() + 3) & ~0x3;
    102   size += 2*sizeof(int32_t) + alignedDataLength;
    103 
    104   // add space for descriptors
    105   size += GetDescriptorCount() * sizeof(MachMsgPortDescriptor);
    106 
    107   Head()->msgh_size = size;
    108 
    109   return size;
    110 }
    111 
    112 //==============================================================================
    113 MachMessage::MessageDataPacket *MachMessage::GetDataPacket() {
    114   int desc_size = sizeof(MachMsgPortDescriptor)*GetDescriptorCount();
    115   MessageDataPacket *packet =
    116     reinterpret_cast<MessageDataPacket*>(storage_->padding + desc_size);
    117 
    118   return packet;
    119 }
    120 
    121 //==============================================================================
    122 void MachMessage::SetDescriptor(int n,
    123                                 const MachMsgPortDescriptor &desc) {
    124   MachMsgPortDescriptor *desc_array =
    125     reinterpret_cast<MachMsgPortDescriptor*>(storage_->padding);
    126   desc_array[n] = desc;
    127 }
    128 
    129 //==============================================================================
    130 // returns true if successful otherwise there was not enough space
    131 bool MachMessage::AddDescriptor(const MachMsgPortDescriptor &desc) {
    132   // first check to make sure we have enough space
    133   int size = CalculateSize();
    134   int new_size = size + sizeof(MachMsgPortDescriptor);
    135 
    136   if ((unsigned)new_size > storage_length_bytes_) {
    137     return false;  // not enough space
    138   }
    139 
    140   // unfortunately, we need to move the data to allow space for the
    141   // new descriptor
    142   u_int8_t *p = reinterpret_cast<u_int8_t*>(GetDataPacket());
    143   bcopy(p, p+sizeof(MachMsgPortDescriptor), GetDataLength()+2*sizeof(int32_t));
    144 
    145   SetDescriptor(GetDescriptorCount(), desc);
    146   SetDescriptorCount(GetDescriptorCount() + 1);
    147 
    148   CalculateSize();
    149 
    150   return true;
    151 }
    152 
    153 //==============================================================================
    154 void MachMessage::SetDescriptorCount(int n) {
    155   storage_->body.msgh_descriptor_count = n;
    156 
    157   if (n > 0) {
    158     Head()->msgh_bits |= MACH_MSGH_BITS_COMPLEX;
    159   } else {
    160     Head()->msgh_bits &= ~MACH_MSGH_BITS_COMPLEX;
    161   }
    162 }
    163 
    164 //==============================================================================
    165 MachMsgPortDescriptor *MachMessage::GetDescriptor(int n) {
    166   if (n < GetDescriptorCount()) {
    167     MachMsgPortDescriptor *desc =
    168         reinterpret_cast<MachMsgPortDescriptor*>(storage_->padding);
    169     return desc + n;
    170   }
    171 
    172   return nil;
    173 }
    174 
    175 //==============================================================================
    176 mach_port_t MachMessage::GetTranslatedPort(int n) {
    177   if (n < GetDescriptorCount()) {
    178     return GetDescriptor(n)->GetMachPort();
    179   }
    180   return MACH_PORT_NULL;
    181 }
    182 
    183 #pragma mark -
    184 
    185 //==============================================================================
    186 // create a new mach port for receiving messages and register a name for it
    187 ReceivePort::ReceivePort(const char *receive_port_name) {
    188   mach_port_t current_task = mach_task_self();
    189 
    190   init_result_ = mach_port_allocate(current_task,
    191                                     MACH_PORT_RIGHT_RECEIVE,
    192                                     &port_);
    193 
    194   if (init_result_ != KERN_SUCCESS)
    195     return;
    196 
    197   init_result_ = mach_port_insert_right(current_task,
    198                                         port_,
    199                                         port_,
    200                                         MACH_MSG_TYPE_MAKE_SEND);
    201 
    202   if (init_result_ != KERN_SUCCESS)
    203     return;
    204 
    205   // Without |NSMachPortDeallocateNone|, the NSMachPort seems to deallocate
    206   // receive rights on port when it is eventually released.  It is not necessary
    207   // to deallocate any rights here as |port_| is fully deallocated in the
    208   // ReceivePort destructor.
    209   NSPort *ns_port = [NSMachPort portWithMachPort:port_
    210                                          options:NSMachPortDeallocateNone];
    211   NSString *port_name = [NSString stringWithUTF8String:receive_port_name];
    212   [[NSMachBootstrapServer sharedInstance] registerPort:ns_port name:port_name];
    213 }
    214 
    215 //==============================================================================
    216 // create a new mach port for receiving messages
    217 ReceivePort::ReceivePort() {
    218   mach_port_t current_task = mach_task_self();
    219 
    220   init_result_ = mach_port_allocate(current_task,
    221                                     MACH_PORT_RIGHT_RECEIVE,
    222                                     &port_);
    223 
    224   if (init_result_ != KERN_SUCCESS)
    225     return;
    226 
    227   init_result_ = mach_port_insert_right(current_task,
    228                                         port_,
    229                                         port_,
    230                                         MACH_MSG_TYPE_MAKE_SEND);
    231 }
    232 
    233 //==============================================================================
    234 // Given an already existing mach port, use it.  We take ownership of the
    235 // port and deallocate it in our destructor.
    236 ReceivePort::ReceivePort(mach_port_t receive_port)
    237   : port_(receive_port),
    238     init_result_(KERN_SUCCESS) {
    239 }
    240 
    241 //==============================================================================
    242 ReceivePort::~ReceivePort() {
    243   if (init_result_ == KERN_SUCCESS)
    244     mach_port_deallocate(mach_task_self(), port_);
    245 }
    246 
    247 //==============================================================================
    248 kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message,
    249                                           mach_msg_timeout_t timeout) {
    250   if (!out_message) {
    251     return KERN_INVALID_ARGUMENT;
    252   }
    253 
    254   // return any error condition encountered in constructor
    255   if (init_result_ != KERN_SUCCESS)
    256     return init_result_;
    257 
    258   out_message->Head()->msgh_bits = 0;
    259   out_message->Head()->msgh_local_port = port_;
    260   out_message->Head()->msgh_remote_port = MACH_PORT_NULL;
    261   out_message->Head()->msgh_reserved = 0;
    262   out_message->Head()->msgh_id = 0;
    263 
    264   mach_msg_option_t rcv_options = MACH_RCV_MSG;
    265   if (timeout != MACH_MSG_TIMEOUT_NONE)
    266     rcv_options |= MACH_RCV_TIMEOUT;
    267 
    268   kern_return_t result = mach_msg(out_message->Head(),
    269                                   rcv_options,
    270                                   0,
    271                                   out_message->MaxSize(),
    272                                   port_,
    273                                   timeout,              // timeout in ms
    274                                   MACH_PORT_NULL);
    275 
    276   return result;
    277 }
    278 
    279 #pragma mark -
    280 
    281 //==============================================================================
    282 // get a port with send rights corresponding to a named registered service
    283 MachPortSender::MachPortSender(const char *receive_port_name) {
    284   mach_port_t bootstrap_port = 0;
    285   init_result_ = task_get_bootstrap_port(mach_task_self(), &bootstrap_port);
    286 
    287   if (init_result_ != KERN_SUCCESS)
    288     return;
    289 
    290   init_result_ = bootstrap_look_up(bootstrap_port,
    291                     const_cast<char*>(receive_port_name),
    292                     &send_port_);
    293 }
    294 
    295 //==============================================================================
    296 MachPortSender::MachPortSender(mach_port_t send_port)
    297   : send_port_(send_port),
    298     init_result_(KERN_SUCCESS) {
    299 }
    300 
    301 //==============================================================================
    302 kern_return_t MachPortSender::SendMessage(MachSendMessage &message,
    303                                           mach_msg_timeout_t timeout) {
    304   if (message.Head()->msgh_size == 0) {
    305     NOTREACHED();
    306     return KERN_INVALID_VALUE;    // just for safety -- never should occur
    307   };
    308 
    309   if (init_result_ != KERN_SUCCESS)
    310     return init_result_;
    311 
    312   message.Head()->msgh_remote_port = send_port_;
    313 
    314   kern_return_t result = mach_msg(message.Head(),
    315                                   MACH_SEND_MSG | MACH_SEND_TIMEOUT,
    316                                   message.Head()->msgh_size,
    317                                   0,
    318                                   MACH_PORT_NULL,
    319                                   timeout,              // timeout in ms
    320                                   MACH_PORT_NULL);
    321 
    322   return result;
    323 }
    324 
    325 }  // namespace base
    326