| //===-- ConnectionMachPort.cpp ----------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| #if defined(__APPLE__) |
| |
| #include "lldb/Core/ConnectionMachPort.h" |
| |
| // C Includes |
| #include <servers/bootstrap.h> |
| |
| // C++ Includes |
| // Other libraries and framework includes |
| // Project includes |
| #include "lldb/lldb-private-log.h" |
| #include "lldb/Core/Communication.h" |
| #include "lldb/Core/Log.h" |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| struct MessageType |
| { |
| mach_msg_header_t head; |
| ConnectionMachPort::PayloadType payload; |
| }; |
| |
| |
| |
| ConnectionMachPort::ConnectionMachPort () : |
| Connection(), |
| m_task(mach_task_self()), |
| m_port(MACH_PORT_TYPE_NONE) |
| { |
| } |
| |
| ConnectionMachPort::~ConnectionMachPort () |
| { |
| Disconnect (NULL); |
| } |
| |
| bool |
| ConnectionMachPort::IsConnected () const |
| { |
| return m_port != MACH_PORT_TYPE_NONE; |
| } |
| |
| ConnectionStatus |
| ConnectionMachPort::Connect (const char *s, Error *error_ptr) |
| { |
| if (IsConnected()) |
| { |
| if (error_ptr) |
| error_ptr->SetErrorString ("already connected"); |
| return eConnectionStatusError; |
| } |
| |
| if (s == NULL || s[0] == '\0') |
| { |
| if (error_ptr) |
| error_ptr->SetErrorString ("empty connect URL"); |
| return eConnectionStatusError; |
| } |
| |
| ConnectionStatus status = eConnectionStatusError; |
| |
| if (strncmp (s, "bootstrap-checkin://", strlen("bootstrap-checkin://"))) |
| { |
| s += strlen("bootstrap-checkin://"); |
| |
| if (*s) |
| { |
| status = BootstrapCheckIn (s, error_ptr); |
| } |
| else |
| { |
| if (error_ptr) |
| error_ptr->SetErrorString ("bootstrap port name is empty"); |
| } |
| } |
| else if (strncmp (s, "bootstrap-lookup://", strlen("bootstrap-lookup://"))) |
| { |
| s += strlen("bootstrap-lookup://"); |
| if (*s) |
| { |
| status = BootstrapLookup (s, error_ptr); |
| } |
| else |
| { |
| if (error_ptr) |
| error_ptr->SetErrorString ("bootstrap port name is empty"); |
| } |
| } |
| else |
| { |
| if (error_ptr) |
| error_ptr->SetErrorStringWithFormat ("unsupported connection URL: '%s'", s); |
| } |
| |
| |
| if (status == eConnectionStatusSuccess) |
| { |
| if (error_ptr) |
| error_ptr->Clear(); |
| } |
| else |
| { |
| Disconnect(NULL); |
| } |
| |
| return status; |
| } |
| |
| ConnectionStatus |
| ConnectionMachPort::BootstrapCheckIn (const char *port, Error *error_ptr) |
| { |
| mach_port_t bootstrap_port = MACH_PORT_TYPE_NONE; |
| |
| /* Getting bootstrap server port */ |
| kern_return_t kret = task_get_bootstrap_port(mach_task_self(), &bootstrap_port); |
| if (kret == KERN_SUCCESS) |
| { |
| name_t port_name; |
| int len = snprintf(port_name, sizeof(port_name), "%s", port); |
| if (len < sizeof(port_name)) |
| { |
| kret = ::bootstrap_check_in (bootstrap_port, |
| port_name, |
| &m_port); |
| } |
| else |
| { |
| Disconnect(NULL); |
| if (error_ptr) |
| error_ptr->SetErrorString ("bootstrap is too long"); |
| return eConnectionStatusError; |
| } |
| } |
| |
| if (kret != KERN_SUCCESS) |
| { |
| Disconnect(NULL); |
| if (error_ptr) |
| error_ptr->SetError (kret, eErrorTypeMachKernel); |
| return eConnectionStatusError; |
| } |
| return eConnectionStatusSuccess; |
| } |
| |
| lldb::ConnectionStatus |
| ConnectionMachPort::BootstrapLookup (const char *port, |
| Error *error_ptr) |
| { |
| name_t port_name; |
| |
| if (port && port[0]) |
| { |
| if (::snprintf (port_name, sizeof (port_name), "%s", port) >= sizeof (port_name)) |
| { |
| if (error_ptr) |
| error_ptr->SetErrorString ("port netname is too long"); |
| return eConnectionStatusError; |
| } |
| } |
| else |
| { |
| if (error_ptr) |
| error_ptr->SetErrorString ("empty port netname"); |
| return eConnectionStatusError; |
| } |
| |
| mach_port_t bootstrap_port = MACH_PORT_TYPE_NONE; |
| |
| /* Getting bootstrap server port */ |
| kern_return_t kret = task_get_bootstrap_port(mach_task_self(), &bootstrap_port); |
| if (kret == KERN_SUCCESS) |
| { |
| kret = ::bootstrap_look_up (bootstrap_port, |
| port_name, |
| &m_port); |
| } |
| |
| if (kret != KERN_SUCCESS) |
| { |
| if (error_ptr) |
| error_ptr->SetError (kret, eErrorTypeMachKernel); |
| return eConnectionStatusError; |
| } |
| |
| return eConnectionStatusSuccess; |
| } |
| |
| ConnectionStatus |
| ConnectionMachPort::Disconnect (Error *error_ptr) |
| { |
| kern_return_t kret; |
| |
| // TODO: verify if we need to netname_check_out for |
| // either or both |
| if (m_port != MACH_PORT_TYPE_NONE) |
| { |
| kret = ::mach_port_deallocate (m_task, m_port); |
| if (error_ptr) |
| error_ptr->SetError (kret, eErrorTypeMachKernel); |
| m_port = MACH_PORT_TYPE_NONE; |
| } |
| |
| return eConnectionStatusSuccess; |
| } |
| |
| size_t |
| ConnectionMachPort::Read (void *dst, |
| size_t dst_len, |
| uint32_t timeout_usec, |
| ConnectionStatus &status, |
| Error *error_ptr) |
| { |
| PayloadType payload; |
| |
| kern_return_t kret = Receive (payload); |
| if (kret == KERN_SUCCESS) |
| { |
| memcpy (dst, payload.data, payload.data_length); |
| status = eConnectionStatusSuccess; |
| return payload.data_length; |
| } |
| |
| if (error_ptr) |
| error_ptr->SetError (kret, eErrorTypeMachKernel); |
| status = eConnectionStatusError; |
| return 0; |
| } |
| |
| size_t |
| ConnectionMachPort::Write (const void *src, size_t src_len, ConnectionStatus &status, Error *error_ptr) |
| { |
| PayloadType payload; |
| payload.command = 0; |
| payload.data_length = src_len; |
| const size_t max_payload_size = sizeof(payload.data); |
| if (src_len > max_payload_size) |
| payload.data_length = max_payload_size; |
| memcpy (payload.data, src, payload.data_length); |
| |
| if (Send (payload) == KERN_SUCCESS) |
| { |
| status = eConnectionStatusSuccess; |
| return payload.data_length; |
| } |
| status = eConnectionStatusError; |
| return 0; |
| } |
| |
| ConnectionStatus |
| ConnectionMachPort::BytesAvailable (uint32_t timeout_usec, Error *error_ptr) |
| { |
| return eConnectionStatusLostConnection; |
| } |
| |
| kern_return_t |
| ConnectionMachPort::Send (const PayloadType &payload) |
| { |
| struct MessageType message; |
| |
| /* (i) Form the message : */ |
| |
| /* (i.a) Fill the header fields : */ |
| message.head.msgh_bits = MACH_MSGH_BITS_REMOTE (MACH_MSG_TYPE_MAKE_SEND) | |
| MACH_MSGH_BITS_OTHER (MACH_MSGH_BITS_COMPLEX); |
| message.head.msgh_size = sizeof(MessageType); |
| message.head.msgh_local_port = MACH_PORT_NULL; |
| message.head.msgh_remote_port = m_port; |
| |
| /* (i.b) Explain the message type ( an integer ) */ |
| // message.type.msgt_name = MACH_MSG_TYPE_INTEGER_32; |
| // message.type.msgt_size = 32; |
| // message.type.msgt_number = 1; |
| // message.type.msgt_inline = TRUE; |
| // message.type.msgt_longform = FALSE; |
| // message.type.msgt_deallocate = FALSE; |
| /* message.type.msgt_unused = 0; */ /* not needed, I think */ |
| |
| /* (i.c) Fill the message with the given integer : */ |
| message.payload = payload; |
| |
| /* (ii) Send the message : */ |
| kern_return_t kret = ::mach_msg (&message.head, |
| MACH_SEND_MSG, |
| message.head.msgh_size, |
| 0, |
| MACH_PORT_NULL, |
| MACH_MSG_TIMEOUT_NONE, |
| MACH_PORT_NULL); |
| |
| return kret; |
| } |
| |
| kern_return_t |
| ConnectionMachPort::Receive (PayloadType &payload) |
| { |
| MessageType message; |
| message.head.msgh_size = sizeof(MessageType); |
| |
| kern_return_t kret = ::mach_msg (&message.head, |
| MACH_RCV_MSG, |
| 0, |
| sizeof(MessageType), |
| m_port, |
| MACH_MSG_TIMEOUT_NONE, |
| MACH_PORT_NULL); |
| |
| if (kret == KERN_SUCCESS) |
| payload = message.payload; |
| |
| return kret; |
| } |
| |
| |
| #endif // #if defined(__APPLE__) |