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