llvm-project/lldb/source/Core/ConnectionMachPort.cpp

260 lines
7.5 KiB
C++

//===-- 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 <mach/mach.h>
#include <servers/bootstrap.h>
// C++ Includes
// Other libraries and framework includes
// Project includes
#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 (0 == 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 (0 ==
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();
m_uri.assign(s);
} 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 (static_cast<size_t>(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 (static_cast<size_t>(::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;
}
m_uri.clear();
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;
}
std::string ConnectionMachPort::GetURI() { return m_uri; }
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__)