diff options
Diffstat (limited to 'base')
-rw-r--r-- | base/base.gypi | 2 | ||||
-rw-r--r-- | base/mach_ipc_mac.h | 322 | ||||
-rw-r--r-- | base/mach_ipc_mac.mm | 308 | ||||
-rw-r--r-- | base/process_util.h | 15 | ||||
-rw-r--r-- | base/process_util_posix.cc | 144 |
5 files changed, 789 insertions, 2 deletions
diff --git a/base/base.gypi b/base/base.gypi index fc7c811..f7584c4 100644 --- a/base/base.gypi +++ b/base/base.gypi @@ -103,6 +103,8 @@ 'logging.h', 'mac_util.h', 'mac_util.mm', + 'mach_ipc_mac.h', + 'mach_ipc_mac.mm', 'memory_debug.cc', 'memory_debug.h', 'message_loop.cc', diff --git a/base/mach_ipc_mac.h b/base/mach_ipc_mac.h new file mode 100644 index 0000000..7e9839c --- /dev/null +++ b/base/mach_ipc_mac.h @@ -0,0 +1,322 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MACH_IPC_MAC_H_ +#define BASE_MACH_IPC_MAC_H_ + +#include <mach/mach.h> +#include <mach/message.h> +#include <servers/bootstrap.h> +#include <sys/types.h> + +#include <CoreServices/CoreServices.h> + +#include "base/basictypes.h" + +//============================================================================== +// DISCUSSION: +// +// The three main classes of interest are +// +// MachMessage: a wrapper for a mach message of the following form +// mach_msg_header_t +// mach_msg_body_t +// optional descriptors +// optional extra message data +// +// MachReceiveMessage and MachSendMessage subclass MachMessage +// and are used instead of MachMessage which is an abstract base class +// +// ReceivePort: +// Represents a mach port for which we have receive rights +// +// MachPortSender: +// Represents a mach port for which we have send rights +// +// Here's an example to receive a message on a server port: +// +// // This creates our named server port +// ReceivePort receivePort("com.Google.MyService"); +// +// MachReceiveMessage message; +// kern_return_t result = receivePort.WaitForMessage(&message, 0); +// +// if (result == KERN_SUCCESS && message.GetMessageID() == 57) { +// mach_port_t task = message.GetTranslatedPort(0); +// mach_port_t thread = message.GetTranslatedPort(1); +// +// char *messageString = message.GetData(); +// +// printf("message string = %s\n", messageString); +// } +// +// Here is an example of using these classes to send a message to this port: +// +// // send to already named port +// MachPortSender sender("com.Google.MyService"); +// MachSendMessage message(57); // our message ID is 57 +// +// // add some ports to be translated for us +// message.AddDescriptor(mach_task_self()); // our task +// message.AddDescriptor(mach_thread_self()); // this thread +// +// char messageString[] = "Hello server!\n"; +// message.SetData(messageString, strlen(messageString)+1); +// // timeout 1000ms +// kern_return_t result = sender.SendMessage(message, 1000); +// + +#define PRINT_MACH_RESULT(result_, message_) \ + printf(message_" %s (%d)\n", mach_error_string(result_), result_ ); + +//============================================================================== +// A wrapper class for mach_msg_port_descriptor_t (with same memory layout) +// with convenient constructors and accessors +class MachMsgPortDescriptor : public mach_msg_port_descriptor_t { + public: + // General-purpose constructor + MachMsgPortDescriptor(mach_port_t in_name, + mach_msg_type_name_t in_disposition) { + name = in_name; + pad1 = 0; + pad2 = 0; + disposition = in_disposition; + type = MACH_MSG_PORT_DESCRIPTOR; + } + + // For passing send rights to a port + MachMsgPortDescriptor(mach_port_t in_name) { + name = in_name; + pad1 = 0; + pad2 = 0; + disposition = MACH_MSG_TYPE_PORT_SEND; + type = MACH_MSG_PORT_DESCRIPTOR; + } + + // Copy constructor + MachMsgPortDescriptor(const MachMsgPortDescriptor& desc) { + name = desc.name; + pad1 = desc.pad1; + pad2 = desc.pad2; + disposition = desc.disposition; + type = desc.type; + } + + mach_port_t GetMachPort() const { + return name; + } + + mach_msg_type_name_t GetDisposition() const { + return disposition; + } + + // We're just a simple wrapper for mach_msg_port_descriptor_t + // and have the same memory layout + operator mach_msg_port_descriptor_t&() { + return *this; + } + + // For convenience + operator mach_port_t() const { + return GetMachPort(); + } +}; + +//============================================================================== +// MachMessage: a wrapper for a mach message +// (mach_msg_header_t, mach_msg_body_t, extra data) +// +// This considerably simplifies the construction of a message for sending +// and the getting at relevant data and descriptors for the receiver. +// +// This class can be initialized using external storage of an arbitrary size +// or it can manage storage internally. +// 1. If storage is allocated internally, the combined size of the descriptors +// plus data must be less than 1024. But as a benefit no memory allocation is +// necessary. +// 2. For external storage, a buffer of at least EmptyMessageSize() must be +// provided. +// +// A MachMessage object is used by ReceivePort::WaitForMessage +// and MachPortSender::SendMessage +// +class MachMessage { + public: + + virtual ~MachMessage(); + + // The receiver of the message can retrieve the raw data this way + u_int8_t *GetData() { + return GetDataLength() > 0 ? GetDataPacket()->data : NULL; + } + + u_int32_t GetDataLength() { + return EndianU32_LtoN(GetDataPacket()->data_length); + } + + // The message ID may be used as a code identifying the type of message + void SetMessageID(int32_t message_id) { + GetDataPacket()->id = EndianU32_NtoL(message_id); + } + + int32_t GetMessageID() { return EndianU32_LtoN(GetDataPacket()->id); } + + // Adds a descriptor (typically a mach port) to be translated + // returns true if successful, otherwise not enough space + bool AddDescriptor(const MachMsgPortDescriptor &desc); + + int GetDescriptorCount() const { + return storage_->body.msgh_descriptor_count; + } + + MachMsgPortDescriptor *GetDescriptor(int n); + + // Convenience method which gets the mach port described by the descriptor + mach_port_t GetTranslatedPort(int n); + + // A simple message is one with no descriptors + bool IsSimpleMessage() const { return GetDescriptorCount() == 0; } + + // Sets raw data for the message (returns false if not enough space) + bool SetData(const void* data, int32_t data_length); + + protected: + // Consider this an abstract base class - must create an actual instance + // of MachReceiveMessage or MachSendMessage + MachMessage(); + + // Constructor for use with preallocate storage. + // storage_length must be >= EmptyMessageSize() + MachMessage(void *storage, size_t storage_length); + + friend class ReceivePort; + friend class MachPortSender; + + // Represents raw data in our message + struct MessageDataPacket { + int32_t id; // little-endian + int32_t data_length; // little-endian + u_int8_t data[1]; // actual size limited by storage_length_bytes_ + }; + + MessageDataPacket* GetDataPacket(); + + void SetDescriptorCount(int n); + void SetDescriptor(int n, const MachMsgPortDescriptor &desc); + + // Returns total message size setting msgh_size in the header to this value + int CalculateSize(); + + // Returns total storage size that this object can grow to, this is inclusive + // of the mach header. + size_t MaxSize() const { return storage_length_bytes_; } + + protected: + mach_msg_header_t *Head() { return &(storage_->head); } + + private: + struct MachMessageData { + mach_msg_header_t head; + mach_msg_body_t body; + // descriptors and data may be embedded here. + u_int8_t padding[1024]; + }; + + // kEmptyMessageSize needs to have the definition of MachMessageData before + // it. + public: + // The size of an empty message with no data. + static const size_t kEmptyMessageSize = sizeof(mach_msg_header_t) + + sizeof(mach_msg_body_t) + + sizeof(MessageDataPacket); + + private: + MachMessageData *storage_; + size_t storage_length_bytes_; + bool own_storage_; // Is storage owned by this object? +}; + +//============================================================================== +// MachReceiveMessage and MachSendMessage are useful to separate the idea +// of a mach message being sent and being received, and adds increased type +// safety: +// ReceivePort::WaitForMessage() only accepts a MachReceiveMessage +// MachPortSender::SendMessage() only accepts a MachSendMessage + +//============================================================================== +class MachReceiveMessage : public MachMessage { + public: + MachReceiveMessage() : MachMessage() {} + MachReceiveMessage(void *storage, size_t storage_length) + : MachMessage(storage, storage_length) {} + + private: + DISALLOW_COPY_AND_ASSIGN(MachReceiveMessage); +}; + +//============================================================================== +class MachSendMessage : public MachMessage { + public: + explicit MachSendMessage(int32_t message_id); + MachSendMessage(void *storage, size_t storage_length, int32_t message_id); + + private: + void Initialize(int32_t message_id); + + DISALLOW_COPY_AND_ASSIGN(MachSendMessage); +}; + +//============================================================================== +// Represents a mach port for which we have receive rights +class ReceivePort { + public: + // Creates a new mach port for receiving messages and registers a name for it + explicit ReceivePort(const char *receive_port_name); + + // Given an already existing mach port, use it. We take ownership of the + // port and deallocate it in our destructor. + explicit ReceivePort(mach_port_t receive_port); + + // Create a new mach port for receiving messages + ReceivePort(); + + ~ReceivePort(); + + // Waits on the mach port until message received or timeout + kern_return_t WaitForMessage(MachReceiveMessage *out_message, + mach_msg_timeout_t timeout); + + // The underlying mach port that we wrap + mach_port_t GetPort() const { return port_; } + + private: + mach_port_t port_; + kern_return_t init_result_; + + DISALLOW_COPY_AND_ASSIGN(ReceivePort); +}; + +//============================================================================== +// Represents a mach port for which we have send rights +class MachPortSender { + public: + // get a port with send rights corresponding to a named registered service + explicit MachPortSender(const char *receive_port_name); + + + // Given an already existing mach port, use it. Does not take ownership of + // |send_port|. + explicit MachPortSender(mach_port_t send_port); + + kern_return_t SendMessage(MachSendMessage &message, + mach_msg_timeout_t timeout); + + private: + mach_port_t send_port_; + kern_return_t init_result_; + + DISALLOW_COPY_AND_ASSIGN(MachPortSender); +}; + +#endif // BASE_MACH_IPC_MAC_H_ diff --git a/base/mach_ipc_mac.mm b/base/mach_ipc_mac.mm new file mode 100644 index 0000000..fc78bcc --- /dev/null +++ b/base/mach_ipc_mac.mm @@ -0,0 +1,308 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/mach_ipc_mac.h" + +#import <Foundation/Foundation.h> + +#include <stdio.h> +#include "base/logging.h" + +//============================================================================== +MachSendMessage::MachSendMessage(int32_t message_id) : MachMessage() { + Initialize(message_id); +} + +MachSendMessage::MachSendMessage(void *storage, size_t storage_length, + int32_t message_id) + : MachMessage(storage, storage_length) { + Initialize(message_id); +} + +void MachSendMessage::Initialize(int32_t message_id) { + Head()->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); + + // head.msgh_remote_port = ...; // filled out in MachPortSender::SendMessage() + Head()->msgh_local_port = MACH_PORT_NULL; + Head()->msgh_reserved = 0; + Head()->msgh_id = 0; + + SetDescriptorCount(0); // start out with no descriptors + + SetMessageID(message_id); + SetData(NULL, 0); // client may add data later +} + +//============================================================================== +MachMessage::MachMessage() + : storage_(new MachMessageData), // Allocate storage_ ourselves + storage_length_bytes_(sizeof(MachMessageData)), + own_storage_(true) { + memset(storage_, 0, storage_length_bytes_); +} + +//============================================================================== +MachMessage::MachMessage(void *storage, size_t storage_length) + : storage_(static_cast<MachMessageData*>(storage)), + storage_length_bytes_(storage_length), + own_storage_(false) { + DCHECK(storage); + DCHECK(storage_length >= kEmptyMessageSize); +} + +//============================================================================== +MachMessage::~MachMessage() { + if (own_storage_) { + delete storage_; + storage_ = NULL; + } +} + +//============================================================================== +// returns true if successful +bool MachMessage::SetData(const void* data, + int32_t data_length) { + // Enforce the fact that it's only safe to call this method once on a + // message. + DCHECK(GetDataPacket()->data_length == 0); + + // first check to make sure we have enough space + int size = CalculateSize(); + int new_size = size + data_length; + + if ((unsigned)new_size > storage_length_bytes_) { + return false; // not enough space + } + + GetDataPacket()->data_length = EndianU32_NtoL(data_length); + if (data) memcpy(GetDataPacket()->data, data, data_length); + + // Update the Mach header with the new aligned size of the message. + CalculateSize(); + + return true; +} + +//============================================================================== +// calculates and returns the total size of the message +// Currently, the entire message MUST fit inside of the MachMessage +// messsage size <= EmptyMessageSize() +int MachMessage::CalculateSize() { + int size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t); + + // add space for MessageDataPacket + int32_t alignedDataLength = (GetDataLength() + 3) & ~0x3; + size += 2*sizeof(int32_t) + alignedDataLength; + + // add space for descriptors + size += GetDescriptorCount() * sizeof(MachMsgPortDescriptor); + + Head()->msgh_size = size; + + return size; +} + +//============================================================================== +MachMessage::MessageDataPacket *MachMessage::GetDataPacket() { + int desc_size = sizeof(MachMsgPortDescriptor)*GetDescriptorCount(); + MessageDataPacket *packet = + reinterpret_cast<MessageDataPacket*>(storage_->padding + desc_size); + + return packet; +} + +//============================================================================== +void MachMessage::SetDescriptor(int n, + const MachMsgPortDescriptor &desc) { + MachMsgPortDescriptor *desc_array = + reinterpret_cast<MachMsgPortDescriptor*>(storage_->padding); + desc_array[n] = desc; +} + +//============================================================================== +// returns true if successful otherwise there was not enough space +bool MachMessage::AddDescriptor(const MachMsgPortDescriptor &desc) { + // first check to make sure we have enough space + int size = CalculateSize(); + int new_size = size + sizeof(MachMsgPortDescriptor); + + if ((unsigned)new_size > storage_length_bytes_) { + return false; // not enough space + } + + // unfortunately, we need to move the data to allow space for the + // new descriptor + u_int8_t *p = reinterpret_cast<u_int8_t*>(GetDataPacket()); + bcopy(p, p+sizeof(MachMsgPortDescriptor), GetDataLength()+2*sizeof(int32_t)); + + SetDescriptor(GetDescriptorCount(), desc); + SetDescriptorCount(GetDescriptorCount() + 1); + + CalculateSize(); + + return true; +} + +//============================================================================== +void MachMessage::SetDescriptorCount(int n) { + storage_->body.msgh_descriptor_count = n; + + if (n > 0) { + Head()->msgh_bits |= MACH_MSGH_BITS_COMPLEX; + } else { + Head()->msgh_bits &= ~MACH_MSGH_BITS_COMPLEX; + } +} + +//============================================================================== +MachMsgPortDescriptor *MachMessage::GetDescriptor(int n) { + if (n < GetDescriptorCount()) { + MachMsgPortDescriptor *desc = + reinterpret_cast<MachMsgPortDescriptor*>(storage_->padding); + return desc + n; + } + + return nil; +} + +//============================================================================== +mach_port_t MachMessage::GetTranslatedPort(int n) { + if (n < GetDescriptorCount()) { + return GetDescriptor(n)->GetMachPort(); + } + return MACH_PORT_NULL; +} + +#pragma mark - + +//============================================================================== +// create a new mach port for receiving messages and register a name for it +ReceivePort::ReceivePort(const char *receive_port_name) { + mach_port_t current_task = mach_task_self(); + + init_result_ = mach_port_allocate(current_task, + MACH_PORT_RIGHT_RECEIVE, + &port_); + + if (init_result_ != KERN_SUCCESS) + return; + + init_result_ = mach_port_insert_right(current_task, + port_, + port_, + MACH_MSG_TYPE_MAKE_SEND); + + if (init_result_ != KERN_SUCCESS) + return; + + NSPort *ns_port = [NSMachPort portWithMachPort:port_]; + NSString *port_name = [NSString stringWithUTF8String:receive_port_name]; + [[NSMachBootstrapServer sharedInstance] registerPort:ns_port name:port_name]; +} + +//============================================================================== +// create a new mach port for receiving messages +ReceivePort::ReceivePort() { + mach_port_t current_task = mach_task_self(); + + init_result_ = mach_port_allocate(current_task, + MACH_PORT_RIGHT_RECEIVE, + &port_); + + if (init_result_ != KERN_SUCCESS) + return; + + init_result_ = mach_port_insert_right(current_task, + port_, + port_, + MACH_MSG_TYPE_MAKE_SEND); +} + +//============================================================================== +// Given an already existing mach port, use it. We take ownership of the +// port and deallocate it in our destructor. +ReceivePort::ReceivePort(mach_port_t receive_port) + : port_(receive_port), + init_result_(KERN_SUCCESS) { +} + +//============================================================================== +ReceivePort::~ReceivePort() { + if (init_result_ == KERN_SUCCESS) + mach_port_deallocate(mach_task_self(), port_); +} + +//============================================================================== +kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message, + mach_msg_timeout_t timeout) { + if (!out_message) { + return KERN_INVALID_ARGUMENT; + } + + // return any error condition encountered in constructor + if (init_result_ != KERN_SUCCESS) + return init_result_; + + out_message->Head()->msgh_bits = 0; + out_message->Head()->msgh_local_port = port_; + out_message->Head()->msgh_remote_port = MACH_PORT_NULL; + out_message->Head()->msgh_reserved = 0; + out_message->Head()->msgh_id = 0; + + kern_return_t result = mach_msg(out_message->Head(), + MACH_RCV_MSG | MACH_RCV_TIMEOUT, + 0, + out_message->MaxSize(), + port_, + timeout, // timeout in ms + MACH_PORT_NULL); + + return result; +} + +#pragma mark - + +//============================================================================== +// get a port with send rights corresponding to a named registered service +MachPortSender::MachPortSender(const char *receive_port_name) { + mach_port_t bootstrap_port = 0; + init_result_ = task_get_bootstrap_port(mach_task_self(), &bootstrap_port); + + if (init_result_ != KERN_SUCCESS) + return; + + init_result_ = bootstrap_look_up(bootstrap_port, + const_cast<char*>(receive_port_name), + &send_port_); +} + +//============================================================================== +MachPortSender::MachPortSender(mach_port_t send_port) + : send_port_(send_port), + init_result_(KERN_SUCCESS) { +} + +//============================================================================== +kern_return_t MachPortSender::SendMessage(MachSendMessage &message, + mach_msg_timeout_t timeout) { + if (message.Head()->msgh_size == 0) { + NOTREACHED(); + return KERN_INVALID_VALUE; // just for safety -- never should occur + }; + + if (init_result_ != KERN_SUCCESS) + return init_result_; + + message.Head()->msgh_remote_port = send_port_; + + kern_return_t result = mach_msg(message.Head(), + MACH_SEND_MSG | MACH_SEND_TIMEOUT, + message.Head()->msgh_size, + 0, + MACH_PORT_NULL, + timeout, // timeout in ms + MACH_PORT_NULL); + + return result; +} diff --git a/base/process_util.h b/base/process_util.h index cec37bd..eb21188 100644 --- a/base/process_util.h +++ b/base/process_util.h @@ -17,6 +17,7 @@ // kinfo_proc is defined in <sys/sysctl.h>, but this forward declaration // is sufficient for the vector<kinfo_proc> below. struct kinfo_proc; +#include <mach/mach.h> #elif defined(OS_POSIX) #include <dirent.h> #include <limits.h> @@ -155,13 +156,25 @@ bool LaunchApp(const std::vector<std::string>& argv, const file_handle_mapping_vector& fds_to_remap, bool wait, ProcessHandle* process_handle); -// Similar to above, but also (un)set environment variables in child process +// Similar to the above, but also (un)set environment variables in child process // through |environ|. typedef std::vector<std::pair<std::string, std::string> > environment_vector; bool LaunchApp(const std::vector<std::string>& argv, const environment_vector& environ, const file_handle_mapping_vector& fds_to_remap, bool wait, ProcessHandle* process_handle); + +#if defined(OS_MACOSX) +// Similar to the above, but also returns the new process's task_t if +// |task_handle| is not NULL. If |task_handle| is not NULL, the caller is +// responsible for calling |mach_port_deallocate()| on the returned handle. +bool LaunchAppAndGetTask(const std::vector<std::string>& argv, + const environment_vector& environ, + const file_handle_mapping_vector& fds_to_remap, + bool wait, + task_t* task_handle, + ProcessHandle* process_handle); +#endif // defined(OS_MACOSX) #endif // defined(OS_POSIX) // Executes the application specified by cl. This function delegates to one diff --git a/base/process_util_posix.cc b/base/process_util_posix.cc index 21626732..a771fa2 100644 --- a/base/process_util_posix.cc +++ b/base/process_util_posix.cc @@ -21,11 +21,16 @@ #include "base/logging.h" #include "base/platform_thread.h" #include "base/process_util.h" +#include "base/rand_util.h" #include "base/scoped_ptr.h" #include "base/sys_info.h" #include "base/time.h" #include "base/waitable_event.h" +#if defined(OS_MACOSX) +#include "base/mach_ipc_mac.h" +#endif + const int kMicrosecondsPerSecond = 1000000; namespace base { @@ -280,11 +285,148 @@ void SetAllFDsToCloseOnExec() { } } +#if defined(OS_MACOSX) +static std::string MachErrorCode(kern_return_t err) { + return StringPrintf("0x%x %s", err, mach_error_string(err)); +} + +// Forks the current process and returns the child's |task_t| in the parent +// process. +static pid_t fork_and_get_task(task_t* child_task) { + const int kTimeoutMs = 100; + kern_return_t err; + + // Put a random number into the channel name, so that a compromised renderer + // can't pretend being the child that's forked off. + std::string mach_connection_name = StringPrintf( + "com.google.Chrome.samplingfork.%p.%d", + child_task, base::RandInt(0, std::numeric_limits<int>::max())); + ReceivePort parent_recv_port(mach_connection_name.c_str()); + + // Error handling philosophy: If Mach IPC fails, don't touch |child_task| but + // return a valid pid. If IPC fails in the child, the parent will have to wait + // until kTimeoutMs is over. This is not optimal, but I've never seen it + // happen, and stuff should still mostly work. + pid_t pid = fork(); + switch (pid) { + case -1: + return pid; + case 0: { // child + ReceivePort child_recv_port; + + MachSendMessage child_message(/* id= */0); + if (!child_message.AddDescriptor(mach_task_self())) { + LOG(ERROR) << "child AddDescriptor(mach_task_self()) failed."; + return pid; + } + mach_port_t raw_child_recv_port = child_recv_port.GetPort(); + if (!child_message.AddDescriptor(raw_child_recv_port)) { + LOG(ERROR) << "child AddDescriptor(" << raw_child_recv_port + << ") failed."; + return pid; + } + + MachPortSender child_sender(mach_connection_name.c_str()); + err = child_sender.SendMessage(child_message, kTimeoutMs); + if (err != KERN_SUCCESS) { + LOG(ERROR) << "child SendMessage() failed: " << MachErrorCode(err); + return pid; + } + + MachReceiveMessage parent_message; + err = child_recv_port.WaitForMessage(&parent_message, kTimeoutMs); + if (err != KERN_SUCCESS) { + LOG(ERROR) << "child WaitForMessage() failed: " << MachErrorCode(err); + return pid; + } + + if (parent_message.GetTranslatedPort(0) == MACH_PORT_NULL) { + LOG(ERROR) << "child GetTranslatedPort(0) failed."; + return pid; + } + err = task_set_bootstrap_port(mach_task_self(), + parent_message.GetTranslatedPort(0)); + if (err != KERN_SUCCESS) { + LOG(ERROR) << "child task_set_bootstrap_port() failed: " + << MachErrorCode(err); + return pid; + } + break; + } + default: { // parent + MachReceiveMessage child_message; + err = parent_recv_port.WaitForMessage(&child_message, kTimeoutMs); + if (err != KERN_SUCCESS) { + LOG(ERROR) << "parent WaitForMessage() failed: " << MachErrorCode(err); + return pid; + } + + if (child_message.GetTranslatedPort(0) == MACH_PORT_NULL) { + LOG(ERROR) << "parent GetTranslatedPort(0) failed."; + return pid; + } + *child_task = child_message.GetTranslatedPort(0); + + if (child_message.GetTranslatedPort(1) == MACH_PORT_NULL) { + LOG(ERROR) << "parent GetTranslatedPort(1) failed."; + return pid; + } + MachPortSender parent_sender(child_message.GetTranslatedPort(1)); + + MachSendMessage parent_message(/* id= */0); + if (!parent_message.AddDescriptor(bootstrap_port)) { + LOG(ERROR) << "parent AddDescriptor(" << bootstrap_port << ") failed."; + return pid; + } + + err = parent_sender.SendMessage(parent_message, kTimeoutMs); + if (err != KERN_SUCCESS) { + LOG(ERROR) << "parent SendMessage() failed: " << MachErrorCode(err); + return pid; + } + break; + } + } + return pid; +} + bool LaunchApp(const std::vector<std::string>& argv, const environment_vector& environ, const file_handle_mapping_vector& fds_to_remap, bool wait, ProcessHandle* process_handle) { - pid_t pid = fork(); + return LaunchAppAndGetTask( + argv, environ, fds_to_remap, wait, NULL, process_handle); +} +#endif // defined(OS_MACOSX) + +#if defined(OS_MACOSX) +bool LaunchAppAndGetTask( +#else +bool LaunchApp( +#endif + const std::vector<std::string>& argv, + const environment_vector& environ, + const file_handle_mapping_vector& fds_to_remap, + bool wait, +#if defined(OS_MACOSX) + task_t* task_handle, +#endif + ProcessHandle* process_handle) { + pid_t pid; +#if defined(OS_MACOSX) + if (task_handle == NULL) { + pid = fork(); + } else { + // On OS X, the task_t for a process is needed for several reasons. Sadly, + // the function task_for_pid() requires privileges a normal user doesn't + // have. Instead, a short-lived Mach IPC connection is opened between parent + // and child, and the child sends its task_t to the parent at fork time. + *task_handle = MACH_PORT_NULL; + pid = fork_and_get_task(task_handle); + } +#else + pid = fork(); +#endif if (pid < 0) return false; |